diff --git a/internal/command/generate.go b/internal/command/generate.go index dff6012..2603cd4 100644 --- a/internal/command/generate.go +++ b/internal/command/generate.go @@ -196,6 +196,14 @@ manifests. All resources and links between Workloads will be resolved and provis if p, ok := internal.FindFirstUnresolvedSecretRef("", manifest); ok { return errors.Errorf("unresolved secret ref in manifest: %s", p) } + mSig := buildManifestSignature(manifest) + outputManifests = slices.DeleteFunc(outputManifests, func(other map[string]interface{}) bool { + if buildManifestSignature(other) == mSig { + slog.Info(fmt.Sprintf("Overriding duplicate resource manifest %s", mSig)) + return true + } + return false + }) outputManifests = append(outputManifests, manifest) } slog.Info(fmt.Sprintf("Wrote %d resource manifests to manifests buffer for resource '%s'", len(res.Extras.Manifests), id)) @@ -217,6 +225,14 @@ manifests. All resources and links between Workloads will be resolved and provis if p, ok := internal.FindFirstUnresolvedSecretRef("", intermediate); ok { return errors.Errorf("unresolved secret ref in manifest: %s", p) } + mSig := buildManifestSignature(intermediate) + outputManifests = slices.DeleteFunc(outputManifests, func(other map[string]interface{}) bool { + if buildManifestSignature(other) == mSig { + slog.Info(fmt.Sprintf("Overriding duplicate resource manifest %s", mSig)) + return true + } + return false + }) outputManifests = append(outputManifests, intermediate) } slog.Info(fmt.Sprintf("Wrote %d manifests to manifests buffer for workload '%s'", len(manifests), workloadName)) @@ -335,6 +351,17 @@ func parseAndApplyManifestPatches(entry string, flagName string, manifests []map return outManifests, nil } +// buildManifestSignature builds a unique manifest signature for each manifest coming out of a resource. This is used +// to deduplicate resource manifests when they share state. +func buildManifestSignature(n map[string]interface{}) string { + apiVersion, _ := n["apiVersion"].(string) + kind, _ := n["kind"].(string) + metadata, _ := n["metadata"].(map[string]interface{}) + namespace, _ := metadata["namespace"].(string) + name, _ := metadata["name"].(string) + return fmt.Sprintf("%s/%s/%s/%s", apiVersion, kind, namespace, name) +} + func init() { generateCmd.Flags().StringP(generateCmdOutputFlag, "o", "manifests.yaml", "The output manifests file to write the manifests to") generateCmd.Flags().String(generateCmdOverridesFileFlag, "", "An optional file of Score overrides to merge in") diff --git a/internal/command/generate_test.go b/internal/command/generate_test.go index 76150e1..f1d7fa4 100644 --- a/internal/command/generate_test.go +++ b/internal/command/generate_test.go @@ -18,6 +18,7 @@ import ( "context" "os" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -236,3 +237,39 @@ containers: assert.EqualError(t, err, "cannot use --override-property, --overrides-file, or --image when 0 or more than 1 score files are provided") assert.Equal(t, "", stdout) } + +func TestDeduplicateResourceManifests(t *testing.T) { + td := changeToTempDir(t) + assert.NoError(t, os.WriteFile(filepath.Join(td, "score.yaml"), []byte(` +apiVersion: score.dev/v1b1 +metadata: + name: example-a +containers: + hello: + image: foo +resources: + d1: + type: dummy + d2: + type: dummy +`), 0644)) + _, _, err := executeAndResetCommand(context.Background(), rootCmd, []string{"init"}) + require.NoError(t, err) + assert.NoError(t, os.WriteFile(filepath.Join(td, ".score-k8s", "00.provisioners.yaml"), []byte(` +- uri: template://dummy + type: dummy + manifests: | + - apiVersion: v1 + kind: Secret + metadata: + name: my-secret + data: + fruit: {{ b64enc "banana" }} +`), 0644)) + _, stderr, err := executeAndResetCommand(context.Background(), rootCmd, []string{"generate", "score.yaml"}) + t.Log(string(stderr)) + require.NoError(t, err) + rawManifests, err := os.ReadFile(filepath.Join(td, "manifests.yaml")) + require.NoError(t, err) + assert.Equal(t, strings.Count(string(rawManifests), "kind: Secret"), 1, "failed to find in", string(rawManifests)) +}