diff --git a/.github/workflows/nightly-ecr.yml b/.github/workflows/nightly-ecr.yml index e4fdb3b431..ccd8fa956c 100644 --- a/.github/workflows/nightly-ecr.yml +++ b/.github/workflows/nightly-ecr.yml @@ -16,6 +16,7 @@ permissions: jobs: ecr-nightly-test: + if: ${{ github.repository == 'zarf-dev/zarf' }} runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/nightly-eks.yml b/.github/workflows/nightly-eks.yml index 262e4b00c2..ce4dab24ba 100644 --- a/.github/workflows/nightly-eks.yml +++ b/.github/workflows/nightly-eks.yml @@ -24,6 +24,7 @@ concurrency: jobs: eks-nightly-test: + if: ${{ github.repository == 'zarf-dev/zarf' }} runs-on: ubuntu-latest steps: - name: Checkout diff --git a/packages/zarf-agent/manifests/deployment.yaml b/packages/zarf-agent/manifests/deployment.yaml index a8e481845f..61731ada18 100644 --- a/packages/zarf-agent/manifests/deployment.yaml +++ b/packages/zarf-agent/manifests/deployment.yaml @@ -21,6 +21,13 @@ spec: - name: private-registry priorityClassName: system-node-critical serviceAccountName: zarf + # Security context to comply with restricted PSS + securityContext: + runAsUser: 1000 + fsGroup: 2000 + runAsGroup: 2000 + seccompProfile: + type: "RuntimeDefault" containers: - name: server image: "###ZARF_REGISTRY###/###ZARF_CONST_AGENT_IMAGE###:###ZARF_CONST_AGENT_IMAGE_TAG###" @@ -32,6 +39,12 @@ spec: scheme: HTTPS ports: - containerPort: 8443 + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + runAsNonRoot: true + capabilities: + drop: ["ALL"] resources: requests: memory: "32Mi" diff --git a/src/cmd/initialize.go b/src/cmd/initialize.go index 0543f97749..d2fe5d5d35 100644 --- a/src/cmd/initialize.go +++ b/src/cmd/initialize.go @@ -18,6 +18,7 @@ import ( "github.com/zarf-dev/zarf/src/cmd/common" "github.com/zarf-dev/zarf/src/config" "github.com/zarf-dev/zarf/src/config/lang" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/packager" "github.com/zarf-dev/zarf/src/pkg/packager/sources" @@ -70,6 +71,9 @@ var initCmd = &cobra.Command{ if err != nil { return err } + // Since the new logger ignores pterm output the credential table is no longer printed on init. + // This note is the intended replacement, rather than printing creds by default. + logger.From(ctx).Info("init complete. To get credentials for Zarf deployed services run `zarf tools get-creds`") return nil }, } @@ -110,28 +114,26 @@ func findInitPackage(ctx context.Context, initPackageName string) (string, error return filepath.Join(absCachePath, initPackageName), nil } + if config.CommonOptions.Confirm { + return "", lang.ErrInitNotFound + } + // Finally, if the init-package doesn't exist in the cache directory, suggest downloading it downloadCacheTarget, err := downloadInitPackage(ctx, absCachePath) if err != nil { - if errors.Is(err, lang.ErrInitNotFound) { - return "", err - } return "", fmt.Errorf("failed to download the init package: %w", err) } return downloadCacheTarget, nil } func downloadInitPackage(ctx context.Context, cacheDirectory string) (string, error) { - if config.CommonOptions.Confirm { - return "", lang.ErrInitNotFound - } - + l := logger.From(ctx) url := zoci.GetInitPackageURL(config.CLIVersion) // Give the user the choice to download the init-package and note that this does require an internet connection message.Question(fmt.Sprintf(lang.CmdInitPullAsk, url)) - message.Note(lang.CmdInitPullNote) + l.Info("the init package was not found locally, but can be pulled in connected environments", "url", fmt.Sprintf("oci://%s", url)) var confirmDownload bool prompt := &survey.Confirm{ diff --git a/src/cmd/root.go b/src/cmd/root.go index 63513c7104..86f01eb1cc 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -222,8 +222,6 @@ func setupMessage(cfg messageCfg) error { message.InitializePTerm(io.Discard) // Disable all progress bars and spinners message.NoProgress = true - // Ensures no user input is needed while we maintain backwards compatibility with message - config.CommonOptions.Confirm = true return nil } diff --git a/src/pkg/cluster/injector.go b/src/pkg/cluster/injector.go index 03d49615d8..b459299460 100644 --- a/src/pkg/cluster/injector.go +++ b/src/pkg/cluster/injector.go @@ -27,6 +27,7 @@ import ( "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/config" "github.com/zarf-dev/zarf/src/internal/healthchecks" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/transform" "github.com/zarf-dev/zarf/src/pkg/utils" @@ -35,6 +36,8 @@ import ( // StartInjection initializes a Zarf injection into the cluster. func (c *Cluster) StartInjection(ctx context.Context, tmpDir, imagesDir string, injectorSeedSrcs []string) error { + l := logger.From(ctx) + start := time.Now() // Stop any previous running injection before starting. err := c.StopInjection(ctx) if err != nil { @@ -43,6 +46,7 @@ func (c *Cluster) StartInjection(ctx context.Context, tmpDir, imagesDir string, spinner := message.NewProgressSpinner("Attempting to bootstrap the seed image into the cluster") defer spinner.Stop() + l.Info("creating Zarf injector resources") resReq := v1ac.ResourceRequirements(). WithRequests(corev1.ResourceList{ @@ -111,11 +115,15 @@ func (c *Cluster) StartInjection(ctx context.Context, tmpDir, imagesDir string, } spinner.Success() + l.Debug("done with injection", "duration", time.Since(start)) return nil } // StopInjection handles cleanup once the seed registry is up. func (c *Cluster) StopInjection(ctx context.Context) error { + start := time.Now() + l := logger.From(ctx) + l.Debug("deleting injector resources") err := c.Clientset.CoreV1().Pods(ZarfNamespaceName).Delete(ctx, "injector", metav1.DeleteOptions{}) if err != nil && !kerrors.IsNotFound(err) { return err @@ -171,6 +179,7 @@ func (c *Cluster) StopInjection(ctx context.Context) error { if err != nil { return err } + l.Debug("done deleting injector resources", "duration", time.Since(start)) return nil } diff --git a/src/pkg/cluster/state.go b/src/pkg/cluster/state.go index 1c16734e81..5990ef66a9 100644 --- a/src/pkg/cluster/state.go +++ b/src/pkg/cluster/state.go @@ -38,12 +38,14 @@ const ( // InitZarfState initializes the Zarf state with the given temporary directory and init configs. func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitOptions) error { + l := logger.From(ctx) spinner := message.NewProgressSpinner("Gathering cluster state information") defer spinner.Stop() // Attempt to load an existing state prior to init. // NOTE: We are ignoring the error here because we don't really expect a state to exist yet. spinner.Updatef("Checking cluster for existing Zarf deployment") + l.Debug("checking cluster for existing Zarf deployment") state, err := c.LoadZarfState(ctx) if err != nil && !kerrors.IsNotFound(err) { return fmt.Errorf("failed to check for existing state: %w", err) @@ -53,7 +55,7 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO if state == nil { state = &types.ZarfState{} spinner.Updatef("New cluster, no prior Zarf deployments found") - + l.Debug("new cluster, no prior Zarf deployments found") if initOptions.ApplianceMode { // If the K3s component is being deployed, skip distro detection. state.Distro = DistroIsK3s @@ -76,6 +78,7 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO if state.Distro != DistroIsUnknown { spinner.Updatef("Detected K8s distro %s", state.Distro) + l.Debug("Detected K8s distro", "name", state.Distro) } // Setup zarf agent PKI @@ -95,6 +98,8 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO continue } spinner.Updatef("Marking existing namespace %s as ignored by Zarf Agent", namespace.Name) + l.Debug("marking namespace as ignored by Zarf Agent", "name", namespace.Name) + if namespace.Labels == nil { // Ensure label map exists to avoid nil panic namespace.Labels = make(map[string]string) @@ -110,6 +115,7 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO // Try to create the zarf namespace. spinner.Updatef("Creating the Zarf namespace") + l.Debug("creating the Zarf namespace") zarfNamespace := NewZarfManagedApplyNamespace(ZarfNamespaceName) _, err = c.Clientset.CoreV1().Namespaces().Apply(ctx, zarfNamespace, metav1.ApplyOptions{FieldManager: FieldManagerName, Force: true}) if err != nil { @@ -145,17 +151,21 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO initOptions.ArtifactServer.FillInEmptyValues() state.ArtifactServer = initOptions.ArtifactServer } else { + // TODO (@austinabro321) validate immediately in `zarf init` if these are set and not equal and error out if so if helpers.IsNotZeroAndNotEqual(initOptions.GitServer, state.GitServer) { message.Warn("Detected a change in Git Server init options on a re-init. Ignoring... To update run:") message.ZarfCommand("tools update-creds git") + l.Warn("ignoring change in git sever init options on re-init, to update run `zarf tools update-creds git`") } if helpers.IsNotZeroAndNotEqual(initOptions.RegistryInfo, state.RegistryInfo) { message.Warn("Detected a change in Image Registry init options on a re-init. Ignoring... To update run:") message.ZarfCommand("tools update-creds registry") + l.Warn("ignoring change to registry init options on re-init, to update run `zarf tools update-creds registry`") } if helpers.IsNotZeroAndNotEqual(initOptions.ArtifactServer, state.ArtifactServer) { message.Warn("Detected a change in Artifact Server init options on a re-init. Ignoring... To update run:") message.ZarfCommand("tools update-creds artifact") + l.Warn("ignoring change to registry init options on re-init, to update run `zarf tools update-creds registry`") } } diff --git a/src/pkg/logger/logger.go b/src/pkg/logger/logger.go index 82e98d44e6..c812f7b3ca 100644 --- a/src/pkg/logger/logger.go +++ b/src/pkg/logger/logger.go @@ -37,6 +37,22 @@ var ( Error = Level(slog.LevelError) // 8 ) +// String returns the string representation of the Level. +func (l Level) String() string { + switch l { + case Debug: + return "debug" + case Info: + return "info" + case Warn: + return "warn" + case Error: + return "error" + default: + return "unknown" + } +} + // validLevels is a set that provides an ergonomic way to check if a level is a member of the set. var validLevels = map[Level]bool{ Debug: true, @@ -99,6 +115,18 @@ var ( DestinationNone Destination = io.Discard ) +// can't define method on Destination type +func destinationString(d Destination) string { + switch { + case d == DestinationDefault: + return "os.Stderr" + case d == DestinationNone: + return "io.Discard" + default: + return "unknown" + } +} + // Config is configuration for a logger. type Config struct { // Level sets the log level. An empty value corresponds to Info aka 0. @@ -107,6 +135,15 @@ type Config struct { Destination } +// LogValue of config +func (c Config) LogValue() slog.Value { + return slog.GroupValue( + slog.String("level", c.Level.String()), + slog.Any("format", c.Format), + slog.Any("Destination", destinationString(c.Destination)), + ) +} + // ConfigDefault returns a Config with defaults like Text formatting at Info level writing to Stderr. func ConfigDefault() Config { return Config{ diff --git a/src/pkg/packager/creator/utils.go b/src/pkg/packager/creator/utils.go index 3d5c5ef016..891e80db5a 100644 --- a/src/pkg/packager/creator/utils.go +++ b/src/pkg/packager/creator/utils.go @@ -54,7 +54,7 @@ func recordPackageMetadata(pkg *v1alpha1.ZarfPackage, createOpts types.ZarfCreat hostname, _ := os.Hostname() pkg.Build.Terminal = hostname - if pkg.IsInitConfig() { + if pkg.IsInitConfig() && pkg.Metadata.Version == "" { pkg.Metadata.Version = config.CLIVersion } diff --git a/src/pkg/packager/deploy.go b/src/pkg/packager/deploy.go index 576926d617..703bdc97ae 100644 --- a/src/pkg/packager/deploy.go +++ b/src/pkg/packager/deploy.go @@ -48,9 +48,11 @@ var ( ) func (p *Packager) resetRegistryHPA(ctx context.Context) { + l := logger.From(ctx) if p.isConnectedToCluster() && p.hpaModified { if err := p.cluster.EnableRegHPAScaleDown(ctx); err != nil { message.Debugf("unable to reenable the registry HPA scale down: %s", err.Error()) + l.Debug("unable to reenable the registry HPA scale down", "error", err.Error()) } } } @@ -283,7 +285,7 @@ func (p *Packager) deployInitComponent(ctx context.Context, component v1alpha1.Z // Do cleanup for when we inject the seed registry during initialization if isSeedRegistry { if err := p.cluster.StopInjection(ctx); err != nil { - return nil, fmt.Errorf("unable to seed the Zarf Registry: %w", err) + return nil, fmt.Errorf("failed to delete injector resources: %w", err) } } diff --git a/src/pkg/packager/dev.go b/src/pkg/packager/dev.go index 1985edb655..589eedb5be 100644 --- a/src/pkg/packager/dev.go +++ b/src/pkg/packager/dev.go @@ -9,10 +9,12 @@ import ( "fmt" "os" "runtime" + "time" "github.com/defenseunicorns/pkg/helpers/v2" "github.com/zarf-dev/zarf/src/config" "github.com/zarf-dev/zarf/src/pkg/layout" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/packager/creator" "github.com/zarf-dev/zarf/src/pkg/packager/filters" @@ -21,6 +23,8 @@ import ( // DevDeploy creates + deploys a package in one shot func (p *Packager) DevDeploy(ctx context.Context) error { + l := logger.From(ctx) + start := time.Now() config.CommonOptions.Confirm = true p.cfg.CreateOpts.SkipSBOM = !p.cfg.CreateOpts.NoYOLO @@ -74,6 +78,7 @@ func (p *Packager) DevDeploy(ctx context.Context) error { } message.HeaderInfof("📦 PACKAGE DEPLOY %s", p.cfg.Pkg.Metadata.Name) + l.Info("starting package deploy", "name", p.cfg.Pkg.Metadata.Name) if !p.cfg.CreateOpts.NoYOLO { p.cfg.Pkg.Metadata.YOLO = true @@ -108,10 +113,12 @@ func (p *Packager) DevDeploy(ctx context.Context) error { } if len(deployedComponents) == 0 { message.Warn("No components were selected for deployment. Inspect the package to view the available components and select components interactively or by name with \"--components\"") + l.Warn("No components were selected for deployment. Inspect the package to view the available components and select components interactively or by name with \"--components\"") } // Notify all the things about the successful deployment message.Successf("Zarf dev deployment complete") + l.Debug("dev deployment complete", "package", p.cfg.Pkg.Metadata.Name, "duration", time.Since(start)) message.HorizontalRule() message.Title("Next steps:", "") diff --git a/src/pkg/packager/generate.go b/src/pkg/packager/generate.go index 5d92d56b8d..0f84a8295c 100644 --- a/src/pkg/packager/generate.go +++ b/src/pkg/packager/generate.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/defenseunicorns/pkg/helpers/v2" goyaml "github.com/goccy/go-yaml" @@ -17,11 +18,14 @@ import ( "github.com/zarf-dev/zarf/src/config" "github.com/zarf-dev/zarf/src/pkg/layout" "github.com/zarf-dev/zarf/src/pkg/lint" + "github.com/zarf-dev/zarf/src/pkg/logger" "github.com/zarf-dev/zarf/src/pkg/message" ) // Generate generates a Zarf package definition. func (p *Packager) Generate(ctx context.Context) error { + l := logger.From(ctx) + start := time.Now() generatedZarfYAMLPath := filepath.Join(p.cfg.GenerateOpts.Output, layout.ZarfYAML) spinner := message.NewProgressSpinner("Generating package for %q at %s", p.cfg.GenerateOpts.Name, generatedZarfYAMLPath) @@ -29,6 +33,9 @@ func (p *Packager) Generate(ctx context.Context) error { prefixed := filepath.Join(p.cfg.GenerateOpts.Output, fmt.Sprintf("%s-%s", p.cfg.GenerateOpts.Name, layout.ZarfYAML)) message.Warnf("%s already exists, writing to %s", generatedZarfYAMLPath, prefixed) + l.Warn("using a prefixed name since zarf.yaml already exists in the output directory", + "output-directory", p.cfg.GenerateOpts.Output, + "name", prefixed) generatedZarfYAMLPath = prefixed @@ -36,6 +43,7 @@ func (p *Packager) Generate(ctx context.Context) error { return fmt.Errorf("unable to generate package, %s already exists", generatedZarfYAMLPath) } } + l.Info("generating package", "name", p.cfg.GenerateOpts.Name, "path", generatedZarfYAMLPath) generatedComponent := v1alpha1.ZarfComponent{ Name: p.cfg.GenerateOpts.Name, @@ -67,6 +75,7 @@ func (p *Packager) Generate(ctx context.Context) error { if err != nil { // purposefully not returning error here, as we can still generate the package without images message.Warnf("Unable to find images: %s", err.Error()) + l.Error("failed to find images", "error", err.Error()) } for i := range p.cfg.Pkg.Components { @@ -96,6 +105,7 @@ func (p *Packager) Generate(ctx context.Context) error { content = strings.Replace(content, "components:\n", "\ncomponents:\n", 1) spinner.Successf("Generated package for %q at %s", p.cfg.GenerateOpts.Name, generatedZarfYAMLPath) + l.Debug("generated package", "name", p.cfg.GenerateOpts.Name, "path", generatedZarfYAMLPath, "duration", time.Since(start)) return os.WriteFile(generatedZarfYAMLPath, []byte(content), helpers.ReadAllWriteUser) } diff --git a/src/pkg/zoci/pull.go b/src/pkg/zoci/pull.go index 1984f87ffd..54e58cb4ed 100644 --- a/src/pkg/zoci/pull.go +++ b/src/pkg/zoci/pull.go @@ -35,7 +35,6 @@ var ( // - zarf.yaml.sig func (r *Remote) PullPackage(ctx context.Context, destinationDir string, concurrency int, layersToPull ...ocispec.Descriptor) (_ []ocispec.Descriptor, err error) { isPartialPull := len(layersToPull) > 0 - r.Log().Debug(fmt.Sprintf("Pulling %s", r.Repo().Reference)) manifest, err := r.FetchRoot(ctx) if err != nil { @@ -52,11 +51,14 @@ func (r *Remote) PullPackage(ctx context.Context, destinationDir string, concurr } layersToPull = append(layersToPull, manifest.Config) + layerSize := oci.SumDescsSize(layersToPull) + // TODO (@austinabro321) change this and other r.Log() calls to the proper slog format + r.Log().Info(fmt.Sprintf("Pulling %s, size: %s", r.Repo().Reference, utils.ByteFormat(float64(layerSize), 2))) + // Create a thread to update a progress bar as we save the package to disk doneSaving := make(chan error) successText := fmt.Sprintf("Pulling %q", helpers.OCIURLPrefix+r.Repo().Reference.String()) - layerSize := oci.SumDescsSize(layersToPull) go utils.RenderProgressBarForLocalDirWrite(destinationDir, layerSize, doneSaving, "Pulling", successText) dst, err := file.New(destinationDir) diff --git a/src/test/e2e/29_config_file_test.go b/src/test/e2e/29_config_file_test.go index bcef00ad87..f291f76ef0 100644 --- a/src/test/e2e/29_config_file_test.go +++ b/src/test/e2e/29_config_file_test.go @@ -5,59 +5,35 @@ package test import ( + "context" "fmt" - "os" "path/filepath" "testing" "github.com/stretchr/testify/require" -) - -const envKey = "ZARF_CONFIG" - -func TestConfigFile(t *testing.T) { - t.Log("E2E: Config file") - - var ( - path = fmt.Sprintf("zarf-package-config-file-%s.tar.zst", e2e.Arch) - dir = "examples/config-file" - config = "zarf-config.toml" - ) - - e2e.CleanFiles(t, path) - - // Test the config file environment variable - t.Setenv(envKey, filepath.Join(dir, config)) - defer func() { - require.NoError(t, os.Unsetenv(envKey)) - }() - configFileTests(t, dir, path) - configFileDefaultTests(t) - - stdOut, stdErr, err := e2e.Zarf(t, "package", "remove", path, "--confirm") - require.NoError(t, err, stdOut, stdErr) + layout2 "github.com/zarf-dev/zarf/src/internal/packager2/layout" +) - // Cleanup - e2e.CleanFiles(t, path) -} +func TestConfigFileCreate(t *testing.T) { + tmpDir := t.TempDir() + dir := "examples/config-file" -func configFileTests(t *testing.T, dir, path string) { - t.Helper() + t.Setenv("ZARF_CONFIG", filepath.Join(dir, "zarf-config.toml")) - _, stdErr, err := e2e.Zarf(t, "package", "create", dir, "--confirm") + _, _, err := e2e.Zarf(t, "package", "create", dir, "--confirm", "-o", tmpDir) require.NoError(t, err) - require.Contains(t, string(stdErr), "This is a zebra and they have stripes") - require.Contains(t, string(stdErr), "This is a leopard and they have spots") - _, stdErr, err = e2e.Zarf(t, "package", "deploy", path, "--confirm") + tarPath := filepath.Join(tmpDir, fmt.Sprintf("zarf-package-config-file-%s.tar.zst", e2e.Arch)) + pkgLayout, err := layout2.LoadFromTar(context.Background(), tarPath, layout2.PackageLayoutOptions{}) + require.NoError(t, err) + require.Equal(t, "This is a zebra and they have stripes", pkgLayout.Pkg.Components[1].Description) + require.Equal(t, "This is a leopard and they have spots", pkgLayout.Pkg.Components[2].Description) + _, err = pkgLayout.GetSBOM(t.TempDir()) require.NoError(t, err) - require.Contains(t, string(stdErr), "📦 LION COMPONENT") - require.NotContains(t, string(stdErr), "📦 LEOPARD COMPONENT") - require.NotContains(t, string(stdErr), "📦 ZEBRA COMPONENT") - // This package does not contain anything SBOMable - require.NotContains(t, string(stdErr), "This package does NOT contain an SBOM.") + _, _, err = e2e.Zarf(t, "package", "deploy", tarPath, "--confirm") + require.NoError(t, err) // Verify the configmap was properly templated kubectlOut, _, err := e2e.Kubectl(t, "-n", "zarf", "get", "configmap", "simple-configmap", "-o", "jsonpath={.data.templateme\\.properties}") @@ -99,9 +75,7 @@ H4RxbE+FpmsMAUCpdrzvFkc= require.Equal(t, tlsKey, kubectlOut) } -func configFileDefaultTests(t *testing.T) { - t.Helper() - +func TestConfigFileDefault(t *testing.T) { globalFlags := []string{ "architecture: 509a38f0", "log_level: 6a845a41", @@ -144,10 +118,7 @@ func configFileDefaultTests(t *testing.T) { } // Test remaining default initializers - t.Setenv(envKey, filepath.Join("src", "test", "zarf-config-test.toml")) - defer func() { - require.NoError(t, os.Unsetenv(envKey)) - }() + t.Setenv("ZARF_CONFIG", filepath.Join("src", "test", "zarf-config-test.toml")) // Test global flags stdOut, _, err := e2e.Zarf(t, "--help")