From e0c982967984d9e353e6d04accdcefc9f87f9ea6 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Tue, 27 Oct 2026 18:14:15 +0100 Subject: [PATCH 1/2] Make envrc configurable through roxie config --- cmd/deploy.go | 22 +++++++++++++++------- cmd/main.go | 1 - internal/deployer/config.go | 5 +++++ internal/deployer/deploy_via_operator.go | 2 +- internal/deployer/deployer.go | 19 +++++++------------ 5 files changed, 28 insertions(+), 21 deletions(-) diff --git a/cmd/deploy.go b/cmd/deploy.go index 4106850..4089b12 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -52,7 +52,13 @@ Examples: cmd.Flags().StringVar(&shell, "shell", "", "Shell to spawn after Central deployment") - cmd.Flags().StringVar(&envrc, "envrc", "", "Write environment to file instead of spawning sub-shell") + registerFlag(cmd, settings, "envrc", "Write environment to file instead of spawning sub-shell", + withApplyFn("file", func(config *deployer.Config, filename string) error { + config.Roxie.Envrc = new(filename) + return nil + }), + ) + cmd.Flags().StringVar(&imagePreLoadCommand, "image-preload-command", "", `Use custom command for pre-loading images to local cluster. Image can be referenced as $IMAGE. Pre-loading support for Kind and Minikube clusters is built-in. In case this is not sufficient @@ -280,9 +286,6 @@ func runDeploy(cmd *cobra.Command, args []string) error { } defer d.Cleanup() - if envrc != "" { - d.SetEnvrcFile(envrc) - } d.SetConfig(deploySettings) if dryRun { @@ -346,16 +349,21 @@ func runDeploy(cmd *cobra.Command, args []string) error { if !dryRun { roxieEnv := roxieenv.AssembleRoxieEnvironment(d.GetCentralDeploymentInfo()) + // The envrc shall not be persisted as part of the on-cluster manifest. + // Hence we zero it out here prior to applying the manifest to the cluster. + // This code can go once the on-cluster manifest has been reworked completely. + manifestConfig := deploySettings + manifestConfig.Roxie.Envrc = nil m := manifest.RoxieManifest{ RoxieEnvironment: roxieEnv, - Config: deploySettings, + Config: manifestConfig, } if err := manifest.CreateManifestSecretOnCluster(ctx, m); err != nil { log.Warningf("Failed to save roxie manifest: %v", err) } } - if components.IncludesCentral() && envrc == "" { + if components.IncludesCentral() && !deploySettings.Roxie.EnvrcSet() { if err := spawnSubshellForDeployerEnv(deploySettings.Roxie, d); err != nil { return fmt.Errorf("failed to spawn subshell: %w", err) } @@ -488,7 +496,7 @@ func deployValidate(components component.Component, deploySettings *deployer.Con return errors.New("already in a roxie sub-shell (ROXIE_SHELL environment variable is set), please exit the shell and try again") } - if components.IncludesCentral() && !env.RunningInteractively && envrc == "" { + if components.IncludesCentral() && !env.RunningInteractively && !deploySettings.Roxie.EnvrcSet() { return errors.New("running without a controlling terminal requires --envrc to be set") } diff --git a/cmd/main.go b/cmd/main.go index 52acea8..99c35bb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,7 +22,6 @@ var ( // Global flags verbose bool shell string - envrc string dryRun bool skipUserConfig bool diff --git a/internal/deployer/config.go b/internal/deployer/config.go index 1ce40a3..a7f0f9e 100644 --- a/internal/deployer/config.go +++ b/internal/deployer/config.go @@ -88,6 +88,7 @@ type RoxieConfig struct { FeatureFlags map[string]bool `yaml:"featureFlags,omitempty"` ClusterType types.ClusterType `yaml:"clusterType,omitempty"` HAProxy HAProxyConfig `yaml:"haProxy,omitempty"` + Envrc *string `yaml:"envrc,omitempty"` } // UsesCustomRegistry returns whether a custom image registry was configured. @@ -103,6 +104,10 @@ func (c *RoxieConfig) KonfluxImagesEnabled() bool { return c.KonfluxImages != nil && *c.KonfluxImages } +func (c *RoxieConfig) EnvrcSet() bool { + return c.Envrc != nil && *c.Envrc != "" +} + // NewRoxieConfig returns a RoxieConfig with initialized defaults. func NewRoxieConfig() RoxieConfig { return RoxieConfig{ diff --git a/internal/deployer/deploy_via_operator.go b/internal/deployer/deploy_via_operator.go index 8be40e1..59e3ae5 100644 --- a/internal/deployer/deploy_via_operator.go +++ b/internal/deployer/deploy_via_operator.go @@ -780,7 +780,7 @@ func (d *Deployer) configureCentralEndpoint(ctx context.Context) error { } } - if d.envrcFile != "" { + if d.config.Roxie.EnvrcSet() { endpoint, pid, err := d.portForward.StartDetached(d.config.Central.Namespace, serviceName, 443, 8443) if err != nil { return fmt.Errorf("failed to start detached port-forward: %w", err) diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 9c9ad1a..be9e739 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -44,7 +44,6 @@ type Deployer struct { dockerAuth *dockerauth.DockerAuth imageCache *imagecache.ImageCache dockerCreds *dockerauth.Credentials - envrcFile string kubeContext string containerRuntimeSocket string @@ -265,7 +264,7 @@ func New() (*Deployer, error) { // Cleanup cleans up any temporary resources created by the deployer, such as temporary files. func (d *Deployer) Cleanup() { - if d.tempDir != "" && d.envrcFile == "" { + if d.tempDir != "" && !d.config.Roxie.EnvrcSet() { // 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 { @@ -419,9 +418,9 @@ func (d *Deployer) deployCentral(ctx context.Context) error { return err } - if d.envrcFile != "" { - log.Dimf("Writing environment variables to %s", d.envrcFile) - if err := d.writeEnvrcFile(ctx); err != nil { + if d.config.Roxie.EnvrcSet() { + log.Dimf("Writing environment variables to %s", *d.config.Roxie.Envrc) + if err := d.writeEnvrcFile(ctx, *d.config.Roxie.Envrc); err != nil { log.Warningf("Failed to write envrc file: %v", err) } } @@ -644,10 +643,6 @@ func generatePassword() string { return string(password) } -func (d *Deployer) SetEnvrcFile(path string) { - d.envrcFile = path -} - func (d *Deployer) doesResourceExist(ctx context.Context, resourceType, resourceName, namespace string) bool { _, err := k8s.RunKubectl(ctx, k8s.KubectlOptions{ Args: []string{ @@ -769,7 +764,7 @@ func (d *Deployer) cleanupTempDir(path string, description string) { } } -func (d *Deployer) writeEnvrcFile(ctx context.Context) error { +func (d *Deployer) writeEnvrcFile(ctx context.Context, filename string) error { var content strings.Builder for name, val := range roxieenv.AssembleRoxieEnvironment(d.GetCentralDeploymentInfo()).Export() { fmt.Fprintf(&content, "export %s=%q\n", name, val) @@ -779,11 +774,11 @@ func (d *Deployer) writeEnvrcFile(ctx context.Context) error { fmt.Fprintf(&content, "export ROXIE_PORT_FORWARD_PID=%d\n", d.portForwardPID) } - if err := os.WriteFile(d.envrcFile, []byte(content.String()), 0600); err != nil { + if err := os.WriteFile(filename, []byte(content.String()), 0600); err != nil { return fmt.Errorf("failed to write envrc file: %w", err) } - log.Successf("✓ Environment variables written to %s", d.envrcFile) + log.Successf("✓ Environment variables written to %s", filename) return nil } From 48c24debf4f9ac33e40635559a48843ae25427e8 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 28 Oct 2026 16:17:47 +0100 Subject: [PATCH 2/2] Add envrc unit tests --- cmd/deploy_test.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/cmd/deploy_test.go b/cmd/deploy_test.go index d0536c8..d6a3e84 100644 --- a/cmd/deploy_test.go +++ b/cmd/deploy_test.go @@ -237,6 +237,57 @@ central: }, }, + { + name: "envrc via CLI flag", + args: []string{"--envrc", "/tmp/my.envrc"}, + assert: func(t *testing.T, cfg deployer.Config) { + require.NotNil(t, cfg.Roxie.Envrc, "Roxie.Envrc should be set") + assert.Equal(t, "/tmp/my.envrc", *cfg.Roxie.Envrc) + }, + }, + { + name: "envrc via config file", + config: ` +roxie: + envrc: /tmp/from-config.envrc +`, + args: []string{"--config", configFilePath}, + assert: func(t *testing.T, cfg deployer.Config) { + require.NotNil(t, cfg.Roxie.Envrc, "Roxie.Envrc should be set") + assert.Equal(t, "/tmp/from-config.envrc", *cfg.Roxie.Envrc) + }, + }, + { + name: "envrc CLI flag overrides config file", + config: ` +roxie: + envrc: /tmp/from-config.envrc +`, + args: []string{"--config", configFilePath, "--envrc", "/tmp/from-cli.envrc"}, + assert: func(t *testing.T, cfg deployer.Config) { + require.NotNil(t, cfg.Roxie.Envrc, "Roxie.Envrc should be set") + assert.Equal(t, "/tmp/from-cli.envrc", *cfg.Roxie.Envrc) + }, + }, + { + name: "envrc CLI flag can clear config file value", + config: ` +roxie: + envrc: /tmp/from-config.envrc +`, + args: []string{"--config", configFilePath, "--envrc", ""}, + assert: func(t *testing.T, cfg deployer.Config) { + require.NotNil(t, cfg.Roxie.Envrc, "Roxie.Envrc should be set (to empty)") + assert.Empty(t, *cfg.Roxie.Envrc) + }, + }, + { + name: "envrc not set by default", + args: []string{"--tag", "4.7.0"}, + assert: func(t *testing.T, cfg deployer.Config) { + assert.Nil(t, cfg.Roxie.Envrc, "Roxie.Envrc should not be set") + }, + }, { name: "set expressions can be used", args: []string{"--set", "roxie.version=0.99.1", "--set", "central.deployTimeout=4m", "--set", "securedCluster.spec.clusterName=foo"},