mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-30 09:51:23 +00:00
Compare commits
108 Commits
release-cm
...
api/v0.9.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c321ef79c | ||
|
|
7938fdb596 | ||
|
|
a2111869e6 | ||
|
|
f8288e2f02 | ||
|
|
f2f90d1185 | ||
|
|
459e800ecf | ||
|
|
8f00d3fd53 | ||
|
|
cd94cb13c6 | ||
|
|
e100be620e | ||
|
|
1e1b9b484a | ||
|
|
360585dfaf | ||
|
|
f604619dd5 | ||
|
|
0fa056327a | ||
|
|
6db2bf69f3 | ||
|
|
20fb9578c0 | ||
|
|
4eb8232495 | ||
|
|
6c4e8019f8 | ||
|
|
28707bf5df | ||
|
|
023a580f00 | ||
|
|
a2eaae5555 | ||
|
|
75df1a5422 | ||
|
|
f0b4cc4581 | ||
|
|
7a41e479c9 | ||
|
|
3350c7213c | ||
|
|
7b5e43d343 | ||
|
|
06661ea310 | ||
|
|
38b2b33503 | ||
|
|
f735d6fb3a | ||
|
|
5cb5e07ac0 | ||
|
|
54778504ed | ||
|
|
1bfe0d08dc | ||
|
|
56da9a58fc | ||
|
|
54383bca25 | ||
|
|
88461b4fed | ||
|
|
aabbea3e78 | ||
|
|
adedca09f2 | ||
|
|
9a27a9f19f | ||
|
|
3ebdb3fcef | ||
|
|
97e7cb1512 | ||
|
|
262a2d9288 | ||
|
|
91b862b556 | ||
|
|
b8ffc725c7 | ||
|
|
76f1411922 | ||
|
|
d1003d6f8f | ||
|
|
91f74e8d16 | ||
|
|
94c5096a95 | ||
|
|
f35aeb6a8e | ||
|
|
d6ce846047 | ||
|
|
ec069e4f19 | ||
|
|
c5adafd9ce | ||
|
|
16dcc98cff | ||
|
|
59c410a70a | ||
|
|
9b586162d0 | ||
|
|
803885049b | ||
|
|
be4fe7540e | ||
|
|
927568eea2 | ||
|
|
436d5e717c | ||
|
|
d37fa66ebc | ||
|
|
e8a4bf6edc | ||
|
|
35d1c3f9b4 | ||
|
|
e17785af21 | ||
|
|
0537b59f27 | ||
|
|
339e33d2f3 | ||
|
|
f082ac02cf | ||
|
|
9538ae1258 | ||
|
|
34981b664f | ||
|
|
477d8930e0 | ||
|
|
b5091a566a | ||
|
|
9981c45554 | ||
|
|
0f736ec7fd | ||
|
|
7826ad1e06 | ||
|
|
f4e6816338 | ||
|
|
4a13725678 | ||
|
|
ab9b010856 | ||
|
|
29be7fabe4 | ||
|
|
74e867833a | ||
|
|
91dc6d2a0f | ||
|
|
3c1fd0e9cf | ||
|
|
4deeb7d59b | ||
|
|
89b12cfc62 | ||
|
|
c07ffa5c1e | ||
|
|
259fcfcef8 | ||
|
|
f81201b74d | ||
|
|
6dbc74b32e | ||
|
|
ed38b5fe2b | ||
|
|
a84badb834 | ||
|
|
e1804cbc76 | ||
|
|
d13eef7951 | ||
|
|
0b4c6baf44 | ||
|
|
b3af54340c | ||
|
|
8c14b9d1af | ||
|
|
d818ccae92 | ||
|
|
4cea8b9785 | ||
|
|
84a36801e0 | ||
|
|
6eb7b3508d | ||
|
|
2a5f4ac7d7 | ||
|
|
518a16d3ac | ||
|
|
d53a2ad45d | ||
|
|
bb02a7645b | ||
|
|
5a9d90c872 | ||
|
|
4fd7269643 | ||
|
|
1eb3c1a075 | ||
|
|
a1746f2f8c | ||
|
|
b727febd08 | ||
|
|
c819d69ae4 | ||
|
|
bb6f83fb96 | ||
|
|
02d14d724a | ||
|
|
78737f5a38 |
20
Makefile
20
Makefile
@@ -4,6 +4,8 @@
|
|||||||
# Makefile for kustomize CLI and API.
|
# Makefile for kustomize CLI and API.
|
||||||
|
|
||||||
SHELL := /usr/bin/env bash
|
SHELL := /usr/bin/env bash
|
||||||
|
GOOS = $(shell go env GOOS)
|
||||||
|
GOARCH = $(shell go env GOARCH)
|
||||||
MYGOBIN = $(shell go env GOBIN)
|
MYGOBIN = $(shell go env GOBIN)
|
||||||
ifeq ($(MYGOBIN),)
|
ifeq ($(MYGOBIN),)
|
||||||
MYGOBIN = $(shell go env GOPATH)/bin
|
MYGOBIN = $(shell go env GOPATH)/bin
|
||||||
@@ -289,8 +291,8 @@ $(MYGOBIN)/kubeval:
|
|||||||
( \
|
( \
|
||||||
set -e; \
|
set -e; \
|
||||||
d=$(shell mktemp -d); cd $$d; \
|
d=$(shell mktemp -d); cd $$d; \
|
||||||
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz; \
|
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-$(GOOS)-$(GOARCH).tar.gz; \
|
||||||
tar xf kubeval-linux-amd64.tar.gz; \
|
tar xf kubeval-$(GOOS)-$(GOARCH).tar.gz; \
|
||||||
mv kubeval $(MYGOBIN); \
|
mv kubeval $(MYGOBIN); \
|
||||||
rm -rf $$d; \
|
rm -rf $$d; \
|
||||||
)
|
)
|
||||||
@@ -304,10 +306,10 @@ $(MYGOBIN)/helmV2:
|
|||||||
( \
|
( \
|
||||||
set -e; \
|
set -e; \
|
||||||
d=$(shell mktemp -d); cd $$d; \
|
d=$(shell mktemp -d); cd $$d; \
|
||||||
tgzFile=helm-v2.13.1-linux-amd64.tar.gz; \
|
tgzFile=helm-v2.13.1-$(GOOS)-$(GOARCH).tar.gz; \
|
||||||
wget https://storage.googleapis.com/kubernetes-helm/$$tgzFile; \
|
wget https://storage.googleapis.com/kubernetes-helm/$$tgzFile; \
|
||||||
tar -xvzf $$tgzFile; \
|
tar -xvzf $$tgzFile; \
|
||||||
mv linux-amd64/helm $(MYGOBIN)/helmV2; \
|
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV2; \
|
||||||
rm -rf $$d \
|
rm -rf $$d \
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -317,10 +319,10 @@ $(MYGOBIN)/helmV3:
|
|||||||
( \
|
( \
|
||||||
set -e; \
|
set -e; \
|
||||||
d=$(shell mktemp -d); cd $$d; \
|
d=$(shell mktemp -d); cd $$d; \
|
||||||
tgzFile=helm-v3.5.3-linux-amd64.tar.gz; \
|
tgzFile=helm-v3.5.3-$(GOOS)-$(GOARCH).tar.gz; \
|
||||||
wget https://get.helm.sh/$$tgzFile; \
|
wget https://get.helm.sh/$$tgzFile; \
|
||||||
tar -xvzf $$tgzFile; \
|
tar -xvzf $$tgzFile; \
|
||||||
mv linux-amd64/helm $(MYGOBIN)/helmV3; \
|
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \
|
||||||
rm -rf $$d \
|
rm -rf $$d \
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -328,7 +330,7 @@ $(MYGOBIN)/kind:
|
|||||||
( \
|
( \
|
||||||
set -e; \
|
set -e; \
|
||||||
d=$(shell mktemp -d); cd $$d; \
|
d=$(shell mktemp -d); cd $$d; \
|
||||||
wget -O ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(shell uname)-amd64; \
|
wget -O ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(GOOS)-$(GOARCH); \
|
||||||
chmod +x ./kind; \
|
chmod +x ./kind; \
|
||||||
mv ./kind $(MYGOBIN); \
|
mv ./kind $(MYGOBIN); \
|
||||||
rm -rf $$d; \
|
rm -rf $$d; \
|
||||||
@@ -339,10 +341,10 @@ $(MYGOBIN)/gh:
|
|||||||
( \
|
( \
|
||||||
set -e; \
|
set -e; \
|
||||||
d=$(shell mktemp -d); cd $$d; \
|
d=$(shell mktemp -d); cd $$d; \
|
||||||
tgzFile=gh_1.0.0_linux_amd64.tar.gz; \
|
tgzFile=gh_1.0.0_$(GOOS)_$(GOARCH).tar.gz; \
|
||||||
wget https://github.com/cli/cli/releases/download/v1.0.0/$$tgzFile; \
|
wget https://github.com/cli/cli/releases/download/v1.0.0/$$tgzFile; \
|
||||||
tar -xvzf $$tgzFile; \
|
tar -xvzf $$tgzFile; \
|
||||||
mv gh_1.0.0_linux_amd64/bin/gh $(MYGOBIN)/gh; \
|
mv gh_1.0.0_$(GOOS)_$(GOARCH)/bin/gh $(MYGOBIN)/gh; \
|
||||||
rm -rf $$d \
|
rm -rf $$d \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ will be reflected in the Kubernetes release notes.
|
|||||||
| < v1.14 | n/a |
|
| < v1.14 | n/a |
|
||||||
| v1.14-v1.20 | v2.0.3 |
|
| v1.14-v1.20 | v2.0.3 |
|
||||||
| v1.21 | v4.0.5 |
|
| v1.21 | v4.0.5 |
|
||||||
|
| v1.22 | v4.2.0 |
|
||||||
|
|
||||||
[v2.0.3]: /../../tree/v2.0.3
|
[v2.0.3]: /../../tree/v2.0.3
|
||||||
[#2506]: https://github.com/kubernetes-sigs/kustomize/issues/2506
|
[#2506]: https://github.com/kubernetes-sigs/kustomize/issues/2506
|
||||||
@@ -151,7 +152,7 @@ is governed by the [Kubernetes Code of Conduct].
|
|||||||
[`make`]: https://www.gnu.org/software/make
|
[`make`]: https://www.gnu.org/software/make
|
||||||
[`sed`]: https://www.gnu.org/software/sed
|
[`sed`]: https://www.gnu.org/software/sed
|
||||||
[DAM]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#declarative-application-management
|
[DAM]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#declarative-application-management
|
||||||
[KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/0008-kustomize.md
|
[KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/2377-Kustomize/README.md
|
||||||
[Kubernetes Code of Conduct]: code-of-conduct.md
|
[Kubernetes Code of Conduct]: code-of-conduct.md
|
||||||
[applied]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#apply
|
[applied]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#apply
|
||||||
[base]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#base
|
[base]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#base
|
||||||
|
|||||||
@@ -1514,9 +1514,9 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: pre-deploy
|
name: pre-deploy
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousNames: deploy,deploy
|
internal.config.kubernetes.io/previousNames: deploy,deploy
|
||||||
config.kubernetes.io/previousKinds: CronJob,Deployment
|
internal.config.kubernetes.io/previousKinds: CronJob,Deployment
|
||||||
config.kubernetes.io/previousNamespaces: default,default
|
internal.config.kubernetes.io/previousNamespaces: default,default
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -1543,9 +1543,9 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: pre-deploy
|
name: pre-deploy
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousNames: deploy,deploy
|
internal.config.kubernetes.io/previousNames: deploy,deploy
|
||||||
config.kubernetes.io/previousKinds: CronJob,Deployment
|
internal.config.kubernetes.io/previousKinds: CronJob,Deployment
|
||||||
config.kubernetes.io/previousNamespaces: default,default
|
internal.config.kubernetes.io/previousNamespaces: default,default
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ require (
|
|||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0
|
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -223,8 +223,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
|
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
|||||||
@@ -897,7 +897,9 @@ func TestNameReferenceClusterWide(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expected.RemoveBuildAnnotations()
|
||||||
m.RemoveBuildAnnotations()
|
m.RemoveBuildAnnotations()
|
||||||
|
|
||||||
if err = expected.ErrorIfNotEqualLists(m); err != nil {
|
if err = expected.ErrorIfNotEqualLists(m); err != nil {
|
||||||
t.Fatalf(notEqualErrFmt, err)
|
t.Fatalf(notEqualErrFmt, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -362,10 +362,10 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
|
|||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "sub-backendOne",
|
"name": "sub-backendOne",
|
||||||
"annotations": map[string]interface{}{
|
"annotations": map[string]interface{}{
|
||||||
"config.kubernetes.io/previousKinds": "Service",
|
"internal.config.kubernetes.io/previousKinds": "Service",
|
||||||
"config.kubernetes.io/previousNames": "backendOne",
|
"internal.config.kubernetes.io/previousNames": "backendOne",
|
||||||
"config.kubernetes.io/previousNamespaces": "default",
|
"internal.config.kubernetes.io/previousNamespaces": "default",
|
||||||
"config.kubernetes.io/prefixes": "sub-",
|
"internal.config.kubernetes.io/prefixes": "sub-",
|
||||||
},
|
},
|
||||||
}}).ResMap()
|
}}).ResMap()
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ func parseGitUrl(n string) (
|
|||||||
index := strings.Index(n, gitSuffix)
|
index := strings.Index(n, gitSuffix)
|
||||||
orgRepo = n[0:index]
|
orgRepo = n[0:index]
|
||||||
n = n[index+len(gitSuffix):]
|
n = n[index+len(gitSuffix):]
|
||||||
if n[0] == '/' {
|
if len(n) > 0 && n[0] == '/' {
|
||||||
n = n[1:]
|
n = n[1:]
|
||||||
}
|
}
|
||||||
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
|
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
|
||||||
|
|||||||
@@ -182,6 +182,12 @@ func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) {
|
|||||||
absPath: notCloned.String(),
|
absPath: notCloned.String(),
|
||||||
ref: "",
|
ref: "",
|
||||||
},
|
},
|
||||||
|
"t12": {
|
||||||
|
input: "https://bitbucket.example.com/scm/project/repository.git",
|
||||||
|
cloneSpec: "https://bitbucket.example.com/scm/project/repository.git",
|
||||||
|
absPath: notCloned.String(),
|
||||||
|
ref: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for tn, tc := range testcases {
|
for tn, tc := range testcases {
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin {
|
|||||||
StorageMounts: toStorageMounts(o.Mounts),
|
StorageMounts: toStorageMounts(o.Mounts),
|
||||||
Env: o.Env,
|
Env: o.Env,
|
||||||
AsCurrentUser: o.AsCurrentUser,
|
AsCurrentUser: o.AsCurrentUser,
|
||||||
|
WorkingDir: o.WorkingDir,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ func (l *Loader) Config() *types.PluginConfig {
|
|||||||
return l.pc
|
return l.pc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetWorkDir sets the working directory for this loader's plugins
|
||||||
|
func (l *Loader) SetWorkDir(wd string) {
|
||||||
|
l.pc.FnpLoadingOptions.WorkingDir = wd
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Loader) LoadGenerators(
|
func (l *Loader) LoadGenerators(
|
||||||
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
|
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
|
||||||
var result []resmap.Generator
|
var result []resmap.Generator
|
||||||
|
|||||||
@@ -231,10 +231,10 @@ func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {
|
|||||||
if err := r.SetAnnotations(annotations); err != nil {
|
if err := r.SetAnnotations(annotations); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.SetOptions(types.NewGenArgs(
|
if needsHash {
|
||||||
&types.GeneratorArgs{
|
r.EnableHashSuffix()
|
||||||
Behavior: behavior,
|
}
|
||||||
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
|
r.SetBehavior(types.NewGenerationBehavior(behavior))
|
||||||
}
|
}
|
||||||
return rm, nil
|
return rm, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
@@ -38,11 +40,13 @@ func NewKustTarget(
|
|||||||
validator ifc.Validator,
|
validator ifc.Validator,
|
||||||
rFactory *resmap.Factory,
|
rFactory *resmap.Factory,
|
||||||
pLdr *loader.Loader) *KustTarget {
|
pLdr *loader.Loader) *KustTarget {
|
||||||
|
pLdrCopy := *pLdr
|
||||||
|
pLdrCopy.SetWorkDir(ldr.Root())
|
||||||
return &KustTarget{
|
return &KustTarget{
|
||||||
ldr: ldr,
|
ldr: ldr,
|
||||||
validator: validator,
|
validator: validator,
|
||||||
rFactory: rFactory,
|
rFactory: rFactory,
|
||||||
pLdr: pLdr,
|
pLdr: &pLdrCopy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +112,7 @@ func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) {
|
func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) {
|
||||||
ra, err := kt.AccumulateTarget()
|
ra, err := kt.AccumulateTarget(&resource.Origin{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -151,20 +155,29 @@ func (kt *KustTarget) addHashesToNames(
|
|||||||
// holding customized resources and the data/rules used
|
// holding customized resources and the data/rules used
|
||||||
// to do so. The name back references and vars are
|
// to do so. The name back references and vars are
|
||||||
// not yet fixed.
|
// not yet fixed.
|
||||||
func (kt *KustTarget) AccumulateTarget() (
|
// The origin parameter is used through the recursive calls
|
||||||
|
// to annotate each resource with information about where
|
||||||
|
// the resource came from, e.g. the file and/or the repository
|
||||||
|
// it originated from.
|
||||||
|
// As an entrypoint, one can pass an empty resource.Origin object to
|
||||||
|
// AccumulateTarget. As AccumulateTarget moves recursively
|
||||||
|
// through kustomization directories, it updates `origin.path`
|
||||||
|
// accordingly. When a remote base is found, it updates `origin.repo`
|
||||||
|
// and `origin.ref` accordingly.
|
||||||
|
func (kt *KustTarget) AccumulateTarget(origin *resource.Origin) (
|
||||||
ra *accumulator.ResAccumulator, err error) {
|
ra *accumulator.ResAccumulator, err error) {
|
||||||
return kt.accumulateTarget(accumulator.MakeEmptyAccumulator())
|
return kt.accumulateTarget(accumulator.MakeEmptyAccumulator(), origin)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ra should be empty when this KustTarget is a Kustomization, or the ra of the parent if this KustTarget is a Component
|
// ra should be empty when this KustTarget is a Kustomization, or the ra of the parent if this KustTarget is a Component
|
||||||
// (or empty if the Component does not have a parent).
|
// (or empty if the Component does not have a parent).
|
||||||
func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
|
func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator, origin *resource.Origin) (
|
||||||
resRa *accumulator.ResAccumulator, err error) {
|
resRa *accumulator.ResAccumulator, err error) {
|
||||||
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources)
|
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources, origin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "accumulating resources")
|
return nil, errors.Wrap(err, "accumulating resources")
|
||||||
}
|
}
|
||||||
ra, err = kt.accumulateComponents(ra, kt.kustomization.Components)
|
ra, err = kt.accumulateComponents(ra, kt.kustomization.Components, origin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "accumulating components")
|
return nil, errors.Wrap(err, "accumulating components")
|
||||||
}
|
}
|
||||||
@@ -247,7 +260,7 @@ func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error)
|
|||||||
}
|
}
|
||||||
ra.AppendAll(rm)
|
ra.AppendAll(rm)
|
||||||
}
|
}
|
||||||
ra, err := kt.accumulateResources(ra, generatorPaths)
|
ra, err := kt.accumulateResources(ra, generatorPaths, &resource.Origin{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -283,8 +296,7 @@ func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]re
|
|||||||
}
|
}
|
||||||
ra.AppendAll(rm)
|
ra.AppendAll(rm)
|
||||||
}
|
}
|
||||||
ra, err := kt.accumulateResources(ra, transformerPaths)
|
ra, err := kt.accumulateResources(ra, transformerPaths, &resource.Origin{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -332,16 +344,16 @@ func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) error {
|
|||||||
// accumulateResources fills the given resourceAccumulator
|
// accumulateResources fills the given resourceAccumulator
|
||||||
// with resources read from the given list of paths.
|
// with resources read from the given list of paths.
|
||||||
func (kt *KustTarget) accumulateResources(
|
func (kt *KustTarget) accumulateResources(
|
||||||
ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) {
|
ra *accumulator.ResAccumulator, paths []string, origin *resource.Origin) (*accumulator.ResAccumulator, error) {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
// try loading resource as file then as base (directory or git repository)
|
// try loading resource as file then as base (directory or git repository)
|
||||||
if errF := kt.accumulateFile(ra, path); errF != nil {
|
if errF := kt.accumulateFile(ra, path, origin); errF != nil {
|
||||||
ldr, err := kt.ldr.New(path)
|
ldr, err := kt.ldr.New(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(
|
return nil, errors.Wrapf(
|
||||||
err, "accumulation err='%s'", errF.Error())
|
err, "accumulation err='%s'", errF.Error())
|
||||||
}
|
}
|
||||||
ra, err = kt.accumulateDirectory(ra, ldr, false)
|
ra, err = kt.accumulateDirectory(ra, ldr, origin.Append(path), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(
|
return nil, errors.Wrapf(
|
||||||
err, "accumulation err='%s'", errF.Error())
|
err, "accumulation err='%s'", errF.Error())
|
||||||
@@ -354,7 +366,7 @@ func (kt *KustTarget) accumulateResources(
|
|||||||
// accumulateResources fills the given resourceAccumulator
|
// accumulateResources fills the given resourceAccumulator
|
||||||
// with resources read from the given list of paths.
|
// with resources read from the given list of paths.
|
||||||
func (kt *KustTarget) accumulateComponents(
|
func (kt *KustTarget) accumulateComponents(
|
||||||
ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) {
|
ra *accumulator.ResAccumulator, paths []string, origin *resource.Origin) (*accumulator.ResAccumulator, error) {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
// Components always refer to directories
|
// Components always refer to directories
|
||||||
ldr, errL := kt.ldr.New(path)
|
ldr, errL := kt.ldr.New(path)
|
||||||
@@ -362,7 +374,8 @@ func (kt *KustTarget) accumulateComponents(
|
|||||||
return nil, fmt.Errorf("loader.New %q", errL)
|
return nil, fmt.Errorf("loader.New %q", errL)
|
||||||
}
|
}
|
||||||
var errD error
|
var errD error
|
||||||
ra, errD = kt.accumulateDirectory(ra, ldr, true)
|
origin.Path = filepath.Join(origin.Path, path)
|
||||||
|
ra, errD = kt.accumulateDirectory(ra, ldr, origin, true)
|
||||||
if errD != nil {
|
if errD != nil {
|
||||||
return nil, fmt.Errorf("accumulateDirectory: %q", errD)
|
return nil, fmt.Errorf("accumulateDirectory: %q", errD)
|
||||||
}
|
}
|
||||||
@@ -371,7 +384,7 @@ func (kt *KustTarget) accumulateComponents(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) accumulateDirectory(
|
func (kt *KustTarget) accumulateDirectory(
|
||||||
ra *accumulator.ResAccumulator, ldr ifc.Loader, isComponent bool) (*accumulator.ResAccumulator, error) {
|
ra *accumulator.ResAccumulator, ldr ifc.Loader, origin *resource.Origin, isComponent bool) (*accumulator.ResAccumulator, error) {
|
||||||
defer ldr.Cleanup()
|
defer ldr.Cleanup()
|
||||||
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
|
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
|
||||||
err := subKt.Load()
|
err := subKt.Load()
|
||||||
@@ -379,6 +392,7 @@ func (kt *KustTarget) accumulateDirectory(
|
|||||||
return nil, errors.Wrapf(
|
return nil, errors.Wrapf(
|
||||||
err, "couldn't make target for path '%s'", ldr.Root())
|
err, "couldn't make target for path '%s'", ldr.Root())
|
||||||
}
|
}
|
||||||
|
subKt.kustomization.BuildMetadata = kt.kustomization.BuildMetadata
|
||||||
var bytes []byte
|
var bytes []byte
|
||||||
path := ldr.Root()
|
path := ldr.Root()
|
||||||
if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists {
|
if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists {
|
||||||
@@ -402,12 +416,12 @@ func (kt *KustTarget) accumulateDirectory(
|
|||||||
var subRa *accumulator.ResAccumulator
|
var subRa *accumulator.ResAccumulator
|
||||||
if isComponent {
|
if isComponent {
|
||||||
// Components don't create a new accumulator: the kustomization directives are added to the current accumulator
|
// Components don't create a new accumulator: the kustomization directives are added to the current accumulator
|
||||||
subRa, err = subKt.accumulateTarget(ra)
|
subRa, err = subKt.accumulateTarget(ra, origin)
|
||||||
ra = accumulator.MakeEmptyAccumulator()
|
ra = accumulator.MakeEmptyAccumulator()
|
||||||
} else {
|
} else {
|
||||||
// Child Kustomizations create a new accumulator which resolves their kustomization directives, which will later
|
// Child Kustomizations create a new accumulator which resolves their kustomization directives, which will later
|
||||||
// be merged into the current accumulator.
|
// be merged into the current accumulator.
|
||||||
subRa, err = subKt.AccumulateTarget()
|
subRa, err = subKt.AccumulateTarget(origin)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(
|
return nil, errors.Wrapf(
|
||||||
@@ -422,11 +436,18 @@ func (kt *KustTarget) accumulateDirectory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) accumulateFile(
|
func (kt *KustTarget) accumulateFile(
|
||||||
ra *accumulator.ResAccumulator, path string) error {
|
ra *accumulator.ResAccumulator, path string, origin *resource.Origin) error {
|
||||||
resources, err := kt.rFactory.FromFile(kt.ldr, path)
|
resources, err := kt.rFactory.FromFile(kt.ldr, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "accumulating resources from '%s'", path)
|
return errors.Wrapf(err, "accumulating resources from '%s'", path)
|
||||||
}
|
}
|
||||||
|
if utils.StringSliceContains(kt.kustomization.BuildMetadata, "originAnnotations") {
|
||||||
|
origin = origin.Append(path)
|
||||||
|
err = resources.AnnotateAll(utils.OriginAnnotation, origin.String())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "cannot add path annotation for '%s'", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
err = ra.AppendAll(resources)
|
err = ra.AppendAll(resources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "merging resources from '%s'", path)
|
return errors.Wrapf(err, "merging resources from '%s'", path)
|
||||||
|
|||||||
@@ -255,5 +255,5 @@ metadata:
|
|||||||
actual.RemoveBuildAnnotations()
|
actual.RemoveBuildAnnotations()
|
||||||
actYaml, err := actual.AsYaml()
|
actYaml, err := actual.AsYaml()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expYaml, actYaml)
|
assert.Equal(t, string(expYaml), string(actYaml))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||||
@@ -65,7 +66,7 @@ vars:
|
|||||||
apiVersion: v300
|
apiVersion: v300
|
||||||
`)
|
`)
|
||||||
ra, err := makeAndLoadKustTarget(
|
ra, err := makeAndLoadKustTarget(
|
||||||
t, th.GetFSys(), "/app").AccumulateTarget()
|
t, th.GetFSys(), "/app").AccumulateTarget(&resource.Origin{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Err: %v", err)
|
t.Fatalf("Err: %v", err)
|
||||||
}
|
}
|
||||||
@@ -120,7 +121,7 @@ resources:
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
ra, err := makeAndLoadKustTarget(
|
ra, err := makeAndLoadKustTarget(
|
||||||
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget()
|
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget(&resource.Origin{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Err: %v", err)
|
t.Fatalf("Err: %v", err)
|
||||||
}
|
}
|
||||||
@@ -177,7 +178,7 @@ resources:
|
|||||||
- ../o1
|
- ../o1
|
||||||
`)
|
`)
|
||||||
_, err := makeAndLoadKustTarget(
|
_, err := makeAndLoadKustTarget(
|
||||||
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget()
|
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget(&resource.Origin{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected var collision")
|
t.Fatalf("expected var collision")
|
||||||
}
|
}
|
||||||
|
|||||||
23
api/internal/utils/annotations.go
Normal file
23
api/internal/utils/annotations.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
|
||||||
|
const (
|
||||||
|
BuildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds"
|
||||||
|
BuildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames"
|
||||||
|
BuildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
|
||||||
|
BuildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
|
||||||
|
BuildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces"
|
||||||
|
BuildAnnotationsRefBy = konfig.ConfigAnnoDomain + "/refBy"
|
||||||
|
BuildAnnotationsGenBehavior = konfig.ConfigAnnoDomain + "/generatorBehavior"
|
||||||
|
BuildAnnotationsGenAddHashSuffix = konfig.ConfigAnnoDomain + "/needsHashSuffix"
|
||||||
|
|
||||||
|
// the following are only for patches, to specify whether they can change names
|
||||||
|
// and kinds of their targets
|
||||||
|
BuildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange"
|
||||||
|
BuildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange"
|
||||||
|
|
||||||
|
OriginAnnotation = "config.kubernetes.io/origin"
|
||||||
|
|
||||||
|
Enabled = "enabled"
|
||||||
|
)
|
||||||
@@ -4,25 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
BuildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds"
|
|
||||||
BuildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames"
|
|
||||||
BuildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
|
|
||||||
BuildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
|
|
||||||
BuildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces"
|
|
||||||
|
|
||||||
// the following are only for patches, to specify whether they can change names
|
|
||||||
// and kinds of their targets
|
|
||||||
BuildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange"
|
|
||||||
BuildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange"
|
|
||||||
Allowed = "allowed"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MakeResIds returns all of an RNode's current and previous Ids
|
// MakeResIds returns all of an RNode's current and previous Ids
|
||||||
func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) {
|
func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) {
|
||||||
var result []resid.ResId
|
var result []resid.ResId
|
||||||
|
|||||||
@@ -31,11 +31,12 @@ const (
|
|||||||
// A program name, for use in help, finding the XDG_CONFIG_DIR, etc.
|
// A program name, for use in help, finding the XDG_CONFIG_DIR, etc.
|
||||||
ProgramName = "kustomize"
|
ProgramName = "kustomize"
|
||||||
|
|
||||||
// ConfigAnnoDomain is configuration-related annotation namespace.
|
// ConfigAnnoDomain is internal configuration-related annotation namespace.
|
||||||
ConfigAnnoDomain = "config.kubernetes.io"
|
// See https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md.
|
||||||
|
ConfigAnnoDomain = "internal.config.kubernetes.io"
|
||||||
|
|
||||||
// If a resource has this annotation, kustomize will drop it.
|
// If a resource has this annotation, kustomize will drop it.
|
||||||
IgnoredByKustomizeAnnotation = ConfigAnnoDomain + "/local-config"
|
IgnoredByKustomizeAnnotation = "config.kubernetes.io/local-config"
|
||||||
|
|
||||||
// Label key that indicates the resources are built from Kustomize
|
// Label key that indicates the resources are built from Kustomize
|
||||||
ManagedbyLabelKey = "app.kubernetes.io/managed-by"
|
ManagedbyLabelKey = "app.kubernetes.io/managed-by"
|
||||||
|
|||||||
265
api/krusty/buildmetadata_test.go
Normal file
265
api/krusty/buildmetadata_test.go
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package krusty_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "sigs.k8s.io/kustomize/api/krusty"
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAnnoOriginLocalFiles(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteF("service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
`)
|
||||||
|
th.WriteK(".", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- service.yaml
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)
|
||||||
|
options := th.MakeDefaultOptions()
|
||||||
|
m := th.Run(".", options)
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: service.yaml
|
||||||
|
name: myService
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginLocalFilesWithOverlay(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK("base", `
|
||||||
|
namePrefix: b-
|
||||||
|
resources:
|
||||||
|
- namespace.yaml
|
||||||
|
- role.yaml
|
||||||
|
- service.yaml
|
||||||
|
- deployment.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("base/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService
|
||||||
|
`)
|
||||||
|
th.WriteF("base/namespace.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: myNs
|
||||||
|
`)
|
||||||
|
th.WriteF("base/role.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: myRole
|
||||||
|
`)
|
||||||
|
th.WriteF("base/deployment.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: myDep
|
||||||
|
`)
|
||||||
|
th.WriteK("prod", `
|
||||||
|
namePrefix: p-
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
- service.yaml
|
||||||
|
- namespace.yaml
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)
|
||||||
|
th.WriteF("prod/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService2
|
||||||
|
`)
|
||||||
|
th.WriteF("prod/namespace.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: myNs2
|
||||||
|
`)
|
||||||
|
m := th.Run("prod", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: ../base/namespace.yaml
|
||||||
|
name: myNs
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: ../base/role.yaml
|
||||||
|
name: p-b-myRole
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: ../base/service.yaml
|
||||||
|
name: p-b-myService
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: ../base/deployment.yaml
|
||||||
|
name: p-b-myDep
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: service.yaml
|
||||||
|
name: p-myService2
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: namespace.yaml
|
||||||
|
name: myNs2
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a copy of TestGeneratorBasics in configmaps_test.go,
|
||||||
|
// except that we've enabled the addAnnoOrigin option
|
||||||
|
// (which doesn't do anything yet).
|
||||||
|
// TODO: Generated resources should receive the annotation
|
||||||
|
// config.kubernetes.io/origin: |
|
||||||
|
// generated-by: path/to/kustomization.yaml
|
||||||
|
func TestGeneratorWithAnnoOrigin(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK(".", `
|
||||||
|
namePrefix: blah-
|
||||||
|
configMapGenerator:
|
||||||
|
- name: bob
|
||||||
|
literals:
|
||||||
|
- fruit=apple
|
||||||
|
- vegetable=broccoli
|
||||||
|
envs:
|
||||||
|
- foo.env
|
||||||
|
env: bar.env
|
||||||
|
files:
|
||||||
|
- passphrase=phrase.dat
|
||||||
|
- forces.txt
|
||||||
|
- name: json
|
||||||
|
literals:
|
||||||
|
- 'v2=[{"path": "var/druid/segment-cache"}]'
|
||||||
|
- >-
|
||||||
|
druid_segmentCache_locations=[{"path":
|
||||||
|
"var/druid/segment-cache",
|
||||||
|
"maxSize": 32000000000,
|
||||||
|
"freeSpacePercent": 1.0}]
|
||||||
|
secretGenerator:
|
||||||
|
- name: bob
|
||||||
|
literals:
|
||||||
|
- fruit=apple
|
||||||
|
- vegetable=broccoli
|
||||||
|
envs:
|
||||||
|
- foo.env
|
||||||
|
files:
|
||||||
|
- passphrase=phrase.dat
|
||||||
|
- forces.txt
|
||||||
|
env: bar.env
|
||||||
|
`)
|
||||||
|
th.WriteF("foo.env", `
|
||||||
|
MOUNTAIN=everest
|
||||||
|
OCEAN=pacific
|
||||||
|
`)
|
||||||
|
th.WriteF("bar.env", `
|
||||||
|
BIRD=falcon
|
||||||
|
`)
|
||||||
|
th.WriteF("phrase.dat", `
|
||||||
|
Life is short.
|
||||||
|
But the years are long.
|
||||||
|
Not while the evil days come not.
|
||||||
|
`)
|
||||||
|
th.WriteF("forces.txt", `
|
||||||
|
gravitational
|
||||||
|
electromagnetic
|
||||||
|
strong nuclear
|
||||||
|
weak nuclear
|
||||||
|
`)
|
||||||
|
opts := th.MakeDefaultOptions()
|
||||||
|
m := th.Run(".", opts)
|
||||||
|
th.AssertActualEqualsExpected(
|
||||||
|
m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
BIRD: falcon
|
||||||
|
MOUNTAIN: everest
|
||||||
|
OCEAN: pacific
|
||||||
|
forces.txt: |2
|
||||||
|
|
||||||
|
gravitational
|
||||||
|
electromagnetic
|
||||||
|
strong nuclear
|
||||||
|
weak nuclear
|
||||||
|
fruit: apple
|
||||||
|
passphrase: |2
|
||||||
|
|
||||||
|
Life is short.
|
||||||
|
But the years are long.
|
||||||
|
Not while the evil days come not.
|
||||||
|
vegetable: broccoli
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blah-bob-g9df72cd5b
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize":
|
||||||
|
32000000000, "freeSpacePercent": 1.0}]'
|
||||||
|
v2: '[{"path": "var/druid/segment-cache"}]'
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blah-json-5298bc8g99
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
BIRD: ZmFsY29u
|
||||||
|
MOUNTAIN: ZXZlcmVzdA==
|
||||||
|
OCEAN: cGFjaWZpYw==
|
||||||
|
forces.txt: |
|
||||||
|
CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbn
|
||||||
|
VjbGVhcgo=
|
||||||
|
fruit: YXBwbGU=
|
||||||
|
passphrase: |
|
||||||
|
CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aG
|
||||||
|
UgZXZpbCBkYXlzIGNvbWUgbm90Lgo=
|
||||||
|
vegetable: YnJvY2NvbGk=
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: blah-bob-58g62h555c
|
||||||
|
type: Opaque
|
||||||
|
`)
|
||||||
|
}
|
||||||
@@ -1,17 +1,52 @@
|
|||||||
package krusty_test
|
package krusty_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFnExecGenerator(t *testing.T) {
|
const generateDeploymentDotSh = `#!/bin/sh
|
||||||
// Function plugins should not need the env setup done by MakeEnhancedHarness
|
|
||||||
th := kusttest_test.MakeHarness(t)
|
|
||||||
|
|
||||||
th.WriteK(".", `
|
cat <<EOF
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
annotations:
|
||||||
|
tshirt-size: small # this injects the resource reservations
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
EOF
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestFnExecGenerator(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
|
||||||
|
th := kusttest_test.MakeHarnessWithFs(t, fSys)
|
||||||
|
o := th.MakeOptionsPluginsEnabled()
|
||||||
|
o.PluginConfig.FnpLoadingOptions.EnableExec = true
|
||||||
|
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
th.WriteK(tmpDir.String(), `
|
||||||
resources:
|
resources:
|
||||||
- short_secret.yaml
|
- short_secret.yaml
|
||||||
generators:
|
generators:
|
||||||
@@ -19,7 +54,8 @@ generators:
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
// Create some additional resource just to make sure everything is added
|
// Create some additional resource just to make sure everything is added
|
||||||
th.WriteF("short_secret.yaml", `
|
th.WriteF(filepath.Join(tmpDir.String(), "short_secret.yaml"),
|
||||||
|
`
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
@@ -32,22 +68,25 @@ stringData:
|
|||||||
bootcmd:
|
bootcmd:
|
||||||
- mkdir /mnt/vda
|
- mkdir /mnt/vda
|
||||||
`)
|
`)
|
||||||
|
th.WriteF(filepath.Join(tmpDir.String(), "generateDeployment.sh"), generateDeploymentDotSh)
|
||||||
|
|
||||||
th.WriteF("gener.yaml", `
|
assert.NoError(t, os.Chmod(filepath.Join(tmpDir.String(), "generateDeployment.sh"), 0777))
|
||||||
|
th.WriteF(filepath.Join(tmpDir.String(), "gener.yaml"), `
|
||||||
kind: executable
|
kind: executable
|
||||||
metadata:
|
metadata:
|
||||||
name: demo
|
name: demo
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/function: |
|
config.kubernetes.io/function: |
|
||||||
exec:
|
exec:
|
||||||
path: ./fnplugin_test/fnexectest.sh
|
path: ./generateDeployment.sh
|
||||||
spec:
|
spec:
|
||||||
`)
|
`)
|
||||||
o := th.MakeOptionsPluginsEnabled()
|
|
||||||
o.PluginConfig.FnpLoadingOptions.EnableExec = true
|
m := th.Run(tmpDir.String(), o)
|
||||||
m := th.Run(".", o)
|
assert.NoError(t, err)
|
||||||
th.AssertActualEqualsExpected(m, `
|
yml, err := m.AsYaml()
|
||||||
apiVersion: v1
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
@@ -63,7 +102,6 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/path: deployment_nginx.yaml
|
|
||||||
tshirt-size: small
|
tshirt-size: small
|
||||||
labels:
|
labels:
|
||||||
app: nginx
|
app: nginx
|
||||||
@@ -80,7 +118,99 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- image: nginx
|
- image: nginx
|
||||||
name: nginx
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFnExecGeneratorWithOverlay(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
|
||||||
|
th := kusttest_test.MakeHarnessWithFs(t, fSys)
|
||||||
|
o := th.MakeOptionsPluginsEnabled()
|
||||||
|
o.PluginConfig.FnpLoadingOptions.EnableExec = true
|
||||||
|
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
base := filepath.Join(tmpDir.String(), "base")
|
||||||
|
prod := filepath.Join(tmpDir.String(), "prod")
|
||||||
|
assert.NoError(t, fSys.Mkdir(base))
|
||||||
|
assert.NoError(t, fSys.Mkdir(prod))
|
||||||
|
th.WriteK(base, `
|
||||||
|
resources:
|
||||||
|
- short_secret.yaml
|
||||||
|
generators:
|
||||||
|
- gener.yaml
|
||||||
`)
|
`)
|
||||||
|
th.WriteK(prod, `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
`)
|
||||||
|
th.WriteF(filepath.Join(base, "short_secret.yaml"),
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
`)
|
||||||
|
th.WriteF(filepath.Join(base, "generateDeployment.sh"), generateDeploymentDotSh)
|
||||||
|
|
||||||
|
assert.NoError(t, os.Chmod(filepath.Join(base, "generateDeployment.sh"), 0777))
|
||||||
|
th.WriteF(filepath.Join(base, "gener.yaml"), `
|
||||||
|
kind: executable
|
||||||
|
metadata:
|
||||||
|
name: demo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: ./generateDeployment.sh
|
||||||
|
spec:
|
||||||
|
`)
|
||||||
|
|
||||||
|
m := th.Run(prod, o)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
tshirt-size: small
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func skipIfNoDocker(t *testing.T) {
|
func skipIfNoDocker(t *testing.T) {
|
||||||
@@ -146,8 +276,6 @@ type: Opaque
|
|||||||
apiVersion: policy/v1beta1
|
apiVersion: policy/v1beta1
|
||||||
kind: PodDisruptionBudget
|
kind: PodDisruptionBudget
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/path: config/demo-budget_poddisruptionbudget.yaml
|
|
||||||
labels:
|
labels:
|
||||||
app: cockroachdb
|
app: cockroachdb
|
||||||
name: demo
|
name: demo
|
||||||
@@ -162,8 +290,6 @@ spec:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/path: config/demo-public_service.yaml
|
|
||||||
labels:
|
labels:
|
||||||
app: cockroachdb
|
app: cockroachdb
|
||||||
name: demo
|
name: demo
|
||||||
@@ -184,7 +310,6 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/path: config/demo_service.yaml
|
|
||||||
prometheus.io/path: _status/vars
|
prometheus.io/path: _status/vars
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
@@ -209,8 +334,6 @@ spec:
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: StatefulSet
|
kind: StatefulSet
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/path: config/demo_statefulset.yaml
|
|
||||||
labels:
|
labels:
|
||||||
app: cockroachdb
|
app: cockroachdb
|
||||||
name: demo
|
name: demo
|
||||||
@@ -383,7 +506,6 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/path: deployment_nginx.yaml
|
|
||||||
tshirt-size: small
|
tshirt-size: small
|
||||||
labels:
|
labels:
|
||||||
app: nginx
|
app: nginx
|
||||||
@@ -450,8 +572,6 @@ data:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Namespace
|
kind: Namespace
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/path: namespace_my-namespace.yaml
|
|
||||||
labels:
|
labels:
|
||||||
my-ns-name: function-test
|
my-ns-name: function-test
|
||||||
name: my-namespace
|
name: my-namespace
|
||||||
@@ -459,8 +579,6 @@ metadata:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Namespace
|
kind: Namespace
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/path: namespace_another-namespace.yaml
|
|
||||||
labels:
|
labels:
|
||||||
my-ns-name: function-test
|
my-ns-name: function-test
|
||||||
name: another-namespace
|
name: another-namespace
|
||||||
@@ -508,8 +626,6 @@ data:
|
|||||||
value: value
|
value: value
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/path: configmap_env.yaml
|
|
||||||
name: env
|
name: env
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
annotations:
|
|
||||||
tshirt-size: small # this injects the resource reservations
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: nginx
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx
|
|
||||||
EOF
|
|
||||||
@@ -230,3 +230,92 @@ spec:
|
|||||||
name: configmap-in-base
|
name: configmap-in-base
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPathWithCronJobV1(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK(".", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- resources.yaml
|
||||||
|
patches:
|
||||||
|
- patch: |
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
env:
|
||||||
|
- name: ENV_NEW
|
||||||
|
value: val_new
|
||||||
|
target:
|
||||||
|
kind: CronJob
|
||||||
|
name: test
|
||||||
|
`)
|
||||||
|
th.WriteF("resources.yaml", `
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
schedule: "5 10 * * 1"
|
||||||
|
concurrencyPolicy: Forbid
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
backoffLimit: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
image: bash
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- echo "test"
|
||||||
|
env:
|
||||||
|
- name: ENV1
|
||||||
|
value: val1
|
||||||
|
- name: ENV2
|
||||||
|
value: val2
|
||||||
|
- name: ENV3
|
||||||
|
value: val3`)
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
concurrencyPolicy: Forbid
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
backoffLimit: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- echo "test"
|
||||||
|
env:
|
||||||
|
- name: ENV_NEW
|
||||||
|
value: val_new
|
||||||
|
- name: ENV1
|
||||||
|
value: val1
|
||||||
|
- name: ENV2
|
||||||
|
value: val2
|
||||||
|
- name: ENV3
|
||||||
|
value: val3
|
||||||
|
image: bash
|
||||||
|
name: test
|
||||||
|
restartPolicy: Never
|
||||||
|
schedule: 5 10 * * 1
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ func writeTestSchema(th kusttest_test.Harness, filepath string) {
|
|||||||
th.WriteF(filepath+"mycrd_schema.json", string(bytes))
|
th.WriteF(filepath+"mycrd_schema.json", string(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeTestSchemaYaml(th kusttest_test.Harness, filepath string) {
|
||||||
|
bytes, _ := ioutil.ReadFile("testdata/customschema.yaml")
|
||||||
|
th.WriteF(filepath+"mycrd_schema.yaml", string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
func writeCustomResource(th kusttest_test.Harness, filepath string) {
|
func writeCustomResource(th kusttest_test.Harness, filepath string) {
|
||||||
th.WriteF(filepath, `
|
th.WriteF(filepath, `
|
||||||
apiVersion: example.com/v1alpha1
|
apiVersion: example.com/v1alpha1
|
||||||
@@ -103,6 +108,21 @@ openapi:
|
|||||||
th.AssertActualEqualsExpected(m, patchedCustomResource)
|
th.AssertActualEqualsExpected(m, patchedCustomResource)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCustomOpenApiFieldYaml(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK(".", `
|
||||||
|
resources:
|
||||||
|
- mycrd.yaml
|
||||||
|
openapi:
|
||||||
|
path: mycrd_schema.yaml
|
||||||
|
`+customSchemaPatch)
|
||||||
|
writeCustomResource(th, "mycrd.yaml")
|
||||||
|
writeTestSchemaYaml(th, "./")
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, patchedCustomResource)
|
||||||
|
}
|
||||||
|
|
||||||
// Error if user tries to specify both builtin version
|
// Error if user tries to specify both builtin version
|
||||||
// and custom schema
|
// and custom schema
|
||||||
func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
|
func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
|
||||||
@@ -111,7 +131,7 @@ func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
|
|||||||
resources:
|
resources:
|
||||||
- mycrd.yaml
|
- mycrd.yaml
|
||||||
openapi:
|
openapi:
|
||||||
version: v1.20.4
|
version: v1.21.2
|
||||||
path: mycrd_schema.json
|
path: mycrd_schema.json
|
||||||
`+customSchemaPatch)
|
`+customSchemaPatch)
|
||||||
writeCustomResource(th, "mycrd.yaml")
|
writeCustomResource(th, "mycrd.yaml")
|
||||||
@@ -197,7 +217,7 @@ openapi:
|
|||||||
resources:
|
resources:
|
||||||
- ../base
|
- ../base
|
||||||
openapi:
|
openapi:
|
||||||
version: v1.20.4
|
version: v1.21.2
|
||||||
`+customSchemaPatch)
|
`+customSchemaPatch)
|
||||||
writeCustomResource(th, "base/mycrd.yaml")
|
writeCustomResource(th, "base/mycrd.yaml")
|
||||||
writeTestSchema(th, "base/")
|
writeTestSchema(th, "base/")
|
||||||
@@ -215,7 +235,7 @@ spec:
|
|||||||
- image: nginx
|
- image: nginx
|
||||||
name: server
|
name: server
|
||||||
`)
|
`)
|
||||||
assert.Equal(t, "v1204", openapi.GetSchemaVersion())
|
assert.Equal(t, "v1212", openapi.GetSchemaVersion())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomOpenAPIFieldFromComponent(t *testing.T) {
|
func TestCustomOpenAPIFieldFromComponent(t *testing.T) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func TestOpenApiFieldBasicUsage(t *testing.T) {
|
|||||||
th := kusttest_test.MakeHarness(t)
|
th := kusttest_test.MakeHarness(t)
|
||||||
th.WriteK(".", `
|
th.WriteK(".", `
|
||||||
openapi:
|
openapi:
|
||||||
version: v1.20.4
|
version: v1.21.2
|
||||||
resources:
|
resources:
|
||||||
- deployment.yaml
|
- deployment.yaml
|
||||||
`)
|
`)
|
||||||
@@ -44,7 +44,8 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- image: whatever
|
- image: whatever
|
||||||
`)
|
`)
|
||||||
assert.Equal(t, "v1204", openapi.GetSchemaVersion())
|
assert.Equal(t, "v1212", openapi.GetSchemaVersion())
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenApiFieldNotBuiltin(t *testing.T) {
|
func TestOpenApiFieldNotBuiltin(t *testing.T) {
|
||||||
@@ -71,6 +72,7 @@ spec:
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected an error")
|
t.Fatalf("expected an error")
|
||||||
}
|
}
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpenApiFieldDefaultVersion(t *testing.T) {
|
func TestOpenApiFieldDefaultVersion(t *testing.T) {
|
||||||
@@ -104,4 +106,5 @@ spec:
|
|||||||
- image: whatever
|
- image: whatever
|
||||||
`)
|
`)
|
||||||
assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion())
|
assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion())
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package krusty_test
|
package krusty_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -39,3 +40,124 @@ spec:
|
|||||||
name: nginx
|
name: nginx
|
||||||
`, string(yml))
|
`, string(yml))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRemoteResource(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(`
|
||||||
|
resources:
|
||||||
|
- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6
|
||||||
|
`)))
|
||||||
|
m, err := b.Run(
|
||||||
|
fSys,
|
||||||
|
tmpDir.String())
|
||||||
|
if utils.IsErrTimeout(err) {
|
||||||
|
// Don't fail on timeouts.
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myapp
|
||||||
|
name: dev-myapp-pod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:1.7.9
|
||||||
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoteResourceAnnoOrigin(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(`
|
||||||
|
resources:
|
||||||
|
- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)))
|
||||||
|
m, err := b.Run(
|
||||||
|
fSys,
|
||||||
|
tmpDir.String())
|
||||||
|
if utils.IsErrTimeout(err) {
|
||||||
|
// Don't fail on timeouts.
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: examples/multibases/base/pod.yaml
|
||||||
|
repo: https://github.com/kubernetes-sigs/kustomize
|
||||||
|
ref: v1.0.6
|
||||||
|
labels:
|
||||||
|
app: myapp
|
||||||
|
name: dev-myapp-pod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:1.7.9
|
||||||
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoteResourceAsBaseWithAnnoOrigin(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
base := filepath.Join(tmpDir.String(), "base")
|
||||||
|
prod := filepath.Join(tmpDir.String(), "prod")
|
||||||
|
assert.NoError(t, fSys.Mkdir(base))
|
||||||
|
assert.NoError(t, fSys.Mkdir(prod))
|
||||||
|
assert.NoError(t, fSys.WriteFile(filepath.Join(base, "kustomization.yaml"), []byte(`
|
||||||
|
resources:
|
||||||
|
- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6
|
||||||
|
`)))
|
||||||
|
assert.NoError(t, fSys.WriteFile(filepath.Join(prod, "kustomization.yaml"), []byte(`
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
namePrefix: prefix-
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)))
|
||||||
|
|
||||||
|
m, err := b.Run(
|
||||||
|
fSys,
|
||||||
|
prod)
|
||||||
|
if utils.IsErrTimeout(err) {
|
||||||
|
// Don't fail on timeouts.
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: examples/multibases/base/pod.yaml
|
||||||
|
repo: https://github.com/kubernetes-sigs/kustomize
|
||||||
|
ref: v1.0.6
|
||||||
|
labels:
|
||||||
|
app: myapp
|
||||||
|
name: prefix-dev-myapp-pod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:1.7.9
|
||||||
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|||||||
@@ -342,3 +342,143 @@ spec:
|
|||||||
name: nginx
|
name: nginx
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Address namePrefix in overlay not applying to replacement targets
|
||||||
|
// The property `data.blue-name` should end up being `overlay-blue` instead of `blue`
|
||||||
|
// https://github.com/kubernetes-sigs/kustomize/issues/4034
|
||||||
|
func TestReplacementTransformerWithNamePrefixOverlay(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarness(t)
|
||||||
|
defer th.Reset()
|
||||||
|
|
||||||
|
th.WriteK("base", `
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
configMapGenerator:
|
||||||
|
- name: blue
|
||||||
|
- name: red
|
||||||
|
replacements:
|
||||||
|
- source:
|
||||||
|
kind: ConfigMap
|
||||||
|
name: blue
|
||||||
|
fieldPath: metadata.name
|
||||||
|
targets:
|
||||||
|
- select:
|
||||||
|
name: red
|
||||||
|
fieldPaths:
|
||||||
|
- data.blue-name
|
||||||
|
options:
|
||||||
|
create: true
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteK(".", `
|
||||||
|
namePrefix: overlay-
|
||||||
|
resources:
|
||||||
|
- base
|
||||||
|
`)
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: overlay-blue
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
blue-name: blue
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: overlay-red
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Address namespace in overlay not applying to replacement targets
|
||||||
|
// The property `data.blue-namespace` should end up being `overlay-namespace` instead of `base-namespace`
|
||||||
|
// https://github.com/kubernetes-sigs/kustomize/issues/4034
|
||||||
|
func TestReplacementTransformerWithNamespaceOverlay(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarness(t)
|
||||||
|
defer th.Reset()
|
||||||
|
|
||||||
|
th.WriteK("base", `
|
||||||
|
namespace: base-namespace
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
configMapGenerator:
|
||||||
|
- name: blue
|
||||||
|
- name: red
|
||||||
|
replacements:
|
||||||
|
- source:
|
||||||
|
kind: ConfigMap
|
||||||
|
name: blue
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
targets:
|
||||||
|
- select:
|
||||||
|
name: red
|
||||||
|
fieldPaths:
|
||||||
|
- data.blue-namespace
|
||||||
|
options:
|
||||||
|
create: true
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteK(".", `
|
||||||
|
namespace: overlay-namespace
|
||||||
|
resources:
|
||||||
|
- base
|
||||||
|
`)
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blue
|
||||||
|
namespace: overlay-namespace
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
blue-namespace: base-namespace
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: red
|
||||||
|
namespace: overlay-namespace
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Address configMapGenerator suffix not applying to replacement targets
|
||||||
|
// The property `data.blue-name` should end up being `blue-6ct58987ht` instead of `blue`
|
||||||
|
// https://github.com/kubernetes-sigs/kustomize/issues/4034
|
||||||
|
func TestReplacementTransformerWithConfigMapGenerator(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarness(t)
|
||||||
|
defer th.Reset()
|
||||||
|
|
||||||
|
th.WriteK(".", `
|
||||||
|
configMapGenerator:
|
||||||
|
- name: blue
|
||||||
|
- name: red
|
||||||
|
replacements:
|
||||||
|
- source:
|
||||||
|
kind: ConfigMap
|
||||||
|
name: blue
|
||||||
|
fieldPath: metadata.name
|
||||||
|
targets:
|
||||||
|
- select:
|
||||||
|
name: red
|
||||||
|
fieldPaths:
|
||||||
|
- data.blue-name
|
||||||
|
options:
|
||||||
|
create: true
|
||||||
|
`)
|
||||||
|
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blue-6ct58987ht
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
blue-name: blue
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: red-dc6gc5btkc
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|||||||
75
api/krusty/testdata/customschema.yaml
vendored
Normal file
75
api/krusty/testdata/customschema.yaml
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
definitions:
|
||||||
|
v1alpha1.MyCRD:
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
properties:
|
||||||
|
template:
|
||||||
|
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
properties:
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
x-kubernetes-group-version-kind:
|
||||||
|
- group: example.com
|
||||||
|
kind: MyCRD
|
||||||
|
version: v1alpha1
|
||||||
|
io.k8s.api.core.v1.PodTemplateSpec:
|
||||||
|
properties:
|
||||||
|
metadata:
|
||||||
|
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
|
||||||
|
spec:
|
||||||
|
"$ref": "#/definitions/io.k8s.api.core.v1.PodSpec"
|
||||||
|
type: object
|
||||||
|
io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
io.k8s.api.core.v1.PodSpec:
|
||||||
|
properties:
|
||||||
|
containers:
|
||||||
|
items:
|
||||||
|
"$ref": "#/definitions/io.k8s.api.core.v1.Container"
|
||||||
|
type: array
|
||||||
|
x-kubernetes-patch-merge-key: name
|
||||||
|
x-kubernetes-patch-strategy: merge
|
||||||
|
type: object
|
||||||
|
io.k8s.api.core.v1.Container:
|
||||||
|
properties:
|
||||||
|
command:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
image:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
ports:
|
||||||
|
items:
|
||||||
|
"$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort"
|
||||||
|
type: array
|
||||||
|
x-kubernetes-list-map-keys:
|
||||||
|
- containerPort
|
||||||
|
- protocol
|
||||||
|
x-kubernetes-list-type: map
|
||||||
|
x-kubernetes-patch-merge-key: containerPort
|
||||||
|
x-kubernetes-patch-strategy: merge
|
||||||
|
type: object
|
||||||
|
io.k8s.api.core.v1.ContainerPort:
|
||||||
|
properties:
|
||||||
|
containerPort:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
protocol:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
@@ -219,8 +219,10 @@ BAR=baz
|
|||||||
}
|
}
|
||||||
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, tc.input)
|
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, tc.input)
|
||||||
assert.NoError(t, err, tc.description)
|
assert.NoError(t, err, tc.description)
|
||||||
|
r.RemoveBuildAnnotations()
|
||||||
rYaml, err := r.AsYaml()
|
rYaml, err := r.AsYaml()
|
||||||
assert.NoError(t, err, tc.description)
|
assert.NoError(t, err, tc.description)
|
||||||
|
tc.expected.RemoveBuildAnnotations()
|
||||||
expYaml, err := tc.expected.AsYaml()
|
expYaml, err := tc.expected.AsYaml()
|
||||||
assert.NoError(t, err, tc.description)
|
assert.NoError(t, err, tc.description)
|
||||||
assert.Equal(t, expYaml, rYaml)
|
assert.Equal(t, expYaml, rYaml)
|
||||||
@@ -252,6 +254,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
actual.RemoveBuildAnnotations()
|
||||||
actYaml, err := actual.AsYaml()
|
actYaml, err := actual.AsYaml()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,10 @@ type ResMap interface {
|
|||||||
// self, then its behavior _cannot_ be merge or replace.
|
// self, then its behavior _cannot_ be merge or replace.
|
||||||
AbsorbAll(ResMap) error
|
AbsorbAll(ResMap) error
|
||||||
|
|
||||||
|
// AnnotateAll annotates all resources in the ResMap with
|
||||||
|
// the provided key value pair.
|
||||||
|
AnnotateAll(key string, value string) error
|
||||||
|
|
||||||
// AsYaml returns the yaml form of resources.
|
// AsYaml returns the yaml form of resources.
|
||||||
AsYaml() ([]byte, error)
|
AsYaml() ([]byte, error)
|
||||||
|
|
||||||
@@ -210,6 +214,35 @@ type ResMap interface {
|
|||||||
// namespaces. Cluster wide objects are never excluded.
|
// namespaces. Cluster wide objects are never excluded.
|
||||||
SubsetThatCouldBeReferencedByResource(*resource.Resource) ResMap
|
SubsetThatCouldBeReferencedByResource(*resource.Resource) ResMap
|
||||||
|
|
||||||
|
// DeAnchor replaces YAML aliases with structured data copied from anchors.
|
||||||
|
// This cannot be undone; if desired, call DeepCopy first.
|
||||||
|
// Subsequent marshalling to YAML will no longer have anchor
|
||||||
|
// definitions ('&') or aliases ('*').
|
||||||
|
//
|
||||||
|
// Anchors are not expected to work across YAML 'documents'.
|
||||||
|
// If three resources are loaded from one file containing three YAML docs:
|
||||||
|
//
|
||||||
|
// {resourceA}
|
||||||
|
// ---
|
||||||
|
// {resourceB}
|
||||||
|
// ---
|
||||||
|
// {resourceC}
|
||||||
|
//
|
||||||
|
// then anchors defined in A cannot be seen from B and C and vice versa.
|
||||||
|
// OTOH, cross-resource links (a field in B referencing fields in A) will
|
||||||
|
// work if the resources are gathered in a ResourceList:
|
||||||
|
//
|
||||||
|
// apiVersion: config.kubernetes.io/v1
|
||||||
|
// kind: ResourceList
|
||||||
|
// metadata:
|
||||||
|
// name: someList
|
||||||
|
// items:
|
||||||
|
// - {resourceA}
|
||||||
|
// - {resourceB}
|
||||||
|
// - {resourceC}
|
||||||
|
//
|
||||||
|
DeAnchor() error
|
||||||
|
|
||||||
// DeepCopy copies the ResMap and underlying resources.
|
// DeepCopy copies the ResMap and underlying resources.
|
||||||
DeepCopy() ResMap
|
DeepCopy() ResMap
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/annotations"
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
@@ -531,6 +532,19 @@ func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AnnotateAll implements ResMap
|
||||||
|
func (m *resWrangler) AnnotateAll(key string, value string) error {
|
||||||
|
return m.ApplyFilter(annotations.Filter{
|
||||||
|
Annotations: map[string]string{
|
||||||
|
key: value,
|
||||||
|
},
|
||||||
|
FsSlice: []types.FieldSpec{{
|
||||||
|
Path: "metadata/annotations",
|
||||||
|
CreateIfNotPresent: true,
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Select returns a list of resources that
|
// Select returns a list of resources that
|
||||||
// are selected by a Selector
|
// are selected by a Selector
|
||||||
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
|
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
|
||||||
@@ -593,6 +607,16 @@ func (m *resWrangler) ToRNodeSlice() []*kyaml.RNode {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeAnchor implements ResMap.
|
||||||
|
func (m *resWrangler) DeAnchor() (err error) {
|
||||||
|
for i := range m.rList {
|
||||||
|
if err = m.rList[i].DeAnchor(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ApplySmPatch applies the patch, and errors on Id collisions.
|
// ApplySmPatch applies the patch, and errors on Id collisions.
|
||||||
func (m *resWrangler) ApplySmPatch(
|
func (m *resWrangler) ApplySmPatch(
|
||||||
selectedSet *resource.IdSet, patch *resource.Resource) error {
|
selectedSet *resource.IdSet, patch *resource.Resource) error {
|
||||||
|
|||||||
@@ -343,9 +343,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
|
|||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "new-alice",
|
"name": "new-alice",
|
||||||
"annotations": map[string]interface{}{
|
"annotations": map[string]interface{}{
|
||||||
"config.kubernetes.io/previousKinds": "ConfigMap",
|
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
|
||||||
"config.kubernetes.io/previousNames": "alice",
|
"internal.config.kubernetes.io/previousNames": "alice",
|
||||||
"config.kubernetes.io/previousNamespaces": "default",
|
"internal.config.kubernetes.io/previousNamespaces": "default",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -356,9 +356,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
|
|||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "new-bob",
|
"name": "new-bob",
|
||||||
"annotations": map[string]interface{}{
|
"annotations": map[string]interface{}{
|
||||||
"config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap",
|
"internal.config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap",
|
||||||
"config.kubernetes.io/previousNames": "bob,bob2",
|
"internal.config.kubernetes.io/previousNames": "bob,bob2",
|
||||||
"config.kubernetes.io/previousNamespaces": "default,default",
|
"internal.config.kubernetes.io/previousNamespaces": "default,default",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -370,9 +370,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
|
|||||||
"name": "new-bob",
|
"name": "new-bob",
|
||||||
"namespace": "new-happy",
|
"namespace": "new-happy",
|
||||||
"annotations": map[string]interface{}{
|
"annotations": map[string]interface{}{
|
||||||
"config.kubernetes.io/previousKinds": "ConfigMap",
|
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
|
||||||
"config.kubernetes.io/previousNames": "bob",
|
"internal.config.kubernetes.io/previousNames": "bob",
|
||||||
"config.kubernetes.io/previousNamespaces": "happy",
|
"internal.config.kubernetes.io/previousNamespaces": "happy",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -384,9 +384,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
|
|||||||
"name": "charlie",
|
"name": "charlie",
|
||||||
"namespace": "happy",
|
"namespace": "happy",
|
||||||
"annotations": map[string]interface{}{
|
"annotations": map[string]interface{}{
|
||||||
"config.kubernetes.io/previousKinds": "ConfigMap",
|
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
|
||||||
"config.kubernetes.io/previousNames": "charlie",
|
"internal.config.kubernetes.io/previousNames": "charlie",
|
||||||
"config.kubernetes.io/previousNamespaces": "default",
|
"internal.config.kubernetes.io/previousNamespaces": "default",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -845,6 +845,8 @@ func TestAbsorbAll(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
w := makeMap1()
|
w := makeMap1()
|
||||||
assert.NoError(t, w.AbsorbAll(makeMap2(types.BehaviorMerge)))
|
assert.NoError(t, w.AbsorbAll(makeMap2(types.BehaviorMerge)))
|
||||||
|
expected.RemoveBuildAnnotations()
|
||||||
|
w.RemoveBuildAnnotations()
|
||||||
assert.NoError(t, expected.ErrorIfNotEqualLists(w))
|
assert.NoError(t, expected.ErrorIfNotEqualLists(w))
|
||||||
w = makeMap1()
|
w = makeMap1()
|
||||||
assert.NoError(t, w.AbsorbAll(nil))
|
assert.NoError(t, w.AbsorbAll(nil))
|
||||||
@@ -853,6 +855,7 @@ func TestAbsorbAll(t *testing.T) {
|
|||||||
w = makeMap1()
|
w = makeMap1()
|
||||||
w2 := makeMap2(types.BehaviorReplace)
|
w2 := makeMap2(types.BehaviorReplace)
|
||||||
assert.NoError(t, w.AbsorbAll(w2))
|
assert.NoError(t, w.AbsorbAll(w2))
|
||||||
|
w2.RemoveBuildAnnotations()
|
||||||
assert.NoError(t, w2.ErrorIfNotEqualLists(w))
|
assert.NoError(t, w2.ErrorIfNotEqualLists(w))
|
||||||
w = makeMap1()
|
w = makeMap1()
|
||||||
w2 = makeMap2(types.BehaviorUnspecified)
|
w2 = makeMap2(types.BehaviorUnspecified)
|
||||||
@@ -899,6 +902,100 @@ rules:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeAnchorSingleDoc(t *testing.T) {
|
||||||
|
input := `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: wildcard
|
||||||
|
data:
|
||||||
|
color: &color-used blue
|
||||||
|
feeling: *color-used
|
||||||
|
`
|
||||||
|
rm, err := rmF.NewResMapFromBytes([]byte(input))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, rm.DeAnchor())
|
||||||
|
yaml, err := rm.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, strings.TrimSpace(`
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
color: blue
|
||||||
|
feeling: blue
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: wildcard
|
||||||
|
`), strings.TrimSpace(string(yaml)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anchor references don't cross YAML document boundaries.
|
||||||
|
func TestDeAnchorMultiDoc(t *testing.T) {
|
||||||
|
input := `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: betty
|
||||||
|
data:
|
||||||
|
color: &color-used blue
|
||||||
|
feeling: *color-used
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: bob
|
||||||
|
data:
|
||||||
|
color: red
|
||||||
|
feeling: *color-used
|
||||||
|
`
|
||||||
|
_, err := rmF.NewResMapFromBytes([]byte(input))
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "unknown anchor 'color-used' referenced")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anchor references cross list elements in a ResourceList.
|
||||||
|
func TestDeAnchorResourceList(t *testing.T) {
|
||||||
|
input := `apiVersion: config.kubernetes.io/v1
|
||||||
|
kind: ResourceList
|
||||||
|
metadata:
|
||||||
|
name: aShortList
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: betty
|
||||||
|
data:
|
||||||
|
color: &color-used blue
|
||||||
|
feeling: *color-used
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: bob
|
||||||
|
data:
|
||||||
|
color: red
|
||||||
|
feeling: *color-used
|
||||||
|
`
|
||||||
|
rm, err := rmF.NewResMapFromBytes([]byte(input))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, rm.DeAnchor())
|
||||||
|
yaml, err := rm.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, strings.TrimSpace(`
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
color: blue
|
||||||
|
feeling: blue
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: betty
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
color: red
|
||||||
|
feeling: blue
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: bob
|
||||||
|
`), strings.TrimSpace(string(yaml)))
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplySmPatch_General(t *testing.T) {
|
func TestApplySmPatch_General(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
myDeployment = "Deployment"
|
myDeployment = "Deployment"
|
||||||
|
|||||||
@@ -64,18 +64,23 @@ func (rf *Factory) FromMapAndOption(
|
|||||||
// TODO: return err instead of log.
|
// TODO: return err instead of log.
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
return rf.makeOne(n, types.NewGenArgs(args))
|
return rf.makeOne(n, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeOne returns a new instance of Resource.
|
// makeOne returns a new instance of Resource.
|
||||||
func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GenArgs) *Resource {
|
func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GeneratorArgs) *Resource {
|
||||||
if rn == nil {
|
if rn == nil {
|
||||||
log.Fatal("RNode must not be null")
|
log.Fatal("RNode must not be null")
|
||||||
}
|
}
|
||||||
if o == nil {
|
resource := &Resource{RNode: *rn}
|
||||||
o = types.NewGenArgs(nil)
|
if o != nil {
|
||||||
|
if o.Options == nil || !o.Options.DisableNameSuffixHash {
|
||||||
|
resource.EnableHashSuffix()
|
||||||
|
}
|
||||||
|
resource.SetBehavior(types.NewGenerationBehavior(o.Behavior))
|
||||||
}
|
}
|
||||||
return &Resource{RNode: *rn, options: o}
|
|
||||||
|
return resource
|
||||||
}
|
}
|
||||||
|
|
||||||
// SliceFromPatches returns a slice of resources given a patch path
|
// SliceFromPatches returns a slice of resources given a patch path
|
||||||
@@ -265,7 +270,7 @@ func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
|
return rf.makeOne(rn, &args.GeneratorArgs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeSecret makes an instance of Resource for Secret
|
// MakeSecret makes an instance of Resource for Secret
|
||||||
@@ -274,5 +279,5 @@ func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Reso
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
|
return rf.makeOne(rn, &args.GeneratorArgs), nil
|
||||||
}
|
}
|
||||||
|
|||||||
60
api/resource/origin.go
Normal file
60
api/resource/origin.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/git"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Origin retains information about where resources in the output
|
||||||
|
// of `kustomize build` originated from
|
||||||
|
type Origin struct {
|
||||||
|
// Path is the path to the resource, rooted from the directory upon
|
||||||
|
// which `kustomize build` was invoked
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Repo is the remote repository that the resource originated from if it is
|
||||||
|
// not from a local file
|
||||||
|
Repo string
|
||||||
|
|
||||||
|
// Ref is the ref of the remote repository that the resource originated from
|
||||||
|
// if it is not from a local file
|
||||||
|
Ref string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of origin
|
||||||
|
func (origin *Origin) Copy() Origin {
|
||||||
|
return *origin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append returns a copy of origin with a path appended to it
|
||||||
|
func (origin *Origin) Append(path string) *Origin {
|
||||||
|
originCopy := origin.Copy()
|
||||||
|
repoSpec, err := git.NewRepoSpecFromUrl(path)
|
||||||
|
if err == nil {
|
||||||
|
originCopy.Repo = repoSpec.Host + repoSpec.OrgRepo
|
||||||
|
absPath := repoSpec.AbsPath()
|
||||||
|
path = absPath[strings.Index(absPath[1:], "/")+1:][1:]
|
||||||
|
originCopy.Path = ""
|
||||||
|
originCopy.Ref = repoSpec.Ref
|
||||||
|
}
|
||||||
|
originCopy.Path = filepath.Join(originCopy.Path, path)
|
||||||
|
return &originCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string version of origin
|
||||||
|
func (origin *Origin) String() string {
|
||||||
|
var anno string
|
||||||
|
anno = anno + "path: " + origin.Path + "\n"
|
||||||
|
if origin.Repo != "" {
|
||||||
|
anno = anno + "repo: " + origin.Repo + "\n"
|
||||||
|
}
|
||||||
|
if origin.Ref != "" {
|
||||||
|
anno = anno + "ref: " + origin.Ref + "\n"
|
||||||
|
}
|
||||||
|
return anno
|
||||||
|
}
|
||||||
83
api/resource/origin_test.go
Normal file
83
api/resource/origin_test.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package resource_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOriginAppend(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in *Origin
|
||||||
|
path string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
in: &Origin{
|
||||||
|
Path: "prod",
|
||||||
|
},
|
||||||
|
path: "service.yaml",
|
||||||
|
expected: `path: prod/service.yaml
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: &Origin{
|
||||||
|
Path: "overlay/prod",
|
||||||
|
},
|
||||||
|
path: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/",
|
||||||
|
expected: `path: examples/multibases/dev
|
||||||
|
repo: https://github.com/kubernetes-sigs/kustomize
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
actual := test.in.Append(test.path).String()
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Fatalf("Expected %v, but got %v\n", test.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOriginString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in *Origin
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
in: &Origin{
|
||||||
|
Path: "prod/service.yaml",
|
||||||
|
Repo: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/",
|
||||||
|
Ref: "v1.0.6",
|
||||||
|
},
|
||||||
|
expected: `path: prod/service.yaml
|
||||||
|
repo: github.com/kubernetes-sigs/kustomize/examples/multibases/dev/
|
||||||
|
ref: v1.0.6
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: &Origin{
|
||||||
|
Path: "prod/service.yaml",
|
||||||
|
Repo: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/",
|
||||||
|
},
|
||||||
|
expected: `path: prod/service.yaml
|
||||||
|
repo: github.com/kubernetes-sigs/kustomize/examples/multibases/dev/
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: &Origin{
|
||||||
|
Path: "prod/service.yaml",
|
||||||
|
},
|
||||||
|
expected: `path: prod/service.yaml
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
if test.in.String() != test.expected {
|
||||||
|
t.Fatalf("Expected %v, but got %v\n", test.expected, test.in.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/api/internal/utils"
|
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||||
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
@@ -22,8 +23,6 @@ import (
|
|||||||
// paired with metadata used by kustomize.
|
// paired with metadata used by kustomize.
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
kyaml.RNode
|
kyaml.RNode
|
||||||
options *types.GenArgs
|
|
||||||
refBy []resid.ResId
|
|
||||||
refVarNames []string
|
refVarNames []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +34,13 @@ var BuildAnnotations = []string{
|
|||||||
utils.BuildAnnotationPreviousNamespaces,
|
utils.BuildAnnotationPreviousNamespaces,
|
||||||
utils.BuildAnnotationAllowNameChange,
|
utils.BuildAnnotationAllowNameChange,
|
||||||
utils.BuildAnnotationAllowKindChange,
|
utils.BuildAnnotationAllowKindChange,
|
||||||
|
utils.BuildAnnotationsRefBy,
|
||||||
|
utils.BuildAnnotationsGenBehavior,
|
||||||
|
utils.BuildAnnotationsGenAddHashSuffix,
|
||||||
|
|
||||||
|
kioutil.PathAnnotation,
|
||||||
|
kioutil.IndexAnnotation,
|
||||||
|
kioutil.SeqIndentAnnotation,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) ResetRNode(incoming *Resource) {
|
func (r *Resource) ResetRNode(incoming *Resource) {
|
||||||
@@ -80,6 +86,8 @@ func (r *Resource) DeepCopy() *Resource {
|
|||||||
// CopyMergeMetaDataFieldsFrom copies everything but the non-metadata in
|
// CopyMergeMetaDataFieldsFrom copies everything but the non-metadata in
|
||||||
// the resource.
|
// the resource.
|
||||||
// TODO: move to RNode, use GetMeta to improve performance.
|
// TODO: move to RNode, use GetMeta to improve performance.
|
||||||
|
// TODO: make a version of mergeStringMaps that is build-annotation aware
|
||||||
|
// to avoid repeatedly setting refby and genargs annotations
|
||||||
// Must remove the kustomize bit at the end.
|
// Must remove the kustomize bit at the end.
|
||||||
func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
|
func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
|
||||||
if err := r.SetLabels(
|
if err := r.SetLabels(
|
||||||
@@ -87,7 +95,7 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
|
|||||||
return fmt.Errorf("copyMerge cannot set labels - %w", err)
|
return fmt.Errorf("copyMerge cannot set labels - %w", err)
|
||||||
}
|
}
|
||||||
if err := r.SetAnnotations(
|
if err := r.SetAnnotations(
|
||||||
mergeStringMaps(other.GetAnnotations(), r.GetAnnotations())); err != nil {
|
mergeStringMapsWithBuildAnnotations(other.GetAnnotations(), r.GetAnnotations())); err != nil {
|
||||||
return fmt.Errorf("copyMerge cannot set annotations - %w", err)
|
return fmt.Errorf("copyMerge cannot set annotations - %w", err)
|
||||||
}
|
}
|
||||||
if err := r.SetName(other.GetName()); err != nil {
|
if err := r.SetName(other.GetName()); err != nil {
|
||||||
@@ -101,8 +109,6 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) copyKustomizeSpecificFields(other *Resource) {
|
func (r *Resource) copyKustomizeSpecificFields(other *Resource) {
|
||||||
r.options = other.options
|
|
||||||
r.refBy = other.copyRefBy()
|
|
||||||
r.refVarNames = copyStringSlice(other.refVarNames)
|
r.refVarNames = copyStringSlice(other.refVarNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,10 +150,10 @@ func (r *Resource) ErrIfNotEquals(o *Resource) error {
|
|||||||
func (r *Resource) ReferencesEqual(other *Resource) bool {
|
func (r *Resource) ReferencesEqual(other *Resource) bool {
|
||||||
setSelf := make(map[resid.ResId]bool)
|
setSelf := make(map[resid.ResId]bool)
|
||||||
setOther := make(map[resid.ResId]bool)
|
setOther := make(map[resid.ResId]bool)
|
||||||
for _, ref := range other.refBy {
|
for _, ref := range other.GetRefBy() {
|
||||||
setOther[ref] = true
|
setOther[ref] = true
|
||||||
}
|
}
|
||||||
for _, ref := range r.refBy {
|
for _, ref := range r.GetRefBy() {
|
||||||
if _, ok := setOther[ref]; !ok {
|
if _, ok := setOther[ref]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -156,15 +162,6 @@ func (r *Resource) ReferencesEqual(other *Resource) bool {
|
|||||||
return len(setSelf) == len(setOther)
|
return len(setSelf) == len(setOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) copyRefBy() []resid.ResId {
|
|
||||||
if r.refBy == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s := make([]resid.ResId, len(r.refBy))
|
|
||||||
copy(s, r.refBy)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyStringSlice(s []string) []string {
|
func copyStringSlice(s []string) []string {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -250,41 +247,45 @@ func (r *Resource) setPreviousId(ns string, n string, k string) *Resource {
|
|||||||
|
|
||||||
// AllowNameChange allows name changes to the resource.
|
// AllowNameChange allows name changes to the resource.
|
||||||
func (r *Resource) AllowNameChange() {
|
func (r *Resource) AllowNameChange() {
|
||||||
annotations := r.GetAnnotations()
|
r.enable(utils.BuildAnnotationAllowNameChange)
|
||||||
annotations[utils.BuildAnnotationAllowNameChange] = utils.Allowed
|
|
||||||
if err := r.SetAnnotations(annotations); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NameChangeAllowed checks if a patch resource is allowed to change another resource's name.
|
||||||
func (r *Resource) NameChangeAllowed() bool {
|
func (r *Resource) NameChangeAllowed() bool {
|
||||||
annotations := r.GetAnnotations()
|
return r.isEnabled(utils.BuildAnnotationAllowNameChange)
|
||||||
v, ok := annotations[utils.BuildAnnotationAllowNameChange]
|
|
||||||
return ok && v == utils.Allowed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllowKindChange allows kind changes to the resource.
|
// AllowKindChange allows kind changes to the resource.
|
||||||
func (r *Resource) AllowKindChange() {
|
func (r *Resource) AllowKindChange() {
|
||||||
|
r.enable(utils.BuildAnnotationAllowKindChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KindChangeAllowed checks if a patch resource is allowed to change another resource's kind.
|
||||||
|
func (r *Resource) KindChangeAllowed() bool {
|
||||||
|
return r.isEnabled(utils.BuildAnnotationAllowKindChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resource) isEnabled(annoKey string) bool {
|
||||||
annotations := r.GetAnnotations()
|
annotations := r.GetAnnotations()
|
||||||
annotations[utils.BuildAnnotationAllowKindChange] = utils.Allowed
|
v, ok := annotations[annoKey]
|
||||||
|
return ok && v == utils.Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resource) enable(annoKey string) {
|
||||||
|
annotations := r.GetAnnotations()
|
||||||
|
annotations[annoKey] = utils.Enabled
|
||||||
if err := r.SetAnnotations(annotations); err != nil {
|
if err := r.SetAnnotations(annotations); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) KindChangeAllowed() bool {
|
|
||||||
annotations := r.GetAnnotations()
|
|
||||||
v, ok := annotations[utils.BuildAnnotationAllowKindChange]
|
|
||||||
return ok && v == utils.Allowed
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns resource as JSON.
|
// String returns resource as JSON.
|
||||||
func (r *Resource) String() string {
|
func (r *Resource) String() string {
|
||||||
bs, err := r.MarshalJSON()
|
bs, err := r.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "<" + err.Error() + ">"
|
return "<" + err.Error() + ">"
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(bs)) + r.options.String()
|
return strings.TrimSpace(string(bs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsYAML returns the resource in Yaml form.
|
// AsYAML returns the resource in Yaml form.
|
||||||
@@ -306,20 +307,34 @@ func (r *Resource) MustYaml() string {
|
|||||||
return string(yml)
|
return string(yml)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOptions updates the generator options for the resource.
|
|
||||||
func (r *Resource) SetOptions(o *types.GenArgs) {
|
|
||||||
r.options = o
|
|
||||||
}
|
|
||||||
|
|
||||||
// Behavior returns the behavior for the resource.
|
// Behavior returns the behavior for the resource.
|
||||||
func (r *Resource) Behavior() types.GenerationBehavior {
|
func (r *Resource) Behavior() types.GenerationBehavior {
|
||||||
return r.options.Behavior()
|
annotations := r.GetAnnotations()
|
||||||
|
if v, ok := annotations[utils.BuildAnnotationsGenBehavior]; ok {
|
||||||
|
return types.NewGenerationBehavior(v)
|
||||||
|
}
|
||||||
|
return types.NewGenerationBehavior("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBehavior sets the behavior for the resource.
|
||||||
|
func (r *Resource) SetBehavior(behavior types.GenerationBehavior) {
|
||||||
|
annotations := r.GetAnnotations()
|
||||||
|
annotations[utils.BuildAnnotationsGenBehavior] = behavior.String()
|
||||||
|
if err := r.SetAnnotations(annotations); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NeedHashSuffix returns true if a resource content
|
// NeedHashSuffix returns true if a resource content
|
||||||
// hash should be appended to the name of the resource.
|
// hash should be appended to the name of the resource.
|
||||||
func (r *Resource) NeedHashSuffix() bool {
|
func (r *Resource) NeedHashSuffix() bool {
|
||||||
return r.options != nil && r.options.ShouldAddHashSuffixToName()
|
return r.isEnabled(utils.BuildAnnotationsGenAddHashSuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableHashSuffix marks the resource as needing a content
|
||||||
|
// hash to be appended to the name of the resource.
|
||||||
|
func (r *Resource) EnableHashSuffix() {
|
||||||
|
r.enable(utils.BuildAnnotationsGenAddHashSuffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrgId returns the original, immutable ResId for the resource.
|
// OrgId returns the original, immutable ResId for the resource.
|
||||||
@@ -363,12 +378,18 @@ func (r *Resource) CurId() resid.ResId {
|
|||||||
|
|
||||||
// GetRefBy returns the ResIds that referred to current resource
|
// GetRefBy returns the ResIds that referred to current resource
|
||||||
func (r *Resource) GetRefBy() []resid.ResId {
|
func (r *Resource) GetRefBy() []resid.ResId {
|
||||||
return r.refBy
|
var resIds []resid.ResId
|
||||||
|
asStrings := r.getCsvAnnotation(utils.BuildAnnotationsRefBy)
|
||||||
|
for _, s := range asStrings {
|
||||||
|
resIds = append(resIds, resid.FromString(s))
|
||||||
|
}
|
||||||
|
return resIds
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendRefBy appends a ResId into the refBy list
|
// AppendRefBy appends a ResId into the refBy list
|
||||||
func (r *Resource) AppendRefBy(id resid.ResId) {
|
// Using any type except fmt.Stringer here results in a compilation error
|
||||||
r.refBy = append(r.refBy, id)
|
func (r *Resource) AppendRefBy(id fmt.Stringer) {
|
||||||
|
r.appendCsvAnnotation(utils.BuildAnnotationsRefBy, id.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRefVarNames returns vars that refer to current resource
|
// GetRefVarNames returns vars that refer to current resource
|
||||||
@@ -424,3 +445,17 @@ func mergeStringMaps(maps ...map[string]string) map[string]string {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mergeStringMapsWithBuildAnnotations(maps ...map[string]string) map[string]string {
|
||||||
|
result := mergeStringMaps(maps...)
|
||||||
|
for i := range BuildAnnotations {
|
||||||
|
if len(maps) > 0 {
|
||||||
|
if v, ok := maps[0][BuildAnnotations[i]]; ok {
|
||||||
|
result[BuildAnnotations[i]] = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(result, BuildAnnotations[i])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ var testConfigMap = factory.FromMap(
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const genArgOptions = "{nsfx:false,beh:unspecified}"
|
|
||||||
|
|
||||||
//nolint:gosec
|
//nolint:gosec
|
||||||
const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
|
const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
|
||||||
|
|
||||||
@@ -66,17 +64,15 @@ func TestResourceString(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
in: testConfigMap,
|
in: testConfigMap,
|
||||||
s: configMapAsString + genArgOptions,
|
s: configMapAsString,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: testDeployment,
|
in: testDeployment,
|
||||||
s: deploymentAsString + genArgOptions,
|
s: deploymentAsString,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
if test.in.String() != test.s {
|
assert.Equal(t, test.in.String(), test.s)
|
||||||
t.Fatalf("Expected %s == %s", test.in.String(), test.s)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,6 +280,266 @@ spec:
|
|||||||
`, string(bytes))
|
`, string(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplySmPatchShouldOutputListItemsInCorrectOrder(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
skip bool
|
||||||
|
patch string
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Order should not change when patch has foo only",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: foo
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: foo
|
||||||
|
- name: bar
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Order changes when patch has bar only",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: bar
|
||||||
|
`,
|
||||||
|
// This test records current behavior, but this behavior might be undesirable.
|
||||||
|
// If so, feel free to change the test to pass with some improved algorithm.
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: bar
|
||||||
|
- name: foo
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Order should not change and should include a new item at the beginning when patch has a new list item",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: baz
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: baz
|
||||||
|
- name: foo
|
||||||
|
- name: bar
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Order should not change when patch has foo and bar in same order",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: foo
|
||||||
|
- name: bar
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: foo
|
||||||
|
- name: bar
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Order should change when patch has foo and bar in different order",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: bar
|
||||||
|
- name: foo
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: bar
|
||||||
|
- name: foo
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if tc.skip {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
resource, err := factory.FromBytes([]byte(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: foo
|
||||||
|
- name: bar
|
||||||
|
`))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
patch, err := factory.FromBytes([]byte(tc.patch))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, resource.ApplySmPatch(patch))
|
||||||
|
bytes, err := resource.AsYAML()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.expectedOutput, string(bytes))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplySmPatchShouldOutputPrimitiveListItemsInCorrectOrder(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
skip bool
|
||||||
|
patch string
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Order should not change when patch has foo only",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
finalizers: ["foo"]
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
finalizers:
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
name: test
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Order should not change when patch has bar only",
|
||||||
|
skip: true, // TODO: This test should pass but fails currently. Fix the problem and unskip this test
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
finalizers: ["bar"]
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
finalizers:
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
name: test
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Order should not change and should include a new item at the beginning when patch has a new list item",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
finalizers: ["baz"]
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
finalizers:
|
||||||
|
- baz
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
name: test
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Order should not change when patch has foo and bar in same order",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
finalizers: ["foo", "bar"]
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
finalizers:
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
name: test
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Order should change when patch has foo and bar in different order",
|
||||||
|
patch: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
finalizers: ["bar", "foo"]
|
||||||
|
`,
|
||||||
|
expectedOutput: `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
finalizers:
|
||||||
|
- bar
|
||||||
|
- foo
|
||||||
|
name: test
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if tc.skip {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
resource, err := factory.FromBytes([]byte(`
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
finalizers: ["foo", "bar"]
|
||||||
|
`))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
patch, err := factory.FromBytes([]byte(tc.patch))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, resource.ApplySmPatch(patch))
|
||||||
|
bytes, err := resource.AsYAML()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.expectedOutput, string(bytes))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMergeDataMapFrom(t *testing.T) {
|
func TestMergeDataMapFrom(t *testing.T) {
|
||||||
resource, err := factory.FromBytes([]byte(`
|
resource, err := factory.FromBytes([]byte(`
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@@ -717,9 +973,9 @@ metadata:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousKinds: Secret
|
internal.config.kubernetes.io/previousKinds: Secret
|
||||||
config.kubernetes.io/previousNames: oldName
|
internal.config.kubernetes.io/previousNames: oldName
|
||||||
config.kubernetes.io/previousNamespaces: default
|
internal.config.kubernetes.io/previousNamespaces: default
|
||||||
name: newName
|
name: newName
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
@@ -729,9 +985,9 @@ metadata:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousKinds: Secret
|
internal.config.kubernetes.io/previousKinds: Secret
|
||||||
config.kubernetes.io/previousNames: oldName
|
internal.config.kubernetes.io/previousNames: oldName
|
||||||
config.kubernetes.io/previousNamespaces: default
|
internal.config.kubernetes.io/previousNamespaces: default
|
||||||
name: oldName2
|
name: oldName2
|
||||||
`,
|
`,
|
||||||
newName: "newName",
|
newName: "newName",
|
||||||
@@ -740,9 +996,9 @@ metadata:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousKinds: Secret,Secret
|
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
||||||
config.kubernetes.io/previousNames: oldName,oldName2
|
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
||||||
config.kubernetes.io/previousNamespaces: default,default
|
internal.config.kubernetes.io/previousNamespaces: default,default
|
||||||
name: newName
|
name: newName
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
@@ -752,9 +1008,9 @@ metadata:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousKinds: Secret
|
internal.config.kubernetes.io/previousKinds: Secret
|
||||||
config.kubernetes.io/previousNames: oldName
|
internal.config.kubernetes.io/previousNames: oldName
|
||||||
config.kubernetes.io/previousNamespaces: default
|
internal.config.kubernetes.io/previousNamespaces: default
|
||||||
name: oldName2
|
name: oldName2
|
||||||
namespace: oldNamespace
|
namespace: oldNamespace
|
||||||
`,
|
`,
|
||||||
@@ -764,9 +1020,9 @@ metadata:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousKinds: Secret,Secret
|
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
||||||
config.kubernetes.io/previousNames: oldName,oldName2
|
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
||||||
config.kubernetes.io/previousNamespaces: default,oldNamespace
|
internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
|
||||||
name: newName
|
name: newName
|
||||||
namespace: newNamespace
|
namespace: newNamespace
|
||||||
`,
|
`,
|
||||||
@@ -814,9 +1070,9 @@ metadata:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousKinds: Secret
|
internal.config.kubernetes.io/previousKinds: Secret
|
||||||
config.kubernetes.io/previousNames: oldName
|
internal.config.kubernetes.io/previousNames: oldName
|
||||||
config.kubernetes.io/previousNamespaces: default
|
internal.config.kubernetes.io/previousNamespaces: default
|
||||||
name: newName
|
name: newName
|
||||||
`,
|
`,
|
||||||
expected: []resid.ResId{
|
expected: []resid.ResId{
|
||||||
@@ -833,9 +1089,9 @@ metadata:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/previousKinds: Secret,Secret
|
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
||||||
config.kubernetes.io/previousNames: oldName,oldName2
|
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
||||||
config.kubernetes.io/previousNamespaces: default,oldNamespace
|
internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
|
||||||
name: newName
|
name: newName
|
||||||
namespace: newNamespace
|
namespace: newNamespace
|
||||||
`,
|
`,
|
||||||
@@ -1133,3 +1389,41 @@ spec:
|
|||||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRefBy(t *testing.T) {
|
||||||
|
r, err := factory.FromBytes([]byte(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: clown
|
||||||
|
spec:
|
||||||
|
numReplicas: 1
|
||||||
|
`))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.AppendRefBy(resid.FromString("gr1_ver1_knd1|ns1|name1"))
|
||||||
|
assert.Equal(t, r.RNode.MustString(), `apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: clown
|
||||||
|
annotations:
|
||||||
|
internal.config.kubernetes.io/refBy: gr1_ver1_knd1|ns1|name1
|
||||||
|
spec:
|
||||||
|
numReplicas: 1
|
||||||
|
`)
|
||||||
|
assert.Equal(t, r.GetRefBy(), []resid.ResId{resid.FromString("gr1_ver1_knd1|ns1|name1")})
|
||||||
|
|
||||||
|
r.AppendRefBy(resid.FromString("gr2_ver2_knd2|ns2|name2"))
|
||||||
|
assert.Equal(t, r.RNode.MustString(), `apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: clown
|
||||||
|
annotations:
|
||||||
|
internal.config.kubernetes.io/refBy: gr1_ver1_knd1|ns1|name1,gr2_ver2_knd2|ns2|name2
|
||||||
|
spec:
|
||||||
|
numReplicas: 1
|
||||||
|
`)
|
||||||
|
assert.Equal(t, r.GetRefBy(), []resid.ResId{
|
||||||
|
resid.FromString("gr1_ver1_knd1|ns1|name1"),
|
||||||
|
resid.FromString("gr2_ver2_knd2|ns2|name2"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -148,6 +148,13 @@ func (th *HarnessEnhanced) ResetLoaderRoot(root string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *HarnessEnhanced) LoadAndRunGenerator(
|
func (th *HarnessEnhanced) LoadAndRunGenerator(
|
||||||
|
config string) resmap.ResMap {
|
||||||
|
rm := th.LoadAndRunGeneratorWithBuildAnnotations(config)
|
||||||
|
rm.RemoveBuildAnnotations()
|
||||||
|
return rm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *HarnessEnhanced) LoadAndRunGeneratorWithBuildAnnotations(
|
||||||
config string) resmap.ResMap {
|
config string) resmap.ResMap {
|
||||||
res, err := th.rf.RF().FromBytes([]byte(config))
|
res, err := th.rf.RF().FromBytes([]byte(config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -162,7 +169,6 @@ func (th *HarnessEnhanced) LoadAndRunGenerator(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
th.t.Fatalf("generate err: %v", err)
|
th.t.Fatalf("generate err: %v", err)
|
||||||
}
|
}
|
||||||
rm.RemoveBuildAnnotations()
|
|
||||||
return rm
|
return rm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GenArgs is a facade over GeneratorArgs, exposing a few readonly properties.
|
|
||||||
type GenArgs struct {
|
|
||||||
args *GeneratorArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGenArgs returns a new instance of GenArgs.
|
|
||||||
func NewGenArgs(args *GeneratorArgs) *GenArgs {
|
|
||||||
return &GenArgs{args: args}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GenArgs) String() string {
|
|
||||||
if g == nil {
|
|
||||||
return "{nilGenArgs}"
|
|
||||||
}
|
|
||||||
return "{" +
|
|
||||||
strings.Join([]string{
|
|
||||||
"nsfx:" + strconv.FormatBool(g.ShouldAddHashSuffixToName()),
|
|
||||||
"beh:" + g.Behavior().String()},
|
|
||||||
",") +
|
|
||||||
"}"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldAddHashSuffixToName returns true if a resource
|
|
||||||
// content hash should be appended to the name of the resource.
|
|
||||||
func (g *GenArgs) ShouldAddHashSuffixToName() bool {
|
|
||||||
return g.args != nil &&
|
|
||||||
(g.args.Options == nil || !g.args.Options.DisableNameSuffixHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Behavior returns Behavior field of GeneratorArgs
|
|
||||||
func (g *GenArgs) Behavior() GenerationBehavior {
|
|
||||||
if g == nil || g.args == nil {
|
|
||||||
return BehaviorUnspecified
|
|
||||||
}
|
|
||||||
return NewGenerationBehavior(g.args.Behavior)
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package types_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "sigs.k8s.io/kustomize/api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGenArgs_String(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
ga *GenArgs
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
ga: nil,
|
|
||||||
expected: "{nilGenArgs}",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ga: &GenArgs{},
|
|
||||||
expected: "{nsfx:false,beh:unspecified}",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ga: NewGenArgs(
|
|
||||||
&GeneratorArgs{
|
|
||||||
Behavior: "merge",
|
|
||||||
Options: &GeneratorOptions{DisableNameSuffixHash: false},
|
|
||||||
}),
|
|
||||||
expected: "{nsfx:true,beh:merge}",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
if test.ga.String() != test.expected {
|
|
||||||
t.Fatalf("Expected '%s', got '%s'", test.expected, test.ga.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -161,6 +161,9 @@ type Kustomization struct {
|
|||||||
// Inventory appends an object that contains the record
|
// Inventory appends an object that contains the record
|
||||||
// of all other objects, which can be used in apply, prune and delete
|
// of all other objects, which can be used in apply, prune and delete
|
||||||
Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"`
|
Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"`
|
||||||
|
|
||||||
|
// BuildMetadata is a list of strings used to toggle different build options
|
||||||
|
BuildMetadata []string `json:"buildMetadata,omitempty" yaml:"buildMetadata,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FixKustomizationPostUnmarshalling fixes things
|
// FixKustomizationPostUnmarshalling fixes things
|
||||||
|
|||||||
@@ -57,4 +57,6 @@ type FnPluginLoadingOptions struct {
|
|||||||
Env []string
|
Env []string
|
||||||
// Run as uid and gid of the command executor
|
// Run as uid and gid of the command executor
|
||||||
AsCurrentUser bool
|
AsCurrentUser bool
|
||||||
|
// Run in this working directory
|
||||||
|
WorkingDir string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,49 +327,51 @@ A function SHOULD preserve comments when input serialization format is YAML.
|
|||||||
This allows for human authoring of configuration to coexist with changes made by
|
This allows for human authoring of configuration to coexist with changes made by
|
||||||
functions.
|
functions.
|
||||||
|
|
||||||
### Annotations
|
### Internal Annotations
|
||||||
|
|
||||||
The orchestrator annotates resources in the wire format with annotation prefix
|
For orchestration purposes, the orchestrator will use a set of annotations,
|
||||||
`config.kubernetes.io`. These annotations are not persisted when the
|
referred to as _internal annotations_, on resources in `Resources.items`. These
|
||||||
orchestrator writes the resources to the filesystem. The orchestrator sets this
|
annotations are not persisted to resource manifests on the filesystem: The
|
||||||
annotation when reading files from the local filesystem and removes the
|
orchestrator sets this annotation when reading files from the local filesystem
|
||||||
annotation when writing the output of functions back to the filesystem.
|
and removes the annotation when writing the output of functions back to the
|
||||||
|
filesystem.
|
||||||
|
|
||||||
In general, a function MUST NOT modify these annotations except the ones
|
Annotation prefix `internal.config.kubernetes.io` is reserved for use for
|
||||||
explicitly listed below.
|
internal annotations. In general, a function MUST NOT modify these annotations with
|
||||||
|
the exception of the specific annotations listed below. This enables orchestrators to add additional internal annotations, without requiring changes to existing functions.
|
||||||
|
|
||||||
#### `config.kubernetes.io/path`
|
#### `internal.config.kubernetes.io/path`
|
||||||
|
|
||||||
Records the slash-delimited, OS-agnostic, relative file path to a resource. The
|
Records the slash-delimited, OS-agnostic, relative file path to a resource. The
|
||||||
path is relative to a fix location on the filesystem. Different orchestrator
|
path is relative to a fix location on the filesystem. Different orchestrator
|
||||||
implementations can choose different fixed points.
|
implementations can choose different fixed points.
|
||||||
|
|
||||||
A function SHOULD NOT modify this annotation.
|
A function SHOULD NOT modify these annotations.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/path: "relative/file/path.yaml"
|
internal.config.kubernetes.io/path: "relative/file/path.yaml"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `config.kubernetes.io/index`
|
#### `internal.config.kubernetes.io/index`
|
||||||
|
|
||||||
Records the index of a Resource in file. In a multi-object YAML file, resources
|
Records the index of a Resource in file. In a multi-object YAML file, resources
|
||||||
are separated by three dashes (`---`), and the index represents the position of
|
are separated by three dashes (`---`), and the index represents the position of
|
||||||
the Resource starting from zero. When this annotation is not specified, it
|
the Resource starting from zero. When this annotation is not specified, it
|
||||||
implies a value of `0`.
|
implies a value of `0`.
|
||||||
|
|
||||||
A function SHOULD NOT modify this annotation.
|
A function SHOULD NOT modify these annotations.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/path: "relative/file/path.yaml"
|
internal.config.kubernetes.io/path: "relative/file/path.yaml"
|
||||||
config.kubernetes.io/index: 2
|
internal.config.kubernetes.io/index: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
This represents the third resource in the file.
|
This represents the third resource in the file.
|
||||||
|
|||||||
11
cmd/config/docs/api-conventions/manifest-annotations.md
Normal file
11
cmd/config/docs/api-conventions/manifest-annotations.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Manifest Annotations
|
||||||
|
|
||||||
|
This document lists the annotations that can be declared in resource manifests.
|
||||||
|
|
||||||
|
### `config.kubernetes.io/local-config`
|
||||||
|
|
||||||
|
A value of `"true"` for this annotation declares that the resource is only consumed by
|
||||||
|
client-side tooling and should not be applied to the API server.
|
||||||
|
|
||||||
|
A value of `"false"` can be used to declare that a resource should be applied to
|
||||||
|
the API server even when it is assumed to be local.
|
||||||
@@ -16,5 +16,5 @@ require (
|
|||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1
|
gopkg.in/inf.v0 v0.9.1
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0
|
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -245,7 +245,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
|
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ import (
|
|||||||
func NewAnnotateRunner(parent string) *AnnotateRunner {
|
func NewAnnotateRunner(parent string) *AnnotateRunner {
|
||||||
r := &AnnotateRunner{}
|
r := &AnnotateRunner{}
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "annotate [DIR]",
|
Use: "annotate [DIR]",
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
Short: commands.AnnotateShort,
|
Short: commands.AnnotateShort,
|
||||||
Long: commands.AnnotateLong,
|
Long: commands.AnnotateLong,
|
||||||
Example: commands.AnnotateExamples,
|
Example: commands.AnnotateExamples,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Deprecated: "use the `commonAnnotations` field in your kustomization file.",
|
||||||
}
|
}
|
||||||
runner.FixDocs(parent, c)
|
runner.FixDocs(parent, c)
|
||||||
r.Command = c
|
r.Command = c
|
||||||
|
|||||||
@@ -559,7 +559,7 @@ added annotations in the package
|
|||||||
|
|
||||||
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
|||||||
Example: commands.CreateSetterExamples,
|
Example: commands.CreateSetterExamples,
|
||||||
PreRunE: r.preRunE,
|
PreRunE: r.preRunE,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
|
||||||
|
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
||||||
}
|
}
|
||||||
set.Flags().StringVar(&r.FieldValue, "value", "",
|
set.Flags().StringVar(&r.FieldValue, "value", "",
|
||||||
"optional flag, alternative to specifying the value as an argument. e.g. used to specify values that start with '-'")
|
"optional flag, alternative to specifying the value as an argument. e.g. used to specify values that start with '-'")
|
||||||
|
|||||||
@@ -869,7 +869,7 @@ setter with name "namespace" already exists, if you want to modify it, please de
|
|||||||
|
|
||||||
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
|
|||||||
Args: cobra.ExactArgs(2),
|
Args: cobra.ExactArgs(2),
|
||||||
PreRun: r.preRun,
|
PreRun: r.preRun,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Deprecated: "imperative substitutions will no longer be available in kustomize v5.\n" +
|
||||||
|
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
||||||
}
|
}
|
||||||
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
|
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
|
||||||
"name of the field to set -- e.g. --field image")
|
"name of the field to set -- e.g. --field image")
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ created substitution "image-tag"`,
|
|||||||
|
|
||||||
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
|
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ func GetInitRunner(name string) *InitRunner {
|
|||||||
Long: commands.InitLong,
|
Long: commands.InitLong,
|
||||||
Example: commands.InitExamples,
|
Example: commands.InitExamples,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Deprecated: "setter commands and substitutions will no longer be available in kustomize v5.\n" +
|
||||||
|
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
||||||
}
|
}
|
||||||
runner.FixDocs(name, c)
|
runner.FixDocs(name, c)
|
||||||
r.Command = c
|
r.Command = c
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ func NewListSettersRunner(parent string) *ListSettersRunner {
|
|||||||
Example: commands.ListSettersExamples,
|
Example: commands.ListSettersExamples,
|
||||||
PreRunE: r.preRunE,
|
PreRunE: r.preRunE,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
|
||||||
|
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
||||||
}
|
}
|
||||||
c.Flags().BoolVar(&r.Markdown, "markdown", false,
|
c.Flags().BoolVar(&r.Markdown, "markdown", false,
|
||||||
"output as github markdown")
|
"output as github markdown")
|
||||||
|
|||||||
@@ -525,7 +525,7 @@ test/testdata/dataset-with-setters/mysql/
|
|||||||
// normalize path format for windows
|
// normalize path format for windows
|
||||||
actualNormalized := strings.Replace(actual.String(), "\\", "/", -1)
|
actualNormalized := strings.Replace(actual.String(), "\\", "/", -1)
|
||||||
|
|
||||||
if !assert.Equal(t, strings.TrimSpace(test.expected), strings.TrimSpace(actualNormalized)) {
|
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(test.expected)) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ func NewSetRunner(parent string) *SetRunner {
|
|||||||
Example: commands.SetExamples,
|
Example: commands.SetExamples,
|
||||||
PreRunE: r.preRunE,
|
PreRunE: r.preRunE,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
|
||||||
|
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
||||||
}
|
}
|
||||||
runner.FixDocs(parent, c)
|
runner.FixDocs(parent, c)
|
||||||
r.Command = c
|
r.Command = c
|
||||||
|
|||||||
@@ -1137,7 +1137,7 @@ set 1 field(s) of setter "namespace" to value "otherspace"
|
|||||||
expectedNormalized := strings.Replace(
|
expectedNormalized := strings.Replace(
|
||||||
strings.Replace(expected, "\\", "/", -1),
|
strings.Replace(expected, "\\", "/", -1),
|
||||||
"//", "/", -1)
|
"//", "/", -1)
|
||||||
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
|
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -542,13 +542,13 @@ kind: Input
|
|||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
annotations:
|
annotations:
|
||||||
a-bool-value: true
|
|
||||||
a-int-value: 2
|
|
||||||
a-string-value: a
|
|
||||||
config.kubernetes.io/function: |
|
config.kubernetes.io/function: |
|
||||||
starlark:
|
starlark:
|
||||||
path: script.star
|
path: script.star
|
||||||
name: fn
|
name: fn
|
||||||
|
a-bool-value: true
|
||||||
|
a-int-value: 2
|
||||||
|
a-string-value: a
|
||||||
data:
|
data:
|
||||||
boolValue: true
|
boolValue: true
|
||||||
intValue: 2
|
intValue: 2
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ func GetFmtRunner(name string) *FmtRunner {
|
|||||||
Example: commands.FmtExamples,
|
Example: commands.FmtExamples,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
PreRunE: r.preRunE,
|
PreRunE: r.preRunE,
|
||||||
|
Deprecated: "imperative formatting will no longer be available in kustomize v5.\n" +
|
||||||
|
"Declare a formatting transformer in your kustomization instead.",
|
||||||
}
|
}
|
||||||
runner.FixDocs(name, c)
|
runner.FixDocs(name, c)
|
||||||
c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern,
|
c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern,
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func TestFmtCommand_stdin(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// verify the output
|
// verify the output
|
||||||
assert.Equal(t, string(testyaml.FormattedYaml1), out.String())
|
assert.Contains(t, out.String(), string(testyaml.FormattedYaml1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCmd_filesAndstdin verifies that if both files and stdin input are provided, only
|
// TestCmd_filesAndstdin verifies that if both files and stdin input are provided, only
|
||||||
@@ -238,7 +238,7 @@ formatted resource files in the package
|
|||||||
|
|
||||||
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
|
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ kind: Krmfile
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, "", b.String()) {
|
if !assert.Equal(t, `Command "init" is deprecated, setter commands and substitutions will no longer be available in kustomize v5.
|
||||||
|
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.
|
||||||
|
`, b.String()) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +80,9 @@ kind: Krmfile
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, "", b.String()) {
|
if !assert.Equal(t, `Command "init" is deprecated, setter commands and substitutions will no longer be available in kustomize v5.
|
||||||
|
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.
|
||||||
|
`, b.String()) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ func GetMergeRunner(name string) *MergeRunner {
|
|||||||
Long: commands.MergeLong,
|
Long: commands.MergeLong,
|
||||||
Example: commands.MergeExamples,
|
Example: commands.MergeExamples,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Deprecated: "this will no longer be available in kustomize v5.\n" +
|
||||||
|
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
||||||
}
|
}
|
||||||
runner.FixDocs(name, c)
|
runner.FixDocs(name, c)
|
||||||
r.Command = c
|
r.Command = c
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ func GetMerge3Runner(name string) *Merge3Runner {
|
|||||||
Long: commands.Merge3Long,
|
Long: commands.Merge3Long,
|
||||||
Example: commands.Merge3Examples,
|
Example: commands.Merge3Examples,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Deprecated: "this will no longer be available in kustomize v5.\n" +
|
||||||
|
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
||||||
}
|
}
|
||||||
runner.FixDocs(name, c)
|
runner.FixDocs(name, c)
|
||||||
c.Flags().StringVar(&r.ancestor, "ancestor", "",
|
c.Flags().StringVar(&r.ancestor, "ancestor", "",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -73,6 +74,7 @@ func GetRunFnRunner(name string) *RunFnRunner {
|
|||||||
"a list of environment variables to be used by functions")
|
"a list of environment variables to be used by functions")
|
||||||
r.Command.Flags().BoolVar(
|
r.Command.Flags().BoolVar(
|
||||||
&r.AsCurrentUser, "as-current-user", false, "use the uid and gid of the command executor to run the function in the container")
|
&r.AsCurrentUser, "as-current-user", false, "use the uid and gid of the command executor to run the function in the container")
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +304,11 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
// parse mounts to set storageMounts
|
// parse mounts to set storageMounts
|
||||||
storageMounts := toStorageMounts(r.Mounts)
|
storageMounts := toStorageMounts(r.Mounts)
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
r.RunFns = runfn.RunFns{
|
r.RunFns = runfn.RunFns{
|
||||||
FunctionPaths: r.FnPaths,
|
FunctionPaths: r.FnPaths,
|
||||||
GlobalScope: r.GlobalScope,
|
GlobalScope: r.GlobalScope,
|
||||||
@@ -317,6 +324,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
LogSteps: r.LogSteps,
|
LogSteps: r.LogSteps,
|
||||||
Env: r.Env,
|
Env: r.Env,
|
||||||
AsCurrentUser: r.AsCurrentUser,
|
AsCurrentUser: r.AsCurrentUser,
|
||||||
|
WorkingDir: wd,
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't consider args for the function
|
// don't consider args for the function
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestRunFnCommand_preRunE verifies that preRunE correctly parses the commandline
|
// TestRunFnCommand_preRunE verifies that preRunE correctly parses the commandline
|
||||||
// flags and arguments into the RunFns structure to be executed.
|
// flags and arguments into the RunFns structure to be executed.
|
||||||
func TestRunFnCommand_preRunE(t *testing.T) {
|
func TestRunFnCommand_preRunE(t *testing.T) {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
assert.NoError(t, err)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
@@ -201,6 +202,7 @@ apiVersion: v1
|
|||||||
Path: "dir",
|
Path: "dir",
|
||||||
EnableStarlark: true,
|
EnableStarlark: true,
|
||||||
Env: []string{},
|
Env: []string{},
|
||||||
|
WorkingDir: wd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -254,6 +256,7 @@ apiVersion: v1
|
|||||||
Path: "dir",
|
Path: "dir",
|
||||||
ResultsDir: "foo/",
|
ResultsDir: "foo/",
|
||||||
Env: []string{},
|
Env: []string{},
|
||||||
|
WorkingDir: wd,
|
||||||
},
|
},
|
||||||
expected: `
|
expected: `
|
||||||
metadata:
|
metadata:
|
||||||
@@ -286,9 +289,10 @@ apiVersion: v1
|
|||||||
args: []string{"run", "dir", "--log-steps"},
|
args: []string{"run", "dir", "--log-steps"},
|
||||||
path: "dir",
|
path: "dir",
|
||||||
expectedStruct: &runfn.RunFns{
|
expectedStruct: &runfn.RunFns{
|
||||||
Path: "dir",
|
Path: "dir",
|
||||||
LogSteps: true,
|
LogSteps: true,
|
||||||
Env: []string{},
|
Env: []string{},
|
||||||
|
WorkingDir: wd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -296,8 +300,9 @@ apiVersion: v1
|
|||||||
args: []string{"run", "dir", "--env", "FOO=BAR", "-e", "BAR"},
|
args: []string{"run", "dir", "--env", "FOO=BAR", "-e", "BAR"},
|
||||||
path: "dir",
|
path: "dir",
|
||||||
expectedStruct: &runfn.RunFns{
|
expectedStruct: &runfn.RunFns{
|
||||||
Path: "dir",
|
Path: "dir",
|
||||||
Env: []string{"FOO=BAR", "BAR"},
|
Env: []string{"FOO=BAR", "BAR"},
|
||||||
|
WorkingDir: wd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -308,6 +313,7 @@ apiVersion: v1
|
|||||||
Path: "dir",
|
Path: "dir",
|
||||||
AsCurrentUser: true,
|
AsCurrentUser: true,
|
||||||
Env: []string{},
|
Env: []string{},
|
||||||
|
WorkingDir: wd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ require (
|
|||||||
github.com/rakyll/statik v0.1.7
|
github.com/rakyll/statik v0.1.7
|
||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/cobra v1.0.0
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
sigs.k8s.io/kustomize/api v0.8.10
|
sigs.k8s.io/kustomize/api v0.8.11
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0
|
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||||
)
|
)
|
||||||
|
|
||||||
replace sigs.k8s.io/kustomize/api => ../../api
|
replace sigs.k8s.io/kustomize/api => ../../api
|
||||||
|
|||||||
@@ -228,8 +228,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
|
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ chart repository.
|
|||||||
This example defines the `helm` command as
|
This example defines the `helm` command as
|
||||||
<!-- @defineHelmCommand @testHelm -->
|
<!-- @defineHelmCommand @testHelm -->
|
||||||
```
|
```
|
||||||
helmCommand=~/go/bin/helmV3
|
helmCommand=${MYGOBIN:-~/go/bin}/helmV3
|
||||||
```
|
```
|
||||||
|
|
||||||
This value is needed for testing this example in CI/CD.
|
This value is needed for testing this example in CI/CD.
|
||||||
|
|||||||
@@ -19,6 +19,10 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Unset CDPATH to restore default cd behavior. An exported CDPATH can
|
||||||
|
# cause cd to output the current directory to STDOUT.
|
||||||
|
unset CDPATH
|
||||||
|
|
||||||
where=$PWD
|
where=$PWD
|
||||||
|
|
||||||
release_url=https://api.github.com/repos/kubernetes-sigs/kustomize/releases
|
release_url=https://api.github.com/repos/kubernetes-sigs/kustomize/releases
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ fi
|
|||||||
# We test against the latest release, and HEAD, and presumably
|
# We test against the latest release, and HEAD, and presumably
|
||||||
# any branch using this label, so it should probably get
|
# any branch using this label, so it should probably get
|
||||||
# a new value.
|
# a new value.
|
||||||
|
export MYGOBIN
|
||||||
mdrip --mode test --blockTimeOut 15m \
|
mdrip --mode test --blockTimeOut 15m \
|
||||||
--label testAgainstLatestRelease examples
|
--label testAgainstLatestRelease examples
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ func NewCmdSet(fSys filesys.FileSystem, ldr ifc.KvLoader, v ifc.Validator) *cobr
|
|||||||
newCmdSetImage(fSys),
|
newCmdSetImage(fSys),
|
||||||
newCmdSetReplicas(fSys),
|
newCmdSetReplicas(fSys),
|
||||||
newCmdSetLabel(fSys, ldr.Validator().MakeLabelValidator()),
|
newCmdSetLabel(fSys, ldr.Validator().MakeLabelValidator()),
|
||||||
|
newCmdSetAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()),
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
99
kustomize/commands/edit/set/setannotation.go
Normal file
99
kustomize/commands/edit/set/setannotation.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
|
||||||
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/util"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
type setAnnotationOptions struct {
|
||||||
|
metadata map[string]string
|
||||||
|
mapValidator func(map[string]string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidKey checks key against regex. First part for prefix segment (DNS1123Label) of an annotation followed by a slash, second part for name segment of an annotation
|
||||||
|
// see https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
|
||||||
|
var IsValidKey = regexp.MustCompile(`^([a-zA-Z](([-a-zA-Z0-9.]{0,251})[a-zA-Z0-9])?\/)?[a-zA-Z0-9]([-a-zA-Z0-9_.]{0,61}[a-zA-Z0-9])?$`).MatchString
|
||||||
|
|
||||||
|
// newCmdSetAnnotation sets one or more commonAnnotations to the kustomization file.
|
||||||
|
func newCmdSetAnnotation(fSys filesys.FileSystem, v func(map[string]string) error) *cobra.Command {
|
||||||
|
var o setAnnotationOptions
|
||||||
|
o.mapValidator = v
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "annotation",
|
||||||
|
Short: "Sets one or more commonAnnotations in " +
|
||||||
|
konfig.DefaultKustomizationFileName(),
|
||||||
|
Example: `
|
||||||
|
set annotation {annotationKey1:annotationValue1} {annotationKey2:annotationValue2}`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return o.runE(args, fSys, o.setAnnotations)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *setAnnotationOptions) runE(
|
||||||
|
args []string, fSys filesys.FileSystem, setter func(*types.Kustomization) error) error {
|
||||||
|
err := o.validateAndParse(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
kf, err := kustfile.NewKustomizationFile(fSys)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m, err := kf.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = setter(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return kf.Write(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAndParse validates `set` commands and parses them into o.metadata
|
||||||
|
func (o *setAnnotationOptions) validateAndParse(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("must specify annotation")
|
||||||
|
}
|
||||||
|
m, err := util.ConvertSliceToMap(args, "annotation")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = o.mapValidator(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for key := range m {
|
||||||
|
if !IsValidKey(key) {
|
||||||
|
return errors.New("invalid annotation key: see the syntax and character set rules at https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o.metadata = m
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *setAnnotationOptions) setAnnotations(m *types.Kustomization) error {
|
||||||
|
if m.CommonAnnotations == nil {
|
||||||
|
m.CommonAnnotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
return o.writeToMap(m.CommonAnnotations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *setAnnotationOptions) writeToMap(m map[string]string) error {
|
||||||
|
for k, v := range o.metadata {
|
||||||
|
m[k] = v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
229
kustomize/commands/edit/set/setannotation_test.go
Normal file
229
kustomize/commands/edit/set/setannotation_test.go
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
|
||||||
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
const invalidAnnotationKey string = "invalid annotation key: see the syntax and character set rules at " +
|
||||||
|
"https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/"
|
||||||
|
|
||||||
|
func makeAnnotationKustomization(t *testing.T) *types.Kustomization {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
kf, err := kustfile.NewKustomizationFile(fSys)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected new error %v", err)
|
||||||
|
}
|
||||||
|
m, err := kf.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected read error %v", err)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunSetAnnotation(t *testing.T) {
|
||||||
|
var o setAnnotationOptions
|
||||||
|
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
|
||||||
|
|
||||||
|
m := makeAnnotationKustomization(t)
|
||||||
|
err := o.setAnnotations(m)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: could not write to kustomization file")
|
||||||
|
}
|
||||||
|
// adding the same test input should work
|
||||||
|
err = o.setAnnotations(m)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: could not write to kustomization file")
|
||||||
|
}
|
||||||
|
// adding new annotations should work
|
||||||
|
o.metadata = map[string]string{"new": "annotation", "owls": "not cute"}
|
||||||
|
err = o.setAnnotations(m)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: could not write to kustomization file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotationNoArgs(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
v := valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
err := cmd.Execute()
|
||||||
|
v.VerifyNoCall()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected an error")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if err.Error() != "must specify annotation" {
|
||||||
|
t.Errorf("incorrect error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotationInvalidFormat(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
v := valtest_test.MakeSadMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
args := []string{"exclamation!:point"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
v.VerifyCall()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected an error")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if err.Error() != valtest_test.SAD {
|
||||||
|
t.Errorf("incorrect error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotationPrefixColonName(t *testing.T) {
|
||||||
|
var o setAnnotationOptions
|
||||||
|
o.metadata = map[string]string{"internal.config.kubernetes.io/options": "true"}
|
||||||
|
|
||||||
|
m := makeAnnotationKustomization(t)
|
||||||
|
err := o.setAnnotations(m)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotation253Prefix63Name(t *testing.T) {
|
||||||
|
var o setAnnotationOptions
|
||||||
|
o.metadata = map[string]string{"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu" +
|
||||||
|
"vwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
|
||||||
|
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde" +
|
||||||
|
"fghijklmnopqrstuvwxyzabcdefghijklmnopqrs/abcdefghijklmnopqrstuvwxyzabcdefghijklmnop" +
|
||||||
|
"qrstuvwxyzabcdefghijk": "true"}
|
||||||
|
|
||||||
|
m := makeAnnotationKustomization(t)
|
||||||
|
err := o.setAnnotations(m)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotation254Prefix62Name(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
v := valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
args := []string{"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi" +
|
||||||
|
"jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn" +
|
||||||
|
"opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrs" +
|
||||||
|
"tuvwxyzabcdefghijklmnopqrst/abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" +
|
||||||
|
"defghij:true"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
v.VerifyCall()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected an error")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if err.Error() != invalidAnnotationKey {
|
||||||
|
t.Errorf("incorrect error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotation252Prefix64Name(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
v := valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
args := []string{"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi" +
|
||||||
|
"jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn" +
|
||||||
|
"opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrs" +
|
||||||
|
"tuvwxyzabcdefghijklmnopqr/abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde" +
|
||||||
|
"fghijkl:true"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
v.VerifyCall()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected an error")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if err.Error() != invalidAnnotationKey {
|
||||||
|
t.Errorf("incorrect error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotationNoKey(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
v := valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
args := []string{":nokey"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
v.VerifyNoCall()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected an error")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if err.Error() != "invalid annotation: ':nokey' (need k:v pair where v may be quoted)" {
|
||||||
|
t.Errorf("incorrect error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotationTooManyColons(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
v := valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
args := []string{"key:v1:v2"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
v.VerifyCall()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotationNoValue(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
v := valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
args := []string{"no,value:"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
v.VerifyCall()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected an error")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if err.Error() != invalidAnnotationKey {
|
||||||
|
t.Errorf("incorrect error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotationMultipleArgs(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
v := valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
args := []string{"this:input", "has:spaces"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
v.VerifyCall()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetAnnotationExisting(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
v := valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd := newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
args := []string{"key:foo"}
|
||||||
|
err := cmd.RunE(cmd, args)
|
||||||
|
v.VerifyCall()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err.Error())
|
||||||
|
}
|
||||||
|
v = valtest_test.MakeHappyMapValidator(t)
|
||||||
|
cmd = newCmdSetAnnotation(fSys, v.Validator)
|
||||||
|
err = cmd.RunE(cmd, args)
|
||||||
|
v.VerifyCall()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,26 +8,38 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var format string
|
||||||
|
|
||||||
// NewCmdFetch makes a new fetch command.
|
// NewCmdFetch makes a new fetch command.
|
||||||
func NewCmdFetch(w io.Writer) *cobra.Command {
|
func NewCmdFetch(w io.Writer) *cobra.Command {
|
||||||
infoCmd := cobra.Command{
|
fetchCmd := cobra.Command{
|
||||||
Use: "fetch",
|
Use: "fetch",
|
||||||
Short: `Fetches the OpenAPI specification from the current kubernetes cluster specified
|
Short: `Fetches the OpenAPI specification from the current kubernetes cluster specified
|
||||||
in the user's kubeconfig`,
|
in the user's kubeconfig`,
|
||||||
Example: `kustomize openapi fetch`,
|
Example: `kustomize openapi fetch`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
printSchema(w)
|
return printSchema(w)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return &infoCmd
|
fetchCmd.Flags().StringVar(
|
||||||
|
&format,
|
||||||
|
"format",
|
||||||
|
"json",
|
||||||
|
"Specify format for fetched schema ('json' or 'yaml')")
|
||||||
|
return &fetchCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func printSchema(w io.Writer) {
|
func printSchema(w io.Writer) error {
|
||||||
|
if format != "json" && format != "yaml" {
|
||||||
|
return fmt.Errorf("format must be either 'json' or 'yaml'")
|
||||||
|
}
|
||||||
|
|
||||||
errMsg := `
|
errMsg := `
|
||||||
Error fetching schema from cluster.
|
Error fetching schema from cluster.
|
||||||
Please make sure kubectl is installed and its context is set correctly.
|
Please make sure kubectl is installed, its context is set correctly, and your cluster is up.
|
||||||
Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/install-kubectl/`
|
Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/install-kubectl/`
|
||||||
|
|
||||||
command := exec.Command("kubectl", []string{"get", "--raw", "/openapi/v2"}...)
|
command := exec.Command("kubectl", []string{"get", "--raw", "/openapi/v2"}...)
|
||||||
@@ -36,9 +48,10 @@ Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/inst
|
|||||||
command.Stdout = &stdout
|
command.Stdout = &stdout
|
||||||
command.Stderr = &stderr
|
command.Stderr = &stderr
|
||||||
err := command.Run()
|
err := command.Run()
|
||||||
if err != nil || stdout.String() == "" {
|
if err != nil {
|
||||||
fmt.Fprintln(w, err, stderr.String()+errMsg)
|
return fmt.Errorf("%w\n%s", err, stderr.String()+errMsg)
|
||||||
return
|
} else if stdout.String() == "" {
|
||||||
|
return fmt.Errorf(stderr.String() + errMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// format and output
|
// format and output
|
||||||
@@ -46,5 +59,14 @@ Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/inst
|
|||||||
output := stdout.Bytes()
|
output := stdout.Bytes()
|
||||||
json.Unmarshal(output, &jsonSchema)
|
json.Unmarshal(output, &jsonSchema)
|
||||||
output, _ = json.MarshalIndent(jsonSchema, "", " ")
|
output, _ = json.MarshalIndent(jsonSchema, "", " ")
|
||||||
|
|
||||||
|
if format == "yaml" {
|
||||||
|
output, err = yaml.JSONToYAML(output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Fprintln(w, string(output))
|
fmt.Fprintln(w, string(output))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ require (
|
|||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/cobra v1.0.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
sigs.k8s.io/kustomize/api v0.8.10
|
sigs.k8s.io/kustomize/api v0.8.11
|
||||||
sigs.k8s.io/kustomize/cmd/config v0.9.12
|
sigs.k8s.io/kustomize/cmd/config v0.10.0
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0
|
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,5 +20,3 @@ exclude (
|
|||||||
)
|
)
|
||||||
|
|
||||||
replace sigs.k8s.io/kustomize/api => ../api
|
replace sigs.k8s.io/kustomize/api => ../api
|
||||||
|
|
||||||
replace sigs.k8s.io/kustomize/cmd/config => ../cmd/config
|
|
||||||
|
|||||||
@@ -253,8 +253,10 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
|
sigs.k8s.io/kustomize/cmd/config v0.10.0 h1:6XPkaOu9eFdvrcqHpfmYAKoBvE01JdvsTVvtDuAFM+8=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
sigs.k8s.io/kustomize/cmd/config v0.10.0/go.mod h1:XS4NFKucjk0/+nPKJwSMoZYCjey2PB0ipNoZabXUFPU=
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
|||||||
@@ -123,11 +123,11 @@ func (n *fsNode) addFile(name string, c []byte) (result *fsNode, err error) {
|
|||||||
if result.offset != nil {
|
if result.offset != nil {
|
||||||
return nil, fmt.Errorf("cannot add already opened file '%s'", n.Path())
|
return nil, fmt.Errorf("cannot add already opened file '%s'", n.Path())
|
||||||
}
|
}
|
||||||
result.content = c
|
result.content = append(result.content[:0], c...)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
result = &fsNode{
|
result = &fsNode{
|
||||||
content: c,
|
content: append([]byte(nil), c...),
|
||||||
parent: parent,
|
parent: parent,
|
||||||
}
|
}
|
||||||
parent.dir[fileName] = result
|
parent.dir[fileName] = result
|
||||||
|
|||||||
@@ -157,9 +157,13 @@ func runBasicOperations(
|
|||||||
if string(stuff) != both {
|
if string(stuff) != both {
|
||||||
t.Fatalf("%s; unexpected content '%s', expected '%s'", c.what, stuff, both)
|
t.Fatalf("%s; unexpected content '%s', expected '%s'", c.what, stuff, both)
|
||||||
}
|
}
|
||||||
if err := fSys.WriteFile(c.path, []byte(shortContent)); err != nil {
|
|
||||||
|
content := []byte(shortContent)
|
||||||
|
if err := fSys.WriteFile(c.path, content); err != nil {
|
||||||
t.Fatalf("%s; unexpected error: %v", c.what, err)
|
t.Fatalf("%s; unexpected error: %v", c.what, err)
|
||||||
}
|
}
|
||||||
|
// This ensures that modifying the original slice does not change the contents of the file.
|
||||||
|
content[0] = '@'
|
||||||
stuff, err = fSys.ReadFile(c.path)
|
stuff, err = fSys.ReadFile(c.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s; unexpected error: %v", c.what, err)
|
t.Fatalf("%s; unexpected error: %v", c.what, err)
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ type CommandResultsChecker struct {
|
|||||||
|
|
||||||
// Command provides the function to run.
|
// Command provides the function to run.
|
||||||
Command func() *cobra.Command
|
Command func() *cobra.Command
|
||||||
|
|
||||||
|
*checkerCore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert runs the command with the input provided in each valid test directory
|
// Assert runs the command with the input provided in each valid test directory
|
||||||
@@ -85,39 +87,51 @@ func (rc *CommandResultsChecker) Assert(t *testing.T) bool {
|
|||||||
if rc.InputFilenameGlob == "" {
|
if rc.InputFilenameGlob == "" {
|
||||||
rc.InputFilenameGlob = DefaultInputFilenameGlob
|
rc.InputFilenameGlob = DefaultInputFilenameGlob
|
||||||
}
|
}
|
||||||
|
rc.checkerCore = &checkerCore{
|
||||||
checker := newResultsChecker(
|
testDataDirectory: rc.TestDataDirectory,
|
||||||
rc.TestDataDirectory, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename,
|
expectedOutputFilename: rc.ExpectedOutputFilename,
|
||||||
rc.OutputAssertionFunc, rc.ErrorAssertionFunc,
|
expectedErrorFilename: rc.ExpectedErrorFilename,
|
||||||
rc.UpdateExpectedFromActual,
|
updateExpectedFromActual: rc.UpdateExpectedFromActual,
|
||||||
)
|
outputAssertionFunc: rc.OutputAssertionFunc,
|
||||||
checker.assert(t, func() (string, string) {
|
errorAssertionFunc: rc.ErrorAssertionFunc,
|
||||||
_, err := os.Stat(rc.ConfigInputFilename)
|
}
|
||||||
if os.IsNotExist(err) {
|
rc.checkerCore.setDefaults()
|
||||||
t.Errorf("Test case is missing FunctionConfig input file (default: %s)", DefaultConfigInputFilename)
|
runAllTestCases(t, rc)
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
args := []string{rc.ConfigInputFilename}
|
|
||||||
|
|
||||||
if rc.InputFilenameGlob != "" {
|
|
||||||
inputs, err := filepath.Glob(rc.InputFilenameGlob)
|
|
||||||
require.NoError(t, err)
|
|
||||||
args = append(args, inputs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var stdOut, stdErr bytes.Buffer
|
|
||||||
cmd := rc.Command()
|
|
||||||
cmd.SetArgs(args)
|
|
||||||
cmd.SetOut(&stdOut)
|
|
||||||
cmd.SetErr(&stdErr)
|
|
||||||
|
|
||||||
err = cmd.Execute()
|
|
||||||
return stdOut.String(), stdErr.String()
|
|
||||||
})
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rc *CommandResultsChecker) isTestDir(path string) bool {
|
||||||
|
return atLeastOneFileExists(
|
||||||
|
filepath.Join(path, rc.ConfigInputFilename),
|
||||||
|
filepath.Join(path, rc.checkerCore.expectedOutputFilename),
|
||||||
|
filepath.Join(path, rc.checkerCore.expectedErrorFilename),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *CommandResultsChecker) runInCurrentDir(t *testing.T) (string, string) {
|
||||||
|
_, err := os.Stat(rc.ConfigInputFilename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Errorf("Test case is missing FunctionConfig input file (default: %s)", DefaultConfigInputFilename)
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
args := []string{rc.ConfigInputFilename}
|
||||||
|
|
||||||
|
if rc.InputFilenameGlob != "" {
|
||||||
|
inputs, err := filepath.Glob(rc.InputFilenameGlob)
|
||||||
|
require.NoError(t, err)
|
||||||
|
args = append(args, inputs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdOut, stdErr bytes.Buffer
|
||||||
|
cmd := rc.Command()
|
||||||
|
cmd.SetArgs(args)
|
||||||
|
cmd.SetOut(&stdOut)
|
||||||
|
cmd.SetErr(&stdErr)
|
||||||
|
|
||||||
|
_ = cmd.Execute()
|
||||||
|
return stdOut.String(), stdErr.String()
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessorResultsChecker tests a processor function by running it with predefined inputs
|
// ProcessorResultsChecker tests a processor function by running it with predefined inputs
|
||||||
// and comparing the outputs to expected results.
|
// and comparing the outputs to expected results.
|
||||||
type ProcessorResultsChecker struct {
|
type ProcessorResultsChecker struct {
|
||||||
@@ -159,6 +173,8 @@ type ProcessorResultsChecker struct {
|
|||||||
|
|
||||||
// Processor returns a ResourceListProcessor to run.
|
// Processor returns a ResourceListProcessor to run.
|
||||||
Processor func() framework.ResourceListProcessor
|
Processor func() framework.ResourceListProcessor
|
||||||
|
|
||||||
|
*checkerCore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert runs the processor with the input provided in each valid test directory
|
// Assert runs the processor with the input provided in each valid test directory
|
||||||
@@ -167,37 +183,50 @@ func (rc *ProcessorResultsChecker) Assert(t *testing.T) bool {
|
|||||||
if rc.InputFilename == "" {
|
if rc.InputFilename == "" {
|
||||||
rc.InputFilename = DefaultInputFilename
|
rc.InputFilename = DefaultInputFilename
|
||||||
}
|
}
|
||||||
|
rc.checkerCore = &checkerCore{
|
||||||
checker := newResultsChecker(
|
testDataDirectory: rc.TestDataDirectory,
|
||||||
rc.TestDataDirectory, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename,
|
expectedOutputFilename: rc.ExpectedOutputFilename,
|
||||||
rc.OutputAssertionFunc, rc.ErrorAssertionFunc,
|
expectedErrorFilename: rc.ExpectedErrorFilename,
|
||||||
rc.UpdateExpectedFromActual,
|
updateExpectedFromActual: rc.UpdateExpectedFromActual,
|
||||||
)
|
outputAssertionFunc: rc.OutputAssertionFunc,
|
||||||
checker.assert(t, func() (string, string) {
|
errorAssertionFunc: rc.ErrorAssertionFunc,
|
||||||
_, err := os.Stat(rc.InputFilename)
|
}
|
||||||
if os.IsNotExist(err) {
|
rc.checkerCore.setDefaults()
|
||||||
t.Error("Test case is missing input file")
|
runAllTestCases(t, rc)
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
actualOutput := bytes.NewBuffer([]byte{})
|
|
||||||
rlBytes, err := ioutil.ReadFile(rc.InputFilename)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
rw := kio.ByteReadWriter{
|
|
||||||
Reader: bytes.NewBuffer(rlBytes),
|
|
||||||
Writer: actualOutput,
|
|
||||||
}
|
|
||||||
err = framework.Execute(rc.Processor(), &rw)
|
|
||||||
if err != nil {
|
|
||||||
require.NotEmptyf(t, err.Error(), "processor returned error with empty message")
|
|
||||||
return actualOutput.String(), err.Error()
|
|
||||||
}
|
|
||||||
return actualOutput.String(), ""
|
|
||||||
})
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rc *ProcessorResultsChecker) isTestDir(path string) bool {
|
||||||
|
return atLeastOneFileExists(
|
||||||
|
filepath.Join(path, rc.InputFilename),
|
||||||
|
filepath.Join(path, rc.checkerCore.expectedOutputFilename),
|
||||||
|
filepath.Join(path, rc.checkerCore.expectedErrorFilename),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *ProcessorResultsChecker) runInCurrentDir(t *testing.T) (string, string) {
|
||||||
|
_, err := os.Stat(rc.InputFilename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Errorf("Test case is missing input file (default: %s)", DefaultInputFilename)
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
actualOutput := bytes.NewBuffer([]byte{})
|
||||||
|
rlBytes, err := ioutil.ReadFile(rc.InputFilename)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rw := kio.ByteReadWriter{
|
||||||
|
Reader: bytes.NewBuffer(rlBytes),
|
||||||
|
Writer: actualOutput,
|
||||||
|
}
|
||||||
|
err = framework.Execute(rc.Processor(), &rw)
|
||||||
|
if err != nil {
|
||||||
|
require.NotEmptyf(t, err.Error(), "processor returned error with empty message")
|
||||||
|
return actualOutput.String(), err.Error()
|
||||||
|
}
|
||||||
|
return actualOutput.String(), ""
|
||||||
|
}
|
||||||
|
|
||||||
type AssertionFunc func(t *testing.T, expected string, actual string)
|
type AssertionFunc func(t *testing.T, expected string, actual string)
|
||||||
|
|
||||||
// RequireEachLineMatches is an AssertionFunc that treats each line of expected string
|
// RequireEachLineMatches is an AssertionFunc that treats each line of expected string
|
||||||
@@ -223,8 +252,35 @@ func standardizeSpacing(s string) string {
|
|||||||
return strings.ReplaceAll(strings.TrimSpace(s), "\r\n", "\n")
|
return strings.ReplaceAll(strings.TrimSpace(s), "\r\n", "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// resultsChecker implements the core logic shared by all results checking types.
|
// resultsChecker is implemented by ProcessorResultsChecker and CommandResultsChecker, partially via checkerCore
|
||||||
type resultsChecker struct {
|
type resultsChecker interface {
|
||||||
|
// TestCasesRun returns a list of the test case directories that have been seen.
|
||||||
|
TestCasesRun() (paths []string)
|
||||||
|
|
||||||
|
// rootDir is the root of the tree of test directories to be searched for fixtures.
|
||||||
|
rootDir() string
|
||||||
|
// isTestDir takes the name of directory and returns whether or not it contains the files required to be a test case.
|
||||||
|
isTestDir(dir string) bool
|
||||||
|
// runInCurrentDir executes the code the checker is checking in the context of the current working directory.
|
||||||
|
runInCurrentDir(t *testing.T) (stdOut, stdErr string)
|
||||||
|
// resetTestCasesRun resets the list of test case directories seen.
|
||||||
|
resetTestCasesRun()
|
||||||
|
// recordTestCase adds to the list of test case directories seen.
|
||||||
|
recordTestCase(path string)
|
||||||
|
// readAssertionFiles returns the contents of the output and error fixtures
|
||||||
|
readAssertionFiles(t *testing.T) (expectedOutput string, expectedError string)
|
||||||
|
// shouldUpdateFixtures indicates whether or not this checker is currently being used to update fixtures instead of run them.
|
||||||
|
shouldUpdateFixtures() bool
|
||||||
|
// updateFixtures modifies the test fixture files to match the given content
|
||||||
|
updateFixtures(t *testing.T, actualOutput string, actualError string)
|
||||||
|
// assertOutputMatches compares the expected output to the output recieved.
|
||||||
|
assertOutputMatches(t *testing.T, expected string, actual string)
|
||||||
|
// assertErrorMatches compares teh expected error to the error received.
|
||||||
|
assertErrorMatches(t *testing.T, expected string, actual string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkerCore implements the resultsChecker methods that are shared between the two checker types
|
||||||
|
type checkerCore struct {
|
||||||
testDataDirectory string
|
testDataDirectory string
|
||||||
expectedOutputFilename string
|
expectedOutputFilename string
|
||||||
expectedErrorFilename string
|
expectedErrorFilename string
|
||||||
@@ -232,20 +288,10 @@ type resultsChecker struct {
|
|||||||
outputAssertionFunc AssertionFunc
|
outputAssertionFunc AssertionFunc
|
||||||
errorAssertionFunc AssertionFunc
|
errorAssertionFunc AssertionFunc
|
||||||
|
|
||||||
testsCasesRun int
|
testsCasesRun []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResultsChecker(testDataDir string, outputFilename string, errorFilename string,
|
func (rc *checkerCore) setDefaults() {
|
||||||
outputAsserter AssertionFunc, errorAsserter AssertionFunc,
|
|
||||||
updateFixtures bool) *resultsChecker {
|
|
||||||
rc := resultsChecker{
|
|
||||||
testDataDirectory: testDataDir,
|
|
||||||
expectedOutputFilename: outputFilename,
|
|
||||||
expectedErrorFilename: errorFilename,
|
|
||||||
updateExpectedFromActual: updateFixtures,
|
|
||||||
outputAssertionFunc: outputAsserter,
|
|
||||||
errorAssertionFunc: errorAsserter,
|
|
||||||
}
|
|
||||||
if rc.testDataDirectory == "" {
|
if rc.testDataDirectory == "" {
|
||||||
rc.testDataDirectory = DefaultTestDataDirectory
|
rc.testDataDirectory = DefaultTestDataDirectory
|
||||||
}
|
}
|
||||||
@@ -261,73 +307,47 @@ func newResultsChecker(testDataDir string, outputFilename string, errorFilename
|
|||||||
if rc.errorAssertionFunc == nil {
|
if rc.errorAssertionFunc == nil {
|
||||||
rc.errorAssertionFunc = RequireEachLineMatches
|
rc.errorAssertionFunc = RequireEachLineMatches
|
||||||
}
|
}
|
||||||
return &rc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// assert traverses TestDataDirectory to find test cases, calls getResult to invoke the function
|
func (rc *checkerCore) rootDir() string {
|
||||||
// under test in each directory, and then runs assertions on the returned output and error results.
|
return rc.testDataDirectory
|
||||||
// It triggers a test failure if no valid test directories were found.
|
|
||||||
func (rc *resultsChecker) assert(t *testing.T, getResult func() (string, string)) {
|
|
||||||
err := filepath.Walk(rc.testDataDirectory,
|
|
||||||
func(path string, info os.FileInfo, err error) error {
|
|
||||||
require.NoError(t, err)
|
|
||||||
if !info.IsDir() {
|
|
||||||
// skip non-directories
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rc.runDirectoryTestCase(t, path, getResult)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotZero(t, rc.testsCasesRun, "No complete test cases found in %s", rc.testDataDirectory)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *resultsChecker) runDirectoryTestCase(t *testing.T, path string, getResult func() (string, string)) {
|
func (rc *checkerCore) TestCasesRun() []string {
|
||||||
// cd into the directory so we can test functions that refer
|
return rc.testsCasesRun
|
||||||
// local files by relative paths
|
}
|
||||||
d, err := os.Getwd()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
defer func() { require.NoError(t, os.Chdir(d)) }()
|
func (rc *checkerCore) resetTestCasesRun() {
|
||||||
require.NoError(t, os.Chdir(path))
|
rc.testsCasesRun = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
expectedOutput, expectedError := rc.readAssertionFiles(t)
|
func (rc *checkerCore) recordTestCase(s string) {
|
||||||
if expectedError == "" && expectedOutput == "" && !rc.updateExpectedFromActual {
|
rc.testsCasesRun = append(rc.testsCasesRun, s)
|
||||||
// not a test directory: missing expectations and updateExpectedFromActual == false
|
}
|
||||||
return
|
|
||||||
|
func (rc *checkerCore) shouldUpdateFixtures() bool {
|
||||||
|
return rc.updateExpectedFromActual
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *checkerCore) updateFixtures(t *testing.T, actualOutput string, actualError string) {
|
||||||
|
if actualError != "" {
|
||||||
|
require.NoError(t, ioutil.WriteFile(rc.expectedErrorFilename, []byte(actualError), 0600))
|
||||||
}
|
}
|
||||||
|
if len(actualOutput) > 0 {
|
||||||
// run the test
|
require.NoError(t, ioutil.WriteFile(rc.expectedOutputFilename, []byte(actualOutput), 0600))
|
||||||
t.Run(path, func(t *testing.T) {
|
}
|
||||||
rc.testsCasesRun += 1
|
t.Skip("Updated fixtures for test case")
|
||||||
actualOutput, actualError := getResult()
|
|
||||||
|
|
||||||
// Configured to update the assertion files instead of comparing them
|
|
||||||
if rc.updateExpectedFromActual {
|
|
||||||
if actualError != "" {
|
|
||||||
require.NoError(t, ioutil.WriteFile(rc.expectedErrorFilename, []byte(actualError), 0600))
|
|
||||||
}
|
|
||||||
if len(actualOutput) > 0 {
|
|
||||||
require.NoError(t, ioutil.WriteFile(rc.expectedOutputFilename, []byte(actualOutput), 0600))
|
|
||||||
}
|
|
||||||
t.Skip("Updated fixtures for test case")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the results to the assertion files
|
|
||||||
if expectedError != "" {
|
|
||||||
// We expected an error, so make sure there was one
|
|
||||||
require.NotEmptyf(t, actualError, "test expected an error but message was empty, and output was:\n%s", actualOutput)
|
|
||||||
rc.errorAssertionFunc(t, expectedError, actualError)
|
|
||||||
} else {
|
|
||||||
// We didn't expect an error, and the output should match
|
|
||||||
require.Emptyf(t, actualError, "test expected no error but got an error message, and output was:\n%s", actualOutput)
|
|
||||||
rc.outputAssertionFunc(t, expectedOutput, actualOutput)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// readAssertionFiles reads the expected results and error files
|
func (rc *checkerCore) assertOutputMatches(t *testing.T, expected string, actual string) {
|
||||||
func (rc *resultsChecker) readAssertionFiles(t *testing.T) (string, string) {
|
rc.outputAssertionFunc(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *checkerCore) assertErrorMatches(t *testing.T, expected string, actual string) {
|
||||||
|
rc.errorAssertionFunc(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *checkerCore) readAssertionFiles(t *testing.T) (string, string) {
|
||||||
// read the expected results
|
// read the expected results
|
||||||
var expectedOutput, expectedError string
|
var expectedOutput, expectedError string
|
||||||
if rc.expectedOutputFilename != "" {
|
if rc.expectedOutputFilename != "" {
|
||||||
@@ -360,3 +380,65 @@ func (rc *resultsChecker) readAssertionFiles(t *testing.T) (string, string) {
|
|||||||
}
|
}
|
||||||
return expectedOutput, expectedError
|
return expectedOutput, expectedError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runAllTestCases traverses rootDir to find test cases, calls getResult to invoke the function
|
||||||
|
// under test in each directory, and then runs assertions on the returned output and error results.
|
||||||
|
// It triggers a test failure if no valid test directories were found.
|
||||||
|
func runAllTestCases(t *testing.T, checker resultsChecker) {
|
||||||
|
checker.resetTestCasesRun()
|
||||||
|
err := filepath.Walk(checker.rootDir(),
|
||||||
|
func(path string, info os.FileInfo, err error) error {
|
||||||
|
require.NoError(t, err)
|
||||||
|
if info.IsDir() && checker.isTestDir(path) {
|
||||||
|
runDirectoryTestCase(t, path, checker)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotZero(t, len(checker.TestCasesRun()), "No complete test cases found in %s", checker.rootDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDirectoryTestCase(t *testing.T, path string, checker resultsChecker) {
|
||||||
|
// cd into the directory so we can test functions that refer to local files by relative paths
|
||||||
|
d, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func() { require.NoError(t, os.Chdir(d)) }()
|
||||||
|
require.NoError(t, os.Chdir(path))
|
||||||
|
|
||||||
|
expectedOutput, expectedError := checker.readAssertionFiles(t)
|
||||||
|
if expectedError == "" && expectedOutput == "" && !checker.shouldUpdateFixtures() {
|
||||||
|
t.Fatalf("test directory %s must include either expected output or expected error fixture", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the test
|
||||||
|
t.Run(path, func(t *testing.T) {
|
||||||
|
checker.recordTestCase(path)
|
||||||
|
actualOutput, actualError := checker.runInCurrentDir(t)
|
||||||
|
|
||||||
|
// Configured to update the assertion files instead of comparing them
|
||||||
|
if checker.shouldUpdateFixtures() {
|
||||||
|
checker.updateFixtures(t, actualOutput, actualError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the results to the assertion files
|
||||||
|
if expectedError != "" {
|
||||||
|
// We expected an error, so make sure there was one
|
||||||
|
require.NotEmptyf(t, actualError, "test expected an error but message was empty, and output was:\n%s", actualOutput)
|
||||||
|
checker.assertErrorMatches(t, expectedError, actualError)
|
||||||
|
} else {
|
||||||
|
// We didn't expect an error, and the output should match
|
||||||
|
require.Emptyf(t, actualError, "test expected no error but got an error message, and output was:\n%s", actualOutput)
|
||||||
|
checker.assertOutputMatches(t, expectedOutput, actualOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func atLeastOneFileExists(files ...string) bool {
|
||||||
|
for _, file := range files {
|
||||||
|
if _, err := os.Stat(file); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2021 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package frameworktestutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/framework/command"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProcessorResultsChecker_UpdateExpectedFromActual(t *testing.T) {
|
||||||
|
dir := filepath.FromSlash("testdata/update_expectations/processor")
|
||||||
|
checker := ProcessorResultsChecker{
|
||||||
|
TestDataDirectory: dir,
|
||||||
|
UpdateExpectedFromActual: true,
|
||||||
|
Processor: testProcessor,
|
||||||
|
}
|
||||||
|
// This should result in the test being skipped. If no tests are found, it will instead fail.
|
||||||
|
checker.Assert(t)
|
||||||
|
require.Contains(t, checker.TestCasesRun(), filepath.Join(dir, "important_subdir"))
|
||||||
|
|
||||||
|
checker.UpdateExpectedFromActual = false
|
||||||
|
// This time should inherently pass
|
||||||
|
checker.Assert(t)
|
||||||
|
require.Contains(t, checker.TestCasesRun(), filepath.Join(dir, "important_subdir"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommandResultsChecker_UpdateExpectedFromActual(t *testing.T) {
|
||||||
|
dir := filepath.FromSlash("testdata/update_expectations/command")
|
||||||
|
checker := CommandResultsChecker{
|
||||||
|
TestDataDirectory: dir,
|
||||||
|
UpdateExpectedFromActual: true,
|
||||||
|
Command: testCommand,
|
||||||
|
}
|
||||||
|
// This should result in the test being skipped. If no tests are found, it will instead fail.
|
||||||
|
checker.Assert(t)
|
||||||
|
require.Contains(t, checker.TestCasesRun(), filepath.Join(dir, "important_subdir"))
|
||||||
|
|
||||||
|
checker.UpdateExpectedFromActual = false
|
||||||
|
// This time should inherently pass
|
||||||
|
checker.Assert(t)
|
||||||
|
require.Contains(t, checker.TestCasesRun(), filepath.Join(dir, "important_subdir"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCommand() *cobra.Command {
|
||||||
|
return command.Build(testProcessor(), command.StandaloneEnabled, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProcessor() framework.ResourceListProcessor {
|
||||||
|
return framework.SimpleProcessor{
|
||||||
|
Filter: kio.FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
for _, node := range nodes {
|
||||||
|
err := node.SetAnnotations(map[string]string{"updated": "true"})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Demo
|
||||||
|
spec:
|
||||||
|
value: a
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test-1
|
||||||
|
annotations:
|
||||||
|
updated: "true"
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test-2
|
||||||
|
annotations:
|
||||||
|
updated: "true"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test-1
|
||||||
|
annotations:
|
||||||
|
baz: foo
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test-2
|
||||||
|
annotations:
|
||||||
|
foo: bar
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
apiVersion:
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test-1
|
||||||
|
annotations:
|
||||||
|
updated: "true"
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test-2
|
||||||
|
annotations:
|
||||||
|
updated: "true"
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Demo
|
||||||
|
spec:
|
||||||
|
value: a
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test-1
|
||||||
|
annotations:
|
||||||
|
baz: foo
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test-2
|
||||||
|
annotations:
|
||||||
|
foo: bar
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Demo
|
||||||
|
spec:
|
||||||
|
value: a
|
||||||
@@ -168,8 +168,7 @@ func (cpt ContainerPatchTemplate) apply(matches []*yaml.RNode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := range matches {
|
for i := range matches {
|
||||||
// TODO(knverey): Make this work for more Kinds and expose the helper for doing so.
|
containers, err := matches[i].Pipe(yaml.LookupFirstMatch(yaml.ConventionalContainerPaths))
|
||||||
containers, err := matches[i].Pipe(yaml.Lookup("spec", "template", "spec", "containers"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package framework_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -276,6 +277,57 @@ functionConfig:
|
|||||||
`), strings.TrimSpace(out.String()))
|
`), strings.TrimSpace(out.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTemplateProcessor_ContainerPatchTemplates_MultipleWorkloadKinds(t *testing.T) {
|
||||||
|
type API struct {
|
||||||
|
Spec struct {
|
||||||
|
Key string `json:"key" yaml:"key"`
|
||||||
|
Value string `json:"value" yaml:"value"`
|
||||||
|
A string `json:"a" yaml:"a"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config := &API{}
|
||||||
|
p := framework.TemplateProcessor{
|
||||||
|
TemplateData: config,
|
||||||
|
ResourceTemplates: []framework.ResourceTemplate{{
|
||||||
|
Templates: parser.TemplateFiles("testdata/template-processor/templates/container-sources"),
|
||||||
|
}},
|
||||||
|
PatchTemplates: []framework.PatchTemplate{
|
||||||
|
&framework.ContainerPatchTemplate{
|
||||||
|
Templates: parser.TemplateFiles("testdata/template-processor/container-patches"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
rw := &kio.ByteReadWriter{Writer: out, Reader: bytes.NewBufferString(`
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items: []
|
||||||
|
functionConfig:
|
||||||
|
spec:
|
||||||
|
key: Hello
|
||||||
|
value: World
|
||||||
|
a: bar
|
||||||
|
`)}
|
||||||
|
|
||||||
|
require.NoError(t, framework.Execute(p, rw))
|
||||||
|
resources, err := (&kio.ByteReader{Reader: out}).Read()
|
||||||
|
require.NoError(t, err)
|
||||||
|
envRegex := regexp.MustCompile(strings.TrimSpace(`
|
||||||
|
\s+ env:
|
||||||
|
\s+ - name: EXISTING
|
||||||
|
\s+ value: variable
|
||||||
|
\s+ - name: Hello
|
||||||
|
\s+ value: World
|
||||||
|
`))
|
||||||
|
require.Equal(t, 9, len(resources))
|
||||||
|
for i, r := range resources {
|
||||||
|
t.Run(r.GetKind(), func(t *testing.T) {
|
||||||
|
assert.Regexp(t, envRegex, resources[i].MustString())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSimpleProcessor_Process_loads_config(t *testing.T) {
|
func TestSimpleProcessor_Process_loads_config(t *testing.T) {
|
||||||
cfg := new(struct {
|
cfg := new(struct {
|
||||||
Value string `yaml:"value"`
|
Value string `yaml:"value"`
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
schedule: "*/1 * * * *"
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
name: hello
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: hello
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: hello
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
16
kyaml/fn/framework/testdata/template-processor/templates/container-sources/job.template.yaml
vendored
Normal file
16
kyaml/fn/framework/testdata/template-processor/templates/container-sources/job.template.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
14
kyaml/fn/framework/testdata/template-processor/templates/container-sources/pod.template.yaml
vendored
Normal file
14
kyaml/fn/framework/testdata/template-processor/templates/container-sources/pod.template.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PodTemplate
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
labels:
|
||||||
|
tier: hello
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: ReplicaSet
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: hello
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: hello
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ReplicationController
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: hello
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
labels:
|
||||||
|
app: hello
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: hello
|
||||||
|
spec:
|
||||||
|
serviceName: "nginx"
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: hello
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: hello
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hello
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: EXISTING
|
||||||
|
value: variable
|
||||||
|
|
||||||
@@ -5,6 +5,7 @@ package container
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
@@ -139,19 +140,27 @@ func (c Filter) GetExit() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
c.setupExec()
|
if err := c.setupExec(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return c.Exec.Filter(nodes)
|
return c.Exec.Filter(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Filter) setupExec() {
|
func (c *Filter) setupExec() error {
|
||||||
// don't init 2x
|
// don't init 2x
|
||||||
if c.Exec.Path != "" {
|
if c.Exec.Path != "" {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Exec.WorkingDir = wd
|
||||||
|
|
||||||
path, args := c.getCommand()
|
path, args := c.getCommand()
|
||||||
c.Exec.Path = path
|
c.Exec.Path = path
|
||||||
c.Exec.Args = args
|
c.Exec.Args = args
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getArgs returns the command + args to run to spawn the container
|
// getArgs returns the command + args to run to spawn the container
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ package container
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -128,7 +130,7 @@ metadata:
|
|||||||
instance := NewContainer(tt.containerSpec, tt.UIDGID)
|
instance := NewContainer(tt.containerSpec, tt.UIDGID)
|
||||||
instance.Exec.FunctionConfig = cfg
|
instance.Exec.FunctionConfig = cfg
|
||||||
instance.Env = append(instance.Env, "KYAML_TEST=FOO")
|
instance.Env = append(instance.Env, "KYAML_TEST=FOO")
|
||||||
instance.setupExec()
|
assert.NoError(t, instance.setupExec())
|
||||||
|
|
||||||
tt.expectedArgs = append(tt.expectedArgs,
|
tt.expectedArgs = append(tt.expectedArgs,
|
||||||
runtimeutil.NewContainerEnvFromStringSlice(instance.Env).GetDockerFlags()...)
|
runtimeutil.NewContainerEnvFromStringSlice(instance.Env).GetDockerFlags()...)
|
||||||
@@ -173,6 +175,8 @@ metadata:
|
|||||||
instance.Exec.FunctionConfig = cfg
|
instance.Exec.FunctionConfig = cfg
|
||||||
instance.Exec.Path = "sed"
|
instance.Exec.Path = "sed"
|
||||||
instance.Exec.Args = []string{"s/Deployment/StatefulSet/g"}
|
instance.Exec.Args = []string{"s/Deployment/StatefulSet/g"}
|
||||||
|
instance.Exec.WorkingDir = getWorkingDir(t)
|
||||||
|
|
||||||
output, err := instance.Filter(input)
|
output, err := instance.Filter(input)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
@@ -219,6 +223,7 @@ func TestFilter_ExitCode(t *testing.T) {
|
|||||||
instance := Filter{}
|
instance := Filter{}
|
||||||
instance.Exec.Path = "/not/real/command"
|
instance.Exec.Path = "/not/real/command"
|
||||||
instance.Exec.DeferFailure = true
|
instance.Exec.DeferFailure = true
|
||||||
|
instance.Exec.WorkingDir = getWorkingDir(t)
|
||||||
_, err := instance.Filter(nil)
|
_, err := instance.Filter(nil)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
@@ -231,3 +236,9 @@ func TestFilter_ExitCode(t *testing.T) {
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getWorkingDir(t *testing.T) string {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
return wd
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
@@ -19,6 +21,10 @@ type Filter struct {
|
|||||||
// Args are the arguments to the executable
|
// Args are the arguments to the executable
|
||||||
Args []string `yaml:"args,omitempty"`
|
Args []string `yaml:"args,omitempty"`
|
||||||
|
|
||||||
|
// WorkingDir is the working directory that the executable
|
||||||
|
// should run in
|
||||||
|
WorkingDir string
|
||||||
|
|
||||||
runtimeutil.FunctionFilter
|
runtimeutil.FunctionFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,5 +38,17 @@ func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
|
|||||||
cmd.Stdin = reader
|
cmd.Stdin = reader
|
||||||
cmd.Stdout = writer
|
cmd.Stdout = writer
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
if c.WorkingDir == "" {
|
||||||
|
return errors.Errorf("no working directory set for exec function")
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(c.WorkingDir) {
|
||||||
|
return errors.Errorf(
|
||||||
|
"relative working directory %s not allowed", c.WorkingDir)
|
||||||
|
}
|
||||||
|
if c.WorkingDir == "/" {
|
||||||
|
return errors.Errorf(
|
||||||
|
"root working directory '/' not allowed")
|
||||||
|
}
|
||||||
|
cmd.Dir = c.WorkingDir
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,19 @@
|
|||||||
package exec_test
|
package exec_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFunctionFilter_Filter(t *testing.T) {
|
func TestFunctionFilter_Filter(t *testing.T) {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
name string
|
name string
|
||||||
input []string
|
input []string
|
||||||
@@ -51,8 +55,9 @@ metadata:
|
|||||||
},
|
},
|
||||||
expectedError: "",
|
expectedError: "",
|
||||||
instance: exec.Filter{
|
instance: exec.Filter{
|
||||||
Path: "sed",
|
Path: "sed",
|
||||||
Args: []string{"s/Deployment/StatefulSet/g"},
|
Args: []string{"s/Deployment/StatefulSet/g"},
|
||||||
|
WorkingDir: wd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/order"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
@@ -169,8 +170,8 @@ func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the comments from the inputs to the outputs
|
// copy the comments and sync the order of fields from the inputs to the outputs
|
||||||
if err := c.setComments(output); err != nil {
|
if err := c.copyCommentsAndSyncOrder(output); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +211,7 @@ func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FunctionFilter) setComments(nodes []*yaml.RNode) error {
|
func (c *FunctionFilter) copyCommentsAndSyncOrder(nodes []*yaml.RNode) error {
|
||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
node := nodes[i]
|
node := nodes[i]
|
||||||
anID, err := node.Pipe(yaml.GetAnnotation(idAnnotation))
|
anID, err := node.Pipe(yaml.GetAnnotation(idAnnotation))
|
||||||
@@ -229,6 +230,9 @@ func (c *FunctionFilter) setComments(nodes []*yaml.RNode) error {
|
|||||||
if err := comments.CopyComments(in, node); err != nil {
|
if err := comments.CopyComments(in, node); err != nil {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
|
if err := order.SyncOrder(in, node); err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
if err := node.PipeE(yaml.ClearAnnotation(idAnnotation)); err != nil {
|
if err := node.PipeE(yaml.ClearAnnotation(idAnnotation)); err != nil {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user