mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Add localize command handle (#4959)
* Add localize command handle * Align to kustomize command conventions * Print success msg
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/kustomize/v4/commands/build"
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/build"
|
||||||
"sigs.k8s.io/kustomize/kustomize/v4/commands/create"
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/create"
|
||||||
"sigs.k8s.io/kustomize/kustomize/v4/commands/edit"
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/edit"
|
||||||
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/localize"
|
||||||
"sigs.k8s.io/kustomize/kustomize/v4/commands/openapi"
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/openapi"
|
||||||
"sigs.k8s.io/kustomize/kustomize/v4/commands/version"
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/version"
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
@@ -53,6 +54,7 @@ See https://sigs.k8s.io/kustomize
|
|||||||
create.NewCmdCreate(fSys, pvd.GetResourceFactory()),
|
create.NewCmdCreate(fSys, pvd.GetResourceFactory()),
|
||||||
version.NewCmdVersion(stdOut),
|
version.NewCmdVersion(stdOut),
|
||||||
openapi.NewCmdOpenAPI(stdOut),
|
openapi.NewCmdOpenAPI(stdOut),
|
||||||
|
localize.NewCmdLocalize(fSys, stdOut),
|
||||||
)
|
)
|
||||||
configcobra.AddCommands(c, konfig.ProgramName)
|
configcobra.AddCommands(c, konfig.ProgramName)
|
||||||
|
|
||||||
|
|||||||
98
kustomize/commands/localize/localize.go
Normal file
98
kustomize/commands/localize/localize.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2022 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package localize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
lclzr "sigs.k8s.io/kustomize/api/krusty/localizer"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
const numArgs = 2
|
||||||
|
|
||||||
|
type arguments struct {
|
||||||
|
target string
|
||||||
|
dest string
|
||||||
|
}
|
||||||
|
|
||||||
|
type flags struct {
|
||||||
|
scope string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCmdLocalize returns a new localize command.
|
||||||
|
func NewCmdLocalize(fs filesys.FileSystem, writer io.Writer) *cobra.Command {
|
||||||
|
log.SetOutput(writer)
|
||||||
|
var f flags
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
For details, see: https://kubectl.docs.kubernetes.io/references/kustomize/cmd/
|
||||||
|
|
||||||
|
Disclaimer:
|
||||||
|
This command does not yet localize helm or KRM plugin fields. This command also
|
||||||
|
alphabetizes kustomization fields in the localized copy.
|
||||||
|
`,
|
||||||
|
Example: `
|
||||||
|
# Localize the current working directory, with default scope and destination
|
||||||
|
kustomize localize
|
||||||
|
|
||||||
|
# Localize some local directory, with scope and default destination
|
||||||
|
kustomize localize /home/path/scope/target --scope /home/path/scope
|
||||||
|
|
||||||
|
# Localize remote at set destination relative to working directory
|
||||||
|
kustomize localize https://github.com/kubernetes-sigs/kustomize//api/krusty/testdata/localize/simple?ref=v4.5.7 path/non-existing-dir
|
||||||
|
`,
|
||||||
|
SilenceUsage: true,
|
||||||
|
Args: cobra.MaximumNArgs(numArgs),
|
||||||
|
RunE: func(cmd *cobra.Command, rawArgs []string) error {
|
||||||
|
args := matchArgs(rawArgs)
|
||||||
|
dst, err := lclzr.Run(fs, args.target, f.scope, args.dest)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
successMsg := fmt.Sprintf("SUCCESS: localized %q to directory %s\n", args.target, dst)
|
||||||
|
_, err = writer.Write([]byte(successMsg))
|
||||||
|
return errors.Wrap(err)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// no shorthand to avoid conflation with other flags
|
||||||
|
cmd.Flags().StringVar(&f.scope,
|
||||||
|
"scope",
|
||||||
|
"",
|
||||||
|
`Path to directory inside of which localize is limited to running.
|
||||||
|
Cannot specify for remote targets, as scope is by default the containing repo.
|
||||||
|
If not specified for local target, scope defaults to target.
|
||||||
|
`)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchArgs matches user-entered userArgs, which cannot exceed max length, with
|
||||||
|
// arguments.
|
||||||
|
func matchArgs(rawArgs []string) arguments {
|
||||||
|
var args arguments
|
||||||
|
switch len(rawArgs) {
|
||||||
|
case numArgs:
|
||||||
|
args.dest = rawArgs[1]
|
||||||
|
fallthrough
|
||||||
|
case 1:
|
||||||
|
args.target = rawArgs[0]
|
||||||
|
case 0:
|
||||||
|
args.target = filesys.SelfDir
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
153
kustomize/commands/localize/localize_test.go
Normal file
153
kustomize/commands/localize/localize_test.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
// Copyright 2022 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package localize_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
loctest "sigs.k8s.io/kustomize/api/testutils/localizertest"
|
||||||
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/localize"
|
||||||
|
)
|
||||||
|
|
||||||
|
const deployment = `apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.14.2
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestScopeFlag(t *testing.T) {
|
||||||
|
kustomizations := map[string]string{
|
||||||
|
filepath.Join("target", "kustomization.yaml"): fmt.Sprintf(`resources:
|
||||||
|
- %s
|
||||||
|
`, filepath.Join("..", "base")),
|
||||||
|
filepath.Join("base", "kustomization.yaml"): `resources:
|
||||||
|
- deployment.yaml
|
||||||
|
`,
|
||||||
|
filepath.Join("base", "deployment.yaml"): deployment,
|
||||||
|
}
|
||||||
|
expected, actual, testDir := loctest.PrepareFs(t, []string{
|
||||||
|
"target",
|
||||||
|
"base",
|
||||||
|
}, kustomizations)
|
||||||
|
|
||||||
|
cmd := localize.NewCmdLocalize(actual, new(bytes.Buffer))
|
||||||
|
require.NoError(t, cmd.Flags().Set("scope", testDir.String()))
|
||||||
|
err := cmd.RunE(cmd, []string{
|
||||||
|
testDir.Join("target"),
|
||||||
|
testDir.Join("dst"),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
loctest.SetupDir(t, expected, testDir.Join("dst"), kustomizations)
|
||||||
|
loctest.CheckFs(t, testDir.String(), expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionalArgs(t *testing.T) {
|
||||||
|
for name, args := range map[string][]string{
|
||||||
|
"no_target": {},
|
||||||
|
"no_dst": {"."},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
kust := map[string]string{
|
||||||
|
"kustomization.yaml": `resources:
|
||||||
|
- deployment.yaml
|
||||||
|
`,
|
||||||
|
"deployment.yaml": deployment,
|
||||||
|
}
|
||||||
|
expected, actual, testDir := loctest.PrepareFs(t, []string{
|
||||||
|
"target",
|
||||||
|
}, nil)
|
||||||
|
target := testDir.Join("target")
|
||||||
|
loctest.SetupDir(t, actual, target, kust)
|
||||||
|
loctest.SetWorkingDir(t, target)
|
||||||
|
|
||||||
|
buffy := new(bytes.Buffer)
|
||||||
|
cmd := localize.NewCmdLocalize(actual, buffy)
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
loctest.SetupDir(t, expected, target, kust)
|
||||||
|
dst := filepath.Join(target, "localized-target")
|
||||||
|
loctest.SetupDir(t, expected, dst, kust)
|
||||||
|
loctest.CheckFs(t, testDir.String(), expected, actual)
|
||||||
|
|
||||||
|
successMsg := fmt.Sprintf(`SUCCESS: localized "." to directory %s
|
||||||
|
`, dst)
|
||||||
|
require.Equal(t, successMsg, buffy.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOutput(t *testing.T) {
|
||||||
|
kustomization := map[string]string{
|
||||||
|
"kustomization.yaml": `namePrefix: test-
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
expected, actual, target := loctest.PrepareFs(t, nil, kustomization)
|
||||||
|
|
||||||
|
buffy := new(bytes.Buffer)
|
||||||
|
cmd := localize.NewCmdLocalize(actual, buffy)
|
||||||
|
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"))
|
||||||
|
require.Equal(t, successMsg, buffy.String())
|
||||||
|
|
||||||
|
const msg = "Check that cmd log output is hooked to buffy."
|
||||||
|
log.Print(msg)
|
||||||
|
require.Contains(t, buffy.String(), msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAlpha(t *testing.T) {
|
||||||
|
_, actual, _ := loctest.PrepareFs(t, nil, map[string]string{
|
||||||
|
"kustomization.yaml": `namePrefix: test-`,
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd := localize.NewCmdLocalize(actual, new(bytes.Buffer))
|
||||||
|
require.Contains(t, cmd.Short, "[Alpha]")
|
||||||
|
require.Contains(t, cmd.Long, "[Alpha]")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTooManyArgs(t *testing.T) {
|
||||||
|
_, actual, target := loctest.PrepareFs(t, nil, map[string]string{
|
||||||
|
"kustomization.yaml": `namePrefix: test-`,
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd := localize.NewCmdLocalize(actual, new(bytes.Buffer))
|
||||||
|
err := cmd.Args(cmd, []string{
|
||||||
|
target.String(),
|
||||||
|
target.Join("dst"),
|
||||||
|
target.String(),
|
||||||
|
})
|
||||||
|
require.EqualError(t, err, "accepts at most 2 arg(s), received 3")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user