mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Merge branch 'master' into container-filter-network
This commit is contained in:
@@ -35,7 +35,7 @@ install: true
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
- ./travis/verify-deps.sh
|
- ./travis/verify-deps.sh
|
||||||
- ./travis/pre-commit.sh
|
- make verify-kustomize
|
||||||
- ./travis/kyaml-pre-commit.sh
|
- ./travis/kyaml-pre-commit.sh
|
||||||
|
|
||||||
# TBD. Suppressing for now.
|
# TBD. Suppressing for now.
|
||||||
|
|||||||
71
Makefile
71
Makefile
@@ -1,25 +1,21 @@
|
|||||||
# This Makefile is (and must be) used by
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
# travis/pre-commit.sh to qualify pull requests.
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
#
|
#
|
||||||
# That script generates all the code that needs
|
# Makefile for the kustomize repo.
|
||||||
# to be generated, and runs all the tests.
|
|
||||||
#
|
|
||||||
# Functionality in that script is gradually moving here.
|
|
||||||
|
|
||||||
MYGOBIN := $(shell go env GOPATH)/bin
|
MYGOBIN := $(shell go env GOPATH)/bin
|
||||||
PATH := $(PATH):$(MYGOBIN)
|
PATH := $(PATH):$(MYGOBIN)
|
||||||
SHELL := env PATH=$(PATH) /bin/bash
|
SHELL := env PATH=$(PATH) /bin/bash
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: pre-commit
|
all: verify-kustomize
|
||||||
|
|
||||||
# The pre-commit.sh script generates, lints and tests.
|
.PHONY: verify-kustomize
|
||||||
# It uses this makefile. For more clarity, would like
|
verify-kustomize: \
|
||||||
# to stop that - any scripts invoked by targets here
|
lint-kustomize \
|
||||||
# shouldn't "call back" to the makefile.
|
test-unit-kustomize-all \
|
||||||
.PHONY: pre-commit
|
test-examples-kustomize-against-HEAD \
|
||||||
pre-commit:
|
test-examples-kustomize-against-latest
|
||||||
./travis/pre-commit.sh
|
|
||||||
|
|
||||||
# Version pinned by api/go.mod
|
# Version pinned by api/go.mod
|
||||||
$(MYGOBIN)/golangci-lint:
|
$(MYGOBIN)/golangci-lint:
|
||||||
@@ -46,6 +42,11 @@ $(MYGOBIN)/pluginator:
|
|||||||
cd api; \
|
cd api; \
|
||||||
go install sigs.k8s.io/kustomize/pluginator/v2
|
go install sigs.k8s.io/kustomize/pluginator/v2
|
||||||
|
|
||||||
|
# Install kustomize from whatever is checked out.
|
||||||
|
$(MYGOBIN)/kustomize:
|
||||||
|
cd kustomize; \
|
||||||
|
go install .
|
||||||
|
|
||||||
.PHONY: install-tools
|
.PHONY: install-tools
|
||||||
install-tools: \
|
install-tools: \
|
||||||
$(MYGOBIN)/goimports \
|
$(MYGOBIN)/goimports \
|
||||||
@@ -72,8 +73,8 @@ builtinplugins = \
|
|||||||
api/builtins/replicacounttransformer.go \
|
api/builtins/replicacounttransformer.go \
|
||||||
api/builtins/secretgenerator.go
|
api/builtins/secretgenerator.go
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint-kustomize
|
||||||
lint: install-tools $(builtinplugins)
|
lint-kustomize: install-tools $(builtinplugins)
|
||||||
cd api; $(MYGOBIN)/golangci-lint run ./...
|
cd api; $(MYGOBIN)/golangci-lint run ./...
|
||||||
cd kustomize; $(MYGOBIN)/golangci-lint run ./...
|
cd kustomize; $(MYGOBIN)/golangci-lint run ./...
|
||||||
cd pluginator; $(MYGOBIN)/golangci-lint run ./...
|
cd pluginator; $(MYGOBIN)/golangci-lint run ./...
|
||||||
@@ -85,23 +86,36 @@ api/builtins/%.go: $(MYGOBIN)/pluginator
|
|||||||
cd ../../../api/builtins; \
|
cd ../../../api/builtins; \
|
||||||
$(MYGOBIN)/goimports -w $*.go
|
$(MYGOBIN)/goimports -w $*.go
|
||||||
|
|
||||||
.PHONY: generate
|
.PHONY: test-unit-kustomize-api
|
||||||
generate: $(builtinplugins)
|
test-unit-kustomize-api: $(builtinplugins)
|
||||||
|
|
||||||
.PHONY: unit-test-api
|
|
||||||
unit-test-api: $(builtinplugins)
|
|
||||||
cd api; go test ./...
|
cd api; go test ./...
|
||||||
|
|
||||||
.PHONY: unit-test-plugins
|
.PHONY: test-unit-kustomize-plugins
|
||||||
unit-test-plugins:
|
test-unit-kustomize-plugins:
|
||||||
./hack/runPluginUnitTests.sh
|
./hack/testUnitKustomizePlugins.sh
|
||||||
|
|
||||||
.PHONY: unit-test-kustomize
|
.PHONY: test-unit-kustomize-cli
|
||||||
unit-test-kustomize:
|
test-unit-kustomize-cli:
|
||||||
cd kustomize; go test ./...
|
cd kustomize; go test ./...
|
||||||
|
|
||||||
.PHONY: unit-test-all
|
.PHONY: test-unit-kustomize-all
|
||||||
unit-test-all: unit-test-api unit-test-kustomize unit-test-plugins
|
test-unit-kustomize-all: \
|
||||||
|
test-unit-kustomize-api \
|
||||||
|
test-unit-kustomize-cli \
|
||||||
|
test-unit-kustomize-plugins
|
||||||
|
|
||||||
|
.PHONY:
|
||||||
|
test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
|
||||||
|
./hack/testExamplesAgainstKustomize.sh HEAD
|
||||||
|
|
||||||
|
.PHONY:
|
||||||
|
test-examples-kustomize-against-latest: $(MYGOBIN)/mdrip
|
||||||
|
/bin/rm -f $(MYGOBIN)/kustomize; \
|
||||||
|
echo "Installing kustomize from latest."; \
|
||||||
|
go install sigs.k8s.io/kustomize/kustomize/v3; \
|
||||||
|
./hack/testExamplesAgainstKustomize.sh latest; \
|
||||||
|
echo "Reinstalling kustomize from HEAD."; \
|
||||||
|
cd kustomize; go install .
|
||||||
|
|
||||||
# linux only.
|
# linux only.
|
||||||
# This is for testing an example plugin that
|
# This is for testing an example plugin that
|
||||||
@@ -133,6 +147,7 @@ $(MYGOBIN)/helm:
|
|||||||
clean:
|
clean:
|
||||||
rm -f $(builtinplugins)
|
rm -f $(builtinplugins)
|
||||||
rm -f $(MYGOBIN)/pluginator
|
rm -f $(MYGOBIN)/pluginator
|
||||||
|
rm -f $(MYGOBIN)/kustomize
|
||||||
|
|
||||||
.PHONY: nuke
|
.PHONY: nuke
|
||||||
nuke: clean
|
nuke: clean
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func GetCatRunner() *CatRunner {
|
|||||||
kyaml cat my-dir/
|
kyaml cat my-dir/
|
||||||
|
|
||||||
# wrap Resource config from a directory in an ResourceList
|
# wrap Resource config from a directory in an ResourceList
|
||||||
kyaml cat my-dir/ --wrap-kind ResourceList --wrap-version kyaml.kustomize.dev/v1alpha1 --function-config fn.yaml
|
kyaml cat my-dir/ --wrap-kind ResourceList --wrap-version config.kubernetes.io/v1alpha1 --function-config fn.yaml
|
||||||
|
|
||||||
# unwrap Resource config from a directory in an ResourceList
|
# unwrap Resource config from a directory in an ResourceList
|
||||||
... | kyaml cat
|
... | kyaml cat
|
||||||
@@ -51,10 +51,10 @@ kyaml cat my-dir/ --wrap-kind ResourceList --wrap-version kyaml.kustomize.dev/v1
|
|||||||
"'FoldedStyle', 'FlowStyle'.")
|
"'FoldedStyle', 'FlowStyle'.")
|
||||||
c.Flags().BoolVar(&r.StripComments, "strip-comments", false,
|
c.Flags().BoolVar(&r.StripComments, "strip-comments", false,
|
||||||
"remove comments from yaml.")
|
"remove comments from yaml.")
|
||||||
c.Flags().BoolVar(&r.IncludeReconcilers, "include-reconcilers", false,
|
c.Flags().BoolVar(&r.IncludeLocal, "include-local", false,
|
||||||
"if true, include reconciler Resources in the output.")
|
"if true, include local-config in the output.")
|
||||||
c.Flags().BoolVar(&r.ExcludeNonReconcilers, "exclude-non-reconcilers", false,
|
c.Flags().BoolVar(&r.ExcludeNonLocal, "exclude-non-local", false,
|
||||||
"if true, exclude non-reconciler Resources in the output.")
|
"if true, exclude non-local-config in the output.")
|
||||||
r.Command = c
|
r.Command = c
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -65,17 +65,17 @@ func CatCommand() *cobra.Command {
|
|||||||
|
|
||||||
// CatRunner contains the run function
|
// CatRunner contains the run function
|
||||||
type CatRunner struct {
|
type CatRunner struct {
|
||||||
IncludeSubpackages bool
|
IncludeSubpackages bool
|
||||||
Format bool
|
Format bool
|
||||||
KeepAnnotations bool
|
KeepAnnotations bool
|
||||||
WrapKind string
|
WrapKind string
|
||||||
WrapApiVersion string
|
WrapApiVersion string
|
||||||
FunctionConfig string
|
FunctionConfig string
|
||||||
Styles []string
|
Styles []string
|
||||||
StripComments bool
|
StripComments bool
|
||||||
IncludeReconcilers bool
|
IncludeLocal bool
|
||||||
ExcludeNonReconcilers bool
|
ExcludeNonLocal bool
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
||||||
@@ -105,9 +105,9 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
var fltr []kio.Filter
|
var fltr []kio.Filter
|
||||||
// don't include reconcilers
|
// don't include reconcilers
|
||||||
fltr = append(fltr, &filters.IsReconcilerFilter{
|
fltr = append(fltr, &filters.IsLocalConfig{
|
||||||
ExcludeReconcilers: !r.IncludeReconcilers,
|
IncludeLocalConfig: r.IncludeLocal,
|
||||||
IncludeNonReconcilers: !r.ExcludeNonReconcilers,
|
ExcludeNonLocalConfig: r.ExcludeNonLocal,
|
||||||
})
|
})
|
||||||
if r.Format {
|
if r.Format {
|
||||||
fltr = append(fltr, filters.FormatFilter{})
|
fltr = append(fltr, filters.FormatFilter{})
|
||||||
|
|||||||
@@ -47,10 +47,15 @@ spec:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||||
apiVersion: gcr.io/example/image:version
|
apiVersion: v1
|
||||||
kind: Abstraction
|
kind: Abstraction
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example/reconciler:v1
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
---
|
---
|
||||||
@@ -149,10 +154,15 @@ spec:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||||
apiVersion: gcr.io/example/image:version
|
apiVersion: v1
|
||||||
kind: Abstraction
|
kind: Abstraction
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example/image:version
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
---
|
---
|
||||||
@@ -173,7 +183,7 @@ spec:
|
|||||||
// fmt the files
|
// fmt the files
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
r := cmd.GetCatRunner()
|
r := cmd.GetCatRunner()
|
||||||
r.Command.SetArgs([]string{d, "--include-reconcilers"})
|
r.Command.SetArgs([]string{d, "--include-local"})
|
||||||
r.Command.SetOut(b)
|
r.Command.SetOut(b)
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
if !assert.NoError(t, r.Command.Execute()) {
|
||||||
return
|
return
|
||||||
@@ -202,13 +212,17 @@ spec:
|
|||||||
selector:
|
selector:
|
||||||
app: nginx
|
app: nginx
|
||||||
---
|
---
|
||||||
apiVersion: gcr.io/example/image:version
|
apiVersion: v1
|
||||||
kind: Abstraction
|
kind: Abstraction
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
annotations:
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
config.kubernetes.io/package: .
|
config.kubernetes.io/package: .
|
||||||
config.kubernetes.io/path: f2.yaml
|
config.kubernetes.io/path: f2.yaml
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example/image:version
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
---
|
---
|
||||||
@@ -259,10 +273,15 @@ spec:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||||
apiVersion: gcr.io/example/image:version
|
apiVersion: v1
|
||||||
kind: Abstraction
|
kind: Abstraction
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example/reconciler:v1
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
---
|
---
|
||||||
@@ -283,19 +302,23 @@ spec:
|
|||||||
// fmt the files
|
// fmt the files
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
r := cmd.GetCatRunner()
|
r := cmd.GetCatRunner()
|
||||||
r.Command.SetArgs([]string{d, "--include-reconcilers", "--exclude-non-reconcilers"})
|
r.Command.SetArgs([]string{d, "--include-local", "--exclude-non-local"})
|
||||||
r.Command.SetOut(b)
|
r.Command.SetOut(b)
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
if !assert.NoError(t, r.Command.Execute()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, `apiVersion: gcr.io/example/image:version
|
if !assert.Equal(t, `apiVersion: v1
|
||||||
kind: Abstraction
|
kind: Abstraction
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
annotations:
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
config.kubernetes.io/package: .
|
config.kubernetes.io/package: .
|
||||||
config.kubernetes.io/path: f2.yaml
|
config.kubernetes.io/path: f2.yaml
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example/reconciler:v1
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
`, b.String()) {
|
`, b.String()) {
|
||||||
|
|||||||
143
cmd/kyaml/cmd/cmdwrap.go
Normal file
143
cmd/kyaml/cmd/cmdwrap.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetWrapRunner returns a command runner.
|
||||||
|
func GetWrapRunner() *WrapRunner {
|
||||||
|
r := &WrapRunner{}
|
||||||
|
c := &cobra.Command{
|
||||||
|
Use: "wrap CMD...",
|
||||||
|
Short: "Wrap an executable so it implements the config fn interface",
|
||||||
|
Long: `Wrap an executable so it implements the config fn interface
|
||||||
|
|
||||||
|
wrap simplifies writing config functions by:
|
||||||
|
|
||||||
|
- invoking an executable command converting an input ResourceList into environment
|
||||||
|
- merging the output onto the original input as a set of patches
|
||||||
|
- setting filenames on any Resources missing them
|
||||||
|
|
||||||
|
config function authors may use wrap by using it to invoke a command from a container image
|
||||||
|
|
||||||
|
The following are equivalent:
|
||||||
|
|
||||||
|
kyaml wrap -- CMD
|
||||||
|
|
||||||
|
kyaml xargs -- CMD | kyaml merge | kyaml fmt --set-filenames
|
||||||
|
|
||||||
|
Environment Variables:
|
||||||
|
|
||||||
|
KUST_OVERRIDE_DIR:
|
||||||
|
|
||||||
|
Path to a directory containing patches to apply to after merging.
|
||||||
|
`,
|
||||||
|
Example: `
|
||||||
|
|
||||||
|
`,
|
||||||
|
RunE: r.runE,
|
||||||
|
SilenceUsage: true,
|
||||||
|
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
}
|
||||||
|
r.Command = c
|
||||||
|
r.XArgs = GetXArgsRunner()
|
||||||
|
c.Flags().BoolVar(&r.XArgs.EnvOnly,
|
||||||
|
"env-only", true, "only set env vars, not arguments.")
|
||||||
|
c.Flags().StringVar(&r.XArgs.WrapKind,
|
||||||
|
"wrap-kind", "List", "wrap the input xargs give to the command in this type.")
|
||||||
|
c.Flags().StringVar(&r.XArgs.WrapVersion,
|
||||||
|
"wrap-version", "v1", "wrap the input xargs give to the command in this type.")
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapRunner contains the run function
|
||||||
|
type WrapRunner struct {
|
||||||
|
Command *cobra.Command
|
||||||
|
XArgs *XArgsRunner
|
||||||
|
getEnv func(key string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
KustMergeEnv = "KUST_MERGE"
|
||||||
|
KustOverrideDirEnv = "KUST_OVERRIDE_DIR"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WrapCommand() *cobra.Command {
|
||||||
|
return GetWrapRunner().Command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *WrapRunner) runE(c *cobra.Command, args []string) error {
|
||||||
|
if r.getEnv == nil {
|
||||||
|
r.getEnv = os.Getenv
|
||||||
|
}
|
||||||
|
xargsIn := &bytes.Buffer{}
|
||||||
|
if _, err := io.Copy(xargsIn, c.InOrStdin()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mergeInput := bytes.NewBuffer(xargsIn.Bytes())
|
||||||
|
// Run the command
|
||||||
|
xargsOut := &bytes.Buffer{}
|
||||||
|
r.XArgs.Command.SetArgs(args)
|
||||||
|
r.XArgs.Command.SetIn(xargsIn)
|
||||||
|
r.XArgs.Command.SetOut(xargsOut)
|
||||||
|
r.XArgs.Command.SetErr(os.Stderr)
|
||||||
|
if err := r.XArgs.Command.Execute(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge the results
|
||||||
|
buff := &kio.PackageBuffer{}
|
||||||
|
|
||||||
|
var fltrs []kio.Filter
|
||||||
|
var inputs []kio.Reader
|
||||||
|
if r.getEnv(KustMergeEnv) == "" || r.getEnv(KustMergeEnv) == "true" || r.getEnv(KustMergeEnv) == "1" {
|
||||||
|
inputs = append(inputs, &kio.ByteReader{Reader: mergeInput})
|
||||||
|
fltrs = append(fltrs, &filters.MergeFilter{})
|
||||||
|
}
|
||||||
|
inputs = append(inputs, &kio.ByteReader{Reader: xargsOut})
|
||||||
|
|
||||||
|
if err := (kio.Pipeline{Inputs: inputs, Filters: fltrs, Outputs: []kio.Writer{buff}}).
|
||||||
|
Execute(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs, fltrs = []kio.Reader{buff}, nil
|
||||||
|
if r.getEnv(KustOverrideDirEnv) != "" {
|
||||||
|
// merge the overrides on top of the output
|
||||||
|
fltrs = append(fltrs, filters.MergeFilter{})
|
||||||
|
inputs = append(inputs,
|
||||||
|
kio.LocalPackageReader{
|
||||||
|
OmitReaderAnnotations: true, // don't set path annotations, as they would override
|
||||||
|
PackagePath: r.getEnv(KustOverrideDirEnv)})
|
||||||
|
}
|
||||||
|
fltrs = append(fltrs,
|
||||||
|
&filters.FileSetter{
|
||||||
|
FilenamePattern: filepath.Join("config", filters.DefaultFilenamePattern)},
|
||||||
|
&filters.FormatFilter{})
|
||||||
|
|
||||||
|
err := kio.Pipeline{
|
||||||
|
Inputs: inputs,
|
||||||
|
Filters: fltrs,
|
||||||
|
Outputs: []kio.Writer{kio.ByteWriter{
|
||||||
|
Sort: true,
|
||||||
|
KeepReaderAnnotations: true,
|
||||||
|
Writer: c.OutOrStdout(),
|
||||||
|
WrappingKind: kio.ResourceListKind,
|
||||||
|
WrappingApiVersion: kio.ResourceListApiVersion}}}.Execute()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
307
cmd/kyaml/cmd/cmdwrap_test.go
Normal file
307
cmd/kyaml/cmd/cmdwrap_test.go
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
input = `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
functionConfig:
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
replicas: 11
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
replicas: 5
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
name: test
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
image: nginx:v1.7
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
# This i the port.
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
name: test
|
||||||
|
`
|
||||||
|
|
||||||
|
output = `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: 0
|
||||||
|
config.kubernetes.io/path: config/test_deployment.yaml
|
||||||
|
spec:
|
||||||
|
replicas: 11
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
image: nginx:v1.7
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: 0
|
||||||
|
config.kubernetes.io/path: config/test_service.yaml
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
# This i the port.
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
`
|
||||||
|
|
||||||
|
outputNoMerge = `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: 0
|
||||||
|
config.kubernetes.io/path: config/test_deployment.yaml
|
||||||
|
spec:
|
||||||
|
replicas: 11
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
image: nginx:v1.7
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: 0
|
||||||
|
config.kubernetes.io/path: config/test_service.yaml
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
# This i the port.
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
`
|
||||||
|
|
||||||
|
outputOverride = `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: 0
|
||||||
|
config.kubernetes.io/path: config/test_deployment.yaml
|
||||||
|
spec:
|
||||||
|
replicas: 11
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
image: nginx:v1.9
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
labels:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: 0
|
||||||
|
config.kubernetes.io/path: config/test_service.yaml
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
name: test
|
||||||
|
app: nginx
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
# This i the port.
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCmd_wrap(t *testing.T) {
|
||||||
|
_, dir, _, ok := runtime.Caller(0)
|
||||||
|
if !assert.True(t, ok) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
dir = filepath.Dir(dir)
|
||||||
|
|
||||||
|
c := GetWrapRunner()
|
||||||
|
c.Command.SetIn(bytes.NewBufferString(input))
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
c.Command.SetOut(out)
|
||||||
|
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
||||||
|
c.Command.SetArgs(args)
|
||||||
|
c.XArgs.Args = args
|
||||||
|
|
||||||
|
if !assert.NoError(t, c.Command.Execute()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, output, out.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCmd_wrapNoMerge(t *testing.T) {
|
||||||
|
_, dir, _, ok := runtime.Caller(0)
|
||||||
|
if !assert.True(t, ok) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
dir = filepath.Dir(dir)
|
||||||
|
|
||||||
|
c := GetWrapRunner()
|
||||||
|
c.getEnv = func(key string) string {
|
||||||
|
if key == KustMergeEnv {
|
||||||
|
return "false"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
c.Command.SetIn(bytes.NewBufferString(input))
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
c.Command.SetOut(out)
|
||||||
|
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
||||||
|
c.Command.SetArgs(args)
|
||||||
|
c.XArgs.Args = args
|
||||||
|
if !assert.NoError(t, c.Command.Execute()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, outputNoMerge, out.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCmd_wrapOverride(t *testing.T) {
|
||||||
|
_, dir, _, ok := runtime.Caller(0)
|
||||||
|
if !assert.True(t, ok) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
dir = filepath.Dir(dir)
|
||||||
|
|
||||||
|
c := GetWrapRunner()
|
||||||
|
c.getEnv = func(key string) string {
|
||||||
|
if key == KustOverrideDirEnv {
|
||||||
|
return filepath.Join(dir, "test")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
c.Command.SetIn(bytes.NewBufferString(input))
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
c.Command.SetOut(out)
|
||||||
|
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
||||||
|
c.Command.SetArgs(args)
|
||||||
|
c.XArgs.Args = args
|
||||||
|
if !assert.NoError(t, c.Command.Execute()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, outputOverride, out.String())
|
||||||
|
}
|
||||||
226
cmd/kyaml/cmd/cmdxargs.go
Normal file
226
cmd/kyaml/cmd/cmdxargs.go
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetXArgsRunner returns a command runner.
|
||||||
|
func GetXArgsRunner() *XArgsRunner {
|
||||||
|
r := &XArgsRunner{}
|
||||||
|
c := &cobra.Command{
|
||||||
|
Use: "xargs -- CMD...",
|
||||||
|
Short: "Convert functionConfig to commandline flags and envs",
|
||||||
|
Long: `Convert functionConfig to commandline flags and envs.
|
||||||
|
|
||||||
|
xargs reads a ResourceList from stdin and parses the functionConfig field. xargs then
|
||||||
|
reads each of the fields under .spec and parses them as flags. If the fields have non-scalar
|
||||||
|
values, then xargs encoded the values as yaml strings.
|
||||||
|
|
||||||
|
CMD:
|
||||||
|
The command to run and pass the functionConfig as arguments.
|
||||||
|
`,
|
||||||
|
Example: `
|
||||||
|
# given this example functionConfig in config.yaml
|
||||||
|
kind: Foo
|
||||||
|
spec:
|
||||||
|
flag1: value1
|
||||||
|
flag2: value2
|
||||||
|
items:
|
||||||
|
- 2
|
||||||
|
- 1
|
||||||
|
|
||||||
|
# this command:
|
||||||
|
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml run-fns xargs -- app
|
||||||
|
|
||||||
|
# is equivalent to this command:
|
||||||
|
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | app --flag1=value1 --flag2=value2 2 1
|
||||||
|
|
||||||
|
# echo: prints the app arguments
|
||||||
|
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- echo
|
||||||
|
--flag1=value1 --flag2=value2 2 1
|
||||||
|
|
||||||
|
# env: prints the app env
|
||||||
|
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- env
|
||||||
|
|
||||||
|
# cat: prints the app stdin -- prints the package contents and functionConfig wrapped in a
|
||||||
|
# ResourceList
|
||||||
|
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs --no-flags -- env
|
||||||
|
|
||||||
|
`,
|
||||||
|
RunE: r.runE,
|
||||||
|
SilenceUsage: true,
|
||||||
|
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
}
|
||||||
|
r.Command = c
|
||||||
|
r.Command.Flags().BoolVar(&r.EnvOnly, "env-only", false, "only add env vars, not flags")
|
||||||
|
c.Flags().StringVar(&r.WrapKind, "wrap-kind", "List", "wrap the input xargs give to the command in this type.")
|
||||||
|
c.Flags().StringVar(&r.WrapVersion, "wrap-version", "v1", "wrap the input xargs give to the command in this type.")
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runner contains the run function
|
||||||
|
type XArgsRunner struct {
|
||||||
|
Command *cobra.Command
|
||||||
|
Args []string
|
||||||
|
EnvOnly bool
|
||||||
|
WrapKind string
|
||||||
|
WrapVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
func XArgsCommand() *cobra.Command {
|
||||||
|
return GetXArgsRunner().Command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error {
|
||||||
|
if len(r.Args) == 0 {
|
||||||
|
r.Args = os.Args
|
||||||
|
}
|
||||||
|
cmdIndex := -1
|
||||||
|
for i := range r.Args {
|
||||||
|
if r.Args[i] == "--" {
|
||||||
|
cmdIndex = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cmdIndex < 0 {
|
||||||
|
return fmt.Errorf("must specify -- before command")
|
||||||
|
}
|
||||||
|
r.Args = r.Args[cmdIndex:]
|
||||||
|
run := exec.Command(r.Args[0])
|
||||||
|
|
||||||
|
if len(r.Args) > 1 {
|
||||||
|
r.Args = r.Args[cmdIndex+1:]
|
||||||
|
} else {
|
||||||
|
r.Args = []string{}
|
||||||
|
}
|
||||||
|
run.Stdout = c.OutOrStdout()
|
||||||
|
run.Stderr = c.ErrOrStderr()
|
||||||
|
|
||||||
|
rw := &kio.ByteReadWriter{
|
||||||
|
Reader: c.InOrStdin(),
|
||||||
|
}
|
||||||
|
nodes, err := rw.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
env := os.Environ()
|
||||||
|
|
||||||
|
// append the config to the flags
|
||||||
|
if err = func() error {
|
||||||
|
if rw.FunctionConfig == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
str, err := rw.FunctionConfig.String()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// add the API object to the env
|
||||||
|
env = append(env, fmt.Sprintf("KUST_FUNCTION_CONFIG=%s", str))
|
||||||
|
|
||||||
|
// parse the fields
|
||||||
|
meta := rw.FunctionConfig.Field("metadata")
|
||||||
|
if meta != nil {
|
||||||
|
err = meta.Value.VisitFields(func(node *yaml.MapNode) error {
|
||||||
|
if !r.EnvOnly {
|
||||||
|
r.Args = append(r.Args, fmt.Sprintf("--%s=%s",
|
||||||
|
node.Key.YNode().Value, parseYNode(node.Value.YNode())))
|
||||||
|
}
|
||||||
|
env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value),
|
||||||
|
node.Value.YNode().Value))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spec := rw.FunctionConfig.Field("spec")
|
||||||
|
if spec != nil {
|
||||||
|
err = spec.Value.VisitFields(func(node *yaml.MapNode) error {
|
||||||
|
if !r.EnvOnly {
|
||||||
|
r.Args = append(r.Args, fmt.Sprintf("--%s=%s",
|
||||||
|
node.Key.YNode().Value, parseYNode(node.Value.YNode())))
|
||||||
|
}
|
||||||
|
env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value),
|
||||||
|
node.Value.YNode().Value))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !r.EnvOnly {
|
||||||
|
items := rw.FunctionConfig.Field("items")
|
||||||
|
if items != nil {
|
||||||
|
err = items.Value.VisitElements(func(node *yaml.RNode) error {
|
||||||
|
r.Args = append(r.Args, parseYNode(node.YNode()))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.WrapKind != "" {
|
||||||
|
if kind := rw.FunctionConfig.Field("kind"); !yaml.IsFieldEmpty(kind) {
|
||||||
|
kind.Value.YNode().Value = r.WrapKind
|
||||||
|
}
|
||||||
|
rw.WrappingKind = r.WrapKind
|
||||||
|
}
|
||||||
|
if r.WrapVersion != "" {
|
||||||
|
if version := rw.FunctionConfig.Field("apiVersion"); !yaml.IsFieldEmpty(version) {
|
||||||
|
version.Value.YNode().Value = r.WrapVersion
|
||||||
|
}
|
||||||
|
rw.WrappingApiVersion = r.WrapVersion
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
run.Args = append(run.Args, r.Args...)
|
||||||
|
run.Env = append(run.Env, env...)
|
||||||
|
|
||||||
|
// write ResourceList to stdin
|
||||||
|
if err = func() error {
|
||||||
|
in, err := run.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
rw.Writer = in
|
||||||
|
if r.WrapKind != kio.ResourceListKind {
|
||||||
|
rw.FunctionConfig = nil
|
||||||
|
}
|
||||||
|
return rw.Write(nodes)
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return run.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseYNode(node *yaml.Node) string {
|
||||||
|
node.Value = strings.TrimSpace(node.Value)
|
||||||
|
for _, b := range node.Value {
|
||||||
|
if unicode.IsSpace(b) {
|
||||||
|
// wrap in '' -- contains whitespace
|
||||||
|
return fmt.Sprintf("'%s'", node.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node.Value
|
||||||
|
}
|
||||||
117
cmd/kyaml/cmd/cmdxargs_test.go
Normal file
117
cmd/kyaml/cmd/cmdxargs_test.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package cmd_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/kyaml/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagsInput = `kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Service
|
||||||
|
spec: {}
|
||||||
|
functionConfig:
|
||||||
|
kind: Foo
|
||||||
|
spec:
|
||||||
|
a: b
|
||||||
|
c: d
|
||||||
|
e: f
|
||||||
|
items:
|
||||||
|
- 1
|
||||||
|
- 3
|
||||||
|
- 2
|
||||||
|
- 4
|
||||||
|
`
|
||||||
|
|
||||||
|
resourceInput = `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Service
|
||||||
|
spec: {}
|
||||||
|
functionConfig:
|
||||||
|
kind: Foo
|
||||||
|
`
|
||||||
|
|
||||||
|
resourceOutput = `apiVersion: v1
|
||||||
|
kind: List
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Service
|
||||||
|
spec: {}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestXArgs_flags(t *testing.T) {
|
||||||
|
c := cmd.GetXArgsRunner()
|
||||||
|
c.Command.SetIn(bytes.NewBufferString(flagsInput))
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
c.Command.SetOut(out)
|
||||||
|
c.Command.SetArgs([]string{"--", "echo"})
|
||||||
|
|
||||||
|
c.Args = []string{"--", "echo"}
|
||||||
|
if !assert.NoError(t, c.Command.Execute()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
assert.Equal(t, `--a=b --c=d --e=f 1 3 2 4
|
||||||
|
`, out.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestXArgs_input(t *testing.T) {
|
||||||
|
c := cmd.GetXArgsRunner()
|
||||||
|
c.Command.SetIn(bytes.NewBufferString(resourceInput))
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
c.Command.SetOut(out)
|
||||||
|
c.Command.SetArgs([]string{"--", "cat"})
|
||||||
|
|
||||||
|
c.Args = []string{"--", "cat"}
|
||||||
|
if !assert.NoError(t, c.Command.Execute()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
assert.Equal(t, resourceOutput, out.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCmd_env(t *testing.T) {
|
||||||
|
c := cmd.GetXArgsRunner()
|
||||||
|
c.Command.SetIn(bytes.NewBufferString(flagsInput))
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
c.Command.SetOut(out)
|
||||||
|
c.Command.SetArgs([]string{"--env-only", "--", "env"})
|
||||||
|
|
||||||
|
c.Args = []string{"--", "env"}
|
||||||
|
if !assert.NoError(t, c.Command.Execute()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
assert.Contains(t, out.String(), "\nA=b\nC=d\nE=f\n")
|
||||||
|
}
|
||||||
98
cmd/kyaml/cmd/run-fns.go
Normal file
98
cmd/kyaml/cmd/run-fns.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetCatRunner returns a RunFnRunner.
|
||||||
|
func GetRunFnRunner() *RunFnRunner {
|
||||||
|
r := &RunFnRunner{}
|
||||||
|
c := &cobra.Command{
|
||||||
|
Use: "run-fns DIR",
|
||||||
|
Short: "Apply config functions to Resources.",
|
||||||
|
Long: `Apply config functions to Resources.
|
||||||
|
|
||||||
|
run-fns sequentially invokes all config functions in the directly, providing Resources
|
||||||
|
in the directory as input to the first function, and writing the output of the last
|
||||||
|
function back to the directory.
|
||||||
|
|
||||||
|
The ordering of functions is determined by the order they are encountered when walking the
|
||||||
|
directory. To clearly specify an ordering of functions, multiple functions may be
|
||||||
|
declared in the same file, separated by '---' (the functions will be invoked in the
|
||||||
|
order they appear in the file).
|
||||||
|
|
||||||
|
### Arguments:
|
||||||
|
|
||||||
|
DIR:
|
||||||
|
Path to local directory.
|
||||||
|
|
||||||
|
|
||||||
|
### Config Functions:
|
||||||
|
|
||||||
|
Config functions are specified as Kubernetes types containing a metadata.configFn.container.image
|
||||||
|
field. This fields tells run-fns how to invoke the container.
|
||||||
|
|
||||||
|
Example config function:
|
||||||
|
|
||||||
|
# in file example/fn.yaml
|
||||||
|
apiVersion: fn.example.com/v1beta1
|
||||||
|
kind: ExampleFunctionKind
|
||||||
|
metadata:
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
# function is invoked as a container running this image
|
||||||
|
image: gcr.io/example/examplefunction:v1.0.1
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true" # tools should ignore this
|
||||||
|
spec:
|
||||||
|
configField: configValue
|
||||||
|
|
||||||
|
In the preceding example, 'kyaml run-fns example/' would identify the function by
|
||||||
|
the metadata.configFn field. It would then write all Resources in the directory to
|
||||||
|
a container stdin (running the gcr.io/example/examplefunction:v1.0.1 image). It
|
||||||
|
would then writer the container stdout back to example/, replacing the directory
|
||||||
|
file contents.
|
||||||
|
`,
|
||||||
|
Example: `
|
||||||
|
kyaml run-fns example/
|
||||||
|
`,
|
||||||
|
RunE: r.runE,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
}
|
||||||
|
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
||||||
|
"also print resources from subpackages.")
|
||||||
|
r.Command = c
|
||||||
|
r.Command.Flags().BoolVar(
|
||||||
|
&r.DryRun, "dry-run", false, "print results to stdout")
|
||||||
|
r.Command.Flags().StringSliceVar(
|
||||||
|
&r.FnPaths, "fn-path", []string{},
|
||||||
|
"directories containing functions without configuration")
|
||||||
|
r.Command.AddCommand(XArgsCommand())
|
||||||
|
r.Command.AddCommand(WrapCommand())
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunFnCommand() *cobra.Command {
|
||||||
|
return GetRunFnRunner().Command
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunFnRunner contains the run function
|
||||||
|
type RunFnRunner struct {
|
||||||
|
IncludeSubpackages bool
|
||||||
|
Command *cobra.Command
|
||||||
|
DryRun bool
|
||||||
|
FnPaths []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
||||||
|
rec := runfn.RunFns{Path: args[0], FunctionPaths: r.FnPaths}
|
||||||
|
if r.DryRun {
|
||||||
|
rec.Output = c.OutOrStdout()
|
||||||
|
}
|
||||||
|
return rec.Execute()
|
||||||
|
|
||||||
|
}
|
||||||
15
cmd/kyaml/cmd/test/override.yaml
Normal file
15
cmd/kyaml/cmd/test/override.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
image: nginx:v1.9
|
||||||
51
cmd/kyaml/cmd/test/test.sh
Executable file
51
cmd/kyaml/cmd/test/test.sh
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cat <<End-of-message
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: ${NAME}
|
||||||
|
spec:
|
||||||
|
replicas: ${REPLICAS}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
name: ${NAME}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: ${NAME}
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ${NAME}
|
||||||
|
image: nginx:v1.7
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ${NAME}
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: ${NAME}
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
# This i the port.
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
name: ${NAME}
|
||||||
|
End-of-message
|
||||||
@@ -84,10 +84,10 @@ kubectl get all,applications,releasetracks -o yaml | kyaml tree --structure=grap
|
|||||||
c.Flags().BoolVar(&r.env, "env", false, "print env field")
|
c.Flags().BoolVar(&r.env, "env", false, "print env field")
|
||||||
c.Flags().BoolVar(&r.all, "all", false, "print all field infos")
|
c.Flags().BoolVar(&r.all, "all", false, "print all field infos")
|
||||||
c.Flags().StringSliceVar(&r.fields, "field", []string{}, "print field")
|
c.Flags().StringSliceVar(&r.fields, "field", []string{}, "print field")
|
||||||
c.Flags().BoolVar(&r.includeReconcilers, "include-reconcilers", false,
|
c.Flags().BoolVar(&r.includeLocal, "include-local", false,
|
||||||
"if true, include reconciler Resources in the output.")
|
"if true, include local-config in the output.")
|
||||||
c.Flags().BoolVar(&r.excludeNonReconcilers, "exclude-non-reconcilers", false,
|
c.Flags().BoolVar(&r.excludeNonLocal, "exclude-non-local", false,
|
||||||
"if true, exclude non-reconciler Resources in the output.")
|
"if true, exclude non-local-config in the output.")
|
||||||
c.Flags().StringVar(&r.structure, "graph-structure", "directory",
|
c.Flags().StringVar(&r.structure, "graph-structure", "directory",
|
||||||
"Graph structure to use for printing the tree. may be 'directory' or 'owners'.")
|
"Graph structure to use for printing the tree. may be 'directory' or 'owners'.")
|
||||||
|
|
||||||
@@ -101,21 +101,21 @@ func TreeCommand() *cobra.Command {
|
|||||||
|
|
||||||
// TreeRunner contains the run function
|
// TreeRunner contains the run function
|
||||||
type TreeRunner struct {
|
type TreeRunner struct {
|
||||||
IncludeSubpackages bool
|
IncludeSubpackages bool
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
name bool
|
name bool
|
||||||
resources bool
|
resources bool
|
||||||
ports bool
|
ports bool
|
||||||
images bool
|
images bool
|
||||||
replicas bool
|
replicas bool
|
||||||
all bool
|
all bool
|
||||||
env bool
|
env bool
|
||||||
args bool
|
args bool
|
||||||
cmd bool
|
cmd bool
|
||||||
fields []string
|
fields []string
|
||||||
includeReconcilers bool
|
includeLocal bool
|
||||||
excludeNonReconcilers bool
|
excludeNonLocal bool
|
||||||
structure string
|
structure string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
|
func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
|
||||||
@@ -189,9 +189,9 @@ func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show reconcilers in tree
|
// show reconcilers in tree
|
||||||
fltrs := []kio.Filter{&filters.IsReconcilerFilter{
|
fltrs := []kio.Filter{&filters.IsLocalConfig{
|
||||||
ExcludeReconcilers: !r.includeReconcilers,
|
IncludeLocalConfig: r.includeLocal,
|
||||||
IncludeNonReconcilers: !r.excludeNonReconcilers,
|
ExcludeNonLocalConfig: r.excludeNonLocal,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
return handleError(c, kio.Pipeline{
|
return handleError(c, kio.Pipeline{
|
||||||
|
|||||||
@@ -24,10 +24,15 @@ func TestTreeCommand_files(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||||
apiVersion: gcr.io/example/reconciler:v1
|
apiVersion: v1
|
||||||
kind: Abstraction
|
kind: Abstraction
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example/reconciler:v1
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
---
|
---
|
||||||
@@ -259,7 +264,7 @@ spec:
|
|||||||
// fmt the files
|
// fmt the files
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
r := cmd.GetTreeRunner()
|
r := cmd.GetTreeRunner()
|
||||||
r.Command.SetArgs([]string{d, "--include-reconcilers"})
|
r.Command.SetArgs([]string{d, "--include-local"})
|
||||||
r.Command.SetOut(b)
|
r.Command.SetOut(b)
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
if !assert.NoError(t, r.Command.Execute()) {
|
||||||
return
|
return
|
||||||
@@ -306,10 +311,15 @@ spec:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||||
apiVersion: gcr.io/example/reconciler:v1
|
apiVersion: v1
|
||||||
kind: Abstraction
|
kind: Abstraction
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example/reconciler:v1
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
---
|
---
|
||||||
@@ -331,7 +341,7 @@ spec:
|
|||||||
// fmt the files
|
// fmt the files
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
r := cmd.GetTreeRunner()
|
r := cmd.GetTreeRunner()
|
||||||
r.Command.SetArgs([]string{d, "--include-reconcilers", "--exclude-non-reconcilers"})
|
r.Command.SetArgs([]string{d, "--include-local", "--exclude-non-local"})
|
||||||
r.Command.SetOut(b)
|
r.Command.SetOut(b)
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
if !assert.NoError(t, r.Command.Execute()) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ func main() {
|
|||||||
root.AddCommand(cmd.FmtCommand())
|
root.AddCommand(cmd.FmtCommand())
|
||||||
root.AddCommand(cmd.MergeCommand())
|
root.AddCommand(cmd.MergeCommand())
|
||||||
root.AddCommand(cmd.CountCommand())
|
root.AddCommand(cmd.CountCommand())
|
||||||
|
root.AddCommand(cmd.RunFnCommand())
|
||||||
root.AddCommand(&cobra.Command{Use: "merge", Long: merge2.Help})
|
root.AddCommand(&cobra.Command{Use: "merge", Long: merge2.Help})
|
||||||
root.AddCommand(&cobra.Command{Use: "merge3", Long: merge3.Help})
|
root.AddCommand(&cobra.Command{Use: "merge3", Long: merge3.Help})
|
||||||
|
|
||||||
|
|||||||
34
hack/testExamplesAgainstKustomize.sh
Executable file
34
hack/testExamplesAgainstKustomize.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
set -o nounset
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
version=$1
|
||||||
|
|
||||||
|
function onLinuxAndNotOnTravis {
|
||||||
|
[[ ("linux" == "$(go env GOOS)") && (-z ${TRAVIS+x}) ]] && return
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: change the label?
|
||||||
|
# We test against the latest release, and HEAD, and presumably
|
||||||
|
# any branch using this label, so it should probably get
|
||||||
|
# a new value.
|
||||||
|
mdrip --mode test \
|
||||||
|
--label testAgainstLatestRelease examples
|
||||||
|
|
||||||
|
# TODO: make work for non-linux
|
||||||
|
if onLinuxAndNotOnTravis; then
|
||||||
|
echo "On linux, and not on travis, so running the notravis example tests."
|
||||||
|
|
||||||
|
# Requires helm.
|
||||||
|
make $(go env GOPATH)/bin/helm
|
||||||
|
mdrip --mode test \
|
||||||
|
--label helmtest examples/chart.md
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Example tests passed against ${version}."
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
# Copyright 2019 The Kubernetes Authors.
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
# TODO: get rid of this script, make individual targets
|
|
||||||
# in the makefile.
|
|
||||||
|
|
||||||
# Run this script with no arguments from the repo root
|
# Run this script with no arguments from the repo root
|
||||||
# to test all the plugins.
|
# to test all the plugins.
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ResourceListKind = "ResourceList"
|
ResourceListKind = "ResourceList"
|
||||||
ResourceListApiVersion = "kyaml.kustomize.dev/v1alpha1"
|
ResourceListApiVersion = "config.kubernetes.io/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ByteReadWriter reads from an input and writes to an output.
|
// ByteReadWriter reads from an input and writes to an output.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ i: j
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestByteReader_Read_wrappedResourceßßList(t *testing.T) {
|
func TestByteReader_Read_wrappedResourceßßList(t *testing.T) {
|
||||||
r := &ByteReader{Reader: bytes.NewBufferString(`apiVersion: kyaml.kustomize.dev/v1alpha1
|
r := &ByteReader{Reader: bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1
|
||||||
kind: ResourceList
|
kind: ResourceList
|
||||||
functionConfig:
|
functionConfig:
|
||||||
foo: bar
|
foo: bar
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ g:
|
|||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
|
assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
kind: ResourceList
|
kind: ResourceList
|
||||||
items:
|
items:
|
||||||
- c: d # second
|
- c: d # second
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ package filters
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
@@ -24,6 +25,8 @@ import (
|
|||||||
// The full set of environment variables from the parent process
|
// The full set of environment variables from the parent process
|
||||||
// are passed to the container.
|
// are passed to the container.
|
||||||
type ContainerFilter struct {
|
type ContainerFilter struct {
|
||||||
|
mountPath string
|
||||||
|
|
||||||
// Image is the container image to use to create a container.
|
// Image is the container image to use to create a container.
|
||||||
Image string `yaml:"image,omitempty"`
|
Image string `yaml:"image,omitempty"`
|
||||||
|
|
||||||
@@ -41,6 +44,10 @@ type ContainerFilter struct {
|
|||||||
checkInput func(string)
|
checkInput func(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ContainerFilter) SetMountPath(path string) {
|
||||||
|
c.mountPath = path
|
||||||
|
}
|
||||||
|
|
||||||
// GrepFilter implements kio.GrepFilter
|
// GrepFilter implements kio.GrepFilter
|
||||||
func (c *ContainerFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (c *ContainerFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
// get the command to filter the Resources
|
// get the command to filter the Resources
|
||||||
@@ -98,6 +105,10 @@ func (c *ContainerFilter) getArgs() []string {
|
|||||||
// don't make fs readonly because things like heredoc rely on writing tmp files
|
// don't make fs readonly because things like heredoc rely on writing tmp files
|
||||||
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
|
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
|
||||||
}
|
}
|
||||||
|
// mount the directory containing the function as read-only
|
||||||
|
if c.mountPath != "" {
|
||||||
|
args = append(args, "-v", fmt.Sprintf("%s:/local/:ro", c.mountPath))
|
||||||
|
}
|
||||||
|
|
||||||
// export the local environment vars to the container
|
// export the local environment vars to the container
|
||||||
for _, pair := range os.Environ() {
|
for _, pair := range os.Environ() {
|
||||||
@@ -150,7 +161,8 @@ type IsReconcilerFilter struct {
|
|||||||
func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
var out []*yaml.RNode
|
var out []*yaml.RNode
|
||||||
for i := range inputs {
|
for i := range inputs {
|
||||||
isContainerResource := GetContainerName(inputs[i]) != ""
|
img, _ := GetContainerName(inputs[i])
|
||||||
|
isContainerResource := img != ""
|
||||||
if isContainerResource && !c.ExcludeReconcilers {
|
if isContainerResource && !c.ExcludeReconcilers {
|
||||||
out = append(out, inputs[i])
|
out = append(out, inputs[i])
|
||||||
}
|
}
|
||||||
@@ -162,19 +174,20 @@ func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetContainerName returns the container image for an API if one exists
|
// GetContainerName returns the container image for an API if one exists
|
||||||
func GetContainerName(n *yaml.RNode) string {
|
func GetContainerName(n *yaml.RNode) (string, string) {
|
||||||
meta, _ := n.GetMeta()
|
meta, _ := n.GetMeta()
|
||||||
container := meta.Annotations["kyaml.kustomize.dev/container"]
|
|
||||||
|
// path to the function, this will be mounted into the container
|
||||||
|
path := meta.Annotations[kioutil.PathAnnotation]
|
||||||
|
|
||||||
|
container := meta.Annotations["config.kubernetes.io/container"]
|
||||||
if container != "" {
|
if container != "" {
|
||||||
return container
|
return container, path
|
||||||
}
|
}
|
||||||
|
|
||||||
if match.MatchString(meta.ApiVersion) {
|
image, err := n.Pipe(yaml.Lookup("metadata", "configFn", "container", "image"))
|
||||||
return meta.ApiVersion
|
if err != nil || yaml.IsMissingOrNull(image) {
|
||||||
|
return "", path
|
||||||
}
|
}
|
||||||
|
return image.YNode().Value, path
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// match specifies the set of apiVersions to recognize as being container images
|
|
||||||
var match = regexp.MustCompile(`(docker\.io|.*\.?gcr\.io)/.*(:.*)?`)
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ package filters
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -60,6 +62,43 @@ metadata:
|
|||||||
assert.True(t, foundKyaml)
|
assert.True(t, foundKyaml)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFilter_commandMountPath(t *testing.T) {
|
||||||
|
cfg, err := yaml.Parse(`apiversion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance := &ContainerFilter{
|
||||||
|
Image: "example.com:version",
|
||||||
|
Config: cfg,
|
||||||
|
mountPath: filepath.Join("mount", "path"),
|
||||||
|
}
|
||||||
|
os.Setenv("KYAML_TEST", "FOO")
|
||||||
|
cmd, err := instance.getCommand()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
"docker", "run",
|
||||||
|
"--rm",
|
||||||
|
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
|
||||||
|
"--network", "none",
|
||||||
|
"--user", "nobody",
|
||||||
|
"--security-opt=no-new-privileges",
|
||||||
|
"-v", fmt.Sprintf("%s:/local/:ro", filepath.Join("mount", "path")),
|
||||||
|
}
|
||||||
|
for _, e := range os.Environ() {
|
||||||
|
// the process env
|
||||||
|
expected = append(expected, "-e", strings.Split(e, "=")[0])
|
||||||
|
}
|
||||||
|
expected = append(expected, "example.com:version")
|
||||||
|
assert.Equal(t, expected, cmd.Args)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFilter_command_network(t *testing.T) {
|
func TestFilter_command_network(t *testing.T) {
|
||||||
cfg, err := yaml.Parse(`apiversion: apps/v1
|
cfg, err := yaml.Parse(`apiversion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -127,7 +166,7 @@ metadata:
|
|||||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||||
checkInput: func(s string) {
|
checkInput: func(s string) {
|
||||||
called = true
|
called = true
|
||||||
if !assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
|
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
kind: ResourceList
|
kind: ResourceList
|
||||||
items:
|
items:
|
||||||
- apiVersion: apps/v1
|
- apiVersion: apps/v1
|
||||||
@@ -209,7 +248,7 @@ metadata:
|
|||||||
args: []string{"sh", "-c", "cat <&0"},
|
args: []string{"sh", "-c", "cat <&0"},
|
||||||
checkInput: func(s string) {
|
checkInput: func(s string) {
|
||||||
called = true
|
called = true
|
||||||
if !assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
|
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
kind: ResourceList
|
kind: ResourceList
|
||||||
items:
|
items:
|
||||||
- apiversion: apps/v1
|
- apiversion: apps/v1
|
||||||
@@ -261,36 +300,30 @@ metadata:
|
|||||||
|
|
||||||
func Test_GetContainerName(t *testing.T) {
|
func Test_GetContainerName(t *testing.T) {
|
||||||
// make sure gcr.io works
|
// make sure gcr.io works
|
||||||
n, err := yaml.Parse(`apiVersion: gcr.io/foo/bar:something
|
n, err := yaml.Parse(`apiVersion: v1beta1
|
||||||
kind: MyThing
|
kind: MyThing
|
||||||
|
metadata:
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/foo/bar:something
|
||||||
`)
|
`)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c := GetContainerName(n)
|
c, _ := GetContainerName(n)
|
||||||
assert.Equal(t, "gcr.io/foo/bar:something", c)
|
assert.Equal(t, "gcr.io/foo/bar:something", c)
|
||||||
|
|
||||||
// make sure regional gcr.io works
|
|
||||||
n, err = yaml.Parse(`apiVersion: us.gcr.io/foo/bar:something
|
|
||||||
kind: MyThing
|
|
||||||
`)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c = GetContainerName(n)
|
|
||||||
assert.Equal(t, "us.gcr.io/foo/bar:something", c)
|
|
||||||
|
|
||||||
// container from annotation
|
// container from annotation
|
||||||
n, err = yaml.Parse(`apiVersion: v1
|
n, err = yaml.Parse(`apiVersion: v1
|
||||||
kind: MyThing
|
kind: MyThing
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
kyaml.kustomize.dev/container: gcr.io/foo/bar:something
|
config.kubernetes.io/container: gcr.io/foo/bar:something
|
||||||
`)
|
`)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c = GetContainerName(n)
|
c, _ = GetContainerName(n)
|
||||||
assert.Equal(t, "gcr.io/foo/bar:something", c)
|
assert.Equal(t, "gcr.io/foo/bar:something", c)
|
||||||
|
|
||||||
// doesn't have a container
|
// doesn't have a container
|
||||||
@@ -301,16 +334,6 @@ metadata:
|
|||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c = GetContainerName(n)
|
c, _ = GetContainerName(n)
|
||||||
assert.Equal(t, "", c)
|
assert.Equal(t, "", c)
|
||||||
|
|
||||||
// make sure docker.io works
|
|
||||||
n, err = yaml.Parse(`apiVersion: docker.io/foo/bar:something
|
|
||||||
kind: MyThing
|
|
||||||
`)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c = GetContainerName(n)
|
|
||||||
assert.Equal(t, "docker.io/foo/bar:something", c)
|
|
||||||
}
|
}
|
||||||
|
|||||||
38
kyaml/kio/filters/local.go
Normal file
38
kyaml/kio/filters/local.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const LocalConfigAnnotation = "config.kubernetes.io/local-config"
|
||||||
|
|
||||||
|
// IsLocalConfig filters Resources using the config.kubernetes.io/local-config annotation
|
||||||
|
type IsLocalConfig struct {
|
||||||
|
// IncludeLocalConfig will include local-config if set to true
|
||||||
|
IncludeLocalConfig bool `yaml:"includeLocalConfig,omitempty"`
|
||||||
|
|
||||||
|
// ExcludeNonLocalConfig will exclude non local-config if set to true
|
||||||
|
ExcludeNonLocalConfig bool `yaml:"excludeNonLocalConfig,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter implements kio.Filter
|
||||||
|
func (c *IsLocalConfig) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
var out []*yaml.RNode
|
||||||
|
for i := range inputs {
|
||||||
|
meta, err := inputs[i].GetMeta()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, local := meta.Annotations[LocalConfigAnnotation]
|
||||||
|
|
||||||
|
if local && c.IncludeLocalConfig {
|
||||||
|
out = append(out, inputs[i])
|
||||||
|
} else if !local && !c.ExcludeNonLocalConfig {
|
||||||
|
out = append(out, inputs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mergeSourceAnnotation = "kyaml.kustomize.dev/merge-source"
|
mergeSourceAnnotation = "config.kubernetes.io/merge-source"
|
||||||
mergeSourceOriginal = "original"
|
mergeSourceOriginal = "original"
|
||||||
mergeSourceUpdated = "updated"
|
mergeSourceUpdated = "updated"
|
||||||
mergeSourceDest = "dest"
|
mergeSourceDest = "dest"
|
||||||
|
|||||||
98
kyaml/runfn/runfn.go
Normal file
98
kyaml/runfn/runfn.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package runfn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunFns runs the set of configuration functions in a local directory against
|
||||||
|
// the Resources in that directory
|
||||||
|
type RunFns struct {
|
||||||
|
// Path is the path to the directory containing functions
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// FunctionPaths Paths allows functions to be specified outside the configuration
|
||||||
|
// directory
|
||||||
|
FunctionPaths []string
|
||||||
|
|
||||||
|
// Output can be set to write the result to Output rather than back to the directory
|
||||||
|
Output io.Writer
|
||||||
|
|
||||||
|
// containerFilterProvider may be override by tests to fake invoking containers
|
||||||
|
containerFilterProvider func(string, string, *yaml.RNode) kio.Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute runs the command
|
||||||
|
func (r RunFns) Execute() error {
|
||||||
|
// make the path absolute so it works on mac
|
||||||
|
var err error
|
||||||
|
r.Path, err = filepath.Abs(r.Path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// default the containerFilterProvider if it hasn't been override. Split out for testing.
|
||||||
|
(&r).init()
|
||||||
|
|
||||||
|
// identify the configuration functions in the directory
|
||||||
|
buff := &kio.PackageBuffer{}
|
||||||
|
err = kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{kio.LocalPackageReader{PackagePath: r.Path}},
|
||||||
|
Filters: []kio.Filter{&filters.IsReconcilerFilter{}},
|
||||||
|
Outputs: []kio.Writer{buff},
|
||||||
|
}.Execute()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// accept a
|
||||||
|
for i := range r.FunctionPaths {
|
||||||
|
err := kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{kio.LocalPackageReader{PackagePath: r.FunctionPaths[i]}},
|
||||||
|
Outputs: []kio.Writer{buff},
|
||||||
|
}.Execute()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconcile each local API
|
||||||
|
var fltrs []kio.Filter
|
||||||
|
for i := range buff.Nodes {
|
||||||
|
api := buff.Nodes[i]
|
||||||
|
img, path := filters.GetContainerName(api)
|
||||||
|
fltrs = append(fltrs, r.containerFilterProvider(img, path, api))
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgIO := &kio.LocalPackageReadWriter{PackagePath: r.Path}
|
||||||
|
inputs := []kio.Reader{pkgIO}
|
||||||
|
var outputs []kio.Writer
|
||||||
|
if r.Output == nil {
|
||||||
|
// write back to the package
|
||||||
|
outputs = append(outputs, pkgIO)
|
||||||
|
} else {
|
||||||
|
// write to the output instead of the directory
|
||||||
|
outputs = append(outputs, kio.ByteWriter{Writer: r.Output})
|
||||||
|
}
|
||||||
|
return kio.Pipeline{Inputs: inputs, Filters: fltrs, Outputs: outputs}.Execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes the RunFns with a containerFilterProvider.
|
||||||
|
func (r *RunFns) init() {
|
||||||
|
// if containerFilterProvider hasn't been set, use the default
|
||||||
|
if r.containerFilterProvider == nil {
|
||||||
|
r.containerFilterProvider = func(image, path string, api *yaml.RNode) kio.Filter {
|
||||||
|
cf := &filters.ContainerFilter{Image: image, Config: api}
|
||||||
|
cf.SetMountPath(filepath.Join(r.Path, path))
|
||||||
|
return cf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
248
kyaml/runfn/runfn_test.go
Normal file
248
kyaml/runfn/runfn_test.go
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package runfn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRunFns_Execute(t *testing.T) {
|
||||||
|
instance := RunFns{}
|
||||||
|
instance.init()
|
||||||
|
api, err := yaml.Parse(`apiVersion: apps/v1
|
||||||
|
kind:
|
||||||
|
`)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filter := instance.containerFilterProvider("example.com:version", "", api)
|
||||||
|
assert.Equal(t, &filters.ContainerFilter{Image: "example.com:version", Config: api}, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCmd_Execute(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "kustomize-kyaml-test")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
_, filename, _, ok := runtime.Caller(0)
|
||||||
|
if !assert.True(t, ok) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
ds, err := filepath.Abs(filepath.Join(filepath.Dir(filename), "test", "testdata"))
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, copyutil.CopyDir(ds, dir)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, os.Chdir(filepath.Dir(dir))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// write a test filter
|
||||||
|
f := `apiVersion: v1
|
||||||
|
kind: ValueReplacer
|
||||||
|
metadata:
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example.com/image:version
|
||||||
|
stringMatch: Deployment
|
||||||
|
replace: StatefulSet
|
||||||
|
`
|
||||||
|
if !assert.NoError(t, ioutil.WriteFile(
|
||||||
|
filepath.Join(dir, "filter.yaml"), []byte(f), 0600)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := RunFns{
|
||||||
|
Path: dir,
|
||||||
|
containerFilterProvider: func(s, _ string, node *yaml.RNode) kio.Filter {
|
||||||
|
// parse the filter from the input
|
||||||
|
filter := yaml.YFilter{}
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
e := yaml.NewEncoder(b)
|
||||||
|
if !assert.NoError(t, e.Encode(node.YNode())) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
e.Close()
|
||||||
|
d := yaml.NewDecoder(b)
|
||||||
|
if !assert.NoError(t, d.Decode(&filter)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters.Modifier{
|
||||||
|
Filters: []yaml.YFilter{{Filter: yaml.Lookup("kind")}, filter},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, instance.Execute()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(
|
||||||
|
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
assert.Contains(t, string(b), "kind: StatefulSet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCmd_Execute_APIs(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "kustomize-kyaml-test")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
_, filename, _, ok := runtime.Caller(0)
|
||||||
|
if !assert.True(t, ok) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
ds, err := filepath.Abs(filepath.Join(filepath.Dir(filename), "test", "testdata"))
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, copyutil.CopyDir(ds, dir)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, os.Chdir(filepath.Dir(dir))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// write a test filter
|
||||||
|
f := `apiVersion: v1
|
||||||
|
kind: ValueReplacer
|
||||||
|
metadata:
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example.com/image:version
|
||||||
|
stringMatch: Deployment
|
||||||
|
replace: StatefulSet
|
||||||
|
`
|
||||||
|
tmpF, err := ioutil.TempFile("", "filter*.yaml")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os.RemoveAll(tmpF.Name())
|
||||||
|
if !assert.NoError(t, ioutil.WriteFile(tmpF.Name(), []byte(f), 0600)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := RunFns{
|
||||||
|
FunctionPaths: []string{tmpF.Name()},
|
||||||
|
Path: dir,
|
||||||
|
containerFilterProvider: func(s, _ string, node *yaml.RNode) kio.Filter {
|
||||||
|
// parse the filter from the input
|
||||||
|
filter := yaml.YFilter{}
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
e := yaml.NewEncoder(b)
|
||||||
|
if !assert.NoError(t, e.Encode(node.YNode())) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
e.Close()
|
||||||
|
d := yaml.NewDecoder(b)
|
||||||
|
if !assert.NoError(t, d.Decode(&filter)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters.Modifier{
|
||||||
|
Filters: []yaml.YFilter{{Filter: yaml.Lookup("kind")}, filter},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = instance.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(
|
||||||
|
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Contains(t, string(b), "kind: StatefulSet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCmd_Execute_Stdout(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "kustomize-kyaml-test")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
_, filename, _, ok := runtime.Caller(0)
|
||||||
|
if !assert.True(t, ok) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
ds, err := filepath.Abs(filepath.Join(filepath.Dir(filename), "test", "testdata"))
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, copyutil.CopyDir(ds, dir)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, os.Chdir(filepath.Dir(dir))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// write a test filter
|
||||||
|
f := `apiVersion: v1
|
||||||
|
kind: ValueReplacer
|
||||||
|
metadata:
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example.com/image:version
|
||||||
|
stringMatch: Deployment
|
||||||
|
replace: StatefulSet
|
||||||
|
`
|
||||||
|
if !assert.NoError(t, ioutil.WriteFile(
|
||||||
|
filepath.Join(dir, "filter.yaml"), []byte(f), 0600)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
instance := RunFns{
|
||||||
|
Output: out,
|
||||||
|
Path: dir,
|
||||||
|
containerFilterProvider: func(s, _ string, node *yaml.RNode) kio.Filter {
|
||||||
|
// parse the filter from the input
|
||||||
|
filter := yaml.YFilter{}
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
e := yaml.NewEncoder(b)
|
||||||
|
if !assert.NoError(t, e.Encode(node.YNode())) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
e.Close()
|
||||||
|
d := yaml.NewDecoder(b)
|
||||||
|
if !assert.NoError(t, d.Decode(&filter)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters.Modifier{
|
||||||
|
Filters: []yaml.YFilter{{Filter: yaml.Lookup("kind")}, filter},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, instance.Execute()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(
|
||||||
|
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NotContains(t, string(b), "kind: StatefulSet")
|
||||||
|
assert.Contains(t, out.String(), "kind: StatefulSet")
|
||||||
|
}
|
||||||
10
kyaml/runfn/test/testdata/java/java-configmap.resource.yaml
vendored
Normal file
10
kyaml/runfn/test/testdata/java/java-configmap.resource.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: app-config
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: undefined
|
||||||
|
app.kubernetes.io/instance: undefined
|
||||||
|
data: {}
|
||||||
36
kyaml/runfn/test/testdata/java/java-deployment.resource.yaml
vendored
Normal file
36
kyaml/runfn/test/testdata/java/java-deployment.resource.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: app
|
||||||
|
labels:
|
||||||
|
app: java
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: java
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: java
|
||||||
|
spec:
|
||||||
|
restartPolicy: Always
|
||||||
|
containers:
|
||||||
|
- name: app
|
||||||
|
image: gcr.io/project/app:version
|
||||||
|
command:
|
||||||
|
- java
|
||||||
|
- -jar
|
||||||
|
- /app.jar
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: app-config
|
||||||
|
env:
|
||||||
|
- name: JAVA_OPTS
|
||||||
|
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
|
||||||
|
-Djava.security.egd=file:/dev/./urandom
|
||||||
|
imagePullPolicy: Always
|
||||||
|
minReadySeconds: 5
|
||||||
15
kyaml/runfn/test/testdata/java/java-service.resource.yaml
vendored
Normal file
15
kyaml/runfn/test/testdata/java/java-service.resource.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: app
|
||||||
|
labels:
|
||||||
|
app: java
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: java
|
||||||
|
ports:
|
||||||
|
- name: "8080"
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
@@ -1,36 +1,57 @@
|
|||||||
# Pseudo Modules
|
# Pseudo k8s
|
||||||
|
|
||||||
This package contains dependencies copied from kubernetes/kubernetes repos which
|
[kubernetes/api]: https://github.com/kubernetes/api
|
||||||
are synced out of staging.
|
[kubernetes/apimachinery]: https://github.com/kubernetes/apimachinery
|
||||||
|
[kubernetes/client-go]: https://github.com/kubernetes/client-go
|
||||||
|
[init-pseudo-module.sh]: init-pseudo-module.sh
|
||||||
|
|
||||||
The long term plan is to move off of the staging libraries entirely in favor of
|
The module defined below in `pseudo/k8s` contains code generated by the
|
||||||
more suitable libraries developed in the Kustomize repo.
|
[init-pseudo-module.sh] script.
|
||||||
|
|
||||||
|
The module contains clones (see the [script][init-pseudo-module.sh] for the specific clone tag) of code from the following repositories:
|
||||||
|
|
||||||
|
- [kubernetes/api]
|
||||||
|
- [kubernetes/apimachinery]
|
||||||
|
- [kubernetes/client-go]
|
||||||
|
|
||||||
|
These repositories are in turn snapshots of code being developed
|
||||||
|
in [kubernetes staging](https://github.com/kubernetes/kubernetes/tree/master/staging).
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
|
|
||||||
1. Vendoring the Kustomize API in other tools
|
[`cli-runtime`]: https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/cli-runtime
|
||||||
|
[`apimachinery`]: https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/apimachinery
|
||||||
|
|
||||||
The Kubernetes staging packages do not have stable APIs, and frequently break compatibility.
|
In what follows, let the word `apimachinery` generally represent one of more of the modules [kubernetes/api], [kubernetes/apimachinery], [kubernetes/client-go].
|
||||||
This makes it difficult for other tools to vendor the Kustomize APIs, as they may depend
|
|
||||||
on incompatible versions of the staging APIs. By forking the staging libraries, we
|
|
||||||
ensure that we are using our own copy which will not conflict with other versions.
|
|
||||||
|
|
||||||
2. Vendoring into kubectl
|
- kubectl depends on `apimachinery` in the k/k repo via a `go.mod` replacement.
|
||||||
|
|
||||||
Packages that depend upon staging may not be vendored into kubernetes/kubernetes. By forking
|
- kubectl must depend the on kustomize API module.
|
||||||
the staging packages, we break this circular dependency so that the kustomize packages may
|
|
||||||
be vendored into kubernetes/kubernetes without depending on code originating out of
|
|
||||||
kubernetes/kubernetes.
|
|
||||||
|
|
||||||
## Who?
|
- `apimachinery` doesn’t yet do releases distinguished by major version number. I.e., one cannot write a `go.mod` file containing the lines
|
||||||
|
> ```
|
||||||
|
> require k8s.io/apimachinery v1.1.4
|
||||||
|
> require k8s.io/apimachinery/v2 v2.1.0
|
||||||
|
> require k8s.io/apimachinery/v3 v3.2.1
|
||||||
|
> ```
|
||||||
|
The only option at this time are pseudo-versions of the form `v0.0.0-20181127025237-2b1284ed4c93` and everything in the k/k repo must depend on exactly one pseudo version, which is typically a v.0.0.0 with a replace directive specifying an in-repo relative path.
|
||||||
|
|
||||||
While it is possible to depend upon them from modules outside the Kustomize repository,
|
- kustomize depends on `apimachinery`.
|
||||||
there is not guarantee that this will continue to work in the future.
|
|
||||||
|
|
||||||
The pseudo modules may be removed at anytime in the future without warning and no
|
This creates a problem, since kubectl and kustomize have no means to declare a dependence on different versions of `apimachinery`, and no means to sync on the same version since kubectl and kustomize live in different repositories.
|
||||||
|
|
||||||
|
A solution to this is a _manual vendor_: either the kustomize API module source code must be manually snapshotted into the k/k repo at some invented path that could never be confused with a normal module import path, or the `apimachinery` module (and friends) must be snapshotted into the kustomize repo using the same notion of import path replacement.
|
||||||
|
|
||||||
|
This module is the result of taking the latter option. kustomize can depend on this snapshot of `apimachinery`, and both can be vendored into the k/k repo safely.
|
||||||
|
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
This pseudo k8s module may be removed at anytime in the future without warning and no
|
||||||
support will be given for these modules.
|
support will be given for these modules.
|
||||||
|
It's best to try to encourage semver development in `apimachinery` directly.
|
||||||
|
|
||||||
## How?
|
## How was this module made?
|
||||||
|
|
||||||
These libraries were forked by running `git clone` to clone the repos.
|
These libraries were forked by running `git clone` to clone the repos.
|
||||||
|
|
||||||
@@ -41,6 +62,4 @@ These libraries were forked by running `git clone` to clone the repos.
|
|||||||
2. Run the [init-pseudo-module.sh](init-pseudo-module.sh) script to clone and configure pseudo deps
|
2. Run the [init-pseudo-module.sh](init-pseudo-module.sh) script to clone and configure pseudo deps
|
||||||
- From the root directory -- `$ psuedo/init-pseudo-module.sh`
|
- From the root directory -- `$ psuedo/init-pseudo-module.sh`
|
||||||
|
|
||||||
### Using the Pseudo Modules in Kustomize
|
|
||||||
|
|
||||||
TODO(pwittrock): Write this once it has been done successfully
|
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Tracks success or failure of various operations.
|
|
||||||
# 0==success, any other value is a failure.
|
|
||||||
rcAccumulator=0
|
|
||||||
|
|
||||||
function removeBin {
|
|
||||||
local d=$(go env GOPATH)/bin/$1
|
|
||||||
echo "Removing binary $d"
|
|
||||||
/bin/rm -f $d
|
|
||||||
}
|
|
||||||
|
|
||||||
function installTools {
|
|
||||||
make install-tools
|
|
||||||
MDRIP=$(go env GOPATH)/bin/mdrip
|
|
||||||
ls -l $(go env GOPATH)/bin
|
|
||||||
}
|
|
||||||
|
|
||||||
function runFunc {
|
|
||||||
local name=$1
|
|
||||||
local result="SUCCESS"
|
|
||||||
printf "============== begin %s\n" "$name"
|
|
||||||
$name
|
|
||||||
local code=$?
|
|
||||||
rcAccumulator=$((rcAccumulator || $code))
|
|
||||||
if [ $code -ne 0 ]; then
|
|
||||||
result="FAILURE"
|
|
||||||
fi
|
|
||||||
printf "============== end %s : %s; exit code=%d\n\n\n" "$name" "$result" $code
|
|
||||||
}
|
|
||||||
|
|
||||||
function runLint {
|
|
||||||
make lint
|
|
||||||
}
|
|
||||||
|
|
||||||
function runUnitTests {
|
|
||||||
make unit-test-all
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLinuxAndNotOnTravis {
|
|
||||||
[[ ("linux" == "$(go env GOOS)") && (-z ${TRAVIS+x}) ]] && return
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
function testExamplesAgainstLatestKustomizeRelease {
|
|
||||||
removeBin kustomize
|
|
||||||
|
|
||||||
local latest=sigs.k8s.io/kustomize/kustomize/v3
|
|
||||||
echo "Installing latest kustomize from $latest"
|
|
||||||
(cd ~; GO111MODULE=on go install $latest)
|
|
||||||
|
|
||||||
$MDRIP --mode test \
|
|
||||||
--label testAgainstLatestRelease examples
|
|
||||||
|
|
||||||
# TODO: make work for non-linux
|
|
||||||
if onLinuxAndNotOnTravis; then
|
|
||||||
echo "On linux, and not on travis, so running the notravis example tests."
|
|
||||||
|
|
||||||
# Requires helm.
|
|
||||||
make $(go env GOPATH)/bin/helm
|
|
||||||
$MDRIP --mode test \
|
|
||||||
--label helmtest examples/chart.md
|
|
||||||
fi
|
|
||||||
echo "Example tests passed against $latest"
|
|
||||||
}
|
|
||||||
|
|
||||||
function testExamplesAgainstLocalHead {
|
|
||||||
removeBin kustomize
|
|
||||||
|
|
||||||
echo "Installing kustomize from HEAD"
|
|
||||||
(cd kustomize; go install .)
|
|
||||||
|
|
||||||
# To test examples of unreleased features, add
|
|
||||||
# examples with code blocks annotated with some
|
|
||||||
# label _other than_ @testAgainstLatestRelease.
|
|
||||||
$MDRIP --mode test \
|
|
||||||
--label testAgainstLatestRelease examples
|
|
||||||
echo "Example tests passed against HEAD"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Having this set might contradict the Makefile,
|
|
||||||
# so assure it's unset.
|
|
||||||
unset GOPATH
|
|
||||||
|
|
||||||
# With GOPATH now undefined, this most likely
|
|
||||||
# puts $HOME/go/bin on the path. Regardless,
|
|
||||||
# subsequent go tool installs will be placing
|
|
||||||
# binaries in this location.
|
|
||||||
PATH=$(go env GOPATH)/bin:$PATH
|
|
||||||
|
|
||||||
# Make sure we run in the root of the repo and
|
|
||||||
# therefore run the tests on all packages
|
|
||||||
base_dir="$( cd "$(dirname "$0")/.." && pwd )"
|
|
||||||
cd "$base_dir" || {
|
|
||||||
echo "Cannot cd to '$base_dir'. Aborting." >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "HOME=$HOME"
|
|
||||||
echo "PATH=$PATH"
|
|
||||||
echo pwd=`pwd`
|
|
||||||
echo " "
|
|
||||||
echo "Working..."
|
|
||||||
|
|
||||||
runFunc installTools
|
|
||||||
runFunc runLint
|
|
||||||
runFunc runUnitTests
|
|
||||||
runFunc testExamplesAgainstLatestKustomizeRelease
|
|
||||||
runFunc testExamplesAgainstLocalHead
|
|
||||||
|
|
||||||
if [ $rcAccumulator -eq 0 ]; then
|
|
||||||
echo "SUCCESS!"
|
|
||||||
else
|
|
||||||
echo "FAILURE; exit code $rcAccumulator"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit $rcAccumulator
|
|
||||||
Reference in New Issue
Block a user