From 0631fe2d1ec579e1058a3b62370b5098f5fd8a9a Mon Sep 17 00:00:00 2001 From: Cong Du Date: Fri, 4 Nov 2022 10:59:11 -0700 Subject: [PATCH 01/15] All changes from my branch squashed --- .../helm/stackrox-central/.helmtplignore.htpl | 12 ++ .../01-central-05-db-tls-secret.yaml | 23 +++ .../templates/01-central-05-tls-secret.yaml | 22 --- .../templates/01-central-11-db-pvc.yaml | 2 +- .../templates/01-central-13-deployment.yaml | 12 +- .../templates/_central_setup.tpl | 2 +- .../stackrox-central/templates/_init.tpl.htpl | 1 + pkg/renderer/central_db_test.go | 168 ++++++++++++++++++ pkg/renderer/helm_values.go | 13 +- pkg/renderer/images.go | 3 +- pkg/renderer/kubernetes.go | 15 +- pkg/renderer/mode_string.go | 5 +- pkg/renderer/readme.go | 8 + pkg/renderer/render_new.go | 21 ++- 14 files changed, 263 insertions(+), 44 deletions(-) create mode 100644 image/templates/helm/stackrox-central/templates/01-central-05-db-tls-secret.yaml create mode 100644 pkg/renderer/central_db_test.go diff --git a/image/templates/helm/stackrox-central/.helmtplignore.htpl b/image/templates/helm/stackrox-central/.helmtplignore.htpl index a6aef589813ff..e824fea56cc88 100644 --- a/image/templates/helm/stackrox-central/.helmtplignore.htpl +++ b/image/templates/helm/stackrox-central/.helmtplignore.htpl @@ -10,5 +10,17 @@ templates/* !templates/_*.tpl !templates/01-central-05-tls-secret.yaml templates/* +[< else if eq .RenderMode "centralDBOnly" >] +!templates/_*.tpl +!templates/01-central-*-central-db.yaml +!templates/01-central-*-db-serviceaccount.yaml +!templates/01-central-*-db-psps.yaml +!templates/01-central-*-db-security.yaml +!templates/01-central-*-db-configmap.yaml +!templates/01-central-*-external-db-configmap.yaml +!templates/01-central-*-db-networkpolicy.yaml +!templates/01-central-*-db-pvc.yaml +!templates/01-central-*-db-tls-secret.yaml +templates/* [< end >] templates/keep.yaml diff --git a/image/templates/helm/stackrox-central/templates/01-central-05-db-tls-secret.yaml b/image/templates/helm/stackrox-central/templates/01-central-05-db-tls-secret.yaml new file mode 100644 index 0000000000000..3a3a1fab39bf1 --- /dev/null +++ b/image/templates/helm/stackrox-central/templates/01-central-05-db-tls-secret.yaml @@ -0,0 +1,23 @@ +{{- include "srox.init" . -}} + +{{- if and ._rox.central.db._serviceTLS ._rox._ca }} +apiVersion: v1 +kind: Secret +metadata: + name: central-db-tls + namespace: {{ .Release.Namespace }} + labels: + {{- include "srox.labels" (list . "secret" "central-db-tls") | nindent 4 }} + annotations: + {{- include "srox.annotations" (list . "secret" "central-db-tls") | nindent 4 }} + "helm.sh/hook": "pre-install,pre-upgrade" + "helm.sh/resource-policy": "keep" +type: Opaque +stringData: + ca.pem: | + {{- ._rox._ca.Cert | nindent 4 }} + cert.pem: | + {{- ._rox.central.db._serviceTLS.Cert | nindent 4 }} + key.pem: | + {{- ._rox.central.db._serviceTLS.Key | nindent 4 }} +{{- end }} diff --git a/image/templates/helm/stackrox-central/templates/01-central-05-tls-secret.yaml b/image/templates/helm/stackrox-central/templates/01-central-05-tls-secret.yaml index 27cdb43fb4fcb..1850d46a8218c 100644 --- a/image/templates/helm/stackrox-central/templates/01-central-05-tls-secret.yaml +++ b/image/templates/helm/stackrox-central/templates/01-central-05-tls-secret.yaml @@ -28,25 +28,3 @@ stringData: {{- else if or ._rox.central._serviceTLS ._rox.central._jwtSigner }} {{ include "srox.fail" "Service TLS certificates and/or JWT signer key can only be created/updated if all data AND the service CA are present/specified." }} {{- end }} -{{- if and ._rox.central.db._serviceTLS ._rox._ca }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: central-db-tls - namespace: {{ .Release.Namespace }} - labels: - {{- include "srox.labels" (list . "secret" "central-db-tls") | nindent 4 }} - annotations: - {{- include "srox.annotations" (list . "secret" "central-db-tls") | nindent 4 }} - "helm.sh/hook": "pre-install,pre-upgrade" - "helm.sh/resource-policy": "keep" -type: Opaque -stringData: - ca.pem: | - {{- ._rox._ca.Cert | nindent 4 }} - cert.pem: | - {{- ._rox.central.db._serviceTLS.Cert | nindent 4 }} - key.pem: | - {{- ._rox.central.db._serviceTLS.Key | nindent 4 }} -{{- end }} diff --git a/image/templates/helm/stackrox-central/templates/01-central-11-db-pvc.yaml b/image/templates/helm/stackrox-central/templates/01-central-11-db-pvc.yaml index be19c37de0ee8..62c2ffe08b3d4 100644 --- a/image/templates/helm/stackrox-central/templates/01-central-11-db-pvc.yaml +++ b/image/templates/helm/stackrox-central/templates/01-central-11-db-pvc.yaml @@ -44,7 +44,7 @@ spec: apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: central-db + name: {{ $claimName }} namespace: {{ .Release.Namespace }} labels: {{- include "srox.labels" (list . "persistentvolumeclaim" "central-db") | nindent 4 }} diff --git a/image/templates/helm/stackrox-central/templates/01-central-13-deployment.yaml b/image/templates/helm/stackrox-central/templates/01-central-13-deployment.yaml index 466b0d200ae47..1f86922142070 100644 --- a/image/templates/helm/stackrox-central/templates/01-central-13-deployment.yaml +++ b/image/templates/helm/stackrox-central/templates/01-central-13-deployment.yaml @@ -144,8 +144,6 @@ spec: mountPath: /var/lib/stackrox - name: central-config-volume mountPath: /etc/stackrox - - name: central-external-db-volume - mountPath: /etc/ext-db - name: proxy-config-volume mountPath: /run/secrets/stackrox.io/proxy-config/ readOnly: true @@ -155,6 +153,8 @@ spec: {{- if ._rox.central.db.enabled }} - name: central-db-password mountPath: /run/secrets/stackrox.io/db-password + - name: central-external-db-volume + mountPath: /etc/ext-db {{- end }} {{- range $extraMount := (default list ._rox.central.extraMounts) }} - name: {{ $extraMount.name }} @@ -199,10 +199,6 @@ spec: configMap: name: central-config optional: true - - name: central-external-db-volume - configMap: - name: central-external-db - optional: true - name: proxy-config-volume secret: secretName: proxy-config @@ -214,6 +210,10 @@ spec: - name: central-db-password secret: secretName: central-db-password + - name: central-external-db-volume + configMap: + name: central-external-db + optional: true {{- end }} - name: stackrox-db {{- toYaml ._rox.central.persistence._volumeCfg | nindent 8 }} diff --git a/image/templates/helm/stackrox-central/templates/_central_setup.tpl b/image/templates/helm/stackrox-central/templates/_central_setup.tpl index f158915fd7be1..67217ae816bbf 100644 --- a/image/templates/helm/stackrox-central/templates/_central_setup.tpl +++ b/image/templates/helm/stackrox-central/templates/_central_setup.tpl @@ -94,7 +94,7 @@ or no other persistence backend has been configured yet. */}} {{ if or (not (deepEqual $._rox._configShape.central.db.persistence.persistentVolumeClaim $centralDBCfg.persistence.persistentVolumeClaim)) (not $dbVolumeCfg) }} {{ $dbPVCCfg := $centralDBCfg.persistence.persistentVolumeClaim }} - {{ $_ := include "srox.mergeInto" (list $dbPVCCfg $._rox._defaults.dbPVCDefaults (dict "createClaim" $.Release.IsInstall)) }} + {{ $_ := include "srox.mergeInto" (list $dbPVCCfg $._rox._defaults.dbPVCDefaults (dict "createClaim" (or .Release.IsInstall (eq $._rox._renderMode "centralDBOnly")))) }} {{ $_ = set $dbVolumeCfg "persistentVolumeClaim" (dict "claimName" $dbPVCCfg.claimName) }} {{ if $dbPVCCfg.createClaim }} {{ $_ = set $centralDBCfg.persistence "_pvcCfg" $dbPVCCfg }} diff --git a/image/templates/helm/stackrox-central/templates/_init.tpl.htpl b/image/templates/helm/stackrox-central/templates/_init.tpl.htpl index d97363265b9cf..7a945a06deabd 100644 --- a/image/templates/helm/stackrox-central/templates/_init.tpl.htpl +++ b/image/templates/helm/stackrox-central/templates/_init.tpl.htpl @@ -220,6 +220,7 @@ [<- end >] {{ end }} {{ $_ = set $env "_proxyConfig" $proxyCfg }} +{{ $_ = set $._rox "_renderMode" "[< .RenderMode >]" }} {{/* Central setup. diff --git a/pkg/renderer/central_db_test.go b/pkg/renderer/central_db_test.go new file mode 100644 index 0000000000000..ce10128017ed1 --- /dev/null +++ b/pkg/renderer/central_db_test.go @@ -0,0 +1,168 @@ +package renderer + +import ( + "fmt" + "strings" + "testing" + + v1 "github.com/stackrox/rox/generated/api/v1" + "github.com/stackrox/rox/generated/storage" + "github.com/stackrox/rox/pkg/buildinfo/testbuildinfo" + "github.com/stackrox/rox/pkg/certgen" + "github.com/stackrox/rox/pkg/images/defaults" + flavorUtils "github.com/stackrox/rox/pkg/images/defaults/testutils" + "github.com/stackrox/rox/pkg/k8sutil" + "github.com/stackrox/rox/pkg/mtls" + "github.com/stackrox/rox/pkg/zip" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestRenderCentralDBOnly(t *testing.T) { + suite.Run(t, new(centralDBTestSuite)) +} + +type centralDBTestSuite struct { + suite.Suite + restorer *testbuildinfo.TestBuildTimestampRestorer + testFlavor defaults.ImageFlavor + testCA mtls.CA + centralDBCert *mtls.IssuedCert +} + +func (suite *centralDBTestSuite) SetupSuite() { + suite.T().Setenv("TEST_VERSIONS", "true") + suite.testFlavor = flavorUtils.MakeImageFlavorForTest(suite.T()) + var err error + suite.testCA, err = certgen.GenerateCA() + require.NoError(suite.T(), err) + suite.centralDBCert, err = suite.testCA.IssueCertForSubject(mtls.CentralDBSubject) + require.NoError(suite.T(), err) +} + +func (suite *centralDBTestSuite) TearDownSuite() { + suite.restorer.Restore() +} + +func (suite *centralDBTestSuite) testWithHostPath(t *testing.T, c Config, m mode) { + log.Info("Test host path") + c.HostPath = &HostPathPersistence{ + DB: &HostPathPersistenceInstance{ + HostPath: "/var/lib/stackrox", + }, + } + files, err := render(c, m, suite.testFlavor) + assert.NoError(t, err) + suite.verifyFiles(t, files, false) + + c.HostPath = &HostPathPersistence{ + DB: &HostPathPersistenceInstance{ + HostPath: "/var/lib/stackrox-db", + NodeSelectorKey: "key", + NodeSelectorValue: "value", + }, + } + files, err = render(c, m, suite.testFlavor) + assert.NoError(t, err) + suite.verifyFiles(t, files, false) +} + +func (suite *centralDBTestSuite) verifyFiles(t *testing.T, files []*zip.File, pvc bool) { + fm := make(map[string][]unstructured.Unstructured, len(files)) + for _, f := range files { + if f.Name == "README" || strings.HasSuffix(f.Name, ".sh") { + assert.NotEmpty(t, f.Content) + continue + } + unstructuredObjs, err := k8sutil.UnstructuredFromYAMLMulti(string(f.Content)) + require.NoError(t, err, f.Name) + fm[strings.TrimPrefix(f.Name, "central/")] = unstructuredObjs + } + // Verify secrets overwrite + suite.verifyFile(t, fm, "01-central-05-db-tls-secret.yaml", "Secret", string(suite.testCA.CertPEM()), "stringData", "ca.pem") + suite.verifyFile(t, fm, "01-central-05-db-tls-secret.yaml", "Secret", string(suite.centralDBCert.CertPEM), "stringData", "cert.pem") + suite.verifyFile(t, fm, "01-central-05-db-tls-secret.yaml", "Secret", string(suite.centralDBCert.KeyPEM), "stringData", "key.pem") + // Verify top level resources + suite.verifyFile(t, fm, "01-central-00-db-serviceaccount.yaml", "ServiceAccount", "central-db", "metadata", "name") + suite.verifyFile(t, fm, "01-central-08-db-configmap.yaml", "ConfigMap", "central-db-config", "metadata", "name") + suite.verifyFile(t, fm, "01-central-08-external-db-configmap.yaml", "ConfigMap", "central-external-db", "metadata", "name") + suite.verifyFile(t, fm, "01-central-12-central-db.yaml", "Deployment", "central-db", "metadata", "name") + // Verify Persistent Volume Claim + if pvc { + suite.verifyFile(t, fm, "01-central-11-db-pvc.yaml", "PersistentVolumeClaim", "name", "metadata", "name") + suite.verifyFile(t, fm, "01-central-11-db-pvc.yaml", "PersistentVolumeClaim", "name", "metadata", "name") + } else { + assert.NotContains(t, files, "01-central-11-db-pvc.yaml") + } +} + +func (suite *centralDBTestSuite) verifyFile(t *testing.T, fileMap map[string][]unstructured.Unstructured, fileName string, kind string, value string, fields ...string) { + objs, ok := fileMap[fileName] + require.True(t, ok) + require.GreaterOrEqual(t, len(objs), 1) + for _, obj := range objs { + val, ok, err := unstructured.NestedString(obj.UnstructuredContent(), "kind") + require.NoError(t, err) + require.True(t, ok) + if val == kind { + val, ok, err := unstructured.NestedString(obj.UnstructuredContent(), fields...) + require.NoError(t, err) + require.True(t, ok) + assert.Equal(t, val, value) + return + } + } + assert.Failf(t, "Cannot find kind", kind) +} + +func (suite *centralDBTestSuite) testWithPV(t *testing.T, c Config, m mode) { + log.Info("Test PV") + c.External = &ExternalPersistence{ + DB: &ExternalPersistenceInstance{ + Name: "name", + }, + } + files, err := render(c, m, suite.testFlavor) + assert.NoError(t, err) + suite.verifyFiles(t, files, true) + + c.External = &ExternalPersistence{ + DB: &ExternalPersistenceInstance{ + Name: "name", + StorageClass: "storageClass", + }, + } + files, err = render(c, m, suite.testFlavor) + assert.NoError(t, err) + suite.verifyFiles(t, files, true) +} + +func (suite *centralDBTestSuite) TestRenderCentralDBBundle() { + for _, orch := range []storage.ClusterType{storage.ClusterType_KUBERNETES_CLUSTER, storage.ClusterType_OPENSHIFT_CLUSTER, storage.ClusterType_OPENSHIFT4_CLUSTER} { + suite.T().Run(fmt.Sprintf("DbBundle-%s", orch), func(t *testing.T) { + centralFileMap := make(map[string][]byte, 4) + centralFileMap["central-db-password"] = []byte("Apassword") + centralFileMap["central-db-cert.pem"] = suite.centralDBCert.CertPEM + centralFileMap["central-db-key.pem"] = suite.centralDBCert.KeyPEM + centralFileMap[mtls.CACertFileName] = suite.testCA.CertPEM() + + conf := Config{ + ClusterType: storage.ClusterType_KUBERNETES_CLUSTER, + K8sConfig: &K8sConfig{ + CommonConfig: CommonConfig{ + CentralDBImage: "stackrox/central-db:2.2.11.0-57-g392c0f5bed-dirty", + }, + EnableCentralDB: true, + }, + SecretsByteMap: centralFileMap, + } + conf.K8sConfig.DeploymentFormat = v1.DeploymentFormat_KUBECTL + conf.ClusterType = orch + + suite.testWithHostPath(t, conf, centralDBOnly) + suite.testWithPV(t, conf, centralDBOnly) + }) + } +} diff --git a/pkg/renderer/helm_values.go b/pkg/renderer/helm_values.go index ba934d3a99996..2269ae06aa623 100644 --- a/pkg/renderer/helm_values.go +++ b/pkg/renderer/helm_values.go @@ -4,7 +4,6 @@ import ( "text/template" "github.com/pkg/errors" - "github.com/stackrox/rox/pkg/env" helmTemplate "github.com/stackrox/rox/pkg/helm/template" "github.com/stackrox/rox/pkg/templates" "github.com/stackrox/rox/pkg/zip" @@ -312,16 +311,20 @@ central: htpasswd: | {{- index .SecretsBase64Map "htpasswd" | b64dec | nindent 6 }} {{- end }} - - {{- if .K8sConfig.EnableCentralDB }} + db: {{- if ne (index .SecretsBase64Map "central-db-password") "" }} # Password for securing the communication between Central and its DB. # This password is not relevant to the user (unless for debugging purposes); # it merely acts as a pre-shared, random secret for securing the connection. - db: password: value: {{ index .SecretsBase64Map "central-db-password" | b64dec }} {{- end }} + {{- if ne (index .SecretsBase64Map "central-db-cert.pem") "" }} + serviceTLS: + cert: | + {{- index .SecretsBase64Map "central-db-cert.pem" | b64dec | nindent 8 }} + key: | + {{- index .SecretsBase64Map "central-db-key.pem" | b64dec | nindent 8 }} {{- end }} {{- if ne (index .SecretsBase64Map "jwt-key.pem") "" }} @@ -394,7 +397,7 @@ var ( // two entries, one for `values-public.yaml`, and one for `values-private.yaml`. func renderNewHelmValues(c Config) ([]*zip.File, error) { privateTemplate := privateValuesTemplate - if env.PostgresDatastoreEnabled.BooleanSetting() { + if c.K8sConfig.EnableCentralDB { privateTemplate = privateValuesPostgresTemplate } diff --git a/pkg/renderer/images.go b/pkg/renderer/images.go index 52c2bcb28dada..f232eeb23e8b0 100644 --- a/pkg/renderer/images.go +++ b/pkg/renderer/images.go @@ -3,7 +3,6 @@ package renderer import ( "strings" - "github.com/stackrox/rox/pkg/env" "github.com/stackrox/rox/pkg/images/defaults" "github.com/stackrox/rox/pkg/stringutils" ) @@ -87,7 +86,7 @@ func configureImageOverrides(c *Config, imageFlavor defaults.ImageFlavor) { delete(mainOverrides, "Registry") } imageOverrides["Main"] = mainOverrides - if env.PostgresDatastoreEnabled.BooleanSetting() { + if c.K8sConfig.EnableCentralDB { imageOverrides["CentralDB"] = ComputeImageOverrides(c.K8sConfig.CentralDBImage, registry, imageFlavor.CentralDBImageName, imageFlavor.CentralDBImageTag) } diff --git a/pkg/renderer/kubernetes.go b/pkg/renderer/kubernetes.go index ba8a0e4aaba36..3a333a1127ccf 100644 --- a/pkg/renderer/kubernetes.go +++ b/pkg/renderer/kubernetes.go @@ -34,6 +34,8 @@ const ( centralTLSOnly // scannerTLSOnly renders only the scanner tls secret scannerTLSOnly + // centralDBOnly renders only the central db + centralDBOnly ) func postProcessConfig(c *Config, mode mode, imageFlavor defaults.ImageFlavor) error { @@ -70,10 +72,14 @@ func postProcessConfig(c *Config, mode mode, imageFlavor defaults.ImageFlavor) e c.K8sConfig.Command = "oc" } + if mode == centralDBOnly { + c.K8sConfig.EnableCentralDB = true + } + configureImageOverrides(c, imageFlavor) var err error - if mode == renderAll { + if mode == renderAll || mode == centralDBOnly { c.K8sConfig.Registry, err = kubernetesPkg.GetResolvedRegistry(c.K8sConfig.MainImage) if err != nil { return err @@ -106,7 +112,12 @@ func Render(c Config, imageFlavor defaults.ImageFlavor) ([]*zip.File, error) { // RenderScannerOnly renders the zip files for the scanner based on the given config. func RenderScannerOnly(c Config, imageFlavor defaults.ImageFlavor) ([]*zip.File, error) { - return render(c, scannerOnly, imageFlavor) + return render(c, renderAll, imageFlavor) +} + +// RenderCentralDBOnly renders the zip files for the Central DB +func RenderCentralDBOnly(c Config, imageFlavor defaults.ImageFlavor) ([]*zip.File, error) { + return render(c, centralDBOnly, imageFlavor) } func renderAndExtractSingleFileContents(c Config, mode mode, imageFlavor defaults.ImageFlavor) ([]byte, error) { diff --git a/pkg/renderer/mode_string.go b/pkg/renderer/mode_string.go index 3d429e2e26d6a..c3ace835f528e 100644 --- a/pkg/renderer/mode_string.go +++ b/pkg/renderer/mode_string.go @@ -12,11 +12,12 @@ func _() { _ = x[scannerOnly-1] _ = x[centralTLSOnly-2] _ = x[scannerTLSOnly-3] + _ = x[centralDBOnly-4] } -const _mode_name = "renderAllscannerOnlycentralTLSOnlyscannerTLSOnly" +const _mode_name = "renderAllscannerOnlycentralTLSOnlyscannerTLSOnlycentralDBOnly" -var _mode_index = [...]uint8{0, 9, 20, 34, 48} +var _mode_index = [...]uint8{0, 9, 20, 34, 48, 61} func (i mode) String() string { if i < 0 || i >= mode(len(_mode_index)-1) { diff --git a/pkg/renderer/readme.go b/pkg/renderer/readme.go index 19558fad1f66b..d6afa3a9d2490 100644 --- a/pkg/renderer/readme.go +++ b/pkg/renderer/readme.go @@ -48,6 +48,12 @@ the login page, and log in with username "admin" and the password found in the If you want to run the StackRox Scanner: - Run scanner/scripts/setup.sh - Run {{.K8sConfig.Command}} create -R -f scanner +` + kubectlCentralDBTemplate = ` + - Deploy Central DB + To deploy Postgres Central DB and prepare for Central upgrade: + - Run scripts/setup.sh + - Run ./deploy-central-db.sh ` recommendHelmInstallationTemplate = ` PLEASE NOTE: The recommended way to deploy StackRox is by using Helm. If you have @@ -100,6 +106,8 @@ func instructions(c Config, mode mode) (string, error) { } else if c.K8sConfig.DeploymentFormat == v1.DeploymentFormat_KUBECTL { if mode == scannerOnly { template = kubectlScannerTemplate + } else if mode == centralDBOnly { + template = kubectlCentralDBTemplate } else { template = kubectlInstructionTemplate + kubectlScannerTemplate + recommendHelmInstallationTemplate } diff --git a/pkg/renderer/render_new.go b/pkg/renderer/render_new.go index ee8d47d7069e6..e3d338fa50bb4 100644 --- a/pkg/renderer/render_new.go +++ b/pkg/renderer/render_new.go @@ -24,6 +24,11 @@ var ( kubectlScannerScriptMap = FileNameMap{ "common/setup-scanner.sh": "scanner/scripts/setup.sh", } + + centralDBScriptMap = FileNameMap{ + "common/deploy-central-db.sh.tpl": "deploy-central-db.sh", + "common/setup-central.sh": "scripts/setup.sh", + } ) func renderHelmChart(chartFiles []*loader.BufferedFile, mode mode, valuesFiles []*zip.File) ([]*zip.File, error) { @@ -144,7 +149,7 @@ func renderNewBasicFiles(c Config, mode mode, imageFlavor defaults.ImageFlavor) } func renderAuxiliaryFiles(c Config, mode mode) ([]*zip.File, error) { - if mode != renderAll && mode != scannerOnly { + if mode != renderAll && mode != scannerOnly && mode != centralDBOnly { return nil, nil } @@ -160,7 +165,11 @@ func renderAuxiliaryFiles(c Config, mode mode) ([]*zip.File, error) { return nil, errors.Wrap(err, "loading asset files") } if c.K8sConfig.DeploymentFormat == v1.DeploymentFormat_KUBECTL { - auxFiles = append(auxFiles, withPrefix("scanner/scripts", assets)...) + if mode == centralDBOnly { + auxFiles = append(auxFiles, withPrefix("scripts", assets)...) + } else { + auxFiles = append(auxFiles, withPrefix("scanner/scripts", assets)...) + } if mode == renderAll { auxFiles = append(auxFiles, withPrefix("central/scripts", assets)...) } @@ -169,7 +178,13 @@ func renderAuxiliaryFiles(c Config, mode mode) ([]*zip.File, error) { } if c.K8sConfig.DeploymentFormat == v1.DeploymentFormat_KUBECTL { - scannerScriptFiles, err := RenderFiles(kubectlScannerScriptMap, &c) + var scriptMap *FileNameMap + if mode == centralDBOnly { + scriptMap = ¢ralDBScriptMap + } else { + scriptMap = &kubectlScannerScriptMap + } + scannerScriptFiles, err := RenderFiles(*scriptMap, &c) if err != nil { return nil, errors.Wrap(err, "rendering scanner script files") } From c393666a5eb0fd07ae4e038d1d0354f8c431b959 Mon Sep 17 00:00:00 2001 From: Cong Du Date: Fri, 4 Nov 2022 11:17:26 -0700 Subject: [PATCH 02/15] Add roxctl to generate Central DB bundle --- roxctl/central/db/db.go | 2 + roxctl/central/db/generate/generate.go | 176 ++++++++++++++++++ roxctl/central/db/generate/k8s.go | 93 +++++++++ roxctl/central/db/generate/validate.go | 30 +++ roxctl/central/db/generate/volumes.go | 67 +++++++ roxctl/central/generate/k8s.go | 4 +- .../generate => common}/output_dir.go | 17 +- roxctl/common/zipdownload/download_zip.go | 47 +++++ 8 files changed, 427 insertions(+), 9 deletions(-) create mode 100644 roxctl/central/db/generate/generate.go create mode 100644 roxctl/central/db/generate/k8s.go create mode 100644 roxctl/central/db/generate/validate.go create mode 100644 roxctl/central/db/generate/volumes.go rename roxctl/{central/generate => common}/output_dir.go (68%) diff --git a/roxctl/central/db/db.go b/roxctl/central/db/db.go index e548b96b58209..5a4d872c3b39f 100644 --- a/roxctl/central/db/db.go +++ b/roxctl/central/db/db.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" "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 +18,7 @@ func Command(cliEnvironment environment.Environment) *cobra.Command { } c.AddCommand(backup.Command(cliEnvironment)) c.AddCommand(restore.V2Command(cliEnvironment)) + 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..8663284aad043 --- /dev/null +++ b/roxctl/central/db/generate/generate.go @@ -0,0 +1,176 @@ +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/errox" + "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/utils" + "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 ( + errCreateCentralDBBundleSupported = errox.InvariantViolation.New("server does not support central db bundle functionality") + 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", + Short: "generate Central DB bundle", + } + c.PersistentFlags().Var(&flags.FileMapVar{ + FileMap: &cmd.config.ConfigFileOverrides, + }, "with-config-file", "Use the given local file(s) to override default config files") + utils.Must(c.PersistentFlags().MarkHidden("with-config-file")) + + 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 { + logger := cmd.env.Logger() + logger.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, + }, logger) + 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(logger logger.Logger, config renderer.Config) error { + logger.InfofLn("Generating Central DB bundle...") + common.LogInfoPsp(logger, config.EnablePodSecurityPolicies) + wrapper, err := generateBundleWrapper(config) + if err != nil { + return err + } + var outputPath string + 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") + } + } else { + var err error + outputPath, err = wrapper.Directory(config.OutputDir) + if err != nil { + return errors.Wrap(err, "error generating directory for Central output") + } + } + + logger.InfofLn("Done!") + + if outputPath != "" { + logger.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 0 == len(v.Content) { + 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..30754e0722950 --- /dev/null +++ b/roxctl/central/db/generate/k8s.go @@ -0,0 +1,93 @@ +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 ( + 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.` + 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) { + clusterType := storage.ClusterType_OPENSHIFT_CLUSTER + switch openshiftVersion { + case 0: + cliEnvironment.Logger().InfofLn("%s", noteOpenShift3xCompatibilityMode) + case 3: + case 4: + clusterType = storage.ClusterType_OPENSHIFT4_CLUSTER + default: + return 0, errors.Errorf("invalid OpenShift version %d, supported values are '3' and '4'", openshiftVersion) + } + return clusterType, nil + }) + + c.PersistentFlags().IntVar(&openshiftVersion, "openshift-version", 0, "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..8876f4d2ee966 --- /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..fb9c3f91573cc --- /dev/null +++ b/roxctl/central/db/generate/volumes.go @@ -0,0 +1,67 @@ +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), + Example: "Enter Central volume type", + } +} + +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 68% rename from roxctl/central/generate/output_dir.go rename to roxctl/common/output_dir.go index e02c585a55748..18ade47377546 100644 --- a/roxctl/central/generate/output_dir.go +++ b/roxctl/common/output_dir.go @@ -1,4 +1,4 @@ -package generate +package common import ( "os" @@ -6,16 +6,17 @@ import ( "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,7 +26,7 @@ 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 diff --git a/roxctl/common/zipdownload/download_zip.go b/roxctl/common/zipdownload/download_zip.go index 5215f4dd54e1f..6ec95e78019aa 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,49 @@ 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, err +} + +func readContents(file *zip.File) ([]byte, error) { + rd, err := file.Open() + if err != nil { + return nil, err + } + defer utils.IgnoreError(rd.Close) + + return io.ReadAll(rd) +} From 07f9eeb438640400b0fa4b7619ca6ab51e326cb2 Mon Sep 17 00:00:00 2001 From: Cong Du Date: Fri, 4 Nov 2022 11:53:58 -0700 Subject: [PATCH 03/15] Make generate command hidden and behind feature env --- roxctl/central/db/db.go | 5 ++++- roxctl/central/db/generate/generate.go | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/roxctl/central/db/db.go b/roxctl/central/db/db.go index 5a4d872c3b39f..6e15a14280f6e 100644 --- a/roxctl/central/db/db.go +++ b/roxctl/central/db/db.go @@ -4,6 +4,7 @@ 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" @@ -18,7 +19,9 @@ func Command(cliEnvironment environment.Environment) *cobra.Command { } c.AddCommand(backup.Command(cliEnvironment)) c.AddCommand(restore.V2Command(cliEnvironment)) - c.AddCommand(generate.Command(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 index 8663284aad043..497f940c91ef8 100644 --- a/roxctl/central/db/generate/generate.go +++ b/roxctl/central/db/generate/generate.go @@ -49,8 +49,9 @@ func Command(cliEnvironment environment.Environment) *cobra.Command { cmd := &generateCommand{config: &cfg, env: cliEnvironment} c := &cobra.Command{ - Use: "generate", - Short: "generate Central DB bundle", + Use: "generate", + Short: "generate Central DB bundle", + Hidden: true, } c.PersistentFlags().Var(&flags.FileMapVar{ FileMap: &cmd.config.ConfigFileOverrides, From 73e7dd45d921d25aff375d0b04738714259359f0 Mon Sep 17 00:00:00 2001 From: Cong Du Date: Fri, 4 Nov 2022 14:04:20 -0700 Subject: [PATCH 04/15] Add missing file --- .../templates/common/deploy-central-db.sh.tpl | 47 +++++++++++++++++++ pkg/renderer/central_db_test.go | 2 +- pkg/renderer/kubernetes.go | 2 +- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100755 image/templates/common/deploy-central-db.sh.tpl diff --git a/image/templates/common/deploy-central-db.sh.tpl b/image/templates/common/deploy-central-db.sh.tpl new file mode 100755 index 0000000000000..a14d3c8b27972 --- /dev/null +++ b/image/templates/common/deploy-central-db.sh.tpl @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" +KUBE_COMMAND=${KUBE_COMMAND:-{{.K8sConfig.Command}}} +NAMESPACE="${ROX_NAMESPACE:-stackrox}" + +echo "Deploy Central DB ..." +${KUBE_COMMAND} -n ${NAMESPACE} apply -f "${DIR}/central/." +${KUBE_COMMAND} -n ${NAMESPACE} patch deploy/central -p ' +{ + "spec": { + "template": { + "spec": { + "containers": [ + { + "name": "central", + "volumeMounts": [ + { + "name": "central-db-password", + "mountPath": "/run/secrets/stackrox.io/db-password" + }, + { + "name": "central-external-db-volume", + "mountPath": "/etc/ext-db" + } + ] + } + ], + "volumes": [ + { + "name": "central-db-password", + "secret": { + "secretName": "central-db-password" + } + }, + { + "name": "central-external-db-volume", + "configMap": { + "name": "central-external-db", + "optional": true + } + } + ] + } + } + } +} +' diff --git a/pkg/renderer/central_db_test.go b/pkg/renderer/central_db_test.go index ce10128017ed1..1bc58d2c404f6 100644 --- a/pkg/renderer/central_db_test.go +++ b/pkg/renderer/central_db_test.go @@ -100,7 +100,7 @@ func (suite *centralDBTestSuite) verifyFiles(t *testing.T, files []*zip.File, pv func (suite *centralDBTestSuite) verifyFile(t *testing.T, fileMap map[string][]unstructured.Unstructured, fileName string, kind string, value string, fields ...string) { objs, ok := fileMap[fileName] - require.True(t, ok) + require.True(t, ok, "%s not found", fileName) require.GreaterOrEqual(t, len(objs), 1) for _, obj := range objs { val, ok, err := unstructured.NestedString(obj.UnstructuredContent(), "kind") diff --git a/pkg/renderer/kubernetes.go b/pkg/renderer/kubernetes.go index 3a333a1127ccf..4c4bbb0f08755 100644 --- a/pkg/renderer/kubernetes.go +++ b/pkg/renderer/kubernetes.go @@ -112,7 +112,7 @@ func Render(c Config, imageFlavor defaults.ImageFlavor) ([]*zip.File, error) { // RenderScannerOnly renders the zip files for the scanner based on the given config. func RenderScannerOnly(c Config, imageFlavor defaults.ImageFlavor) ([]*zip.File, error) { - return render(c, renderAll, imageFlavor) + return render(c, scannerOnly, imageFlavor) } // RenderCentralDBOnly renders the zip files for the Central DB From 6c75aa80b67b8bd1865a807d281fbea5f40985c6 Mon Sep 17 00:00:00 2001 From: Cong Du Date: Mon, 7 Nov 2022 15:31:13 -0800 Subject: [PATCH 05/15] add test --- roxctl/central/db/generate/generate.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/roxctl/central/db/generate/generate.go b/roxctl/central/db/generate/generate.go index 497f940c91ef8..68236e70e1f12 100644 --- a/roxctl/central/db/generate/generate.go +++ b/roxctl/central/db/generate/generate.go @@ -15,7 +15,6 @@ import ( "github.com/stackrox/rox/pkg/renderer" "github.com/stackrox/rox/pkg/roxctl" "github.com/stackrox/rox/pkg/set" - "github.com/stackrox/rox/pkg/utils" "github.com/stackrox/rox/pkg/zip" "github.com/stackrox/rox/roxctl/common" "github.com/stackrox/rox/roxctl/common/environment" @@ -53,10 +52,6 @@ func Command(cliEnvironment environment.Environment) *cobra.Command { Short: "generate Central DB bundle", Hidden: true, } - c.PersistentFlags().Var(&flags.FileMapVar{ - FileMap: &cmd.config.ConfigFileOverrides, - }, "with-config-file", "Use the given local file(s) to override default config files") - utils.Must(c.PersistentFlags().MarkHidden("with-config-file")) if !buildinfo.ReleaseBuild { flags.AddHelmChartDebugSetting(c) From 0ced44758755adc0bab930d571bd6a1297024c07 Mon Sep 17 00:00:00 2001 From: Cong Du Date: Mon, 7 Nov 2022 18:48:43 -0800 Subject: [PATCH 06/15] Add bats --- .../cluster/centraldb-generate.bats | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100755 tests/roxctl/bats-tests/cluster/centraldb-generate.bats 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..32211ff18e344 --- /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 k8s 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 +} From 04b9d695b5664fabe2b3f5e218f30c0cbd36ab4f Mon Sep 17 00:00:00 2001 From: Cong Du Date: Mon, 7 Nov 2022 18:59:18 -0800 Subject: [PATCH 07/15] tiny --- tests/roxctl/bats-tests/cluster/centraldb-generate.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/roxctl/bats-tests/cluster/centraldb-generate.bats b/tests/roxctl/bats-tests/cluster/centraldb-generate.bats index 32211ff18e344..333b4dea84611 100755 --- a/tests/roxctl/bats-tests/cluster/centraldb-generate.bats +++ b/tests/roxctl/bats-tests/cluster/centraldb-generate.bats @@ -43,7 +43,7 @@ assert_number_of_resources() { expected=$((${expected} + 1)) fi - [[ "${resources_count}" = "${expected}" ]] || fail "Unexpected number of k8s resources, expected ${expected} actual ${resources_count}" + [[ "${resources_count}" = "${expected}" ]] || fail "Unexpected number of resources, expected ${expected} actual ${resources_count}" } assert_essential_files() { From be6ee336e02eff4150d2d0296b3cbcce8ef0afff Mon Sep 17 00:00:00 2001 From: Cong Du Date: Tue, 8 Nov 2022 09:16:47 -0800 Subject: [PATCH 08/15] Apply suggestions from code review Co-authored-by: dhaus67 --- roxctl/central/db/generate/generate.go | 2 +- roxctl/central/db/generate/k8s.go | 2 +- roxctl/central/db/generate/validate.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/roxctl/central/db/generate/generate.go b/roxctl/central/db/generate/generate.go index 68236e70e1f12..56601eeb2916c 100644 --- a/roxctl/central/db/generate/generate.go +++ b/roxctl/central/db/generate/generate.go @@ -28,7 +28,7 @@ const ( ) var ( - errCreateCentralDBBundleSupported = errox.InvariantViolation.New("server does not support central db bundle functionality") + errCreateCentralDBBundleSupported = errox.InvariantViolation.New("central does not support central db bundle functionality") centralDBCertBundle = set.NewFrozenStringSet(mtls.CACertFileName, mtls.CentralDBCertFileName, mtls.CentralDBKeyFileName) ) diff --git a/roxctl/central/db/generate/k8s.go b/roxctl/central/db/generate/k8s.go index 30754e0722950..e0d53f2910014 100644 --- a/roxctl/central/db/generate/k8s.go +++ b/roxctl/central/db/generate/k8s.go @@ -16,7 +16,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.` + 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 to take advantage of OpenShift 4.x features.` defaultCentralDBBundle = "central-db-bundle" ) diff --git a/roxctl/central/db/generate/validate.go b/roxctl/central/db/generate/validate.go index 8876f4d2ee966..d5e49dc3bb793 100644 --- a/roxctl/central/db/generate/validate.go +++ b/roxctl/central/db/generate/validate.go @@ -21,7 +21,7 @@ func validateHostPathInstance(instance *renderer.HostPathPersistenceInstance) er return nil } if instance.HostPath == "" { - return errox.InvalidArgs.New("Non-empty HostPath must be specified") + 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") From 0831fd404c729dcfd5d68803acc6787f8a3cad2a Mon Sep 17 00:00:00 2001 From: Cong Du Date: Tue, 8 Nov 2022 09:24:45 -0800 Subject: [PATCH 09/15] resolve review comments --- roxctl/central/db/generate/generate.go | 3 ++- roxctl/central/db/generate/validate.go | 2 +- roxctl/central/db/generate/volumes.go | 7 +++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/roxctl/central/db/generate/generate.go b/roxctl/central/db/generate/generate.go index 56601eeb2916c..a898c56869162 100644 --- a/roxctl/central/db/generate/generate.go +++ b/roxctl/central/db/generate/generate.go @@ -49,7 +49,8 @@ func Command(cliEnvironment environment.Environment) *cobra.Command { c := &cobra.Command{ Use: "generate", - Short: "generate Central DB bundle", + Short: "generate a Central DB bundle", + Long: "generate a Central DB bundle which contains all required YAML files and scripts to deploy the central DB", Hidden: true, } diff --git a/roxctl/central/db/generate/validate.go b/roxctl/central/db/generate/validate.go index d5e49dc3bb793..b37d6d43c5e36 100644 --- a/roxctl/central/db/generate/validate.go +++ b/roxctl/central/db/generate/validate.go @@ -24,7 +24,7 @@ func validateHostPathInstance(instance *renderer.HostPathPersistenceInstance) er 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 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 index fb9c3f91573cc..c627b1798bdc5 100644 --- a/roxctl/central/db/generate/volumes.go +++ b/roxctl/central/db/generate/volumes.go @@ -10,10 +10,9 @@ import ( 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), - Example: "Enter Central volume type", + Use: name, + Short: fmt.Sprintf("adds a %s", name), + Long: fmt.Sprintf(`adds a %s external volume to Central DB`, name), } } From 990b118e44a87df874a155f041ab4bb9f8577b22 Mon Sep 17 00:00:00 2001 From: Cong Du Date: Tue, 8 Nov 2022 11:11:08 -0800 Subject: [PATCH 10/15] fix style --- roxctl/central/db/generate/generate.go | 2 +- roxctl/central/db/generate/volumes.go | 6 +++--- roxctl/common/output_dir.go | 3 ++- roxctl/common/zipdownload/download_zip.go | 11 +++++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/roxctl/central/db/generate/generate.go b/roxctl/central/db/generate/generate.go index a898c56869162..99c79f0092988 100644 --- a/roxctl/central/db/generate/generate.go +++ b/roxctl/central/db/generate/generate.go @@ -110,7 +110,7 @@ func generateBundleWrapper(config renderer.Config) (*zip.Wrapper, error) { return wrapper, errors.Wrap(err, "could not get scanner bundle") } -func OutputZip(logger logger.Logger, config renderer.Config) error { +func outputZip(logger logger.Logger, config renderer.Config) error { logger.InfofLn("Generating Central DB bundle...") common.LogInfoPsp(logger, config.EnablePodSecurityPolicies) wrapper, err := generateBundleWrapper(config) diff --git a/roxctl/central/db/generate/volumes.go b/roxctl/central/db/generate/volumes.go index c627b1798bdc5..2eccaa5a772a8 100644 --- a/roxctl/central/db/generate/volumes.go +++ b/roxctl/central/db/generate/volumes.go @@ -26,7 +26,7 @@ func externalVolume(cliEnvironment environment.Environment) *cobra.Command { if err := validateConfig(&cfg); err != nil { return err } - return OutputZip(cliEnvironment.Logger(), cfg) + 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)") @@ -40,7 +40,7 @@ func noVolume(cliEnvironment environment.Environment) *cobra.Command { if err := validateConfig(&cfg); err != nil { return err } - return OutputZip(cliEnvironment.Logger(), cfg) + return outputZip(cliEnvironment.Logger(), cfg) } c.Hidden = true return c @@ -56,7 +56,7 @@ func hostPathVolume(cliEnvironment environment.Environment) *cobra.Command { if err := validateConfig(&cfg); err != nil { return err } - return OutputZip(cliEnvironment.Logger(), cfg) + 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)") diff --git a/roxctl/common/output_dir.go b/roxctl/common/output_dir.go index 18ade47377546..401a1eb2ef040 100644 --- a/roxctl/common/output_dir.go +++ b/roxctl/common/output_dir.go @@ -3,6 +3,7 @@ package common import ( "os" + "github.com/pkg/errors" "github.com/stackrox/rox/pkg/errox" ) @@ -29,7 +30,7 @@ func (o *outputDirWrapper) Set(input string) error { 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 6ec95e78019aa..15fadb668d29f 100644 --- a/roxctl/common/zipdownload/download_zip.go +++ b/roxctl/common/zipdownload/download_zip.go @@ -187,15 +187,18 @@ func GetZipFiles(opts GetZipOptions, log logger.Logger) (map[string]*pkgZip.File fileMap[f.Name] = pkgZip.NewFile(f.Name, bytes, pkgZip.Sensitive) log.InfofLn("%s extracted", f.Name) } - return fileMap, err + return fileMap, nil } func readContents(file *zip.File) ([]byte, error) { rd, err := file.Open() if err != nil { - return nil, err + return nil, errors.Wrapf(err, "failed to open zipped file %q", file.Name) } defer utils.IgnoreError(rd.Close) - - return io.ReadAll(rd) + 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 } From dd2220a5ab504bd5f8daa4e80b738d11af1c7daa Mon Sep 17 00:00:00 2001 From: Cong Du Date: Tue, 8 Nov 2022 13:54:09 -0800 Subject: [PATCH 11/15] move --- roxctl/central/db/generate/generate.go | 2 -- roxctl/help/help.properties | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/roxctl/central/db/generate/generate.go b/roxctl/central/db/generate/generate.go index 99c79f0092988..bc1fd8104715c 100644 --- a/roxctl/central/db/generate/generate.go +++ b/roxctl/central/db/generate/generate.go @@ -49,8 +49,6 @@ func Command(cliEnvironment environment.Environment) *cobra.Command { c := &cobra.Command{ Use: "generate", - Short: "generate a Central DB bundle", - Long: "generate a Central DB bundle which contains all required YAML files and scripts to deploy the central DB", Hidden: true, } 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. From 774ac9246bcad2229347a3555b64fc22a6d22f0d Mon Sep 17 00:00:00 2001 From: Cong Du Date: Tue, 8 Nov 2022 14:42:31 -0800 Subject: [PATCH 12/15] fix test --- roxctl/properties/properties_test.go | 5 +++++ 1 file changed, 5 insertions(+) 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: ") From 64dccec3309c7355bb7ded90c0a205531311b84e Mon Sep 17 00:00:00 2001 From: Cong Du Date: Wed, 9 Nov 2022 09:19:17 -0800 Subject: [PATCH 13/15] Update roxctl/central/db/generate/k8s.go Co-authored-by: Tomasz Janiszewski --- roxctl/central/db/generate/k8s.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roxctl/central/db/generate/k8s.go b/roxctl/central/db/generate/k8s.go index e0d53f2910014..434f8be55566d 100644 --- a/roxctl/central/db/generate/k8s.go +++ b/roxctl/central/db/generate/k8s.go @@ -75,17 +75,17 @@ func openshift(cliEnvironment environment.Environment) *cobra.Command { var openshiftVersion int c := k8sBasedOrchestrator(cliEnvironment, k8sConfig, "openshift", "Openshift", func() (storage.ClusterType, error) { - clusterType := storage.ClusterType_OPENSHIFT_CLUSTER switch openshiftVersion { case 0: cliEnvironment.Logger().InfofLn("%s", noteOpenShift3xCompatibilityMode) + return storage.ClusterType_OPENSHIFT_CLUSTER, nil case 3: + return storage.ClusterType_OPENSHIFT_CLUSTER, nil case 4: - clusterType = storage.ClusterType_OPENSHIFT4_CLUSTER + return storage.ClusterType_OPENSHIFT4_CLUSTER, nil default: return 0, errors.Errorf("invalid OpenShift version %d, supported values are '3' and '4'", openshiftVersion) } - return clusterType, nil }) c.PersistentFlags().IntVar(&openshiftVersion, "openshift-version", 0, "the OpenShift major version (3 or 4) to deploy on") From 88b938b8b358dbea038715379a089a2b0e1d8315 Mon Sep 17 00:00:00 2001 From: Cong Du Date: Wed, 9 Nov 2022 10:00:50 -0800 Subject: [PATCH 14/15] Update roxctl/central/db/generate/generate.go Co-authored-by: Tomasz Janiszewski --- roxctl/central/db/generate/generate.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/roxctl/central/db/generate/generate.go b/roxctl/central/db/generate/generate.go index bc1fd8104715c..afe2320018268 100644 --- a/roxctl/central/db/generate/generate.go +++ b/roxctl/central/db/generate/generate.go @@ -115,7 +115,6 @@ func outputZip(logger logger.Logger, config renderer.Config) error { if err != nil { return err } - var outputPath string if roxctl.InMainImage() { bytes, err := wrapper.Zip() if err != nil { @@ -125,20 +124,13 @@ func outputZip(logger logger.Logger, config renderer.Config) error { if err != nil { return errors.Wrap(err, "couldn't write zip file") } - } else { - var err error - outputPath, err = wrapper.Directory(config.OutputDir) - if err != nil { - return errors.Wrap(err, "error generating directory for Central output") - } + return nil } - - logger.InfofLn("Done!") - - if outputPath != "" { - logger.InfofLn("Wrote central bundle to %q", outputPath) + outputPath, err := wrapper.Directory(config.OutputDir) + if err != nil { + return errors.Wrap(err, "error generating directory for Central output") } - + logger.InfofLn("Wrote central bundle to %q", outputPath) return nil } From 5e68048903a13afe4d05afe7168a53038e52a58f Mon Sep 17 00:00:00 2001 From: Cong Du Date: Wed, 9 Nov 2022 10:23:49 -0800 Subject: [PATCH 15/15] Address review comment --- roxctl/central/db/generate/generate.go | 20 +++++++++----------- roxctl/central/db/generate/k8s.go | 8 ++------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/roxctl/central/db/generate/generate.go b/roxctl/central/db/generate/generate.go index afe2320018268..27bfe4a88bb9d 100644 --- a/roxctl/central/db/generate/generate.go +++ b/roxctl/central/db/generate/generate.go @@ -9,7 +9,6 @@ import ( "github.com/spf13/cobra" "github.com/stackrox/rox/pkg/buildinfo" "github.com/stackrox/rox/pkg/errorhelpers" - "github.com/stackrox/rox/pkg/errox" "github.com/stackrox/rox/pkg/images/defaults" "github.com/stackrox/rox/pkg/mtls" "github.com/stackrox/rox/pkg/renderer" @@ -28,8 +27,7 @@ const ( ) var ( - errCreateCentralDBBundleSupported = errox.InvariantViolation.New("central does not support central db bundle functionality") - centralDBCertBundle = set.NewFrozenStringSet(mtls.CACertFileName, mtls.CentralDBCertFileName, mtls.CentralDBKeyFileName) + centralDBCertBundle = set.NewFrozenStringSet(mtls.CACertFileName, mtls.CentralDBCertFileName, mtls.CentralDBKeyFileName) ) type generateCommand struct { @@ -68,15 +66,15 @@ func Command(cliEnvironment environment.Environment) *cobra.Command { } func (cmd *generateCommand) populateMTLS() error { - logger := cmd.env.Logger() - logger.InfofLn("Populating Central DB Certificate from bundle...") + 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, - }, logger) + }, envLogger) if err != nil { return err } @@ -108,9 +106,9 @@ func generateBundleWrapper(config renderer.Config) (*zip.Wrapper, error) { return wrapper, errors.Wrap(err, "could not get scanner bundle") } -func outputZip(logger logger.Logger, config renderer.Config) error { - logger.InfofLn("Generating Central DB bundle...") - common.LogInfoPsp(logger, config.EnablePodSecurityPolicies) +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 @@ -130,7 +128,7 @@ func outputZip(logger logger.Logger, config renderer.Config) error { if err != nil { return errors.Wrap(err, "error generating directory for Central output") } - logger.InfofLn("Wrote central bundle to %q", outputPath) + envLogger.InfofLn("Wrote central bundle to %q", outputPath) return nil } @@ -148,7 +146,7 @@ func verifyCentralDBBundleFiles(fm map[string]*zip.File) error { checkList := centralDBCertBundle.Unfreeze() for k, v := range fm { - if 0 == len(v.Content) { + if len(v.Content) == 0 { errs.AddError(errors.Errorf("empty file in Central DB certificate bundle: %s", v.Name)) } if !centralDBCertBundle.Contains(k) { diff --git a/roxctl/central/db/generate/k8s.go b/roxctl/central/db/generate/k8s.go index 434f8be55566d..2633c539e31d9 100644 --- a/roxctl/central/db/generate/k8s.go +++ b/roxctl/central/db/generate/k8s.go @@ -16,8 +16,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 to take advantage of OpenShift 4.x features.` - defaultCentralDBBundle = "central-db-bundle" + defaultCentralDBBundle = "central-db-bundle" ) func orchestratorCommand(shortName, longName string) *cobra.Command { @@ -76,9 +75,6 @@ func openshift(cliEnvironment environment.Environment) *cobra.Command { var openshiftVersion int c := k8sBasedOrchestrator(cliEnvironment, k8sConfig, "openshift", "Openshift", func() (storage.ClusterType, error) { switch openshiftVersion { - case 0: - cliEnvironment.Logger().InfofLn("%s", noteOpenShift3xCompatibilityMode) - return storage.ClusterType_OPENSHIFT_CLUSTER, nil case 3: return storage.ClusterType_OPENSHIFT_CLUSTER, nil case 4: @@ -88,6 +84,6 @@ func openshift(cliEnvironment environment.Environment) *cobra.Command { } }) - c.PersistentFlags().IntVar(&openshiftVersion, "openshift-version", 0, "the OpenShift major version (3 or 4) to deploy on") + c.PersistentFlags().IntVar(&openshiftVersion, "openshift-version", 3, "the OpenShift major version (3 or 4) to deploy on") return c }