From d94849b51fe048a208a957c475958d7af997d315 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Wed, 8 Apr 2026 13:06:12 -0500 Subject: [PATCH] feat(understackctl): deploy check uses kustomize build to validate Validate the manifests by running 'kustomize build' against each enabled component. --- go/understackctl/cmd/deploy/check.go | 40 +++++++++++++++++++++- go/understackctl/cmd/deploy/deploy_test.go | 2 +- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/go/understackctl/cmd/deploy/check.go b/go/understackctl/cmd/deploy/check.go index fed96b14a..edabce890 100644 --- a/go/understackctl/cmd/deploy/check.go +++ b/go/understackctl/cmd/deploy/check.go @@ -3,12 +3,23 @@ package deploy import ( "fmt" "os" + "os/exec" "path/filepath" + "strings" "github.com/charmbracelet/log" "github.com/spf13/cobra" ) +// kustomizeBuildArgs mirrors the kustomize.buildOptions configured in +// components/argocd/values.yaml so local validation matches ArgoCD's behaviour. +var kustomizeBuildArgs = []string{ + "--enable-alpha-plugins", + "--enable-exec", + "--enable-helm", + "--load-restrictor", "LoadRestrictionsNone", +} + func newCmdDeployCheck() *cobra.Command { cmd := &cobra.Command{ Use: "check ", @@ -36,7 +47,14 @@ func runDeployCheck(clusterName string) error { return nil } + kustomizePath, err := exec.LookPath("kustomize") + if err != nil { + log.Warn("kustomize not found in PATH, skipping kustomization.yaml validation") + kustomizePath = "" + } + missing := []string{} + var kustomizeErrors []string for _, comp := range components { compDir := filepath.Join(clusterName, comp.Name) @@ -52,6 +70,16 @@ func runDeployCheck(clusterName string) error { kustomPath := filepath.Join(compDir, "kustomization.yaml") if _, err := os.Stat(kustomPath); os.IsNotExist(err) { missing = append(missing, kustomPath) + continue + } + + if kustomizePath != "" { + args := append(append([]string{"build"}, kustomizeBuildArgs...), compDir) + out, err := exec.Command(kustomizePath, args...).CombinedOutput() + if err != nil { + kustomizeErrors = append(kustomizeErrors, + fmt.Sprintf("%s: %s", kustomPath, strings.TrimSpace(string(out)))) + } } } } @@ -61,7 +89,17 @@ func runDeployCheck(clusterName string) error { for _, path := range missing { log.Errorf(" - %s", path) } - return fmt.Errorf("validation failed: %d missing files", len(missing)) + } + + if len(kustomizeErrors) > 0 { + log.Error("kustomize build failures:") + for _, msg := range kustomizeErrors { + log.Errorf(" - %s", msg) + } + } + + if total := len(missing) + len(kustomizeErrors); total > 0 { + return fmt.Errorf("validation failed: %d error(s)", total) } log.Infof("All %d components validated successfully", len(components)) diff --git a/go/understackctl/cmd/deploy/deploy_test.go b/go/understackctl/cmd/deploy/deploy_test.go index d419c5ba2..361ad94db 100644 --- a/go/understackctl/cmd/deploy/deploy_test.go +++ b/go/understackctl/cmd/deploy/deploy_test.go @@ -284,7 +284,7 @@ func TestDeployCheck(t *testing.T) { kustomPath := filepath.Join(keystoneDir, "kustomization.yaml") valuesPath := filepath.Join(keystoneDir, "values.yaml") - if err := os.WriteFile(kustomPath, []byte("test"), 0644); err != nil { + if err := os.WriteFile(kustomPath, []byte(kustomizationContent), 0644); err != nil { t.Fatalf("failed to write kustomization.yaml: %v", err) }