From d09859ea0e248a942ce10a221e7aa017d53376b6 Mon Sep 17 00:00:00 2001 From: akuitybot <105087302+akuitybot@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:18:30 -0700 Subject: [PATCH] chore(backport release-0.9): feat(directives): support writing kustomize output to multiple files (#2632) Co-authored-by: Hidde Beydals Co-authored-by: Kent Rancourt --- internal/directives/kustomize_builder.go | 55 ++++++++++++++++--- internal/directives/kustomize_builder_test.go | 30 ++++++++++ 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/internal/directives/kustomize_builder.go b/internal/directives/kustomize_builder.go index f87180996..28374ebc3 100644 --- a/internal/directives/kustomize_builder.go +++ b/internal/directives/kustomize_builder.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "sync" securejoin "github.com/cyphar/filepath-securejoin" @@ -88,21 +89,57 @@ func (k *kustomizeBuilder) runPromotionStep( if err != nil { return PromotionStepResult{Status: PromotionStatusFailure}, err } - if err = os.MkdirAll(filepath.Dir(outPath), 0o700); err != nil { - return PromotionStepResult{Status: PromotionStatusFailure}, err - } // Write the built manifests to the output path. - b, err := rm.AsYaml() - if err != nil { - return PromotionStepResult{Status: PromotionStatusFailure}, err - } - if err = os.WriteFile(outPath, b, 0o600); err != nil { - return PromotionStepResult{Status: PromotionStatusFailure}, err + if err := k.writeResult(rm, outPath); err != nil { + return PromotionStepResult{Status: PromotionStatusFailure}, fmt.Errorf( + "failed to write built manifests to %q: %w", cfg.OutPath, + sanitizePathError(err, stepCtx.WorkDir), + ) } return PromotionStepResult{Status: PromotionStatusSuccess}, nil } +func (k *kustomizeBuilder) writeResult(rm resmap.ResMap, outPath string) error { + if ext := filepath.Ext(outPath); ext == ".yaml" || ext == ".yml" { + if err := os.MkdirAll(filepath.Dir(outPath), 0o700); err != nil { + return err + } + b, err := rm.AsYaml() + if err != nil { + return err + } + return os.WriteFile(outPath, b, 0o600) + } + + // If the output path is a directory, write each manifest to a separate file. + if err := os.MkdirAll(outPath, 0o700); err != nil { + return err + } + for _, r := range rm.Resources() { + kind, namespace, name := r.GetKind(), r.GetNamespace(), r.GetName() + if kind == "" || name == "" { + return fmt.Errorf("resource kind and name of %q must be non-empty to write to a directory", r.CurId()) + } + + fileName := fmt.Sprintf("%s-%s", kind, name) + if namespace != "" { + fileName = fmt.Sprintf("%s-%s", namespace, fileName) + } + + b, err := r.AsYAML() + if err != nil { + return fmt.Errorf("failed to convert %q to YAML: %w", r.CurId(), err) + } + + path := filepath.Join(outPath, fmt.Sprintf("%s.yaml", strings.ToLower(fileName))) + if err = os.WriteFile(path, b, 0o600); err != nil { + return err + } + } + return nil +} + // kustomizeBuild builds the manifests in the given directory using Kustomize. func kustomizeBuild(fs filesys.FileSystem, path string) (_ resmap.ResMap, err error) { kustomizeRenderMutex.Lock() diff --git a/internal/directives/kustomize_builder_test.go b/internal/directives/kustomize_builder_test.go index 0c97e2de7..8d790d0fa 100644 --- a/internal/directives/kustomize_builder_test.go +++ b/internal/directives/kustomize_builder_test.go @@ -46,6 +46,36 @@ metadata: assert.Contains(t, string(b), "test-deployment") }, }, + { + name: "successful build with output directory", + setupFiles: func(t *testing.T, dir string) { + require.NoError(t, os.WriteFile(filepath.Join(dir, "kustomization.yaml"), []byte(` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml +`), 0o600)) + require.NoError(t, os.WriteFile(filepath.Join(dir, "deployment.yaml"), []byte(`--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +`), 0o600)) + }, + config: KustomizeBuildConfig{ + Path: ".", + OutPath: "output/", + }, + assertions: func(t *testing.T, dir string, result PromotionStepResult, err error) { + require.NoError(t, err) + assert.Equal(t, PromotionStepResult{Status: PromotionStatusSuccess}, result) + + assert.DirExists(t, filepath.Join(dir, "output")) + b, err := os.ReadFile(filepath.Join(dir, "output", "deployment-test-deployment.yaml")) + require.NoError(t, err) + assert.Contains(t, string(b), "test-deployment") + }, + }, { name: "kustomization file not found", setupFiles: func(*testing.T, string) {},