Compare commits

...

74 Commits

Author SHA1 Message Date
Kubernetes Prow Robot
b2d65ddc98 Merge pull request #4283 from KnVerey/pinToApi
Pin to api v0.10.1
2021-11-11 15:27:14 -08:00
Katrina Verey
3db4a94281 Pin to api v0.10.1 2021-11-11 15:18:05 -08:00
Kubernetes Prow Robot
326a57a9cc Merge pull request #4282 from KnVerey/pinToCmdConfig
Pin to cmd/config v0.10.2
2021-11-11 15:03:14 -08:00
Katrina Verey
9dfdebc6c7 Pin to cmd/config v0.10.2 2021-11-11 14:53:17 -08:00
Natasha Sarkar
b896e04c20 Merge pull request #4281 from KnVerey/pinToKyaml
Pin to kyaml v0.13.0
2021-11-11 14:39:53 -08:00
Katrina Verey
6ecae1ad50 Also pin patch and patchjson transformers 2021-11-11 14:30:39 -08:00
Katrina Verey
9abb72e4d6 Pin to kyaml v0.13.0 2021-11-11 14:12:13 -08:00
Kubernetes Prow Robot
6365b3d0cf Merge pull request #4280 from KnVerey/more_releases
Add s390x and ppc64le binaries to releases
2021-11-11 13:52:07 -08:00
Katrina Verey
33c2ea01c4 Add s390x and ppc64le binaries to releases 2021-11-11 13:37:50 -08:00
Kubernetes Prow Robot
863ff0ef1b Merge pull request #4262 from patricknelson/fix-windows-build
Fix windows build, add clarity to goreleaser build (due to race conditions).
2021-11-11 13:32:08 -08:00
Patrick Nelson
a5117083ec Step 2 of 2: Adding windows build back and added ability to reproduce goreleaser builds locally (localbuild.sh) in a way exactly consistent with Cloud Build (cloudbuild.sh) but as a *build* only, without being coupled to Cloud Build or it's dependencies (like Cloud KMS, GitHub, etc). 2021-11-11 12:17:42 -08:00
Patrick Nelson
a143688a1d Step 1 of 2: Renaming localbuild.sh to cloudbuild-local.sh (preserve commit history) to make way for new localbuild.sh which will actually be entirely local, since this script is still very specific to Cloud Build. 2021-11-10 19:03:22 -08:00
Kubernetes Prow Robot
0676d0bd11 Merge pull request #4266 from Serializator/issue-4111-patchJson6902
Fix name suffix not being applied when "patchesJson6902" is used
2021-11-10 15:13:50 -08:00
Julian van den Berkmortel
b6cb6c8ae9 fix build annotations getting lost after applying JSON 6902 patch 2021-11-10 23:02:42 +01:00
Julian van den Berkmortel
b16e4ec566 add test to demonstrate internal annotations getting lost (#4111) 2021-11-10 21:33:50 +01:00
Mohd Bilal
cb1cbbe044 Fixes 4108; remove hidden files in kustomize edit command to correctly mimic shell globbing behaviour (#4170)
* util and util_test corrected

* fixed behaviour of fsondisk with test updated

* glob behaviour fixed in fsnode

* removed commented code
2021-11-10 08:51:26 -08:00
Kubernetes Prow Robot
86fb408b2c Merge pull request #4276 from natasha41575/fixFunctionSpec
fix function spec example
2021-11-09 21:05:26 -08:00
natasha41575
ca5d691199 fix function spec example 2021-11-09 20:56:36 -08:00
Kubernetes Prow Robot
394567079d Merge pull request #4272 from mengqiy/pointer
Make ResourceList follow k8s api conventions
2021-11-09 15:41:26 -08:00
Kubernetes Prow Robot
e0c8ebc41f Merge pull request #4235 from Goodwine/kyaml-wrap-bug
Fix kyaml readwriter inconsistencies when wrapping resources
2021-11-09 12:37:48 -08:00
Kubernetes Prow Robot
8668691ade Merge pull request #4271 from natasha41575/ReplacementsEdit
Fix: replacements entries get source and targets with null value appended
2021-11-09 10:49:47 -08:00
Mengqi Yu
374d790a21 Make ResourceList follow k8s api conventions
Make optional fields as pointers.
Add omitempty for optional fields.
2021-11-08 21:35:49 -08:00
natasha41575
d8f406d06f add omitempty tag to replacement sources and targets 2021-11-08 12:33:30 -08:00
natasha41575
46b3cd2109 modify edit test for null replacements fields 2021-11-08 12:21:45 -08:00
Carlos Ortiz García
20c608989a Move kio.ByteRW tests from framework_test to byteio_readwriter_test 2021-11-08 11:55:02 -08:00
Carlos Ortiz García
ba4d83f75f only override wrapping kind if it wasn't set already 2021-11-08 11:29:57 -08:00
Carlos Ortiz García
067559127d test cases for framework wrapping/unwrapping bug 2021-11-08 10:13:00 -08:00
Kubernetes Prow Robot
37ab5579f0 Merge pull request #4229 from natasha41575/annotationAndLabelSelectionInReplacements
Annotation and label selection in replacement targets
2021-11-04 13:08:23 -07:00
natasha41575
ef5f1d347d support label and annotation selection in replacement targets 2021-11-04 12:25:15 -07:00
Kubernetes Prow Robot
2c4b195516 Merge pull request #4259 from MikaelSmith/fix-patch-example
docs: Update example for patching multiple objects
2021-11-01 16:01:54 -07:00
Kubernetes Prow Robot
04396ab4e6 Merge pull request #4203 from timofurrer/config-map-consistency
Add consistency to ConfigMap spelling
2021-11-01 15:47:54 -07:00
Kubernetes Prow Robot
4fd77b3a6e Merge pull request #4248 from natasha41575/resourceListResults
[breaking] update results field of ResourceList to implement function spec v1
2021-10-29 14:51:09 -07:00
natasha41575
3ea8b79925 update results field of ResourceList to implement function spec v1 2021-10-29 14:28:50 -07:00
Natasha Sarkar
71b978da1a Merge pull request #4253 from natasha41575/resourceListSDKHelpers
provide utility helpers for preserving internal annotations
2021-10-29 14:25:06 -07:00
Natasha Sarkar
7110298c52 Merge pull request #4249 from natasha41575/resourceListV1
bump ResourceList from v1alpha1 to v1
2021-10-29 14:24:10 -07:00
natasha41575
b4a69f08c0 provide utility helpers for preserving internal annotations 2021-10-29 12:30:55 -07:00
Michael Smith
572d5841c6 docs: Update example for patching multiple objects
Updates the example for patching multiple objects to match the
implementation in #1355, which supports name as a regular expression
(not wildcard pattern).
2021-10-26 12:28:42 -07:00
Kubernetes Prow Robot
984a2dab3d Merge pull request #4242 from natasha41575/configmapissue
fix issue with quotations being dropped in configmap generation
2021-10-24 18:54:22 -07:00
natasha41575
c3c02887ec bump ResourceList from v1alpha1 to v1 2021-10-19 16:15:27 -07:00
natasha41575
ba051c863b fix issue with quote being dropped in configmap generation 2021-10-14 18:30:28 -07:00
natasha41575
4d59146e48 test for dropped quote in configmap generation 2021-10-14 18:13:49 -07:00
Kubernetes Prow Robot
5765ab4dbc Merge pull request #4236 from Goodwine/docs-fn
Update documents from 'kustomize configuration' to 'kustomize fn'
2021-10-13 18:35:28 -07:00
Kubernetes Prow Robot
4769751943 Merge pull request #4160 from osherdp/fix/github-rate-limiter-output
Return a meaningful message if we hit the rate-limiter of GitHub
2021-10-13 18:23:28 -07:00
Kubernetes Prow Robot
e506ce021e Merge pull request #4230 from yutachaos/feature/update_unified_go_version
Update go version 1.16 for CI
2021-10-13 18:09:28 -07:00
Kubernetes Prow Robot
ed763991de Merge pull request #4227 from natasha41575/MigrateIndexPathIdAnnotations
fix bug with migrating kyaml reader path, index, and id annotations
2021-10-12 18:37:48 -07:00
natasha41575
55ac9ca88d fix bug with migrating annotations 2021-10-12 15:42:54 -07:00
Carlos Ortiz García
548f5ffca9 Update documents from 'configuration' to 'fn' 2021-10-11 12:35:22 -05:00
yutachaos
dd3377b1a0 Update CI go version 1.16
Signed-off-by: yutachaos <18604471+yutachaos@users.noreply.github.com>
2021-10-08 14:02:50 +09:00
natasha41575
605239a1e5 test to demonstrate broken annotation selection 2021-10-07 15:40:23 -07:00
Kubernetes Prow Robot
67c58ad4f4 Merge pull request #4210 from natasha41575/openapiFromComponent
fix issue with getting openapi schema from components
2021-10-04 19:27:49 -07:00
natasha41575
11e19a3d0f separate custom openapi test from the others 2021-10-04 18:48:54 -07:00
Kubernetes Prow Robot
6fffcb9203 Merge pull request #4202 from cedarkuo/update-helloworld-example-readme
Updated README to use 'resources' instead of 'bases' in overlays example.
2021-10-04 14:26:41 -07:00
Kubernetes Prow Robot
68790e00a9 Merge pull request #4216 from mengqiy/updatedockerfilegen
update the dockerfile gen command
2021-09-29 13:51:22 -07:00
Mengqi Yu
6cf06fac12 update the dockerfile gen command 2021-09-29 13:34:11 -07:00
natasha41575
0d8c107362 fix issue with openapi schema from components 2021-09-27 17:00:14 -07:00
natasha41575
f30e45c549 test to demonstrate issue with openapi from components (#4179) 2021-09-27 16:29:08 -07:00
Kubernetes Prow Robot
250ea13767 Merge pull request #4208 from KnVerey/release_instructions
Small improvements to release instructions
2021-09-27 11:41:45 -07:00
Katrina Verey
608128738d Small improvements to release instructions 2021-09-27 10:44:02 -07:00
Kubernetes Prow Robot
7153f33466 Merge pull request #4207 from KnVerey/updateProwExamplesTarget
Test examples against latest release
2021-09-27 10:11:36 -07:00
Kubernetes Prow Robot
274b12318d Merge pull request #4205 from natasha41575/releasing
add link to k8s-staging-kustomize project to releasing instructions
2021-09-27 10:01:37 -07:00
Katrina Verey
94c82f61a3 Test examples against latest release 2021-09-27 09:51:01 -07:00
Kubernetes Prow Robot
d260f50573 Merge pull request #4206 from KnVerey/unpinEverything
Back to development mode; unpin the modules
2021-09-27 09:47:37 -07:00
Katrina Verey
40c014a991 Back to development mode; unpin the modules 2021-09-27 09:37:38 -07:00
natasha41575
75c8aec29d add link to k8s-staging-kustomize project to releasing instructions 2021-09-27 09:35:40 -07:00
Kubernetes Prow Robot
fcb9c0065e Merge pull request #4200 from KnVerey/go1.17_build_tags
Add go 1.17 build tags, which go fmt auto-synchronizes
2021-09-27 09:27:36 -07:00
Katrina Verey
63ec6bdb3d Merge pull request #4199 from KnVerey/pinToApi
Pin to api v0.10.0
2021-09-27 09:13:36 -07:00
Kubernetes Prow Robot
25bfe6f306 Merge pull request #4204 from KnVerey/release_updates
Use keyring from kustomize GCP project with new token
2021-09-27 09:01:35 -07:00
Timo Furrer
635c4fd43b Merge branch 'master' into config-map-consistency 2021-09-27 09:16:42 +02:00
Timo Furrer
c455215f55 Add consistency to ConfigMap spelling 2021-09-27 09:11:56 +02:00
cedarkuo
8f56f51307 Updated README to use 'resources' instead of 'bases' in overlays example.
Kubernetes v1.21 upgrade kustomize-in-kubectl to v4.0.5. https://github.com/kubernetes/kubernetes/pull/98946
So I think this example could update back to use resources instead of bases?
2021-09-27 12:40:36 +08:00
Katrina Verey
70a8ed6ed3 Add go 1.17 build tags, which go fmt auto-synchronizes 2021-09-24 17:28:21 -07:00
Katrina Verey
febfaf16dc Use keyring from kustomize GCP project with new token 2021-09-24 20:24:10 -04:00
Katrina Verey
8268b17700 Pin to api v0.10.0 2021-09-24 16:58:20 -07:00
Osher De Paz
f7cd553044 return a meaningful message if we hit the rate-limiter of GitHub 2021-08-31 23:26:49 +03:00
93 changed files with 2146 additions and 473 deletions

View File

@@ -16,7 +16,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: ^1.13 go-version: ^1.16
id: go id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
@@ -35,7 +35,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: ^1.13 go-version: ^1.16
id: go id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
@@ -63,7 +63,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: ^1.13 go-version: ^1.16
id: go id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
@@ -91,7 +91,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: ^1.13 go-version: ^1.16
id: go id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory

View File

@@ -12,7 +12,7 @@ MYGOBIN = $(shell go env GOPATH)/bin
endif endif
export PATH := $(MYGOBIN):$(PATH) export PATH := $(MYGOBIN):$(PATH)
MODULES := '"cmd/config" "api/" "kustomize/" "kyaml/"' MODULES := '"cmd/config" "api/" "kustomize/" "kyaml/"'
LATEST_V4_RELEASE=v4.3.0 LATEST_V4_RELEASE=v4.4.0
# Provide defaults for REPO_OWNER and REPO_NAME if not present. # Provide defaults for REPO_OWNER and REPO_NAME if not present.
# Typically these values would be provided by Prow. # Typically these values would be provided by Prow.
@@ -229,7 +229,8 @@ generate-kustomize-api: $(MYGOBIN)/k8scopy
.PHONY: test-unit-kustomize-api .PHONY: test-unit-kustomize-api
test-unit-kustomize-api: build-kustomize-api test-unit-kustomize-api: build-kustomize-api
cd api; go test ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222" cd api; go test ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222"; \
cd krusty; OPENAPI_TEST=true go test -run TestCustomOpenAPIFieldFromComponentWithOverlays
.PHONY: test-unit-kustomize-plugins .PHONY: test-unit-kustomize-plugins
test-unit-kustomize-plugins: test-unit-kustomize-plugins:

View File

@@ -12,6 +12,7 @@ import (
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -78,12 +79,23 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
return err return err
} }
for _, res := range resources { for _, res := range resources {
internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode)
err = res.ApplyFilter(patchjson6902.Filter{ err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.JsonOp, Patch: p.JsonOp,
}) })
if err != nil { if err != nil {
return err return err
} }
annotations := res.GetAnnotations()
for key, value := range internalAnnotations {
annotations[key] = value
}
err = res.SetAnnotations(annotations)
if err != nil {
return err
}
} }
return nil return nil
} }

View File

@@ -12,6 +12,7 @@ import (
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -111,12 +112,19 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
} }
for _, res := range resources { for _, res := range resources {
res.StorePreviousId() res.StorePreviousId()
internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode)
err = res.ApplyFilter(patchjson6902.Filter{ err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.Patch, Patch: p.Patch,
}) })
if err != nil { if err != nil {
return err return err
} }
annotations := res.GetAnnotations()
for key, value := range internalAnnotations {
annotations[key] = value
}
err = res.SetAnnotations(annotations)
} }
return nil return nil
} }

View File

@@ -8,6 +8,7 @@ import (
"strings" "strings"
"sigs.k8s.io/kustomize/api/internal/utils" "sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/resid" "sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
@@ -48,6 +49,17 @@ func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targets []*types.T
if err != nil { if err != nil {
return nil, err return nil, err
} }
// filter targets by label and annotation selectors
selectByAnnoAndLabel, err := selectByAnnoAndLabel(n, t)
if err != nil {
return nil, err
}
if !selectByAnnoAndLabel {
continue
}
// filter targets by matching resource IDs
for _, id := range ids { for _, id := range ids {
if id.IsSelectedBy(t.Select.ResId) && !rejectId(t.Reject, &id) { if id.IsSelectedBy(t.Select.ResId) && !rejectId(t.Reject, &id) {
err := applyToNode(n, value, t) err := applyToNode(n, value, t)
@@ -62,9 +74,37 @@ func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targets []*types.T
return nodes, nil return nodes, nil
} }
func selectByAnnoAndLabel(n *yaml.RNode, t *types.TargetSelector) (bool, error) {
if matchesSelect, err := matchesAnnoAndLabelSelector(n, t.Select); !matchesSelect || err != nil {
return false, err
}
for _, reject := range t.Reject {
if reject.AnnotationSelector == "" && reject.LabelSelector == "" {
continue
}
if m, err := matchesAnnoAndLabelSelector(n, reject); m || err != nil {
return false, err
}
}
return true, nil
}
func matchesAnnoAndLabelSelector(n *yaml.RNode, selector *types.Selector) (bool, error) {
r := resource.Resource{RNode: *n}
annoMatch, err := r.MatchesAnnotationSelector(selector.AnnotationSelector)
if err != nil {
return false, err
}
labelMatch, err := r.MatchesLabelSelector(selector.LabelSelector)
if err != nil {
return false, err
}
return annoMatch && labelMatch, nil
}
func rejectId(rejects []*types.Selector, id *resid.ResId) bool { func rejectId(rejects []*types.Selector, id *resid.ResId) bool {
for _, r := range rejects { for _, r := range rejects {
if id.IsSelectedBy(r.ResId) { if !r.ResId.IsEmpty() && id.IsSelectedBy(r.ResId) {
return true return true
} }
} }

View File

@@ -1588,6 +1588,230 @@ data:
`, `,
expectedErr: "fieldPath `data.httpPort` is missing for replacement source ~G_~V_ConfigMap|~X|ports-from:data.httpPort", expectedErr: "fieldPath `data.httpPort` is missing for replacement source ~G_~V_ConfigMap|~X|ports-from:data.httpPort",
}, },
"annotationSelector": {
input: `apiVersion: v1
kind: Deployment
metadata:
name: deploy-1
annotations:
foo: bar-1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy-2
annotations:
foo: bar-2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
replacements: `replacements:
- source:
kind: Deployment
name: deploy-1
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
annotationSelector: foo=bar-1
fieldPaths:
- spec.template.spec.containers.1.image
`,
expected: `apiVersion: v1
kind: Deployment
metadata:
name: deploy-1
annotations:
foo: bar-1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:1.7.9
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy-2
annotations:
foo: bar-2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
},
"labelSelector": {
input: `apiVersion: v1
kind: Deployment
metadata:
name: deploy-1
labels:
foo: bar-1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy-2
labels:
foo: bar-2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
replacements: `replacements:
- source:
kind: Deployment
name: deploy-1
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
labelSelector: foo=bar-1
fieldPaths:
- spec.template.spec.containers.1.image
`,
expected: `apiVersion: v1
kind: Deployment
metadata:
name: deploy-1
labels:
foo: bar-1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:1.7.9
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy-2
labels:
foo: bar-2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
},
"reject via labelSelector": {
input: `apiVersion: v1
kind: Deployment
metadata:
name: deploy-1
labels:
foo: bar-1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy-2
labels:
foo: bar-2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
replacements: `replacements:
- source:
kind: Deployment
name: deploy-1
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
kind: Deployment
reject:
- labelSelector: foo=bar-2
fieldPaths:
- spec.template.spec.containers.1.image
`,
expected: `apiVersion: v1
kind: Deployment
metadata:
name: deploy-1
labels:
foo: bar-1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:1.7.9
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy-2
labels:
foo: bar-2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
},
} }
for tn, tc := range testCases { for tn, tc := range testCases {

View File

@@ -11,6 +11,6 @@ require (
github.com/stretchr/testify v1.5.1 github.com/stretchr/testify v1.5.1
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
sigs.k8s.io/kustomize/kyaml v0.12.0 sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@@ -223,8 +223,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4= sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -549,3 +549,26 @@ metadata:
name: testing-tt4769fb52 name: testing-tt4769fb52
`) `)
} }
// regression test for https://github.com/kubernetes-sigs/kustomize/issues/4233
func TestDataEndsWithQuotes(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: test
literals:
- TEST=this is a 'test'
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(
m, `apiVersion: v1
data:
TEST: this is a 'test'
kind: ConfigMap
metadata:
name: test-k9cc55dfm5
`)
}

View File

@@ -1646,3 +1646,64 @@ spec:
type: NodePort type: NodePort
`) `)
} }
// test for #4111
func TestPatchPreservesInternalAnnotations(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
nameSuffix: -abc
resources:
- fluentd.yaml
patchesJson6902:
- path: patch.yaml
target:
name: fluentd-sa
kind: ServiceAccount
version: v1
`)
th.WriteF("fluentd.yaml", `
apiVersion: v1
kind: DaemonSet
metadata:
name: fluentd
spec:
template:
spec:
containers:
- image: fluentd:latest
name: fluentd
serviceAccountName: fluentd-sa
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd-sa
`)
th.WriteF("patch.yaml", `
- op: add
path: /metadata/annotations
value:
note: this is a test annotation
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: DaemonSet
metadata:
name: fluentd-abc
spec:
template:
spec:
containers:
- image: fluentd:latest
name: fluentd
serviceAccountName: fluentd-sa-abc
---
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
note: this is a test annotation
name: fluentd-sa-abc
`)
}

View File

@@ -5,6 +5,7 @@ package krusty_test
import ( import (
"io/ioutil" "io/ioutil"
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -346,3 +347,109 @@ func TestCustomOpenAPIFieldFromComponent(t *testing.T) {
th.Run("prod", th.MakeDefaultOptions()) th.Run("prod", th.MakeDefaultOptions())
assert.Equal(t, "using custom schema from file provided", openapi.GetSchemaVersion()) assert.Equal(t, "using custom schema from file provided", openapi.GetSchemaVersion())
} }
// test for https://github.com/kubernetes-sigs/kustomize/issues/4179
// kustomize is not seeing the openapi field from the component defined in the overlay
func TestCustomOpenAPIFieldFromComponentWithOverlays(t *testing.T) {
if val, ok := os.LookupEnv("OPENAPI_TEST"); !ok || val != "true" {
t.SkipNow()
}
th := kusttest_test.MakeHarness(t)
// overlay declaring the component
th.WriteK("overlays/overlay-component-openapi", `resources:
- ../base/
components:
- ../../components/dc-openapi
`)
// base kustomization
th.WriteK("overlays/base", `resources:
- dc.yml
`)
// resource declared in the base kustomization
th.WriteF("overlays/base/dc.yml", `apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
name: my-dc
spec:
template:
spec:
initContainers:
- name: init
containers:
- name: container
env:
- name: foo
value: bar
volumeMounts:
- name: cm
mountPath: /opt/cm
volumes:
- name: cm
configMap:
name: cm
`)
// openapi schema referred to by the component
bytes, _ := ioutil.ReadFile("testdata/openshiftschema.json")
th.WriteF("components/dc-openapi/openapi.json", string(bytes))
// patch referred to by the component
th.WriteF("components/dc-openapi/patch.yml", `apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
name: my-dc
spec:
template:
spec:
containers:
- name: container
volumeMounts:
- name: additional-cm
mountPath: /mnt
volumes:
- name: additional-cm
configMap:
name: additional-cm
`)
// component declared in overlay with custom schema and patch
th.WriteC("components/dc-openapi", `patches:
- patch.yml
openapi:
path: openapi.json
`)
openapi.ResetOpenAPI()
m := th.Run("overlays/overlay-component-openapi", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
name: my-dc
spec:
template:
spec:
containers:
- env:
- name: foo
value: bar
name: container
volumeMounts:
- mountPath: /mnt
name: additional-cm
- mountPath: /opt/cm
name: cm
initContainers:
- name: init
volumes:
- configMap:
name: additional-cm
name: additional-cm
- configMap:
name: cm
name: cm
`)
}

View File

@@ -0,0 +1,76 @@
{
"definitions": {
"com.github.openshift.api.apps.v1.DeploymentConfig": {
"type": "object",
"required": [
"spec"
],
"properties": {
"apiVersion": {
"type": "string"
},
"kind": {
"type": "string"
},
"metadata": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
},
"spec": {
"$ref": "#/definitions/com.github.openshift.api.apps.v1.DeploymentConfigSpec"
}
},
"x-kubernetes-group-version-kind": [
{
"group": "apps.openshift.io",
"kind": "DeploymentConfig",
"version": "v1"
}
]
},
"com.github.openshift.api.apps.v1.DeploymentConfigSpec": {
"type": "object",
"properties": {
"template": {
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
}
}
},
"io.k8s.api.core.v1.Volume": {
"type": "object",
"required": [
"name"
],
"properties": {
"configMap": {
"$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapVolumeSource"
},
"name": {
"type": "string"
}
}
},
"io.k8s.api.core.v1.VolumeMount": {
"type": "object",
"required": [
"name",
"mountPath"
],
"properties": {
"mountPath": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
}
}

View File

@@ -209,5 +209,17 @@ func parseLiteralSource(source string) (keyName, value string, err error) {
if len(items) != 2 { if len(items) != 2 {
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source) return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
} }
return items[0], strings.Trim(items[1], "\"'"), nil return items[0], removeQuotes(items[1]), nil
}
// removeQuotes removes the surrounding quotes from the provided string only if it is surrounded on both sides
// rather than blindly trimming all quotation marks on either side.
func removeQuotes(str string) string {
if len(str) == 0 || str[0] != str[len(str)-1] {
return str
}
if str[0] == '"' || str[0] == '\'' {
return str[1 : len(str)-1]
}
return str
} }

View File

@@ -26,6 +26,7 @@ type Resource struct {
refVarNames []string refVarNames []string
} }
// nolint
var BuildAnnotations = []string{ var BuildAnnotations = []string{
utils.BuildAnnotationPreviousKinds, utils.BuildAnnotationPreviousKinds,
utils.BuildAnnotationPreviousNames, utils.BuildAnnotationPreviousNames,

View File

@@ -16,10 +16,10 @@ const DefaultReplacementFieldPath = "metadata.name"
// where it is from and where it is to. // where it is from and where it is to.
type Replacement struct { type Replacement struct {
// The source of the value. // The source of the value.
Source *SourceSelector `json:"source" yaml:"source"` Source *SourceSelector `json:"source,omitempty" yaml:"source,omitempty"`
// The N fields to write the value to. // The N fields to write the value to.
Targets []*TargetSelector `json:"targets" yaml:"targets"` Targets []*TargetSelector `json:"targets,omitempty" yaml:"targets,omitempty"`
} }
// SourceSelector is the source of the replacement transformer. // SourceSelector is the source of the replacement transformer.

View File

@@ -5,7 +5,7 @@ container workflow orchestrator including Tekton, Cloud Build, or run directly u
Run `config help docs-fn-spec` to see the Configuration Functions Specification. Run `config help docs-fn-spec` to see the Configuration Functions Specification.
`kustomize config run` is an example orchestrator for invoking Configuration Functions. This `kustomize fn run` is an example orchestrator for invoking Configuration Functions. This
document describes how to implement and invoke an example function. document describes how to implement and invoke an example function.
## Example Function Implementation ## Example Function Implementation
@@ -28,7 +28,7 @@ The script wraps itself using `config run wrap -- $0` which will:
```bash ```bash
#!/bin/bash #!/bin/bash
# script must run wrapped by "kustomize config run wrap" # script must run wrapped by "kustomize fn run wrap"
# for parsing input the functionConfig into env vars # for parsing input the functionConfig into env vars
if [ -z ${WRAPPED} ]; then if [ -z ${WRAPPED} ]; then
export WRAPPED=true export WRAPPED=true
@@ -82,7 +82,7 @@ End-of-message
### Dockerfile ### Dockerfile
`Dockerfile` installs `kustomize config` and copies the script into the container image. `Dockerfile` installs `kustomize fn` and copies the script into the container image.
``` ```
FROM golang:1.13-stretch FROM golang:1.13-stretch
@@ -94,9 +94,9 @@ CMD ["nginx-template.sh]
## Example Function Usage ## Example Function Usage
Following is an example of running the `kustomize config run` using the preceding API. Following is an example of running the `kustomize fn run` using the preceding API.
When run by `kustomize config run`, functions are run in containers with the When run by `kustomize fn run`, functions are run in containers with the
following environment: following environment:
- Network: `none` - Network: `none`
@@ -129,7 +129,7 @@ spec:
### Output ### Output
The function is invoked using byrunning `kustomize config run dir/`. The function is invoked using byrunning `kustomize fn run dir/`.
`dir/my-instance_deployment.yaml` contains the Deployment: `dir/my-instance_deployment.yaml` contains the Deployment:

View File

@@ -245,8 +245,8 @@ items:
labels: labels:
app: wordpress app: wordpress
annotations: annotations:
config.kubernetes.io/index: "0" internal.config.kubernetes.io/index: "0"
config.kubernetes.io/path: "service.yaml" internal.config.kubernetes.io/path: "service.yaml"
spec: # Example comment spec: # Example comment
type: LoadBalancer type: LoadBalancer
selector: selector:
@@ -271,8 +271,8 @@ items:
labels: labels:
app: wordpress app: wordpress
annotations: annotations:
config.kubernetes.io/index: "0" internal.config.kubernetes.io/index: "0"
config.kubernetes.io/path: "service.yaml" internal.config.kubernetes.io/path: "service.yaml"
spec: # Example comment spec: # Example comment
type: LoadBalancer type: LoadBalancer
selector: selector:

View File

@@ -2,7 +2,7 @@
### Synopsis ### Synopsis
`kustomize config` enables encapsulating function for manipulating Resource `kustomize fn` enables encapsulating function for manipulating Resource
configuration inside containers, which are run using `run`. configuration inside containers, which are run using `run`.
First fetch the kustomize repository, which contains a collection of example First fetch the kustomize repository, which contains a collection of example
@@ -14,7 +14,7 @@
### Templating -- CockroachDB ### Templating -- CockroachDB
This section demonstrates how to leverage templating based solutions from This section demonstrates how to leverage templating based solutions from
`kustomize config`. The templating function is implemented as a `bash` script `kustomize fn`. The templating function is implemented as a `bash` script
using a `heredoc`. using a `heredoc`.
#### 1: Generate the Resources #### 1: Generate the Resources

View File

@@ -16,5 +16,5 @@ require (
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/inf.v0 v0.9.1 gopkg.in/inf.v0 v0.9.1
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
sigs.k8s.io/kustomize/kyaml v0.12.0 sigs.k8s.io/kustomize/kyaml v0.13.0
) )

View File

@@ -245,8 +245,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4= sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -166,6 +166,7 @@ func (r *CatRunner) catFilters() []kio.Filter {
return fltrs return fltrs
} }
// nolint
func (r *CatRunner) out(w io.Writer) ([]kio.Writer, error) { func (r *CatRunner) out(w io.Writer) ([]kio.Writer, error) {
var outputs []kio.Writer var outputs []kio.Writer
var functionConfig *yaml.RNode var functionConfig *yaml.RNode

View File

@@ -14,7 +14,7 @@ import (
) )
const ( const (
input = `apiVersion: config.kubernetes.io/v1alpha1 input = `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
functionConfig: functionConfig:
metadata: metadata:
@@ -68,7 +68,7 @@ items:
name: test name: test
` `
output = `apiVersion: config.kubernetes.io/v1alpha1 output = `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -127,7 +127,7 @@ items:
targetPort: 8080 targetPort: 8080
` `
outputNoMerge = `apiVersion: config.kubernetes.io/v1alpha1 outputNoMerge = `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -183,7 +183,7 @@ items:
targetPort: 8080 targetPort: 8080
` `
outputOverride = `apiVersion: config.kubernetes.io/v1alpha1 outputOverride = `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1

View File

@@ -39,7 +39,7 @@ functionConfig:
- 4 - 4
` `
resourceInput = `apiVersion: config.kubernetes.io/v1alpha1 resourceInput = `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1

View File

@@ -46,6 +46,7 @@ See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
return err return err
} }
// nolint
func (r *SinkRunner) runE(c *cobra.Command, args []string) error { func (r *SinkRunner) runE(c *cobra.Command, args []string) error {
var outputs []kio.Writer var outputs []kio.Writer
if len(args) == 1 { if len(args) == 1 {

View File

@@ -22,7 +22,7 @@ func TestSinkCommand(t *testing.T) {
defer os.RemoveAll(d) defer os.RemoveAll(d)
r := commands.GetSinkRunner("") r := commands.GetSinkRunner("")
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1 r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -144,7 +144,7 @@ func TestSinkCommandJSON(t *testing.T) {
defer os.RemoveAll(d) defer os.RemoveAll(d)
r := commands.GetSinkRunner("") r := commands.GetSinkRunner("")
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1 r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", - {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo",
@@ -191,7 +191,7 @@ func TestSinkCommand_Stdout(t *testing.T) {
// fmt the files // fmt the files
out := &bytes.Buffer{} out := &bytes.Buffer{}
r := commands.GetSinkRunner("") r := commands.GetSinkRunner("")
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1 r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -305,7 +305,7 @@ func TestSinkCommandJSON_Stdout(t *testing.T) {
// fmt the files // fmt the files
out := &bytes.Buffer{} out := &bytes.Buffer{}
r := commands.GetSinkRunner("") r := commands.GetSinkRunner("")
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1 r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", - {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo",

View File

@@ -81,7 +81,7 @@ spec:
return return
} }
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1 if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -199,7 +199,7 @@ func TestSourceCommandJSON(t *testing.T) {
return return
} }
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1 if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f1.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}} - {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f1.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}}
@@ -246,7 +246,7 @@ spec:
return return
} }
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1 if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -309,7 +309,7 @@ func TestSourceCommandJSON_Stdin(t *testing.T) {
return return
} }
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1 if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', internal.config.kubernetes.io/index: '0'}}, "spec": {"replicas": 1}} - {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', internal.config.kubernetes.io/index: '0'}}, "spec": {"replicas": 1}}

View File

@@ -61,15 +61,16 @@ Example:
annotations: annotations:
config.kubernetes.io/local-config: "true"` config.kubernetes.io/local-config: "true"`
var FunctionsImplShort = `Following is an example for implementing an nginx abstraction using a configuration` var (
var FunctionsImplLong = `# Running Configuration Functions using kustomize CLI FunctionsImplShort = `Following is an example for implementing an nginx abstraction using a configuration`
FunctionsImplLong = `# Running Configuration Functions using kustomize CLI
Configuration functions can be implemented using any toolchain and invoked using any Configuration functions can be implemented using any toolchain and invoked using any
container workflow orchestrator including Tekton, Cloud Build, or run directly using ` + "`" + `docker run` + "`" + `. container workflow orchestrator including Tekton, Cloud Build, or run directly using ` + "`" + `docker run` + "`" + `.
Run ` + "`" + `config help docs-fn-spec` + "`" + ` to see the Configuration Functions Specification. Run ` + "`" + `config help docs-fn-spec` + "`" + ` to see the Configuration Functions Specification.
` + "`" + `kustomize config run` + "`" + ` is an example orchestrator for invoking Configuration Functions. This ` + "`" + `kustomize fn run` + "`" + ` is an example orchestrator for invoking Configuration Functions. This
document describes how to implement and invoke an example function. document describes how to implement and invoke an example function.
function. function.
@@ -88,7 +89,7 @@ The script wraps itself using ` + "`" + `config run wrap -- $0` + "`" + ` which
4. Format the output 4. Format the output
#!/bin/bash #!/bin/bash
# script must run wrapped by "kustomize config run wrap" # script must run wrapped by "kustomize fn run wrap"
# for parsing input the functionConfig into env vars # for parsing input the functionConfig into env vars
if [ -z ${WRAPPED} ]; then if [ -z ${WRAPPED} ]; then
export WRAPPED=true export WRAPPED=true
@@ -141,7 +142,7 @@ The script wraps itself using ` + "`" + `config run wrap -- $0` + "`" + ` which
### Dockerfile ### Dockerfile
` + "`" + `Dockerfile` + "`" + ` installs ` + "`" + `kustomize config` + "`" + ` and copies the script into the container image. ` + "`" + `Dockerfile` + "`" + ` installs ` + "`" + `kustomize fn` + "`" + ` and copies the script into the container image.
FROM golang:1.13-stretch FROM golang:1.13-stretch
RUN go get sigs.k8s.io/kustomize/cmd/config RUN go get sigs.k8s.io/kustomize/cmd/config
@@ -151,9 +152,9 @@ The script wraps itself using ` + "`" + `config run wrap -- $0` + "`" + ` which
## Example Function Usage ## Example Function Usage
Following is an example of running the ` + "`" + `kustomize config run` + "`" + ` using the preceding API. Following is an example of running the ` + "`" + `kustomize fn run` + "`" + ` using the preceding API.
When run by ` + "`" + `kustomize config run` + "`" + `, functions are run in containers with the When run by ` + "`" + `kustomize fn run` + "`" + `, functions are run in containers with the
following environment: following environment:
- Network: ` + "`" + `none` + "`" + ` - Network: ` + "`" + `none` + "`" + `
@@ -184,7 +185,7 @@ are passed to the Function through the ` + "`" + `ResourceList.functionConfig` +
### Output ### Output
The function is invoked using byrunning ` + "`" + `kustomize config run dir/` + "`" + `. The function is invoked using byrunning ` + "`" + `kustomize fn run dir/` + "`" + `.
` + "`" + `dir/my-instance_deployment.yaml` + "`" + ` contains the Deployment: ` + "`" + `dir/my-instance_deployment.yaml` + "`" + ` contains the Deployment:
@@ -230,9 +231,11 @@ The function is invoked using byrunning ` + "`" + `kustomize config run dir/` +
selector: selector:
app: nginx app: nginx
instance: my-instance` instance: my-instance`
)
var FunctionsSpecShort = `_Configuration functions_ enable shift-left practices (client-side) through:` var (
var FunctionsSpecLong = `# Configuration Functions Specification FunctionsSpecShort = `_Configuration functions_ enable shift-left practices (client-side) through:`
FunctionsSpecLong = `# Configuration Functions Specification
This document specifies a standard for client-side functions that operate on This document specifies a standard for client-side functions that operate on
Kubernetes declarative configurations. This standard enables creating Kubernetes declarative configurations. This standard enables creating
@@ -408,6 +411,7 @@ A non-zero exit code indicates a failure.
[1]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md [1]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[2]: https://tools.ietf.org/html/rfc2119 [2]: https://tools.ietf.org/html/rfc2119
[3]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds` [3]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds`
)
var Merge2Long = `# Merge (2-way) var Merge2Long = `# Merge (2-way)

View File

@@ -2,6 +2,7 @@
package tutorials package tutorials
var ConfigurationBasicsShort = `### Synopsis` var ConfigurationBasicsShort = `### Synopsis`
var ConfigurationBasicsLong = ` var ConfigurationBasicsLong = `
` + "`" + `kustomize cfg` + "`" + ` provides tools for working with local configuration directories. ` + "`" + `kustomize cfg` + "`" + ` provides tools for working with local configuration directories.
@@ -274,9 +275,10 @@ var ConfigurationBasicsLong = `
│   └── image: <YOUR-CONTAINER> │   └── image: <YOUR-CONTAINER>
...` ...`
var FunctionBasicsShort = `### Synopsis` var (
var FunctionBasicsLong = ` FunctionBasicsShort = `### Synopsis`
` + "`" + `kustomize config` + "`" + ` enables encapsulating function for manipulating Resource FunctionBasicsLong = `
` + "`" + `kustomize fn` + "`" + ` enables encapsulating function for manipulating Resource
configuration inside containers, which are run using ` + "`" + `run` + "`" + `. configuration inside containers, which are run using ` + "`" + `run` + "`" + `.
First fetch the kustomize repository, which contains a collection of example First fetch the kustomize repository, which contains a collection of example
@@ -288,7 +290,7 @@ var FunctionBasicsLong = `
### Templating -- CockroachDB ### Templating -- CockroachDB
This section demonstrates how to leverage templating based solutions from This section demonstrates how to leverage templating based solutions from
` + "`" + `kustomize config` + "`" + `. The templating function is implemented as a ` + "`" + `bash` + "`" + ` script ` + "`" + `kustomize fn` + "`" + `. The templating function is implemented as a ` + "`" + `bash` + "`" + ` script
using a ` + "`" + `heredoc` + "`" + `. using a ` + "`" + `heredoc` + "`" + `.
#### 1: Generate the Resources #### 1: Generate the Resources
@@ -455,3 +457,4 @@ Functions may be composed together. Try putting the Injection (tshirt-size) and
Validation functions together in the same .yaml file (separated by ` + "`" + `---` + "`" + `). Run Validation functions together in the same .yaml file (separated by ` + "`" + `---` + "`" + `). Run
` + "`" + `run` + "`" + ` and observe that the first function in the file is applied to the Resources, ` + "`" + `run` + "`" + ` and observe that the first function in the file is applied to the Resources,
and then the second function in the file is applied.` and then the second function in the file is applied.`
)

View File

@@ -129,7 +129,7 @@ var ExitOnError bool
// StackOnError if true, will print a stack trace on failure. // StackOnError if true, will print a stack trace on failure.
var StackOnError bool var StackOnError bool
const cmdName = "kustomize config" const cmdName = "kustomize fn"
// FixDocs replaces instances of old with new in the docs for c // FixDocs replaces instances of old with new in the docs for c
func FixDocs(new string, c *cobra.Command) { func FixDocs(new string, c *cobra.Command) {

View File

@@ -6,8 +6,6 @@ require (
github.com/rakyll/statik v0.1.7 github.com/rakyll/statik v0.1.7
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.5.1 github.com/stretchr/testify v1.5.1
sigs.k8s.io/kustomize/api v0.9.0 sigs.k8s.io/kustomize/api v0.10.1
sigs.k8s.io/kustomize/kyaml v0.12.0 sigs.k8s.io/kustomize/kyaml v0.13.0
) )
replace sigs.k8s.io/kustomize/api => ../../api

View File

@@ -228,8 +228,10 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4= sigs.k8s.io/kustomize/api v0.10.1 h1:KgU7hfYoscuqag84kxtzKdEC3mKMb99DPI3a0eaV1d0=
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=
sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -74,7 +74,7 @@ func (p *plugin) Transform(rm resmap.ResMap) error {
func getTransformerInputResource() []byte { func getTransformerInputResource() []byte {
return []byte(` return []byte(`
apiVersion: config.kubernetes.io/v1beta1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
functionConfig: functionConfig:
apiVersion: foo-corp.com/v1 apiVersion: foo-corp.com/v1
@@ -128,7 +128,7 @@ func TestTransformerConverter(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
output := runKrmFunction(t, getTransformerInputResource(), filepath.Join(dir, "output")) output := runKrmFunction(t, getTransformerInputResource(), filepath.Join(dir, "output"))
assert.Equal(t, `apiVersion: config.kubernetes.io/v1beta1 assert.Equal(t, `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -193,7 +193,7 @@ func (p *plugin) Generate() (resmap.ResMap, error) {
func getGeneratorInputResource() []byte { func getGeneratorInputResource() []byte {
return []byte(` return []byte(`
apiVersion: config.kubernetes.io/v1beta1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
functionConfig: functionConfig:
apiVersion: foo-corp.com/v1 apiVersion: foo-corp.com/v1
@@ -224,7 +224,7 @@ func TestGeneratorConverter(t *testing.T) {
err := c.Convert() err := c.Convert()
assert.NoError(t, err) assert.NoError(t, err)
output := runKrmFunction(t, getGeneratorInputResource(), filepath.Join(dir, "output")) output := runKrmFunction(t, getGeneratorInputResource(), filepath.Join(dir, "output"))
assert.Equal(t, `apiVersion: config.kubernetes.io/v1beta1 assert.Equal(t, `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1

View File

@@ -23,14 +23,12 @@ Kustomize provides two ways of adding ConfigMap in one `kustomization`, either b
The ConfigMaps declared as [resource] are treated the same way as other resources. Kustomize doesn't append any hash to the ConfigMap name. The ConfigMap declared from a ConfigMapGenerator is treated differently. A hash is appended to the name and any change in the ConfigMap will trigger a rolling update. The ConfigMaps declared as [resource] are treated the same way as other resources. Kustomize doesn't append any hash to the ConfigMap name. The ConfigMap declared from a ConfigMapGenerator is treated differently. A hash is appended to the name and any change in the ConfigMap will trigger a rolling update.
In this demo, the same [hello_world](helloWorld/README.md) is used while the ConfigMap declared as [resources] is replaced by a ConfigMap declared from a ConfigmapGenerator. The change in this ConfigMap will result in a hash change and a rolling update. In this demo, the same [hello_world](helloWorld/README.md) is used while the ConfigMap declared as [resources] is replaced by a ConfigMap declared from a ConfigMapGenerator. The change in this ConfigMap will result in a hash change and a rolling update.
### Establish base and staging ### Establish base and staging
Establish the base with a configMapGenerator Establish the base with a `configMapGenerator`:
<!-- @establishBase @testAgainstLatestRelease --> <!-- @establishBase @testAgainstLatestRelease -->
``` ```
DEMO_HOME=$(mktemp -d) DEMO_HOME=$(mktemp -d)
@@ -92,7 +90,7 @@ EOF
### Review ### Review
The _hello-world_ deployment running in this cluster is The _hello-world_ deployment running in this cluster is
configured with data from a configMap. configured with data from a ConfigMap.
The deployment refers to this map by name: The deployment refers to this map by name:
@@ -102,26 +100,26 @@ The deployment refers to this map by name:
grep -C 2 configMapKeyRef $BASE/deployment.yaml grep -C 2 configMapKeyRef $BASE/deployment.yaml
``` ```
Changing the data held by a live configMap in a cluster Changing the data held by a live ConfigMap in a cluster
is considered bad practice. Deployments have no means is considered bad practice. Deployments have no means
to know that the configMaps they refer to have to know that the ConfigMaps they refer to have
changed, so such updates have no effect. changed, so such updates have no effect.
The recommended way to change a deployment's The recommended way to change a deployment's
configuration is to configuration is to
1. create a new configMap with a new name, 1. create a new ConfigMap with a new name,
1. patch the _deployment_, modifying the name value of 1. patch the _deployment_, modifying the name value of
the appropriate `configMapKeyRef` field. the appropriate `configMapKeyRef` field.
This latter change initiates rolling update to the pods This latter change initiates rolling update to the pods
in the deployment. The older configMap, when no longer in the deployment. The older ConfigMap, when no longer
referenced by any other resource, is eventually [garbage referenced by any other resource, is eventually [garbage
collected](/../../issues/242). collected](/../../issues/242).
### How this works with kustomize ### How this works with kustomize
The _staging_ [variant] here has a configMap [patch]: The _staging_ [variant] here has a ConfigMap [patch]:
<!-- @showMapPatch @testAgainstLatestRelease --> <!-- @showMapPatch @testAgainstLatestRelease -->
@@ -133,7 +131,7 @@ This patch is by definition a named but not necessarily
complete resource spec intended to modify a complete complete resource spec intended to modify a complete
resource spec. resource spec.
The ConfigMap it modifies is declared from a configMapGenerator. The ConfigMap it modifies is declared from a `configMapGenerator`.
<!-- @showMapBase @testAgainstLatestRelease --> <!-- @showMapBase @testAgainstLatestRelease -->
@@ -156,15 +154,15 @@ kustomize build $OVERLAYS/staging |\
grep -B 8 -A 1 staging-the-map grep -B 8 -A 1 staging-the-map
``` ```
The configMap name is prefixed by _staging-_, per the The ConfigMap name is prefixed by _staging-_, per the
`namePrefix` field in `namePrefix` field in
`$OVERLAYS/staging/kustomization.yaml`. `$OVERLAYS/staging/kustomization.yaml`.
The configMap name is suffixed by _-v1_, per the The ConfigMap name is suffixed by _-v1_, per the
`nameSuffix` field in `nameSuffix` field in
`$OVERLAYS/staging/kustomization.yaml`. `$OVERLAYS/staging/kustomization.yaml`.
The suffix to the configMap name is generated from a The suffix to the ConfigMap name is generated from a
hash of the maps content - in this case the name suffix hash of the maps content - in this case the name suffix
is _5276h4th55_: is _5276h4th55_:
@@ -190,7 +188,7 @@ kustomize build $OVERLAYS/staging |\
grep -B 2 -A 3 kiwi grep -B 2 -A 3 kiwi
``` ```
Run kustomize again to see the new configMap names: Run kustomize again to see the new ConfigMap names:
<!-- @grepStagingName @testAgainstLatestRelease --> <!-- @grepStagingName @testAgainstLatestRelease -->
@@ -199,9 +197,9 @@ kustomize build $OVERLAYS/staging |\
grep -B 8 -A 1 staging-the-map grep -B 8 -A 1 staging-the-map
``` ```
Confirm that the change in configMap content resulted Confirm that the change in ConfigMap content resulted
in three new names ending in _c2g8fcbf88_ - one in the in three new names ending in _c2g8fcbf88_ - one in the
configMap name itself, and two in the deployment that ConfigMap name itself, and two in the deployment that
uses the map: uses the map:
<!-- @countHashes @testAgainstLatestRelease --> <!-- @countHashes @testAgainstLatestRelease -->

View File

@@ -148,7 +148,7 @@ commonLabels:
org: acmeCorporation org: acmeCorporation
commonAnnotations: commonAnnotations:
note: Hello, I am staging! note: Hello, I am staging!
bases: resources:
- ../../base - ../../base
patchesStrategicMerge: patchesStrategicMerge:
- map.yaml - map.yaml
@@ -189,7 +189,7 @@ commonLabels:
org: acmeCorporation org: acmeCorporation
commonAnnotations: commonAnnotations:
note: Hello, I am production! note: Hello, I am production!
bases: resources:
- ../../base - ../../base
patchesStrategicMerge: patchesStrategicMerge:
- deployment.yaml - deployment.yaml

View File

@@ -22,17 +22,17 @@ a _target selector_:
> group: <optional group> > group: <optional group>
> version: <optional version> > version: <optional version>
> kind: <optional kind> > kind: <optional kind>
> name: <optional name> > name: <optional name or regex pattern>
> namespace: <optional namespace> > namespace: <optional namespace>
> labelSelector: <optional label selector> > labelSelector: <optional label selector>
> annotationSelector: <optional annotation selector> > annotationSelector: <optional annotation selector>
> ``` > ```
E.g. select resources with _name_ matching `foo*`: E.g. select resources with _name_ matching the regular expression `foo.*`:
> ```yaml > ```yaml
> target: > target:
> name: foo* > name: foo.*
> ``` > ```
Select all resources of _kind_ `Deployment`: Select all resources of _kind_ `Deployment`:

View File

@@ -17,7 +17,7 @@ Resource configuration, and looks for invalid configuration.
The function is invoked by authoring a [local Resource](local-resource) The function is invoked by authoring a [local Resource](local-resource)
with `metadata.annotations.[config.kubernetes.io/function]` and running: with `metadata.annotations.[config.kubernetes.io/function]` and running:
kustomize config run local-resource/ --fn-path config/ kustomize fn run local-resource/ --fn-path config/
This exits non-zero if there is an error. This exits non-zero if there is an error.
@@ -25,6 +25,6 @@ This exits non-zero if there is an error.
Run the validator with: Run the validator with:
kustomize config run local-resource/ --fn-path config/ kustomize fn run local-resource/ --fn-path config/
This will append an Application CR. This will append an Application CR.

View File

@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package main implements adding an Application CR to a group of resources and // Package main implements adding an Application CR to a group of resources and
// is run with `kustomize config run -- DIR/`. // is run with `kustomize fn run -- DIR/`.
package main package main
import ( import (
@@ -82,7 +82,6 @@ func (f appCRFilter) Filter(in []*yaml.RNode) ([]*yaml.RNode, error) {
return append(in, app), nil return append(in, app), nil
} }
return in, nil return in, nil
} }
// parseAPI parses the functionConfig into an API struct. // parseAPI parses the functionConfig into an API struct.

View File

@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package main implements adding an Application CR to a group of resources and // Package main implements adding an Application CR to a group of resources and
// is run with `kustomize config run -- DIR/`. // is run with `kustomize fn run -- DIR/`.
package main package main
import ( import (
@@ -13,7 +13,7 @@ import (
"testing" "testing"
) )
var input = `apiVersion: config.kubernetes.io/v1alpha1 var input = `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -89,7 +89,7 @@ functionConfig:
url: https://metrics/internal/worldpress-01/web-app url: https://metrics/internal/worldpress-01/web-app
` `
var output = `apiVersion: config.kubernetes.io/v1alpha1 var output = `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1

View File

@@ -17,7 +17,7 @@ Resource configuration, and looks for invalid configuration.
The function is invoked by authoring a [local Resource](local-resource) The function is invoked by authoring a [local Resource](local-resource)
with `metadata.annotations.[config.kubernetes.io/function]` and running: with `metadata.annotations.[config.kubernetes.io/function]` and running:
kustomize config run local-resource/ kustomize fn run local-resource/
This exits non-zero if there is an error. This exits non-zero if there is an error.
@@ -25,11 +25,11 @@ This exits non-zero if there is an error.
Run the validator with: Run the validator with:
kustomize config run local-resource/ kustomize fn run local-resource/
This will add resource reservations to the Deployment. Change the `tshirt-size` This will add resource reservations to the Deployment. Change the `tshirt-size`
annotation from `medium` to `small` and rerun: annotation from `medium` to `small` and rerun:
kustomize config run local-resource/ kustomize fn run local-resource/
Observe that the reservations have changed. Observe that the reservations have changed.

View File

@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package main implements an injection function for resource reservations and // Package main implements an injection function for resource reservations and
// is run with `kustomize config run -- DIR/`. // is run with `kustomize fn run -- DIR/`.
package main package main
import ( import (

View File

@@ -27,7 +27,7 @@ function input, and writing the function output.
The function is invoked by authoring a [local Resource](local-resource) The function is invoked by authoring a [local Resource](local-resource)
with `metadata.annotations.[config.kubernetes.io/function]` and running: with `metadata.annotations.[config.kubernetes.io/function]` and running:
kustomize config run local-resource/ kustomize fn run local-resource/
This generates the `local-resource/config` directory containing the template output. This generates the `local-resource/config` directory containing the template output.
@@ -41,7 +41,7 @@ This generates the `local-resource/config` directory containing the template out
Run the config with: Run the config with:
kustomize config run local-resource/ kustomize fn run local-resource/
This will create the directory This will create the directory
@@ -50,6 +50,6 @@ This will create the directory
Add an annotation to the Deployment Resource and change the replica count of the Add an annotation to the Deployment Resource and change the replica count of the
`kind: Nginx` Resource in `example-use.yaml`. Rerun the template: `kind: Nginx` Resource in `example-use.yaml`. Rerun the template:
kustomize config run local-resource/ kustomize fn run local-resource/
The replica count should be updated, but your annotation should remain. The replica count should be updated, but your annotation should remain.

View File

@@ -1,7 +1,7 @@
# Copyright 2019 The Kubernetes Authors. # Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
apiVersion: examples.config.kubernetes.io/v1beta1 # call `kustomize config run` on a directory containing this file apiVersion: examples.config.kubernetes.io/v1beta1 # call `kustomize fn run` on a directory containing this file
kind: Nginx kind: Nginx
metadata: metadata:
name: demo name: demo

View File

@@ -11,7 +11,7 @@ The function is implemented as an [image](image), and built using `make image`.
The template is implemented as a heredoc, which substitutes environment variables The template is implemented as a heredoc, which substitutes environment variables
into a static string. into a static string.
This simple implementation uses `kustomize config run wrap --` to perform the This simple implementation uses `kustomize fn run wrap --` to perform the
heavy lifting of implementing the function interface. heavy lifting of implementing the function interface.
- parse functionConfig from stdin into environment variables - parse functionConfig from stdin into environment variables
@@ -22,7 +22,7 @@ heavy lifting of implementing the function interface.
The function is invoked by authoring a [local Resource](local-resource) The function is invoked by authoring a [local Resource](local-resource)
with `metadata.annotations.[config.kubernetes.io/function]` and running: with `metadata.annotations.[config.kubernetes.io/function]` and running:
kustomize config run local-resource/ kustomize fn run local-resource/
This generates the `local-resource/config` directory containing the template output. This generates the `local-resource/config` directory containing the template output.
@@ -36,7 +36,7 @@ This generates the `local-resource/config` directory containing the template out
Run the config with: Run the config with:
kustomize config run local-resource/ kustomize fn run local-resource/
This will create the directory This will create the directory
@@ -45,6 +45,6 @@ This will create the directory
Add an annotation to the StatefulSet Resource and change the replica count of the Add an annotation to the StatefulSet Resource and change the replica count of the
`kind: CockroachDB` Resource in `example-use.yaml`. Rerun the template: `kind: CockroachDB` Resource in `example-use.yaml`. Rerun the template:
kustomize config run local-resource/ kustomize fn run local-resource/
The replica count should be updated, but your annotation should remain. The replica count should be updated, but your annotation should remain.

View File

@@ -3,18 +3,18 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# use `kustomize config run wrap` to parse the container stdin into # use `kustomize fn run wrap` to parse the container stdin into
# environment variables, and to merge the template output into the # environment variables, and to merge the template output into the
# input Resources. # input Resources.
if [ -z ${WRAPPED} ]; then if [ -z ${WRAPPED} ]; then
export WRAPPED=true export WRAPPED=true
kustomize config run wrap -- $0 kustomize fn run wrap -- $0
exit $? exit $?
fi fi
# this is the template for a cockroachdb instance # this is the template for a cockroachdb instance
# environment variables are parsed from the input functionConfig by # environment variables are parsed from the input functionConfig by
# `kustomize config run wrap` # `kustomize fn run wrap`
cat <<End-of-message cat <<End-of-message
apiVersion: v1 apiVersion: v1
kind: Service kind: Service

View File

@@ -1,7 +1,7 @@
# Copyright 2019 The Kubernetes Authors. # Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# call `kustomize config run` on a directory containing this file # call `kustomize fn run` on a directory containing this file
apiVersion: examples.config.kubernetes.io/v1beta1 apiVersion: examples.config.kubernetes.io/v1beta1
kind: CockroachDB kind: CockroachDB
metadata: metadata:

View File

@@ -20,7 +20,7 @@ the `API` struct definition in [main.go](image/main.go) for documentation.
The function is invoked by authoring a [local Resource](local-resource) The function is invoked by authoring a [local Resource](local-resource)
with `metadata.annotations.[config.kubernetes.io/function]` and running: with `metadata.annotations.[config.kubernetes.io/function]` and running:
kustomize config run local-resource/ kustomize fn run local-resource/
This exists non-zero if kubeval detects an invalid Resource. This exists non-zero if kubeval detects an invalid Resource.
@@ -28,7 +28,7 @@ This exists non-zero if kubeval detects an invalid Resource.
Run the validator with: Run the validator with:
kustomize config run local-resource/ kustomize fn run local-resource/
This will return an error: This will return an error:
@@ -39,6 +39,6 @@ This will return an error:
Now fix the typo in [example-use.yaml](local-resource/example-use.yaml) and Now fix the typo in [example-use.yaml](local-resource/example-use.yaml) and
run: run:
kustomize config run local-resource/ kustomize fn run local-resource/
This will return success (no output). This will return success (no output).

View File

@@ -1,7 +1,7 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package main implements a validator function run by `kustomize config run` // Package main implements a validator function run by `kustomize fn run`
package main package main
import ( import (

View File

@@ -17,7 +17,7 @@ Resource configuration, and looks for invalid configuration.
The function is invoked by authoring a [local Resource](local-resource) The function is invoked by authoring a [local Resource](local-resource)
with `metadata.annotations.[config.kubernetes.io/function]` and running: with `metadata.annotations.[config.kubernetes.io/function]` and running:
kustomize config run local-resource/ kustomize fn run local-resource/
This exists non-zero if there is an error. This exists non-zero if there is an error.
@@ -25,7 +25,7 @@ This exists non-zero if there is an error.
Run the validator with: Run the validator with:
kustomize config run local-resource/ kustomize fn run local-resource/
This will return an error: This will return an error:
@@ -33,6 +33,6 @@ This will return an error:
Now uncomment the resource reservations and run again: Now uncomment the resource reservations and run again:
kustomize config run local-resource/ kustomize fn run local-resource/
This will return success This will return success

View File

@@ -1,7 +1,7 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package main implements a validator function run by `kustomize config run` // Package main implements a validator function run by `kustomize fn run`
package main package main
import ( import (
@@ -18,7 +18,8 @@ func main() {
p := kio.Pipeline{ p := kio.Pipeline{
Inputs: []kio.Reader{rw}, // read the inputs into a slice Inputs: []kio.Reader{rw}, // read the inputs into a slice
Filters: []kio.Filter{filter{}}, // run the filter against the inputs Filters: []kio.Filter{filter{}}, // run the filter against the inputs
Outputs: []kio.Writer{rw}} // copy the inputs to the output Outputs: []kio.Writer{rw}, // copy the inputs to the output
}
if err := p.Execute(); err != nil { if err := p.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1) os.Exit(1)
@@ -59,7 +60,6 @@ func validate(r *yaml.RNode) error {
// visit each container in the list and validate // visit each container in the list and validate
return containers.VisitElements(func(node *yaml.RNode) error { return containers.VisitElements(func(node *yaml.RNode) error {
// check cpu is non-nil // check cpu is non-nil
f, err := node.Pipe(yaml.Lookup("resources", "requests", "cpu")) f, err := node.Pipe(yaml.Lookup("resources", "requests", "cpu"))
if err != nil { if err != nil {

View File

@@ -102,7 +102,14 @@ elif [[ "$OSTYPE" == darwin* ]]; then
opsys=darwin opsys=darwin
fi fi
RELEASE_URL=$(curl -s $release_url |\ releases=$(curl -s $release_url)
if [[ $releases == *"API rate limit exceeded"* ]]; then
echo "Github rate-limiter failed the request. Either authenticate or wait a couple of minutes."
exit 1
fi
RELEASE_URL=$(echo "${releases}" |\
grep browser_download.*${opsys}_${arch} |\ grep browser_download.*${opsys}_${arch} |\
cut -d '"' -f 4 |\ cut -d '"' -f 4 |\
sort -V | tail -n 1) sort -V | tail -n 1)

View File

@@ -33,8 +33,27 @@ func TestAddResourceHappyPath(t *testing.T) {
assert.NoError(t, cmd.RunE(cmd, args)) assert.NoError(t, cmd.RunE(cmd, args))
content, err := testutils_test.ReadTestKustomization(fSys) content, err := testutils_test.ReadTestKustomization(fSys)
assert.NoError(t, err) assert.NoError(t, err)
assert.Contains(t, string(content), resourceFileName) assert.Equal(t, string(content), `apiVersion: kustomize.config.k8s.io/v1beta1
assert.Contains(t, string(content), resourceFileName+"another") kind: Kustomization
namePrefix: some-prefix
nameSuffix: some-suffix
# Labels to add to all objects and selectors.
# These labels would also be used to form the selector for apply --prune
# Named differently than “labels” to avoid confusion with metadata for this object
commonLabels:
app: helloworld
commonAnnotations:
note: This is an example annotation
resources:
- myWonderfulResource.yaml
- myWonderfulResource.yamlanother
#- service.yaml
#- ../some-dir/
# There could also be configmaps in Base, which would make these overlays
# There could be secrets in Base, if just using a fork/rebase workflow
replacements:
- path: replacement.yaml
`)
} }
func TestAddResourceAlreadyThere(t *testing.T) { func TestAddResourceAlreadyThere(t *testing.T) {

View File

@@ -28,6 +28,8 @@ resources: []
configMapGenerator: [] configMapGenerator: []
# There could be secrets in Base, if just using a fork/rebase workflow # There could be secrets in Base, if just using a fork/rebase workflow
secretGenerator: [] secretGenerator: []
replacements:
- path: replacement.yaml
` `
) )

View File

@@ -8,9 +8,9 @@ require (
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
sigs.k8s.io/kustomize/api v0.9.0 sigs.k8s.io/kustomize/api v0.10.1
sigs.k8s.io/kustomize/cmd/config v0.10.1 sigs.k8s.io/kustomize/cmd/config v0.10.2
sigs.k8s.io/kustomize/kyaml v0.12.0 sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
@@ -18,5 +18,3 @@ exclude (
sigs.k8s.io/kustomize/api v0.2.0 sigs.k8s.io/kustomize/api v0.2.0
sigs.k8s.io/kustomize/cmd/config v0.2.0 sigs.k8s.io/kustomize/cmd/config v0.2.0
) )
replace sigs.k8s.io/kustomize/api => ../api

View File

@@ -253,10 +253,12 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/cmd/config v0.10.1 h1:eqpN9eUSn3XIfvPabit8lpIqUbWKS7f4lOB4D2cr5CQ= sigs.k8s.io/kustomize/api v0.10.1 h1:KgU7hfYoscuqag84kxtzKdEC3mKMb99DPI3a0eaV1d0=
sigs.k8s.io/kustomize/cmd/config v0.10.1/go.mod h1:9W5pDv3cgDfMjOXEga4yC9lUpkgAaecW+lZmHOMeX2I= sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4= sigs.k8s.io/kustomize/cmd/config v0.10.2 h1:2GD3+knDaqZo6rSibkc4kKGp8auNBJrGPZQCTWN4Rtc=
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ=
sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -1,6 +1,7 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
//go:build !windows
// +build !windows // +build !windows
package filesys package filesys

View File

@@ -612,6 +612,7 @@ func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
// This is how /bin/ls behaves. // This is how /bin/ls behaves.
func (n *fsNode) Glob(pattern string) ([]string, error) { func (n *fsNode) Glob(pattern string) ([]string, error) {
var result []string var result []string
var allFiles []string
err := n.WalkMe(func(path string, info os.FileInfo, err error) error { err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
@@ -622,7 +623,7 @@ func (n *fsNode) Glob(pattern string) ([]string, error) {
return err return err
} }
if match { if match {
result = append(result, path) allFiles = append(allFiles, path)
} }
} }
return nil return nil
@@ -630,6 +631,11 @@ func (n *fsNode) Glob(pattern string) ([]string, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if IsHiddenFilePath(pattern) {
result = allFiles
} else {
result = RemoveHiddenFiles(allFiles)
}
sort.Strings(result) sort.Strings(result)
return result, nil return result, nil
} }

View File

@@ -1,6 +1,7 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
//go:build !windows
// +build !windows // +build !windows
package filesys package filesys
@@ -463,6 +464,13 @@ var bunchOfFiles = []struct {
{ {
path: filepath.Join("b", "d", "a", "c", "u"), path: filepath.Join("b", "d", "a", "c", "u"),
}, },
{
path: filepath.Join("b", "d", ".hidden_file"),
},
{
path: filepath.Join("b", "d", ".hidden_dir"),
addAsDir: true,
},
} }
func makeLoadedFileTree(t *testing.T) *fsNode { func makeLoadedFileTree(t *testing.T) *fsNode {
@@ -579,6 +587,7 @@ func TestExists(t *testing.T) {
func TestRegExpGlob(t *testing.T) { func TestRegExpGlob(t *testing.T) {
n := makeLoadedFileTree(t) n := makeLoadedFileTree(t)
expected := []string{ expected := []string{
filepath.Join("b", "d", ".hidden_file"),
filepath.Join("b", "d", "a", "c", "i", "beans"), filepath.Join("b", "d", "a", "c", "i", "beans"),
filepath.Join("b", "d", "a", "c", "m"), filepath.Join("b", "d", "a", "c", "m"),
filepath.Join("b", "d", "a", "c", "u"), filepath.Join("b", "d", "a", "c", "u"),
@@ -598,16 +607,36 @@ func TestRegExpGlob(t *testing.T) {
func TestGlob(t *testing.T) { func TestGlob(t *testing.T) {
n := makeLoadedFileTree(t) n := makeLoadedFileTree(t)
expected := []string{
filepath.Join("b", "d", "x"), tests := map[string]struct {
filepath.Join("b", "d", "y"), globPattern string
filepath.Join("b", "d", "z"), expectedFiles []string
}{
"VisibleFiles": {
globPattern: "b/d/*",
expectedFiles: []string{
filepath.Join("b", "d", "x"),
filepath.Join("b", "d", "y"),
filepath.Join("b", "d", "z"),
},
},
"HiddenFiles": {
globPattern: "b/d/.*",
expectedFiles: []string{
filepath.Join("b", "d", ".hidden_file"),
},
},
} }
paths, err := n.Glob("b/d/*")
if err != nil { for test, c := range tests {
t.Fatalf("glob error: %v", err) t.Run(test, func(t *testing.T) {
paths, err := n.Glob(c.globPattern)
if err != nil {
t.Fatalf("glob error: %v", err)
}
assertEqualStringSlices(t, c.expectedFiles, paths, "glob test")
})
} }
assertEqualStringSlices(t, expected, paths, "glob test")
} }
func assertEqualStringSlices(t *testing.T, expected, actual []string, message string) { func assertEqualStringSlices(t *testing.T, expected, actual []string, message string) {

View File

@@ -88,7 +88,17 @@ func (fsOnDisk) Exists(name string) bool {
// Glob returns the list of matching files // Glob returns the list of matching files
func (fsOnDisk) Glob(pattern string) ([]string, error) { func (fsOnDisk) Glob(pattern string) ([]string, error) {
return filepath.Glob(pattern) var result []string
allFilePaths, err := filepath.Glob(pattern)
if err != nil {
return nil, err
}
if IsHiddenFilePath(pattern) {
result = allFilePaths
} else {
result = RemoveHiddenFiles(allFilePaths)
}
return result, nil
} }
// IsDir delegates to os.Stat and FileInfo.IsDir // IsDir delegates to os.Stat and FileInfo.IsDir

View File

@@ -1,6 +1,7 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
//go:build !windows
// +build !windows // +build !windows
package filesys package filesys
@@ -11,6 +12,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"sort"
"testing" "testing"
) )
@@ -135,31 +137,113 @@ func TestReadFilesRealFS(t *testing.T) {
fSys, testDir := makeTestDir(t) fSys, testDir := makeTestDir(t)
defer os.RemoveAll(testDir) defer os.RemoveAll(testDir)
err := fSys.WriteFile(path.Join(testDir, "foo"), []byte(`foo`)) dir := path.Join(testDir, "dir")
if err != nil { nestedDir := path.Join(dir, "nestedDir")
t.Fatalf("unexpected error %s", err) hiddenDir := path.Join(testDir, ".hiddenDir")
dirs := []string{
testDir,
dir,
nestedDir,
hiddenDir,
} }
if !fSys.Exists(path.Join(testDir, "foo")) { // all directories will have all these files
t.Fatalf("expected foo") files := []string{
} "bar",
if fSys.IsDir(path.Join(testDir, "foo")) { "foo",
t.Fatalf("expected foo not to be a directory") "file-1.xtn",
".file-2.xtn",
".some-file-3.xtn",
".some-file-4.xtn",
} }
err = fSys.WriteFile(path.Join(testDir, "bar"), []byte(`bar`)) err := fSys.MkdirAll(nestedDir)
if err != nil { if err != nil {
t.Fatalf("unexpected error %s", err) t.Fatalf("Unexpected Error %v\n", err)
}
err = fSys.MkdirAll(hiddenDir)
if err != nil {
t.Fatalf("Unexpected Error %v\n", err)
} }
files, err := fSys.Glob(path.Join("testDir", "*")) // adding all files in every directory that we had defined
expected := []string{ for _, d := range dirs {
path.Join(testDir, "bar"), if !fSys.IsDir(d) {
path.Join(testDir, "foo"), t.Fatalf("Expected %s to be a dir\n", d)
}
for _, f := range files {
err = fSys.WriteFile(path.Join(d, f), []byte(f))
if err != nil {
t.Fatalf("unexpected error %s", err)
}
if !fSys.Exists(path.Join(d, f)) {
t.Fatalf("expected %s", f)
}
}
} }
if err != nil {
t.Fatalf("expected no error") tests := map[string]struct {
globPattern string
expectedFiles []string
expectedDirs map[string][]string // glob returns directories as well, so we need to add those to expected files
}{
"AllVisibleFiles": {
globPattern: "*",
expectedFiles: []string{
"bar",
"foo",
"file-1.xtn",
},
expectedDirs: map[string][]string{
testDir: []string{dir},
dir: []string{nestedDir},
},
},
"AllHiddenFiles": {
globPattern: ".*",
expectedFiles: []string{
".file-2.xtn",
".some-file-3.xtn",
".some-file-4.xtn",
},
expectedDirs: map[string][]string{
testDir: []string{hiddenDir},
},
},
"foo_File": {
globPattern: "foo",
expectedFiles: []string{
"foo",
},
},
"dotsome-file_PrefixedFiles": {
globPattern: ".some-file*",
expectedFiles: []string{
".some-file-3.xtn",
".some-file-4.xtn",
},
},
} }
if reflect.DeepEqual(files, expected) {
t.Fatalf("incorrect files found by glob: %v", files) for n, c := range tests {
t.Run(n, func(t *testing.T) {
for _, d := range dirs {
var expectedPaths []string
for _, f := range c.expectedFiles {
expectedPaths = append(expectedPaths, path.Join(d, f))
}
if c.expectedDirs != nil {
expectedPaths = append(expectedPaths, c.expectedDirs[d]...)
}
actualPaths, globErr := fSys.Glob(path.Join(d, c.globPattern))
if globErr != nil {
t.Fatalf("Unexpected Error : %v\n", globErr)
}
sort.Strings(actualPaths)
sort.Strings(expectedPaths)
if !reflect.DeepEqual(actualPaths, expectedPaths) {
t.Fatalf("incorrect files found by glob: expected=%v, actual=%v", expectedPaths, actualPaths)
}
}
})
} }
} }

View File

@@ -123,3 +123,21 @@ func InsertPathPart(path string, pos int, part string) string {
result[pos] = part result[pos] = part
return PathJoin(append(result, parts[pos:]...)) return PathJoin(append(result, parts[pos:]...))
} }
func IsHiddenFilePath(pattern string) bool {
return strings.HasPrefix(filepath.Base(pattern), ".")
}
// Removes paths containing hidden files/folders from a list of paths
func RemoveHiddenFiles(paths []string) []string {
if len(paths) == 0 {
return paths
}
var result []string
for _, path := range paths {
if !IsHiddenFilePath(path) {
result = append(result, path)
}
}
return result
}

View File

@@ -1,6 +1,7 @@
// Copyright 2021 The Kubernetes Authors. // Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
//go:build !windows
// +build !windows // +build !windows
package filesys package filesys
@@ -8,6 +9,7 @@ package filesys
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"testing" "testing"
) )
@@ -375,3 +377,90 @@ func TestStripLeadingSeps(t *testing.T) {
} }
} }
} }
func TestIsHiddenFilePath(t *testing.T) {
tests := map[string]struct {
paths []string
expectHidden bool
}{
"hiddenGlobs": {
expectHidden: true,
paths: []string{
".*",
"/.*",
"dir/.*",
"dir1/dir2/dir3/.*",
"../../.*",
"../../dir/.*",
},
},
"visibleGlobes": {
expectHidden: false,
paths: []string{
"*",
"/*",
"dir/*",
"dir1/dir2/dir3/*",
"../../*",
"../../dir/*",
},
},
"hiddenFiles": {
expectHidden: true,
paths: []string{
".root_file.xtn",
"/.file_1.xtn",
"dir/.file_2.xtn",
"dir1/dir2/dir3/.file_3.xtn",
"../../.file_4.xtn",
"../../dir/.file_5.xtn",
},
},
"visibleFiles": {
expectHidden: false,
paths: []string{
"root_file.xtn",
"/file_1.xtn",
"dir/file_2.xtn",
"dir1/dir2/dir3/file_3.xtn",
"../../file_4.xtn",
"../../dir/file_5.xtn",
},
},
}
for n, c := range tests {
t.Run(n, func(t *testing.T) {
for _, path := range c.paths {
actual := IsHiddenFilePath(path)
if actual != c.expectHidden {
t.Fatalf("For file path %q, expected hidden: %v, got hidden: %v", path, c.expectHidden, actual)
}
}
})
}
}
func TestRemoveHiddenFiles(t *testing.T) {
paths := []string{
"file1.xtn",
".file2.xtn",
"dir/fa1",
"dir/fa2",
"dir/.fa3",
"../../.fa4",
"../../fa5",
"../../dir/fa6",
"../../dir/.fa7",
}
result := RemoveHiddenFiles(paths)
expected := []string{
"file1.xtn",
"dir/fa1",
"dir/fa2",
"../../fa5",
"../../dir/fa6",
}
if !reflect.DeepEqual(result, expected) {
t.Fatalf("Hidden dirs not correctly removed, expected %v but got %v\n", expected, result)
}
}

View File

@@ -122,15 +122,17 @@ func AddGenerateDockerfile(cmd *cobra.Command) {
Use: "gen [DIR]", Use: "gen [DIR]",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return ioutil.WriteFile(filepath.Join(args[0], "Dockerfile"), []byte(`FROM golang:1.15-alpine as builder return ioutil.WriteFile(filepath.Join(args[0], "Dockerfile"), []byte(`FROM golang:1.16-alpine as builder
ENV CGO_ENABLED=0 ENV CGO_ENABLED=0
WORKDIR /go/src/ WORKDIR /go/src/
COPY go.mod go.sum ./
RUN go mod download
COPY . . COPY . .
RUN go build -tags netgo -ldflags '-w' -v -o /usr/local/bin/function ./ RUN go build -ldflags '-w -s' -v -o /usr/local/bin/function ./
FROM alpine:latest FROM alpine:latest
COPY --from=builder /usr/local/bin/function /usr/local/bin/function COPY --from=builder /usr/local/bin/function /usr/local/bin/function
CMD ["function"] ENTRYPOINT ["function"]
`), 0600) `), 0600)
}, },
} }

View File

@@ -45,15 +45,17 @@ func TestCommand_dockerfile(t *testing.T) {
t.FailNow() t.FailNow()
} }
expected := `FROM golang:1.15-alpine as builder expected := `FROM golang:1.16-alpine as builder
ENV CGO_ENABLED=0 ENV CGO_ENABLED=0
WORKDIR /go/src/ WORKDIR /go/src/
COPY go.mod go.sum ./
RUN go mod download
COPY . . COPY . .
RUN go build -tags netgo -ldflags '-w' -v -o /usr/local/bin/function ./ RUN go build -ldflags '-w -s' -v -o /usr/local/bin/function ./
FROM alpine:latest FROM alpine:latest
COPY --from=builder /usr/local/bin/function /usr/local/bin/function COPY --from=builder /usr/local/bin/function /usr/local/bin/function
CMD ["function"] ENTRYPOINT ["function"]
` `
if !assert.Equal(t, expected, string(b)) { if !assert.Equal(t, expected, string(b)) {
t.FailNow() t.FailNow()

View File

@@ -37,7 +37,7 @@ func ExampleBuild_modify() {
// for testing purposes only -- normally read from stdin when Executing // for testing purposes only -- normally read from stdin when Executing
cmd.SetIn(bytes.NewBufferString(` cmd.SetIn(bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
# items are provided as nodes # items are provided as nodes
items: items:
@@ -61,7 +61,7 @@ functionConfig:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1alpha1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -133,7 +133,7 @@ metadata:
// for testing purposes only -- normally read from stdin when Executing // for testing purposes only -- normally read from stdin when Executing
cmd.SetIn(bytes.NewBufferString(` cmd.SetIn(bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
# items are provided as nodes # items are provided as nodes
items: items:
@@ -154,7 +154,7 @@ functionConfig:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1alpha1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -241,7 +241,7 @@ metadata:
// for testing purposes only -- normally read from stdin when Executing // for testing purposes only -- normally read from stdin when Executing
cmd.SetIn(bytes.NewBufferString(` cmd.SetIn(bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
# items are provided as nodes # items are provided as nodes
items: items:
@@ -268,7 +268,7 @@ functionConfig:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1alpha1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -296,7 +296,7 @@ functionConfig:
func ExampleBuild_validate() { func ExampleBuild_validate() {
fn := func(rl *framework.ResourceList) error { fn := func(rl *framework.ResourceList) error {
// validation results // validation results
var validationResults []framework.ResultItem var validationResults framework.Results
// validate that each Deployment resource has spec.replicas set // validate that each Deployment resource has spec.replicas set
for i := range rl.Items { for i := range rl.Items {
@@ -319,34 +319,31 @@ func ExampleBuild_validate() {
if r != nil { if r != nil {
continue continue
} }
validationResults = append(validationResults, framework.ResultItem{ validationResults = append(validationResults, &framework.Result{
Severity: framework.Error, Severity: framework.Error,
Message: "field is required", Message: "field is required",
ResourceRef: yaml.ResourceIdentifier{ ResourceRef: &yaml.ResourceIdentifier{
TypeMeta: meta.TypeMeta, TypeMeta: meta.TypeMeta,
NameMeta: meta.ObjectMeta.NameMeta, NameMeta: meta.ObjectMeta.NameMeta,
}, },
Field: framework.Field{ Field: &framework.Field{
Path: "spec.replicas", Path: "spec.replicas",
SuggestedValue: "1", ProposedValue: "1",
}, },
}) })
} }
if len(validationResults) > 0 { if len(validationResults) > 0 {
rl.Result = &framework.Result{ rl.Results = validationResults
Name: "replicas-validator",
Items: validationResults,
}
} }
return rl.Result return rl.Results
} }
cmd := command.Build(framework.ResourceListProcessorFunc(fn), command.StandaloneDisabled, true) cmd := command.Build(framework.ResourceListProcessorFunc(fn), command.StandaloneDisabled, true)
// for testing purposes only -- normally read from stdin when Executing // for testing purposes only -- normally read from stdin when Executing
cmd.SetIn(bytes.NewBufferString(` cmd.SetIn(bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
# items are provided as nodes # items are provided as nodes
items: items:
@@ -362,7 +359,7 @@ items:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1alpha1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -370,15 +367,13 @@ items:
// metadata: // metadata:
// name: foo // name: foo
// results: // results:
// name: replicas-validator // - message: field is required
// items: // severity: error
// - message: field is required // resourceRef:
// severity: error // apiVersion: apps/v1
// resourceRef: // kind: Deployment
// apiVersion: apps/v1 // name: foo
// kind: Deployment // field:
// name: foo // path: spec.replicas
// field: // proposedValue: "1"
// path: spec.replicas
// suggestedValue: "1"
} }

View File

@@ -23,7 +23,7 @@ const service = "Service"
// ExampleSimpleProcessor_modify implements a function that sets an annotation on each resource. // ExampleSimpleProcessor_modify implements a function that sets an annotation on each resource.
func ExampleSimpleProcessor_modify() { func ExampleSimpleProcessor_modify() {
input := bytes.NewBufferString(` input := bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
# items are provided as nodes # items are provided as nodes
items: items:
@@ -60,7 +60,7 @@ functionConfig:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1alpha1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -86,7 +86,7 @@ functionConfig:
// If the resource already exists, it replaces the resource with a new copy. // If the resource already exists, it replaces the resource with a new copy.
func ExampleSimpleProcessor_generateReplace() { func ExampleSimpleProcessor_generateReplace() {
input := bytes.NewBufferString(` input := bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
# items are provided as nodes # items are provided as nodes
items: items:
@@ -150,7 +150,7 @@ metadata:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1alpha1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -471,7 +471,7 @@ spec:
// The second resource will be treated as a patch since its metadata matches the resource // The second resource will be treated as a patch since its metadata matches the resource
// generated by ResourceTemplates and MergeResources is true. // generated by ResourceTemplates and MergeResources is true.
rw := kio.ByteReadWriter{Reader: bytes.NewBufferString(` rw := kio.ByteReadWriter{Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -501,7 +501,7 @@ functionConfig:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1alpha1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -543,7 +543,7 @@ func ExampleSelector_templatizeKinds() {
} }
rw := &kio.ByteReadWriter{ rw := &kio.ByteReadWriter{
Reader: bytes.NewBufferString(` Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1beta1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
functionConfig: functionConfig:
kindName: Deployment kindName: Deployment
@@ -575,7 +575,7 @@ items:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1beta1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -595,7 +595,7 @@ func ExampleSelector_templatizeAnnotations() {
Value string `yaml:"value"` Value string `yaml:"value"`
} }
rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(` rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1beta1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
functionConfig: functionConfig:
value: bar value: bar
@@ -629,7 +629,7 @@ items:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1beta1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1
@@ -995,7 +995,7 @@ func ExampleVersionedAPIProcessor() {
}}} }}}
source := &kio.ByteReadWriter{Reader: bytes.NewBufferString(` source := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1beta1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
functionConfig: functionConfig:
apiVersion: example.com/v1alpha1 apiVersion: example.com/v1alpha1
@@ -1011,7 +1011,7 @@ functionConfig:
} }
// Output: // Output:
// apiVersion: config.kubernetes.io/v1beta1 // apiVersion: config.kubernetes.io/v1
// kind: ResourceList // kind: ResourceList
// items: // items:
// - apiVersion: apps/v1 // - apiVersion: apps/v1

View File

@@ -17,6 +17,22 @@ import (
// This framework facilitates building functions that receive and emit ResourceLists, // This framework facilitates building functions that receive and emit ResourceLists,
// as required by the specification. // as required by the specification.
type ResourceList struct { type ResourceList struct {
// Items is the ResourceList.items input and output value.
//
// e.g. given the function input:
//
// kind: ResourceList
// items:
// - kind: Deployment
// ...
// - kind: Service
// ...
//
// Items will be a slice containing the Deployment and Service resources
// Mutating functions will alter this field during processing.
// This field is required.
Items []*yaml.RNode `yaml:"items" json:"items"`
// FunctionConfig is the ResourceList.functionConfig input value. // FunctionConfig is the ResourceList.functionConfig input value.
// //
// e.g. given the input: // e.g. given the input:
@@ -31,27 +47,12 @@ type ResourceList struct {
// kind: Example // kind: Example
// spec: // spec:
// foo: var // foo: var
FunctionConfig *yaml.RNode `yaml:"functionConfig" json:"functionConfig"` FunctionConfig *yaml.RNode `yaml:"functionConfig,omitempty" json:"functionConfig,omitempty"`
// Items is the ResourceList.items input and output value. // Results is ResourceList.results output value.
//
// e.g. given the function input:
//
// kind: ResourceList
// items:
// - kind: Deployment
// ...
// - kind: Service
// ...
//
// Items will be a slice containing the Deployment and Service resources
// Mutating functions will alter this field during processing.
Items []*yaml.RNode `yaml:"items" json:"items"`
// Result is ResourceList.result output value.
// Validating functions can optionally use this field to communicate structured // Validating functions can optionally use this field to communicate structured
// validation error data to downstream functions. // validation error data to downstream functions.
Result *Result `yaml:"results" json:"results"` Results Results `yaml:"results,omitempty" json:"results,omitempty"`
} }
// ResourceListProcessor is implemented by configuration functions built with this framework // ResourceListProcessor is implemented by configuration functions built with this framework
@@ -119,8 +120,8 @@ func Execute(p ResourceListProcessor, rlSource *kio.ByteReadWriter) error {
// Write the results // Write the results
// Set the ResourceList.results for validating functions // Set the ResourceList.results for validating functions
if rl.Result != nil && len(rl.Result.Items) > 0 { if len(rl.Results) > 0 {
b, err := yaml.Marshal(rl.Result) b, err := yaml.Marshal(rl.Results)
if err != nil { if err != nil {
return errors.Wrap(err) return errors.Wrap(err)
} }

View File

@@ -16,39 +16,37 @@ import (
func TestExecute_Result(t *testing.T) { func TestExecute_Result(t *testing.T) {
p := framework.ResourceListProcessorFunc(func(rl *framework.ResourceList) error { p := framework.ResourceListProcessorFunc(func(rl *framework.ResourceList) error {
err := &framework.Result{ err := &framework.Results{
Name: "Incompatible config", {
Items: []framework.ResultItem{ Message: "bad value for replicas",
{ Severity: framework.Error,
Message: "bad value for replicas", ResourceRef: &yaml.ResourceIdentifier{
Severity: framework.Error, TypeMeta: yaml.TypeMeta{APIVersion: "v1", Kind: "Deployment"},
ResourceRef: yaml.ResourceIdentifier{ NameMeta: yaml.NameMeta{Name: "tester", Namespace: "default"},
TypeMeta: yaml.TypeMeta{APIVersion: "v1", Kind: "Deployment"},
NameMeta: yaml.NameMeta{Name: "tester", Namespace: "default"},
},
Field: framework.Field{
Path: ".spec.Replicas",
CurrentValue: "0",
SuggestedValue: "3",
},
File: framework.File{
Path: "/path/to/deployment.yaml",
Index: 0,
},
}, },
{ Field: &framework.Field{
Message: "some error", Path: ".spec.Replicas",
Severity: framework.Error, CurrentValue: "0",
ProposedValue: "3",
},
File: &framework.File{
Path: "/path/to/deployment.yaml",
Index: 0,
}, },
}, },
{
Message: "some error",
Severity: framework.Error,
Tags: map[string]string{"foo": "bar"},
},
} }
rl.Result = err rl.Results = *err
return err return err
}) })
out := new(bytes.Buffer) out := new(bytes.Buffer)
source := &kio.ByteReadWriter{Reader: bytes.NewBufferString(` source := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
kind: ResourceList kind: ResourceList
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
items: items:
- kind: Deployment - kind: Deployment
apiVersion: v1 apiVersion: v1
@@ -61,9 +59,9 @@ items:
err := framework.Execute(p, source) err := framework.Execute(p, source)
assert.EqualError(t, err, `[error] v1/Deployment/default/tester .spec.Replicas: bad value for replicas assert.EqualError(t, err, `[error] v1/Deployment/default/tester .spec.Replicas: bad value for replicas
[error] : some error`) [error]: some error`)
assert.Equal(t, 1, err.(*framework.Result).ExitCode()) assert.Equal(t, 1, err.(*framework.Results).ExitCode())
assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1 assert.Equal(t, `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -74,21 +72,21 @@ items:
spec: spec:
replicas: 0 replicas: 0
results: results:
name: Incompatible config - message: bad value for replicas
items: severity: error
- message: bad value for replicas resourceRef:
severity: error apiVersion: v1
resourceRef: kind: Deployment
apiVersion: v1 name: tester
kind: Deployment namespace: default
name: tester field:
namespace: default path: .spec.Replicas
field: currentValue: "0"
path: .spec.Replicas proposedValue: "3"
currentValue: "0" file:
suggestedValue: "3" path: /path/to/deployment.yaml
file: - message: some error
path: /path/to/deployment.yaml severity: error
- message: some error tags:
severity: error`, strings.TrimSpace(out.String())) foo: bar`, strings.TrimSpace(out.String()))
} }

View File

@@ -35,7 +35,7 @@ func TestTemplateProcessor_ResourceTemplates(t *testing.T) {
out := new(bytes.Buffer) out := new(bytes.Buffer)
rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(` rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -47,7 +47,7 @@ functionConfig:
require.NoError(t, framework.Execute(p, rw)) require.NoError(t, framework.Execute(p, rw))
require.Equal(t, strings.TrimSpace(` require.Equal(t, strings.TrimSpace(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -98,7 +98,7 @@ metadata:
out := new(bytes.Buffer) out := new(bytes.Buffer)
rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(` rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -130,7 +130,7 @@ functionConfig:
require.NoError(t, framework.Execute(p, rw)) require.NoError(t, framework.Execute(p, rw))
require.Equal(t, strings.TrimSpace(` require.Equal(t, strings.TrimSpace(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -194,7 +194,7 @@ env:
out := new(bytes.Buffer) out := new(bytes.Buffer)
rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(` rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -231,7 +231,7 @@ functionConfig:
require.NoError(t, framework.Execute(p, rw)) require.NoError(t, framework.Execute(p, rw))
require.Equal(t, strings.TrimSpace(` require.Equal(t, strings.TrimSpace(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -300,7 +300,7 @@ func TestTemplateProcessor_ContainerPatchTemplates_MultipleWorkloadKinds(t *test
out := new(bytes.Buffer) out := new(bytes.Buffer)
rw := &kio.ByteReadWriter{Writer: out, Reader: bytes.NewBufferString(` rw := &kio.ByteReadWriter{Writer: out, Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: [] items: []
functionConfig: functionConfig:
@@ -591,7 +591,7 @@ func TestTemplateProcessor_AdditionalSchemas(t *testing.T) {
out := new(bytes.Buffer) out := new(bytes.Buffer)
rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(` rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: example.com/v1 - apiVersion: example.com/v1
@@ -606,7 +606,7 @@ items:
Writer: out} Writer: out}
require.NoError(t, framework.Execute(p, rw)) require.NoError(t, framework.Execute(p, rw))
require.Equal(t, strings.TrimSpace(` require.Equal(t, strings.TrimSpace(`
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: example.com/v1 - apiVersion: example.com/v1

View File

@@ -23,89 +23,98 @@ const (
) )
// ResultItem defines a validation result // ResultItem defines a validation result
type ResultItem struct { type Result struct {
// Message is a human readable message // Message is a human readable message. This field is required.
Message string `yaml:"message,omitempty"` Message string `yaml:"message,omitempty" json:"message,omitempty"`
// Severity is the severity of this result // Severity is the severity of this result
Severity Severity `yaml:"severity,omitempty"` Severity Severity `yaml:"severity,omitempty" json:"severity,omitempty"`
// ResourceRef is a reference to a resource // ResourceRef is a reference to a resource.
ResourceRef yaml.ResourceIdentifier `yaml:"resourceRef,omitempty"` // Required fields: apiVersion, kind, name.
ResourceRef *yaml.ResourceIdentifier `yaml:"resourceRef,omitempty" json:"resourceRef,omitempty"`
// Field is a reference to the field in a resource this result refers to // Field is a reference to the field in a resource this result refers to
Field Field `yaml:"field,omitempty"` Field *Field `yaml:"field,omitempty" json:"field,omitempty"`
// File references a file containing the resource this result refers to // File references a file containing the resource this result refers to
File File `yaml:"file,omitempty"` File *File `yaml:"file,omitempty" json:"file,omitempty"`
// Tags is an unstructured key value map stored with a result that may be set
// by external tools to store and retrieve arbitrary metadata
Tags map[string]string `yaml:"tags,omitempty" json:"tags,omitempty"`
} }
// String provides a human-readable message for the result item // String provides a human-readable message for the result item
func (i ResultItem) String() string { func (i Result) String() string {
identifier := i.ResourceRef identifier := i.ResourceRef
var idStringList []string var idStringList []string
if identifier.APIVersion != "" { if identifier != nil {
idStringList = append(idStringList, identifier.APIVersion) if identifier.APIVersion != "" {
} idStringList = append(idStringList, identifier.APIVersion)
if identifier.Kind != "" { }
idStringList = append(idStringList, identifier.Kind) if identifier.Kind != "" {
} idStringList = append(idStringList, identifier.Kind)
if identifier.Namespace != "" { }
idStringList = append(idStringList, identifier.Namespace) if identifier.Namespace != "" {
} idStringList = append(idStringList, identifier.Namespace)
if identifier.Name != "" { }
idStringList = append(idStringList, identifier.Name) if identifier.Name != "" {
idStringList = append(idStringList, identifier.Name)
}
} }
formatString := "[%s]"
list := []interface{}{i.Severity}
if len(idStringList) > 0 { if len(idStringList) > 0 {
idString := strings.Join(idStringList, "/") formatString += " %s"
return fmt.Sprintf("[%s] %s %s: %s", i.Severity, idString, i.Field.Path, i.Message) list = append(list, strings.Join(idStringList, "/"))
} }
return fmt.Sprintf("[%s] %s: %s", i.Severity, i.Field.Path, i.Message) if i.Field != nil {
formatString += " %s"
list = append(list, i.Field.Path)
}
formatString += ": %s"
list = append(list, i.Message)
return fmt.Sprintf(formatString, list...)
} }
// File references a file containing a resource // File references a file containing a resource
type File struct { type File struct {
// Path is relative path to the file containing the resource // Path is relative path to the file containing the resource.
Path string `yaml:"path,omitempty"` // This field is required.
Path string `yaml:"path,omitempty" json:"path,omitempty"`
// Index is the index into the file containing the resource // Index is the index into the file containing the resource
// (i.e. if there are multiple resources in a single file) // (i.e. if there are multiple resources in a single file)
Index int `yaml:"index,omitempty"` Index int `yaml:"index,omitempty" json:"index,omitempty"`
} }
// Field references a field in a resource // Field references a field in a resource
type Field struct { type Field struct {
// Path is the field path // Path is the field path. This field is required.
Path string `yaml:"path,omitempty"` Path string `yaml:"path,omitempty" json:"path,omitempty"`
// CurrentValue is the current field value // CurrentValue is the current field value
CurrentValue string `yaml:"currentValue,omitempty"` CurrentValue interface{} `yaml:"currentValue,omitempty" json:"currentValue,omitempty"`
// SuggestedValue is the suggested field value // ProposedValue is the proposed value of the field to fix an issue.
SuggestedValue string `yaml:"suggestedValue,omitempty"` ProposedValue interface{} `yaml:"proposedValue,omitempty" json:"proposedValue,omitempty"`
} }
// Result defines a function result which will be set on the emitted ResourceList type Results []*Result
type Result struct {
// Name is the name of the function creating the result
Name string `yaml:"name,omitempty"`
// Items are the individual results // Error enables Results to be returned as an error
Items []ResultItem `yaml:"items,omitempty"` func (e Results) Error() string {
}
// Error enables a Result to be returned as an error
func (e Result) Error() string {
var msgs []string var msgs []string
for _, i := range e.Items { for _, i := range e {
msgs = append(msgs, i.String()) msgs = append(msgs, i.String())
} }
return strings.Join(msgs, "\n\n") return strings.Join(msgs, "\n\n")
} }
// ExitCode provides the exit code based on the result's severity // ExitCode provides the exit code based on the result's severity
func (e Result) ExitCode() int { func (e Results) ExitCode() int {
for _, i := range e.Items { for _, i := range e {
if i.Severity == Error { if i.Severity == Error {
return 1 return 1
} }

View File

@@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
kind: ResourceList kind: ResourceList
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
functionConfig: functionConfig:
apiVersion: example.com/v1alpha1 apiVersion: example.com/v1alpha1
kind: JavaSpringBoot kind: JavaSpringBoot

View File

@@ -62,7 +62,7 @@ func TestFunctionFilter_Filter(t *testing.T) {
name: "default_file_path_annotation", name: "default_file_path_annotation",
run: testRun{ run: testRun{
output: ` output: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -103,7 +103,7 @@ metadata:
name: "no_default_file_path_annotation", name: "no_default_file_path_annotation",
run: testRun{ run: testRun{
output: ` output: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -146,7 +146,7 @@ metadata:
name: "write_read", name: "write_read",
run: testRun{ run: testRun{
output: ` output: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -203,7 +203,7 @@ metadata:
{ {
name: "write_results_file", name: "write_results_file",
run: testRun{ run: testRun{
output: `apiVersion: config.kubernetes.io/v1alpha1 output: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -283,7 +283,7 @@ metadata:
run: testRun{ run: testRun{
err: fmt.Errorf("failed"), err: fmt.Errorf("failed"),
output: ` output: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -363,7 +363,7 @@ metadata:
run: testRun{ run: testRun{
err: fmt.Errorf("failed"), err: fmt.Errorf("failed"),
output: ` output: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -440,7 +440,7 @@ metadata:
noMakeResultsFile: true, noMakeResultsFile: true,
run: testRun{ run: testRun{
output: ` output: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -487,7 +487,7 @@ metadata:
{ {
name: "scope_resources_by_directory", name: "scope_resources_by_directory",
run: testRun{ run: testRun{
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1 expectedInput: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -506,7 +506,7 @@ functionConfig:
annotations: annotations:
config.kubernetes.io/path: 'foo/bar.yaml' config.kubernetes.io/path: 'foo/bar.yaml'
`, `,
output: `apiVersion: config.kubernetes.io/v1alpha1 output: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -578,7 +578,7 @@ metadata:
{ {
name: "scope_resources_by_directory_resources_missing_path", name: "scope_resources_by_directory_resources_missing_path",
run: testRun{ run: testRun{
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1 expectedInput: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -597,7 +597,7 @@ functionConfig:
annotations: annotations:
config.kubernetes.io/path: 'foo/bar.yaml' config.kubernetes.io/path: 'foo/bar.yaml'
`, `,
output: `apiVersion: config.kubernetes.io/v1alpha1 output: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -666,7 +666,7 @@ metadata:
name: "scope_resources_global", name: "scope_resources_global",
instance: FunctionFilter{GlobalScope: true}, instance: FunctionFilter{GlobalScope: true},
run: testRun{ run: testRun{
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1 expectedInput: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -693,7 +693,7 @@ functionConfig:
annotations: annotations:
config.kubernetes.io/path: 'foo/bar.yaml' config.kubernetes.io/path: 'foo/bar.yaml'
`, `,
output: `apiVersion: config.kubernetes.io/v1alpha1 output: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -770,7 +770,7 @@ metadata:
{ {
name: "scope_no_resources", name: "scope_no_resources",
run: testRun{ run: testRun{
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1 expectedInput: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: [] items: []
functionConfig: functionConfig:
@@ -781,7 +781,7 @@ functionConfig:
annotations: annotations:
config.kubernetes.io/path: 'foo/bar.yaml' config.kubernetes.io/path: 'foo/bar.yaml'
`, `,
output: `apiVersion: config.kubernetes.io/v1alpha1 output: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: [] items: []
functionConfig: functionConfig:
@@ -840,7 +840,7 @@ metadata:
{ {
name: "scope_functions_dir", name: "scope_functions_dir",
run: testRun{ run: testRun{
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1 expectedInput: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -859,7 +859,7 @@ functionConfig:
annotations: annotations:
config.kubernetes.io/path: 'foo/functions/bar.yaml' config.kubernetes.io/path: 'foo/functions/bar.yaml'
`, `,
output: `apiVersion: config.kubernetes.io/v1alpha1 output: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: v1 - apiVersion: v1
@@ -931,7 +931,7 @@ metadata:
{ {
name: "copy_comments", name: "copy_comments",
run: testRun{ run: testRun{
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1 expectedInput: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -959,7 +959,7 @@ functionConfig:
config.kubernetes.io/path: 'foo/f.yaml' config.kubernetes.io/path: 'foo/f.yaml'
`, `,
// delete the comment // delete the comment
output: `apiVersion: config.kubernetes.io/v1alpha1 output: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- apiVersion: apps/v1 - apiVersion: apps/v1

View File

@@ -18,7 +18,7 @@ import (
const ( const (
ResourceListKind = "ResourceList" ResourceListKind = "ResourceList"
ResourceListAPIVersion = "config.kubernetes.io/v1alpha1" ResourceListAPIVersion = "config.kubernetes.io/v1"
) )
// ByteReadWriter reads from an input and writes to an output. // ByteReadWriter reads from an input and writes to an output.
@@ -67,12 +67,12 @@ func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
WrapBareSeqNode: rw.WrapBareSeqNode, WrapBareSeqNode: rw.WrapBareSeqNode,
} }
val, err := b.Read() val, err := b.Read()
rw.Results = b.Results
if rw.FunctionConfig == nil { if rw.FunctionConfig == nil {
rw.FunctionConfig = b.FunctionConfig rw.FunctionConfig = b.FunctionConfig
} }
rw.Results = b.Results if !rw.NoWrap && rw.WrappingKind == "" {
if !rw.NoWrap {
rw.WrappingAPIVersion = b.WrappingAPIVersion rw.WrappingAPIVersion = b.WrappingAPIVersion
rw.WrappingKind = b.WrappingKind rw.WrappingKind = b.WrappingKind
} }
@@ -80,15 +80,18 @@ func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
} }
func (rw *ByteReadWriter) Write(nodes []*yaml.RNode) error { func (rw *ByteReadWriter) Write(nodes []*yaml.RNode) error {
return ByteWriter{ w := ByteWriter{
Writer: rw.Writer, Writer: rw.Writer,
KeepReaderAnnotations: rw.KeepReaderAnnotations, KeepReaderAnnotations: rw.KeepReaderAnnotations,
Style: rw.Style, Style: rw.Style,
FunctionConfig: rw.FunctionConfig, FunctionConfig: rw.FunctionConfig,
Results: rw.Results, Results: rw.Results,
WrappingAPIVersion: rw.WrappingAPIVersion, }
WrappingKind: rw.WrappingKind, if !rw.NoWrap {
}.Write(nodes) w.WrappingAPIVersion = rw.WrappingAPIVersion
w.WrappingKind = rw.WrappingKind
}
return w.Write(nodes)
} }
// ParseAll reads all of the inputs into resources // ParseAll reads all of the inputs into resources
@@ -316,6 +319,10 @@ func (r *ByteReader) decode(originalYAML string, index int, decoder *yaml.Decode
r.SetAnnotations = map[string]string{} r.SetAnnotations = map[string]string{}
} }
if !r.OmitReaderAnnotations { if !r.OmitReaderAnnotations {
err := kioutil.CopyLegacyAnnotations(n)
if err != nil {
return nil, err
}
r.SetAnnotations[kioutil.IndexAnnotation] = fmt.Sprintf("%d", index) r.SetAnnotations[kioutil.IndexAnnotation] = fmt.Sprintf("%d", index)
r.SetAnnotations[kioutil.LegacyIndexAnnotation] = fmt.Sprintf("%d", index) r.SetAnnotations[kioutil.LegacyIndexAnnotation] = fmt.Sprintf("%d", index)

View File

@@ -33,7 +33,7 @@ func TestByteReader(t *testing.T) {
// //
{ {
name: "wrapped_resource_list", name: "wrapped_resource_list",
input: `apiVersion: config.kubernetes.io/v1alpha1 input: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -64,7 +64,7 @@ spec:
// //
{ {
name: "wrapped_resource_list_function_config", name: "wrapped_resource_list_function_config",
input: `apiVersion: config.kubernetes.io/v1alpha1 input: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
functionConfig: functionConfig:
foo: bar foo: bar
@@ -105,7 +105,7 @@ elems:
// //
{ {
name: "wrapped_resource_list_function_config_without_items", name: "wrapped_resource_list_function_config_without_items",
input: `apiVersion: config.kubernetes.io/v1alpha1 input: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
functionConfig: functionConfig:
foo: bar foo: bar

View File

@@ -26,7 +26,7 @@ func TestByteReadWriter(t *testing.T) {
{ {
name: "round_trip", name: "round_trip",
input: ` input: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -38,7 +38,7 @@ items:
foo: bar foo: bar
`, `,
expectedOutput: ` expectedOutput: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -54,7 +54,7 @@ items:
{ {
name: "function_config", name: "function_config",
input: ` input: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -68,7 +68,7 @@ functionConfig:
a: b # something a: b # something
`, `,
expectedOutput: ` expectedOutput: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -86,7 +86,7 @@ functionConfig:
{ {
name: "results", name: "results",
input: ` input: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -100,7 +100,7 @@ results:
a: b # something a: b # something
`, `,
expectedOutput: ` expectedOutput: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -118,7 +118,7 @@ results:
{ {
name: "drop_invalid_resource_list_field", name: "drop_invalid_resource_list_field",
input: ` input: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -132,7 +132,7 @@ foo:
a: b # something a: b # something
`, `,
expectedOutput: ` expectedOutput: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -233,7 +233,7 @@ metadata:
{ {
name: "manual_override_wrap", name: "manual_override_wrap",
input: ` input: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -262,7 +262,7 @@ spec:
{ {
name: "manual_override_function_config", name: "manual_override_function_config",
input: ` input: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -276,7 +276,7 @@ functionConfig:
a: b # something a: b # something
`, `,
expectedOutput: ` expectedOutput: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -499,7 +499,7 @@ env:
{ {
name: "unwrap ResourceList with annotations", name: "unwrap ResourceList with annotations",
input: ` input: `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- kind: Deployment - kind: Deployment
@@ -747,3 +747,122 @@ func TestByteReadWriter_WrapBareSeqNode(t *testing.T) {
}) })
} }
} }
func TestByteReadWriter_ResourceListWrapping(t *testing.T) {
singleDeployment := `kind: Deployment
apiVersion: v1
metadata:
name: tester
namespace: default
spec:
replicas: 0`
resourceList := `apiVersion: config.kubernetes.io/v1
kind: ResourceList
items:
- kind: Deployment
apiVersion: v1
metadata:
name: tester
namespace: default
spec:
replicas: 0`
resourceListWithError := `apiVersion: config.kubernetes.io/v1
kind: ResourceList
items:
- kind: Deployment
apiVersion: v1
metadata:
name: tester
namespace: default
spec:
replicas: 0
results:
- message: some error
severity: error`
resourceListDifferentWrapper := strings.NewReplacer(
"kind: ResourceList", "kind: SomethingElse",
"apiVersion: config.kubernetes.io/v1", "apiVersion: fakeVersion",
).Replace(resourceList)
testCases := []struct {
desc string
noWrap bool
wrapKind string
wrapAPIVersion string
input string
want string
}{
{
desc: "resource list",
input: resourceList,
want: resourceList,
},
{
desc: "individual resources",
input: singleDeployment,
want: singleDeployment,
},
{
desc: "no nested wrapping",
wrapKind: kio.ResourceListKind,
wrapAPIVersion: kio.ResourceListAPIVersion,
input: resourceList,
want: resourceList,
},
{
desc: "unwrap resource list",
noWrap: true,
input: resourceList,
want: singleDeployment,
},
{
desc: "wrap individual resources",
wrapKind: kio.ResourceListKind,
wrapAPIVersion: kio.ResourceListAPIVersion,
input: singleDeployment,
want: resourceList,
},
{
desc: "NoWrap has precedence",
noWrap: true,
wrapKind: kio.ResourceListKind,
wrapAPIVersion: kio.ResourceListAPIVersion,
input: singleDeployment,
want: singleDeployment,
},
{
desc: "honor specified wrapping kind",
wrapKind: "SomethingElse",
wrapAPIVersion: "fakeVersion",
input: resourceList,
want: resourceListDifferentWrapper,
},
{
desc: "passthrough results",
input: resourceListWithError,
want: resourceListWithError,
},
}
for i := range testCases {
tc := testCases[i]
t.Run(tc.desc, func(t *testing.T) {
var got bytes.Buffer
rw := kio.ByteReadWriter{
Reader: strings.NewReader(tc.input),
Writer: &got,
NoWrap: tc.noWrap,
WrappingAPIVersion: tc.wrapAPIVersion,
WrappingKind: tc.wrapKind,
}
rnodes, err := rw.Read()
assert.NoError(t, err)
err = rw.Write(rnodes)
assert.NoError(t, err)
assert.Equal(t, tc.want, strings.TrimSpace(got.String()))
})
}
}

View File

@@ -45,7 +45,7 @@ g:
h: h:
- i # has a list - i # has a list
- j`, - j`,
expectedOutput: `apiVersion: config.kubernetes.io/v1alpha1 expectedOutput: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- a: b #first - a: b #first
@@ -299,7 +299,13 @@ g:
`, `,
}, },
expectedOutput: `a: b #first expectedOutput: `e: f
g:
h:
- i # has a list
- j
---
a: b #first
metadata: metadata:
annotations: annotations:
internal.config.kubernetes.io/path: "a/b/a_test.yaml" internal.config.kubernetes.io/path: "a/b/a_test.yaml"
@@ -310,12 +316,6 @@ metadata:
annotations: annotations:
internal.config.kubernetes.io/path: "a/b/a_test.yaml" internal.config.kubernetes.io/path: "a/b/a_test.yaml"
config.kubernetes.io/path: 'a/b/a_test.yaml' config.kubernetes.io/path: 'a/b/a_test.yaml'
---
e: f
g:
h:
- i # has a list
- j
`, `,
}, },
@@ -477,7 +477,7 @@ metadata:
}`, }`,
}, },
expectedOutput: `apiVersion: config.kubernetes.io/v1alpha1 expectedOutput: `apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- {"a": "b", "metadata": {"annotations": {"internal.config.kubernetes.io/path": "test.json"}}} - {"a": "b", "metadata": {"annotations": {"internal.config.kubernetes.io/path": "test.json"}}}

View File

@@ -181,6 +181,10 @@ func storeInternalAnnotations(result []*yaml.RNode) (map[string]map[string]strin
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := checkMismatchedAnnos(meta); err != nil {
return nil, err
}
path := meta.Annotations[kioutil.PathAnnotation] path := meta.Annotations[kioutil.PathAnnotation]
index := meta.Annotations[kioutil.IndexAnnotation] index := meta.Annotations[kioutil.IndexAnnotation]
id := meta.Annotations[kioutil.IdAnnotation] id := meta.Annotations[kioutil.IdAnnotation]
@@ -193,6 +197,29 @@ func storeInternalAnnotations(result []*yaml.RNode) (map[string]map[string]strin
return nodeAnnosMap, nil return nodeAnnosMap, nil
} }
func checkMismatchedAnnos(meta yaml.ResourceMeta) error {
path := meta.Annotations[kioutil.PathAnnotation]
index := meta.Annotations[kioutil.IndexAnnotation]
id := meta.Annotations[kioutil.IdAnnotation]
legacyPath := meta.Annotations[kioutil.LegacyPathAnnotation]
legacyIndex := meta.Annotations[kioutil.LegacyIndexAnnotation]
legacyId := meta.Annotations[kioutil.LegacyIdAnnotation]
// if prior to running the functions, the legacy and internal annotations differ,
// throw an error as we cannot infer the user's intent.
if path != legacyPath {
return fmt.Errorf("resource input to function has mismatched legacy and internal path annotations")
}
if index != legacyIndex {
return fmt.Errorf("resource input to function has mismatched legacy and internal index annotations")
}
if id != legacyId {
return fmt.Errorf("resource input to function has mismatched legacy and internal id annotations")
}
return nil
}
type nodeAnnotations struct { type nodeAnnotations struct {
path string path string
index string index string
@@ -217,9 +244,12 @@ func reconcileInternalAnnotations(result []*yaml.RNode, nodeAnnosMap map[string]
if err != nil { if err != nil {
return err return err
} }
// if the annotations are still somehow out of sync, prefer the internal annotations // if the annotations are still somehow out of sync, throw an error
// and copy them to the legacy ones meta, err = node.GetMeta()
err = kioutil.CopyLegacyAnnotations(node) if err != nil {
return err
}
err = checkMismatchedAnnos(meta)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -97,7 +97,7 @@ func TestEmptyInput(t *testing.T) {
} }
expected := ` expected := `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: [] items: []
` `
@@ -138,7 +138,7 @@ func TestEmptyInputWithFilter(t *testing.T) {
} }
expected := ` expected := `
apiVersion: config.kubernetes.io/v1alpha1 apiVersion: config.kubernetes.io/v1
kind: ResourceList kind: ResourceList
items: items:
- foo: bar - foo: bar
@@ -233,6 +233,17 @@ func TestLegacyAnnotationReconciliation(t *testing.T) {
} }
return nodes, nil return nodes, nil
} }
changeBothPathAnnos := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
for _, rn := range nodes {
if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, "legacy")); err != nil {
return nil, err
}
if err := rn.PipeE(yaml.SetAnnotation(kioutil.PathAnnotation, "new")); err != nil {
return nil, err
}
}
return nodes, nil
}
noops := []Filter{ noops := []Filter{
FilterFunc(noopFilter1), FilterFunc(noopFilter1),
@@ -242,11 +253,13 @@ func TestLegacyAnnotationReconciliation(t *testing.T) {
legacy := []Filter{FilterFunc(changeLegacyAnnos)} legacy := []Filter{FilterFunc(changeLegacyAnnos)}
legacyId := []Filter{FilterFunc(changeLegacyId)} legacyId := []Filter{FilterFunc(changeLegacyId)}
internalId := []Filter{FilterFunc(changeInternalId)} internalId := []Filter{FilterFunc(changeInternalId)}
changeBoth := []Filter{FilterFunc(changeBothPathAnnos), FilterFunc(noopFilter1)}
testCases := map[string]struct { testCases := map[string]struct {
input string input string
filters []Filter filters []Filter
expected string expected string
expectedErr string
}{ }{
// the orchestrator should copy the legacy annotations to the new // the orchestrator should copy the legacy annotations to the new
// annotations // annotations
@@ -510,6 +523,32 @@ data:
grpcPort: 8080 grpcPort: 8080
`, `,
}, },
// the function changes both the legacy and new path annotation,
// so we should get an error
"change both": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
annotations:
config.kubernetes.io/path: 'configmap.yaml'
internal.kubernetes.io/path: 'configmap.yaml'
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
annotations:
config.kubernetes.io/path: "configmap.yaml"
config.kubernetes.io/index: '1'
data:
grpcPort: 8081
`,
filters: changeBoth,
expectedErr: "resource input to function has mismatched legacy and internal path annotations",
},
} }
for tn, tc := range testCases { for tn, tc := range testCases {
@@ -526,8 +565,16 @@ data:
Filters: tc.filters, Filters: tc.filters,
Outputs: []Writer{&input}, Outputs: []Writer{&input},
} }
assert.NoError(t, p.Execute())
assert.Equal(t, tc.expected, out.String()) err := p.Execute()
if tc.expectedErr == "" {
assert.NoError(t, err)
assert.Equal(t, tc.expected, out.String())
} else {
assert.Error(t, err)
assert.Equal(t, tc.expectedErr, err.Error())
}
}) })
} }
} }

View File

@@ -17,25 +17,29 @@ import (
type AnnotationKey = string type AnnotationKey = string
const ( const (
// internalPrefix is the prefix given to internal annotations that are used
// internally by the orchestrator
internalPrefix string = "internal.config.kubernetes.io/"
// IndexAnnotation records the index of a specific resource in a file or input stream. // IndexAnnotation records the index of a specific resource in a file or input stream.
IndexAnnotation AnnotationKey = "internal.config.kubernetes.io/index" IndexAnnotation AnnotationKey = internalPrefix + "index"
// PathAnnotation records the path to the file the Resource was read from // PathAnnotation records the path to the file the Resource was read from
PathAnnotation AnnotationKey = "internal.config.kubernetes.io/path" PathAnnotation AnnotationKey = internalPrefix + "path"
// SeqIndentAnnotation records the sequence nodes indentation of the input resource // SeqIndentAnnotation records the sequence nodes indentation of the input resource
SeqIndentAnnotation AnnotationKey = "internal.config.kubernetes.io/seqindent" SeqIndentAnnotation AnnotationKey = internalPrefix + "seqindent"
// IdAnnotation records the id of the resource to map inputs to outputs // IdAnnotation records the id of the resource to map inputs to outputs
IdAnnotation = "internal.config.kubernetes.io/id" IdAnnotation AnnotationKey = internalPrefix + "id"
// LegacyIndexAnnotation is the deprecated annotation key for resource index // Deprecated: Use IndexAnnotation instead.
LegacyIndexAnnotation AnnotationKey = "config.kubernetes.io/index" LegacyIndexAnnotation AnnotationKey = "config.kubernetes.io/index"
// LegacyPathAnnotation is the deprecated annotation key for resource path // Deprecated: use PathAnnotation instead.
LegacyPathAnnotation AnnotationKey = "config.kubernetes.io/path" LegacyPathAnnotation AnnotationKey = "config.kubernetes.io/path"
// LegacyIdAnnotation is the deprecated annotation key for resource ids // Deprecated: use IdAnnotation instead.
LegacyIdAnnotation = "config.k8s.io/id" LegacyIdAnnotation = "config.k8s.io/id"
) )
@@ -55,6 +59,10 @@ func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
func CopyLegacyAnnotations(rn *yaml.RNode) error { func CopyLegacyAnnotations(rn *yaml.RNode) error {
meta, err := rn.GetMeta() meta, err := rn.GetMeta()
if err != nil { if err != nil {
if err == yaml.ErrMissingMetadata {
// resource has no metadata, this should be a no-op
return nil
}
return err return err
} }
if err := copyAnnotations(meta, rn, LegacyPathAnnotation, PathAnnotation); err != nil { if err := copyAnnotations(meta, rn, LegacyPathAnnotation, PathAnnotation); err != nil {
@@ -71,12 +79,14 @@ func CopyLegacyAnnotations(rn *yaml.RNode) error {
func copyAnnotations(meta yaml.ResourceMeta, rn *yaml.RNode, legacyKey string, newKey string) error { func copyAnnotations(meta yaml.ResourceMeta, rn *yaml.RNode, legacyKey string, newKey string) error {
newValue := meta.Annotations[newKey] newValue := meta.Annotations[newKey]
legacyValue := meta.Annotations[legacyKey]
if newValue != "" { if newValue != "" {
if err := rn.PipeE(yaml.SetAnnotation(legacyKey, newValue)); err != nil { if legacyValue == "" {
return err if err := rn.PipeE(yaml.SetAnnotation(legacyKey, newValue)); err != nil {
return err
}
} }
} else { } else {
legacyValue := meta.Annotations[legacyKey]
if legacyValue != "" { if legacyValue != "" {
if err := rn.PipeE(yaml.SetAnnotation(newKey, legacyValue)); err != nil { if err := rn.PipeE(yaml.SetAnnotation(newKey, legacyValue)); err != nil {
return err return err
@@ -305,3 +315,87 @@ func SortNodes(nodes []*yaml.RNode) error {
}) })
return errors.Wrap(err) return errors.Wrap(err)
} }
// CopyInternalAnnotations copies the annotations that begin with the prefix
// `internal.config.kubernetes.io` from the source RNode to the destination RNode.
// It takes a parameter exclusions, which is a list of annotation keys to ignore.
func CopyInternalAnnotations(src *yaml.RNode, dst *yaml.RNode, exclusions ...AnnotationKey) error {
srcAnnotations := GetInternalAnnotations(src)
for k, v := range srcAnnotations {
if stringSliceContains(exclusions, k) {
continue
}
if err := dst.PipeE(yaml.SetAnnotation(k, v)); err != nil {
return err
}
}
return nil
}
// ConfirmInternalAnnotationUnchanged compares the annotations of the RNodes that begin with the prefix
// `internal.config.kubernetes.io`, throwing an error if they differ. It takes a parameter exclusions,
// which is a list of annotation keys to ignore.
func ConfirmInternalAnnotationUnchanged(r1 *yaml.RNode, r2 *yaml.RNode, exclusions ...AnnotationKey) error {
r1Annotations := GetInternalAnnotations(r1)
r2Annotations := GetInternalAnnotations(r2)
// this is a map to prevent duplicates
diffAnnos := make(map[string]bool)
for k, v1 := range r1Annotations {
if stringSliceContains(exclusions, k) {
continue
}
if v2, ok := r2Annotations[k]; !ok || v1 != v2 {
diffAnnos[k] = true
}
}
for k, v2 := range r2Annotations {
if stringSliceContains(exclusions, k) {
continue
}
if v1, ok := r1Annotations[k]; !ok || v2 != v1 {
diffAnnos[k] = true
}
}
if len(diffAnnos) > 0 {
keys := make([]string, 0, len(diffAnnos))
for k := range diffAnnos {
keys = append(keys, k)
}
sort.Strings(keys)
errorString := "internal annotations differ: "
for _, key := range keys {
errorString = errorString + key + ", "
}
return errors.Errorf(errorString[0 : len(errorString)-2])
}
return nil
}
// GetInternalAnnotations returns a map of all the annotations of the provided RNode that begin
// with the prefix `internal.config.kubernetes.io`
func GetInternalAnnotations(rn *yaml.RNode) map[string]string {
annotations := rn.GetAnnotations()
result := make(map[string]string)
for k, v := range annotations {
if strings.HasPrefix(k, internalPrefix) {
result[k] = v
}
}
return result
}
// stringSliceContains returns true if the slice has the string.
func stringSliceContains(slice []string, str string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}

View File

@@ -374,3 +374,330 @@ func TestCreatePathAnnotationValue(t *testing.T) {
} }
} }
} }
func TestCopyLegacyAnnotations(t *testing.T) {
var tests = []struct {
input string
expected string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/index: '5'
`,
expected: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/index: '5'
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
`,
expected: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/index: '5'
`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/path: 'c/d.yaml'
`,
expected: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/path: 'c/d.yaml'
`,
},
}
for _, tc := range tests {
rw := kio.ByteReadWriter{
Reader: bytes.NewBufferString(tc.input),
OmitReaderAnnotations: true,
}
nodes, err := rw.Read()
assert.NoError(t, err)
assert.NoError(t, kioutil.CopyLegacyAnnotations(nodes[0]))
assert.Equal(t, tc.expected, nodes[0].MustString())
}
}
func TestCopyInternalAnnotations(t *testing.T) {
var tests = []struct {
input string
exclusions []kioutil.AnnotationKey
expected string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: src
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar'
---
apiVersion: v1
kind: Foo
metadata:
name: dst
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
`,
expected: `apiVersion: v1
kind: Foo
metadata:
name: dst
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar'
`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: src
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar-src'
---
apiVersion: v1
kind: Foo
metadata:
name: dst
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
internal.config.kubernetes.io/foo: 'bar-dst'
`,
exclusions: []kioutil.AnnotationKey{
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
},
expected: `apiVersion: v1
kind: Foo
metadata:
name: dst
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
internal.config.kubernetes.io/foo: 'bar-src'
`,
},
}
for _, tc := range tests {
rw := kio.ByteReadWriter{
Reader: bytes.NewBufferString(tc.input),
OmitReaderAnnotations: true,
}
nodes, err := rw.Read()
assert.NoError(t, err)
assert.NoError(t, kioutil.CopyInternalAnnotations(nodes[0], nodes[1], tc.exclusions...))
assert.Equal(t, tc.expected, nodes[1].MustString())
}
}
func TestConfirmInternalAnnotationUnchanged(t *testing.T) {
var tests = []struct {
input string
exclusions []kioutil.AnnotationKey
expectedErr string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
`,
expectedErr: `internal annotations differ: internal.config.kubernetes.io/index, internal.config.kubernetes.io/path`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
`,
exclusions: []kioutil.AnnotationKey{
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
},
expectedErr: ``,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar-1'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
internal.config.kubernetes.io/foo: 'bar-2'
`,
exclusions: []kioutil.AnnotationKey{
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
},
expectedErr: `internal annotations differ: internal.config.kubernetes.io/foo`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar-1'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
internal.config.kubernetes.io/foo: 'bar-1'
`,
expectedErr: `internal annotations differ: internal.config.kubernetes.io/index, internal.config.kubernetes.io/path`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/a: 'b'
internal.config.kubernetes.io/c: 'd'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/e: 'f'
internal.config.kubernetes.io/g: 'h'
`,
expectedErr: `internal annotations differ: internal.config.kubernetes.io/a, internal.config.kubernetes.io/c, internal.config.kubernetes.io/e, internal.config.kubernetes.io/g`,
},
}
for _, tc := range tests {
rw := kio.ByteReadWriter{
Reader: bytes.NewBufferString(tc.input),
OmitReaderAnnotations: true,
}
nodes, err := rw.Read()
assert.NoError(t, err)
err = kioutil.ConfirmInternalAnnotationUnchanged(nodes[0], nodes[1], tc.exclusions...)
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
if err == nil {
t.Fatalf("expected error: %s\n", tc.expectedErr)
}
assert.Equal(t, tc.expectedErr, err.Error())
}
}
}
func TestGetInternalAnnotations(t *testing.T) {
var tests = []struct {
input string
expected map[string]string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
foo: bar
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar'
`,
expected: map[string]string{
"internal.config.kubernetes.io/path": "a/b.yaml",
"internal.config.kubernetes.io/index": "5",
"internal.config.kubernetes.io/foo": "bar",
},
},
}
for _, tc := range tests {
rw := kio.ByteReadWriter{
Reader: bytes.NewBufferString(tc.input),
OmitReaderAnnotations: true,
}
nodes, err := rw.Read()
assert.NoError(t, err)
assert.Equal(t, tc.expected, kioutil.GetInternalAnnotations(nodes[0]))
}
}

View File

@@ -555,6 +555,8 @@ func SetSchema(openAPIField map[string]string, schema []byte, reset bool) error
if schema != nil { // use custom schema if schema != nil { // use custom schema
customSchema = schema customSchema = schema
kubernetesOpenAPIVersion = "custom" kubernetesOpenAPIVersion = "custom"
// if the schema is changed, initSchema should parse the new schema
globalSchema.schemaInit = false
return nil return nil
} }
@@ -567,6 +569,8 @@ func SetSchema(openAPIField map[string]string, schema []byte, reset bool) error
return fmt.Errorf("the specified OpenAPI version is not built in") return fmt.Errorf("the specified OpenAPI version is not built in")
} }
customSchema = nil customSchema = nil
// if the schema is changed, initSchema should parse the new schema
globalSchema.schemaInit = false
return nil return nil
} }

View File

@@ -4,6 +4,7 @@
package resid package resid
import ( import (
"reflect"
"strings" "strings"
) )
@@ -129,3 +130,9 @@ func (id ResId) EffectiveNamespace() string {
} }
return id.Namespace return id.Namespace
} }
// IsEmpty returns true of all of the id's fields are
// empty strings
func (id ResId) IsEmpty() bool {
return reflect.DeepEqual(id, ResId{})
}

View File

@@ -1,6 +1,7 @@
// Code generated by k8scopy from k8s.io/apimachinery@v0.19.8; DO NOT EDIT. // Code generated by k8scopy from k8s.io/apimachinery@v0.19.8; DO NOT EDIT.
// File content copied from k8s.io/apimachinery@v0.19.8/pkg/labels/zz_generated.deepcopy.go // File content copied from k8s.io/apimachinery@v0.19.8/pkg/labels/zz_generated.deepcopy.go
//go:build !ignore_autogenerated
// +build !ignore_autogenerated // +build !ignore_autogenerated
/* /*

View File

@@ -13,6 +13,7 @@ import (
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -82,12 +83,23 @@ func (p *plugin) Transform(m resmap.ResMap) error {
return err return err
} }
for _, res := range resources { for _, res := range resources {
internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode)
err = res.ApplyFilter(patchjson6902.Filter{ err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.JsonOp, Patch: p.JsonOp,
}) })
if err != nil { if err != nil {
return err return err
} }
annotations := res.GetAnnotations()
for key, value := range internalAnnotations {
annotations[key] = value
}
err = res.SetAnnotations(annotations)
if err != nil {
return err
}
} }
return nil return nil
} }

View File

@@ -6,6 +6,7 @@ require (
github.com/evanphx/json-patch v4.11.0+incompatible github.com/evanphx/json-patch v4.11.0+incompatible
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
sigs.k8s.io/kustomize/api v0.8.9 sigs.k8s.io/kustomize/api v0.8.9
sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@@ -13,6 +13,7 @@ import (
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -115,12 +116,19 @@ func (p *plugin) transformJson6902(m resmap.ResMap, patch jsonpatch.Patch) error
} }
for _, res := range resources { for _, res := range resources {
res.StorePreviousId() res.StorePreviousId()
internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode)
err = res.ApplyFilter(patchjson6902.Filter{ err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.Patch, Patch: p.Patch,
}) })
if err != nil { if err != nil {
return err return err
} }
annotations := res.GetAnnotations()
for key, value := range internalAnnotations {
annotations[key] = value
}
err = res.SetAnnotations(annotations)
} }
return nil return nil
} }

View File

@@ -5,6 +5,7 @@ go 1.16
require ( require (
github.com/evanphx/json-patch v4.11.0+incompatible github.com/evanphx/json-patch v4.11.0+incompatible
sigs.k8s.io/kustomize/api v0.8.9 sigs.k8s.io/kustomize/api v0.8.9
sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@@ -4,7 +4,7 @@ go 1.16
require ( require (
sigs.k8s.io/kustomize/api v0.8.9 sigs.k8s.io/kustomize/api v0.8.9
sigs.k8s.io/kustomize/kyaml v0.12.0 sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@@ -4,7 +4,7 @@ go 1.16
require ( require (
sigs.k8s.io/kustomize/api v0.8.9 sigs.k8s.io/kustomize/api v0.8.9
sigs.k8s.io/kustomize/kyaml v0.12.0 sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@@ -11,6 +11,7 @@
[`cloudbuild.yaml`]: cloudbuild.yaml [`cloudbuild.yaml`]: cloudbuild.yaml
[kustomize repo release page]: https://github.com/kubernetes-sigs/kustomize/releases [kustomize repo release page]: https://github.com/kubernetes-sigs/kustomize/releases
[OpenAPI Readme]: ../kyaml/openapi/README.md [OpenAPI Readme]: ../kyaml/openapi/README.md
[project cloud build history page]: https://console.cloud.google.com/cloud-build/builds?project=k8s-staging-kustomize
This document describes how to perform a [semver release] This document describes how to perform a [semver release]
of one of the several [Go modules] in this repository. of one of the several [Go modules] in this repository.
@@ -86,7 +87,7 @@ echo $GITHUB_TOKEN | gh auth login --scopes repo --with-token
#### Establish clean state #### Establish clean state
``` ```
refreshMaster refreshMaster &&
testKustomizeRepo testKustomizeRepo
``` ```
@@ -108,6 +109,9 @@ Note the version:
versionKyaml=v0.10.20 # EDIT THIS! versionKyaml=v0.10.20 # EDIT THIS!
``` ```
See the process of the cloud build job
on the [project cloud build history page].
Undraft the release on the [kustomize repo release page]: Undraft the release on the [kustomize repo release page]:
* Make sure the version number is what you expect. * Make sure the version number is what you expect.
* Remove references to commits that aren't relevant to end users of this module (e.g. test commits, refactors). * Remove references to commits that aren't relevant to end users of this module (e.g. test commits, refactors).
@@ -119,16 +123,18 @@ Undraft the release on the [kustomize repo release page]:
#### Pin to the most recent kyaml #### Pin to the most recent kyaml
``` ```
gorepomod pin kyaml --doIt gorepomod pin kyaml --doIt &&
go mod edit -require=sigs.k8s.io/kustomize/kyaml@$versionKyaml plugin/builtin/prefixsuffixtransformer/go.mod go mod edit -require=sigs.k8s.io/kustomize/kyaml@$versionKyaml plugin/builtin/prefixsuffixtransformer/go.mod &&
go mod edit -require=sigs.k8s.io/kustomize/kyaml@$versionKyaml plugin/builtin/replicacounttransformer/go.mod go mod edit -require=sigs.k8s.io/kustomize/kyaml@$versionKyaml plugin/builtin/replicacounttransformer/go.mod &&
go mod edit -require=sigs.k8s.io/kustomize/kyaml@$versionKyaml plugin/builtin/patchtransformer/go.mod &&
go mod edit -require=sigs.k8s.io/kustomize/kyaml@$versionKyaml plugin/builtin/patchjson6902transformer/go.mod
``` ```
Create the PR: Create the PR:
``` ```
title="Pin to kyaml $versionKyaml" createBranch pinToKyaml "Pin to kyaml $versionKyaml"
createBranch pinToKyaml $title ```
```
createPr createPr
``` ```
@@ -140,12 +146,14 @@ testKustomizeRepo
Wait for tests to pass, then merge the PR: Wait for tests to pass, then merge the PR:
``` ```
gh pr status gh pr status
```
```
gh pr merge -m gh pr merge -m
``` ```
Get back on master and do paranoia test: Get back on master and do paranoia test:
``` ```
refreshMaster refreshMaster &&
testKustomizeRepo testKustomizeRepo
``` ```
@@ -162,6 +170,9 @@ Note the version:
versionCmdConfig=v0.9.12 # EDIT THIS! versionCmdConfig=v0.9.12 # EDIT THIS!
``` ```
See the process of the cloud build job
on the [project cloud build history page].
Undraft the release on the [kustomize repo release page]: Undraft the release on the [kustomize repo release page]:
* Make sure the version number is what you expect. * Make sure the version number is what you expect.
* Remove references to commits that aren't relevant to end users of this module (e.g. test commits, refactors). * Remove references to commits that aren't relevant to end users of this module (e.g. test commits, refactors).
@@ -181,8 +192,7 @@ gorepomod pin cmd/config --doIt
Create the PR: Create the PR:
``` ```
title="Pin to cmd/config $versionCmdConfig" createBranch pinToCmdConfig "Pin to cmd/config $versionCmdConfig" &&
createBranch pinToCmdConfig $title
createPr createPr
``` ```
@@ -194,12 +204,14 @@ testKustomizeRepo
Wait for tests to pass, then merge the PR: Wait for tests to pass, then merge the PR:
``` ```
gh pr status # rinse, repeat gh pr status # rinse, repeat
```
```
gh pr merge -m gh pr merge -m
``` ```
Get back on master and do paranoia test: Get back on master and do paranoia test:
``` ```
refreshMaster refreshMaster &&
testKustomizeRepo testKustomizeRepo
``` ```
@@ -216,6 +228,9 @@ Note the version:
versionApi=v0.8.10 # EDIT THIS! versionApi=v0.8.10 # EDIT THIS!
``` ```
See the process of the cloud build job
on the [project cloud build history page].
Undraft the release on the [kustomize repo release page]: Undraft the release on the [kustomize repo release page]:
* Make sure the version number is what you expect. * Make sure the version number is what you expect.
* Remove references to commits that aren't relevant to end users of this module (e.g. test commits, refactors). * Remove references to commits that aren't relevant to end users of this module (e.g. test commits, refactors).
@@ -232,8 +247,7 @@ gorepomod pin api --doIt
Create the PR: Create the PR:
``` ```
title="Pin to api $versionApi" createBranch pinToApi "Pin to api $versionApi" &&
createBranch pinToApi $title
createPr createPr
``` ```
@@ -245,12 +259,14 @@ testKustomizeRepo
Wait for tests to pass, then merge the PR: Wait for tests to pass, then merge the PR:
``` ```
gh pr status # rinse, repeat gh pr status # rinse, repeat
```
```
gh pr merge -m gh pr merge -m
``` ```
Get back on master and do paranoia test: Get back on master and do paranoia test:
``` ```
refreshMaster refreshMaster &&
testKustomizeRepo testKustomizeRepo
``` ```
@@ -262,6 +278,9 @@ While you're waiting for the tests, review the commit log. Based on the changes
gorepomod release kustomize [patch|minor|major] --doIt gorepomod release kustomize [patch|minor|major] --doIt
``` ```
See the process of the cloud build job
on the [project cloud build history page].
Undraft the release on the [kustomize repo release page]: Undraft the release on the [kustomize repo release page]:
* Make sure the version number is what you expect. * Make sure the version number is what you expect.
* Remove references to commits that aren't relevant to end users of the CLI (e.g. test commits, refactors, changes that only surface in Go). * Remove references to commits that aren't relevant to end users of the CLI (e.g. test commits, refactors, changes that only surface in Go).
@@ -269,14 +288,14 @@ Undraft the release on the [kustomize repo release page]:
## Confirm the kustomize binary is correct ## Confirm the kustomize binary is correct
> [installation instructions]: https://kubectl.docs.kubernetes.io/installation/kustomize/binaries/ [installation instructions]: https://kubectl.docs.kubernetes.io/installation/kustomize/binaries/
>
> * Follow the [installation instructions] to install your new * Follow the [installation instructions] to install your new
> release and make sure it reports the expected version number. release and make sure it reports the expected version number.
>
> If not, something is very wrong. If not, something is very wrong.
>
> * Visit the [release page] and edit the release notes as desired. * Visit the [release page] and edit the release notes as desired.
## Unpin everything ## Unpin everything
@@ -285,15 +304,14 @@ Undraft the release on the [kustomize repo release page]:
Go back into development mode, where all modules depend on in-repo code: Go back into development mode, where all modules depend on in-repo code:
``` ```
gorepomod unpin api --doIt gorepomod unpin api --doIt &&
gorepomod unpin cmd/config --doIt gorepomod unpin cmd/config --doIt &&
gorepomod unpin kyaml --doIt gorepomod unpin kyaml --doIt
``` ```
Create the PR: Create the PR:
``` ```
title="Back to development mode; unpin the modules" createBranch unpinEverything "Back to development mode; unpin the modules" &&
createBranch unpinEverything
createPr createPr
``` ```
@@ -305,12 +323,14 @@ testKustomizeRepo
Wait for tests to pass, then merge the PR: Wait for tests to pass, then merge the PR:
``` ```
gh pr status # rinse, repeat gh pr status # rinse, repeat
```
```
gh pr merge -m gh pr merge -m
``` ```
Get back on master and do paranoia test: Get back on master and do paranoia test:
``` ```
refreshMaster refreshMaster &&
testKustomizeRepo testKustomizeRepo
``` ```
@@ -323,14 +343,17 @@ to test examples against your new release.
``` ```
sed -i "" "s/LATEST_V4_RELEASE=.*/LATEST_V4_RELEASE=v4.3.0/" Makefile sed -i "" "s/LATEST_V4_RELEASE=.*/LATEST_V4_RELEASE=v4.3.0/" Makefile
title="Test examples against latest release" ```
createBranch updateProwExamplesTarget $title ```
createBranch updateProwExamplesTarget "Test examples against latest release" &&
createPr createPr
``` ```
Wait for tests to pass, then merge the PR: Wait for tests to pass, then merge the PR:
``` ```
gh pr status # rinse, repeat gh pr status # rinse, repeat
```
```
gh pr merge -m gh pr merge -m
``` ```
@@ -574,14 +597,32 @@ git push upstream :latest_kustomize
git tag -a latest_kustomize git tag -a latest_kustomize
``` ```
### Optionally build a release locally ### Optionally build locally
[localbuild.sh]: localbuild.sh [localbuild.sh]: localbuild.sh
Install [`cloud-build-local`], then run [localbuild.sh]: Load the same version of `goreleaser` referenced in `cloudbuild.yaml` via docker and run [localbuild.sh] from the container's command line:
``` ```
./releasing/localbuild.sh $module # Get goreleaser image from cloudbuild.yaml
export GORELEASER_IMAGE=goreleaser/goreleaser:v0.172.1
# Drop into a shell
docker run -it --entrypoint=/bin/bash -v $(pwd):/go/src/github.com/kubernetes-sigs/kustomize -w /go/src/github.com/kubernetes-sigs/kustomize $GORELEASER_IMAGE
# Run build
./releasing/localbuild.sh TAG [--snapshot]
```
### Optionally build and release locally
[cloudbuild-local.sh]: cloudbuild-local.sh
Install [`cloud-build-local`], then run [cloudbuild-local.sh]:
```
./releasing/cloudbuild-local.sh $module
``` ```
This should create release artifacts in a local directory. This should create release artifacts in a local directory.

54
releasing/cloudbuild-local.sh Executable file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
#
# To test the release process, this script attempts
# to use a Google cloudbuild configuration to create
# release artifacts locally.
#
# See https://cloud.google.com/cloud-build/docs/build-debug-locally
#
# Usage: from the repo root, enter:
#
# ./releasing/cloudbuild-local.sh kustomize/v1.2.3
#
# or some other valid tag value.
#
# IMPORTANT:
# The process clones the repo at the given tag,
# so the repo must have the tag applied upstream.
# Either use an old tag, or disable the cloud build
# trigger so that a new testing tag can be applied
# without setting off a cloud build.
set -e
config=$(mktemp)
cp releasing/cloudbuild.yaml $config
# Add the --snapshot flag to suppress the
# github release and leave the build output
# in the kustomize/dist directory.
sed -i "s|# - '--snapshot|- '--snapshot|" $config
echo "Executing cloud-build-local with config file $config :"
echo "========================="
cat $config
echo "========================="
workspace=~/cloud-build-local-workspace
cloud-build-local \
--config=$config \
--substitutions=TAG_NAME=$1 \
--write-workspace=$workspace \
--dryrun=false \
.
# --bind-mount-source \
echo " "
echo "Result of local build:"
echo "##########################################"
tree ./$module/dist
echo "##########################################"
tree ./$workspace
echo "##########################################"

View File

@@ -92,10 +92,13 @@ builds:
goos: goos:
- linux - linux
- darwin - darwin
- windows
goarch: goarch:
- amd64 - amd64
- arm64 - arm64
- s390x
- ppc64le
checksum: checksum:
name_template: 'checksums.txt' name_template: 'checksums.txt'
@@ -117,8 +120,9 @@ cat $goReleaserConfigFile
date date
time /usr/local/bin/goreleaser release \ time /usr/local/bin/goreleaser release \
--debug \
--timeout 10m \ --timeout 10m \
--parallelism 4 \ --parallelism 7 \
--config=$goReleaserConfigFile \ --config=$goReleaserConfigFile \
--release-notes=$changeLogFile \ --release-notes=$changeLogFile \
--rm-dist \ --rm-dist \

View File

@@ -57,6 +57,6 @@ timeout: 14m
# The base64 of that is shown below. It's decrypted by cloud build # The base64 of that is shown below. It's decrypted by cloud build
# and provided back to goreleaser. # and provided back to goreleaser.
secrets: secrets:
- kmsKeyName: projects/jregan-corp-gke-dev/locations/global/keyRings/kust-cloud-key-ring/cryptoKeys/kust-cloud-key-name - kmsKeyName: projects/k8s-staging-kustomize/locations/global/keyRings/kust-cloud-key-ring/cryptoKeys/kust-cloud-key-name
secretEnv: secretEnv:
GITHUB_TOKEN: CiQAwfbOkSP4tJf3ZJZMjzHaRPZ2RxiQhORZ3xxlVtpoy8631uQSUACk6WMKjtkpsRkRl+uxWUVvN29M5qveyXjaDDO094/qwsSc8RiYlHYt7Ii1bWkkz3P1kG0nHfG7Fd46A+GJ6R5NhmNfingd/nu9iKrNwLXK GITHUB_TOKEN: CiQAJ+XRL07Aror04bf6N0PpMDxRpxzs1PXVVDztB+HNu3fW7FESUQA2EggaBGI1cpFJC1YT93h9r50WzyLMD28LDDBTO8QJxZsU6UEToBfpVDr0ohnSazBTbvCcy5NJK0ooKyDifFsKzkT5ym3LPyHzPIXiejCmAg==

View File

@@ -1,54 +1,127 @@
#!/bin/bash #!/bin/bash
# #
# To test the release process, this script attempts # Works exactly like cloudbuild.sh but doesn't perform a release.
# to use a Google cloudbuild configuration to create
# release artifacts locally.
# #
# See https://cloud.google.com/cloud-build/docs/build-debug-locally # Usage (from top of repo):
# #
# Usage: from the repo root, enter: # releasing/localbuild.sh TAG [--snapshot]
# #
# ./releasing/localbuild.sh kustomize/v1.2.3 # Where TAG is in the form
# #
# or some other valid tag value. # api/v1.2.3
# kustomize/v1.2.3
# cmd/config/v1.2.3
# ... etc.
#
# This script runs a build through goreleaser (http://goreleaser.com) but nothing else.
# #
# IMPORTANT:
# The process clones the repo at the given tag,
# so the repo must have the tag applied upstream.
# Either use an old tag, or disable the cloud build
# trigger so that a new testing tag can be applied
# without setting off a cloud build.
set -e set -e
set -x
config=$(mktemp) fullTag=$1
cp releasing/cloudbuild.yaml $config shift
echo "fullTag=$fullTag"
# Add the --snapshot flag to suppress the remainingArgs="$@"
# github release and leave the build output echo "Remaining args: $remainingArgs"
# in the kustomize/dist directory.
sed -i "s|# - '--snapshot|- '--snapshot|" $config
echo "Executing cloud-build-local with config file $config :" # Take everything before the last slash.
echo "=========================" # This is expected to match $module.
cat $config module=${fullTag%/*}
echo "=========================" echo "module=$module"
workspace=~/cloud-build-local-workspace # Find previous tag that matches the tags module
prevTag=$(git tag -l "$module*" --sort=-version:refname --no-contains=$fullTag | head -n 1)
cloud-build-local \ # Generate the changelog for this release
--config=$config \ # using the last two tags for the module
--substitutions=TAG_NAME=$1 \ changeLogFile=$(mktemp)
--write-workspace=$workspace \ git log $prevTag..$fullTag \
--dryrun=false \ --pretty=oneline \
. --abbrev-commit --no-decorate --no-color --no-merges \
-- $module > $changeLogFile
echo "Release notes:"
cat $changeLogFile
# --bind-mount-source \ # Take everything after the last slash.
# This should be something like "v1.2.3".
semVer=`echo $fullTag | sed "s|$module/||"`
echo "semVer=$semVer"
echo " " # This is probably a directory called /workspace
echo "Result of local build:" echo "pwd = $PWD"
echo "##########################################"
tree ./$module/dist # Sanity check
echo "##########################################" echo "### ls -las . ################################"
tree ./$workspace ls -las .
echo "##########################################" echo "###################################"
# CD into the module directory.
# This directory expected to contain a main.go, so there's
# no need for extra details in the `build` stanza below.
cd $module
skipBuild=true
if [[ "$module" == "kustomize" || "$module" == "pluginator" ]]; then
# If releasing a main program, don't skip the build.
skipBuild=false
fi
goReleaserConfigFile=$(mktemp)
cat <<EOF >$goReleaserConfigFile
project_name: $module
archives:
- name_template: "${module}_${semVer}_{{ .Os }}_{{ .Arch }}"
builds:
- skip: $skipBuild
ldflags: >
-s
-X sigs.k8s.io/kustomize/api/provenance.version={{.Version}}
-X sigs.k8s.io/kustomize/api/provenance.gitCommit={{.Commit}}
-X sigs.k8s.io/kustomize/api/provenance.buildDate={{.Date}}
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
- s390x
- ppc64le
checksum:
name_template: 'checksums.txt'
env:
- CGO_ENABLED=0
- GO111MODULE=on
release:
github:
owner: kubernetes-sigs
name: kustomize
draft: true
EOF
cat $goReleaserConfigFile
date
time /usr/local/bin/goreleaser build \
--debug \
--timeout 10m \
--parallelism 7 \
--config=$goReleaserConfigFile \
--rm-dist \
--skip-validate $remainingArgs
date