mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 18:40:55 +00:00
Compare commits
106 Commits
api/v0.8.1
...
release-ap
| 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 | ||
|
|
02d14d724a | ||
|
|
78737f5a38 |
20
Makefile
20
Makefile
@@ -4,6 +4,8 @@
|
||||
# Makefile for kustomize CLI and API.
|
||||
|
||||
SHELL := /usr/bin/env bash
|
||||
GOOS = $(shell go env GOOS)
|
||||
GOARCH = $(shell go env GOARCH)
|
||||
MYGOBIN = $(shell go env GOBIN)
|
||||
ifeq ($(MYGOBIN),)
|
||||
MYGOBIN = $(shell go env GOPATH)/bin
|
||||
@@ -289,8 +291,8 @@ $(MYGOBIN)/kubeval:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz; \
|
||||
tar xf kubeval-linux-amd64.tar.gz; \
|
||||
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-$(GOOS)-$(GOARCH).tar.gz; \
|
||||
tar xf kubeval-$(GOOS)-$(GOARCH).tar.gz; \
|
||||
mv kubeval $(MYGOBIN); \
|
||||
rm -rf $$d; \
|
||||
)
|
||||
@@ -304,10 +306,10 @@ $(MYGOBIN)/helmV2:
|
||||
( \
|
||||
set -e; \
|
||||
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; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv linux-amd64/helm $(MYGOBIN)/helmV2; \
|
||||
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV2; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
|
||||
@@ -317,10 +319,10 @@ $(MYGOBIN)/helmV3:
|
||||
( \
|
||||
set -e; \
|
||||
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; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv linux-amd64/helm $(MYGOBIN)/helmV3; \
|
||||
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
|
||||
@@ -328,7 +330,7 @@ $(MYGOBIN)/kind:
|
||||
( \
|
||||
set -e; \
|
||||
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; \
|
||||
mv ./kind $(MYGOBIN); \
|
||||
rm -rf $$d; \
|
||||
@@ -339,10 +341,10 @@ $(MYGOBIN)/gh:
|
||||
( \
|
||||
set -e; \
|
||||
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; \
|
||||
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 \
|
||||
)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ will be reflected in the Kubernetes release notes.
|
||||
| < v1.14 | n/a |
|
||||
| v1.14-v1.20 | v2.0.3 |
|
||||
| v1.21 | v4.0.5 |
|
||||
| v1.22 | v4.2.0 |
|
||||
|
||||
[v2.0.3]: /../../tree/v2.0.3
|
||||
[#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
|
||||
[`sed`]: https://www.gnu.org/software/sed
|
||||
[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
|
||||
[applied]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#apply
|
||||
[base]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#base
|
||||
|
||||
@@ -1514,9 +1514,9 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pre-deploy
|
||||
annotations:
|
||||
config.kubernetes.io/previousNames: deploy,deploy
|
||||
config.kubernetes.io/previousKinds: CronJob,Deployment
|
||||
config.kubernetes.io/previousNamespaces: default,default
|
||||
internal.config.kubernetes.io/previousNames: deploy,deploy
|
||||
internal.config.kubernetes.io/previousKinds: CronJob,Deployment
|
||||
internal.config.kubernetes.io/previousNamespaces: default,default
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
@@ -1543,9 +1543,9 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pre-deploy
|
||||
annotations:
|
||||
config.kubernetes.io/previousNames: deploy,deploy
|
||||
config.kubernetes.io/previousKinds: CronJob,Deployment
|
||||
config.kubernetes.io/previousNamespaces: default,default
|
||||
internal.config.kubernetes.io/previousNames: deploy,deploy
|
||||
internal.config.kubernetes.io/previousKinds: CronJob,Deployment
|
||||
internal.config.kubernetes.io/previousNamespaces: default,default
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
|
||||
@@ -11,6 +11,6 @@ require (
|
||||
github.com/stretchr/testify v1.5.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
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
|
||||
)
|
||||
|
||||
@@ -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/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
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.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
||||
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/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
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)
|
||||
}
|
||||
|
||||
expected.RemoveBuildAnnotations()
|
||||
m.RemoveBuildAnnotations()
|
||||
|
||||
if err = expected.ErrorIfNotEqualLists(m); err != nil {
|
||||
t.Fatalf(notEqualErrFmt, err)
|
||||
}
|
||||
|
||||
@@ -362,10 +362,10 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "sub-backendOne",
|
||||
"annotations": map[string]interface{}{
|
||||
"config.kubernetes.io/previousKinds": "Service",
|
||||
"config.kubernetes.io/previousNames": "backendOne",
|
||||
"config.kubernetes.io/previousNamespaces": "default",
|
||||
"config.kubernetes.io/prefixes": "sub-",
|
||||
"internal.config.kubernetes.io/previousKinds": "Service",
|
||||
"internal.config.kubernetes.io/previousNames": "backendOne",
|
||||
"internal.config.kubernetes.io/previousNamespaces": "default",
|
||||
"internal.config.kubernetes.io/prefixes": "sub-",
|
||||
},
|
||||
}}).ResMap()
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ func parseGitUrl(n string) (
|
||||
index := strings.Index(n, gitSuffix)
|
||||
orgRepo = n[0:index]
|
||||
n = n[index+len(gitSuffix):]
|
||||
if n[0] == '/' {
|
||||
if len(n) > 0 && n[0] == '/' {
|
||||
n = n[1:]
|
||||
}
|
||||
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
|
||||
|
||||
@@ -182,6 +182,12 @@ func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) {
|
||||
absPath: notCloned.String(),
|
||||
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 {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
|
||||
@@ -79,6 +79,7 @@ func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin {
|
||||
StorageMounts: toStorageMounts(o.Mounts),
|
||||
Env: o.Env,
|
||||
AsCurrentUser: o.AsCurrentUser,
|
||||
WorkingDir: o.WorkingDir,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,11 @@ func (l *Loader) Config() *types.PluginConfig {
|
||||
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(
|
||||
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
|
||||
var result []resmap.Generator
|
||||
|
||||
@@ -231,10 +231,10 @@ func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {
|
||||
if err := r.SetAnnotations(annotations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.SetOptions(types.NewGenArgs(
|
||||
&types.GeneratorArgs{
|
||||
Behavior: behavior,
|
||||
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
|
||||
if needsHash {
|
||||
r.EnableHashSuffix()
|
||||
}
|
||||
r.SetBehavior(types.NewGenerationBehavior(behavior))
|
||||
}
|
||||
return rm, nil
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
||||
"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/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/yaml"
|
||||
@@ -38,11 +40,13 @@ func NewKustTarget(
|
||||
validator ifc.Validator,
|
||||
rFactory *resmap.Factory,
|
||||
pLdr *loader.Loader) *KustTarget {
|
||||
pLdrCopy := *pLdr
|
||||
pLdrCopy.SetWorkDir(ldr.Root())
|
||||
return &KustTarget{
|
||||
ldr: ldr,
|
||||
validator: validator,
|
||||
rFactory: rFactory,
|
||||
pLdr: pLdr,
|
||||
pLdr: &pLdrCopy,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +112,7 @@ 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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -151,20 +155,29 @@ func (kt *KustTarget) addHashesToNames(
|
||||
// holding customized resources and the data/rules used
|
||||
// to do so. The name back references and vars are
|
||||
// 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) {
|
||||
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
|
||||
// (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) {
|
||||
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources)
|
||||
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources, origin)
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, errors.Wrap(err, "accumulating components")
|
||||
}
|
||||
@@ -247,7 +260,7 @@ func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error)
|
||||
}
|
||||
ra.AppendAll(rm)
|
||||
}
|
||||
ra, err := kt.accumulateResources(ra, generatorPaths)
|
||||
ra, err := kt.accumulateResources(ra, generatorPaths, &resource.Origin{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -283,8 +296,7 @@ func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]re
|
||||
}
|
||||
ra.AppendAll(rm)
|
||||
}
|
||||
ra, err := kt.accumulateResources(ra, transformerPaths)
|
||||
|
||||
ra, err := kt.accumulateResources(ra, transformerPaths, &resource.Origin{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -332,16 +344,16 @@ func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) error {
|
||||
// accumulateResources fills the given resourceAccumulator
|
||||
// with resources read from the given list of paths.
|
||||
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 {
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
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 {
|
||||
return nil, errors.Wrapf(
|
||||
err, "accumulation err='%s'", errF.Error())
|
||||
@@ -354,7 +366,7 @@ func (kt *KustTarget) accumulateResources(
|
||||
// accumulateResources fills the given resourceAccumulator
|
||||
// with resources read from the given list of paths.
|
||||
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 {
|
||||
// Components always refer to directories
|
||||
ldr, errL := kt.ldr.New(path)
|
||||
@@ -362,7 +374,8 @@ func (kt *KustTarget) accumulateComponents(
|
||||
return nil, fmt.Errorf("loader.New %q", errL)
|
||||
}
|
||||
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 {
|
||||
return nil, fmt.Errorf("accumulateDirectory: %q", errD)
|
||||
}
|
||||
@@ -371,7 +384,7 @@ func (kt *KustTarget) accumulateComponents(
|
||||
}
|
||||
|
||||
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()
|
||||
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
|
||||
err := subKt.Load()
|
||||
@@ -379,6 +392,7 @@ func (kt *KustTarget) accumulateDirectory(
|
||||
return nil, errors.Wrapf(
|
||||
err, "couldn't make target for path '%s'", ldr.Root())
|
||||
}
|
||||
subKt.kustomization.BuildMetadata = kt.kustomization.BuildMetadata
|
||||
var bytes []byte
|
||||
path := ldr.Root()
|
||||
if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists {
|
||||
@@ -402,12 +416,12 @@ func (kt *KustTarget) accumulateDirectory(
|
||||
var subRa *accumulator.ResAccumulator
|
||||
if isComponent {
|
||||
// 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()
|
||||
} else {
|
||||
// Child Kustomizations create a new accumulator which resolves their kustomization directives, which will later
|
||||
// be merged into the current accumulator.
|
||||
subRa, err = subKt.AccumulateTarget()
|
||||
subRa, err = subKt.AccumulateTarget(origin)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
@@ -422,11 +436,18 @@ func (kt *KustTarget) accumulateDirectory(
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "merging resources from '%s'", path)
|
||||
|
||||
@@ -255,5 +255,5 @@ metadata:
|
||||
actual.RemoveBuildAnnotations()
|
||||
actYaml, err := actual.AsYaml()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expYaml, actYaml)
|
||||
assert.Equal(t, string(expYaml), string(actYaml))
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
@@ -65,7 +66,7 @@ vars:
|
||||
apiVersion: v300
|
||||
`)
|
||||
ra, err := makeAndLoadKustTarget(
|
||||
t, th.GetFSys(), "/app").AccumulateTarget()
|
||||
t, th.GetFSys(), "/app").AccumulateTarget(&resource.Origin{})
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
}
|
||||
@@ -120,7 +121,7 @@ resources:
|
||||
`)
|
||||
|
||||
ra, err := makeAndLoadKustTarget(
|
||||
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget()
|
||||
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget(&resource.Origin{})
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
}
|
||||
@@ -177,7 +178,7 @@ resources:
|
||||
- ../o1
|
||||
`)
|
||||
_, err := makeAndLoadKustTarget(
|
||||
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget()
|
||||
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget(&resource.Origin{})
|
||||
if err == nil {
|
||||
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"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
"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
|
||||
func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) {
|
||||
var result []resid.ResId
|
||||
|
||||
@@ -31,11 +31,12 @@ const (
|
||||
// A program name, for use in help, finding the XDG_CONFIG_DIR, etc.
|
||||
ProgramName = "kustomize"
|
||||
|
||||
// ConfigAnnoDomain is configuration-related annotation namespace.
|
||||
ConfigAnnoDomain = "config.kubernetes.io"
|
||||
// ConfigAnnoDomain is internal configuration-related annotation namespace.
|
||||
// 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.
|
||||
IgnoredByKustomizeAnnotation = ConfigAnnoDomain + "/local-config"
|
||||
IgnoredByKustomizeAnnotation = "config.kubernetes.io/local-config"
|
||||
|
||||
// Label key that indicates the resources are built from Kustomize
|
||||
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
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
func TestFnExecGenerator(t *testing.T) {
|
||||
// Function plugins should not need the env setup done by MakeEnhancedHarness
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
const generateDeploymentDotSh = `#!/bin/sh
|
||||
|
||||
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:
|
||||
- short_secret.yaml
|
||||
generators:
|
||||
@@ -19,7 +54,8 @@ generators:
|
||||
`)
|
||||
|
||||
// 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
|
||||
kind: Secret
|
||||
metadata:
|
||||
@@ -32,22 +68,25 @@ stringData:
|
||||
bootcmd:
|
||||
- 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
|
||||
metadata:
|
||||
name: demo
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
exec:
|
||||
path: ./fnplugin_test/fnexectest.sh
|
||||
path: ./generateDeployment.sh
|
||||
spec:
|
||||
`)
|
||||
o := th.MakeOptionsPluginsEnabled()
|
||||
o.PluginConfig.FnpLoadingOptions.EnableExec = true
|
||||
m := th.Run(".", o)
|
||||
th.AssertActualEqualsExpected(m, `
|
||||
apiVersion: v1
|
||||
|
||||
m := th.Run(tmpDir.String(), o)
|
||||
assert.NoError(t, err)
|
||||
yml, err := m.AsYaml()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
@@ -63,7 +102,6 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: deployment_nginx.yaml
|
||||
tshirt-size: small
|
||||
labels:
|
||||
app: nginx
|
||||
@@ -80,7 +118,99 @@ spec:
|
||||
containers:
|
||||
- image: 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) {
|
||||
@@ -146,8 +276,6 @@ type: Opaque
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: config/demo-budget_poddisruptionbudget.yaml
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: demo
|
||||
@@ -162,8 +290,6 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: config/demo-public_service.yaml
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: demo
|
||||
@@ -184,7 +310,6 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: config/demo_service.yaml
|
||||
prometheus.io/path: _status/vars
|
||||
prometheus.io/port: "8080"
|
||||
prometheus.io/scrape: "true"
|
||||
@@ -209,8 +334,6 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: config/demo_statefulset.yaml
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: demo
|
||||
@@ -383,7 +506,6 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: deployment_nginx.yaml
|
||||
tshirt-size: small
|
||||
labels:
|
||||
app: nginx
|
||||
@@ -450,8 +572,6 @@ data:
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: namespace_my-namespace.yaml
|
||||
labels:
|
||||
my-ns-name: function-test
|
||||
name: my-namespace
|
||||
@@ -459,8 +579,6 @@ metadata:
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: namespace_another-namespace.yaml
|
||||
labels:
|
||||
my-ns-name: function-test
|
||||
name: another-namespace
|
||||
@@ -508,8 +626,6 @@ data:
|
||||
value: value
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: configmap_env.yaml
|
||||
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
|
||||
`)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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) {
|
||||
th.WriteF(filepath, `
|
||||
apiVersion: example.com/v1alpha1
|
||||
@@ -103,6 +108,21 @@ openapi:
|
||||
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
|
||||
// and custom schema
|
||||
func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
|
||||
@@ -111,7 +131,7 @@ func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
|
||||
resources:
|
||||
- mycrd.yaml
|
||||
openapi:
|
||||
version: v1.20.4
|
||||
version: v1.21.2
|
||||
path: mycrd_schema.json
|
||||
`+customSchemaPatch)
|
||||
writeCustomResource(th, "mycrd.yaml")
|
||||
@@ -197,7 +217,7 @@ openapi:
|
||||
resources:
|
||||
- ../base
|
||||
openapi:
|
||||
version: v1.20.4
|
||||
version: v1.21.2
|
||||
`+customSchemaPatch)
|
||||
writeCustomResource(th, "base/mycrd.yaml")
|
||||
writeTestSchema(th, "base/")
|
||||
@@ -215,7 +235,7 @@ spec:
|
||||
- image: nginx
|
||||
name: server
|
||||
`)
|
||||
assert.Equal(t, "v1204", openapi.GetSchemaVersion())
|
||||
assert.Equal(t, "v1212", openapi.GetSchemaVersion())
|
||||
}
|
||||
|
||||
func TestCustomOpenAPIFieldFromComponent(t *testing.T) {
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestOpenApiFieldBasicUsage(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK(".", `
|
||||
openapi:
|
||||
version: v1.20.4
|
||||
version: v1.21.2
|
||||
resources:
|
||||
- deployment.yaml
|
||||
`)
|
||||
@@ -44,7 +44,8 @@ spec:
|
||||
containers:
|
||||
- image: whatever
|
||||
`)
|
||||
assert.Equal(t, "v1204", openapi.GetSchemaVersion())
|
||||
assert.Equal(t, "v1212", openapi.GetSchemaVersion())
|
||||
openapi.ResetOpenAPI()
|
||||
}
|
||||
|
||||
func TestOpenApiFieldNotBuiltin(t *testing.T) {
|
||||
@@ -71,6 +72,7 @@ spec:
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
openapi.ResetOpenAPI()
|
||||
}
|
||||
|
||||
func TestOpenApiFieldDefaultVersion(t *testing.T) {
|
||||
@@ -104,4 +106,5 @@ spec:
|
||||
- image: whatever
|
||||
`)
|
||||
assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion())
|
||||
openapi.ResetOpenAPI()
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package krusty_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -39,3 +40,124 @@ spec:
|
||||
name: nginx
|
||||
`, 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
|
||||
`)
|
||||
}
|
||||
|
||||
// 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)
|
||||
assert.NoError(t, err, tc.description)
|
||||
r.RemoveBuildAnnotations()
|
||||
rYaml, err := r.AsYaml()
|
||||
assert.NoError(t, err, tc.description)
|
||||
tc.expected.RemoveBuildAnnotations()
|
||||
expYaml, err := tc.expected.AsYaml()
|
||||
assert.NoError(t, err, tc.description)
|
||||
assert.Equal(t, expYaml, rYaml)
|
||||
@@ -252,6 +254,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
actual.RemoveBuildAnnotations()
|
||||
actYaml, err := actual.AsYaml()
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
||||
@@ -136,6 +136,10 @@ type ResMap interface {
|
||||
// self, then its behavior _cannot_ be merge or replace.
|
||||
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() ([]byte, error)
|
||||
|
||||
@@ -210,6 +214,35 @@ type ResMap interface {
|
||||
// namespaces. Cluster wide objects are never excluded.
|
||||
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() ResMap
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filters/annotations"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"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
|
||||
// are selected by a Selector
|
||||
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
|
||||
@@ -593,6 +607,16 @@ func (m *resWrangler) ToRNodeSlice() []*kyaml.RNode {
|
||||
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.
|
||||
func (m *resWrangler) ApplySmPatch(
|
||||
selectedSet *resource.IdSet, patch *resource.Resource) error {
|
||||
|
||||
@@ -343,9 +343,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "new-alice",
|
||||
"annotations": map[string]interface{}{
|
||||
"config.kubernetes.io/previousKinds": "ConfigMap",
|
||||
"config.kubernetes.io/previousNames": "alice",
|
||||
"config.kubernetes.io/previousNamespaces": "default",
|
||||
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
|
||||
"internal.config.kubernetes.io/previousNames": "alice",
|
||||
"internal.config.kubernetes.io/previousNamespaces": "default",
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -356,9 +356,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "new-bob",
|
||||
"annotations": map[string]interface{}{
|
||||
"config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap",
|
||||
"config.kubernetes.io/previousNames": "bob,bob2",
|
||||
"config.kubernetes.io/previousNamespaces": "default,default",
|
||||
"internal.config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap",
|
||||
"internal.config.kubernetes.io/previousNames": "bob,bob2",
|
||||
"internal.config.kubernetes.io/previousNamespaces": "default,default",
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -370,9 +370,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
|
||||
"name": "new-bob",
|
||||
"namespace": "new-happy",
|
||||
"annotations": map[string]interface{}{
|
||||
"config.kubernetes.io/previousKinds": "ConfigMap",
|
||||
"config.kubernetes.io/previousNames": "bob",
|
||||
"config.kubernetes.io/previousNamespaces": "happy",
|
||||
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
|
||||
"internal.config.kubernetes.io/previousNames": "bob",
|
||||
"internal.config.kubernetes.io/previousNamespaces": "happy",
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -384,9 +384,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
|
||||
"name": "charlie",
|
||||
"namespace": "happy",
|
||||
"annotations": map[string]interface{}{
|
||||
"config.kubernetes.io/previousKinds": "ConfigMap",
|
||||
"config.kubernetes.io/previousNames": "charlie",
|
||||
"config.kubernetes.io/previousNamespaces": "default",
|
||||
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
|
||||
"internal.config.kubernetes.io/previousNames": "charlie",
|
||||
"internal.config.kubernetes.io/previousNamespaces": "default",
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -845,6 +845,8 @@ func TestAbsorbAll(t *testing.T) {
|
||||
}))
|
||||
w := makeMap1()
|
||||
assert.NoError(t, w.AbsorbAll(makeMap2(types.BehaviorMerge)))
|
||||
expected.RemoveBuildAnnotations()
|
||||
w.RemoveBuildAnnotations()
|
||||
assert.NoError(t, expected.ErrorIfNotEqualLists(w))
|
||||
w = makeMap1()
|
||||
assert.NoError(t, w.AbsorbAll(nil))
|
||||
@@ -853,6 +855,7 @@ func TestAbsorbAll(t *testing.T) {
|
||||
w = makeMap1()
|
||||
w2 := makeMap2(types.BehaviorReplace)
|
||||
assert.NoError(t, w.AbsorbAll(w2))
|
||||
w2.RemoveBuildAnnotations()
|
||||
assert.NoError(t, w2.ErrorIfNotEqualLists(w))
|
||||
w = makeMap1()
|
||||
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) {
|
||||
const (
|
||||
myDeployment = "Deployment"
|
||||
|
||||
@@ -64,18 +64,23 @@ func (rf *Factory) FromMapAndOption(
|
||||
// TODO: return err instead of log.
|
||||
log.Fatal(err)
|
||||
}
|
||||
return rf.makeOne(n, types.NewGenArgs(args))
|
||||
return rf.makeOne(n, args)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Fatal("RNode must not be null")
|
||||
}
|
||||
if o == nil {
|
||||
o = types.NewGenArgs(nil)
|
||||
resource := &Resource{RNode: *rn}
|
||||
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
|
||||
@@ -265,7 +270,7 @@ func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs)
|
||||
if err != nil {
|
||||
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
|
||||
@@ -274,5 +279,5 @@ func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Reso
|
||||
if err != nil {
|
||||
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/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/yaml"
|
||||
@@ -22,8 +23,6 @@ import (
|
||||
// paired with metadata used by kustomize.
|
||||
type Resource struct {
|
||||
kyaml.RNode
|
||||
options *types.GenArgs
|
||||
refBy []resid.ResId
|
||||
refVarNames []string
|
||||
}
|
||||
|
||||
@@ -35,6 +34,13 @@ var BuildAnnotations = []string{
|
||||
utils.BuildAnnotationPreviousNamespaces,
|
||||
utils.BuildAnnotationAllowNameChange,
|
||||
utils.BuildAnnotationAllowKindChange,
|
||||
utils.BuildAnnotationsRefBy,
|
||||
utils.BuildAnnotationsGenBehavior,
|
||||
utils.BuildAnnotationsGenAddHashSuffix,
|
||||
|
||||
kioutil.PathAnnotation,
|
||||
kioutil.IndexAnnotation,
|
||||
kioutil.SeqIndentAnnotation,
|
||||
}
|
||||
|
||||
func (r *Resource) ResetRNode(incoming *Resource) {
|
||||
@@ -80,6 +86,8 @@ func (r *Resource) DeepCopy() *Resource {
|
||||
// CopyMergeMetaDataFieldsFrom copies everything but the non-metadata in
|
||||
// the resource.
|
||||
// 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.
|
||||
func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
|
||||
if err := r.SetLabels(
|
||||
@@ -87,7 +95,7 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
|
||||
return fmt.Errorf("copyMerge cannot set labels - %w", err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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) {
|
||||
r.options = other.options
|
||||
r.refBy = other.copyRefBy()
|
||||
r.refVarNames = copyStringSlice(other.refVarNames)
|
||||
}
|
||||
|
||||
@@ -144,10 +150,10 @@ func (r *Resource) ErrIfNotEquals(o *Resource) error {
|
||||
func (r *Resource) ReferencesEqual(other *Resource) bool {
|
||||
setSelf := make(map[resid.ResId]bool)
|
||||
setOther := make(map[resid.ResId]bool)
|
||||
for _, ref := range other.refBy {
|
||||
for _, ref := range other.GetRefBy() {
|
||||
setOther[ref] = true
|
||||
}
|
||||
for _, ref := range r.refBy {
|
||||
for _, ref := range r.GetRefBy() {
|
||||
if _, ok := setOther[ref]; !ok {
|
||||
return false
|
||||
}
|
||||
@@ -156,15 +162,6 @@ func (r *Resource) ReferencesEqual(other *Resource) bool {
|
||||
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 {
|
||||
if s == 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.
|
||||
func (r *Resource) AllowNameChange() {
|
||||
annotations := r.GetAnnotations()
|
||||
annotations[utils.BuildAnnotationAllowNameChange] = utils.Allowed
|
||||
if err := r.SetAnnotations(annotations); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r.enable(utils.BuildAnnotationAllowNameChange)
|
||||
}
|
||||
|
||||
// NameChangeAllowed checks if a patch resource is allowed to change another resource's name.
|
||||
func (r *Resource) NameChangeAllowed() bool {
|
||||
annotations := r.GetAnnotations()
|
||||
v, ok := annotations[utils.BuildAnnotationAllowNameChange]
|
||||
return ok && v == utils.Allowed
|
||||
return r.isEnabled(utils.BuildAnnotationAllowNameChange)
|
||||
}
|
||||
|
||||
// AllowKindChange allows kind changes to the resource.
|
||||
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[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 {
|
||||
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.
|
||||
func (r *Resource) String() string {
|
||||
bs, err := r.MarshalJSON()
|
||||
if err != nil {
|
||||
return "<" + err.Error() + ">"
|
||||
}
|
||||
return strings.TrimSpace(string(bs)) + r.options.String()
|
||||
return strings.TrimSpace(string(bs))
|
||||
}
|
||||
|
||||
// AsYAML returns the resource in Yaml form.
|
||||
@@ -306,20 +307,34 @@ func (r *Resource) MustYaml() string {
|
||||
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.
|
||||
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
|
||||
// hash should be appended to the name of the resource.
|
||||
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.
|
||||
@@ -363,12 +378,18 @@ func (r *Resource) CurId() resid.ResId {
|
||||
|
||||
// GetRefBy returns the ResIds that referred to current resource
|
||||
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
|
||||
func (r *Resource) AppendRefBy(id resid.ResId) {
|
||||
r.refBy = append(r.refBy, id)
|
||||
// Using any type except fmt.Stringer here results in a compilation error
|
||||
func (r *Resource) AppendRefBy(id fmt.Stringer) {
|
||||
r.appendCsvAnnotation(utils.BuildAnnotationsRefBy, id.String())
|
||||
}
|
||||
|
||||
// GetRefVarNames returns vars that refer to current resource
|
||||
@@ -424,3 +445,17 @@ func mergeStringMaps(maps ...map[string]string) map[string]string {
|
||||
}
|
||||
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
|
||||
const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
|
||||
|
||||
@@ -66,17 +64,15 @@ func TestResourceString(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
in: testConfigMap,
|
||||
s: configMapAsString + genArgOptions,
|
||||
s: configMapAsString,
|
||||
},
|
||||
{
|
||||
in: testDeployment,
|
||||
s: deploymentAsString + genArgOptions,
|
||||
s: deploymentAsString,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if test.in.String() != test.s {
|
||||
t.Fatalf("Expected %s == %s", test.in.String(), test.s)
|
||||
}
|
||||
assert.Equal(t, test.in.String(), test.s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,6 +280,266 @@ spec:
|
||||
`, 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) {
|
||||
resource, err := factory.FromBytes([]byte(`
|
||||
apiVersion: v1
|
||||
@@ -717,9 +973,9 @@ metadata:
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/previousKinds: Secret
|
||||
config.kubernetes.io/previousNames: oldName
|
||||
config.kubernetes.io/previousNamespaces: default
|
||||
internal.config.kubernetes.io/previousKinds: Secret
|
||||
internal.config.kubernetes.io/previousNames: oldName
|
||||
internal.config.kubernetes.io/previousNamespaces: default
|
||||
name: newName
|
||||
`,
|
||||
},
|
||||
@@ -729,9 +985,9 @@ metadata:
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/previousKinds: Secret
|
||||
config.kubernetes.io/previousNames: oldName
|
||||
config.kubernetes.io/previousNamespaces: default
|
||||
internal.config.kubernetes.io/previousKinds: Secret
|
||||
internal.config.kubernetes.io/previousNames: oldName
|
||||
internal.config.kubernetes.io/previousNamespaces: default
|
||||
name: oldName2
|
||||
`,
|
||||
newName: "newName",
|
||||
@@ -740,9 +996,9 @@ metadata:
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/previousKinds: Secret,Secret
|
||||
config.kubernetes.io/previousNames: oldName,oldName2
|
||||
config.kubernetes.io/previousNamespaces: default,default
|
||||
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
||||
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
||||
internal.config.kubernetes.io/previousNamespaces: default,default
|
||||
name: newName
|
||||
`,
|
||||
},
|
||||
@@ -752,9 +1008,9 @@ metadata:
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/previousKinds: Secret
|
||||
config.kubernetes.io/previousNames: oldName
|
||||
config.kubernetes.io/previousNamespaces: default
|
||||
internal.config.kubernetes.io/previousKinds: Secret
|
||||
internal.config.kubernetes.io/previousNames: oldName
|
||||
internal.config.kubernetes.io/previousNamespaces: default
|
||||
name: oldName2
|
||||
namespace: oldNamespace
|
||||
`,
|
||||
@@ -764,9 +1020,9 @@ metadata:
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/previousKinds: Secret,Secret
|
||||
config.kubernetes.io/previousNames: oldName,oldName2
|
||||
config.kubernetes.io/previousNamespaces: default,oldNamespace
|
||||
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
||||
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
||||
internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
|
||||
name: newName
|
||||
namespace: newNamespace
|
||||
`,
|
||||
@@ -814,9 +1070,9 @@ metadata:
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/previousKinds: Secret
|
||||
config.kubernetes.io/previousNames: oldName
|
||||
config.kubernetes.io/previousNamespaces: default
|
||||
internal.config.kubernetes.io/previousKinds: Secret
|
||||
internal.config.kubernetes.io/previousNames: oldName
|
||||
internal.config.kubernetes.io/previousNamespaces: default
|
||||
name: newName
|
||||
`,
|
||||
expected: []resid.ResId{
|
||||
@@ -833,9 +1089,9 @@ metadata:
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/previousKinds: Secret,Secret
|
||||
config.kubernetes.io/previousNames: oldName,oldName2
|
||||
config.kubernetes.io/previousNamespaces: default,oldNamespace
|
||||
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
||||
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
||||
internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
|
||||
name: newName
|
||||
namespace: newNamespace
|
||||
`,
|
||||
@@ -1133,3 +1389,41 @@ spec:
|
||||
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(
|
||||
config string) resmap.ResMap {
|
||||
rm := th.LoadAndRunGeneratorWithBuildAnnotations(config)
|
||||
rm.RemoveBuildAnnotations()
|
||||
return rm
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) LoadAndRunGeneratorWithBuildAnnotations(
|
||||
config string) resmap.ResMap {
|
||||
res, err := th.rf.RF().FromBytes([]byte(config))
|
||||
if err != nil {
|
||||
@@ -162,7 +169,6 @@ func (th *HarnessEnhanced) LoadAndRunGenerator(
|
||||
if err != nil {
|
||||
th.t.Fatalf("generate err: %v", err)
|
||||
}
|
||||
rm.RemoveBuildAnnotations()
|
||||
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
|
||||
// of all other objects, which can be used in apply, prune and delete
|
||||
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
|
||||
|
||||
@@ -57,4 +57,6 @@ type FnPluginLoadingOptions struct {
|
||||
Env []string
|
||||
// Run as uid and gid of the command executor
|
||||
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
|
||||
functions.
|
||||
|
||||
### Annotations
|
||||
### Internal Annotations
|
||||
|
||||
The orchestrator annotates resources in the wire format with annotation prefix
|
||||
`config.kubernetes.io`. These annotations are not persisted when the
|
||||
orchestrator writes the resources to the filesystem. The orchestrator sets this
|
||||
annotation when reading files from the local filesystem and removes the
|
||||
annotation when writing the output of functions back to the filesystem.
|
||||
For orchestration purposes, the orchestrator will use a set of annotations,
|
||||
referred to as _internal annotations_, on resources in `Resources.items`. These
|
||||
annotations are not persisted to resource manifests on the filesystem: The
|
||||
orchestrator sets this annotation when reading files from the local 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
|
||||
explicitly listed below.
|
||||
Annotation prefix `internal.config.kubernetes.io` is reserved for use for
|
||||
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
|
||||
path is relative to a fix location on the filesystem. Different orchestrator
|
||||
implementations can choose different fixed points.
|
||||
|
||||
A function SHOULD NOT modify this annotation.
|
||||
A function SHOULD NOT modify these annotations.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
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
|
||||
are separated by three dashes (`---`), and the index represents the position of
|
||||
the Resource starting from zero. When this annotation is not specified, it
|
||||
implies a value of `0`.
|
||||
|
||||
A function SHOULD NOT modify this annotation.
|
||||
A function SHOULD NOT modify these annotations.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: "relative/file/path.yaml"
|
||||
config.kubernetes.io/index: 2
|
||||
internal.config.kubernetes.io/path: "relative/file/path.yaml"
|
||||
internal.config.kubernetes.io/index: 2
|
||||
```
|
||||
|
||||
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/inf.v0 v0.9.1
|
||||
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/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
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.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
||||
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/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
||||
@@ -21,12 +21,13 @@ import (
|
||||
func NewAnnotateRunner(parent string) *AnnotateRunner {
|
||||
r := &AnnotateRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "annotate [DIR]",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Short: commands.AnnotateShort,
|
||||
Long: commands.AnnotateLong,
|
||||
Example: commands.AnnotateExamples,
|
||||
RunE: r.runE,
|
||||
Use: "annotate [DIR]",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Short: commands.AnnotateShort,
|
||||
Long: commands.AnnotateLong,
|
||||
Example: commands.AnnotateExamples,
|
||||
RunE: r.runE,
|
||||
Deprecated: "use the `commonAnnotations` field in your kustomization file.",
|
||||
}
|
||||
runner.FixDocs(parent, c)
|
||||
r.Command = c
|
||||
|
||||
@@ -559,7 +559,7 @@ added annotations in the package
|
||||
|
||||
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -32,6 +32,8 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
||||
Example: commands.CreateSetterExamples,
|
||||
PreRunE: r.preRunE,
|
||||
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", "",
|
||||
"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)
|
||||
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -23,6 +23,8 @@ func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
|
||||
Args: cobra.ExactArgs(2),
|
||||
PreRun: r.preRun,
|
||||
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", "",
|
||||
"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)
|
||||
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()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -26,6 +26,8 @@ func GetInitRunner(name string) *InitRunner {
|
||||
Long: commands.InitLong,
|
||||
Example: commands.InitExamples,
|
||||
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)
|
||||
r.Command = c
|
||||
|
||||
@@ -31,6 +31,8 @@ func NewListSettersRunner(parent string) *ListSettersRunner {
|
||||
Example: commands.ListSettersExamples,
|
||||
PreRunE: r.preRunE,
|
||||
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,
|
||||
"output as github markdown")
|
||||
|
||||
@@ -525,7 +525,7 @@ test/testdata/dataset-with-setters/mysql/
|
||||
// normalize path format for windows
|
||||
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()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -27,6 +27,8 @@ func NewSetRunner(parent string) *SetRunner {
|
||||
Example: commands.SetExamples,
|
||||
PreRunE: r.preRunE,
|
||||
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)
|
||||
r.Command = c
|
||||
|
||||
@@ -1137,7 +1137,7 @@ set 1 field(s) of setter "namespace" to value "otherspace"
|
||||
expectedNormalized := strings.Replace(
|
||||
strings.Replace(expected, "\\", "/", -1),
|
||||
"//", "/", -1)
|
||||
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
|
||||
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -542,13 +542,13 @@ kind: Input
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
a-bool-value: true
|
||||
a-int-value: 2
|
||||
a-string-value: a
|
||||
config.kubernetes.io/function: |
|
||||
starlark:
|
||||
path: script.star
|
||||
name: fn
|
||||
a-bool-value: true
|
||||
a-int-value: 2
|
||||
a-string-value: a
|
||||
data:
|
||||
boolValue: true
|
||||
intValue: 2
|
||||
|
||||
@@ -25,6 +25,8 @@ func GetFmtRunner(name string) *FmtRunner {
|
||||
Example: commands.FmtExamples,
|
||||
RunE: r.runE,
|
||||
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)
|
||||
c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern,
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestFmtCommand_stdin(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 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
|
||||
@@ -238,7 +238,7 @@ formatted resource files in the package
|
||||
|
||||
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -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()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -43,7 +43,9 @@ kind: Krmfile
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -78,7 +80,9 @@ kind: Krmfile
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ func GetMergeRunner(name string) *MergeRunner {
|
||||
Long: commands.MergeLong,
|
||||
Example: commands.MergeExamples,
|
||||
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)
|
||||
r.Command = c
|
||||
|
||||
@@ -18,6 +18,8 @@ func GetMerge3Runner(name string) *Merge3Runner {
|
||||
Long: commands.Merge3Long,
|
||||
Example: commands.Merge3Examples,
|
||||
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)
|
||||
c.Flags().StringVar(&r.ancestor, "ancestor", "",
|
||||
|
||||
@@ -6,6 +6,7 @@ package commands
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -73,6 +74,7 @@ func GetRunFnRunner(name string) *RunFnRunner {
|
||||
"a list of environment variables to be used by functions")
|
||||
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")
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -302,6 +304,11 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
// parse mounts to set storageMounts
|
||||
storageMounts := toStorageMounts(r.Mounts)
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.RunFns = runfn.RunFns{
|
||||
FunctionPaths: r.FnPaths,
|
||||
GlobalScope: r.GlobalScope,
|
||||
@@ -317,6 +324,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
LogSteps: r.LogSteps,
|
||||
Env: r.Env,
|
||||
AsCurrentUser: r.AsCurrentUser,
|
||||
WorkingDir: wd,
|
||||
}
|
||||
|
||||
// don't consider args for the function
|
||||
|
||||
@@ -11,13 +11,14 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||
)
|
||||
|
||||
// TestRunFnCommand_preRunE verifies that preRunE correctly parses the commandline
|
||||
// flags and arguments into the RunFns structure to be executed.
|
||||
func TestRunFnCommand_preRunE(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
assert.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
@@ -201,6 +202,7 @@ apiVersion: v1
|
||||
Path: "dir",
|
||||
EnableStarlark: true,
|
||||
Env: []string{},
|
||||
WorkingDir: wd,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -254,6 +256,7 @@ apiVersion: v1
|
||||
Path: "dir",
|
||||
ResultsDir: "foo/",
|
||||
Env: []string{},
|
||||
WorkingDir: wd,
|
||||
},
|
||||
expected: `
|
||||
metadata:
|
||||
@@ -286,9 +289,10 @@ apiVersion: v1
|
||||
args: []string{"run", "dir", "--log-steps"},
|
||||
path: "dir",
|
||||
expectedStruct: &runfn.RunFns{
|
||||
Path: "dir",
|
||||
LogSteps: true,
|
||||
Env: []string{},
|
||||
Path: "dir",
|
||||
LogSteps: true,
|
||||
Env: []string{},
|
||||
WorkingDir: wd,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -296,8 +300,9 @@ apiVersion: v1
|
||||
args: []string{"run", "dir", "--env", "FOO=BAR", "-e", "BAR"},
|
||||
path: "dir",
|
||||
expectedStruct: &runfn.RunFns{
|
||||
Path: "dir",
|
||||
Env: []string{"FOO=BAR", "BAR"},
|
||||
Path: "dir",
|
||||
Env: []string{"FOO=BAR", "BAR"},
|
||||
WorkingDir: wd,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -308,6 +313,7 @@ apiVersion: v1
|
||||
Path: "dir",
|
||||
AsCurrentUser: true,
|
||||
Env: []string{},
|
||||
WorkingDir: wd,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ require (
|
||||
github.com/rakyll/statik v0.1.7
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
sigs.k8s.io/kustomize/api v0.8.10
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0
|
||||
sigs.k8s.io/kustomize/api v0.8.11
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||
)
|
||||
|
||||
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/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
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.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
||||
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/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
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
|
||||
<!-- @defineHelmCommand @testHelm -->
|
||||
```
|
||||
helmCommand=~/go/bin/helmV3
|
||||
helmCommand=${MYGOBIN:-~/go/bin}/helmV3
|
||||
```
|
||||
|
||||
This value is needed for testing this example in CI/CD.
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
# any branch using this label, so it should probably get
|
||||
# a new value.
|
||||
export MYGOBIN
|
||||
mdrip --mode test --blockTimeOut 15m \
|
||||
--label testAgainstLatestRelease examples
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ func NewCmdSet(fSys filesys.FileSystem, ldr ifc.KvLoader, v ifc.Validator) *cobr
|
||||
newCmdSetImage(fSys),
|
||||
newCmdSetReplicas(fSys),
|
||||
newCmdSetLabel(fSys, ldr.Validator().MakeLabelValidator()),
|
||||
newCmdSetAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()),
|
||||
)
|
||||
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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var format string
|
||||
|
||||
// NewCmdFetch makes a new fetch command.
|
||||
func NewCmdFetch(w io.Writer) *cobra.Command {
|
||||
infoCmd := cobra.Command{
|
||||
fetchCmd := cobra.Command{
|
||||
Use: "fetch",
|
||||
Short: `Fetches the OpenAPI specification from the current kubernetes cluster specified
|
||||
in the user's kubeconfig`,
|
||||
Example: `kustomize openapi fetch`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
printSchema(w)
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
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 := `
|
||||
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/`
|
||||
|
||||
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.Stderr = &stderr
|
||||
err := command.Run()
|
||||
if err != nil || stdout.String() == "" {
|
||||
fmt.Fprintln(w, err, stderr.String()+errMsg)
|
||||
return
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w\n%s", err, stderr.String()+errMsg)
|
||||
} else if stdout.String() == "" {
|
||||
return fmt.Errorf(stderr.String() + errMsg)
|
||||
}
|
||||
|
||||
// format and output
|
||||
@@ -46,5 +59,14 @@ Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/inst
|
||||
output := stdout.Bytes()
|
||||
json.Unmarshal(output, &jsonSchema)
|
||||
output, _ = json.MarshalIndent(jsonSchema, "", " ")
|
||||
|
||||
if format == "yaml" {
|
||||
output, err = yaml.JSONToYAML(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, string(output))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ require (
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
sigs.k8s.io/kustomize/api v0.8.10
|
||||
sigs.k8s.io/kustomize/cmd/config v0.9.13
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0
|
||||
sigs.k8s.io/kustomize/api v0.8.11
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
|
||||
@@ -253,10 +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/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.9.13 h1:lqOf0QcFhNvgZkgrPINNRs7TxEO7IGVtLMyUJId3oRE=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.0 h1:6XPkaOu9eFdvrcqHpfmYAKoBvE01JdvsTVvtDuAFM+8=
|
||||
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/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
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 {
|
||||
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
|
||||
}
|
||||
result = &fsNode{
|
||||
content: c,
|
||||
content: append([]byte(nil), c...),
|
||||
parent: parent,
|
||||
}
|
||||
parent.dir[fileName] = result
|
||||
|
||||
@@ -157,9 +157,13 @@ func runBasicOperations(
|
||||
if string(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)
|
||||
}
|
||||
// This ensures that modifying the original slice does not change the contents of the file.
|
||||
content[0] = '@'
|
||||
stuff, err = fSys.ReadFile(c.path)
|
||||
if err != nil {
|
||||
t.Fatalf("%s; unexpected error: %v", c.what, err)
|
||||
|
||||
@@ -74,6 +74,8 @@ type CommandResultsChecker struct {
|
||||
|
||||
// Command provides the function to run.
|
||||
Command func() *cobra.Command
|
||||
|
||||
*checkerCore
|
||||
}
|
||||
|
||||
// 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 == "" {
|
||||
rc.InputFilenameGlob = DefaultInputFilenameGlob
|
||||
}
|
||||
|
||||
checker := newResultsChecker(
|
||||
rc.TestDataDirectory, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename,
|
||||
rc.OutputAssertionFunc, rc.ErrorAssertionFunc,
|
||||
rc.UpdateExpectedFromActual,
|
||||
)
|
||||
checker.assert(t, func() (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)
|
||||
|
||||
err = cmd.Execute()
|
||||
return stdOut.String(), stdErr.String()
|
||||
})
|
||||
|
||||
rc.checkerCore = &checkerCore{
|
||||
testDataDirectory: rc.TestDataDirectory,
|
||||
expectedOutputFilename: rc.ExpectedOutputFilename,
|
||||
expectedErrorFilename: rc.ExpectedErrorFilename,
|
||||
updateExpectedFromActual: rc.UpdateExpectedFromActual,
|
||||
outputAssertionFunc: rc.OutputAssertionFunc,
|
||||
errorAssertionFunc: rc.ErrorAssertionFunc,
|
||||
}
|
||||
rc.checkerCore.setDefaults()
|
||||
runAllTestCases(t, rc)
|
||||
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
|
||||
// and comparing the outputs to expected results.
|
||||
type ProcessorResultsChecker struct {
|
||||
@@ -159,6 +173,8 @@ type ProcessorResultsChecker struct {
|
||||
|
||||
// Processor returns a ResourceListProcessor to run.
|
||||
Processor func() framework.ResourceListProcessor
|
||||
|
||||
*checkerCore
|
||||
}
|
||||
|
||||
// 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 == "" {
|
||||
rc.InputFilename = DefaultInputFilename
|
||||
}
|
||||
|
||||
checker := newResultsChecker(
|
||||
rc.TestDataDirectory, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename,
|
||||
rc.OutputAssertionFunc, rc.ErrorAssertionFunc,
|
||||
rc.UpdateExpectedFromActual,
|
||||
)
|
||||
checker.assert(t, func() (string, string) {
|
||||
_, err := os.Stat(rc.InputFilename)
|
||||
if os.IsNotExist(err) {
|
||||
t.Error("Test case is missing input file")
|
||||
}
|
||||
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(), ""
|
||||
})
|
||||
rc.checkerCore = &checkerCore{
|
||||
testDataDirectory: rc.TestDataDirectory,
|
||||
expectedOutputFilename: rc.ExpectedOutputFilename,
|
||||
expectedErrorFilename: rc.ExpectedErrorFilename,
|
||||
updateExpectedFromActual: rc.UpdateExpectedFromActual,
|
||||
outputAssertionFunc: rc.OutputAssertionFunc,
|
||||
errorAssertionFunc: rc.ErrorAssertionFunc,
|
||||
}
|
||||
rc.checkerCore.setDefaults()
|
||||
runAllTestCases(t, rc)
|
||||
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)
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
// resultsChecker implements the core logic shared by all results checking types.
|
||||
type resultsChecker struct {
|
||||
// resultsChecker is implemented by ProcessorResultsChecker and CommandResultsChecker, partially via checkerCore
|
||||
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
|
||||
expectedOutputFilename string
|
||||
expectedErrorFilename string
|
||||
@@ -232,20 +288,10 @@ type resultsChecker struct {
|
||||
outputAssertionFunc AssertionFunc
|
||||
errorAssertionFunc AssertionFunc
|
||||
|
||||
testsCasesRun int
|
||||
testsCasesRun []string
|
||||
}
|
||||
|
||||
func newResultsChecker(testDataDir string, outputFilename string, errorFilename string,
|
||||
outputAsserter AssertionFunc, errorAsserter AssertionFunc,
|
||||
updateFixtures bool) *resultsChecker {
|
||||
rc := resultsChecker{
|
||||
testDataDirectory: testDataDir,
|
||||
expectedOutputFilename: outputFilename,
|
||||
expectedErrorFilename: errorFilename,
|
||||
updateExpectedFromActual: updateFixtures,
|
||||
outputAssertionFunc: outputAsserter,
|
||||
errorAssertionFunc: errorAsserter,
|
||||
}
|
||||
func (rc *checkerCore) setDefaults() {
|
||||
if rc.testDataDirectory == "" {
|
||||
rc.testDataDirectory = DefaultTestDataDirectory
|
||||
}
|
||||
@@ -261,73 +307,47 @@ func newResultsChecker(testDataDir string, outputFilename string, errorFilename
|
||||
if rc.errorAssertionFunc == nil {
|
||||
rc.errorAssertionFunc = RequireEachLineMatches
|
||||
}
|
||||
return &rc
|
||||
}
|
||||
|
||||
// assert traverses TestDataDirectory 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 (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 *checkerCore) rootDir() string {
|
||||
return rc.testDataDirectory
|
||||
}
|
||||
|
||||
func (rc *resultsChecker) runDirectoryTestCase(t *testing.T, path string, getResult func() (string, string)) {
|
||||
// cd into the directory so we can test functions that refer
|
||||
// local files by relative paths
|
||||
d, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
func (rc *checkerCore) TestCasesRun() []string {
|
||||
return rc.testsCasesRun
|
||||
}
|
||||
|
||||
defer func() { require.NoError(t, os.Chdir(d)) }()
|
||||
require.NoError(t, os.Chdir(path))
|
||||
func (rc *checkerCore) resetTestCasesRun() {
|
||||
rc.testsCasesRun = []string{}
|
||||
}
|
||||
|
||||
expectedOutput, expectedError := rc.readAssertionFiles(t)
|
||||
if expectedError == "" && expectedOutput == "" && !rc.updateExpectedFromActual {
|
||||
// not a test directory: missing expectations and updateExpectedFromActual == false
|
||||
return
|
||||
func (rc *checkerCore) recordTestCase(s string) {
|
||||
rc.testsCasesRun = append(rc.testsCasesRun, s)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
// run the test
|
||||
t.Run(path, func(t *testing.T) {
|
||||
rc.testsCasesRun += 1
|
||||
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)
|
||||
}
|
||||
})
|
||||
if len(actualOutput) > 0 {
|
||||
require.NoError(t, ioutil.WriteFile(rc.expectedOutputFilename, []byte(actualOutput), 0600))
|
||||
}
|
||||
t.Skip("Updated fixtures for test case")
|
||||
}
|
||||
|
||||
// readAssertionFiles reads the expected results and error files
|
||||
func (rc *resultsChecker) readAssertionFiles(t *testing.T) (string, string) {
|
||||
func (rc *checkerCore) assertOutputMatches(t *testing.T, expected string, actual 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
|
||||
var expectedOutput, expectedError string
|
||||
if rc.expectedOutputFilename != "" {
|
||||
@@ -360,3 +380,65 @@ func (rc *resultsChecker) readAssertionFiles(t *testing.T) (string, string) {
|
||||
}
|
||||
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 {
|
||||
// TODO(knverey): Make this work for more Kinds and expose the helper for doing so.
|
||||
containers, err := matches[i].Pipe(yaml.Lookup("spec", "template", "spec", "containers"))
|
||||
containers, err := matches[i].Pipe(yaml.LookupFirstMatch(yaml.ConventionalContainerPaths))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package framework_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -276,6 +277,57 @@ functionConfig:
|
||||
`), 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) {
|
||||
cfg := new(struct {
|
||||
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 (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||
"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) {
|
||||
c.setupExec()
|
||||
if err := c.setupExec(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Exec.Filter(nodes)
|
||||
}
|
||||
|
||||
func (c *Filter) setupExec() {
|
||||
func (c *Filter) setupExec() error {
|
||||
// don't init 2x
|
||||
if c.Exec.Path != "" {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Exec.WorkingDir = wd
|
||||
|
||||
path, args := c.getCommand()
|
||||
c.Exec.Path = path
|
||||
c.Exec.Args = args
|
||||
return nil
|
||||
}
|
||||
|
||||
// getArgs returns the command + args to run to spawn the container
|
||||
|
||||
@@ -6,6 +6,8 @@ package container
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -128,7 +130,7 @@ metadata:
|
||||
instance := NewContainer(tt.containerSpec, tt.UIDGID)
|
||||
instance.Exec.FunctionConfig = cfg
|
||||
instance.Env = append(instance.Env, "KYAML_TEST=FOO")
|
||||
instance.setupExec()
|
||||
assert.NoError(t, instance.setupExec())
|
||||
|
||||
tt.expectedArgs = append(tt.expectedArgs,
|
||||
runtimeutil.NewContainerEnvFromStringSlice(instance.Env).GetDockerFlags()...)
|
||||
@@ -173,6 +175,8 @@ metadata:
|
||||
instance.Exec.FunctionConfig = cfg
|
||||
instance.Exec.Path = "sed"
|
||||
instance.Exec.Args = []string{"s/Deployment/StatefulSet/g"}
|
||||
instance.Exec.WorkingDir = getWorkingDir(t)
|
||||
|
||||
output, err := instance.Filter(input)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
@@ -219,6 +223,7 @@ func TestFilter_ExitCode(t *testing.T) {
|
||||
instance := Filter{}
|
||||
instance.Exec.Path = "/not/real/command"
|
||||
instance.Exec.DeferFailure = true
|
||||
instance.Exec.WorkingDir = getWorkingDir(t)
|
||||
_, err := instance.Filter(nil)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
@@ -231,3 +236,9 @@ func TestFilter_ExitCode(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func getWorkingDir(t *testing.T) string {
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
return wd
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -19,6 +21,10 @@ type Filter struct {
|
||||
// Args are the arguments to the executable
|
||||
Args []string `yaml:"args,omitempty"`
|
||||
|
||||
// WorkingDir is the working directory that the executable
|
||||
// should run in
|
||||
WorkingDir string
|
||||
|
||||
runtimeutil.FunctionFilter
|
||||
}
|
||||
|
||||
@@ -32,5 +38,17 @@ func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
|
||||
cmd.Stdin = reader
|
||||
cmd.Stdout = writer
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -4,15 +4,19 @@
|
||||
package exec_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestFunctionFilter_Filter(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
var tests = []struct {
|
||||
name string
|
||||
input []string
|
||||
@@ -51,8 +55,9 @@ metadata:
|
||||
},
|
||||
expectedError: "",
|
||||
instance: exec.Filter{
|
||||
Path: "sed",
|
||||
Args: []string{"s/Deployment/StatefulSet/g"},
|
||||
Path: "sed",
|
||||
Args: []string{"s/Deployment/StatefulSet/g"},
|
||||
WorkingDir: wd,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/order"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -169,8 +170,8 @@ func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// copy the comments from the inputs to the outputs
|
||||
if err := c.setComments(output); err != nil {
|
||||
// copy the comments and sync the order of fields from the inputs to the outputs
|
||||
if err := c.copyCommentsAndSyncOrder(output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -210,7 +211,7 @@ func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FunctionFilter) setComments(nodes []*yaml.RNode) error {
|
||||
func (c *FunctionFilter) copyCommentsAndSyncOrder(nodes []*yaml.RNode) error {
|
||||
for i := range nodes {
|
||||
node := nodes[i]
|
||||
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 {
|
||||
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 {
|
||||
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