diff --git a/roxctl/central/db/db.go b/roxctl/central/db/db.go index e548b96b58209..6e15a14280f6e 100644 --- a/roxctl/central/db/db.go +++ b/roxctl/central/db/db.go @@ -4,7 +4,9 @@ import ( "time" "github.com/spf13/cobra" + "github.com/stackrox/rox/pkg/env" "github.com/stackrox/rox/roxctl/central/db/backup" + "github.com/stackrox/rox/roxctl/central/db/generate" "github.com/stackrox/rox/roxctl/central/db/restore" "github.com/stackrox/rox/roxctl/common/environment" "github.com/stackrox/rox/roxctl/common/flags" @@ -17,6 +19,9 @@ func Command(cliEnvironment environment.Environment) *cobra.Command { } c.AddCommand(backup.Command(cliEnvironment)) c.AddCommand(restore.V2Command(cliEnvironment)) + if env.PostgresDatastoreEnabled.BooleanSetting() { + c.AddCommand(generate.Command(cliEnvironment)) + } flags.AddTimeoutWithDefault(c, 1*time.Hour) return c } diff --git a/roxctl/central/db/generate/generate.go b/roxctl/central/db/generate/generate.go new file mode 100644 index 0000000000000..27bfe4a88bb9d --- /dev/null +++ b/roxctl/central/db/generate/generate.go @@ -0,0 +1,161 @@ +package generate + +import ( + "net/http" + "os" + "time" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/stackrox/rox/pkg/buildinfo" + "github.com/stackrox/rox/pkg/errorhelpers" + "github.com/stackrox/rox/pkg/images/defaults" + "github.com/stackrox/rox/pkg/mtls" + "github.com/stackrox/rox/pkg/renderer" + "github.com/stackrox/rox/pkg/roxctl" + "github.com/stackrox/rox/pkg/set" + "github.com/stackrox/rox/pkg/zip" + "github.com/stackrox/rox/roxctl/common" + "github.com/stackrox/rox/roxctl/common/environment" + "github.com/stackrox/rox/roxctl/common/flags" + "github.com/stackrox/rox/roxctl/common/logger" + "github.com/stackrox/rox/roxctl/common/zipdownload" +) + +const ( + centralDBCertGeneratePath = "/api/extensions/certgen/centraldb" +) + +var ( + centralDBCertBundle = set.NewFrozenStringSet(mtls.CACertFileName, mtls.CentralDBCertFileName, mtls.CentralDBKeyFileName) +) + +type generateCommand struct { + // Properties that are bound to cobra flags. + config *renderer.Config + + // Properties that are injected or constructed. + env environment.Environment + + // timeout to make Central API call + timeout time.Duration +} + +// Command represents the generate command. +func Command(cliEnvironment environment.Environment) *cobra.Command { + cmd := &generateCommand{config: &cfg, env: cliEnvironment} + + c := &cobra.Command{ + Use: "generate", + Hidden: true, + } + + if !buildinfo.ReleaseBuild { + flags.AddHelmChartDebugSetting(c) + } + c.PersistentFlags().BoolVar(&cmd.config.EnablePodSecurityPolicies, "enable-pod-security-policies", true, "Create PodSecurityPolicy resources (for pre-v1.25 Kubernetes)") + c.PersistentPreRunE = func(*cobra.Command, []string) error { + cmd.construct(c) + return cmd.populateMTLS() + } + + c.AddCommand(k8s(cliEnvironment)) + c.AddCommand(openshift(cliEnvironment)) + + return c +} + +func (cmd *generateCommand) populateMTLS() error { + envLogger := cmd.env.Logger() + envLogger.InfofLn("Populating Central DB Certificate from bundle...") + fileMap, err := zipdownload.GetZipFiles(zipdownload.GetZipOptions{ + Path: centralDBCertGeneratePath, + Method: http.MethodPost, + Timeout: cmd.timeout, + BundleType: "central-db", + ExpandZip: true, + }, envLogger) + if err != nil { + return err + } + err = verifyCentralDBBundleFiles(fileMap) + if err != nil { + return err + } + cmd.config.SecretsByteMap = map[string][]byte{ + "ca.pem": fileMap[mtls.CACertFileName].Content, + "central-db-cert.pem": fileMap[mtls.CentralDBCertFileName].Content, + "central-db-key.pem": fileMap[mtls.CentralDBKeyFileName].Content, + "central-db-password": []byte(renderer.CreatePassword()), + } + return nil +} + +func (cmd *generateCommand) construct(c *cobra.Command) { + cmd.timeout = flags.Timeout(c) +} + +func generateBundleWrapper(config renderer.Config) (*zip.Wrapper, error) { + rendered, err := render(config) + if err != nil { + return nil, err + } + + wrapper := zip.NewWrapper() + wrapper.AddFiles(rendered...) + return wrapper, errors.Wrap(err, "could not get scanner bundle") +} + +func outputZip(envLogger logger.Logger, config renderer.Config) error { + envLogger.InfofLn("Generating Central DB bundle...") + common.LogInfoPsp(envLogger, config.EnablePodSecurityPolicies) + wrapper, err := generateBundleWrapper(config) + if err != nil { + return err + } + if roxctl.InMainImage() { + bytes, err := wrapper.Zip() + if err != nil { + return errors.Wrap(err, "error generating zip file") + } + _, err = os.Stdout.Write(bytes) + if err != nil { + return errors.Wrap(err, "couldn't write zip file") + } + return nil + } + outputPath, err := wrapper.Directory(config.OutputDir) + if err != nil { + return errors.Wrap(err, "error generating directory for Central output") + } + envLogger.InfofLn("Wrote central bundle to %q", outputPath) + return nil +} + +func render(config renderer.Config) ([]*zip.File, error) { + flavor, err := defaults.GetImageFlavorByName(config.K8sConfig.ImageFlavorName, buildinfo.ReleaseBuild) + if err != nil { + return nil, common.ErrInvalidCommandOption.CausedByf("'--%s': %v", flags.ImageDefaultsFlagName, err) + } + + return renderer.RenderCentralDBOnly(config, flavor) +} + +func verifyCentralDBBundleFiles(fm map[string]*zip.File) error { + var errs errorhelpers.ErrorList + + checkList := centralDBCertBundle.Unfreeze() + for k, v := range fm { + if len(v.Content) == 0 { + errs.AddError(errors.Errorf("empty file in Central DB certificate bundle: %s", v.Name)) + } + if !centralDBCertBundle.Contains(k) { + errs.AddError(errors.Errorf("unexpected file in Central DB certificate bundle: %s", k)) + } + checkList.Remove(k) + } + if checkList.Cardinality() != 0 { + errs.AddError(errors.Errorf("missing file(s) in Central DB certificate bundle %s", checkList.ElementsString(","))) + } + return errs.ToError() +} diff --git a/roxctl/central/db/generate/k8s.go b/roxctl/central/db/generate/k8s.go new file mode 100644 index 0000000000000..2633c539e31d9 --- /dev/null +++ b/roxctl/central/db/generate/k8s.go @@ -0,0 +1,89 @@ +package generate + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/stackrox/rox/generated/storage" + "github.com/stackrox/rox/pkg/errox" + "github.com/stackrox/rox/pkg/renderer" + "github.com/stackrox/rox/pkg/roxctl" + "github.com/stackrox/rox/roxctl/common" + "github.com/stackrox/rox/roxctl/common/environment" + "github.com/stackrox/rox/roxctl/common/flags" + "github.com/stackrox/rox/roxctl/common/util" +) + +const ( + defaultCentralDBBundle = "central-db-bundle" +) + +func orchestratorCommand(shortName, longName string) *cobra.Command { + c := &cobra.Command{ + Use: shortName, + Short: shortName, + Long: longName, + RunE: util.RunENoArgs(func(*cobra.Command) error { + return errox.InvalidArgs.New("storage type must be specified") + }), + } + if !roxctl.InMainImage() { + c.PersistentFlags().Var(common.NewOutputDir(&cfg.OutputDir, defaultCentralDBBundle), "output-dir", "the directory to output the deployment bundle to") + } + return c +} + +func k8sBasedOrchestrator(cliEnvironment environment.Environment, k8sConfig *renderer.K8sConfig, shortName, longName string, getClusterType func() (storage.ClusterType, error)) *cobra.Command { + c := orchestratorCommand(shortName, longName) + c.PersistentPreRunE = func(*cobra.Command, []string) error { + clusterType, err := getClusterType() + if err != nil { + return errors.Wrap(err, "determining cluster type") + } + cfg.K8sConfig = k8sConfig + cfg.ClusterType = clusterType + return nil + } + + c.AddCommand(externalVolume(cliEnvironment)) + c.AddCommand(hostPathVolume(cliEnvironment)) + c.AddCommand(noVolume(cliEnvironment)) + + // Adds k8s specific flags + flags.AddImageDefaults(c.PersistentFlags(), &k8sConfig.ImageFlavorName) + + defaultImageHelp := fmt.Sprintf("(if unset, a default will be used according to --%s)", flags.ImageDefaultsFlagName) + c.PersistentFlags().StringVarP(&k8sConfig.CentralDBImage, flags.FlagNameCentralDBImage, "", "", "central-db image to use"+defaultImageHelp) + k8sConfig.EnableCentralDB = true + + return c +} + +func newK8sConfig() *renderer.K8sConfig { + return &renderer.K8sConfig{} +} + +func k8s(cliEnvironment environment.Environment) *cobra.Command { + k8sConfig := newK8sConfig() + return k8sBasedOrchestrator(cliEnvironment, k8sConfig, "k8s", "Kubernetes", func() (storage.ClusterType, error) { return storage.ClusterType_KUBERNETES_CLUSTER, nil }) +} + +func openshift(cliEnvironment environment.Environment) *cobra.Command { + k8sConfig := newK8sConfig() + + var openshiftVersion int + c := k8sBasedOrchestrator(cliEnvironment, k8sConfig, "openshift", "Openshift", func() (storage.ClusterType, error) { + switch openshiftVersion { + case 3: + return storage.ClusterType_OPENSHIFT_CLUSTER, nil + case 4: + return storage.ClusterType_OPENSHIFT4_CLUSTER, nil + default: + return 0, errors.Errorf("invalid OpenShift version %d, supported values are '3' and '4'", openshiftVersion) + } + }) + + c.PersistentFlags().IntVar(&openshiftVersion, "openshift-version", 3, "the OpenShift major version (3 or 4) to deploy on") + return c +} diff --git a/roxctl/central/db/generate/validate.go b/roxctl/central/db/generate/validate.go new file mode 100644 index 0000000000000..b37d6d43c5e36 --- /dev/null +++ b/roxctl/central/db/generate/validate.go @@ -0,0 +1,30 @@ +package generate + +import ( + "github.com/stackrox/rox/pkg/errox" + "github.com/stackrox/rox/pkg/renderer" +) + +var ( + cfg renderer.Config +) + +func validateConfig(c *renderer.Config) error { + if c.HostPath == nil { + return nil + } + return validateHostPathInstance(c.HostPath.DB) +} + +func validateHostPathInstance(instance *renderer.HostPathPersistenceInstance) error { + if instance == nil { + return nil + } + if instance.HostPath == "" { + return errox.InvalidArgs.New("non-empty HostPath must be specified") + } + if (instance.NodeSelectorKey == "") != (instance.NodeSelectorValue == "") { + return errox.InvalidArgs.New("both node selector key and node selector value must be specified when using a hostpath") + } + return nil +} diff --git a/roxctl/central/db/generate/volumes.go b/roxctl/central/db/generate/volumes.go new file mode 100644 index 0000000000000..2eccaa5a772a8 --- /dev/null +++ b/roxctl/central/db/generate/volumes.go @@ -0,0 +1,66 @@ +package generate + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/stackrox/rox/pkg/renderer" + "github.com/stackrox/rox/roxctl/common/environment" +) + +func volumeCommand(name string) *cobra.Command { + return &cobra.Command{ + Use: name, + Short: fmt.Sprintf("adds a %s", name), + Long: fmt.Sprintf(`adds a %s external volume to Central DB`, name), + } +} + +func externalVolume(cliEnvironment environment.Environment) *cobra.Command { + external := &renderer.ExternalPersistence{ + DB: &renderer.ExternalPersistenceInstance{}, + } + c := volumeCommand("pvc") + c.RunE = func(c *cobra.Command, args []string) error { + cfg.External = external + if err := validateConfig(&cfg); err != nil { + return err + } + return outputZip(cliEnvironment.Logger(), cfg) + } + c.Flags().StringVarP(&external.DB.Name, "name", "", "central-db", "external volume name for Central DB") + c.Flags().StringVarP(&external.DB.StorageClass, "storage-class", "", "", "storage class name for Central DB (optional if you have a default StorageClass configured)") + c.Flags().Uint32VarP(&external.DB.Size, "size", "", 100, "external volume size in Gi for Central DB") + return c +} + +func noVolume(cliEnvironment environment.Environment) *cobra.Command { + c := volumeCommand("none") + c.RunE = func(c *cobra.Command, args []string) error { + if err := validateConfig(&cfg); err != nil { + return err + } + return outputZip(cliEnvironment.Logger(), cfg) + } + c.Hidden = true + return c +} + +func hostPathVolume(cliEnvironment environment.Environment) *cobra.Command { + hostpath := &renderer.HostPathPersistence{ + DB: &renderer.HostPathPersistenceInstance{}, + } + c := volumeCommand("hostpath") + c.RunE = func(c *cobra.Command, args []string) error { + cfg.HostPath = hostpath + if err := validateConfig(&cfg); err != nil { + return err + } + return outputZip(cliEnvironment.Logger(), cfg) + } + c.Flags().StringVarP(&hostpath.DB.HostPath, "hostpath", "", "/var/lib/stackrox-central", "path on the host") + c.Flags().StringVarP(&hostpath.DB.NodeSelectorKey, "node-selector-key", "", "", "node selector key (e.g. kubernetes.io/hostname)") + c.Flags().StringVarP(&hostpath.DB.NodeSelectorValue, "node-selector-value", "", "", "node selector value") + + return c +} diff --git a/roxctl/central/generate/k8s.go b/roxctl/central/generate/k8s.go index e5887d05b07bb..df7e6c33fb63d 100644 --- a/roxctl/central/generate/k8s.go +++ b/roxctl/central/generate/k8s.go @@ -14,6 +14,7 @@ import ( "github.com/stackrox/rox/pkg/renderer" "github.com/stackrox/rox/pkg/roxctl" "github.com/stackrox/rox/pkg/utils" + "github.com/stackrox/rox/roxctl/common" "github.com/stackrox/rox/roxctl/common/environment" "github.com/stackrox/rox/roxctl/common/flags" "github.com/stackrox/rox/roxctl/common/util" @@ -21,6 +22,7 @@ import ( const ( noteOpenShift3xCompatibilityMode = `NOTE: Deployment files are generated in OpenShift 3.x compatibility mode. Set the --openshift-version flag to 3 to suppress this note, or to 4 take advantage of OpenShift 4.x features.` + defaultBundlePath = "central-bundle" ) type flagsWrapper struct { @@ -67,7 +69,7 @@ func orchestratorCommand(shortName, longName string) *cobra.Command { }), } if !roxctl.InMainImage() { - c.PersistentFlags().Var(newOutputDir(&cfg.OutputDir), "output-dir", "the directory to output the deployment bundle to") + c.PersistentFlags().Var(common.NewOutputDir(&cfg.OutputDir, defaultBundlePath), "output-dir", "the directory to output the deployment bundle to") } return c } diff --git a/roxctl/central/generate/output_dir.go b/roxctl/common/output_dir.go similarity index 60% rename from roxctl/central/generate/output_dir.go rename to roxctl/common/output_dir.go index e02c585a55748..401a1eb2ef040 100644 --- a/roxctl/central/generate/output_dir.go +++ b/roxctl/common/output_dir.go @@ -1,21 +1,23 @@ -package generate +package common import ( "os" + "github.com/pkg/errors" "github.com/stackrox/rox/pkg/errox" ) -const defaultPath = "central-bundle" - type outputDirWrapper struct { - OutputDir *string + OutputDir *string + defaultDir string } -func newOutputDir(s *string) *outputDirWrapper { - *s = defaultPath +// NewOutputDir generates an output directory wrapper with a default for cobra +func NewOutputDir(s *string, defaultDir string) *outputDirWrapper { + *s = defaultDir return &outputDirWrapper{ - OutputDir: s, + OutputDir: s, + defaultDir: defaultDir, } } @@ -25,10 +27,10 @@ func (o *outputDirWrapper) String() string { func (o *outputDirWrapper) Set(input string) error { if input == "" { - input = defaultPath + input = o.defaultDir } if _, err := os.Stat(input); err != nil && !os.IsNotExist(err) { - return err + return errors.Wrapf(err, "failed to check status of directory %q", input) } else if err == nil { return errox.InvalidArgs.Newf("directory %q already exists. Please specify and new path to ensure expected results", input) } diff --git a/roxctl/common/zipdownload/download_zip.go b/roxctl/common/zipdownload/download_zip.go index 5215f4dd54e1f..15fadb668d29f 100644 --- a/roxctl/common/zipdownload/download_zip.go +++ b/roxctl/common/zipdownload/download_zip.go @@ -15,6 +15,7 @@ import ( "github.com/stackrox/rox/pkg/ioutils" "github.com/stackrox/rox/pkg/roxctl" "github.com/stackrox/rox/pkg/utils" + pkgZip "github.com/stackrox/rox/pkg/zip" "github.com/stackrox/rox/roxctl/common" "github.com/stackrox/rox/roxctl/common/download" "github.com/stackrox/rox/roxctl/common/environment" @@ -152,3 +153,52 @@ func GetZip(opts GetZipOptions, log logger.Logger) error { return extractZipToFolder(contents, size, opts.BundleType, outputDir, log) } + +// GetZipFiles downloads a zip from the given endpoint and returns a slice of zip Files. +func GetZipFiles(opts GetZipOptions, log logger.Logger) (map[string]*pkgZip.File, error) { + resp, err := common.DoHTTPRequestAndCheck200(opts.Path, opts.Timeout, opts.Method, bytes.NewBuffer(opts.Body), log) + if err != nil { + return nil, errors.Wrap(err, "could not download zip") + } + defer utils.IgnoreError(resp.Body.Close) + + buf := ioutils.NewRWBuf(ioutils.RWBufOptions{MemLimit: inMemFileSizeThreshold}) + defer utils.IgnoreError(buf.Close) + + if _, err := io.Copy(buf, resp.Body); err != nil { + return nil, errors.Wrap(err, "error downloading Zip file") + } + + contents, size, err := buf.Contents() + if err != nil { + return nil, errors.Wrap(err, "accessing buffer contents") + } + + zipReader, err := zip.NewReader(contents, size) + if err != nil { + return nil, errors.Wrap(err, "create reader from zip contents") + } + fileMap := make(map[string]*pkgZip.File, len(zipReader.File)) + for _, f := range zipReader.File { + bytes, err := readContents(f) + if err != nil { + return nil, errors.Wrapf(err, "read from zip file %s", f.Name) + } + fileMap[f.Name] = pkgZip.NewFile(f.Name, bytes, pkgZip.Sensitive) + log.InfofLn("%s extracted", f.Name) + } + return fileMap, nil +} + +func readContents(file *zip.File) ([]byte, error) { + rd, err := file.Open() + if err != nil { + return nil, errors.Wrapf(err, "failed to open zipped file %q", file.Name) + } + defer utils.IgnoreError(rd.Close) + bytes, err := io.ReadAll(rd) + if err != nil { + return nil, errors.Wrapf(err, "failed to read content from zip file %q", file.Name) + } + return bytes, nil +} diff --git a/roxctl/help/help.properties b/roxctl/help/help.properties index 26da9f66ed480..f8008eb5c1d43 100644 --- a/roxctl/help/help.properties +++ b/roxctl/help/help.properties @@ -7,6 +7,9 @@ central.cert.long = Download certificate chain for the Central service or its as central.db.backup.short = Create a backup of the StackRox database. central.db.backup.long = Create a backup of the StackRox database (.zip file). You can use it to restore the database. +central.db.generate.short = Generate a Central DB bundle. +central.db.generate.long = Generate a Central DB bundle which contains all required YAML files and scripts to deploy the central DB. + central.db.restore.short = Restore the StackRox database from a previous backup. central.db.restore.long = Restore the StackRox database from a backup (.zip file) that you created by using the `roxctl central db backup` command. diff --git a/roxctl/properties/properties_test.go b/roxctl/properties/properties_test.go index d6558d728f1f9..af2a4d58c429d 100644 --- a/roxctl/properties/properties_test.go +++ b/roxctl/properties/properties_test.go @@ -7,6 +7,7 @@ import ( "github.com/magiconair/properties" "github.com/spf13/cobra" "github.com/stackrox/rox/pkg/buildinfo/testbuildinfo" + "github.com/stackrox/rox/pkg/env" "github.com/stackrox/rox/pkg/features" "github.com/stackrox/rox/roxctl/help" "github.com/stackrox/rox/roxctl/maincommand" @@ -54,6 +55,10 @@ func (s *helpKeysTestSuite) TestNoDanglingHelpKeys() { props.Delete("generate.netpol.short") props.Delete("generate.netpol.long") } + if !env.PostgresDatastoreEnabled.BooleanSetting() { + props.Delete("central.db.generate.short") + props.Delete("central.db.generate.long") + } if len(props.Keys()) != 0 { fmt.Println("Unused help keys: ") diff --git a/tests/roxctl/bats-tests/cluster/centraldb-generate.bats b/tests/roxctl/bats-tests/cluster/centraldb-generate.bats new file mode 100755 index 0000000000000..333b4dea84611 --- /dev/null +++ b/tests/roxctl/bats-tests/cluster/centraldb-generate.bats @@ -0,0 +1,174 @@ +#!/usr/bin/env bats + +load "../helpers.bash" + +output_dir="" + +setup_file() { + echo "Testing roxctl version: '$(roxctl-development version)' with ROX_POSTGRES_DATASTORE=true" >&3 + + command -v cat || skip "Command 'cat' required." + command -v grep || skip "Command 'grep' required." + [[ -n "${API_ENDPOINT}" ]] || fail "Environment variable 'API_ENDPOINT' required" + [[ -n "${ROX_PASSWORD}" ]] || fail "Environment variable 'ROX_PASSWORD' required" + export ROX_POSTGRES_DATASTORE=true +} + +setup() { + output_dir="$(mktemp -d -u)" +} + +teardown() { + rm -rf "${output_dir}" +} + +central_db_generate() { + run roxctl_authenticated central db generate --output-dir "${output_dir}" "$@" + + assert_success +} + +run_central_db_generate_and_check() { + local -r cluster_type="$1";shift + + central_db_generate "${cluster_type}" "$@" + assert_success +} + +assert_number_of_resources() { + local -r cluster_type="$1"; shift + local expected="$1"; shift + local resources_count=$(cat "${output_dir}/central/"*.yaml | grep -c "^apiVersion") || true + if [ "${cluster_type}" = "openshift" ]; then + expected=$((${expected} + 1)) + fi + + [[ "${resources_count}" = "${expected}" ]] || fail "Unexpected number of resources, expected ${expected} actual ${resources_count}" +} + +assert_essential_files() { + assert_file_exist "${output_dir}/README" + assert_file_exist "${output_dir}/scripts/setup.sh" + assert_file_exist "${output_dir}/scripts/docker-auth.sh" + assert_file_exist "${output_dir}/central/01-central-00-db-serviceaccount.yaml" + assert_file_exist "${output_dir}/central/01-central-05-db-tls-secret.yaml" + assert_file_exist "${output_dir}/central/01-central-08-db-configmap.yaml" + assert_file_exist "${output_dir}/central/01-central-08-external-db-configmap.yaml" + assert_file_exist "${output_dir}/central/01-central-10-db-networkpolicy.yaml" + assert_file_exist "${output_dir}/central/01-central-12-central-db.yaml" + if [ "${cluster_type}" = "openshift" ]; then + assert_file_exist "${output_dir}/central/01-central-02-db-security.yaml" + run -0 grep -q "kind: SecurityContextConstraints" "${output_dir}/central/01-central-02-db-security.yaml" + fi +} + +test_generate_hostpath() { + local -r cluster_type="$1";shift + run_central_db_generate_and_check "${cluster_type}" hostpath + + assert_essential_files + assert_number_of_resources "${cluster_type}" 11 + + run -0 grep -q "mountPath: /var/lib/postgresql/data" "${output_dir}/central/01-central-12-central-db.yaml" +} + +test_generate_pvc() { + local -r cluster_type="$1";shift + run_central_db_generate_and_check "${cluster_type}" pvc + + assert_essential_files + assert_number_of_resources "${cluster_type}" 12 + assert_file_exist "${output_dir}/central/01-central-11-db-pvc.yaml" + run -0 grep -q "kind: PersistentVolumeClaim" "${output_dir}/central/01-central-11-db-pvc.yaml" + run -0 grep -q "claimName: central-db" "${output_dir}/central/01-central-12-central-db.yaml" +} + +test_generate_with_image() { + local -r cluster_type="$1";shift + run_central_db_generate_and_check "${cluster_type}" none --central-db-image bats-tests + + assert_essential_files + assert_number_of_resources "${cluster_type}" 11 + run -0 grep -q "bats-tests" "${output_dir}/central/01-central-12-central-db.yaml" +} + +test_generate_with_image_default() { + local -r cluster_type="$1";shift + run_central_db_generate_and_check "${cluster_type}" none --image-defaults=opensource + + assert_essential_files + assert_number_of_resources "${cluster_type}" 11 + assert_file_exist "${output_dir}/central/01-central-02-db-psps.yaml" + run -0 grep -q "image: \"quay.io/stackrox-io" "${output_dir}/central/01-central-12-central-db.yaml" +} + +test_generate_security_policies_false() { + local -r cluster_type="$1";shift + run_central_db_generate_and_check "${cluster_type}" none --enable-pod-security-policies=false + + assert_essential_files + assert_number_of_resources "${cluster_type}" 8 + assert_file_not_exist "${output_dir}/central/01-central-02-db-psps.yaml" + assert_file_not_exist "${output_dir}/central/01-central-11-db-pvc.yaml" +} + +test_generate_default() { + local -r cluster_type="$1";shift + run_central_db_generate_and_check "${cluster_type}" none + + assert_essential_files + assert_number_of_resources "${cluster_type}" 11 + assert_file_exist "${output_dir}/central/01-central-02-db-psps.yaml" + assert_file_not_exist "${output_dir}/central/01-central-11-db-pvc.yaml" + run -0 grep -q "image: \"quay.io/rhacs-eng" "${output_dir}/central/01-central-12-central-db.yaml" +} + +# K8s tests +@test "[central-db-bundle k8s] roxctl central db generate hostpath" { + test_generate_hostpath k8s +} + +@test "[central-db-bundle k8s] roxctl central db generate k8s pvc" { + test_generate_pvc k8s +} + +@test "[central-db-bundle k8s] roxctl central db generate with image" { + test_generate_with_image k8s +} + +@test "[central-db-bundle k8s] roxctl central db generate image-defaults opensource" { + test_generate_with_image_default k8s +} + +@test "[central-db-bundle k8s] roxctl central db generate enable-pod-security-policies false" { + test_generate_security_policies_false k8s +} + +@test "[central-db-bundle k8s] roxctl central db generate default" { + test_generate_default k8s +} + +# Openshift tests +@test "[central-db-bundle openshift] roxctl central db generate hostpath" { + test_generate_hostpath openshift +} + +@test "[central-db-bundle openshift] roxctl central db generate openshift pvc" { + test_generate_pvc openshift +} + +@test "[central-db-bundle openshift] roxctl central db generate with image" { + test_generate_with_image openshift +} + +@test "[central-db-bundle openshift] roxctl central db generate image-defaults opensource" { + test_generate_with_image_default openshift +} + +@test "[central-db-bundle openshift] roxctl central db generate enable-pod-security-policies false" { + test_generate_security_policies_false openshift +} + +@test "[central-db-bundle openshift] roxctl central db generate default" { + test_generate_default openshift +}