mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-10 08:20:59 +00:00
Merge branch 'master' into container-filter-network
This commit is contained in:
@@ -35,7 +35,7 @@ install: true
|
||||
|
||||
script:
|
||||
- ./travis/verify-deps.sh
|
||||
- ./travis/pre-commit.sh
|
||||
- make verify-kustomize
|
||||
- ./travis/kyaml-pre-commit.sh
|
||||
|
||||
# TBD. Suppressing for now.
|
||||
|
||||
71
Makefile
71
Makefile
@@ -1,25 +1,21 @@
|
||||
# This Makefile is (and must be) used by
|
||||
# travis/pre-commit.sh to qualify pull requests.
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# That script generates all the code that needs
|
||||
# to be generated, and runs all the tests.
|
||||
#
|
||||
# Functionality in that script is gradually moving here.
|
||||
# Makefile for the kustomize repo.
|
||||
|
||||
MYGOBIN := $(shell go env GOPATH)/bin
|
||||
PATH := $(PATH):$(MYGOBIN)
|
||||
SHELL := env PATH=$(PATH) /bin/bash
|
||||
|
||||
.PHONY: all
|
||||
all: pre-commit
|
||||
all: verify-kustomize
|
||||
|
||||
# The pre-commit.sh script generates, lints and tests.
|
||||
# It uses this makefile. For more clarity, would like
|
||||
# to stop that - any scripts invoked by targets here
|
||||
# shouldn't "call back" to the makefile.
|
||||
.PHONY: pre-commit
|
||||
pre-commit:
|
||||
./travis/pre-commit.sh
|
||||
.PHONY: verify-kustomize
|
||||
verify-kustomize: \
|
||||
lint-kustomize \
|
||||
test-unit-kustomize-all \
|
||||
test-examples-kustomize-against-HEAD \
|
||||
test-examples-kustomize-against-latest
|
||||
|
||||
# Version pinned by api/go.mod
|
||||
$(MYGOBIN)/golangci-lint:
|
||||
@@ -46,6 +42,11 @@ $(MYGOBIN)/pluginator:
|
||||
cd api; \
|
||||
go install sigs.k8s.io/kustomize/pluginator/v2
|
||||
|
||||
# Install kustomize from whatever is checked out.
|
||||
$(MYGOBIN)/kustomize:
|
||||
cd kustomize; \
|
||||
go install .
|
||||
|
||||
.PHONY: install-tools
|
||||
install-tools: \
|
||||
$(MYGOBIN)/goimports \
|
||||
@@ -72,8 +73,8 @@ builtinplugins = \
|
||||
api/builtins/replicacounttransformer.go \
|
||||
api/builtins/secretgenerator.go
|
||||
|
||||
.PHONY: lint
|
||||
lint: install-tools $(builtinplugins)
|
||||
.PHONY: lint-kustomize
|
||||
lint-kustomize: install-tools $(builtinplugins)
|
||||
cd api; $(MYGOBIN)/golangci-lint run ./...
|
||||
cd kustomize; $(MYGOBIN)/golangci-lint run ./...
|
||||
cd pluginator; $(MYGOBIN)/golangci-lint run ./...
|
||||
@@ -85,23 +86,36 @@ api/builtins/%.go: $(MYGOBIN)/pluginator
|
||||
cd ../../../api/builtins; \
|
||||
$(MYGOBIN)/goimports -w $*.go
|
||||
|
||||
.PHONY: generate
|
||||
generate: $(builtinplugins)
|
||||
|
||||
.PHONY: unit-test-api
|
||||
unit-test-api: $(builtinplugins)
|
||||
.PHONY: test-unit-kustomize-api
|
||||
test-unit-kustomize-api: $(builtinplugins)
|
||||
cd api; go test ./...
|
||||
|
||||
.PHONY: unit-test-plugins
|
||||
unit-test-plugins:
|
||||
./hack/runPluginUnitTests.sh
|
||||
.PHONY: test-unit-kustomize-plugins
|
||||
test-unit-kustomize-plugins:
|
||||
./hack/testUnitKustomizePlugins.sh
|
||||
|
||||
.PHONY: unit-test-kustomize
|
||||
unit-test-kustomize:
|
||||
.PHONY: test-unit-kustomize-cli
|
||||
test-unit-kustomize-cli:
|
||||
cd kustomize; go test ./...
|
||||
|
||||
.PHONY: unit-test-all
|
||||
unit-test-all: unit-test-api unit-test-kustomize unit-test-plugins
|
||||
.PHONY: test-unit-kustomize-all
|
||||
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.
|
||||
# This is for testing an example plugin that
|
||||
@@ -133,6 +147,7 @@ $(MYGOBIN)/helm:
|
||||
clean:
|
||||
rm -f $(builtinplugins)
|
||||
rm -f $(MYGOBIN)/pluginator
|
||||
rm -f $(MYGOBIN)/kustomize
|
||||
|
||||
.PHONY: nuke
|
||||
nuke: clean
|
||||
|
||||
@@ -27,7 +27,7 @@ func GetCatRunner() *CatRunner {
|
||||
kyaml cat my-dir/
|
||||
|
||||
# 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
|
||||
... | kyaml cat
|
||||
@@ -51,10 +51,10 @@ kyaml cat my-dir/ --wrap-kind ResourceList --wrap-version kyaml.kustomize.dev/v1
|
||||
"'FoldedStyle', 'FlowStyle'.")
|
||||
c.Flags().BoolVar(&r.StripComments, "strip-comments", false,
|
||||
"remove comments from yaml.")
|
||||
c.Flags().BoolVar(&r.IncludeReconcilers, "include-reconcilers", false,
|
||||
"if true, include reconciler Resources in the output.")
|
||||
c.Flags().BoolVar(&r.ExcludeNonReconcilers, "exclude-non-reconcilers", false,
|
||||
"if true, exclude non-reconciler Resources in the output.")
|
||||
c.Flags().BoolVar(&r.IncludeLocal, "include-local", false,
|
||||
"if true, include local-config in the output.")
|
||||
c.Flags().BoolVar(&r.ExcludeNonLocal, "exclude-non-local", false,
|
||||
"if true, exclude non-local-config in the output.")
|
||||
r.Command = c
|
||||
return r
|
||||
}
|
||||
@@ -65,17 +65,17 @@ func CatCommand() *cobra.Command {
|
||||
|
||||
// CatRunner contains the run function
|
||||
type CatRunner struct {
|
||||
IncludeSubpackages bool
|
||||
Format bool
|
||||
KeepAnnotations bool
|
||||
WrapKind string
|
||||
WrapApiVersion string
|
||||
FunctionConfig string
|
||||
Styles []string
|
||||
StripComments bool
|
||||
IncludeReconcilers bool
|
||||
ExcludeNonReconcilers bool
|
||||
Command *cobra.Command
|
||||
IncludeSubpackages bool
|
||||
Format bool
|
||||
KeepAnnotations bool
|
||||
WrapKind string
|
||||
WrapApiVersion string
|
||||
FunctionConfig string
|
||||
Styles []string
|
||||
StripComments bool
|
||||
IncludeLocal bool
|
||||
ExcludeNonLocal bool
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
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
|
||||
// don't include reconcilers
|
||||
fltr = append(fltr, &filters.IsReconcilerFilter{
|
||||
ExcludeReconcilers: !r.IncludeReconcilers,
|
||||
IncludeNonReconcilers: !r.ExcludeNonReconcilers,
|
||||
fltr = append(fltr, &filters.IsLocalConfig{
|
||||
IncludeLocalConfig: r.IncludeLocal,
|
||||
ExcludeNonLocalConfig: r.ExcludeNonLocal,
|
||||
})
|
||||
if r.Format {
|
||||
fltr = append(fltr, filters.FormatFilter{})
|
||||
|
||||
@@ -47,10 +47,15 @@ spec:
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: gcr.io/example/image:version
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
@@ -149,10 +154,15 @@ spec:
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: gcr.io/example/image:version
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/image:version
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
@@ -173,7 +183,7 @@ spec:
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := cmd.GetCatRunner()
|
||||
r.Command.SetArgs([]string{d, "--include-reconcilers"})
|
||||
r.Command.SetArgs([]string{d, "--include-local"})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
@@ -202,13 +212,17 @@ spec:
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: gcr.io/example/image:version
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/image:version
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
@@ -259,10 +273,15 @@ spec:
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: gcr.io/example/image:version
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
@@ -283,19 +302,23 @@ spec:
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
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)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, `apiVersion: gcr.io/example/image:version
|
||||
if !assert.Equal(t, `apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
spec:
|
||||
replicas: 3
|
||||
`, 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.all, "all", false, "print all field infos")
|
||||
c.Flags().StringSliceVar(&r.fields, "field", []string{}, "print field")
|
||||
c.Flags().BoolVar(&r.includeReconcilers, "include-reconcilers", false,
|
||||
"if true, include reconciler Resources in the output.")
|
||||
c.Flags().BoolVar(&r.excludeNonReconcilers, "exclude-non-reconcilers", false,
|
||||
"if true, exclude non-reconciler Resources in the output.")
|
||||
c.Flags().BoolVar(&r.includeLocal, "include-local", false,
|
||||
"if true, include local-config in the output.")
|
||||
c.Flags().BoolVar(&r.excludeNonLocal, "exclude-non-local", false,
|
||||
"if true, exclude non-local-config in the output.")
|
||||
c.Flags().StringVar(&r.structure, "graph-structure", "directory",
|
||||
"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
|
||||
type TreeRunner struct {
|
||||
IncludeSubpackages bool
|
||||
Command *cobra.Command
|
||||
name bool
|
||||
resources bool
|
||||
ports bool
|
||||
images bool
|
||||
replicas bool
|
||||
all bool
|
||||
env bool
|
||||
args bool
|
||||
cmd bool
|
||||
fields []string
|
||||
includeReconcilers bool
|
||||
excludeNonReconcilers bool
|
||||
structure string
|
||||
IncludeSubpackages bool
|
||||
Command *cobra.Command
|
||||
name bool
|
||||
resources bool
|
||||
ports bool
|
||||
images bool
|
||||
replicas bool
|
||||
all bool
|
||||
env bool
|
||||
args bool
|
||||
cmd bool
|
||||
fields []string
|
||||
includeLocal bool
|
||||
excludeNonLocal bool
|
||||
structure string
|
||||
}
|
||||
|
||||
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
|
||||
fltrs := []kio.Filter{&filters.IsReconcilerFilter{
|
||||
ExcludeReconcilers: !r.includeReconcilers,
|
||||
IncludeNonReconcilers: !r.excludeNonReconcilers,
|
||||
fltrs := []kio.Filter{&filters.IsLocalConfig{
|
||||
IncludeLocalConfig: r.includeLocal,
|
||||
ExcludeNonLocalConfig: r.excludeNonLocal,
|
||||
}}
|
||||
|
||||
return handleError(c, kio.Pipeline{
|
||||
|
||||
@@ -24,10 +24,15 @@ func TestTreeCommand_files(t *testing.T) {
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
apiVersion: gcr.io/example/reconciler:v1
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -259,7 +264,7 @@ spec:
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := cmd.GetTreeRunner()
|
||||
r.Command.SetArgs([]string{d, "--include-reconcilers"})
|
||||
r.Command.SetArgs([]string{d, "--include-local"})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
@@ -306,10 +311,15 @@ spec:
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: gcr.io/example/reconciler:v1
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -331,7 +341,7 @@ spec:
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
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)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
|
||||
@@ -32,6 +32,7 @@ func main() {
|
||||
root.AddCommand(cmd.FmtCommand())
|
||||
root.AddCommand(cmd.MergeCommand())
|
||||
root.AddCommand(cmd.CountCommand())
|
||||
root.AddCommand(cmd.RunFnCommand())
|
||||
root.AddCommand(&cobra.Command{Use: "merge", Long: merge2.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
|
||||
#
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
# 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
|
||||
# to test all the plugins.
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
const (
|
||||
ResourceListKind = "ResourceList"
|
||||
ResourceListApiVersion = "kyaml.kustomize.dev/v1alpha1"
|
||||
ResourceListApiVersion = "config.kubernetes.io/v1alpha1"
|
||||
)
|
||||
|
||||
// ByteReadWriter reads from an input and writes to an output.
|
||||
|
||||
@@ -34,7 +34,7 @@ i: j
|
||||
}
|
||||
|
||||
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
|
||||
functionConfig:
|
||||
foo: bar
|
||||
|
||||
@@ -45,7 +45,7 @@ g:
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
|
||||
assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- c: d # second
|
||||
|
||||
@@ -5,12 +5,13 @@ package filters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -24,6 +25,8 @@ import (
|
||||
// The full set of environment variables from the parent process
|
||||
// are passed to the container.
|
||||
type ContainerFilter struct {
|
||||
mountPath string
|
||||
|
||||
// Image is the container image to use to create a container.
|
||||
Image string `yaml:"image,omitempty"`
|
||||
|
||||
@@ -41,6 +44,10 @@ type ContainerFilter struct {
|
||||
checkInput func(string)
|
||||
}
|
||||
|
||||
func (c *ContainerFilter) SetMountPath(path string) {
|
||||
c.mountPath = path
|
||||
}
|
||||
|
||||
// GrepFilter implements kio.GrepFilter
|
||||
func (c *ContainerFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
// 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
|
||||
"--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
|
||||
for _, pair := range os.Environ() {
|
||||
@@ -150,7 +161,8 @@ type IsReconcilerFilter struct {
|
||||
func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
var out []*yaml.RNode
|
||||
for i := range inputs {
|
||||
isContainerResource := GetContainerName(inputs[i]) != ""
|
||||
img, _ := GetContainerName(inputs[i])
|
||||
isContainerResource := img != ""
|
||||
if isContainerResource && !c.ExcludeReconcilers {
|
||||
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
|
||||
func GetContainerName(n *yaml.RNode) string {
|
||||
func GetContainerName(n *yaml.RNode) (string, string) {
|
||||
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 != "" {
|
||||
return container
|
||||
return container, path
|
||||
}
|
||||
|
||||
if match.MatchString(meta.ApiVersion) {
|
||||
return meta.ApiVersion
|
||||
image, err := n.Pipe(yaml.Lookup("metadata", "configFn", "container", "image"))
|
||||
if err != nil || yaml.IsMissingOrNull(image) {
|
||||
return "", path
|
||||
}
|
||||
|
||||
return ""
|
||||
return image.YNode().Value, path
|
||||
}
|
||||
|
||||
// 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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -60,6 +62,43 @@ metadata:
|
||||
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) {
|
||||
cfg, err := yaml.Parse(`apiversion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -127,7 +166,7 @@ metadata:
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
@@ -209,7 +248,7 @@ metadata:
|
||||
args: []string{"sh", "-c", "cat <&0"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiversion: apps/v1
|
||||
@@ -261,36 +300,30 @@ metadata:
|
||||
|
||||
func Test_GetContainerName(t *testing.T) {
|
||||
// make sure gcr.io works
|
||||
n, err := yaml.Parse(`apiVersion: gcr.io/foo/bar:something
|
||||
n, err := yaml.Parse(`apiVersion: v1beta1
|
||||
kind: MyThing
|
||||
metadata:
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/foo/bar:something
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
c := GetContainerName(n)
|
||||
c, _ := GetContainerName(n)
|
||||
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
|
||||
n, err = yaml.Parse(`apiVersion: v1
|
||||
kind: MyThing
|
||||
metadata:
|
||||
annotations:
|
||||
kyaml.kustomize.dev/container: gcr.io/foo/bar:something
|
||||
config.kubernetes.io/container: gcr.io/foo/bar:something
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
c = GetContainerName(n)
|
||||
c, _ = GetContainerName(n)
|
||||
assert.Equal(t, "gcr.io/foo/bar:something", c)
|
||||
|
||||
// doesn't have a container
|
||||
@@ -301,16 +334,6 @@ metadata:
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
c = GetContainerName(n)
|
||||
c, _ = GetContainerName(n)
|
||||
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 (
|
||||
mergeSourceAnnotation = "kyaml.kustomize.dev/merge-source"
|
||||
mergeSourceAnnotation = "config.kubernetes.io/merge-source"
|
||||
mergeSourceOriginal = "original"
|
||||
mergeSourceUpdated = "updated"
|
||||
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
|
||||
are synced out of staging.
|
||||
[kubernetes/api]: https://github.com/kubernetes/api
|
||||
[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
|
||||
more suitable libraries developed in the Kustomize repo.
|
||||
The module defined below in `pseudo/k8s` contains code generated by the
|
||||
[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?
|
||||
|
||||
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.
|
||||
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.
|
||||
In what follows, let the word `apimachinery` generally represent one of more of the modules [kubernetes/api], [kubernetes/apimachinery], [kubernetes/client-go].
|
||||
|
||||
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
|
||||
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.
|
||||
- kubectl must depend the on kustomize API module.
|
||||
|
||||
## 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,
|
||||
there is not guarantee that this will continue to work in the future.
|
||||
- kustomize depends on `apimachinery`.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
- 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