Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run kustomize build with kustomize localize and add a no-verify flag. #5544

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 56 additions & 8 deletions kustomize/commands/localize/localize.go
Expand Up @@ -4,10 +4,14 @@
package localize

import (
"bytes"
"log"
"path/filepath"

"github.com/spf13/cobra"
lclzr "sigs.k8s.io/kustomize/api/krusty/localizer"
"sigs.k8s.io/kustomize/kustomize/v5/commands/build"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
Expand All @@ -20,23 +24,26 @@ type arguments struct {
}

type flags struct {
scope string
scope string
noVerify bool
}

// NewCmdLocalize returns a new localize command.
func NewCmdLocalize(fs filesys.FileSystem) *cobra.Command {
var f flags
var buildBuffer bytes.Buffer
buildCmd := build.NewCmdBuild(fs, &build.Help{}, &buildBuffer)
sanaasy marked this conversation as resolved.
Show resolved Hide resolved
cmd := &cobra.Command{
Use: "localize [target [destination]]",
Short: "[Alpha] Creates localized copy of target kustomization root at destination",
Long: `[Alpha] Creates copy of target kustomization directory or
versioned URL at destination, where remote references in the original
Long: `[Alpha] Creates copy of target kustomization directory or
versioned URL at destination, where remote references in the original
are replaced by local references to the downloaded remote content.

If target is not specified, the current working directory will be used.
Destination is a path to a new directory in an existing directory. If
destination is not specified, a new directory will be created in the current
working directory.
If target is not specified, the current working directory will be used.
Destination is a path to a new directory in an existing directory. If
destination is not specified, a new directory will be created in the current
working directory.

For details, see: https://kubectl.docs.kubernetes.io/references/kustomize/cmd/

Expand All @@ -46,7 +53,7 @@ alphabetizes kustomization fields in the localized copy.
`,
Example: `
# Localize the current working directory, with default scope and destination
kustomize localize
kustomize localize

# Localize some local directory, with scope and default destination
kustomize localize /home/path/scope/target --scope /home/path/scope
Expand All @@ -62,6 +69,30 @@ kustomize localize https://github.com/kubernetes-sigs/kustomize//api/krusty/test
if err != nil {
return errors.Wrap(err)
}

if !f.noVerify {
sanaasy marked this conversation as resolved.
Show resolved Hide resolved
originalBuild, err := runBuildCmd(buildBuffer, buildCmd, args.target)
if err != nil {
return errors.Wrap(err)
}

buildDst := dst
if f.scope != "" && f.scope != args.target {
buildDst = filepath.Join(dst, filepath.Base(args.target))
sanaasy marked this conversation as resolved.
Show resolved Hide resolved
}

localizedBuild, err := runBuildCmd(buildBuffer, buildCmd, buildDst)
if err != nil {
return errors.Wrap(err)
}

if localizedBuild != originalBuild {
copyutil.PrettyFileDiff(originalBuild, localizedBuild)
log.Fatalf("VERIFICATION FAILED: `kustomize build` for %s and %s are different after localization.\n", args.target, dst)
}
log.Printf("VERIFICATION SUCCESS: `kustomize build` for %s and %s are the same after localization.\n", args.target, dst)
}

log.Printf("SUCCESS: localized %q to directory %s\n", args.target, dst)
return nil
},
Expand All @@ -74,6 +105,12 @@ kustomize localize https://github.com/kubernetes-sigs/kustomize//api/krusty/test
Cannot specify for remote targets, as scope is by default the containing repo.
If not specified for local target, scope defaults to target.
`)
cmd.Flags().BoolVar(&f.noVerify,
"no-verify",
false,
`Does not verify that the outputs of kustomize build for target and newDir are the same after localization.
If not specified, this flag defaults to false and will run kustomize build.
`)
return cmd
}

Expand All @@ -92,3 +129,14 @@ func matchArgs(rawArgs []string) arguments {
}
return args
}

func runBuildCmd(buffer bytes.Buffer, cmd *cobra.Command, folder string) (buildOutput string, err error) {
buffer.Reset()
buildErr := cmd.RunE(cmd, []string{folder})
if buildErr != nil {
log.Printf("If your target directory requires flags to build: \n 1. Add executable permissions for the downloaded exec binaries in '%s'. \n 2. Run kustomize build with the necessary flags and self-verify the outputs.", folder)
return "", errors.Wrap(buildErr)
}

return buffer.String(), nil
}
76 changes: 76 additions & 0 deletions kustomize/commands/localize/localize_test.go
sanaasy marked this conversation as resolved.
Show resolved Hide resolved
Expand Up @@ -39,6 +39,24 @@ spec:
- containerPort: 80
`

const helmKustomization = `apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
helmCharts:
- name: external-dns
repo: oci://registry-1.docker.io/bitnamicharts
version: 6.19.2
releaseName: test
valuesInline:
crd:
create: false
rbac:
create: false
serviceAccount:
create: false
service:
enabled: false
`

func TestScopeFlag(t *testing.T) {
kustomizations := map[string]string{
filepath.Join("target", "kustomization.yaml"): fmt.Sprintf(`resources:
Expand Down Expand Up @@ -66,6 +84,60 @@ func TestScopeFlag(t *testing.T) {
loctest.CheckFs(t, testDir.String(), expected, actual)
}

func TestNoVerifyFlag(t *testing.T) {
kustomization := map[string]string{
"kustomization.yaml": `resources:
- deployment.yaml
`,
"deployment.yaml": deployment,
}
expected, actual, target := loctest.PrepareFs(t, nil, kustomization)

buffy := new(bytes.Buffer)
log.SetOutput(buffy)
defer func() {
log.SetOutput(os.Stderr)
}()
cmd := localize.NewCmdLocalize(actual)
require.NoError(t, cmd.Flags().Set("no-verify", "true"))
err := cmd.RunE(cmd, []string{
target.String(),
target.Join("dst"),
})
require.NoError(t, err)

loctest.SetupDir(t, expected, target.Join("dst"), kustomization)
loctest.CheckFs(t, target.String(), expected, actual)

successMsg := fmt.Sprintf(`SUCCESS: localized "%s" to directory %s
`, target.String(), target.Join("dst"))
verifyMsg := "VERIFICATION"
require.NotContains(t, buffy.String(), verifyMsg)
require.Contains(t, buffy.String(), successMsg)
}

func TestFailingBuildCmd(t *testing.T) {
kustomization := map[string]string{
"kustomization.yaml": helmKustomization,
}
_, actual, target := loctest.PrepareFs(t, nil, kustomization)

buffy := new(bytes.Buffer)
log.SetOutput(buffy)
defer func() {
log.SetOutput(os.Stderr)
}()
cmd := localize.NewCmdLocalize(actual)
err := cmd.RunE(cmd, []string{
target.String(),
target.Join("dst"),
})
require.Error(t, err)

verifyMsg := "If your target directory requires flags to build"
require.Contains(t, buffy.String(), verifyMsg)
}

func TestOptionalArgs(t *testing.T) {
for name, args := range map[string][]string{
"no_target": {},
Expand Down Expand Up @@ -99,6 +171,8 @@ func TestOptionalArgs(t *testing.T) {
loctest.SetupDir(t, expected, dst, kust)
loctest.CheckFs(t, testDir.String(), expected, actual)

verifyMsg := "VERIFICATION SUCCESS"
require.Contains(t, buffy.String(), verifyMsg)
successMsg := fmt.Sprintf(`SUCCESS: localized "." to directory %s
`, dst)
require.Contains(t, buffy.String(), successMsg)
Expand Down Expand Up @@ -128,6 +202,8 @@ func TestOutput(t *testing.T) {
loctest.SetupDir(t, expected, target.Join("dst"), kustomization)
loctest.CheckFs(t, target.String(), expected, actual)

verifyMsg := "VERIFICATION SUCCESS"
require.Contains(t, buffy.String(), verifyMsg)
successMsg := fmt.Sprintf(`SUCCESS: localized "%s" to directory %s
`, target.String(), target.Join("dst"))
require.Contains(t, buffy.String(), successMsg)
Expand Down