diff --git a/cmd/config.go b/cmd/config.go index e722c402..8888dd47 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -18,7 +18,7 @@ func assembleConfigForCommand(configBase *deployer.Config, configFromArgs deploy // Apply user config on top (overriding defaults). if !skipUserConfig { - if err := tryApplyUserDefaults(globalLogger, &config); err != nil { + if err := tryApplyUserDefaults(&config); err != nil { return deployer.Config{}, fmt.Errorf("applying user config: %w", err) } } diff --git a/cmd/deploy.go b/cmd/deploy.go index 42c6dd8f..c3e9ce6a 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -19,7 +19,7 @@ import ( "github.com/stackrox/roxie/internal/helpers" "github.com/stackrox/roxie/internal/imagetag" "github.com/stackrox/roxie/internal/k8s" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/manifest" "github.com/stackrox/roxie/internal/roxieenv" "github.com/stackrox/roxie/internal/stackroxversions" @@ -216,9 +216,8 @@ this flag can be used to tell roxie how to pre-load images for the current clust } func runDeploy(cmd *cobra.Command, args []string) error { - log := globalLogger if !dryRun { - if err := env.Initialize(log); err != nil { + if err := env.Initialize(); err != nil { return err } } @@ -239,7 +238,7 @@ func runDeploy(cmd *cobra.Command, args []string) error { setupCtx, setupCancel := context.WithTimeout(context.Background(), 10*time.Minute) defer setupCancel() - clusterConfig := retrieveClusterConfigForComponents(setupCtx, log, components) + clusterConfig := retrieveClusterConfigForComponents(setupCtx, components) deploySettings, err := assembleConfigForCommand(clusterConfig, deploySettingsFromArgs, skipUserConfig) if err != nil { @@ -252,7 +251,7 @@ func runDeploy(cmd *cobra.Command, args []string) error { if deploySettings.Roxie.Version != "" { log.Dimf("Using main image tag %s", deploySettings.Roxie.Version) } else { - mainImageTag, err := helpers.LookupMainImageTag(ctx, log) + mainImageTag, err := helpers.LookupMainImageTag(ctx) if err != nil { return fmt.Errorf("looking up main image tag: %w", err) } @@ -265,15 +264,15 @@ func runDeploy(cmd *cobra.Command, args []string) error { } } - if err := configureConfig(log, components, &deploySettings); err != nil { + if err := configureConfig(components, &deploySettings); err != nil { return err } - if err := deployValidate(log, components, &deploySettings); err != nil { + if err := deployValidate(components, &deploySettings); err != nil { return err } - d, err := deployer.New(log) + d, err := deployer.New() if err != nil { return fmt.Errorf("failed to create deployer: %w", err) } @@ -282,7 +281,6 @@ func runDeploy(cmd *cobra.Command, args []string) error { if envrc != "" { d.SetEnvrcFile(envrc) } - d.SetVerbose(verbose) d.SetConfig(deploySettings) if dryRun { @@ -295,7 +293,7 @@ func runDeploy(cmd *cobra.Command, args []string) error { if deploySettings.Roxie.ClusterType.IsLocal() && !deploySettings.Roxie.KonfluxImagesEnabled() { var preLoader deployer.ImagePreLoader if imagePreLoadCommand != "" { - preLoader = deployer.NewCustomImagePreloader(ctx, log, imagePreLoadCommand) + preLoader = deployer.NewCustomImagePreloader(imagePreLoadCommand) } else { preLoader, err = d.GetPreLoaderForCluster() if err != nil { @@ -344,13 +342,13 @@ func runDeploy(cmd *cobra.Command, args []string) error { RoxieEnvironment: roxieEnv, Config: deploySettings, } - if err := manifest.CreateManifestSecretOnCluster(ctx, log, m); err != nil { + if err := manifest.CreateManifestSecretOnCluster(ctx, m); err != nil { log.Warningf("Failed to save roxie manifest: %v", err) } } if components.IncludesCentral() && envrc == "" { - if err := spawnSubshellForDeployerEnv(deploySettings.Roxie, d, log); err != nil { + if err := spawnSubshellForDeployerEnv(deploySettings.Roxie, d); err != nil { return fmt.Errorf("failed to spawn subshell: %w", err) } } @@ -388,10 +386,9 @@ func computeDeployContextTimeout(components component.Component, cfg deployer.Co func retrieveClusterConfigForComponents( ctx context.Context, - log *logger.Logger, components component.Component, ) *deployer.Config { - clusterManifest, err := manifest.LoadManifestSecret(ctx, log) + clusterManifest, err := manifest.LoadManifestSecret(ctx) if err != nil { if errors.Is(err, k8s.ErrResourceNotFound) { log.Dim("No existing manifest found on cluster, starting from defaults") @@ -411,7 +408,7 @@ func retrieveClusterConfigForComponents( return &clusterManifest.Config } -func configureConfig(log *logger.Logger, components component.Component, deploySettings *deployer.Config) error { +func configureConfig(components component.Component, deploySettings *deployer.Config) error { if deploySettings.Roxie.ClusterType == "" { clusterType := env.GetAutoDetectedClusterType() log.Dimf("Detected cluster type: %v", clusterType) @@ -425,10 +422,8 @@ func configureConfig(log *logger.Logger, components component.Component, deployS if err != nil { return err } - if verbose { - log.Dimf("Applying the following defaults based on cluster type %v:", clusterType) - helpers.LogMultilineYaml(log, defaults) - } + log.Debugf("Applying the following defaults based on cluster type %v:", clusterType) + log.LogMultilineYaml(defaults) // Deal with the "auto" resourceProfile. if deploySettings.Central.ResourceProfile == types.ResourceProfileAuto { @@ -466,10 +461,8 @@ func configureConfig(log *logger.Logger, components component.Component, deployS return fmt.Errorf("configuring SecuredCluster spec: %w", err) } } - if verbose { - log.Dim("Deployment configuration:") - helpers.LogMultilineYaml(log, deploySettings) - } + log.Debug("Deployment configuration:") + log.LogMultilineYaml(deploySettings) if !deploySettings.Central.PortForwardingSet() && !deploySettings.Central.ExposureEnabled() { log.Info("Enabling port-forwarding due to no exposure") @@ -479,7 +472,7 @@ func configureConfig(log *logger.Logger, components component.Component, deployS return nil } -func deployValidate(log *logger.Logger, components component.Component, deploySettings *deployer.Config) error { +func deployValidate(components component.Component, deploySettings *deployer.Config) error { if components.IncludesCentral() && os.Getenv("ROXIE_SHELL") != "" { return errors.New("already in a roxie sub-shell (ROXIE_SHELL environment variable is set), please exit the shell and try again") } diff --git a/cmd/deploy_test.go b/cmd/deploy_test.go index f2f16048..98ceb442 100644 --- a/cmd/deploy_test.go +++ b/cmd/deploy_test.go @@ -11,7 +11,6 @@ import ( "github.com/stackrox/roxie/internal/component" "github.com/stackrox/roxie/internal/deployer" "github.com/stackrox/roxie/internal/imagetag" - "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/paths" "github.com/stackrox/roxie/internal/types" "github.com/stretchr/testify/assert" @@ -316,8 +315,6 @@ func TestNewDeployCmd_SetRejectsSpec(t *testing.T) { } func TestApplyUserDefaults(t *testing.T) { - log := logger.New() - tests := []struct { name string config deployer.Config @@ -405,7 +402,7 @@ func TestApplyUserDefaults(t *testing.T) { cfg := deployer.NewConfig() require.NoError(t, mergo.Merge(&cfg, &tt.config, mergo.WithOverride, mergo.WithoutDereference)) - require.NoError(t, tryApplyUserDefaults(log, &cfg)) + require.NoError(t, tryApplyUserDefaults(&cfg)) expected := deployer.NewConfig() require.NoError(t, mergo.Merge(&expected, &tt.expected, mergo.WithOverride, mergo.WithoutDereference)) @@ -425,7 +422,7 @@ func TestApplyUserDefaults(t *testing.T) { require.NoError(t, os.WriteFile(configPath, []byte(`invalid: [yaml`), 0o644)) cfg := deployer.NewConfig() - assert.Error(t, tryApplyUserDefaults(log, &cfg)) + assert.Error(t, tryApplyUserDefaults(&cfg)) }) } diff --git a/cmd/env.go b/cmd/env.go index 129356bd..f4454902 100644 --- a/cmd/env.go +++ b/cmd/env.go @@ -21,8 +21,7 @@ func newEnvCmd() *cobra.Command { } func runEnv(cmd *cobra.Command, args []string) error { - log := globalLogger - if err := env.Initialize(log); err != nil { + if err := env.Initialize(); err != nil { return err } diff --git a/cmd/main.go b/cmd/main.go index 5810f4ef..52acea85 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,7 +12,7 @@ import ( "github.com/spf13/cobra" "github.com/stackrox/roxie/internal/deployer" "github.com/stackrox/roxie/internal/helpers" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/paths" "gopkg.in/yaml.v3" "helm.sh/helm/v3/pkg/strvals" @@ -27,8 +27,6 @@ var ( skipUserConfig bool - globalLogger = logger.New() - // We need this set up before command line flags are parsed. deploySettingsFromArgs = deployer.NewConfig() ) @@ -44,7 +42,7 @@ func main() { // If a user config file exists, apply those user defaults on top the // current config. This essentially means, that the user config can // override values, which are already initialized in NewConfig(). -func tryApplyUserDefaults(log *logger.Logger, config *deployer.Config) error { +func tryApplyUserDefaults(config *deployer.Config) error { path, err := paths.UserConfigPath(true) if err != nil { return err @@ -72,6 +70,10 @@ var rootCmd = &cobra.Command{ Short: "roxie - Advanced Cluster Security Deployment Tool", Long: `roxie is a fast, developer-friendly CLI to deploy and manage Red Hat Advanced Cluster Security (ACS) on any Kubernetes/OpenShift cluster.`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + log.SetVerbose(verbose) + return nil + }, SilenceUsage: true, SilenceErrors: true, } diff --git a/cmd/shell.go b/cmd/shell.go index a5a4b5d1..a403332f 100644 --- a/cmd/shell.go +++ b/cmd/shell.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" "github.com/stackrox/roxie/internal/env" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/manifest" ) @@ -52,8 +52,7 @@ Examples: } func runShell(args []string) error { - log := logger.New() - if err := env.Initialize(log); err != nil { + if err := env.Initialize(); err != nil { return err } @@ -66,7 +65,7 @@ func runShell(args []string) error { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() - m, err := manifest.LoadManifestSecret(ctx, log) + m, err := manifest.LoadManifestSecret(ctx) if err != nil { return fmt.Errorf("failed to load roxie manifest: %w", err) } @@ -85,10 +84,10 @@ func runShell(args []string) error { } defer os.RemoveAll(tempDir) - centralDeploymentInfo, err := manifest.ManifestToCentralDeploymentInfo(ctx, log, tempDir, m) + centralDeploymentInfo, err := manifest.ManifestToCentralDeploymentInfo(ctx, tempDir, m) if err != nil { return fmt.Errorf("extracting central deployment info from manifest: %w", err) } - return runCommandOrSubshell(config.Roxie, centralDeploymentInfo, log, args) + return runCommandOrSubshell(config.Roxie, centralDeploymentInfo, args) } diff --git a/cmd/subshell.go b/cmd/subshell.go index 2a06823a..1eaa7384 100644 --- a/cmd/subshell.go +++ b/cmd/subshell.go @@ -11,20 +11,20 @@ import ( "github.com/stackrox/roxie/internal/deployer" "github.com/stackrox/roxie/internal/env" "github.com/stackrox/roxie/internal/haproxy" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/roxieenv" "github.com/stackrox/roxie/internal/types" ) // spawnSubshellForDeployerEnv assembles the roxie environment from a Deployer and invokes an interactive subshell. -func spawnSubshellForDeployerEnv(roxieConfig deployer.RoxieConfig, d *deployer.Deployer, log *logger.Logger) error { - return runCommandOrSubshell(roxieConfig, d.GetCentralDeploymentInfo(), log, nil) +func spawnSubshellForDeployerEnv(roxieConfig deployer.RoxieConfig, d *deployer.Deployer) error { + return runCommandOrSubshell(roxieConfig, d.GetCentralDeploymentInfo(), nil) } // runCommandOrSubshell spawns an interactive subshell or runs the provided command using the given // central deployment info. // It handles HAProxy setup, prints the connection banner, and manages shell lifecycle. -func runCommandOrSubshell(roxieConfig deployer.RoxieConfig, centralDeploymentInfo types.CentralDeploymentInfo, log *logger.Logger, args []string) error { +func runCommandOrSubshell(roxieConfig deployer.RoxieConfig, centralDeploymentInfo types.CentralDeploymentInfo, args []string) error { cmdEnv := os.Environ() for name, val := range roxieenv.AssembleRoxieEnvironment(centralDeploymentInfo).Export() { cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", name, val)) @@ -33,7 +33,7 @@ func runCommandOrSubshell(roxieConfig deployer.RoxieConfig, centralDeploymentInf cmdEnv = append(cmdEnv, fmt.Sprintf("name=acs@%s", centralDeploymentInfo.KubeContext)) if roxieConfig.HAProxy.Enabled() { - cleanupFunc, err := tryStartHAProxy(log, roxieConfig, ¢ralDeploymentInfo) + cleanupFunc, err := tryStartHAProxy(roxieConfig, ¢ralDeploymentInfo) if err != nil { log.Warningf("Failed to start HAProxy: %v", err) } @@ -89,7 +89,6 @@ func runCommandOrSubshell(roxieConfig deployer.RoxieConfig, centralDeploymentInf } func tryStartHAProxy( - log *logger.Logger, roxieConfig deployer.RoxieConfig, centralDeploymentInfo *types.CentralDeploymentInfo) (func(), error) { diff --git a/cmd/teardown.go b/cmd/teardown.go index 1a2729ff..0b5c09d2 100644 --- a/cmd/teardown.go +++ b/cmd/teardown.go @@ -11,6 +11,7 @@ import ( "github.com/stackrox/roxie/internal/deployer" "github.com/stackrox/roxie/internal/env" "github.com/stackrox/roxie/internal/k8s" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/manifest" ) @@ -40,8 +41,7 @@ func newTeardownCmd(settings *deployer.Config) *cobra.Command { } func runTeardown(cmd *cobra.Command, args []string) error { - log := globalLogger - if err := env.Initialize(log); err != nil { + if err := env.Initialize(); err != nil { return err } @@ -61,7 +61,7 @@ func runTeardown(cmd *cobra.Command, args []string) error { defer cancel() var clusterConfig *deployer.Config - clusterManifest, err := manifest.LoadManifestSecret(ctx, log) + clusterManifest, err := manifest.LoadManifestSecret(ctx) if errors.Is(err, k8s.ErrResourceNotFound) { log.Infof("No roxie manifest found in cluster, proceeding without it") } else if err != nil { @@ -75,26 +75,25 @@ func runTeardown(cmd *cobra.Command, args []string) error { return err } - d, err := deployer.New(log) + d, err := deployer.New() if err != nil { return fmt.Errorf("failed to create deployer: %w", err) } defer d.Cleanup() d.SetConfig(deploySettings) - d.SetVerbose(verbose) if err := d.Teardown(ctx, components); err != nil { return fmt.Errorf("teardown failed: %w", err) } if components.IncludesCentral() { - if err := manifest.DeleteManifestSecret(ctx, log); err != nil { + if err := manifest.DeleteManifestSecret(ctx); err != nil { log.Warningf("Failed to delete roxie manifest: %v", err) } } if components == component.All { - if err := manifest.DeleteRoxieNamespace(ctx, log); err != nil { + if err := manifest.DeleteRoxieNamespace(ctx); err != nil { log.Warningf("Failed to delete roxie namespace: %v", err) } } diff --git a/internal/containerrt/containerrt.go b/internal/containerrt/containerrt.go index df659438..87dce5be 100644 --- a/internal/containerrt/containerrt.go +++ b/internal/containerrt/containerrt.go @@ -8,7 +8,7 @@ import ( "os" "strings" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/moby/moby/api/pkg/stdcopy" "github.com/moby/moby/client" @@ -38,7 +38,7 @@ func ListLocalImages(ctx context.Context, host string) ([]string, error) { } // ExecInContainer runs a command inside a container and returns its stdout. -func ExecInContainer(ctx context.Context, log *logger.Logger, host, containerName string, cmd []string) ([]byte, error) { +func ExecInContainer(ctx context.Context, host, containerName string, cmd []string) ([]byte, error) { cli, err := client.New(client.WithHost(host)) if err != nil { return nil, fmt.Errorf("creating container runtime client: %w", err) @@ -102,7 +102,7 @@ func ParseCrictlImages(data []byte) ([]string, error) { // ResolveSocket returns the container runtime socket URI by checking DOCKER_HOST, // then probing well-known paths for Docker, Podman. Returns "" if none found. -func ResolveSocket(log *logger.Logger) string { +func ResolveSocket() string { if host := os.Getenv("DOCKER_HOST"); host != "" { log.Dimf("Using container runtime socket from DOCKER_HOST: %s", host) return host diff --git a/internal/deployer/addons.go b/internal/deployer/addons.go index 3d683db6..e8221844 100644 --- a/internal/deployer/addons.go +++ b/internal/deployer/addons.go @@ -8,7 +8,7 @@ import ( "regexp" "slices" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) var ( @@ -36,16 +36,16 @@ func (d *Deployer) deployAddOns(ctx context.Context, addOns []AddOn) error { return fmt.Errorf("failed to prepare namespace: %w", err) } - d.logger.Infof("Deploying %d add-on(s)...", len(addOns)) + log.Infof("Deploying %d add-on(s)...", len(addOns)) for _, addon := range addOns { if err := addon.Deploy(ctx); err != nil { if !addon.IsOptional() { return fmt.Errorf("installing non-optional add-on %q: %w", addon.Name(), err) } - d.logger.Warningf("Failed to install add-on %q: %v", addon.Name(), err) + log.Warningf("Failed to install add-on %q: %v", addon.Name(), err) } else { - d.logger.Successf("Add-on %q installed", addon.Name()) + log.Successf("Add-on %q installed", addon.Name()) } } @@ -57,26 +57,23 @@ func (d *Deployer) teardownAddOns(ctx context.Context, addOns []AddOn) { return } - d.logger.Infof("Tearing down %d add-on(s)...", len(addOns)) + log.Infof("Tearing down %d add-on(s)...", len(addOns)) for _, addon := range addOns { if err := addon.Teardown(ctx); err != nil { if !addon.IsOptional() { - d.logger.Errorf("Failed to tear down non-optional add-on %q: %v", addon.Name(), err) + log.Errorf("Failed to tear down non-optional add-on %q: %v", addon.Name(), err) } else { - d.logger.Warningf("Failed to tear down optional add-on %q: %v", addon.Name(), err) + log.Warningf("Failed to tear down optional add-on %q: %v", addon.Name(), err) } } else { - d.logger.Successf("Add-on %q torn down", addon.Name()) + log.Successf("Add-on %q torn down", addon.Name()) } } } -// AddOnConfig carries runtime dependencies (logger, verbosity) needed to construct add-on instances. -type AddOnConfig struct { - log *logger.Logger - verbose bool -} +// AddOnConfig carries runtime dependencies needed to construct add-on instances. +type AddOnConfig struct{} // ResolveEnabledAddOns returns the enabled add-ons sorted by descending priority (name for ties at zero). func (d *Deployer) ResolveEnabledAddOns() ([]AddOn, error) { @@ -155,8 +152,5 @@ func createAddOnFromDefinition( // AddOnConfiguration builds an AddOnConfig from the deployer's runtime state. func (d *Deployer) AddOnConfiguration() AddOnConfig { - return AddOnConfig{ - log: d.logger, - verbose: d.verbose, - } + return AddOnConfig{} } diff --git a/internal/deployer/addons_helm_chart.go b/internal/deployer/addons_helm_chart.go index 85f97e65..4fb4e1cd 100644 --- a/internal/deployer/addons_helm_chart.go +++ b/internal/deployer/addons_helm_chart.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/stackrox/roxie/internal/helm" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) const ( @@ -14,8 +14,6 @@ const ( ) type helmAddOn struct { - log *logger.Logger - verbose bool name string releaseName string installOpts helm.InstallOptions @@ -36,23 +34,13 @@ func (a *helmAddOn) IsOptional() bool { } func (a *helmAddOn) Deploy(ctx context.Context) error { - a.log.Infof("Installing add-on %q as Helm release %q", a.name, a.releaseName) - helmCtx := helm.HelmCtx{ - Ctx: ctx, - Log: a.log, - Verbose: a.verbose, - } - return helm.Install(helmCtx, a.installOpts) + log.Infof("Installing add-on %q as Helm release %q", a.name, a.releaseName) + return helm.Install(ctx, a.installOpts) } func (a *helmAddOn) Teardown(ctx context.Context) error { - a.log.Infof("Uninstalling add-on Helm release %q", a.releaseName) - helmCtx := helm.HelmCtx{ - Ctx: ctx, - Log: a.log, - Verbose: a.verbose, - } - return helm.Uninstall(helmCtx, a.releaseName, a.installOpts.Namespace) + log.Infof("Uninstalling add-on Helm release %q", a.releaseName) + return helm.Uninstall(ctx, a.releaseName, a.installOpts.Namespace) } func newHelmAddOn( @@ -68,8 +56,6 @@ func newHelmAddOn( opts.ReleaseName = releaseName opts.Namespace = namespace return &helmAddOn{ - log: addOnCfg.log, - verbose: addOnCfg.verbose, name: name, releaseName: releaseName, installOpts: opts, @@ -83,7 +69,7 @@ func (h *HelmChartRepoAddOn) New( addOnCfg AddOnConfig, commonProperties CommonAddOnProperties, name, namespace string) (AddOn, error) { - addOnCfg.log.Infof("Add-on %s: using Helm chart %s from %s", name, h.Chart, h.Repo) + log.Infof("Add-on %s: using Helm chart %s from %s", name, h.Chart, h.Repo) values, err := h.GetValues() if err != nil { diff --git a/internal/deployer/addons_stackrox_helm_chart.go b/internal/deployer/addons_stackrox_helm_chart.go index b00b9f84..bcd790eb 100644 --- a/internal/deployer/addons_stackrox_helm_chart.go +++ b/internal/deployer/addons_stackrox_helm_chart.go @@ -7,6 +7,7 @@ import ( "github.com/stackrox/roxie/internal/env" "github.com/stackrox/roxie/internal/helm" + log "github.com/stackrox/roxie/internal/logger" ) // New creates a helmAddOn that installs a chart from a local stackrox repository checkout. @@ -15,8 +16,8 @@ func (h *StackRoxRepoHelmChartAddOn) New( commonProperties CommonAddOnProperties, name, namespace string, ) (AddOn, error) { - if !env.IsInStackroxRepository(addOnCfg.log) { - addOnCfg.log.Errorf("the Helm chart add-on %q uses stackroxRepoHelmChart but roxie is not running from a stackrox checkout", name) + if !env.IsInStackroxRepository() { + log.Errorf("the Helm chart add-on %q uses stackroxRepoHelmChart but roxie is not running from a stackrox checkout", name) return nil, errors.New("not invoked in StackRox repository") } topLevelDir, err := env.GetStackRoxTopLevelDir() @@ -25,14 +26,14 @@ func (h *StackRoxRepoHelmChartAddOn) New( } chartPath := filepath.Join(topLevelDir, h.Path) - if err := helm.BuildDependencies(addOnCfg.log, chartPath); err != nil { + if err := helm.BuildDependencies(chartPath); err != nil { return nil, fmt.Errorf("building dependencies for Helm chart add-on %q: %w", name, err) } opts := helm.InstallOptions{ ChartPath: chartPath, } - addOnCfg.log.Infof("Add-on %q: using Helm chart add-on from stackrox repo at %s", name, opts.ChartPath) + log.Infof("Add-on %q: using Helm chart add-on from stackrox repo at %s", name, opts.ChartPath) // Shallow copy, because we modify the ValuesFile and don't want to mutate the actual receiver: hCopy := *h diff --git a/internal/deployer/addons_test.go b/internal/deployer/addons_test.go index 988c8d25..7a23229e 100644 --- a/internal/deployer/addons_test.go +++ b/internal/deployer/addons_test.go @@ -4,7 +4,6 @@ import ( "strings" "testing" - "github.com/stackrox/roxie/internal/logger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -238,9 +237,7 @@ func TestResolveAddOns(t *testing.T) { }, } - addOnCfg := AddOnConfig{ - log: logger.New(), - } + addOnCfg := AddOnConfig{} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { resolved, err := resolveEnabledAddOns(tt.cfg, addOnCfg) diff --git a/internal/deployer/crs.go b/internal/deployer/crs.go index 529ebb87..2fb006b9 100644 --- a/internal/deployer/crs.go +++ b/internal/deployer/crs.go @@ -17,7 +17,7 @@ import ( "github.com/stackrox/roxie/internal/helpers" "github.com/stackrox/roxie/internal/k8s" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) var ( @@ -61,7 +61,7 @@ func (d *Deployer) generateCRS(ctx context.Context, clusterName string) (string, for attempt := 1; attempt <= maxAttempts; attempt++ { if attempt > 1 { waitTime := time.Duration(attempt*baseRetryDelay) * time.Second - d.logger.Infof("Retrying CRS generation (attempt %d/%d) after %v...", attempt, maxAttempts, waitTime) + log.Infof("Retrying CRS generation (attempt %d/%d) after %v...", attempt, maxAttempts, waitTime) select { case <-ctx.Done(): return "", ctx.Err() @@ -76,14 +76,14 @@ func (d *Deployer) generateCRS(ctx context.Context, clusterName string) (string, crsContent, err := d.generateCRSOnce(ctx, client, crsName) if err != nil { if d.isRetryableError(err) { - d.logger.Warningf("Transient error generating CRS: %v", err) + log.Warningf("Transient error generating CRS: %v", err) lastErr = err continue } return "", fmt.Errorf("CRS generation failed with non-retryable error: %w", err) } - d.logger.Success("✓ CRS generated") + log.Success("✓ CRS generated") return crsContent, nil } @@ -111,12 +111,12 @@ func (d *Deployer) centralHTTPClient() (*http.Client, error) { } pool.AddCert(cert) caCertsAdded++ - d.logger.Dimf("CA cert #%d: Subject.CN=%q, Issuer.CN=%q, SubjectKeyId=%x", caCertsAdded, cert.Subject.CommonName, cert.Issuer.CommonName, cert.SubjectKeyId) + log.Dimf("CA cert #%d: Subject.CN=%q, Issuer.CN=%q, SubjectKeyId=%x", caCertsAdded, cert.Subject.CommonName, cert.Issuer.CommonName, cert.SubjectKeyId) } - d.logger.Infof("Loaded %d CA certificate(s) from %q", caCertsAdded, d.roxCACertFile) + log.Infof("Loaded %d CA certificate(s) from %q", caCertsAdded, d.roxCACertFile) tlsConfig.RootCAs = pool tlsConfig.InsecureSkipVerify = true - tlsConfig.VerifyPeerCertificate = centralVerifyFunc(d.logger, tlsConfig) + tlsConfig.VerifyPeerCertificate = centralVerifyFunc(tlsConfig) } return &http.Client{ @@ -129,7 +129,7 @@ func (d *Deployer) centralHTTPClient() (*http.Client, error) { // generateCRSOnce generates a Cluster Registration Secret via Central's REST API. func (d *Deployer) generateCRSOnce(ctx context.Context, client *http.Client, crsName string) (string, error) { - d.logger.Infof("Generating CRS named %q via Central API...", crsName) + log.Infof("Generating CRS named %q via Central API...", crsName) reqBody, err := json.Marshal(crsGenRequest{Name: crsName}) if err != nil { @@ -199,7 +199,7 @@ func (d *Deployer) isRetryableError(err error) bool { // Forging a certificate trusted by this pool requires the CA private key, which // is stored in the central-tls secret on the cluster. Reading that secret requires // kubectl access — at which point the attacker can read the admin password directly. -func centralVerifyFunc(log *logger.Logger, conf *tls.Config) func([][]byte, [][]*x509.Certificate) error { +func centralVerifyFunc(conf *tls.Config) func([][]byte, [][]*x509.Certificate) error { return func(rawCerts [][]byte, _ [][]*x509.Certificate) error { if len(rawCerts) == 0 { return errors.New("remote peer presented no certificates") @@ -235,18 +235,18 @@ func centralVerifyFunc(log *logger.Logger, conf *tls.Config) func([][]byte, [][] // applyCRS applies the CRS content to the sensor namespace func (d *Deployer) applyCRS(ctx context.Context, crsContent string) error { - d.logger.Info("Applying CRS to sensor namespace") + log.Info("Applying CRS to sensor namespace") result, err := d.runKubectl(ctx, k8s.KubectlOptions{ Args: []string{"apply", "-n", d.config.SecuredCluster.Namespace, "-f", "-"}, Stdin: strings.NewReader(crsContent), }) if err != nil { - d.logger.Errorf("kubectl stdout: %s", result.Stdout) - d.logger.Errorf("kubectl stderr: %s", result.Stderr) + log.Errorf("kubectl stdout: %s", result.Stdout) + log.Errorf("kubectl stderr: %s", result.Stderr) return fmt.Errorf("failed to apply CRS: %w\nStderr: %s", err, result.Stderr) } - d.logger.Success("✓ CRS applied") + log.Success("✓ CRS applied") return nil } diff --git a/internal/deployer/deploy_via_operator.go b/internal/deployer/deploy_via_operator.go index 33b7af76..82eaf793 100644 --- a/internal/deployer/deploy_via_operator.go +++ b/internal/deployer/deploy_via_operator.go @@ -14,8 +14,8 @@ import ( "github.com/stackrox/roxie/internal/component" "github.com/stackrox/roxie/internal/env" - "github.com/stackrox/roxie/internal/helpers" "github.com/stackrox/roxie/internal/k8s" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/types" "gopkg.in/yaml.v3" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -34,14 +34,14 @@ var ( // deployOperatorOnly deploys only the operator without any Central or SecuredCluster resources func (d *Deployer) deployOperatorOnly(ctx context.Context) error { - d.logger.Info("🚀 Deploying Operator only...") + log.Info("🚀 Deploying Operator only...") if err := d.ensureOperatorDeployed(ctx); err != nil { return err } - d.logger.Success("✓ Operator deployed successfully") - d.logger.Info("You can now deploy Central or SecuredCluster components separately") + log.Success("✓ Operator deployed successfully") + log.Info("You can now deploy Central or SecuredCluster components separately") return nil } @@ -49,8 +49,8 @@ func (d *Deployer) deployOperatorOnly(ctx context.Context) error { func (d *Deployer) ensureOperatorDeployed(ctx context.Context) error { // Skip operator deployment/checks if flag is set to false if d.config.Operator.SkipDeploymentEnabled() { - d.logger.Info("ℹ️ Skipping operator deployment checks (--deploy-operator=false)") - d.logger.Info(" Assuming operator is already running...") + log.Info("ℹ️ Skipping operator deployment checks (--deploy-operator=false)") + log.Info(" Assuming operator is already running...") return nil } @@ -69,7 +69,7 @@ func (d *Deployer) ensureOperatorDeployed(ctx context.Context) error { return fmt.Errorf("detecting operator deployment mode: %w", err) } if operatorExists && currentMode == OperatorModeOLM { - d.logger.Info("🔄 Switching operator from OLM to non-OLM mode...") + log.Info("🔄 Switching operator from OLM to non-OLM mode...") if err := d.teardownOperatorOLM(ctx); err != nil { return fmt.Errorf("failed to teardown OLM operator: %w", err) } @@ -107,7 +107,7 @@ func (d *Deployer) teardownStaleOperatorNamespaces(ctx context.Context, desired if !d.operatorDeploymentExists(ctx, ns) && !d.namespaceExists(ns) { continue } - d.logger.Infof("🔄 Removing previous operator from namespace %s (no longer needed)...", ns) + log.Infof("🔄 Removing previous operator from namespace %s (no longer needed)...", ns) instance := OperatorInstanceConfig{Namespace: ns} switch ns { case operatorNamespaceCentral: @@ -130,10 +130,10 @@ func (d *Deployer) ensureOperatorInstanceNonOLM(ctx context.Context, instance Op if exists { if d.isOperatorVersionCorrect(ctx, instance) { - d.logger.Infof("✓ Operator already deployed with correct version in namespace %s", instance.Namespace) + log.Infof("✓ Operator already deployed with correct version in namespace %s", instance.Namespace) return nil } - d.logger.Infof("🔄 Operator version mismatch in namespace %s, redeploying...", instance.Namespace) + log.Infof("🔄 Operator version mismatch in namespace %s, redeploying...", instance.Namespace) needsTeardown = true needsDeployment = true } @@ -171,7 +171,7 @@ func (d *Deployer) ensureOperatorDeployedOLM(ctx context.Context) error { if !operatorExists { needsDeployment = true } else if currentMode == OperatorModeNonOLM { - d.logger.Info("🔄 Switching operator from non-OLM to OLM mode...") + log.Info("🔄 Switching operator from non-OLM to OLM mode...") needsTeardown = true needsDeployment = true } else { @@ -181,9 +181,9 @@ func (d *Deployer) ensureOperatorDeployedOLM(ctx context.Context) error { EnvVars: d.config.Operator.EnvVars, } if d.isOperatorVersionCorrect(ctx, instance) { - d.logger.Info("✓ Operator already deployed with correct version") + log.Info("✓ Operator already deployed with correct version") } else { - d.logger.Info("🔄 Operator version mismatch, redeploying...") + log.Info("🔄 Operator version mismatch, redeploying...") needsTeardown = true needsDeployment = true } @@ -212,7 +212,7 @@ func (d *Deployer) ensureOperatorDeployedOLM(ctx context.Context) error { // deployCentralOperator deploys Central using the operator func (d *Deployer) deployCentralOperator(ctx context.Context) error { - d.logger.Info("🚀 Deploying Central via Operator...") + log.Info("🚀 Deploying Central via Operator...") needPullSecrets := d.config.Roxie.ClusterType.NeedsPullSecrets() if err := d.prepareNamespace(ctx, d.config.Central.Namespace, needPullSecrets); err != nil { @@ -237,7 +237,7 @@ func (d *Deployer) deployCentralOperator(ctx context.Context) error { } if d.config.Central.PauseReconciliationEnabled() { - d.logger.Infof("Adding pause-reconcile annotation to Central") + log.Infof("Adding pause-reconcile annotation to Central") err := d.addPauseReconcileAnnotation(ctx, "Central", centralCrName, d.config.Central.Namespace) if err != nil { return err @@ -251,23 +251,23 @@ func (d *Deployer) deployCentralOperator(ctx context.Context) error { func (d *Deployer) isOperatorVersionCorrect(ctx context.Context, instance OperatorInstanceConfig) bool { currentImage, err := d.getDeployedOperatorImage(ctx, instance.Namespace) if err != nil { - d.logger.Warningf("Could not retrieve operator image: %v", err) + log.Warningf("Could not retrieve operator image: %v", err) return false } // Extract the tag from the current image parts := strings.SplitN(currentImage, ":", 2) if len(parts) < 2 { - d.logger.Warningf("Could not parse operator image tag from: %s", currentImage) + log.Warningf("Could not parse operator image tag from: %s", currentImage) return false } currentTag := parts[1] desiredTag := instance.Version.ToOperatorTag().String() if currentTag != desiredTag { - d.logger.Info("Operator version mismatch detected:") - d.logger.Infof(" Current: %s", currentTag) - d.logger.Infof(" Desired: %s", desiredTag) + log.Info("Operator version mismatch detected:") + log.Infof(" Current: %s", currentTag) + log.Infof(" Desired: %s", desiredTag) return false } return true @@ -289,7 +289,7 @@ func (d *Deployer) getDeployedOperatorImage(ctx context.Context, namespace strin // prepareNamespace creates pull secrets in the namespace if needed func (d *Deployer) prepareNamespace(ctx context.Context, namespace string, needPullSecrets bool) error { - d.logger.Infof("Preparing namespace %s", namespace) + log.Infof("Preparing namespace %s", namespace) if err := d.ensureNamespaceExists(namespace); err != nil { return err @@ -315,7 +315,7 @@ func (d *Deployer) ensurePullSecretExists(ctx context.Context, namespace string) Stdin: strings.NewReader(pullSecretYAML), }) if err != nil { - d.logger.Warningf("Could not apply pull secret: %v", err) + log.Warningf("Could not apply pull secret: %v", err) } return nil @@ -348,15 +348,15 @@ func (d *Deployer) createAdminPasswordSecret(ctx context.Context) error { if err != nil { stderr := strings.TrimSpace(result.Stderr) if len(stderr) > 0 { - d.logger.Errorf("kubectl apply produced error output:") + log.Errorf("kubectl apply produced error output:") for line := range strings.SplitSeq(stderr, "\n") { - d.logger.Dim("| " + line) + log.Dim("| " + line) } } return err } - d.logger.Success("✓ Admin password secret created") + log.Success("✓ Admin password secret created") return nil } @@ -469,14 +469,14 @@ func getCentralResourcesOperator(resourcesProfile types.ResourceProfile) map[str // applyCentralCR applies the Central CR to the cluster func (d *Deployer) applyCentralCR(ctx context.Context, cr map[string]interface{}) error { - d.logger.Info("Applying Central custom resource") + log.Info("Applying Central custom resource") - if d.verbose { + if log.IsVerbose() { if env.RunningInteractively { - d.logger.Dim("Central CR YAML:") - helpers.LogMultilineYaml(d.logger, cr) + log.Debug("Central CR YAML:") + log.LogMultilineYaml(cr) } else { - d.logger.Dim("Skipping emitting Central CR in non-interactive mode, because it could leak confidential information") + log.Debug("Skipping emitting Central CR in non-interactive mode, because it could leak confidential information") } } @@ -490,12 +490,12 @@ func (d *Deployer) applyCentralCR(ctx context.Context, cr map[string]interface{} Stdin: bytes.NewReader(yamlData), }) if err != nil { - d.logger.Errorf("kubectl stdout: %s", result.Stdout) - d.logger.Errorf("kubectl stderr: %s", result.Stderr) + log.Errorf("kubectl stdout: %s", result.Stdout) + log.Errorf("kubectl stderr: %s", result.Stderr) return fmt.Errorf("failed to apply Central CR: %w\nStderr: %s", err, result.Stderr) } - d.logger.Success("✓ Central Custom Resource applied") + log.Success("✓ Central Custom Resource applied") return nil } @@ -521,20 +521,20 @@ func (d *Deployer) waitForAvailableCondition(ctx context.Context, resource, name if err != nil { stderr := strings.TrimSpace(result.Stderr) if len(stderr) > 0 { - d.logger.Errorf("kubectl wait produced error output:") + log.Errorf("kubectl wait produced error output:") for line := range strings.SplitSeq(stderr, "\n") { - d.logger.Dim("| " + line) + log.Dim("| " + line) } } return fmt.Errorf("error waiting for resource %s in namespace %s to become Available: %v", resource, namespace, err) } - d.logger.Successf("✓ Resource %s in namespace %s is ready", resource, namespace) + log.Successf("✓ Resource %s in namespace %s is ready", resource, namespace) return nil } // waitForResourceToExist polls until the given resource exists in the namespace. func (d *Deployer) waitForResourceToExist(ctx context.Context, resource, namespace string) error { - d.logger.Infof("Waiting for resource %s to exist in namespace %s...", resource, namespace) + log.Infof("Waiting for resource %s to exist in namespace %s...", resource, namespace) for { _, err := d.runKubectl(ctx, k8s.KubectlOptions{ Args: []string{"get", resource, "-n", namespace}, @@ -567,7 +567,7 @@ func (d *Deployer) waitForComponentReady(ctx context.Context, comp component.Com if err != nil { return err } - d.logger.Infof("⏳ Waiting for %s to become ready (timeout: %s)...", comp, waitCfg.Timeout) + log.Infof("⏳ Waiting for %s to become ready (timeout: %s)...", comp, waitCfg.Timeout) const padding = 5 * time.Second waitCtx, cancel := context.WithTimeout(ctx, waitCfg.Timeout+padding) @@ -582,7 +582,7 @@ func (d *Deployer) waitForComponentReady(ctx context.Context, comp component.Com waitChannel <- fmt.Errorf("error waiting for %s deployment to become Available: %v", comp, err) return } - d.logger.Infof("Resource %s is now ready.", waitCfg.WaitFor) + log.Infof("Resource %s is now ready.", waitCfg.WaitFor) waitChannel <- nil }() @@ -606,17 +606,17 @@ func (d *Deployer) waitForComponentReady(ctx context.Context, comp component.Com // Track seen deployments and their states to avoid duplicate messages. deploymentsProgressed, err := d.checkDeploymentProgressInNamespace(waitCtx, waitCfg.Namespace, seenDeployments) if err != nil { - d.logger.Warningf("failed to check for deployment progress in namespace %s: %v", waitCfg.Namespace, err) + log.Warningf("failed to check for deployment progress in namespace %s: %v", waitCfg.Namespace, err) } podsProgressed, err := d.checkPodProgressInNamespace(waitCtx, waitCfg.Namespace, seenPods) if err != nil { - d.logger.Warningf("failed to check for pod progress in namespace %s: %v", waitCfg.Namespace, err) + log.Warningf("failed to check for pod progress in namespace %s: %v", waitCfg.Namespace, err) } if deploymentsProgressed || podsProgressed { lastUpdate = time.Now() } else { if time.Since(lastUpdate) > progressUpdatePeriod { - d.logger.Dimf("Still waiting for component %s in namespace %s", comp, waitCfg.Namespace) + log.Dimf("Still waiting for component %s in namespace %s", comp, waitCfg.Namespace) lastUpdate = time.Now() } } @@ -627,7 +627,7 @@ func (d *Deployer) waitForComponentReady(ctx context.Context, comp component.Com // waitForLoadBalancer waits for a LoadBalancer service to get an external IP. // Returns the endpoint as "host:port", with no https:// prefix. func (d *Deployer) waitForLoadBalancer(ctx context.Context, namespace, serviceName string, timeout int) (string, error) { - d.logger.Infof("⏳ Waiting for LoadBalancer %s to get external IP...", serviceName) + log.Infof("⏳ Waiting for LoadBalancer %s to get external IP...", serviceName) start := time.Now() for time.Since(start) < time.Duration(timeout)*time.Second { @@ -638,9 +638,9 @@ func (d *Deployer) waitForLoadBalancer(ctx context.Context, namespace, serviceNa ip := strings.TrimSpace(result.Stdout) if ip != "" && ip != "" { if env.RunningInteractively { - d.logger.Successf("✓ LoadBalancer IP: %s", ip) + log.Successf("✓ LoadBalancer IP: %s", ip) } else { - d.logger.Success("✓ LoadBalancer IP") + log.Success("✓ LoadBalancer IP") } return fmt.Sprintf("%s:443", ip), nil } @@ -654,9 +654,9 @@ func (d *Deployer) waitForLoadBalancer(ctx context.Context, namespace, serviceNa hostname := strings.TrimSpace(result.Stdout) if hostname != "" && hostname != "" { if env.RunningInteractively { - d.logger.Successf("✓ LoadBalancer hostname: %s", hostname) + log.Successf("✓ LoadBalancer hostname: %s", hostname) } else { - d.logger.Success("✓ LoadBalancer hostname") + log.Success("✓ LoadBalancer hostname") } return fmt.Sprintf("%s:443", hostname), nil } @@ -676,7 +676,7 @@ func (d *Deployer) fetchCentralCACerts(ctx context.Context) error { var caPEMs [][]byte // Fetch the internal StackRox CA from the central-tls secret. - d.logger.Info("Fetching internal CA certificate from central-tls secret...") + log.Info("Fetching internal CA certificate from central-tls secret...") internalCA, err := d.fetchSecretField(ctx, "central-tls", "ca\\.pem") if err != nil { return fmt.Errorf("failed to get CA cert from central-tls secret: %w", err) @@ -687,10 +687,10 @@ func (d *Deployer) fetchCentralCACerts(ctx context.Context) error { // trust pool. This includes the leaf — Central itself does the same // when building the trust bundle for Sensor. if secretName := d.defaultTLSSecretName(); secretName != "" { - d.logger.Infof("Fetching custom TLS certificates from secret %s...", secretName) + log.Infof("Fetching custom TLS certificates from secret %s...", secretName) customCerts, err := d.fetchCustomTLSCerts(ctx, secretName) if err != nil { - d.logger.Warningf("Could not fetch custom TLS certs from secret %s: %v", secretName, err) + log.Warningf("Could not fetch custom TLS certs from secret %s: %v", secretName, err) // Try to continue. } else { caPEMs = append(caPEMs, customCerts...) @@ -717,7 +717,7 @@ func (d *Deployer) fetchCentralCACerts(ctx context.Context) error { } d.roxCACertFile = fileName - d.logger.Successf("✓ CA certificates saved to: %s", d.roxCACertFile) + log.Successf("✓ CA certificates saved to: %s", d.roxCACertFile) return nil } @@ -766,7 +766,7 @@ func (d *Deployer) fetchCustomTLSCerts(ctx context.Context, secretName string) ( if err != nil { return nil, fmt.Errorf("parsing certificate from secret %s: %w", secretName, err) } - d.logger.Infof("Found certificate in %s: Subject.CN=%q, IsCA=%v", + log.Infof("Found certificate in %s: Subject.CN=%q, IsCA=%v", secretName, cert.Subject.CommonName, cert.IsCA) pems = append(pems, pem.EncodeToMemory(block)) } @@ -782,7 +782,7 @@ func (d *Deployer) configureCentralEndpoint(ctx context.Context) error { if exposure == types.ExposureLoadBalancer { _, err := d.waitForLoadBalancer(ctx, d.config.Central.Namespace, "central-loadbalancer", 300) if err != nil { - d.logger.Warningf("LoadBalancer not ready: %v", err) + log.Warningf("LoadBalancer not ready: %v", err) } else { serviceName = "central-loadbalancer" } @@ -813,12 +813,12 @@ func (d *Deployer) configureCentralEndpoint(ctx context.Context) error { } if err := d.fetchCentralCACerts(ctx); err != nil { - d.logger.Warningf("Could not fetch Central CA certs: %v", err) + log.Warningf("Could not fetch Central CA certs: %v", err) } if env.RunningInteractively { - d.logger.Successf("✓ Central is ready at: %s", d.centralEndpoint) - d.logger.Successf("✓ Admin password: %s", d.centralPassword) + log.Successf("✓ Central is ready at: %s", d.centralEndpoint) + log.Successf("✓ Admin password: %s", d.centralPassword) } return nil @@ -826,7 +826,7 @@ func (d *Deployer) configureCentralEndpoint(ctx context.Context) error { // deploySecuredClusterOperator deploys SecuredCluster using the operator. func (d *Deployer) deploySecuredClusterOperator(ctx context.Context) error { - d.logger.Info("🚀 Deploying SecuredCluster via Operator...") + log.Info("🚀 Deploying SecuredCluster via Operator...") needPullSecrets := d.config.Roxie.ClusterType.NeedsPullSecrets() if err := d.prepareNamespace(ctx, d.config.SecuredCluster.Namespace, needPullSecrets); err != nil { @@ -845,7 +845,7 @@ func (d *Deployer) deploySecuredClusterOperator(ctx context.Context) error { if !found || clusterName == "" { return fmt.Errorf("cluster name not found in SecuredCluster CR") } - d.logger.Infof("Using cluster name: %s", clusterName) + log.Infof("Using cluster name: %s", clusterName) crsContent, err := d.generateCRS(ctx, clusterName) if err != nil { @@ -865,14 +865,14 @@ func (d *Deployer) deploySecuredClusterOperator(ctx context.Context) error { } if d.config.SecuredCluster.PauseReconciliationEnabled() { - d.logger.Infof("Adding pause-reconcile annotation to SecuredCluster") + log.Infof("Adding pause-reconcile annotation to SecuredCluster") err := d.addPauseReconcileAnnotation(ctx, "SecuredCluster", securedClusterCrName, d.config.SecuredCluster.Namespace) if err != nil { return err } } - d.logger.Successf("✓ SecuredCluster '%s' is ready", clusterName) + log.Successf("✓ SecuredCluster '%s' is ready", clusterName) return nil } @@ -933,19 +933,19 @@ func getSecuredClusterResourcesOperator(resourceProfile types.ResourceProfile) m // applySecuredClusterCR applies the SecuredCluster CR to the cluster func (d *Deployer) applySecuredClusterCR(ctx context.Context, cr map[string]interface{}) error { - d.logger.Info("Applying SecuredCluster custom resource") + log.Info("Applying SecuredCluster custom resource") yamlData, err := yaml.Marshal(cr) if err != nil { return fmt.Errorf("failed to marshal SecuredCluster CR: %w", err) } - if d.verbose { + if log.IsVerbose() { if env.RunningInteractively { - d.logger.Dim("SecuredCluster CR YAML:") - d.logger.Dim(string(yamlData)) + log.Debug("SecuredCluster CR YAML:") + log.Debug(string(yamlData)) } else { - d.logger.Dim("Skipping emitting SecuredCluster CR in non-interactive mode, because it could leak confidential information") + log.Debug("Skipping emitting SecuredCluster CR in non-interactive mode, because it could leak confidential information") } } @@ -954,10 +954,10 @@ func (d *Deployer) applySecuredClusterCR(ctx context.Context, cr map[string]inte Stdin: bytes.NewReader(yamlData), }) if err != nil { - d.logger.Errorf("kubectl error: %s", result.Stderr) + log.Errorf("kubectl error: %s", result.Stderr) return fmt.Errorf("failed to apply SecuredCluster CR: %w", err) } - d.logger.Success("✓ SecuredCluster CR applied") + log.Success("✓ SecuredCluster CR applied") return nil } diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index f399a3cb..68af1bd0 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -21,7 +21,7 @@ import ( "github.com/stackrox/roxie/internal/env" "github.com/stackrox/roxie/internal/imagecache" "github.com/stackrox/roxie/internal/k8s" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/portforward" "github.com/stackrox/roxie/internal/roxieenv" "github.com/stackrox/roxie/internal/types" @@ -40,8 +40,6 @@ var ( // Deployer is the base deployer for ACS type Deployer struct { // Influencing roxies mode of operation. - verbose bool - logger *logger.Logger startTime time.Time dockerAuth *dockerauth.DockerAuth imageCache *imagecache.ImageCache @@ -90,10 +88,10 @@ func (d *Deployer) deleteResources(ctx context.Context, namespace string, resour // Expects that reconciliation for the RHACS operator is paused. func (d *Deployer) deleteCentralResources(ctx context.Context) error { - d.logger.Info("Deleting Central resources") + log.Info("Deleting Central resources") crExists := true - if _, err := k8s.RetrieveResourceFromCluster(ctx, d.logger, d.config.Central.Namespace, "central", "stackrox-central-services"); err != nil { + if _, err := k8s.RetrieveResourceFromCluster(ctx, d.config.Central.Namespace, "central", "stackrox-central-services"); err != nil { if !k8s.IsResourceNotFound(err) { return fmt.Errorf("retrieving Central CR: %w", err) } @@ -101,23 +99,19 @@ func (d *Deployer) deleteCentralResources(ctx context.Context) error { } if crExists { - d.logger.Info("Removing any pause-reconcile annotation from Central") + log.Info("Removing any pause-reconcile annotation from Central") if err := d.removePauseReconcileAnnotation(ctx, "central", "stackrox-central-services", d.config.Central.Namespace); err != nil { return err } - if d.verbose { - d.logger.Dim("Removed any pause-reconcile annotation from Central") - } + log.Debug("Removed any pause-reconcile annotation from Central") err := d.deleteResource(ctx, d.config.Central.Namespace, "central", "stackrox-central-services", "--wait") if err != nil { return err } - if d.verbose { - d.logger.Dim("Deleted Central CR") - } + log.Debug("Deleted Central CR") } else { - d.logger.Info("Deletion of Central resources requested, but Central CR is not present anymore") + log.Info("Deletion of Central resources requested, but Central CR is not present anymore") } for _, resource := range []ResourceToDelete{ @@ -127,19 +121,19 @@ func (d *Deployer) deleteCentralResources(ctx context.Context) error { {Name: "scanner-db-password", Kind: "secret", OwnerName: centralCrName}, {Name: "stackrox-central-helm", Kind: "configmap"}, } { - d.logger.Dimf("Attempting to delete %s/%s", resource.Kind, resource.Name) + log.Dimf("Attempting to delete %s/%s", resource.Kind, resource.Name) if resource.OwnerName != "" { // Avoid deletion if the resource does not have the expected owner. // (e.g. in case central and secured cluster are deployed into the same namespace). - obj, err := k8s.RetrieveResourceFromCluster(ctx, d.logger, d.config.Central.Namespace, resource.Kind, resource.Name) + obj, err := k8s.RetrieveResourceFromCluster(ctx, d.config.Central.Namespace, resource.Kind, resource.Name) if err != nil { if !k8s.IsResourceNotFound(err) { - d.logger.Warningf("Failed to retrieve %s/%s for owner checking: %v. Skipping deletion. Deployment might be affected.", resource.Kind, resource.Name, err) + log.Warningf("Failed to retrieve %s/%s for owner checking: %v. Skipping deletion. Deployment might be affected.", resource.Kind, resource.Name, err) } continue } if k8s.ResourceNotOwnedByName(obj, resource.OwnerName) { - d.logger.Dimf("Skipping deletion of %s/%s: not owned by %s", resource.Kind, resource.Name, resource.OwnerName) + log.Dimf("Skipping deletion of %s/%s: not owned by %s", resource.Kind, resource.Name, resource.OwnerName) continue } } @@ -153,10 +147,10 @@ func (d *Deployer) deleteCentralResources(ctx context.Context) error { } func (d *Deployer) deleteSecuredClusterResources(ctx context.Context) error { - d.logger.Info("Deleting SecuredCluster resources") + log.Info("Deleting SecuredCluster resources") crExists := true - if _, err := k8s.RetrieveResourceFromCluster(ctx, d.logger, d.config.SecuredCluster.Namespace, "securedcluster", "stackrox-secured-cluster-services"); err != nil { + if _, err := k8s.RetrieveResourceFromCluster(ctx, d.config.SecuredCluster.Namespace, "securedcluster", "stackrox-secured-cluster-services"); err != nil { if !k8s.IsResourceNotFound(err) { return fmt.Errorf("retrieving SecuredCluster CR: %w", err) } @@ -164,23 +158,19 @@ func (d *Deployer) deleteSecuredClusterResources(ctx context.Context) error { } if crExists { - d.logger.Info("Removing any pause-reconcile annotation from SecuredCluster") + log.Info("Removing any pause-reconcile annotation from SecuredCluster") if err := d.removePauseReconcileAnnotation(ctx, "securedcluster", "stackrox-secured-cluster-services", d.config.SecuredCluster.Namespace); err != nil { return err } - if d.verbose { - d.logger.Dim("Removed any pause-reconcile annotation from SecuredCluster") - } + log.Debug("Removed any pause-reconcile annotation from SecuredCluster") err := d.deleteResource(ctx, d.config.SecuredCluster.Namespace, "securedcluster", "stackrox-secured-cluster-services", "--wait") if err != nil { return err } - if d.verbose { - d.logger.Dim("Deleted SecuredCluster CR") - } + log.Debug("Deleted SecuredCluster CR") } else { - d.logger.Info("Deletion of SecuredCluster resources requested, but SecuredCluster CR is not present anymore") + log.Info("Deletion of SecuredCluster resources requested, but SecuredCluster CR is not present anymore") } // Delete resources, which are treated special. @@ -190,19 +180,19 @@ func (d *Deployer) deleteSecuredClusterResources(ctx context.Context) error { // when both are deployed into the same namespace. {Name: "scanner-db-password", Kind: "secret", OwnerName: securedClusterCrName}, } { - d.logger.Dimf("Attempting to delete %s/%s", resource.Kind, resource.Name) + log.Dimf("Attempting to delete %s/%s", resource.Kind, resource.Name) if resource.OwnerName != "" { // Avoid deletion if the resource does not have the expected owner. // (e.g. in case central and secured cluster are deployed into the same namespace). - obj, err := k8s.RetrieveResourceFromCluster(ctx, d.logger, d.config.SecuredCluster.Namespace, resource.Kind, resource.Name) + obj, err := k8s.RetrieveResourceFromCluster(ctx, d.config.SecuredCluster.Namespace, resource.Kind, resource.Name) if err != nil { if !k8s.IsResourceNotFound(err) { - d.logger.Warningf("Failed to retrieve %s/%s for owner checking: %v. Skipping deletion. Deployment might be affected.", resource.Kind, resource.Name, err) + log.Warningf("Failed to retrieve %s/%s for owner checking: %v. Skipping deletion. Deployment might be affected.", resource.Kind, resource.Name, err) } continue } if k8s.ResourceNotOwnedByName(obj, resource.OwnerName) { - d.logger.Dimf("Skipping deletion of %s/%s: not owned by %s", resource.Kind, resource.Name, resource.OwnerName) + log.Dimf("Skipping deletion of %s/%s: not owned by %s", resource.Kind, resource.Name, resource.OwnerName) continue } } @@ -222,12 +212,12 @@ func (d *Deployer) SetConfig(config Config) { // It verifies that the current environment contains necessary tools. // It creates a temporary directory for the deployer to use during deployment, // and it is the caller's responsibility to clean it up using the Cleanup() method when not used anymore. -func New(log *logger.Logger) (*Deployer, error) { +func New() (*Deployer, error) { if err := checkRequiredTools(); err != nil { return nil, err } - imageCache, err := imagecache.New(log, "", 20) + imageCache, err := imagecache.New("", 20) if err != nil { return nil, err } @@ -238,14 +228,13 @@ func New(log *logger.Logger) (*Deployer, error) { } d := &Deployer{ - logger: log, startTime: time.Now(), tempDir: tempDir, imageCache: imageCache, - dockerAuth: dockerauth.New(log), - portForward: portforward.New(k8s.GetKubectl(), log), + dockerAuth: dockerauth.New(), + portForward: portforward.New(k8s.GetKubectl()), kubeContext: env.GetCurrentContext(), - containerRuntimeSocket: containerrt.ResolveSocket(log), + containerRuntimeSocket: containerrt.ResolveSocket(), } if password := os.Getenv("ROX_ADMIN_PASSWORD"); password != "" { @@ -279,7 +268,7 @@ func (d *Deployer) Cleanup() { // In the case of envrc file usage, we need to keep temporary files around after deployment. // (It contains CA certificates, for example.) if err := os.RemoveAll(d.tempDir); err != nil { - d.logger.Warningf("Deployer Cleanup failed to remove %q: %v", d.tempDir, err) + log.Warningf("Deployer Cleanup failed to remove %q: %v", d.tempDir, err) } } } @@ -293,10 +282,10 @@ func (d *Deployer) stopDetachedPortForward() { return } if err := proc.Signal(syscall.SIGKILL); err != nil { - d.logger.Dimf("Detached port-forward (pid %d) already gone", d.portForwardPID) + log.Dimf("Detached port-forward (pid %d) already gone", d.portForwardPID) return } - d.logger.Dimf("Stopped detached port-forward (pid %d)", d.portForwardPID) + log.Dimf("Stopped detached port-forward (pid %d)", d.portForwardPID) d.portForwardPID = 0 } @@ -320,7 +309,7 @@ func (d *Deployer) Deploy(ctx context.Context, components component.Component) e } } - d.logger.Infof("Initiating deployment of %s", components) + log.Infof("Initiating deployment of %s", components) // If only deploying operator, use the operator-only flow. if components.IncludesOperatorExplicitly() { @@ -355,7 +344,7 @@ func (d *Deployer) Deploy(ctx context.Context, components component.Component) e // prepareCredentials prepares and verifies Docker credentials early to allow failing fast. // The verified credentials are stored in the Deployer object for later use. func (d *Deployer) prepareCredentials() error { - d.logger.Dimf("Preparing and verifying Docker credentials...") + log.Dimf("Preparing and verifying Docker credentials...") // This will retrieve and verify credentials, returning error if invalid creds, err := d.dockerAuth.GetAndVerifyCredentials() @@ -365,16 +354,16 @@ func (d *Deployer) prepareCredentials() error { d.dockerCreds = creds - d.logger.Dimf("Docker credentials verified successfully") + log.Dimf("Docker credentials verified successfully") return nil } func (d *Deployer) deployCentral(ctx context.Context) error { - d.logger.Infof("Deploying Central to namespace %s", d.config.Central.Namespace) + log.Infof("Deploying Central to namespace %s", d.config.Central.Namespace) if d.namespaceExists(d.config.Central.Namespace) { - d.logger.Info("Existing Central deployment found, tearing down...") + log.Info("Existing Central deployment found, tearing down...") if err := d.teardownCentral(ctx); err != nil { - d.logger.Warningf("Error during teardown: %v", err) + log.Warningf("Error during teardown: %v", err) } } @@ -383,9 +372,9 @@ func (d *Deployer) deployCentral(ctx context.Context) error { } if d.envrcFile != "" { - d.logger.Dimf("Writing environment variables to %s", d.envrcFile) + log.Dimf("Writing environment variables to %s", d.envrcFile) if err := d.writeEnvrcFile(ctx); err != nil { - d.logger.Warningf("Failed to write envrc file: %v", err) + log.Warningf("Failed to write envrc file: %v", err) } } @@ -393,11 +382,11 @@ func (d *Deployer) deployCentral(ctx context.Context) error { } func (d *Deployer) deploySecuredCluster(ctx context.Context) error { - d.logger.Infof("Deploying SecuredCluster to namespace %s", d.config.SecuredCluster.Namespace) + log.Infof("Deploying SecuredCluster to namespace %s", d.config.SecuredCluster.Namespace) if d.namespaceExists(d.config.SecuredCluster.Namespace) { - d.logger.Info("Existing SecuredCluster deployment found, tearing down...") + log.Info("Existing SecuredCluster deployment found, tearing down...") if err := d.teardownSecuredCluster(ctx); err != nil { - d.logger.Warningf("Error during teardown: %v", err) + log.Warningf("Error during teardown: %v", err) } } @@ -405,7 +394,7 @@ func (d *Deployer) deploySecuredCluster(ctx context.Context) error { } func (d *Deployer) Teardown(ctx context.Context, components component.Component) error { - d.logger.Infof("Starting teardown of %s", components) + log.Infof("Starting teardown of %s", components) if components.IncludesAddOns() { enabledAddOns, err := d.ResolveEnabledAddOns() @@ -431,14 +420,14 @@ func (d *Deployer) Teardown(ctx context.Context, components component.Component) go func() { defer wg.Done() if err := d.teardownSecuredCluster(ctx); err != nil { - d.logger.Warningf("Error tearing down secured cluster: %v", err) + log.Warningf("Error tearing down secured cluster: %v", err) } }() go func() { defer wg.Done() if err := d.teardownCentral(ctx); err != nil { - d.logger.Warningf("Error tearing down central: %v", err) + log.Warningf("Error tearing down central: %v", err) } }() @@ -448,7 +437,7 @@ func (d *Deployer) Teardown(ctx context.Context, components component.Component) // because the operator manages finalizers on their custom resources. if components == component.All { if err := d.teardownOperator(ctx); err != nil { - d.logger.Warningf("Error tearing down operator: %v", err) + log.Warningf("Error tearing down operator: %v", err) } } return nil @@ -460,10 +449,10 @@ func (d *Deployer) Teardown(ctx context.Context, components component.Component) } func (d *Deployer) teardownCentral(ctx context.Context) error { - d.logger.Infof("🗑️ Tearing down central in namespace %s", d.config.Central.Namespace) + log.Infof("🗑️ Tearing down central in namespace %s", d.config.Central.Namespace) if !d.namespaceExists(d.config.Central.Namespace) { - d.logger.Infof("Namespace %s doesn't exist, skipping", d.config.Central.Namespace) + log.Infof("Namespace %s doesn't exist, skipping", d.config.Central.Namespace) return nil } @@ -473,41 +462,41 @@ func (d *Deployer) teardownCentral(ctx context.Context) error { // Add pause-reconcile annotation to not have the operator interfere during resource deletion. if d.doesResourceExist(ctx, "central", "stackrox-central-services", d.config.Central.Namespace) { if err := d.addPauseReconcileAnnotation(ctx, "central", "stackrox-central-services", d.config.Central.Namespace); err != nil { - d.logger.Warningf("Error adding pause-reconcile annotation: %v", err) + log.Warningf("Error adding pause-reconcile annotation: %v", err) } } - d.logger.Info("⏳ Waiting for Central resources to be fully deleted...") + log.Info("⏳ Waiting for Central resources to be fully deleted...") if err := d.deleteCentralResources(ctx); err != nil { return fmt.Errorf("failed to delete Central resources: %w", err) } - d.logger.Successf("✓ Central resources in namespace %s have been deleted", d.config.Central.Namespace) + log.Successf("✓ Central resources in namespace %s have been deleted", d.config.Central.Namespace) return nil } func (d *Deployer) teardownSecuredCluster(ctx context.Context) error { - d.logger.Infof("🗑️ Tearing down secured cluster in namespace %s", d.config.SecuredCluster.Namespace) + log.Infof("🗑️ Tearing down secured cluster in namespace %s", d.config.SecuredCluster.Namespace) if !d.namespaceExists(d.config.SecuredCluster.Namespace) { - d.logger.Infof("Namespace %s doesn't exist, skipping", d.config.SecuredCluster.Namespace) + log.Infof("Namespace %s doesn't exist, skipping", d.config.SecuredCluster.Namespace) return nil } if d.doesResourceExist(ctx, "securedcluster", "stackrox-secured-cluster-services", d.config.SecuredCluster.Namespace) { // Add pause-reconcile annotation to not have the operator interfere during resource deletion. if err := d.addPauseReconcileAnnotation(ctx, "securedcluster", "stackrox-secured-cluster-services", d.config.SecuredCluster.Namespace); err != nil { - d.logger.Warningf("Error adding pause-reconcile annotation: %v", err) + log.Warningf("Error adding pause-reconcile annotation: %v", err) } } - d.logger.Info("⏳ Waiting for SecuredCluster resources to be fully deleted...") + log.Info("⏳ Waiting for SecuredCluster resources to be fully deleted...") err := d.deleteSecuredClusterResources(ctx) if err != nil { return fmt.Errorf("failed to delete SecuredCluster resources: %w", err) } - d.logger.Successf("✓ SecuredCluster resources in namespace %s have been deleted", d.config.SecuredCluster.Namespace) + log.Successf("✓ SecuredCluster resources in namespace %s have been deleted", d.config.SecuredCluster.Namespace) return nil } @@ -516,7 +505,7 @@ func (d *Deployer) ensureNamespaceExists(namespace string) error { return nil } - d.logger.Infof("Creating namespace %s", namespace) + log.Infof("Creating namespace %s", namespace) _, err := d.runKubectl(context.Background(), k8s.KubectlOptions{ Args: []string{"create", "namespace", namespace}, }) @@ -530,7 +519,7 @@ func (d *Deployer) ensureNamespaceExists(namespace string) error { "app.kubernetes.io/managed-by=roxie", "--overwrite"}, }) if err != nil { - d.logger.Warningf("failed to label namespace %s: %v", namespace, err) + log.Warningf("failed to label namespace %s: %v", namespace, err) } return nil @@ -552,14 +541,14 @@ func (d *Deployer) waitForNamespaceDeletion(namespace string) error { for time.Now().Before(deadline) { if !d.namespaceExists(namespace) { - d.logger.Infof("Namespace %s has been deleted", namespace) + log.Infof("Namespace %s has been deleted", namespace) return nil } // Report progress periodically if time.Since(lastProgressReport) >= progressInterval { elapsed := time.Since(deadline.Add(-timeout)) - d.logger.Dim(fmt.Sprintf(" ⋯ Still waiting for namespace deletion... (%.0fs elapsed)", elapsed.Seconds())) + log.Dim(fmt.Sprintf(" ⋯ Still waiting for namespace deletion... (%.0fs elapsed)", elapsed.Seconds())) lastProgressReport = time.Now() } @@ -609,10 +598,6 @@ func (d *Deployer) SetEnvrcFile(path string) { d.envrcFile = path } -func (d *Deployer) SetVerbose(verbose bool) { - d.verbose = verbose -} - func (d *Deployer) doesResourceExist(ctx context.Context, resourceType, resourceName, namespace string) bool { _, err := d.runKubectl(ctx, k8s.KubectlOptions{ Args: []string{ @@ -659,14 +644,14 @@ func (d *Deployer) removePauseReconcileAnnotation(ctx context.Context, resourceT // Returns true if Central is ready, false if timeout occurs func (d *Deployer) WaitForCentral(timeout time.Duration) bool { if d.centralEndpoint == "" { - d.logger.Dim("No Central endpoint configured, skipping readiness check") + log.Dim("No Central endpoint configured, skipping readiness check") return false } if env.RunningInteractively { - d.logger.Infof("⏳ Waiting for Central to be ready at %s (timeout: %v)", d.centralEndpoint, timeout) + log.Infof("⏳ Waiting for Central to be ready at %s (timeout: %v)", d.centralEndpoint, timeout) } else { - d.logger.Infof("⏳ Waiting for Central to be ready (timeout: %v)", timeout) + log.Infof("⏳ Waiting for Central to be ready (timeout: %v)", timeout) } deadline := time.Now().Add(timeout) @@ -677,7 +662,7 @@ func (d *Deployer) WaitForCentral(timeout time.Duration) bool { for time.Now().Before(deadline) { // Try to connect to Central if d.isCentralReady() { - d.logger.Success("✓ Central is ready and responding!") + log.Success("✓ Central is ready and responding!") return true } @@ -685,7 +670,7 @@ func (d *Deployer) WaitForCentral(timeout time.Duration) bool { if time.Since(lastProgressReport) >= progressInterval { elapsed := time.Since(deadline.Add(-timeout)) remaining := timeout - elapsed - d.logger.Dim(fmt.Sprintf(" ⋯ Still waiting for Central... (%v elapsed, %v remaining)", + log.Dim(fmt.Sprintf(" ⋯ Still waiting for Central... (%v elapsed, %v remaining)", elapsed.Round(time.Second), remaining.Round(time.Second))) lastProgressReport = time.Now() } @@ -693,9 +678,9 @@ func (d *Deployer) WaitForCentral(timeout time.Duration) bool { time.Sleep(checkInterval) } - d.logger.Warning("⚠️ Central did not become ready within the timeout period") - d.logger.Warning(" This is not necessarily an error - Central may still be initializing") - d.logger.Warning(" You can check Central status manually or wait a bit longer") + log.Warning("⚠️ Central did not become ready within the timeout period") + log.Warning(" This is not necessarily an error - Central may still be initializing") + log.Warning(" You can check Central status manually or wait a bit longer") return false } @@ -728,9 +713,9 @@ func (d *Deployer) cleanupTempDir(path string, description string) { return } if err := os.RemoveAll(path); err != nil { - d.logger.Warningf("Failed to cleanup %s at %s: %v", description, path, err) + log.Warningf("Failed to cleanup %s at %s: %v", description, path, err) } else { - d.logger.Dim(fmt.Sprintf("Cleaned up %s: %s", description, path)) + log.Dim(fmt.Sprintf("Cleaned up %s: %s", description, path)) } } @@ -748,7 +733,7 @@ func (d *Deployer) writeEnvrcFile(ctx context.Context) error { return fmt.Errorf("failed to write envrc file: %w", err) } - d.logger.Successf("✓ Environment variables written to %s", d.envrcFile) + log.Successf("✓ Environment variables written to %s", d.envrcFile) return nil } @@ -758,7 +743,6 @@ func (d *Deployer) PrintCentralDeploymentSummary() { olm := d.config.Operator.DeployViaOlmEnabled() exposure := d.config.Central.GetExposure() portForwarding := d.config.Central.PortForwardingEnabled() - log := d.logger kubeContext := d.kubeContext // Calculate box width @@ -859,15 +843,15 @@ func (d *Deployer) checkDeploymentProgressInNamespace(ctx context.Context, names // Check if this is a new deployment or state change if prevState, exists := seenDeployments[name]; !exists { // New deployment detected - d.logger.Dimf(" → Deployment '%s' created (%s/%s replicas ready)", name, ready, replicas) + log.Dimf(" → Deployment '%s' created (%s/%s replicas ready)", name, ready, replicas) seenDeployments[name] = stateKey updated = true } else if prevState != stateKey { // State changed if available != "" && available != "0" && available == replicas { - d.logger.Dimf(" ✓ Deployment '%s' is available (%s/%s replicas)", name, available, replicas) + log.Dimf(" ✓ Deployment '%s' is available (%s/%s replicas)", name, available, replicas) } else if ready != prevState[len(name)+1:] { - d.logger.Dimf(" ⋯ Deployment '%s' progressing (%s/%s replicas ready)", name, ready, replicas) + log.Dimf(" ⋯ Deployment '%s' progressing (%s/%s replicas ready)", name, ready, replicas) } seenDeployments[name] = stateKey updated = true @@ -911,17 +895,17 @@ func (d *Deployer) checkPodProgressInNamespace(ctx context.Context, namespace st // Only report significant state changes if prevState, exists := seenPods[name]; !exists { if phase == "Pending" { - d.logger.Dim(fmt.Sprintf(" • Pod '%s' starting...", name)) + log.Dim(fmt.Sprintf(" • Pod '%s' starting...", name)) } else if phase == "Running" && ready == "true" { - d.logger.Dim(fmt.Sprintf(" • Pod '%s' running", name)) + log.Dim(fmt.Sprintf(" • Pod '%s' running", name)) } seenPods[name] = stateKey updated = true } else if prevState != stateKey { if phase == "Running" && ready == "true" { - d.logger.Dim(fmt.Sprintf(" • Pod '%s' is ready", name)) + log.Dim(fmt.Sprintf(" • Pod '%s' is ready", name)) } else if phase == "Running" && ready == "false" { - d.logger.Dim(fmt.Sprintf(" • Pod '%s' running (not ready yet)", name)) + log.Dim(fmt.Sprintf(" • Pod '%s' running (not ready yet)", name)) } seenPods[name] = stateKey updated = true @@ -937,7 +921,6 @@ func (d *Deployer) PrintSecuredClusterDeploymentSummary() { component := "Secured Cluster" imageTag := d.config.SecuredClusterVersion() olm := d.config.Operator.DeployViaOlmEnabled() - log := d.logger kubeContext := d.kubeContext // Calculate box width diff --git a/internal/deployer/kubectl.go b/internal/deployer/kubectl.go index 8acf6efb..21357234 100644 --- a/internal/deployer/kubectl.go +++ b/internal/deployer/kubectl.go @@ -6,7 +6,7 @@ import ( "github.com/stackrox/roxie/internal/k8s" ) -// runKubectl is a thin wrapper around k8s.RunKubectl that injects the deployer's logger. +// runKubectl is a thin wrapper around k8s.RunKubectl. func (d *Deployer) runKubectl(ctx context.Context, opts k8s.KubectlOptions) (k8s.KubectlResult, error) { - return k8s.RunKubectl(ctx, d.logger, opts) + return k8s.RunKubectl(ctx, opts) } diff --git a/internal/deployer/local_images.go b/internal/deployer/local_images.go index 4984c4fb..9af91239 100644 --- a/internal/deployer/local_images.go +++ b/internal/deployer/local_images.go @@ -7,6 +7,7 @@ import ( "slices" "github.com/stackrox/roxie/internal/containerrt" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/types" ) @@ -40,11 +41,11 @@ func (d *Deployer) GetPreLoaderForCluster() (ImagePreLoader, error) { func (d *Deployer) TryTransferLocalImages(ctx context.Context, preLoader ImagePreLoader) error { localImages, err := d.collectLocalImages(ctx) if err != nil { - d.logger.Dimf("Collecting local images failed: %v", err) + log.Dimf("Collecting local images failed: %v", err) return err } if len(localImages) == 0 { - d.logger.Dim("No local images found") + log.Dim("No local images found") return nil } @@ -57,12 +58,12 @@ func (d *Deployer) TryTransferLocalImages(ctx context.Context, preLoader ImagePr for _, image := range localImages { if slices.Contains(availableImagesInCluster, image) { // Exists already in local cluster registry. - d.logger.Dimf("Image %s already available in local cluster, skipping.", image) + log.Dimf("Image %s already available in local cluster, skipping.", image) continue } - d.logger.Dimf("Transferring local image %s to local cluster...", image) + log.Dimf("Transferring local image %s to local cluster...", image) if err := preLoader.SendImage(ctx, image); err != nil { - d.logger.Warningf("Transferring local image %s to %s cluster failed: %s", + log.Warningf("Transferring local image %s to %s cluster failed: %s", image, d.config.Roxie.ClusterType, err) } } @@ -78,12 +79,10 @@ func (d *Deployer) collectLocalImages(ctx context.Context) ([]string, error) { return nil, nil } - if d.verbose { - d.logger.Dimf("Using container runtime socket %s", socket) - } + log.Debugf("Using container runtime socket %s", socket) available, err := containerrt.ListLocalImages(ctx, socket) if err != nil { - d.logger.Dimf("Could not query container runtime at %s: %v", socket, err) + log.Dimf("Could not query container runtime at %s: %v", socket, err) return nil, err } @@ -96,10 +95,10 @@ func (d *Deployer) collectLocalImages(ctx context.Context) ([]string, error) { localImages := make([]string, 0, len(wanted)) for _, img := range wanted { if _, ok := availableSet[img]; ok { - d.logger.Dimf("Image %s exists locally", img) + log.Dimf("Image %s exists locally", img) localImages = append(localImages, img) } else { - d.logger.Dimf("Image %s needs to be pulled from registry", img) + log.Dimf("Image %s needs to be pulled from registry", img) } } return localImages, nil diff --git a/internal/deployer/local_images_custom.go b/internal/deployer/local_images_custom.go index b136840b..78af17dc 100644 --- a/internal/deployer/local_images_custom.go +++ b/internal/deployer/local_images_custom.go @@ -7,17 +7,15 @@ import ( "os/exec" "strings" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) type customImagePreLoader struct { - log *logger.Logger command string } -func NewCustomImagePreloader(_ context.Context, log *logger.Logger, command string) ImagePreLoader { +func NewCustomImagePreloader(command string) ImagePreLoader { return &customImagePreLoader{ - log: log, command: command, } } @@ -29,14 +27,14 @@ func (c *customImagePreLoader) GetImages(_ context.Context) ([]string, error) { func (c *customImagePreLoader) SendImage(ctx context.Context, image string) error { env := os.Environ() env = append(env, fmt.Sprintf("IMAGE=%s", image)) - c.log.Dimf("Invoking %q...", c.command) + log.Dimf("Invoking %q...", c.command) cmd := exec.CommandContext(ctx, "/bin/sh", "-c", c.command) cmd.Env = env output, err := cmd.CombinedOutput() if err != nil { - c.log.Warningf("Image preloading failed: %v", err) + log.Warningf("Image preloading failed: %v", err) for line := range strings.SplitSeq(strings.TrimSpace(string(output)), "\n") { - c.log.Dimf("| %s", line) + log.Dimf("| %s", line) } return fmt.Errorf("sending image failed: %w", err) } diff --git a/internal/deployer/local_images_generic.go b/internal/deployer/local_images_generic.go index fc17b98f..1929e434 100644 --- a/internal/deployer/local_images_generic.go +++ b/internal/deployer/local_images_generic.go @@ -8,7 +8,7 @@ import ( "context" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) const ( @@ -16,13 +16,11 @@ const ( ) type genericImageSender struct { - log *logger.Logger args []string } -func newGenericImageSender(log *logger.Logger, args ...string) genericImageSender { +func newGenericImageSender(args ...string) genericImageSender { return genericImageSender{ - log: log, args: args, } } @@ -45,9 +43,9 @@ func (g *genericImageSender) SendImage(ctx context.Context, imageTag string) err output, err := cmd.CombinedOutput() if err != nil { argsJoined := strings.Join(args, " ") - g.log.Errorf("Executing %s failed:", argsJoined) + log.Errorf("Executing %s failed:", argsJoined) for line := range strings.SplitSeq(strings.TrimSpace(string(output)), "\n") { - g.log.Errorf("| %s", line) + log.Errorf("| %s", line) } return fmt.Errorf("executing '%s': %w", argsJoined, err) } diff --git a/internal/deployer/local_images_kind.go b/internal/deployer/local_images_kind.go index 38073a59..bb527a4b 100644 --- a/internal/deployer/local_images_kind.go +++ b/internal/deployer/local_images_kind.go @@ -6,11 +6,10 @@ import ( "strings" "github.com/stackrox/roxie/internal/containerrt" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) type kindImagePreLoader struct { - log *logger.Logger genericImageSender kindClusterName string containerRuntimeSocket string @@ -18,11 +17,10 @@ type kindImagePreLoader struct { func (d *Deployer) newKindImagePreloader() (*kindImagePreLoader, error) { kindClusterName := kubeContextToKindClusterName(d.kubeContext) - d.logger.Dimf("Kind cluster name is %s", kindClusterName) + log.Dimf("Kind cluster name is %s", kindClusterName) return &kindImagePreLoader{ - log: d.logger, kindClusterName: kindClusterName, - genericImageSender: newGenericImageSender(d.logger, "kind", "load", "docker-image", "", "--name", kindClusterName), + genericImageSender: newGenericImageSender("kind", "load", "docker-image", "", "--name", kindClusterName), containerRuntimeSocket: d.containerRuntimeSocket, }, nil } @@ -33,7 +31,7 @@ func (k *kindImagePreLoader) GetImages(ctx context.Context) ([]string, error) { } nodeName := k.kindClusterName + "-control-plane" - output, err := containerrt.ExecInContainer(ctx, k.log, k.containerRuntimeSocket, nodeName, + output, err := containerrt.ExecInContainer(ctx, k.containerRuntimeSocket, nodeName, []string{"crictl", "images", "-o", "json"}) if err != nil { return nil, fmt.Errorf("listing images in kind node %s: %w", nodeName, err) diff --git a/internal/deployer/local_images_minikube.go b/internal/deployer/local_images_minikube.go index a5fe6e95..548710a2 100644 --- a/internal/deployer/local_images_minikube.go +++ b/internal/deployer/local_images_minikube.go @@ -7,11 +7,10 @@ import ( "strings" "github.com/stackrox/roxie/internal/containerrt" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) type minikubeImagePreLoader struct { - log *logger.Logger genericImageSender } @@ -21,8 +20,7 @@ var ( func (d *Deployer) newMinikubeImagePreloader() (*minikubeImagePreLoader, error) { return &minikubeImagePreLoader{ - log: d.logger, - genericImageSender: newGenericImageSender(d.logger, "minikube", "image", "load", ""), + genericImageSender: newGenericImageSender("minikube", "image", "load", ""), }, nil } @@ -34,9 +32,9 @@ func (k *minikubeImagePreLoader) GetImages(ctx context.Context) ([]string, error cmd := exec.CommandContext(ctx, minikubeGetImagesCommand[0], minikubeGetImagesCommand[1:]...) output, err := cmd.CombinedOutput() if err != nil { - k.log.Warningf("Command %q failed: %v", strings.Join(minikubeGetImagesCommand, " "), err) + log.Warningf("Command %q failed: %v", strings.Join(minikubeGetImagesCommand, " "), err) for line := range strings.SplitSeq(strings.TrimSpace(string(output)), "\n") { - k.log.Dimf("| %s", line) + log.Dimf("| %s", line) } return nil, fmt.Errorf("listing images in minikube node: %w", err) } diff --git a/internal/deployer/operator.go b/internal/deployer/operator.go index 20f5ed02..8a9d88d8 100644 --- a/internal/deployer/operator.go +++ b/internal/deployer/operator.go @@ -14,6 +14,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/stackrox/roxie/internal/k8s" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/ocihelper" ) @@ -34,7 +35,7 @@ var requiredCRDs = []string{ // deployOperatorNonOLM deploys one RHACS operator instance without OLM. func (d *Deployer) deployOperatorNonOLM(ctx context.Context, instance OperatorInstanceConfig) error { - d.logger.Infof("Operator tag: %s (namespace %s)", instance.Version, instance.Namespace) + log.Infof("Operator tag: %s (namespace %s)", instance.Version, instance.Namespace) bundleImage := instance.BundleImage() bundleDir, err := d.downloadAndExtractOperatorBundle(ctx, bundleImage) @@ -43,7 +44,7 @@ func (d *Deployer) deployOperatorNonOLM(ctx context.Context, instance OperatorIn } defer d.cleanupTempDir(bundleDir, "operator bundle directory") - d.logger.Infof("Bundle image: %s", bundleImage) + log.Infof("Bundle image: %s", bundleImage) // Only the newest planned operator version may apply CRDs, so an older // companion operator cannot downgrade cluster CRD schemas. @@ -57,7 +58,7 @@ func (d *Deployer) deployOperatorNonOLM(ctx context.Context, instance OperatorIn return err } } else { - d.logger.Dimf("Skipping CRD apply for older operator version %s (newest is %s)", + log.Dimf("Skipping CRD apply for older operator version %s (newest is %s)", operatorTag, d.config.NewestOperatorVersion()) } @@ -75,16 +76,16 @@ func (d *Deployer) downloadAndExtractOperatorBundle(ctx context.Context, bundleI return "", fmt.Errorf("failed to create temp dir: %w", err) } - d.logger.Dimf("Created temporary directory: %s", bundleDir) - d.logger.Info("Pulling and extracting operator bundle image...") + log.Dimf("Created temporary directory: %s", bundleDir) + log.Info("Pulling and extracting operator bundle image...") // The bundle images only contain platform-agnostic YAML files. - if err := ocihelper.ExtractManifestsFromImage(ctx, d.logger, bundleImage, bundleDir, d.containerRuntimeSocket); err != nil { + if err := ocihelper.ExtractManifestsFromImage(ctx, bundleImage, bundleDir, d.containerRuntimeSocket); err != nil { os.RemoveAll(bundleDir) return "", fmt.Errorf("failed to copy bundle contents: %w", err) } - d.logger.Successf("✓ Bundle extracted to: %s", bundleDir) + log.Successf("✓ Bundle extracted to: %s", bundleDir) return bundleDir, nil } @@ -108,7 +109,7 @@ func (d *Deployer) identifyCRDFileNames(bundleDir string) ([]string, error) { content, err := os.ReadFile(path) if err != nil { - d.logger.Warningf("Failed to read file %q from extracted bundle: %v", path, err) + log.Warningf("Failed to read file %q from extracted bundle: %v", path, err) return nil } @@ -116,7 +117,7 @@ func (d *Deployer) identifyCRDFileNames(bundleDir string) ([]string, error) { Kind string `yaml:"kind"` } if err := yaml.Unmarshal(content, &meta); err != nil { - d.logger.Warningf("Failed to unmarshal file %q from extracted bundle: %v", path, err) + log.Warningf("Failed to unmarshal file %q from extracted bundle: %v", path, err) return nil } @@ -136,19 +137,19 @@ func (d *Deployer) identifyCRDFileNames(bundleDir string) ([]string, error) { // applyCRDsToCluster applies CRD files to the cluster func (d *Deployer) applyCRDsToCluster(ctx context.Context, crdFiles []string) error { - d.logger.Infof("Applying %d CRD(s) to cluster", len(crdFiles)) + log.Infof("Applying %d CRD(s) to cluster", len(crdFiles)) for _, crdFile := range crdFiles { result, err := d.runKubectl(ctx, k8s.KubectlOptions{ Args: []string{"apply", "-f", crdFile}, }) if err != nil { - d.logger.Errorf("kubectl stderr: %s", result.Stderr) + log.Errorf("kubectl stderr: %s", result.Stderr) return fmt.Errorf("failed to apply CRD %s: %w\nStderr: %s", crdFile, err, result.Stderr) } basename := filepath.Base(crdFile) - d.logger.Successf("✓ Successfully applied CRD %s", basename) + log.Successf("✓ Successfully applied CRD %s", basename) } return nil @@ -169,8 +170,8 @@ func (d *Deployer) ensureCRDsInstalled(ctx context.Context) error { if len(missing) > 0 { crdInstance := d.config.NewestOperatorInstance() bundleImage := crdInstance.BundleImage() - d.logger.Warningf("Missing CRDs detected (%s)", strings.Join(missing, ", ")) - d.logger.Warningf("Fetching bundle %s", bundleImage) + log.Warningf("Missing CRDs detected (%s)", strings.Join(missing, ", ")) + log.Warningf("Fetching bundle %s", bundleImage) bundleDir, err := d.downloadAndExtractOperatorBundle(ctx, bundleImage) if err != nil { @@ -196,7 +197,7 @@ func (d *Deployer) deployOperatorFromCSV(ctx context.Context, bundleDir string, return errors.New("ClusterServiceVersion file not found in bundle") } - d.logger.Info("🔍 Parsing ClusterServiceVersion deployment specification") + log.Info("🔍 Parsing ClusterServiceVersion deployment specification") deploymentSpec, err := d.parseCSVDeploymentSpec(csvFile) if err != nil { @@ -206,15 +207,15 @@ func (d *Deployer) deployOperatorFromCSV(ctx context.Context, bundleDir string, serviceAccountName := deploymentSpec["service_account"].(string) d.useOperatorPullSecrets = instance.KonfluxImagesEnabled() && d.config.Roxie.ClusterType.NeedsPullSecrets() - d.logger.Info("📋 Operator deployment plan:") - d.logger.Dimf(" • Namespace: %s", instance.Namespace) - d.logger.Dimf(" • ServiceAccount: %s", serviceAccountName) - d.logger.Dimf(" • Setting up pull secrets: %v", d.useOperatorPullSecrets) - if d.verbose && len(instance.EnvVars) > 0 { - d.logger.Dimf(" • Custom operator env vars: %d", len(instance.EnvVars)) + log.Info("📋 Operator deployment plan:") + log.Dimf(" • Namespace: %s", instance.Namespace) + log.Dimf(" • ServiceAccount: %s", serviceAccountName) + log.Dimf(" • Setting up pull secrets: %v", d.useOperatorPullSecrets) + if log.IsVerbose() && len(instance.EnvVars) > 0 { + log.Debugf(" • Custom operator env vars: %d", len(instance.EnvVars)) for _, envVar := range envVarsToSortedList(instance.EnvVars) { ev := envVar.(map[string]any) - d.logger.Dimf(" %s=%s", ev["name"], ev["value"]) + log.Debugf(" %s=%s", ev["name"], ev["value"]) } } @@ -245,7 +246,7 @@ func (d *Deployer) deployOperatorFromCSV(ctx context.Context, bundleDir string, return err } - d.logger.Successf("🎉 Operator deployment completed successfully in %s!", instance.Namespace) + log.Successf("🎉 Operator deployment completed successfully in %s!", instance.Namespace) return nil } @@ -323,7 +324,7 @@ func (d *Deployer) createServiceAccount(ctx context.Context, namespace, name str func (d *Deployer) createClusterRoleFromCSV(ctx context.Context, deploymentSpec map[string]any, instance OperatorInstanceConfig) error { clusterPermissions := deploymentSpec["cluster_permissions"].([]any) if len(clusterPermissions) == 0 { - d.logger.Warning("No cluster permissions found in CSV") + log.Warning("No cluster permissions found in CSV") return nil } @@ -443,7 +444,7 @@ func (d *Deployer) createDeploymentFromCSV(ctx context.Context, instance Operato podSpec["serviceAccountName"] = deploymentSpec["service_account"] if current, _ := managerContainer["image"].(string); current != instance.OperatorImage() { // Currently this should only happen in Konflux mode. - d.logger.Infof("Rewriting operator image to %s", instance.OperatorImage()) + log.Infof("Rewriting operator image to %s", instance.OperatorImage()) managerContainer["image"] = instance.OperatorImage() } @@ -531,7 +532,7 @@ func (d *Deployer) applyBundleServiceResources(ctx context.Context, bundleDir, n // waitForOperatorReady waits for operator deployment to be ready func (d *Deployer) waitForOperatorReady(ctx context.Context, namespace, deploymentName string, timeout int) error { - d.logger.Info("⏳ Waiting for operator deployment to become ready...") + log.Info("⏳ Waiting for operator deployment to become ready...") start := time.Now() for time.Since(start) < time.Duration(timeout)*time.Second { @@ -541,7 +542,7 @@ func (d *Deployer) waitForOperatorReady(ctx context.Context, namespace, deployme if err == nil && result.Stdout != "" { replicas := strings.TrimSpace(result.Stdout) if replicas != "0" && replicas != "" { - d.logger.Successf("✓ Operator deployment is ready (%s replicas)", replicas) + log.Successf("✓ Operator deployment is ready (%s replicas)", replicas) return nil } } @@ -555,7 +556,7 @@ func (d *Deployer) waitForOperatorReady(ctx context.Context, namespace, deployme // teardownOperatorNonOLMInNamespace removes a non-OLM operator from the given namespace // and deletes its cluster-scoped RBAC resources for that instance. func (d *Deployer) teardownOperatorNonOLMInNamespace(ctx context.Context, instance OperatorInstanceConfig) error { - d.logger.Infof("🧹 Tearing down non-OLM operator in namespace %s...", instance.Namespace) + log.Infof("🧹 Tearing down non-OLM operator in namespace %s...", instance.Namespace) d.runKubectl(ctx, k8s.KubectlOptions{ Args: []string{"delete", "namespace", instance.Namespace, "--wait=false"}, @@ -576,10 +577,10 @@ func (d *Deployer) teardownOperatorNonOLMInNamespace(ctx context.Context, instan } if err := d.waitForNamespaceDeletion(instance.Namespace); err != nil { - d.logger.Warningf("Namespace %s deletion incomplete: %v", instance.Namespace, err) + log.Warningf("Namespace %s deletion incomplete: %v", instance.Namespace, err) } - d.logger.Successf("✓ Non-OLM operator resources removed from %s", instance.Namespace) + log.Successf("✓ Non-OLM operator resources removed from %s", instance.Namespace) return nil } @@ -603,7 +604,7 @@ func (d *Deployer) teardownAllOperatorClusterRBAC(ctx context.Context) { // teardownOperatorNonOLM removes non-OLM operators from all known namespaces. func (d *Deployer) teardownOperatorNonOLM(ctx context.Context) error { - d.logger.Info("🧹 Tearing down operator deployed without OLM...") + log.Info("🧹 Tearing down operator deployed without OLM...") for _, ns := range AllOperatorNamespaces { if !d.namespaceExists(ns) { @@ -620,7 +621,7 @@ func (d *Deployer) teardownOperatorNonOLM(ctx context.Context) error { } d.teardownAllOperatorClusterRBAC(ctx) - d.logger.Success("✓ Non-OLM operator resources removed") + log.Success("✓ Non-OLM operator resources removed") return nil } @@ -645,7 +646,7 @@ func (d *Deployer) teardownOperator(ctx context.Context) error { } } if !foundAny { - d.logger.Dim("No operator deployment found, skipping operator teardown") + log.Dim("No operator deployment found, skipping operator teardown") return nil } diff --git a/internal/deployer/operator_olm.go b/internal/deployer/operator_olm.go index 1dfd6833..2b1c310c 100644 --- a/internal/deployer/operator_olm.go +++ b/internal/deployer/operator_olm.go @@ -10,6 +10,7 @@ import ( "github.com/stackrox/roxie/internal/constants" "github.com/stackrox/roxie/internal/k8s" + log "github.com/stackrox/roxie/internal/logger" "gopkg.in/yaml.v3" ) @@ -32,13 +33,13 @@ const ( // deployOperatorViaOLM deploys the RHACS operator using OLM. func (d *Deployer) deployOperatorViaOLM(ctx context.Context) error { - d.logger.Info("🚀 Deploying operator via OLM...") - d.logger.Infof("Operator tag: %s", d.config.Operator.Version) + log.Info("🚀 Deploying operator via OLM...") + log.Infof("Operator tag: %s", d.config.Operator.Version) if len(d.config.Operator.EnvVars) > 0 { - d.logger.Infof("Custom operator env vars: %d", len(d.config.Operator.EnvVars)) + log.Infof("Custom operator env vars: %d", len(d.config.Operator.EnvVars)) for _, envVar := range envVarsToSortedList(d.config.Operator.EnvVars) { ev := envVar.(map[string]interface{}) - d.logger.Dimf(" %s=%s", ev["name"], ev["value"]) + log.Dimf(" %s=%s", ev["name"], ev["value"]) } } @@ -47,7 +48,7 @@ func (d *Deployer) deployOperatorViaOLM(ctx context.Context) error { } indexImage := d.getOperatorIndexImage() - d.logger.Infof("Index image: %s", indexImage) + log.Infof("Index image: %s", indexImage) if err := d.prepareNamespace(ctx, operatorNamespace, false); err != nil { return err @@ -77,7 +78,7 @@ func (d *Deployer) deployOperatorViaOLM(ctx context.Context) error { return fmt.Errorf("failed waiting for operator in namespace %s to become ready: %w", operatorNamespace, err) } - d.logger.Success("🎉 Operator deployed successfully via OLM!") + log.Success("🎉 Operator deployed successfully via OLM!") return nil } @@ -96,9 +97,9 @@ func (d *Deployer) checkOLMInstalled(ctx context.Context) error { }) if err != nil { if result.Stderr != "" { - d.logger.Error("kubectl stderr:") + log.Error("kubectl stderr:") for stderrLine := range strings.SplitSeq(result.Stderr, "\n") { - d.logger.Errorf("stderr: %s", stderrLine) + log.Errorf("stderr: %s", stderrLine) } } return fmt.Errorf("failed to query api-group operators.coreos.com: %w", err) @@ -118,12 +119,12 @@ func (d *Deployer) checkOLMInstalled(ctx context.Context) error { } if len(missingResources) > 0 { for _, resource := range missingResources { - d.logger.Errorf("OLM resource not served by the API server: %s", resource) + log.Errorf("OLM resource not served by the API server: %s", resource) } return fmt.Errorf("OLM is not properly installed, %d required resource(s) missing", len(missingResources)) } - d.logger.Success("✓ OLM detected in cluster") + log.Success("✓ OLM detected in cluster") return nil } @@ -134,7 +135,7 @@ func (d *Deployer) getOperatorIndexImage() string { // createCatalogSource creates the CatalogSource for the operator. func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) error { - d.logger.Info("Creating CatalogSource...") + log.Info("Creating CatalogSource...") catalogSource := map[string]interface{}{ "apiVersion": "operators.coreos.com/v1alpha1", @@ -168,13 +169,13 @@ func (d *Deployer) createCatalogSource(ctx context.Context, indexImage string) e return fmt.Errorf("failed to create CatalogSource: %w", err) } - d.logger.Success("✓ CatalogSource created") + log.Success("✓ CatalogSource created") return nil } // createOperatorGroup creates the OperatorGroup. func (d *Deployer) createOperatorGroup(ctx context.Context) error { - d.logger.Info("Creating OperatorGroup...") + log.Info("Creating OperatorGroup...") operatorGroup := map[string]interface{}{ "apiVersion": "operators.coreos.com/v1alpha2", @@ -198,13 +199,13 @@ func (d *Deployer) createOperatorGroup(ctx context.Context) error { return fmt.Errorf("failed to create OperatorGroup: %w", err) } - d.logger.Success("✓ OperatorGroup created") + log.Success("✓ OperatorGroup created") return nil } // createSubscription creates the Subscription for the operator. func (d *Deployer) createSubscription(ctx context.Context) error { - d.logger.Info("Creating Subscription...") + log.Info("Creating Subscription...") startingCSV := fmt.Sprintf("rhacs-operator.v%s", d.config.Operator.Version) @@ -246,13 +247,13 @@ func (d *Deployer) createSubscription(ctx context.Context) error { return fmt.Errorf("failed to create Subscription %s: %w", namespacedSubscriptionName, err) } - d.logger.Success("✓ Subscription created") + log.Success("✓ Subscription created") return nil } // waitForAndApproveInstallPlan waits for the InstallPlan to be created and approves it. func (d *Deployer) waitForAndApproveInstallPlan(ctx context.Context) error { - d.logger.Info("⏳ Waiting for InstallPlan to be created...") + log.Info("⏳ Waiting for InstallPlan to be created...") // Wait for subscription to have InstallPlanPending condition. start := time.Now() @@ -300,7 +301,7 @@ func (d *Deployer) waitForAndApproveInstallPlan(ctx context.Context) error { return errors.New("InstallPlan name is empty") } - d.logger.Infof("Approving InstallPlan: %s", installPlanName) + log.Infof("Approving InstallPlan: %s", installPlanName) // Approve the InstallPlan. _, err = d.runKubectl(ctx, k8s.KubectlOptions{ @@ -310,14 +311,14 @@ func (d *Deployer) waitForAndApproveInstallPlan(ctx context.Context) error { return fmt.Errorf("failed to approve InstallPlan %s for Subscription %s: %w", installPlanName, namespacedSubscriptionName, err) } - d.logger.Success("✓ InstallPlan approved") + log.Success("✓ InstallPlan approved") return nil } // waitForCSVSuccess waits for the CSV to reach Succeeded phase. func (d *Deployer) waitForCSVSuccess(ctx context.Context) error { csvName := fmt.Sprintf("rhacs-operator.v%s", d.config.Operator.Version) - d.logger.Infof("⏳ Waiting for CSV %s to succeed...", csvName) + log.Infof("⏳ Waiting for CSV %s to succeed...", csvName) start := time.Now() timeout := 10 * time.Minute @@ -329,7 +330,7 @@ func (d *Deployer) waitForCSVSuccess(ctx context.Context) error { if err == nil { phase := strings.TrimSpace(result.Stdout) if phase == "Succeeded" { - d.logger.Success("✓ CSV succeeded") + log.Success("✓ CSV succeeded") return nil } if phase == "Failed" { @@ -357,7 +358,7 @@ func (d *Deployer) detectOperatorDeploymentMode(ctx context.Context) (bool, Oper } // If no subscription, check if operator deployment exists/if it has the expected OLM label. - labelValue, err := k8s.RetrieveClusterResourceLabel(ctx, d.logger, operatorNamespace, "deployment", operatorDeploymentName, olmOwnerLabel) + labelValue, err := k8s.RetrieveClusterResourceLabel(ctx, operatorNamespace, "deployment", operatorDeploymentName, olmOwnerLabel) if k8s.IsResourceNotFound(err) { // No operator deployment found. return false, OperatorModeNonOLM, nil @@ -377,7 +378,7 @@ func (d *Deployer) detectOperatorDeploymentMode(ctx context.Context) (bool, Oper // teardownOperatorOLM removes the operator when installed via OLM. func (d *Deployer) teardownOperatorOLM(ctx context.Context) error { - d.logger.Info("🧹 Tearing down operator deployed via OLM...") + log.Info("🧹 Tearing down operator deployed via OLM...") // Delete Subscription (this typically cascades CSV and operands depending on OLM behavior). d.runKubectl(ctx, k8s.KubectlOptions{ @@ -418,9 +419,9 @@ func (d *Deployer) teardownOperatorOLM(ctx context.Context) error { }) if err := d.waitForNamespaceDeletion(operatorNamespace); err != nil { - d.logger.Warningf("Namespace %s deletion incomplete: %v", operatorNamespace, err) + log.Warningf("Namespace %s deletion incomplete: %v", operatorNamespace, err) } - d.logger.Success("✓ OLM operator resources removed") + log.Success("✓ OLM operator resources removed") return nil } diff --git a/internal/dockerauth/dockerauth.go b/internal/dockerauth/dockerauth.go index fde6bd07..39267f9d 100644 --- a/internal/dockerauth/dockerauth.go +++ b/internal/dockerauth/dockerauth.go @@ -11,7 +11,7 @@ import ( "path/filepath" "github.com/stackrox/roxie/internal/constants" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) const ( @@ -21,7 +21,6 @@ const ( // DockerAuth handles Docker authentication and pull secret management. type DockerAuth struct { - logger *logger.Logger skipCredVerification bool } @@ -50,10 +49,8 @@ type Credentials struct { } // New creates a new DockerAuth instance. -func New(log *logger.Logger) *DockerAuth { - return &DockerAuth{ - logger: log, - } +func New() *DockerAuth { + return &DockerAuth{} } // GetAndVerifyCredentials retrieves and verifies Docker credentials. @@ -75,7 +72,7 @@ func (d *DockerAuth) GetAndVerifyCredentials() (*Credentials, error) { if username == "" { // Try to get from Docker config file. dockerConfigPath := filepath.Join(os.Getenv("HOME"), ".docker", "config.json") - d.logger.Dimf("REGISTRY_USERNAME/REGISTRY_PASSWORD unset. Trying to obtain Docker credentials from config file: %s", dockerConfigPath) + log.Dimf("REGISTRY_USERNAME/REGISTRY_PASSWORD unset. Trying to obtain Docker credentials from config file: %s", dockerConfigPath) if _, err := os.Stat(dockerConfigPath); err == nil { var err error username, password, err = d.getCredentialsFromDockerConfig(dockerConfigPath) @@ -197,8 +194,8 @@ func (d *DockerAuth) VerifyCredentials(username, password string) error { output, err := cmd.CombinedOutput() if err != nil { - d.logger.Warningf("Failed to verify credentials for %s: %v", acsImageRegistry, err) - d.logger.Dimf("Verification output: %s", string(output)) + log.Warningf("Failed to verify credentials for %s: %v", acsImageRegistry, err) + log.Dimf("Verification output: %s", string(output)) return fmt.Errorf("credential verification failed for %s: %w", acsImageRegistry, err) } @@ -212,7 +209,7 @@ func (d *DockerAuth) VerifyCredentials(username, password string) error { return fmt.Errorf("credential verification failed: no token received from %s", acsImageRegistry) } - d.logger.Dimf("Successfully verified credentials for %s (repository: %s)", acsImageRegistry, mainImageRepository) + log.Dimf("Successfully verified credentials for %s (repository: %s)", acsImageRegistry, mainImageRepository) return nil } diff --git a/internal/dockerauth/dockerauth_test.go b/internal/dockerauth/dockerauth_test.go index 597acc23..474ffd1b 100644 --- a/internal/dockerauth/dockerauth_test.go +++ b/internal/dockerauth/dockerauth_test.go @@ -6,7 +6,6 @@ import ( "strings" "testing" - "github.com/stackrox/roxie/internal/logger" ) func TestGetAndVerifyCredentialsFromEnv(t *testing.T) { @@ -14,8 +13,7 @@ func TestGetAndVerifyCredentialsFromEnv(t *testing.T) { t.Setenv("REGISTRY_USERNAME", "user") t.Setenv("REGISTRY_PASSWORD", "pass") - log := logger.New() - da := New(log) + da := New() da.skipCredVerification = true // Skip verification in tests creds, err := da.GetAndVerifyCredentials() @@ -85,8 +83,7 @@ func TestGetAndVerifyCredentialsNoCredentials(t *testing.T) { // Use a temporary home directory to simulate missing credentials. t.Setenv("HOME", t.TempDir()) - log := logger.New() - da := New(log) + da := New() da.skipCredVerification = true // Skip verification in tests _, err := da.GetAndVerifyCredentials() diff --git a/internal/env/env.go b/internal/env/env.go index 8e01145e..ca1d3f9f 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -14,7 +14,7 @@ import ( "time" "github.com/stackrox/roxie/internal/containerutil" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/types" "golang.org/x/term" ) @@ -59,12 +59,12 @@ func isRunningInteractively() bool { // ensureInitialized performs lazy initialization of cluster information // This avoids contacting the cluster on package import -func ensureInitialized(log *logger.Logger) error { +func ensureInitialized() error { initializationMutex.Lock() defer initializationMutex.Unlock() if !initialized { - kubeConfig, err := fetchKubeConfig(log) + kubeConfig, err := fetchKubeConfig() if err != nil { return err } @@ -105,10 +105,7 @@ type KubeCluster struct { // Retries on failure to handle race conditions during container startup, which I have // observed in relation with podman :U mounts: the container was starting before the gcloud config // was writable by the container user, hence GKE authentication failed immediately. -func Initialize(log *logger.Logger) error { - if log == nil { - log = logger.New() - } +func Initialize() error { if RunningInRoxieContainer { log.Dim("Running containerized.") } @@ -118,7 +115,7 @@ func Initialize(log *logger.Logger) error { var lastErr error for attempt := 1; attempt <= maxRetries; attempt++ { - err := ensureInitialized(log) + err := ensureInitialized() if err == nil { return nil } @@ -217,8 +214,8 @@ func isOpenShift4(apiResources []string) bool { } // fetchKubeConfig retrieves the current kubectl configuration -func fetchKubeConfig(log *logger.Logger) (KubeConfig, error) { - if err := kubeconfigChecks(log); err != nil { +func fetchKubeConfig() (KubeConfig, error) { + if err := kubeconfigChecks(); err != nil { return KubeConfig{}, err } // Get current context @@ -264,7 +261,7 @@ func fetchKubeConfig(log *logger.Logger) (KubeConfig, error) { }, nil } -func kubeconfigChecks(log *logger.Logger) error { +func kubeconfigChecks() error { kubeConfigPath, err := getKubeConfigPath() if err != nil { return fmt.Errorf("getting kubeconfig path: %w", err) @@ -318,7 +315,7 @@ func fetchAPIResources() ([]string, error) { return lines, nil } -func IsInStackroxRepository(log *logger.Logger) bool { +func IsInStackroxRepository() bool { out, err := exec.Command("git", "remote", "-v").Output() if exitErr, ok := errors.AsType[*exec.ExitError](err); ok { log.Dimf("Not a git repository, ignoring ('git remote' returned %d)", exitErr.ExitCode()) @@ -349,7 +346,7 @@ func isStackRoxRepositoryRemote(remote string) bool { return stackroxRepoPattern.MatchString(remote) } -func GetStackroxRepositoryTag(log *logger.Logger) (string, error) { +func GetStackroxRepositoryTag() (string, error) { topLevelDir, err := GetStackRoxTopLevelDir() if err != nil { return "", fmt.Errorf("getting stackrox top level directory: %w", err) diff --git a/internal/helm/helm.go b/internal/helm/helm.go index c60a7642..09c0a171 100644 --- a/internal/helm/helm.go +++ b/internal/helm/helm.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/cli" @@ -47,31 +47,25 @@ var retryableErrors = []string{ "broken pipe", } -type HelmCtx struct { - Ctx context.Context - Log *logger.Logger - Verbose bool -} - // Install installs or upgrades a Helm chart idempotently. -func Install(helmCtx HelmCtx, opts InstallOptions) error { +func Install(ctx context.Context, opts InstallOptions) error { if opts.ChartPath == "" && (opts.RepoURL == "" || opts.ChartName == "") { return fmt.Errorf("either ChartPath or RepoURL+ChartName must be set") } - return executeHelmActionWithRetries(helmCtx, "install", - func(helmCtx HelmCtx) error { - return doInstall(helmCtx, opts) + return executeHelmActionWithRetries(ctx, "install", + func(ctx context.Context) error { + return doInstall(ctx, opts) }) } // Uninstall removes a Helm release, ignoring "not found" errors. -func Uninstall(helmCtx HelmCtx, releaseName, namespace string) error { - return executeHelmActionWithRetries(helmCtx, "uninstall", - func(helmCtx HelmCtx) error { - err := doUninstall(helmCtx, releaseName, namespace) +func Uninstall(ctx context.Context, releaseName, namespace string) error { + return executeHelmActionWithRetries(ctx, "uninstall", + func(ctx context.Context) error { + err := doUninstall(ctx, releaseName, namespace) if err != nil && strings.Contains(strings.ToLower(err.Error()), "not found") { - helmCtx.Log.Dimf("Helm release %q not found in namespace %s, skipping uninstall", releaseName, namespace) + log.Dimf("Helm release %q not found in namespace %s, skipping uninstall", releaseName, namespace) return nil } return err @@ -79,12 +73,12 @@ func Uninstall(helmCtx HelmCtx, releaseName, namespace string) error { } // ListByPrefix returns the names of Helm releases whose name starts with the given prefix. -func ListByPrefix(helmCtx HelmCtx, prefix, namespace string) ([]string, error) { +func ListByPrefix(ctx context.Context, prefix, namespace string) ([]string, error) { var releases []string - err := executeHelmActionWithRetries(helmCtx, "list", - func(helmCtx HelmCtx) error { - result, err := doListByPrefix(helmCtx, prefix, namespace) + err := executeHelmActionWithRetries(ctx, "list", + func(ctx context.Context) error { + result, err := doListByPrefix(ctx, prefix, namespace) if err != nil { return err } @@ -98,20 +92,20 @@ func ListByPrefix(helmCtx HelmCtx, prefix, namespace string) ([]string, error) { return releases, nil } -func executeHelmActionWithRetries(helmCtx HelmCtx, actionName string, helmAction func(helmCtx HelmCtx) error) error { +func executeHelmActionWithRetries(ctx context.Context, actionName string, helmAction func(ctx context.Context) error) error { var err error for attempt := 1; attempt <= maxAttempts; attempt++ { if attempt > 1 { waitTime := time.Duration(attempt) * retryDelay - helmCtx.Log.Infof("Retrying helm %s (attempt %d/%d) after %v...", actionName, attempt, maxAttempts, waitTime) + log.Infof("Retrying helm %s (attempt %d/%d) after %v...", actionName, attempt, maxAttempts, waitTime) select { - case <-helmCtx.Ctx.Done(): - return fmt.Errorf("helm %s aborted while waiting to retry: %w", actionName, helmCtx.Ctx.Err()) + case <-ctx.Done(): + return fmt.Errorf("helm %s aborted while waiting to retry: %w", actionName, ctx.Err()) case <-time.After(waitTime): } } - err = helmAction(helmCtx) + err = helmAction(ctx) if err == nil { return nil } @@ -120,13 +114,13 @@ func executeHelmActionWithRetries(helmCtx HelmCtx, actionName string, helmAction return fmt.Errorf("helm %s failed: %w", actionName, err) } - helmCtx.Log.Warningf("Transient error during helm %s: %v", actionName, err) + log.Warningf("Transient error during helm %s: %v", actionName, err) } return fmt.Errorf("helm %s failed after %d attempts: %w", actionName, maxAttempts, err) } -func doInstall(helmCtx HelmCtx, opts InstallOptions) error { - cfg, err := newActionConfig(helmCtx, opts.Namespace) +func doInstall(ctx context.Context, opts InstallOptions) error { + cfg, err := newActionConfig(ctx, opts.Namespace) if err != nil { return err } @@ -135,29 +129,25 @@ func doInstall(helmCtx HelmCtx, opts InstallOptions) error { if err != nil { return err } - if helmCtx.Verbose { - helmCtx.Log.Dimf("resolved Helm chart for release %s as %q", opts.ReleaseName, chartPath) - } + log.Debugf("resolved Helm chart for release %s as %q", opts.ReleaseName, chartPath) - if helmCtx.Verbose { - if opts.ChartPath != "" { - helmCtx.Log.Dimf("installing Helm chart from directory %q as release %s into namespace %s", - opts.ChartPath, opts.ReleaseName, opts.Namespace) - } else { - helmCtx.Log.Dimf("installing Helm chart %s/%s:%s as release %s into namespace %s", - opts.RepoURL, opts.ChartName, opts.ChartVersion, opts.ReleaseName, opts.Namespace) - } + if opts.ChartPath != "" { + log.Debugf("installing Helm chart from directory %q as release %s into namespace %s", + opts.ChartPath, opts.ReleaseName, opts.Namespace) + } else { + log.Debugf("installing Helm chart %s/%s:%s as release %s into namespace %s", + opts.RepoURL, opts.ChartName, opts.ChartVersion, opts.ReleaseName, opts.Namespace) } status := releaseStatus(cfg, opts.ReleaseName) if status.IsPending() || status == release.StatusFailed { - helmCtx.Log.Warningf("Helm release %s is in state %q, forcing uninstall before reinstall", opts.ReleaseName, status) + log.Warningf("Helm release %s is in state %q, forcing uninstall before reinstall", opts.ReleaseName, status) uninstall := action.NewUninstall(cfg) if _, err := uninstall.Run(opts.ReleaseName); err != nil && !strings.Contains(strings.ToLower(err.Error()), "not found") { return fmt.Errorf("cleaning up stuck release %s: %w", opts.ReleaseName, err) } } else if status == release.StatusDeployed { - return doUpgrade(helmCtx, cfg, opts, chartPath) + return doUpgrade(ctx, cfg, opts, chartPath) } install := action.NewInstall(cfg) @@ -170,15 +160,13 @@ func doInstall(helmCtx HelmCtx, opts InstallOptions) error { return fmt.Errorf("loading chart from %q: %w", chartPath, err) } - _, err = install.RunWithContext(helmCtx.Ctx, chart, opts.Values) + _, err = install.RunWithContext(ctx, chart, opts.Values) return err } -func doUpgrade(helmCtx HelmCtx, cfg *action.Configuration, opts InstallOptions, chartPath string) error { - if helmCtx.Verbose { - helmCtx.Log.Dimf("a Helm release named %s already exists in namespace %s, conducting upgrade", - opts.ReleaseName, opts.Namespace) - } +func doUpgrade(ctx context.Context, cfg *action.Configuration, opts InstallOptions, chartPath string) error { + log.Debugf("a Helm release named %s already exists in namespace %s, conducting upgrade", + opts.ReleaseName, opts.Namespace) upgrade := action.NewUpgrade(cfg) upgrade.Namespace = opts.Namespace upgrade.Wait = false @@ -188,27 +176,25 @@ func doUpgrade(helmCtx HelmCtx, cfg *action.Configuration, opts InstallOptions, return fmt.Errorf("loading chart from %q: %w", chartPath, err) } - _, err = upgrade.RunWithContext(helmCtx.Ctx, opts.ReleaseName, chart, opts.Values) + _, err = upgrade.RunWithContext(ctx, opts.ReleaseName, chart, opts.Values) return err } -func doUninstall(helmCtx HelmCtx, releaseName, namespace string) error { - cfg, err := newActionConfig(helmCtx, namespace) +func doUninstall(ctx context.Context, releaseName, namespace string) error { + cfg, err := newActionConfig(ctx, namespace) if err != nil { return err } - if helmCtx.Verbose { - helmCtx.Log.Dimf("uninstalling Helm release %s from namespace %s", releaseName, namespace) - } + log.Debugf("uninstalling Helm release %s from namespace %s", releaseName, namespace) uninstall := action.NewUninstall(cfg) _, err = uninstall.Run(releaseName) return err } -func doListByPrefix(helmCtx HelmCtx, prefix, namespace string) ([]string, error) { - cfg, err := newActionConfig(helmCtx, namespace) +func doListByPrefix(ctx context.Context, prefix, namespace string) ([]string, error) { + cfg, err := newActionConfig(ctx, namespace) if err != nil { return nil, err } @@ -241,15 +227,13 @@ func isRetryable(err error) bool { return false } -func newActionConfig(helmCtx HelmCtx, namespace string) (*action.Configuration, error) { +func newActionConfig(ctx context.Context, namespace string) (*action.Configuration, error) { settings := cli.New() settings.SetNamespace(namespace) cfg := new(action.Configuration) logFunc := func(format string, v ...any) { - if helmCtx.Verbose { - helmCtx.Log.Dimf("[helm] "+format, v...) - } + log.Debugf("[helm] "+format, v...) } if err := cfg.Init(settings.RESTClientGetter(), namespace, helmDriver, logFunc); err != nil { return nil, fmt.Errorf("initializing helm configuration: %w", err) @@ -274,7 +258,7 @@ func resolveChart(opts InstallOptions) (string, error) { // BuildDependencies fetches missing sub-chart dependencies for a local chart directory. // It is a no-op if the chart has no dependencies or all are already present. -func BuildDependencies(log *logger.Logger, chartPath string) error { +func BuildDependencies(chartPath string) error { ch, err := loader.Load(chartPath) if err != nil { return fmt.Errorf("loading chart from %q: %w", chartPath, err) diff --git a/internal/helm/helm_integration_test.go b/internal/helm/helm_integration_test.go index 705526da..59e16437 100644 --- a/internal/helm/helm_integration_test.go +++ b/internal/helm/helm_integration_test.go @@ -7,7 +7,6 @@ import ( "strings" "testing" - "github.com/stackrox/roxie/internal/logger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -18,11 +17,6 @@ const ( func TestInstallAndUninstall(t *testing.T) { ctx := t.Context() - helmCtx := HelmCtx{ - Ctx: ctx, - Log: logger.New(), - Verbose: true, - } namespace := createTestNamespace(t) releaseName := "helm-integ-lifecycle" @@ -31,41 +25,36 @@ func TestInstallAndUninstall(t *testing.T) { ChartPath: testChartPath, Namespace: namespace, } - err := Install(helmCtx, opts) + err := Install(ctx, opts) require.NoError(t, err, "initial install") - releases, err := ListByPrefix(helmCtx, releaseName, namespace) + releases, err := ListByPrefix(ctx, releaseName, namespace) require.NoError(t, err) assert.Len(t, releases, 1, "multiple releases listed") assert.Contains(t, releases, releaseName, "release should exist after install") - err = Install(helmCtx, opts) + err = Install(ctx, opts) require.NoError(t, err, "idempotent re-install") - releases, err = ListByPrefix(helmCtx, releaseName, namespace) + releases, err = ListByPrefix(ctx, releaseName, namespace) require.NoError(t, err) assert.Len(t, releases, 1, "multiple releases listed") assert.Contains(t, releases, releaseName, "release should exist after install") - err = Uninstall(helmCtx, releaseName, namespace) + err = Uninstall(ctx, releaseName, namespace) require.NoError(t, err, "uninstall") - releases, err = ListByPrefix(helmCtx, releaseName, namespace) + releases, err = ListByPrefix(ctx, releaseName, namespace) require.NoError(t, err) assert.NotContains(t, releases, releaseName, "release should be gone after uninstall") assert.Empty(t, releases, "releases still listed") - err = Uninstall(helmCtx, releaseName, namespace) + err = Uninstall(ctx, releaseName, namespace) require.NoError(t, err, "uninstall of already-removed release should succeed") } func TestInstallWithValues(t *testing.T) { ctx := t.Context() - helmCtx := HelmCtx{ - Ctx: ctx, - Log: logger.New(), - Verbose: true, - } namespace := createTestNamespace(t) releaseName := "helm-integ-values" @@ -76,7 +65,7 @@ func TestInstallWithValues(t *testing.T) { Values: map[string]any{"target": "mundo"}, } - err := Install(helmCtx, opts) + err := Install(ctx, opts) require.NoError(t, err) out, err := exec.Command("kubectl", "get", "configmap", releaseName+"-cm", @@ -87,12 +76,7 @@ func TestInstallWithValues(t *testing.T) { func TestListByPrefix_NoMatches(t *testing.T) { ctx := t.Context() - helmCtx := HelmCtx{ - Ctx: ctx, - Log: logger.New(), - Verbose: true, - } - releases, err := ListByPrefix(helmCtx, "nonexistent-prefix-xyz-", "default") + releases, err := ListByPrefix(ctx, "nonexistent-prefix-xyz-", "default") require.NoError(t, err) assert.Empty(t, releases) } diff --git a/internal/helpers/helpers.go b/internal/helpers/helpers.go index c6992e48..4c086058 100644 --- a/internal/helpers/helpers.go +++ b/internal/helpers/helpers.go @@ -6,9 +6,7 @@ import ( "os" "os/exec" "reflect" - "strings" - "github.com/stackrox/roxie/internal/logger" "gopkg.in/yaml.v3" ) @@ -141,19 +139,6 @@ func MapToStruct(m map[string]interface{}, out interface{}) error { return yaml.Unmarshal(bytes, out) } -func LogMultilineYaml(log *logger.Logger, v any) error { - log.Dim("-------------------------") - bytes, err := yaml.Marshal(v) - if err != nil { - return err - } - for line := range strings.SplitSeq(string(bytes), "\n") { - log.Dim(line) - } - log.Dim("-------------------------") - return nil -} - // IsNil uses reflection to reliably check if the provided argument is a Nil pointer. func IsNil(i interface{}) bool { if i == nil { diff --git a/internal/helpers/tag.go b/internal/helpers/tag.go index d22ee73e..2c5e7302 100644 --- a/internal/helpers/tag.go +++ b/internal/helpers/tag.go @@ -11,20 +11,20 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/stackrox/roxie/internal/constants" "github.com/stackrox/roxie/internal/env" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/ocihelper" "github.com/stackrox/roxie/internal/stackroxversions" ) -func LookupMainImageTag(ctx context.Context, log *logger.Logger) (string, error) { +func LookupMainImageTag(ctx context.Context) (string, error) { log.Dim("Checking if main image tag is defined in the environment") if tag := os.Getenv("MAIN_IMAGE_TAG"); tag != "" { log.Infof("Using MAIN_IMAGE_TAG from environment: %s", tag) return tag, nil } log.Dim("Checking if current working directory is checkout of stackrox/stackrox repository") - if env.IsInStackroxRepository(log) { - tag, err := env.GetStackroxRepositoryTag(log) + if env.IsInStackroxRepository() { + tag, err := env.GetStackroxRepositoryTag() if err != nil { log.Dimf("Error retrieving stackrox repository tag: %v", err) return "", err @@ -38,7 +38,7 @@ func LookupMainImageTag(ctx context.Context, log *logger.Logger) (string, error) log.Warning("Alternatively, execute roxie from within the stackrox repository, in which case the currently checked out stackrox tag will be used") log.Dim("Checking what the latest released version tag is") - latestTag, err := LookupLatestTag(ctx, log) + latestTag, err := LookupLatestTag(ctx) if err != nil { return "", fmt.Errorf("looking up latest release tag: %w", err) } @@ -48,7 +48,7 @@ func LookupMainImageTag(ctx context.Context, log *logger.Logger) (string, error) } // Computes the latest image tag for a pullable, released main image. -func LookupLatestTag(ctx context.Context, log *logger.Logger) (string, error) { +func LookupLatestTag(ctx context.Context) (string, error) { const atMost = 5 tags, err := stackroxversions.LookupLatestReleaseTagsViaGitHub(ctx, atMost) @@ -59,7 +59,7 @@ func LookupLatestTag(ctx context.Context, log *logger.Logger) (string, error) { // Verify we have a pullable main image. for _, tag := range tags { mainImage := fmt.Sprintf("%s/main:%s", constants.DefaultRegistry, tag) - if err := ocihelper.VerifyImageExistence(ctx, log, mainImage); err != nil { + if err := ocihelper.VerifyImageExistence(ctx, mainImage); err != nil { var te *transport.Error if errors.As(err, &te) && te.StatusCode == http.StatusNotFound { continue diff --git a/internal/helpers/tag_integration_test.go b/internal/helpers/tag_integration_test.go index 990ddcb2..5384d8cf 100644 --- a/internal/helpers/tag_integration_test.go +++ b/internal/helpers/tag_integration_test.go @@ -14,18 +14,16 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/stackrox/roxie/internal/constants" - "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/ocihelper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestLookupLatestTag_Integration(t *testing.T) { - log := logger.New() ctx, cancel := context.WithTimeout(t.Context(), 2*time.Minute) defer cancel() - tag, err := LookupLatestTag(ctx, log) + tag, err := LookupLatestTag(ctx) require.NoError(t, err) require.NotEmpty(t, tag) @@ -40,12 +38,11 @@ func TestLookupLatestTag_Integration(t *testing.T) { } func TestVerifyImageExistence_NotFound_Integration(t *testing.T) { - log := logger.New() ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second) defer cancel() madeUpImage := fmt.Sprintf("%s/main:99.99.99", constants.DefaultRegistry) - err := ocihelper.VerifyImageExistence(ctx, log, madeUpImage) + err := ocihelper.VerifyImageExistence(ctx, madeUpImage) require.Error(t, err) var te *transport.Error diff --git a/internal/imagecache/imagecache.go b/internal/imagecache/imagecache.go index c8e6d177..fe0dd3ea 100644 --- a/internal/imagecache/imagecache.go +++ b/internal/imagecache/imagecache.go @@ -8,7 +8,7 @@ import ( "path/filepath" "sync" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/ocihelper" "github.com/stackrox/roxie/internal/paths" ) @@ -18,7 +18,6 @@ type ImageCache struct { cacheFile string maxEntries int cache []string - logger *logger.Logger mu sync.Mutex } @@ -28,7 +27,7 @@ type CacheData struct { } // New creates a new ImageCache instance -func New(log *logger.Logger, cacheFile string, maxEntries int) (*ImageCache, error) { +func New(cacheFile string, maxEntries int) (*ImageCache, error) { if cacheFile == "" { cacheDir, err := paths.CacheDir() if err != nil { @@ -44,7 +43,6 @@ func New(log *logger.Logger, cacheFile string, maxEntries int) (*ImageCache, err ic := &ImageCache{ cacheFile: cacheFile, maxEntries: maxEntries, - logger: log, } ic.cache = ic.loadCache() @@ -135,7 +133,7 @@ func (ic *ImageCache) VerifyImagePullable(ctx context.Context, imageRef string) } // Use OCI registry client to verify image accessibility. - err := ocihelper.VerifyImageExistence(ctx, ic.logger, imageRef) + err := ocihelper.VerifyImageExistence(ctx, imageRef) if err == nil { ic.AddToCache(imageRef) return true @@ -153,7 +151,7 @@ func (ic *ImageCache) VerifyImagesPullable(ctx context.Context, images ...string // Skip verification if environment variable is set if skip := os.Getenv("SKIP_IMAGE_VERIFICATION"); skip == "true" || skip == "1" || skip == "yes" { - ic.logger.Infof("Skipping image verification for %d images (SKIP_IMAGE_VERIFICATION=true)", len(images)) + log.Infof("Skipping image verification for %d images (SKIP_IMAGE_VERIFICATION=true)", len(images)) return true } @@ -169,9 +167,9 @@ func (ic *ImageCache) VerifyImagesPullable(ctx context.Context, images ...string // Report cached results immediately if len(cachedImages) > 0 { - ic.logger.Successf("✓ %d images verified from cache", len(cachedImages)) + log.Successf("✓ %d images verified from cache", len(cachedImages)) for _, img := range cachedImages { - ic.logger.Dim(fmt.Sprintf("✓ Image %s (cached)", img)) + log.Dim(fmt.Sprintf("✓ Image %s (cached)", img)) } } @@ -220,25 +218,25 @@ func (ic *ImageCache) VerifyImagesPullable(ctx context.Context, images ...string // Collect results for res := range results { if res.success { - ic.logger.Dim(fmt.Sprintf("✓ Image %s verified", res.img)) + log.Dim(fmt.Sprintf("✓ Image %s verified", res.img)) } else { - ic.logger.Errorf("✗ Image %s failed: %s", res.img, res.errMsg) + log.Errorf("✗ Image %s failed: %s", res.img, res.errMsg) failedImages = append(failedImages, res.img) } } } if len(failedImages) > 0 { - ic.logger.Errorf("Failed to verify %d images:", len(failedImages)) + log.Errorf("Failed to verify %d images:", len(failedImages)) for _, img := range failedImages { - ic.logger.Errorf(" - %s", img) + log.Errorf(" - %s", img) } return false } cachedCount := len(cachedImages) verifiedCount := len(uncachedImages) - len(failedImages) - ic.logger.Successf("✓ All %d images verified successfully (%d cached, %d verified)", + log.Successf("✓ All %d images verified successfully (%d cached, %d verified)", len(images), cachedCount, verifiedCount) return true diff --git a/internal/imagecache/imagecache_test.go b/internal/imagecache/imagecache_test.go index 25aeebcf..b22850e0 100644 --- a/internal/imagecache/imagecache_test.go +++ b/internal/imagecache/imagecache_test.go @@ -6,7 +6,6 @@ import ( "path/filepath" "testing" - "github.com/stackrox/roxie/internal/logger" "github.com/stretchr/testify/require" ) @@ -14,8 +13,7 @@ func TestImageCacheLoadSaveRoundtrip(t *testing.T) { tmpDir := t.TempDir() cachePath := filepath.Join(tmpDir, ".roxie.image_cache") - log := logger.New() - c, err := New(log, cachePath, 20) + c, err := New(cachePath, 20) require.NoError(t, err, "creating ImageCache failed") if len(c.cache) != 0 { @@ -30,7 +28,7 @@ func TestImageCacheLoadSaveRoundtrip(t *testing.T) { } // Reopen cache and verify persistence - c2, err := New(log, cachePath, 20) + c2, err := New(cachePath, 20) require.NoError(t, err, "creating ImageCache failed") if !c2.IsCached("quay.io/example/app:1") { t.Error("Image should be cached after reopening") @@ -52,8 +50,7 @@ func TestImageCacheHandlesOldFormat(t *testing.T) { t.Fatalf("Failed to write cache file: %v", err) } - log := logger.New() - c, err := New(log, cachePath, 20) + c, err := New(cachePath, 20) require.NoError(t, err, "creating ImageCache failed") if !c.IsCached("a") { @@ -68,9 +65,8 @@ func TestImageCacheMaxEntries(t *testing.T) { tmpDir := t.TempDir() cachePath := filepath.Join(tmpDir, ".roxie.image_cache") - log := logger.New() maxEntries := 5 - c, err := New(log, cachePath, maxEntries) + c, err := New(cachePath, maxEntries) require.NoError(t, err, "creating ImageCache failed") // Add more than maxEntries @@ -92,8 +88,7 @@ func TestImageCacheMoveToEnd(t *testing.T) { tmpDir := t.TempDir() cachePath := filepath.Join(tmpDir, ".roxie.image_cache") - log := logger.New() - c, err := New(log, cachePath, 5) + c, err := New(cachePath, 5) require.NoError(t, err, "creating ImageCache failed") c.AddToCache("image1") diff --git a/internal/k8s/kubectl.go b/internal/k8s/kubectl.go index b0695f56..25f87bd1 100644 --- a/internal/k8s/kubectl.go +++ b/internal/k8s/kubectl.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) // KubectlOptions contains options for running kubectl commands @@ -28,7 +28,7 @@ type KubectlResult struct { } // RunKubectl executes a kubectl command with automatic retries on transient errors -func RunKubectl(ctx context.Context, log *logger.Logger, opts KubectlOptions) (KubectlResult, error) { +func RunKubectl(ctx context.Context, opts KubectlOptions) (KubectlResult, error) { if opts.MaxAttempts <= 0 { opts.MaxAttempts = 3 } @@ -63,9 +63,7 @@ func RunKubectl(ctx context.Context, log *logger.Logger, opts KubectlOptions) (K for attempt := 1; attempt <= opts.MaxAttempts; attempt++ { if attempt > 1 { waitTime := time.Duration(attempt*opts.RetryDelay) * time.Second - if log != nil { - log.Infof("Retrying kubectl command (attempt %d/%d) after %v...", attempt, opts.MaxAttempts, waitTime) - } + log.Infof("Retrying kubectl command (attempt %d/%d) after %v...", attempt, opts.MaxAttempts, waitTime) time.Sleep(waitTime) } @@ -117,9 +115,7 @@ func RunKubectl(ctx context.Context, log *logger.Logger, opts KubectlOptions) (K }, fmt.Errorf("kubectl command failed: %w", err) } - if log != nil { - log.Warningf("Transient error in kubectl command: %s", lastStderr) - } + log.Warningf("Transient error in kubectl command: %s", lastStderr) } return KubectlResult{ diff --git a/internal/k8s/resource.go b/internal/k8s/resource.go index 25489dae..67a04ae5 100644 --- a/internal/k8s/resource.go +++ b/internal/k8s/resource.go @@ -7,7 +7,7 @@ import ( "fmt" "strings" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -20,7 +20,6 @@ var ( // // Parameters: // - ctx: The context for the kubectl command -// - log: Logger for diagnostic output (can be nil for silent operation) // - namespace: The namespace where the resource is located (use "" for cluster-scoped resources) // - resourceType: The resource type (e.g., "pod", "secret", "pvc") // - resourceName: The name of the resource @@ -28,14 +27,14 @@ var ( // Returns: // - *unstructured.Unstructured: The resource as an unstructured object containing all metadata // - error: nil if successful, error otherwise (including ErrResourceNotFound for not found resources) -func RetrieveResourceFromCluster(ctx context.Context, log *logger.Logger, namespace, resourceType, resourceName string) (*unstructured.Unstructured, error) { +func RetrieveResourceFromCluster(ctx context.Context, namespace, resourceType, resourceName string) (*unstructured.Unstructured, error) { // We use --ignore-not-found=true for more reliable distinction between "not found" and other errors. args := []string{"get", resourceType, resourceName, "-o", "json", "--ignore-not-found=true"} if namespace != "" { args = append([]string{"-n", namespace}, args...) } - result, err := RunKubectl(ctx, log, KubectlOptions{ + result, err := RunKubectl(ctx, KubectlOptions{ Args: args, }) @@ -45,17 +44,13 @@ func RetrieveResourceFromCluster(ctx context.Context, log *logger.Logger, namesp return nil, ErrResourceNotFound } if err != nil { - if log != nil { - log.Warningf("Failed to retrieve %s/%s from namespace %s: %v", resourceType, resourceName, namespace, err) - } + log.Warningf("Failed to retrieve %s/%s from namespace %s: %v", resourceType, resourceName, namespace, err) return nil, fmt.Errorf("kubectl get failed: %w", err) } obj := &unstructured.Unstructured{} if err := json.Unmarshal([]byte(result.Stdout), obj); err != nil { - if log != nil { - log.Warningf("Failed to unmarshal %s/%s: %v", resourceType, resourceName, err) - } + log.Warningf("Failed to unmarshal %s/%s: %v", resourceType, resourceName, err) return nil, fmt.Errorf("failed to unmarshal resource JSON: %w", err) } @@ -89,8 +84,8 @@ func ResourceNotOwnedByName(obj *unstructured.Unstructured, ownerName string) bo // Returns: // - string: the label value -- if the label is not found, an empty string is returned without error. // - error: nil if successful, error otherwise (including ErrResourceNotFound for not found resources) -func RetrieveClusterResourceLabel(ctx context.Context, log *logger.Logger, namespace, resourceType, resourceName, label string) (string, error) { - u, err := RetrieveResourceFromCluster(ctx, log, namespace, resourceType, resourceName) +func RetrieveClusterResourceLabel(ctx context.Context, namespace, resourceType, resourceName, label string) (string, error) { + u, err := RetrieveResourceFromCluster(ctx, namespace, resourceType, resourceName) if err != nil { return "", fmt.Errorf("retrieving resource %s/%s from namespace %s: %w", resourceType, resourceName, namespace, err) } diff --git a/internal/k8s/resource_integration_test.go b/internal/k8s/resource_integration_test.go index 13cc7259..0f335aec 100644 --- a/internal/k8s/resource_integration_test.go +++ b/internal/k8s/resource_integration_test.go @@ -15,7 +15,7 @@ func TestRetrieveResourceFromCluster_NotFound(t *testing.T) { resourceType := "pod" resourceName := "i-do-not-exist-0987654321" - obj, err := RetrieveResourceFromCluster(ctx, nil, namespace, resourceType, resourceName) + obj, err := RetrieveResourceFromCluster(ctx, namespace, resourceType, resourceName) assert.Nil(t, obj, "Expected no object to be returned when resource is not found") assert.ErrorIs(t, err, ErrResourceNotFound, "Expected ErrResourceNotFound when resource is not found") } @@ -23,7 +23,7 @@ func TestRetrieveResourceFromCluster_NotFound(t *testing.T) { func TestRetrieveResourceFromCluster_Found(t *testing.T) { ctx := context.Background() - obj, err := RetrieveResourceFromCluster(ctx, nil, "", "namespace", "default") + obj, err := RetrieveResourceFromCluster(ctx, "", "namespace", "default") assert.NotNil(t, obj, "Expected an object to be returned when resource is found") assert.Nil(t, err, "Expected no error to be returned when resource is found") assert.Equal(t, "default", obj.GetName(), "Expected resource name to match") diff --git a/internal/logger/default.go b/internal/logger/default.go new file mode 100644 index 00000000..c8756599 --- /dev/null +++ b/internal/logger/default.go @@ -0,0 +1,31 @@ +package logger + +import ( + "sync/atomic" +) + +var ( + std atomic.Pointer[Logger] +) + +func init() { + std.Store(New()) +} + +func Default() *Logger { return std.Load() } +func SetDefault(l *Logger) *Logger { return std.Swap(l) } +func SetVerbose(v bool) { std.Load().SetVerbose(v) } +func IsVerbose() bool { return std.Load().IsVerbose() } +func Info(msg string) { std.Load().Info(msg) } +func Infof(f string, a ...any) { std.Load().Infof(f, a...) } +func Error(msg string) { std.Load().Error(msg) } +func Errorf(f string, a ...any) { std.Load().Errorf(f, a...) } +func Success(msg string) { std.Load().Success(msg) } +func Successf(f string, a ...any) { std.Load().Successf(f, a...) } +func Warning(msg string) { std.Load().Warning(msg) } +func Warningf(f string, a ...any) { std.Load().Warningf(f, a...) } +func Dim(msg string) { std.Load().Dim(msg) } +func Dimf(f string, a ...any) { std.Load().Dimf(f, a...) } +func Debug(msg string) { std.Load().Debug(msg) } +func Debugf(f string, a ...any) { std.Load().Debugf(f, a...) } +func LogMultilineYaml(v any) { std.Load().LogMultilineYaml(v) } diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 7ec2aa71..1cfa5954 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -1,109 +1,214 @@ package logger import ( + "context" "fmt" "io" + "log/slog" "os" + "strings" "time" "github.com/fatih/color" + "gopkg.in/yaml.v3" ) -// Logger provides timestamped logging functionality +const ( + LevelDim = slog.Level(-2) + LevelSuccess = slog.Level(2) +) + +// Logger wraps slog.Logger with roxie's CLI output style: +// elapsed MM:SS timestamps, per-level coloring, and stdout/stderr routing. type Logger struct { - startTime time.Time - stdout io.Writer - stderr io.Writer + log *slog.Logger + level *slog.LevelVar } -// New creates a new Logger instance +// New creates a new roxie logger. func New() *Logger { - return &Logger{ + return NewWithWriters(os.Stdout, os.Stderr) +} + +// NewWithWriters creates a new roxie logger with configurable writers for stdout/stderr. +func NewWithWriters(stdout, stderr io.Writer) *Logger { + level := &slog.LevelVar{} + level.Set(LevelDim) + + h := &handler{ + level: level, startTime: time.Now(), - stdout: os.Stdout, - stderr: os.Stderr, + stdout: stdout, + stderr: stderr, + } + + return &Logger{ + log: slog.New(h), + level: level, } } -// getTimestamp returns the elapsed time since logger creation in MM:SS format -func (l *Logger) getTimestamp() string { - elapsed := time.Since(l.startTime) - minutes := int(elapsed.Minutes()) - seconds := int(elapsed.Seconds()) % 60 - return fmt.Sprintf("%02d:%02d", minutes, seconds) +// SetVerbose enables verbose mode for the logger, which means that debug-level messages will be emitted. +func (l *Logger) SetVerbose(verbose bool) { + if l == nil { + return + } + if verbose { + l.level.Set(slog.LevelDebug) + } else { + l.level.Set(LevelDim) + } } -// Info prints an info message with magenta styling +// IsVerbose returns true if verbose mode is enabled. +func (l *Logger) IsVerbose() bool { + return l != nil && l.level.Level() <= slog.LevelDebug +} + +// Info prints an info message with magenta styling. func (l *Logger) Info(message string) { if l == nil { return } - timestamp := color.GreenString(l.getTimestamp()) - info := color.New(color.FgMagenta, color.Bold).Sprint(message) - fmt.Fprintf(l.stdout, "%s %s\n", timestamp, info) + l.log.Log(context.Background(), slog.LevelInfo, message) } -// Infof prints a formatted info message with magenta styling -func (l *Logger) Infof(format string, args ...interface{}) { +// Infof prints a formatted info message with magenta styling. +func (l *Logger) Infof(format string, args ...any) { l.Info(fmt.Sprintf(format, args...)) } -// Error prints an error message with red styling to stderr +// Error prints an error message with red styling to stderr. func (l *Logger) Error(message string) { if l == nil { return } - timestamp := color.GreenString(l.getTimestamp()) - errMsg := color.New(color.FgRed, color.Bold).Sprint(message) - fmt.Fprintf(l.stderr, "%s %s\n", timestamp, errMsg) + l.log.Log(context.Background(), slog.LevelError, message) } -// Errorf prints a formatted error message with red styling to stderr -func (l *Logger) Errorf(format string, args ...interface{}) { +// Errorf prints a formatted error message with red styling to stderr. +func (l *Logger) Errorf(format string, args ...any) { l.Error(fmt.Sprintf(format, args...)) } -// Success prints a success message with green styling +// Success prints a success message with green styling. func (l *Logger) Success(message string) { if l == nil { return } - timestamp := color.GreenString(l.getTimestamp()) - success := color.New(color.FgGreen, color.Bold).Sprint(message) - fmt.Fprintf(l.stdout, "%s %s\n", timestamp, success) + l.log.Log(context.Background(), LevelSuccess, message) } -// Successf prints a formatted success message with green styling -func (l *Logger) Successf(format string, args ...interface{}) { +// Successf prints a formatted success message with green styling. +func (l *Logger) Successf(format string, args ...any) { l.Success(fmt.Sprintf(format, args...)) } -// Warning prints a warning message with yellow styling +// Warning prints a warning message with yellow styling. func (l *Logger) Warning(message string) { if l == nil { return } - timestamp := color.GreenString(l.getTimestamp()) - warning := color.New(color.FgYellow, color.Bold).Sprint(message) - fmt.Fprintf(l.stdout, "%s %s\n", timestamp, warning) + l.log.Log(context.Background(), slog.LevelWarn, message) } -// Warning prints a formatted warning message with yellow styling -func (l *Logger) Warningf(format string, args ...interface{}) { +// Warningf prints a formatted warning message with yellow styling. +func (l *Logger) Warningf(format string, args ...any) { l.Warning(fmt.Sprintf(format, args...)) } -// Dim prints a dimmed message +// Dim prints a faint message that is always visible. +// For verbose-only output, use Debug/Debugf. func (l *Logger) Dim(message string) { if l == nil { return } - timestamp := color.GreenString(l.getTimestamp()) - dim := color.New(color.Faint).Sprint(message) - fmt.Fprintf(l.stdout, "%s %s\n", timestamp, dim) + l.log.Log(context.Background(), LevelDim, message) } -// Dimf prints a formatted dimmed message -func (l *Logger) Dimf(format string, args ...interface{}) { +// Dimf prints a formatted faint message that is always visible. +func (l *Logger) Dimf(format string, args ...any) { l.Dim(fmt.Sprintf(format, args...)) } + +// Debug prints a faint message only visible in verbose mode. +func (l *Logger) Debug(message string) { + if l == nil { + return + } + l.log.Log(context.Background(), slog.LevelDebug, message) +} + +// Debug prints a formatted faint message only visible in verbose mode. +func (l *Logger) Debugf(format string, args ...any) { + l.Debug(fmt.Sprintf(format, args...)) +} + +// LogMultilineYaml marshals v to YAML and logs it line by line at debug level. +// It is a no-op unless verbose mode is enabled, so callers need no guard. +func (l *Logger) LogMultilineYaml(v any) { + if !l.IsVerbose() { + return + } + bytes, err := yaml.Marshal(v) + if err != nil { + l.Debugf("failed to marshal YAML: %v", err) + return + } + l.Debug("-------------------------") + for line := range strings.SplitSeq(string(bytes), "\n") { + l.Debug(line) + } + l.Debug("-------------------------") +} + +// handler implements slog.Handler with roxie's CLI output format. +type handler struct { + level *slog.LevelVar + startTime time.Time + stdout io.Writer + stderr io.Writer +} + +// Retrurns true if the given log level is enabled for the provided log handler. +func (h *handler) Enabled(_ context.Context, level slog.Level) bool { + return level >= h.level.Level() +} + +func (h *handler) Handle(_ context.Context, r slog.Record) error { + elapsed := time.Since(h.startTime) + if !r.Time.IsZero() { + elapsed = r.Time.Sub(h.startTime) + } + + minutes := int(elapsed.Minutes()) + seconds := int(elapsed.Seconds()) % 60 + timestamp := color.GreenString("%02d:%02d", minutes, seconds) + message := styleForLevel(r.Level).Sprint(r.Message) + + w := h.stdout + if r.Level >= slog.LevelError { + w = h.stderr + } + + fmt.Fprintf(w, "%s %s\n", timestamp, message) + return nil +} + +func (h *handler) WithAttrs(_ []slog.Attr) slog.Handler { return h } +func (h *handler) WithGroup(_ string) slog.Handler { return h } + +func styleForLevel(level slog.Level) *color.Color { + switch { + case level >= slog.LevelError: + return color.New(color.FgRed, color.Bold) + case level >= slog.LevelWarn: + return color.New(color.FgYellow, color.Bold) + case level >= LevelSuccess: + return color.New(color.FgGreen, color.Bold) + case level >= slog.LevelInfo: + return color.New(color.FgMagenta, color.Bold) + default: + return color.New(color.Faint) + } +} diff --git a/internal/manifest/manifest.go b/internal/manifest/manifest.go index ae4b6256..09386cf6 100644 --- a/internal/manifest/manifest.go +++ b/internal/manifest/manifest.go @@ -12,7 +12,7 @@ import ( "github.com/stackrox/roxie/internal/deployer" "github.com/stackrox/roxie/internal/env" "github.com/stackrox/roxie/internal/k8s" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/types" "gopkg.in/yaml.v3" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -58,7 +58,7 @@ func manifestToSecret(m RoxieManifest) (*unstructured.Unstructured, error) { return secret, nil } -func CreateManifestSecretOnCluster(ctx context.Context, log *logger.Logger, m RoxieManifest) error { +func CreateManifestSecretOnCluster(ctx context.Context, m RoxieManifest) error { secret, err := manifestToSecret(m) if err != nil { return fmt.Errorf("failed to convert manifest to secret: %w", err) @@ -69,11 +69,11 @@ func CreateManifestSecretOnCluster(ctx context.Context, log *logger.Logger, m Ro return fmt.Errorf("failed to marshal manifest secret: %w", err) } - if err := ensureRoxieNamespace(ctx, log); err != nil { + if err := ensureRoxieNamespace(ctx); err != nil { return fmt.Errorf("failed to ensure roxie namespace exists: %w", err) } - _, err = k8s.RunKubectl(ctx, log, k8s.KubectlOptions{ + _, err = k8s.RunKubectl(ctx, k8s.KubectlOptions{ Args: []string{"apply", "-f", "-"}, Stdin: bytes.NewReader(yamlData), }) @@ -85,8 +85,8 @@ func CreateManifestSecretOnCluster(ctx context.Context, log *logger.Logger, m Ro return nil } -func LoadManifestSecret(ctx context.Context, log *logger.Logger) (*RoxieManifest, error) { - obj, err := k8s.RetrieveResourceFromCluster(ctx, log, roxieNamespace, "secret", manifestSecretName) +func LoadManifestSecret(ctx context.Context) (*RoxieManifest, error) { + obj, err := k8s.RetrieveResourceFromCluster(ctx, roxieNamespace, "secret", manifestSecretName) if err != nil { return nil, fmt.Errorf("failed to retrieve manifest secret: %w", err) } @@ -112,23 +112,23 @@ func LoadManifestSecret(ctx context.Context, log *logger.Logger) (*RoxieManifest return &m, nil } -func DeleteManifestSecret(ctx context.Context, log *logger.Logger) error { - _, err := k8s.RunKubectl(ctx, log, k8s.KubectlOptions{ +func DeleteManifestSecret(ctx context.Context) error { + _, err := k8s.RunKubectl(ctx, k8s.KubectlOptions{ Args: []string{"delete", "secret", manifestSecretName, "-n", roxieNamespace, "--ignore-not-found=true"}, IgnoreErrors: true, }) return err } -func DeleteRoxieNamespace(ctx context.Context, log *logger.Logger) error { - _, err := k8s.RunKubectl(ctx, log, k8s.KubectlOptions{ +func DeleteRoxieNamespace(ctx context.Context) error { + _, err := k8s.RunKubectl(ctx, k8s.KubectlOptions{ Args: []string{"delete", "namespace", roxieNamespace, "--ignore-not-found=true"}, IgnoreErrors: true, }) return err } -func ensureRoxieNamespace(ctx context.Context, log *logger.Logger) error { +func ensureRoxieNamespace(ctx context.Context) error { ns := &unstructured.Unstructured{ Object: map[string]any{ "apiVersion": "v1", @@ -145,7 +145,7 @@ func ensureRoxieNamespace(ctx context.Context, log *logger.Logger) error { if err != nil { return fmt.Errorf("failed to marshal namespace: %w", err) } - _, err = k8s.RunKubectl(ctx, log, k8s.KubectlOptions{ + _, err = k8s.RunKubectl(ctx, k8s.KubectlOptions{ Args: []string{"apply", "-f", "-"}, Stdin: bytes.NewReader(nsYAML), }) @@ -156,10 +156,10 @@ func ensureRoxieNamespace(ctx context.Context, log *logger.Logger) error { return nil } -func ManifestToCentralDeploymentInfo(ctx context.Context, log *logger.Logger, tempDir string, m *RoxieManifest) (types.CentralDeploymentInfo, error) { +func ManifestToCentralDeploymentInfo(ctx context.Context, tempDir string, m *RoxieManifest) (types.CentralDeploymentInfo, error) { roxieEnv := m.RoxieEnvironment - caCertFile, err := fetchCACertForShell(ctx, log, m.Config.Central.Namespace, tempDir) + caCertFile, err := fetchCACertForShell(ctx, m.Config.Central.Namespace, tempDir) if err != nil { // Nothing we expect to happen, but in any case, don't let the deployment fail here. log.Warningf("Could not fetch CA cert: %v", err) @@ -175,10 +175,10 @@ func ManifestToCentralDeploymentInfo(ctx context.Context, log *logger.Logger, te }, nil } -func fetchCACertForShell(ctx context.Context, log *logger.Logger, centralNamespace, tempDir string) (string, error) { +func fetchCACertForShell(ctx context.Context, centralNamespace, tempDir string) (string, error) { log.Info("Fetching Central CA certificate...") - result, err := k8s.RunKubectl(ctx, log, k8s.KubectlOptions{ + result, err := k8s.RunKubectl(ctx, k8s.KubectlOptions{ Args: []string{"get", "secret", "central-tls", "-n", centralNamespace, "-o", "jsonpath={.data.ca\\.pem}"}, }) if err != nil { diff --git a/internal/manifest/manifest_integration_test.go b/internal/manifest/manifest_integration_test.go index d47afdf1..c8e69fea 100644 --- a/internal/manifest/manifest_integration_test.go +++ b/internal/manifest/manifest_integration_test.go @@ -9,7 +9,6 @@ import ( "github.com/stackrox/roxie/internal/deployer" "github.com/stackrox/roxie/internal/k8s" - "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,10 +16,9 @@ import ( func cleanupRoxieNamespace(t *testing.T) { t.Helper() - log := logger.New() ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() - err := DeleteRoxieNamespace(ctx, log) + err := DeleteRoxieNamespace(ctx) assert.NoError(t, err, "deleting roxie namespace failed") } @@ -28,7 +26,6 @@ func TestCreateAndLoadManifest_Integration(t *testing.T) { t.Cleanup(func() { cleanupRoxieNamespace(t) }) cleanupRoxieNamespace(t) - log := logger.New() ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() @@ -47,10 +44,10 @@ func TestCreateAndLoadManifest_Integration(t *testing.T) { }, } - err := CreateManifestSecretOnCluster(ctx, log, original) + err := CreateManifestSecretOnCluster(ctx, original) require.NoError(t, err) - loaded, err := LoadManifestSecret(ctx, log) + loaded, err := LoadManifestSecret(ctx) require.NoError(t, err) assert.Empty(t, loaded.RoxieEnvironment.RoxCaCertFile) @@ -61,7 +58,6 @@ func TestDeleteManifest_Integration(t *testing.T) { t.Cleanup(func() { cleanupRoxieNamespace(t) }) cleanupRoxieNamespace(t) - log := logger.New() ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() @@ -71,10 +67,10 @@ func TestDeleteManifest_Integration(t *testing.T) { }, } - err := CreateManifestSecretOnCluster(ctx, log, m) + err := CreateManifestSecretOnCluster(ctx, m) require.NoError(t, err) - err = DeleteManifestSecret(ctx, log) + err = DeleteManifestSecret(ctx) assert.NoError(t, err) } @@ -82,17 +78,16 @@ func TestDeleteRoxieNamespace_Integration(t *testing.T) { t.Cleanup(func() { cleanupRoxieNamespace(t) }) cleanupRoxieNamespace(t) - log := logger.New() ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() - err := ensureRoxieNamespace(ctx, log) + err := ensureRoxieNamespace(ctx) require.NoError(t, err) - err = DeleteRoxieNamespace(ctx, log) + err = DeleteRoxieNamespace(ctx) require.NoError(t, err) - _, err = k8s.RunKubectl(ctx, log, k8s.KubectlOptions{ + _, err = k8s.RunKubectl(ctx, k8s.KubectlOptions{ Args: []string{"get", "namespace", roxieNamespace}, }) assert.Error(t, err, "namespace should no longer exist") @@ -102,10 +97,9 @@ func TestLoadManifest_NotFound_Integration(t *testing.T) { t.Cleanup(func() { cleanupRoxieNamespace(t) }) cleanupRoxieNamespace(t) - log := logger.New() ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - _, err := LoadManifestSecret(ctx, log) + _, err := LoadManifestSecret(ctx) assert.Error(t, err) } diff --git a/internal/ocihelper/ocihelper.go b/internal/ocihelper/ocihelper.go index 81856bab..2cc2e02a 100644 --- a/internal/ocihelper/ocihelper.go +++ b/internal/ocihelper/ocihelper.go @@ -15,12 +15,12 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote" mobyclient "github.com/moby/moby/client" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) // VerifyImageExistence verifies that an OCI image is accessible. // Authentication is handled automatically from ~/.docker/config.json or $REGISTRY_AUTH_FILE. -func VerifyImageExistence(ctx context.Context, log *logger.Logger, imageRef string) error { +func VerifyImageExistence(ctx context.Context, imageRef string) error { log.Dimf("Inspecting image %s", imageRef) ref, err := name.ParseReference(imageRef) @@ -42,7 +42,7 @@ func VerifyImageExistence(ctx context.Context, log *logger.Logger, imageRef stri // ExtractManifestsFromImage extracts the /manifests/ directory from an operator bundle image. // Authentication is handled automatically from ~/.docker/config.json or $REGISTRY_AUTH_FILE. -func ExtractManifestsFromImage(ctx context.Context, log *logger.Logger, imageRef, destDir, containerRuntimeSocket string) error { +func ExtractManifestsFromImage(ctx context.Context, imageRef, destDir, containerRuntimeSocket string) error { tempDir, err := os.MkdirTemp("", "oci-image-") if err != nil { return fmt.Errorf("failed to create temp dir: %w", err) @@ -51,13 +51,13 @@ func ExtractManifestsFromImage(ctx context.Context, log *logger.Logger, imageRef log.Dimf("Using temporary directory: %s", tempDir) - img, err := assureImageExistsLocally(ctx, log, imageRef, containerRuntimeSocket) + img, err := assureImageExistsLocally(ctx, imageRef, containerRuntimeSocket) if err != nil { return err } log.Dim("Extracting /manifests/ directory from image layers...") - if err := extractManifestsFromImage(log, img, tempDir, destDir); err != nil { + if err := extractManifestsFromImage(img, tempDir, destDir); err != nil { return err } @@ -65,7 +65,7 @@ func ExtractManifestsFromImage(ctx context.Context, log *logger.Logger, imageRef return nil } -func assureImageExistsLocally(ctx context.Context, log *logger.Logger, imageRef, containerRuntimeSocket string) (v1.Image, error) { +func assureImageExistsLocally(ctx context.Context, imageRef, containerRuntimeSocket string) (v1.Image, error) { log.Dimf("Fetching image %s", imageRef) ref, err := name.ParseReference(imageRef) @@ -111,7 +111,7 @@ func assureImageExistsLocally(ctx context.Context, log *logger.Logger, imageRef, } // extractManifestsFromImage extracts /manifests/ from an OCI image. -func extractManifestsFromImage(log *logger.Logger, img v1.Image, tempExtractDir, destDir string) error { +func extractManifestsFromImage(img v1.Image, tempExtractDir, destDir string) error { layers, err := img.Layers() if err != nil { return fmt.Errorf("failed to get image layers: %w", err) @@ -122,7 +122,7 @@ func extractManifestsFromImage(log *logger.Logger, img v1.Image, tempExtractDir, // Extract all layers into tempExtractDir for i, layer := range layers { log.Dimf("Extracting layer %d/%d...", i+1, len(layers)) - if err := extractLayerToDir(log, layer, tempExtractDir); err != nil { + if err := extractLayerToDir(layer, tempExtractDir); err != nil { return fmt.Errorf("failed to extract layer %d: %w", i+1, err) } } @@ -139,18 +139,18 @@ func extractManifestsFromImage(log *logger.Logger, img v1.Image, tempExtractDir, } // extractLayerToDir extracts a single image layer to a directory. -func extractLayerToDir(log *logger.Logger, layer v1.Layer, destDir string) error { +func extractLayerToDir(layer v1.Layer, destDir string) error { rc, err := layer.Uncompressed() if err != nil { return fmt.Errorf("failed to get layer contents: %w", err) } defer rc.Close() - return extractTarToDir(log, rc, destDir) + return extractTarToDir(rc, destDir) } // extractTarToDir extracts an uncompressed tar stream to a directory. -func extractTarToDir(log *logger.Logger, r io.Reader, destDir string) error { +func extractTarToDir(r io.Reader, destDir string) error { // Open a Root directory to prevent path traversal attacks. root, err := os.OpenRoot(destDir) if err != nil { diff --git a/internal/ocihelper/ocihelper_integration_test.go b/internal/ocihelper/ocihelper_integration_test.go index 15d4af12..667d5bd6 100644 --- a/internal/ocihelper/ocihelper_integration_test.go +++ b/internal/ocihelper/ocihelper_integration_test.go @@ -9,8 +9,6 @@ import ( "strings" "testing" "time" - - "github.com/stackrox/roxie/internal/logger" ) func TestExtractManifestsFromImage_Integration(t *testing.T) { @@ -23,11 +21,10 @@ func TestExtractManifestsFromImage_Integration(t *testing.T) { } defer os.RemoveAll(destDir) - log := logger.New() ctx := context.Background() t.Logf("Extracting manifests from %s", bundleImage) - err = ExtractManifestsFromImage(ctx, log, bundleImage, destDir, "") + err = ExtractManifestsFromImage(ctx, bundleImage, destDir, "") if err != nil { t.Fatalf("ExtractManifestsFromImage failed: %v", err) } @@ -85,12 +82,11 @@ func TestExtractManifestsFromImage_Integration(t *testing.T) { func TestVerifyImageExistence_Integration(t *testing.T) { bundleImage := "quay.io/rhacs-eng/stackrox-operator-bundle:v4.10.0" - log := logger.New() ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() t.Logf("Verifying image %s exists", bundleImage) - err := VerifyImageExistence(ctx, log, bundleImage) + err := VerifyImageExistence(ctx, bundleImage) if err != nil { t.Fatalf("VerifyImageExistence failed: %v", err) } @@ -101,12 +97,11 @@ func TestVerifyImageExistence_Integration(t *testing.T) { func TestVerifyImageExistence_NonExistent_Integration(t *testing.T) { nonExistentImage := "quay.io/rhacs-eng/this-image-does-not-exist:v999.999.999" - log := logger.New() ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() t.Logf("Verifying image %s does not exist", nonExistentImage) - err := VerifyImageExistence(ctx, log, nonExistentImage) + err := VerifyImageExistence(ctx, nonExistentImage) if err == nil { t.Fatal("Expected VerifyImageExistence to fail for non-existent image, but it succeeded") } diff --git a/internal/portforward/portforward.go b/internal/portforward/portforward.go index 410dede5..880911fb 100644 --- a/internal/portforward/portforward.go +++ b/internal/portforward/portforward.go @@ -7,22 +7,20 @@ import ( "syscall" "time" - "github.com/stackrox/roxie/internal/logger" + log "github.com/stackrox/roxie/internal/logger" ) // Manager manages a kubectl port-forward subprocess and exposes a localhost endpoint type Manager struct { kubectl string - logger *logger.Logger proc *exec.Cmd localPort int } // New creates a new PortForwardManager -func New(kubectl string, log *logger.Logger) *Manager { +func New(kubectl string) *Manager { return &Manager{ kubectl: kubectl, - logger: log, localPort: 0, } } @@ -110,7 +108,7 @@ func (m *Manager) Start(namespace, serviceName string, remotePort, preferredLoca m.proc = cmd m.localPort = localPort endpoint := fmt.Sprintf("127.0.0.1:%d", localPort) - m.logger.Successf("✓ Port-forward active at https://%s", endpoint) + log.Successf("✓ Port-forward active at https://%s", endpoint) return endpoint, nil } @@ -155,7 +153,7 @@ func (m *Manager) StartDetached(namespace, serviceName string, remotePort, prefe } endpoint := fmt.Sprintf("127.0.0.1:%d", localPort) - m.logger.Successf("✓ Detached port-forward active at https://%s (pid %d)", endpoint, pid) + log.Successf("✓ Detached port-forward active at https://%s (pid %d)", endpoint, pid) return endpoint, pid, nil } diff --git a/tests/e2e/addons_test.go b/tests/e2e/addons_test.go index 220ee576..6ed9bdc8 100644 --- a/tests/e2e/addons_test.go +++ b/tests/e2e/addons_test.go @@ -7,8 +7,6 @@ import ( "path/filepath" "testing" - "github.com/stackrox/roxie/internal/helm" - "github.com/stackrox/roxie/internal/logger" "github.com/stretchr/testify/require" ) @@ -44,18 +42,12 @@ central: }, commonDeployArgs...) runCommand(t, deployTimeout, nil, args...) - helmCtx := helm.HelmCtx{ - Ctx: t.Context(), - Log: logger.New(), - Verbose: true, - } - verifyCentralInstalled(t, centralNamespace) - verifyHelmReleaseExists(t, helmCtx, "roxie-addon-test-chart", centralNamespace) + verifyHelmReleaseExists(t, t.Context(), "roxie-addon-test-chart", centralNamespace) t.Log("=== Tearing down Central ===") teardownArgs := []string{roxieBinary, "teardown", "--skip-user-config", "central"} runCommand(t, teardownTimeout, nil, teardownArgs...) - verifyHelmReleaseNotExists(t, helmCtx, "roxie-addon-test-chart", centralNamespace) + verifyHelmReleaseNotExists(t, t.Context(), "roxie-addon-test-chart", centralNamespace) } diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index e42d2708..d5f70bda 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/stackrox/roxie/internal/helpers" - "github.com/stackrox/roxie/internal/logger" ) func TestMain(m *testing.M) { @@ -49,10 +48,9 @@ func TestMain(m *testing.M) { } func lookupLatestTag() (string, error) { - log := logger.New() ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() - tag, err := helpers.LookupLatestTag(ctx, log) + tag, err := helpers.LookupLatestTag(ctx) if err != nil { return "", err } diff --git a/tests/e2e/helpers.go b/tests/e2e/helpers.go index 4737ded0..712ffd96 100644 --- a/tests/e2e/helpers.go +++ b/tests/e2e/helpers.go @@ -383,20 +383,20 @@ func testCentralAPI(t *testing.T, endpoint, caCertFile string) { t.Logf("Central at %s responded with status: %d", endpoint, resp.StatusCode) } -func verifyHelmReleaseExists(t *testing.T, helmCtx helm.HelmCtx, releaseName, namespace string) { +func verifyHelmReleaseExists(t *testing.T, ctx context.Context, releaseName, namespace string) { t.Helper() - releases, err := helm.ListByPrefix(helmCtx, releaseName, namespace) + releases, err := helm.ListByPrefix(ctx, releaseName, namespace) require.NoError(t, err, "listing Helm releases in namespace %s", namespace) if !slices.Contains(releases, releaseName) { t.Fatalf("Helm release %s does not exist in namespace %s", releaseName, namespace) } } -func verifyHelmReleaseNotExists(t *testing.T, helmCtx helm.HelmCtx, releaseName, namespace string) { +func verifyHelmReleaseNotExists(t *testing.T, ctx context.Context, releaseName, namespace string) { t.Helper() - releases, err := helm.ListByPrefix(helmCtx, releaseName, namespace) + releases, err := helm.ListByPrefix(ctx, releaseName, namespace) require.NoError(t, err, "listing Helm releases in namespace %s", namespace) if slices.Contains(releases, releaseName) { t.Fatalf("Helm release %s already exists in namespace %s", releaseName, namespace) diff --git a/tests/e2e/mixed_versions_test.go b/tests/e2e/mixed_versions_test.go index 5236dc09..2d6adae5 100644 --- a/tests/e2e/mixed_versions_test.go +++ b/tests/e2e/mixed_versions_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/stackrox/roxie/internal/constants" - "github.com/stackrox/roxie/internal/logger" "github.com/stackrox/roxie/internal/ocihelper" "github.com/stackrox/roxie/internal/stackroxversions" ) @@ -27,11 +26,10 @@ func lookupTwoReleasedTags(t *testing.T) (string, string) { t.Fatalf("Failed to look up release tags: %v", err) } - log := logger.New() var verified []string for _, tag := range tags { mainImage := fmt.Sprintf("%s/main:%s", constants.DefaultRegistry, tag) - if err := ocihelper.VerifyImageExistence(ctx, log, mainImage); err != nil { + if err := ocihelper.VerifyImageExistence(ctx, mainImage); err != nil { continue } verified = append(verified, tag)