From 6e073a066b8e2d7beca7c7de200b31c1e2582fb8 Mon Sep 17 00:00:00 2001 From: divolgin Date: Mon, 25 Mar 2024 15:06:03 -0700 Subject: [PATCH] Allow duplicate kots kinds in secrets and configmaps --- pkg/handlers/lint_test.go | 58 ++++++++++++++----- .../testchart-with-labels-16.2.2.yaml} | 0 ...-with-preflightspec-in-secret-16.2.2.yaml} | 0 .../kots-app.yaml | 0 .../kots-config.yaml | 0 .../preflight.yaml | 0 .../support-bundle.yaml | 0 .../kots/without-preflight/chart.yaml | 8 --- pkg/kots/lint.go | 18 +++--- pkg/kots/rego/builders-opa.rego | 3 +- pkg/kots/rego/enterprise-opa-prepend.rego | 6 +- pkg/kots/rego/kots-spec-opa-nonrendered.rego | 8 ++- pkg/kots/spec.go | 15 +++-- pkg/kots/troubleshoot.go | 22 ++++--- 14 files changed, 86 insertions(+), 52 deletions(-) rename pkg/handlers/test-data/kots/{with-kots-kinds/chart.yaml => chart-crds/testchart-with-labels-16.2.2.yaml} (100%) rename pkg/handlers/test-data/kots/{with-preflight/chart.yaml => chart-crds/testchart-with-labels-with-preflightspec-in-secret-16.2.2.yaml} (100%) rename pkg/handlers/test-data/kots/{with-kots-kinds => kots-kinds}/kots-app.yaml (100%) rename pkg/handlers/test-data/kots/{with-kots-kinds => kots-kinds}/kots-config.yaml (100%) rename pkg/handlers/test-data/kots/{with-kots-kinds => kots-kinds}/preflight.yaml (100%) rename pkg/handlers/test-data/kots/{with-kots-kinds => kots-kinds}/support-bundle.yaml (100%) delete mode 100644 pkg/handlers/test-data/kots/without-preflight/chart.yaml diff --git a/pkg/handlers/lint_test.go b/pkg/handlers/lint_test.go index 75f8123..a3de0f4 100644 --- a/pkg/handlers/lint_test.go +++ b/pkg/handlers/lint_test.go @@ -65,14 +65,9 @@ func Test_LintRelease(t *testing.T) { { name: "one valid chart without kotskinds", chartReader: func(t *testing.T) io.ReadCloser { - yamlFiles, err := testdata.ReadDir("test-data/kots/without-preflight") - assert.NoError(t, err) - files := []string{ "test-data/builders/testchart-with-labels-16.2.2.tgz", - } - for _, f := range yamlFiles { - files = append(files, filepath.Join("test-data/kots/without-preflight", f.Name())) + "test-data/kots/chart-crds/testchart-with-labels-16.2.2.yaml", } return io.NopCloser(getTarReader(files)) @@ -112,16 +107,11 @@ func Test_LintRelease(t *testing.T) { }, }, { - name: "one valid chart without kotskinds but with preflights", + name: "one valid chart with preflights but without kotskinds in release", chartReader: func(t *testing.T) io.ReadCloser { - yamlFiles, err := testdata.ReadDir("test-data/kots/with-preflight") - assert.NoError(t, err) - files := []string{ "test-data/builders/testchart-with-labels-with-preflightspec-in-secret-16.2.2.tgz", - } - for _, f := range yamlFiles { - files = append(files, filepath.Join("test-data/kots/with-preflight", f.Name())) + "test-data/kots/chart-crds/testchart-with-labels-with-preflightspec-in-secret-16.2.2.yaml", } return io.NopCloser(getTarReader(files)) @@ -154,16 +144,52 @@ func Test_LintRelease(t *testing.T) { }, }, { - name: "one valid chart with kotskinds but without preflights", + name: "one valid chart without preflights but with kotskinds in release", chartReader: func(t *testing.T) io.ReadCloser { - yamlFiles, err := testdata.ReadDir("test-data/kots/with-kots-kinds") + yamlFiles, err := testdata.ReadDir("test-data/kots/kots-kinds") assert.NoError(t, err) files := []string{ "test-data/builders/testchart-with-labels-16.2.2.tgz", + "test-data/kots/chart-crds/testchart-with-labels-16.2.2.yaml", + } + for _, f := range yamlFiles { + files = append(files, filepath.Join("test-data/kots/kots-kinds", f.Name())) + } + + return io.NopCloser(getTarReader(files)) + }, + contentType: "application/tar", + want: resultType{ + LintExpressions: []kots.LintExpression{ + { + Rule: "application-statusInformers", + Type: "warn", + Message: "Missing application statusInformers", + Path: "kots-app.yaml", + Positions: []kots.LintExpressionItemPosition{ + { + Start: kots.LintExpressionItemLinePosition{ + Line: 5, + }, + }, + }, + }, + }, + }, + }, + { + name: "one valid chart with preflights and with kotskinds in release", + chartReader: func(t *testing.T) io.ReadCloser { + yamlFiles, err := testdata.ReadDir("test-data/kots/kots-kinds") + assert.NoError(t, err) + + files := []string{ + "test-data/builders/testchart-with-labels-with-preflightspec-in-secret-16.2.2.tgz", + "test-data/kots/chart-crds/testchart-with-labels-with-preflightspec-in-secret-16.2.2.yaml", } for _, f := range yamlFiles { - files = append(files, filepath.Join("test-data/kots/with-kots-kinds", f.Name())) + files = append(files, filepath.Join("test-data/kots/kots-kinds", f.Name())) } return io.NopCloser(getTarReader(files)) diff --git a/pkg/handlers/test-data/kots/with-kots-kinds/chart.yaml b/pkg/handlers/test-data/kots/chart-crds/testchart-with-labels-16.2.2.yaml similarity index 100% rename from pkg/handlers/test-data/kots/with-kots-kinds/chart.yaml rename to pkg/handlers/test-data/kots/chart-crds/testchart-with-labels-16.2.2.yaml diff --git a/pkg/handlers/test-data/kots/with-preflight/chart.yaml b/pkg/handlers/test-data/kots/chart-crds/testchart-with-labels-with-preflightspec-in-secret-16.2.2.yaml similarity index 100% rename from pkg/handlers/test-data/kots/with-preflight/chart.yaml rename to pkg/handlers/test-data/kots/chart-crds/testchart-with-labels-with-preflightspec-in-secret-16.2.2.yaml diff --git a/pkg/handlers/test-data/kots/with-kots-kinds/kots-app.yaml b/pkg/handlers/test-data/kots/kots-kinds/kots-app.yaml similarity index 100% rename from pkg/handlers/test-data/kots/with-kots-kinds/kots-app.yaml rename to pkg/handlers/test-data/kots/kots-kinds/kots-app.yaml diff --git a/pkg/handlers/test-data/kots/with-kots-kinds/kots-config.yaml b/pkg/handlers/test-data/kots/kots-kinds/kots-config.yaml similarity index 100% rename from pkg/handlers/test-data/kots/with-kots-kinds/kots-config.yaml rename to pkg/handlers/test-data/kots/kots-kinds/kots-config.yaml diff --git a/pkg/handlers/test-data/kots/with-kots-kinds/preflight.yaml b/pkg/handlers/test-data/kots/kots-kinds/preflight.yaml similarity index 100% rename from pkg/handlers/test-data/kots/with-kots-kinds/preflight.yaml rename to pkg/handlers/test-data/kots/kots-kinds/preflight.yaml diff --git a/pkg/handlers/test-data/kots/with-kots-kinds/support-bundle.yaml b/pkg/handlers/test-data/kots/kots-kinds/support-bundle.yaml similarity index 100% rename from pkg/handlers/test-data/kots/with-kots-kinds/support-bundle.yaml rename to pkg/handlers/test-data/kots/kots-kinds/support-bundle.yaml diff --git a/pkg/handlers/test-data/kots/without-preflight/chart.yaml b/pkg/handlers/test-data/kots/without-preflight/chart.yaml deleted file mode 100644 index ff22928..0000000 --- a/pkg/handlers/test-data/kots/without-preflight/chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: kots.io/v1beta2 -kind: HelmChart -metadata: - name: testchart-with-labels -spec: - chart: - name: testchart-with-labels - chartVersion: 16.2.2 diff --git a/pkg/kots/lint.go b/pkg/kots/lint.go index c9d02a1..4356f0e 100644 --- a/pkg/kots/lint.go +++ b/pkg/kots/lint.go @@ -154,10 +154,11 @@ func LintSpecFiles(ctx context.Context, specFiles SpecFiles) ([]LintExpression, troubleshootSpecs := GetEmbeddedTroubleshootSpecs(ctx, yamlFiles) for _, tsSpec := range troubleshootSpecs { yamlFiles = append(yamlFiles, SpecFile{ - Name: tsSpec.Name, - Path: tsSpec.Path, - Content: tsSpec.Content, - DocIndex: len(yamlFiles), + Name: tsSpec.Name, + Path: tsSpec.Path, + Content: tsSpec.Content, + DocIndex: len(yamlFiles), + AllowDuplicates: tsSpec.AllowDuplicates, }) } @@ -176,10 +177,11 @@ func LintSpecFiles(ctx context.Context, specFiles SpecFiles) ([]LintExpression, troubleshootSpecs := GetEmbeddedTroubleshootSpecs(ctx, files) for _, tsSpec := range troubleshootSpecs { yamlFiles = append(yamlFiles, SpecFile{ - Name: tsSpec.Name, - Path: tsSpec.Path, - Content: tsSpec.Content, - DocIndex: len(yamlFiles), + Name: tsSpec.Name, + Path: tsSpec.Path, + Content: tsSpec.Content, + DocIndex: len(yamlFiles), + AllowDuplicates: tsSpec.AllowDuplicates, }) } } diff --git a/pkg/kots/rego/builders-opa.rego b/pkg/kots/rego/builders-opa.rego index 7383b59..529a64b 100644 --- a/pkg/kots/rego/builders-opa.rego +++ b/pkg/kots/rego/builders-opa.rego @@ -7,7 +7,8 @@ files[output] { "name": file.name, "path": file.path, "content": yaml.unmarshal(file.content), - "docIndex": object.get(file, "docIndex", 0) + "docIndex": object.get(file, "docIndex", 0), + "allowDuplicates": object.get(file, "allowDuplicates", false) } } diff --git a/pkg/kots/rego/enterprise-opa-prepend.rego b/pkg/kots/rego/enterprise-opa-prepend.rego index 8dae47f..89f6c08 100644 --- a/pkg/kots/rego/enterprise-opa-prepend.rego +++ b/pkg/kots/rego/enterprise-opa-prepend.rego @@ -7,7 +7,8 @@ files[output] { "name": file.name, "path": file.path, "content": yaml.unmarshal(file.content), - "docIndex": object.get(file, "docIndex", 0) + "docIndex": object.get(file, "docIndex", 0), + "allowDuplicates": object.get(file, "allowDuplicates", false) } } @@ -46,7 +47,6 @@ specs[output] { output := { "path": file.path, "spec": spec, - "field": field, - "docIndex": file.docIndex + "field": field } } \ No newline at end of file diff --git a/pkg/kots/rego/kots-spec-opa-nonrendered.rego b/pkg/kots/rego/kots-spec-opa-nonrendered.rego index 0506e91..54a1198 100644 --- a/pkg/kots/rego/kots-spec-opa-nonrendered.rego +++ b/pkg/kots/rego/kots-spec-opa-nonrendered.rego @@ -47,7 +47,8 @@ files[output] { "name": file.name, "path": file.path, "content": yaml.unmarshal(file.content), - "docIndex": object.get(file, "docIndex", 0) + "docIndex": object.get(file, "docIndex", 0), + "allowDuplicates": object.get(file, "allowDuplicates", false) } } @@ -146,7 +147,8 @@ kots_kinds[output] { "apiVersion": file.content.apiVersion, "kind": file.content.kind, "filePath": file.path, - "docIndex": file.docIndex + "docIndex": file.docIndex, + "allowDuplicates": file.allowDuplicates } } @@ -487,6 +489,8 @@ lint[output] { kki := kots_kinds[i] kkj := kots_kinds[j] i != j + not kki.allowDuplicates + not kkj.allowDuplicates is_same_kots_kind(kki, kkj) output := { "rule": rule_name, diff --git a/pkg/kots/spec.go b/pkg/kots/spec.go index 986b040..483ff52 100644 --- a/pkg/kots/spec.go +++ b/pkg/kots/spec.go @@ -18,11 +18,12 @@ import ( type SpecFiles []SpecFile type SpecFile struct { - Name string `json:"name"` - Path string `json:"path"` - Content string `json:"content"` - DocIndex int `json:"docIndex,omitempty"` - Children SpecFiles `json:"children"` + Name string `json:"name"` + Path string `json:"path"` + Content string `json:"content"` + DocIndex int `json:"docIndex,omitempty"` + AllowDuplicates bool `json:"allowDuplicates"` // kotskinds can be duplicated if they are coming from secrets or configmaps + Children SpecFiles `json:"children"` } type GVKDoc struct { @@ -94,6 +95,10 @@ func (fs SpecFiles) separate() (SpecFiles, error) { Path: file.Path, // keep original path to be able to link it back Content: doc, DocIndex: index, + // Split files inherit the original file's AllowDuplicates. + // This will only be set for KotsKinds extracted from Secrets and ConfigMaps, so this works. + // But also there is no good way to allow split docs to have their own flag. + AllowDuplicates: file.AllowDuplicates, } separatedSpecFiles = append(separatedSpecFiles, separatedSpecFile) diff --git a/pkg/kots/troubleshoot.go b/pkg/kots/troubleshoot.go index b9c97d7..d7bf541 100644 --- a/pkg/kots/troubleshoot.go +++ b/pkg/kots/troubleshoot.go @@ -28,9 +28,10 @@ func GetEmbeddedTroubleshootSpecs(ctx context.Context, specsFiles SpecFiles) Spe troubleshootSpecs := findTroubleshootSpecs(ctx, specFile.Content) for _, tsSpec := range troubleshootSpecs { tsSpecs = append(tsSpecs, SpecFile{ - Name: path.Join(specFile.Name, tsSpec.Name), - Path: specFile.Name, - Content: tsSpec.Content, + Name: path.Join(specFile.Name, tsSpec.Name), + Path: specFile.Name, + Content: tsSpec.Content, + AllowDuplicates: tsSpec.AllowDuplicates, }) } } @@ -76,8 +77,9 @@ func getSpecFromConfigMap(cm *v1.ConfigMap, namePrefix string) SpecFiles { str, ok := cm.Data[key] if ok { specs = append(specs, SpecFile{ - Name: namePrefix + key, - Content: str, + Name: namePrefix + key, + Content: str, + AllowDuplicates: true, }) } } @@ -98,16 +100,18 @@ func getSpecFromSecret(secret *v1.Secret, namePrefix string) SpecFiles { data, ok := secret.Data[key] if ok { specs = append(specs, SpecFile{ - Name: namePrefix + key, - Content: string(data), + Name: namePrefix + key, + Content: string(data), + AllowDuplicates: true, }) } str, ok := secret.StringData[key] if ok { specs = append(specs, SpecFile{ - Name: namePrefix + key, - Content: str, + Name: namePrefix + key, + Content: str, + AllowDuplicates: true, }) } }