Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"fmt"

"dario.cat/mergo"
"github.com/stackrox/roxie/internal/deployer"
)

func assembleConfigForCommand(configBase *deployer.Config, configFromArgs deployer.Config, skipUserConfig bool) (deployer.Config, error) {
var config deployer.Config
if configBase == nil {
// Start with default configuration.
config = deployer.DefaultConfig()
} else {
config = *configBase
}

// Apply user config on top (overriding defaults).
if !skipUserConfig {
if err := tryApplyUserDefaults(globalLogger, &config); err != nil {
return deployer.Config{}, fmt.Errorf("applying user config: %w", err)
}
}

// Apply changes from arg parsing.
if err := mergo.Merge(&config, &configFromArgs, mergo.WithOverride, mergo.WithoutDereference); err != nil {
return deployer.Config{}, fmt.Errorf("applying config patches from command line argument: %w", err)
}

return config, nil
}
67 changes: 4 additions & 63 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import (
"errors"
"fmt"
"os"
"reflect"
"time"

"dario.cat/mergo"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/stackrox/roxie/internal/clusterdefaults"
Expand All @@ -22,7 +20,6 @@ import (
"github.com/stackrox/roxie/internal/stackroxversions"
"github.com/stackrox/roxie/internal/types"
"gopkg.in/yaml.v3"
"helm.sh/helm/v3/pkg/strvals"
)

var (
Expand Down Expand Up @@ -95,27 +92,6 @@ this flag can be used to tell roxie how to pre-load images for the current clust
}),
)

registerFlag(cmd, settings, "config", "Path to YAML config file",
withShortName("c"),
withApplyFn("filename", func(config *deployer.Config, filename string) error {
if filename == "-" {
filename = "/dev/stdin"
}
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("failed to read config file %q: %w", filename, err)
}
var configFromFile deployer.Config
if err := yaml.Unmarshal(data, &configFromFile); err != nil {
return fmt.Errorf("failed to unmarshal config file %q: %w", filename, err)
}
if err := mergo.Merge(config, configFromFile, mergo.WithOverride, mergo.WithoutDereference); err != nil {
return fmt.Errorf("merging config file %q into deployer Config: %w", filename, err)
}
return nil
}),
)

registerFlag(cmd, settings, "exposure", "Central exposure backend (loadbalancer, none)",
withApplyFn("exposure", func(config *deployer.Config, val string) error {
var exposure types.Exposure
Expand All @@ -139,31 +115,6 @@ this flag can be used to tell roxie how to pre-load images for the current clust
}),
)

registerFlag(cmd, settings, "set", "Set expressions, e.g. securedCluster.spec.clusterName=sensor",
withApplyFn("set-expression", func(config *deployer.Config, expr string) error {
unstructuredPatch, err := strvals.Parse(expr)
if err != nil {
return fmt.Errorf("parsing set expression %q: %w", expr, err)
}
if _, forbidden := unstructuredPatch["spec"]; forbidden {
return errors.New("set expression must not set top-level 'spec'; prefix with 'central.' or 'securedCluster.'")
}
var patch deployer.Config
if err := helpers.MapToStruct(unstructuredPatch, &patch); err != nil {
return err
}
if reflect.DeepEqual(patch, deployer.Config{}) {
return fmt.Errorf("set expression %q had no effect -- typo?", expr)
}

if err := mergo.Merge(config, &patch, mergo.WithOverride, mergo.WithoutDereference); err != nil {
return fmt.Errorf("merging set-expression %q into deployer Config: %w", expr, err)
}

return nil
}),
)

registerFlag(cmd, settings, "single-namespace", "Deploy all components in a single namespace ('stackrox')",
withNoOptDefVal("true"),
withApplyFnBool(func(config *deployer.Config, val bool) error {
Expand Down Expand Up @@ -264,19 +215,9 @@ func runDeploy(cmd *cobra.Command, args []string) error {
return err
}

// Start with default configuration.
deploySettings := deployer.DefaultConfig()

// Apply user config on top (overriding defaults).
if !skipUserConfig {
if err := tryApplyUserDefaults(globalLogger, &deploySettings); err != nil {
return fmt.Errorf("applying user config: %w", err)
}
}

// Apply changes from arg parsing.
if err := mergo.Merge(&deploySettings, &deploySettingsFromArgs, mergo.WithOverride, mergo.WithoutDereference); err != nil {
return fmt.Errorf("applying config patches from command line argument: %w", err)
deploySettings, err := assembleConfigForCommand(nil, deploySettingsFromArgs, skipUserConfig)
if err != nil {
return err
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
Expand Down Expand Up @@ -396,7 +337,7 @@ func runDeploy(cmd *cobra.Command, args []string) error {
}

if components.IncludesCentral() && envrc == "" {
if err := spawnSubshellForDeployerEnv(d, log); err != nil {
if err := spawnSubshellForDeployerEnv(deploySettings.Roxie, d, log); err != nil {
return fmt.Errorf("failed to spawn subshell: %w", err)
}
}
Expand Down
16 changes: 9 additions & 7 deletions cmd/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,11 @@ central:
if tt.config != "" {
require.NoError(t, os.WriteFile(configFilePath, []byte(tt.config), 0o644))
}
cfg := deployer.NewConfig()
cmd := newDeployCmd(&cfg)
require.NoError(t, cmd.ParseFlags(tt.args))
tt.assert(t, cfg)
deploySettingsFromArgs = deployer.NewConfig() // Need to reset this global after each test.
deployCmd, _, err := rootCmd.Find([]string{"deploy"})
require.NoError(t, err)
require.NoError(t, deployCmd.ParseFlags(tt.args))
tt.assert(t, deploySettingsFromArgs)
})
}
}
Expand All @@ -302,9 +303,10 @@ func TestNewDeployCmd_SetRejectsSpec(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := deployer.NewConfig()
cmd := newDeployCmd(&cfg)
err := cmd.ParseFlags(tt.args)
deploySettingsFromArgs = deployer.NewConfig() // Need to reset this global after each test.
cmd, _, err := rootCmd.Find([]string{"deploy"})
require.NoError(t, err)
err = cmd.ParseFlags(tt.args)
require.Error(t, err)
assert.Contains(t, err.Error(), "spec")
})
Expand Down
13 changes: 12 additions & 1 deletion cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type cliFlag struct {
applyFn func(config *deployer.Config, val string) error
noOptDefVal string
description string
persistent bool
}

type flagOpt func(opts *cliFlag)
Expand Down Expand Up @@ -80,6 +81,12 @@ func withShortName(shortName string) flagOpt {
}
}

func withPersistent() flagOpt {
return func(opts *cliFlag) {
opts.persistent = true
}
}

func registerFlag(cmd *cobra.Command, settings *deployer.Config, longName string, description string, flagOpts ...flagOpt) {
f := cliFlag{
config: settings,
Expand All @@ -89,7 +96,11 @@ func registerFlag(cmd *cobra.Command, settings *deployer.Config, longName string
for _, applyOpt := range flagOpts {
applyOpt(&f)
}
flag := cmd.Flags().VarPF(&f, f.longName, f.shortName, f.description)
flagSet := cmd.Flags()
if f.persistent {
flagSet = cmd.PersistentFlags()
}
flag := flagSet.VarPF(&f, f.longName, f.shortName, f.description)
if f.noOptDefVal != "" {
flag.NoOptDefVal = f.noOptDefVal
}
Expand Down
57 changes: 57 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package main

import (
"errors"
"fmt"
"io"
"os"
"reflect"

"dario.cat/mergo"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/stackrox/roxie/internal/deployer"
"github.com/stackrox/roxie/internal/helpers"
"github.com/stackrox/roxie/internal/logger"
"github.com/stackrox/roxie/internal/paths"
"gopkg.in/yaml.v3"
"helm.sh/helm/v3/pkg/strvals"
)

var (
Expand Down Expand Up @@ -76,6 +81,58 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&dryRun, "dry-run", false, "Do not actually modify cluster")
rootCmd.PersistentFlags().BoolVar(&skipUserConfig, "skip-user-config", false,
fmt.Sprintf("Skips reading of user's configuration (%s)", paths.UserConfigPathString()))
registerFlag(rootCmd, &deploySettingsFromArgs, "config", "Path to YAML config file",
withPersistent(),
withShortName("c"),
withApplyFn("filename", func(config *deployer.Config, filename string) error {
var data []byte
var err error
if filename == "-" {
data, err = io.ReadAll(os.Stdin)
filename = "stdin"
} else {
data, err = os.ReadFile(filename)
}
if err != nil {
return fmt.Errorf("failed to read config from %q: %w", filename, err)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

var configFromFile deployer.Config
if err := yaml.Unmarshal(data, &configFromFile); err != nil {
return fmt.Errorf("failed to unmarshal config from %q: %w", filename, err)
}
if err := mergo.Merge(config, &configFromFile, mergo.WithOverride, mergo.WithoutDereference); err != nil {
return fmt.Errorf("merging config from %q into deployer Config: %w", filename, err)
}
return nil
}),
)
registerFlag(rootCmd, &deploySettingsFromArgs, "set", "Set expressions, e.g. securedCluster.spec.clusterName=sensor",
withPersistent(),
withApplyFn("set-expression", func(config *deployer.Config, expr string) error {
unstructuredPatch, err := strvals.Parse(expr)
if err != nil {
return fmt.Errorf("parsing set expression %q: %w", expr, err)
}
if _, forbidden := unstructuredPatch["spec"]; forbidden {
return errors.New("set expression must not set top-level 'spec'; prefix with 'central.' or 'securedCluster.'")
}
var patch deployer.Config
if err := helpers.MapToStruct(unstructuredPatch, &patch); err != nil {
return err
}
if reflect.DeepEqual(patch, deployer.Config{}) {
return fmt.Errorf("set expression %q had no effect -- typo?", expr)
}

if err := mergo.Merge(config, &patch, mergo.WithOverride, mergo.WithoutDereference); err != nil {
return fmt.Errorf("merging set-expression %q into deployer Config: %w", expr, err)
}

return nil
}),
)

rootCmd.AddCommand(newDeployCmd(&deploySettingsFromArgs))
rootCmd.AddCommand(newTeardownCmd(&deploySettingsFromArgs))
rootCmd.AddCommand(newShellCmd())
Expand Down
12 changes: 9 additions & 3 deletions cmd/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Examples:
roxie shell -- roxctl central whoami
roxie shell -- bash -c 'echo $ROX_ENDPOINT'`,
Run: func(cmd *cobra.Command, args []string) {
err := runShell(cmd, args)
err := runShell(args)
if err != nil {
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok {
// Propagate exit error from the child process.
Expand All @@ -51,7 +51,7 @@ Examples:
return cmd
}

func runShell(cmd *cobra.Command, args []string) error {
func runShell(args []string) error {
log := logger.New()
if err := env.Initialize(log); err != nil {
return err
Expand All @@ -71,6 +71,12 @@ func runShell(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to load roxie manifest: %w", err)
}
log.Dim("roxie manifest loaded")
configBase := m.Config

config, err := assembleConfigForCommand(&configBase, deploySettingsFromArgs, skipUserConfig)
if err != nil {
return err
}

// We need this for the setup of the CA cert.
tempDir, err := os.MkdirTemp("", "roxie-shell-*")
Expand All @@ -84,5 +90,5 @@ func runShell(cmd *cobra.Command, args []string) error {
return fmt.Errorf("extracting central deployment info from manifest: %w", err)
}

return runCommandOrSubshell(centralDeploymentInfo, log, args)
return runCommandOrSubshell(config.Roxie, centralDeploymentInfo, log, args)
}
Loading
Loading