mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 10:30:59 +00:00
Compare commits
55 Commits
release-ku
...
kustomize/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63ec6bdb3d | ||
|
|
25bfe6f306 | ||
|
|
febfaf16dc | ||
|
|
8268b17700 | ||
|
|
0889995a61 | ||
|
|
4e476ae574 | ||
|
|
7efd7d23fe | ||
|
|
6fb944815b | ||
|
|
5e3432fbbe | ||
|
|
f0c6bd7773 | ||
|
|
dd579c905d | ||
|
|
22b735885a | ||
|
|
b7c5058e37 | ||
|
|
baff5f4359 | ||
|
|
dce4ea5846 | ||
|
|
c47fc48607 | ||
|
|
1d9b6cbe57 | ||
|
|
1cb93123fc | ||
|
|
c6cb42ec27 | ||
|
|
67a5f6d68f | ||
|
|
e997cc5486 | ||
|
|
53577a5190 | ||
|
|
c1ae234a64 | ||
|
|
02cb395ec2 | ||
|
|
65e7529ca0 | ||
|
|
f70743b267 | ||
|
|
f4382738ab | ||
|
|
a100dca303 | ||
|
|
50414208d1 | ||
|
|
e17a007719 | ||
|
|
dd3c5f5c0a | ||
|
|
fb3f560e0c | ||
|
|
12c177a365 | ||
|
|
402f6ca72b | ||
|
|
2b8a39373e | ||
|
|
17f18604e4 | ||
|
|
99e404cb61 | ||
|
|
d4e3b4f832 | ||
|
|
6552b90657 | ||
|
|
bf57d698b1 | ||
|
|
4d002af735 | ||
|
|
2bfc7cc1b0 | ||
|
|
0244f0919e | ||
|
|
f122fb12f3 | ||
|
|
7d0b7e2113 | ||
|
|
c3a67cfdca | ||
|
|
4315e982be | ||
|
|
634464353f | ||
|
|
9c36004493 | ||
|
|
1b1034442c | ||
|
|
a89863c84c | ||
|
|
f7340e0615 | ||
|
|
bf6b207cc9 | ||
|
|
f93b4877f7 | ||
|
|
3e7246690f |
@@ -1,6 +1,25 @@
|
||||
[SIG-CLI]: https://github.com/kubernetes/community/tree/master/sig-cli
|
||||
[Slack channel]: https://kubernetes.slack.com/messages/kustomize
|
||||
[Mailing list]: https://groups.google.com/forum/#!forum/kubernetes-sig-cli
|
||||
|
||||
[OWNERS file spec]: https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md
|
||||
[Kustomize OWNERS_ALIASES]: https://github.com/kubernetes-sigs/kustomize/blob/8049f7b1af52e8a7ec26faf6cf714f560d0043c5/OWNERS_ALIASES
|
||||
[SIG-CLI Teams]: https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml
|
||||
[Github permissions]: https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level
|
||||
|
||||
[Contributor License Agreement]: https://git.k8s.io/community/CLA.md
|
||||
[Kubernetes Contributor Guide]: http://git.k8s.io/community/contributors/guide
|
||||
[Contributor Cheat Sheet]: https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md
|
||||
[CNCF Code of Conduct]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md
|
||||
[Kubernetes Community Membership]: https://github.com/kubernetes/community/blob/master/community-membership.md
|
||||
|
||||
[Contribution Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/
|
||||
[MacOS Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/mac/
|
||||
[Windows Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/windows/
|
||||
|
||||
# Contributing Guidelines
|
||||
|
||||
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt:
|
||||
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the [CNCF Code of Conduct]. Here is an excerpt:
|
||||
|
||||
_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._
|
||||
|
||||
@@ -8,13 +27,22 @@ _As contributors and maintainers of this project, and in the interest of fosteri
|
||||
|
||||
Dev guides:
|
||||
|
||||
- [Mac](docs/macDevGuide.md)
|
||||
- [Contribution Guide]
|
||||
- [MacOS Dev Guide]
|
||||
- [Windows Dev Guide]
|
||||
|
||||
We have full documentation on how to get started contributing here:
|
||||
General resources for contributors:
|
||||
|
||||
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
|
||||
- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing)
|
||||
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md) - Common resources for existing developers
|
||||
- [Contributor License Agreement] - Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests.
|
||||
- [Kubernetes Contributor Guide] - Main contributor documentation.
|
||||
- [Contributor Cheat Sheet] - Common resources for existing developers.
|
||||
|
||||
Here are some additional ideas to help you get started with Kustomize:
|
||||
- Attend a Kustomize Bug Scrub. Check the [SIG-CLI] meetings list to find the next one.
|
||||
- Help triage issues by confirming validity and applying the appropriate `kind` label (e.g. comment `/kind bug`).
|
||||
- Pick up an issue to fix. Issues with the `help-wanted` label are a good place to start, but you can also look for any issue with the `triage/accepted` label and no assignee. Remember to `/assign` yourself to let others know you're working on it.
|
||||
- Help confirm new issues labelled `kind/bug` by reproducing them with the latest release.
|
||||
- Support Kustomize users by responding to questions on issues labelled `kind/support` or in the [Slack channel].
|
||||
|
||||
## Mentorship
|
||||
|
||||
@@ -22,19 +50,22 @@ We have full documentation on how to get started contributing here:
|
||||
|
||||
## Contributor Ladder
|
||||
|
||||
Kustomize generally follows the [Kubernetes Community Membership](https://github.com/kubernetes/community/blob/master/community-membership.md) contributor ladder. Roles are as follows:
|
||||
Kustomize follows the [Kubernetes Community Membership] contributor ladder. Roles are as follows:
|
||||
|
||||
1. Contributor: Anyone who actively contributes code, issues or reviews to the project. There are no Kustomize-specific requirements for this status. All contributors must [sign the CLA](https://github.com/kubernetes/community/tree/master/contributors/guide#prerequisites).
|
||||
1. Member/Reviewer: All Kubernetes-SIGs org members have LGTM rights on the Kustomize repo. There are no Kustomize-specific requirements. Kustomize does not currently have any formal reviewers, but the role will be created if there is interest.
|
||||
1. Maintainer/Approver: Highly experienced active reviewer and contributor to Kustomize. Has both LTGM and approval rights on the Kustomize repo, as well as [Github "maintain" rights](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level).
|
||||
1. Admin/Owner: Maintainer who sets technical direction and makes or approves design decisions for the project. Has LGTM and approval rights on the Kustomize repo as well as [Github "admin" rights](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level).
|
||||
1. Contributor: Anyone who actively contributes code, issues or reviews to the project. All contributors must sign the [Contributor License Agreement].
|
||||
1. Reviewer: Contributors with a history of review and authorship on Kustomize. Has LGTM rights on the Kustomize repo (as do all kubernetes-sigs org members). Active contributors are encouraged to join the reviewers list to be automatically pinged on PRs.
|
||||
1. Approver: Highly experienced active reviewer and contributor to Kustomize. Has both LTGM and approval rights on the Kustomize repo, as well as "maintain" [Github permissions].
|
||||
1. Owner: Approver who sets technical direction and makes or approves design decisions for the project. Has LGTM and approval rights on the Kustomize repo as well as "admin" [Github permissions].
|
||||
|
||||
The kyaml module within the Kustomize repo has additional owners following the same ladder.
|
||||
|
||||
Administrative notes:
|
||||
- Maintainers and admins must be added to the appropriate list both [in the Kustomize repo](https://github.com/kubernetes-sigs/kustomize/blob/8049f7b1af52e8a7ec26faf6cf714f560d0043c5/OWNERS_ALIASES) and [in the community repo](https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml). If this isn't done, the individual in question will lack either PR approval rights (Kustomize list) or the appropriate Github repository permissions (community list).
|
||||
- The spec for the OWNERS file is [in the community repo](https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md).
|
||||
|
||||
- The [OWNERS file spec] is a useful resources in making changes.
|
||||
- Maintainers and admins must be added to the appropriate lists in both [Kustomize OWNERS_ALIASES] and [SIG-CLI Teams]. If this isn't done, the individual in question will lack either PR approval rights (Kustomize list) or the appropriate Github repository permissions (community list).
|
||||
|
||||
|
||||
## Contact Information
|
||||
|
||||
- [Slack channel](https://kubernetes.slack.com/messages/sig-cli)
|
||||
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-cli)
|
||||
- [Slack channel]
|
||||
- [Mailing list]
|
||||
|
||||
11
Makefile
11
Makefile
@@ -12,6 +12,7 @@ MYGOBIN = $(shell go env GOPATH)/bin
|
||||
endif
|
||||
export PATH := $(MYGOBIN):$(PATH)
|
||||
MODULES := '"cmd/config" "api/" "kustomize/" "kyaml/"'
|
||||
LATEST_V4_RELEASE=v4.3.0
|
||||
|
||||
# Provide defaults for REPO_OWNER and REPO_NAME if not present.
|
||||
# Typically these values would be provided by Prow.
|
||||
@@ -31,7 +32,7 @@ verify-kustomize: \
|
||||
lint-kustomize \
|
||||
test-unit-kustomize-all \
|
||||
test-examples-kustomize-against-HEAD \
|
||||
test-examples-kustomize-against-4.1
|
||||
test-examples-kustomize-against-v4-release
|
||||
|
||||
# The following target referenced by a file in
|
||||
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
|
||||
@@ -44,7 +45,7 @@ prow-presubmit-check: \
|
||||
test-unit-cmd-all \
|
||||
test-go-mod \
|
||||
test-examples-kustomize-against-HEAD \
|
||||
test-examples-kustomize-against-4.1
|
||||
test-examples-kustomize-against-v4-release
|
||||
|
||||
.PHONY: verify-kustomize-e2e
|
||||
verify-kustomize-e2e: test-examples-e2e-kustomize
|
||||
@@ -278,8 +279,8 @@ test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
|
||||
./hack/testExamplesAgainstKustomize.sh HEAD
|
||||
|
||||
.PHONY:
|
||||
test-examples-kustomize-against-4.1: $(MYGOBIN)/mdrip
|
||||
./hack/testExamplesAgainstKustomize.sh v4@v4.1.2
|
||||
test-examples-kustomize-against-v4-release: $(MYGOBIN)/mdrip
|
||||
./hack/testExamplesAgainstKustomize.sh v4@$(LATEST_V4_RELEASE)
|
||||
|
||||
# linux only.
|
||||
# This is for testing an example plugin that
|
||||
@@ -319,7 +320,7 @@ $(MYGOBIN)/helmV3:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
tgzFile=helm-v3.5.3-$(GOOS)-$(GOARCH).tar.gz; \
|
||||
tgzFile=helm-v3.6.3-$(GOOS)-$(GOARCH).tar.gz; \
|
||||
wget https://get.helm.sh/$$tgzFile; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \
|
||||
|
||||
6
OWNERS
6
OWNERS
@@ -1,4 +1,6 @@
|
||||
# See https://github.com/kubernetes/community/blob/master/community-membership.md
|
||||
approvers:
|
||||
- kustomize-admins
|
||||
- kustomize-maintainers
|
||||
- kustomize-approvers
|
||||
|
||||
reviewers:
|
||||
- kustomize-reviewers
|
||||
|
||||
@@ -1,16 +1,30 @@
|
||||
# Keep *-admins and *-maintainers list in sync with corresponding lists in
|
||||
# Keep *-owners and *-approvers lists in sync with *-admins and *-maintainers in
|
||||
# https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml
|
||||
aliases:
|
||||
kustomize-admins:
|
||||
kustomize-owners:
|
||||
- knverey
|
||||
- monopole
|
||||
- pwittrock
|
||||
kustomize-maintainers:
|
||||
kustomize-approvers:
|
||||
- justinsb
|
||||
- mortent
|
||||
- knverey
|
||||
- monopole
|
||||
- natasha41575
|
||||
- phanimarupaka
|
||||
- Shell32-Natsu
|
||||
emeritus-maintainers:
|
||||
- liujingfang1
|
||||
- pwittrock
|
||||
kustomize-reviewers:
|
||||
- knverey
|
||||
- monopole
|
||||
- natasha41575
|
||||
|
||||
kyaml-approvers:
|
||||
- mengqiy
|
||||
- mortent
|
||||
- phanimarupaka
|
||||
kyaml-reviewers:
|
||||
- mengqiy
|
||||
- mortent
|
||||
- phanimarupaka
|
||||
|
||||
emeritus-approvers:
|
||||
- liujingfang1
|
||||
- Shell32-Natsu
|
||||
|
||||
@@ -56,6 +56,7 @@ func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
// sanity check.
|
||||
return nil, err
|
||||
}
|
||||
f.NameFieldToUpdate.Gvk = f.Referrer.GetGvk()
|
||||
if err := node.PipeE(fieldspec.Filter{
|
||||
FieldSpec: f.NameFieldToUpdate,
|
||||
SetValue: f.set,
|
||||
|
||||
@@ -250,6 +250,7 @@ metadata:
|
||||
name: dep
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
data:
|
||||
slice:
|
||||
- false
|
||||
@@ -276,6 +277,7 @@ metadata:
|
||||
name: dep
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
data:
|
||||
1: str
|
||||
: invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`,
|
||||
|
||||
@@ -131,10 +131,11 @@ func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !rn.IsNilOrEmpty() {
|
||||
return getRefinedValue(r.Source.Options, rn)
|
||||
if rn.IsNilOrEmpty() {
|
||||
return nil, fmt.Errorf("fieldPath `%s` is missing for replacement source %s", r.Source.FieldPath, r.Source)
|
||||
}
|
||||
return rn, nil
|
||||
|
||||
return getRefinedValue(r.Source.Options, rn)
|
||||
}
|
||||
|
||||
func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode, error) {
|
||||
|
||||
@@ -1556,6 +1556,38 @@ spec:
|
||||
name: postgresdb
|
||||
`,
|
||||
},
|
||||
|
||||
"replacement source.fieldPath does not exist": {
|
||||
input: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
data:
|
||||
grpcPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-to
|
||||
data:
|
||||
grpcPort: 8081
|
||||
`,
|
||||
replacements: `replacements:
|
||||
- source:
|
||||
kind: ConfigMap
|
||||
name: ports-from
|
||||
fieldPath: data.httpPort
|
||||
targets:
|
||||
- select:
|
||||
kind: ConfigMap
|
||||
name: ports-to
|
||||
fieldPaths:
|
||||
- data.grpcPort
|
||||
options:
|
||||
create: true
|
||||
`,
|
||||
expectedErr: "fieldPath `data.httpPort` is missing for replacement source ~G_~V_ConfigMap|~X|ports-from:data.httpPort",
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
|
||||
@@ -11,6 +11,6 @@ require (
|
||||
github.com/stretchr/testify v1.5.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
@@ -223,8 +223,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
)
|
||||
|
||||
type nameReferenceTransformer struct {
|
||||
@@ -109,11 +110,18 @@ func debug(fMap filterMap) {
|
||||
// 'spec/scaleTargetRef/name' field. Return a filter that can do that.
|
||||
func (t *nameReferenceTransformer) determineFilters(
|
||||
resources []*resource.Resource) (fMap filterMap) {
|
||||
|
||||
// We cache the resource OrgId values because they don't change and otherwise are very visible in a memory pprof
|
||||
resourceOrgIds := make([]resid.ResId, len(resources))
|
||||
for i, resource := range resources {
|
||||
resourceOrgIds[i] = resource.OrgId()
|
||||
}
|
||||
|
||||
fMap = make(filterMap)
|
||||
for _, backReference := range t.backRefs {
|
||||
for _, referrerSpec := range backReference.Referrers {
|
||||
for _, res := range resources {
|
||||
if res.OrgId().IsSelected(&referrerSpec.Gvk) {
|
||||
for i, res := range resources {
|
||||
if resourceOrgIds[i].IsSelected(&referrerSpec.Gvk) {
|
||||
// If this is true, the res might be a referrer, and if
|
||||
// so, the name reference it holds might need an update.
|
||||
if resHasField(res, referrerSpec.Path) {
|
||||
|
||||
@@ -168,3 +168,23 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
|
||||
return ra.Transform(
|
||||
newNameReferenceTransformer(ra.tConfig.NameReference))
|
||||
}
|
||||
|
||||
// Intersection drops the resources which "other" does not have.
|
||||
func (ra *ResAccumulator) Intersection(other resmap.ResMap) error {
|
||||
for _, curId := range ra.resMap.AllIds() {
|
||||
toDelete := true
|
||||
for _, otherId := range other.AllIds() {
|
||||
if otherId == curId {
|
||||
toDelete = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if toDelete {
|
||||
err := ra.resMap.Remove(curId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/builtins"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||
@@ -218,9 +219,26 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator, origin *r
|
||||
return nil, errors.Wrapf(
|
||||
err, "merging vars %v", kt.kustomization.Vars)
|
||||
}
|
||||
err = kt.IgnoreLocal(ra)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ra, nil
|
||||
}
|
||||
|
||||
// IgnoreLocal drops the local resource by checking the annotation "config.kubernetes.io/local-config".
|
||||
func (kt *KustTarget) IgnoreLocal(ra *accumulator.ResAccumulator) error {
|
||||
rf := kt.rFactory.RF()
|
||||
if rf.IncludeLocalConfigs {
|
||||
return nil
|
||||
}
|
||||
remainRes, err := rf.DropLocalNodes(ra.ResMap().ToRNodeSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ra.Intersection(kt.rFactory.FromResourceSlice(remainRes))
|
||||
}
|
||||
|
||||
func (kt *KustTarget) runGenerators(
|
||||
ra *accumulator.ResAccumulator) error {
|
||||
var generators []resmap.Generator
|
||||
|
||||
@@ -685,3 +685,167 @@ spec:
|
||||
name: new-name
|
||||
`)
|
||||
}
|
||||
|
||||
func TestNameReferenceAfterJsonPatch(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteF("resources.yaml", `
|
||||
apiVersion: v1
|
||||
data:
|
||||
bar: bar
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: foo
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
foo: foo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
foo: foo
|
||||
spec:
|
||||
containers:
|
||||
- name: foo
|
||||
image: example
|
||||
volumeMounts:
|
||||
- mountPath: /path
|
||||
name: myvol
|
||||
volumes:
|
||||
- configMap:
|
||||
name: cm
|
||||
name: myvol
|
||||
`)
|
||||
th.WriteK(".", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: foo-
|
||||
resources:
|
||||
- resources.yaml
|
||||
patches:
|
||||
- target:
|
||||
group: apps
|
||||
version: v1
|
||||
name: foo
|
||||
patch: |
|
||||
- op: replace
|
||||
path: /kind
|
||||
value: Deployment
|
||||
`)
|
||||
m := th.Run(".", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `apiVersion: v1
|
||||
data:
|
||||
bar: bar
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: foo-cm
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: foo-foo
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
foo: foo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
foo: foo
|
||||
spec:
|
||||
containers:
|
||||
- image: example
|
||||
name: foo
|
||||
volumeMounts:
|
||||
- mountPath: /path
|
||||
name: myvol
|
||||
volumes:
|
||||
- configMap:
|
||||
name: foo-cm
|
||||
name: myvol
|
||||
`)
|
||||
}
|
||||
|
||||
func TestNameReferenceAfterJsonPatchConfigMapGenerator(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteF("statefulset.yaml", `
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: foo
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
foo: foo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
foo: foo
|
||||
spec:
|
||||
containers:
|
||||
- name: foo
|
||||
image: example
|
||||
volumeMounts:
|
||||
- mountPath: /path
|
||||
name: myvol
|
||||
volumes:
|
||||
- configMap:
|
||||
name: cm
|
||||
name: myvol
|
||||
`)
|
||||
th.WriteK(".", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- statefulset.yaml
|
||||
patches:
|
||||
- target:
|
||||
group: apps
|
||||
version: v1
|
||||
name: foo
|
||||
patch: |
|
||||
- op: replace
|
||||
path: /kind
|
||||
value: Deployment
|
||||
configMapGenerator:
|
||||
- name: cm
|
||||
literals:
|
||||
- bar=bar
|
||||
`)
|
||||
m := th.Run(".", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: foo
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
foo: foo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
foo: foo
|
||||
spec:
|
||||
containers:
|
||||
- image: example
|
||||
name: foo
|
||||
volumeMounts:
|
||||
- mountPath: /path
|
||||
name: myvol
|
||||
volumes:
|
||||
- configMap:
|
||||
name: cm-8hm8224gfd
|
||||
name: myvol
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
bar: bar
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm-8hm8224gfd
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
// used instead of performing an exec to a kustomize CLI subprocess.
|
||||
// To use, load a filesystem with kustomization files (any
|
||||
// number of overlays and bases), then make a Kustomizer
|
||||
// injected with the given fileystem, then call Run.
|
||||
// injected with the given filesystem, then call Run.
|
||||
type Kustomizer struct {
|
||||
options *Options
|
||||
depProvider *provider.DepProvider
|
||||
|
||||
70
api/krusty/localconfig_test.go
Normal file
70
api/krusty/localconfig_test.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package krusty_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
)
|
||||
|
||||
// This test checks that if a resource is annotated as "local-config", this resource won't be ignored until
|
||||
// all transformations have completed. This makes sure the local resource can be used as a transformation input.
|
||||
// See https://github.com/kubernetes-sigs/kustomize/issues/4124 for details.
|
||||
func TestSKipLocalConfigAfterTransform(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK(".", `apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- pod.yaml
|
||||
- deployment.yaml
|
||||
transformers:
|
||||
- replacement.yaml
|
||||
`)
|
||||
th.WriteF("pod.yaml", `apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: buildup
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
containers:
|
||||
- name: app
|
||||
image: nginx
|
||||
`)
|
||||
th.WriteF("deployment.yaml", `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: buildup
|
||||
`)
|
||||
th.WriteF("replacement.yaml", `
|
||||
apiVersion: builtin
|
||||
kind: ReplacementTransformer
|
||||
metadata:
|
||||
name: buildup
|
||||
replacements:
|
||||
- source:
|
||||
kind: Pod
|
||||
fieldPath: spec
|
||||
targets:
|
||||
- select:
|
||||
kind: Deployment
|
||||
fieldPaths:
|
||||
- spec.template.spec
|
||||
options:
|
||||
create: true
|
||||
`)
|
||||
m := th.Run(".", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: buildup
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: app
|
||||
`)
|
||||
}
|
||||
@@ -42,6 +42,26 @@ spec:
|
||||
`)
|
||||
}
|
||||
|
||||
func writeOtherCustomResource(th kusttest_test.Harness, filepath string) {
|
||||
th.WriteF(filepath, `
|
||||
apiVersion: v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: server
|
||||
command: example
|
||||
ports:
|
||||
- name: grpc
|
||||
protocol: TCP
|
||||
containerPort: 8080
|
||||
`)
|
||||
}
|
||||
|
||||
func writeTestComponentWithCustomSchema(th kusttest_test.Harness) {
|
||||
writeTestSchema(th, "comp/")
|
||||
openapi.ResetOpenAPI()
|
||||
@@ -74,6 +94,32 @@ patchesStrategicMerge:
|
||||
image: nginx
|
||||
`
|
||||
|
||||
const customSchemaPatchMultipleGvks = `
|
||||
patchesStrategicMerge:
|
||||
- |-
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: nginx
|
||||
- |-
|
||||
apiVersion: v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: nginx
|
||||
`
|
||||
|
||||
const patchedCustomResource = `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
@@ -108,6 +154,54 @@ openapi:
|
||||
th.AssertActualEqualsExpected(m, patchedCustomResource)
|
||||
}
|
||||
|
||||
func TestCustomOpenApiFieldWithTwoGvks(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK(".", `
|
||||
resources:
|
||||
- mycrd.yaml
|
||||
- myothercrd.yaml
|
||||
openapi:
|
||||
path: mycrd_schema.json
|
||||
`+customSchemaPatchMultipleGvks)
|
||||
writeCustomResource(th, "mycrd.yaml")
|
||||
writeOtherCustomResource(th, "myothercrd.yaml")
|
||||
writeTestSchema(th, "./")
|
||||
openapi.ResetOpenAPI()
|
||||
m := th.Run(".", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `apiVersion: example.com/v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- command: example
|
||||
image: nginx
|
||||
name: server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: grpc
|
||||
protocol: TCP
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: MyCRD
|
||||
metadata:
|
||||
name: service
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- command: example
|
||||
image: nginx
|
||||
name: server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: grpc
|
||||
protocol: TCP
|
||||
`)
|
||||
}
|
||||
|
||||
func TestCustomOpenApiFieldYaml(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK(".", `
|
||||
|
||||
5
api/krusty/testdata/customschema.json
vendored
5
api/krusty/testdata/customschema.json
vendored
@@ -34,6 +34,11 @@
|
||||
"group": "example.com",
|
||||
"kind": "MyCRD",
|
||||
"version": "v1alpha1"
|
||||
},
|
||||
{
|
||||
"group": "",
|
||||
"kind": "MyCRD",
|
||||
"version": "v1alpha1"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
3
api/krusty/testdata/customschema.yaml
vendored
3
api/krusty/testdata/customschema.yaml
vendored
@@ -22,6 +22,9 @@ definitions:
|
||||
- group: example.com
|
||||
kind: MyCRD
|
||||
version: v1alpha1
|
||||
- group: ""
|
||||
kind: MyCRD
|
||||
version: v1alpha1
|
||||
io.k8s.api.core.v1.PodTemplateSpec:
|
||||
properties:
|
||||
metadata:
|
||||
|
||||
@@ -124,14 +124,34 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
||||
return rf.resourcesFromRNodes(nodes), nil
|
||||
}
|
||||
|
||||
// DropLocalNodes removes the local nodes by default. Local nodes are detected via the annotation `config.kubernetes.io/local-config: "true"`
|
||||
func (rf *Factory) DropLocalNodes(nodes []*yaml.RNode) ([]*Resource, error) {
|
||||
var result []*yaml.RNode
|
||||
for _, node := range nodes {
|
||||
if node.IsNilOrEmpty() {
|
||||
continue
|
||||
}
|
||||
md, err := node.GetValidatedMetadata()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rf.IncludeLocalConfigs {
|
||||
result = append(result, node)
|
||||
continue
|
||||
}
|
||||
localConfig, exist := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
|
||||
if !exist || localConfig == "false" {
|
||||
result = append(result, node)
|
||||
}
|
||||
}
|
||||
return rf.resourcesFromRNodes(result), nil
|
||||
}
|
||||
|
||||
// ResourcesFromRNodes converts RNodes to Resources.
|
||||
func (rf *Factory) ResourcesFromRNodes(
|
||||
nodes []*yaml.RNode) (result []*Resource, err error) {
|
||||
nodes, err = rf.dropBadNodes(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.resourcesFromRNodes(nodes), nil
|
||||
return rf.DropLocalNodes(nodes)
|
||||
}
|
||||
|
||||
// resourcesFromRNode assumes all nodes are good.
|
||||
@@ -143,7 +163,7 @@ func (rf *Factory) resourcesFromRNodes(
|
||||
return
|
||||
}
|
||||
|
||||
func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
|
||||
func (rf *Factory) RNodesFromBytes(b []byte) ([]*yaml.RNode, error) {
|
||||
nodes, err := kio.FromBytes(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -152,9 +172,17 @@ func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.inlineAnyEmbeddedLists(nodes)
|
||||
}
|
||||
|
||||
// inlineAnyEmbeddedLists scans the RNode slice for nodes named FooList.
|
||||
// Such nodes are expected to be lists of resources, each of type Foo.
|
||||
// These lists are replaced in the result by their inlined resources.
|
||||
func (rf *Factory) inlineAnyEmbeddedLists(
|
||||
nodes []*yaml.RNode) (result []*yaml.RNode, err error) {
|
||||
var n0 *yaml.RNode
|
||||
for len(nodes) > 0 {
|
||||
n0 := nodes[0]
|
||||
nodes = nodes[1:]
|
||||
n0, nodes = nodes[0], nodes[1:]
|
||||
kind := n0.GetKind()
|
||||
if !strings.HasSuffix(kind, "List") {
|
||||
result = append(result, n0)
|
||||
@@ -164,7 +192,7 @@ func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
|
||||
var m map[string]interface{}
|
||||
m, err = n0.Map()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("trouble expanding list of %s; %w", kind, err)
|
||||
}
|
||||
items, ok := m["items"]
|
||||
if !ok {
|
||||
@@ -216,38 +244,20 @@ func (rf *Factory) convertObjectSliceToNodeSlice(
|
||||
func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
var result []*yaml.RNode
|
||||
for _, n := range nodes {
|
||||
ignore, err := rf.shouldIgnore(n)
|
||||
if err != nil {
|
||||
if n.IsNilOrEmpty() {
|
||||
continue
|
||||
}
|
||||
if _, err := n.GetValidatedMetadata(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ignore {
|
||||
result = append(result, n)
|
||||
if foundNil, path := n.HasNilEntryInList(); foundNil {
|
||||
return nil, fmt.Errorf("empty item at %v in object %v", path, n)
|
||||
}
|
||||
result = append(result, n)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// shouldIgnore returns true if there's some reason to ignore the node.
|
||||
func (rf *Factory) shouldIgnore(n *yaml.RNode) (bool, error) {
|
||||
if n.IsNilOrEmpty() {
|
||||
return true, nil
|
||||
}
|
||||
if !rf.IncludeLocalConfigs {
|
||||
md, err := n.GetValidatedMetadata()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
_, ignore := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
|
||||
if ignore {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
if foundNil, path := n.HasNilEntryInList(); foundNil {
|
||||
return true, fmt.Errorf("empty item at %v in object %v", path, n)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original
|
||||
// name.
|
||||
func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) {
|
||||
|
||||
@@ -5,7 +5,7 @@ package resource_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -13,9 +13,10 @@ import (
|
||||
. "sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func TestSliceFromBytes(t *testing.T) {
|
||||
func TestRNodesFromBytes(t *testing.T) {
|
||||
type testCase struct {
|
||||
input string
|
||||
expected []string
|
||||
@@ -399,60 +400,11 @@ binaryData:
|
||||
}
|
||||
}
|
||||
|
||||
func TestSliceFromBytesMore(t *testing.T) {
|
||||
testConfigMap :=
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "winnie",
|
||||
},
|
||||
}
|
||||
testDeploymentSpec := map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"hostAliases": []interface{}{
|
||||
map[string]interface{}{
|
||||
"hostnames": []interface{}{
|
||||
"a.example.com",
|
||||
},
|
||||
"ip": "8.8.8.8",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
testDeploymentA := map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deployment-a",
|
||||
},
|
||||
"spec": testDeploymentSpec,
|
||||
}
|
||||
testDeploymentB := map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "deployment-b",
|
||||
},
|
||||
"spec": testDeploymentSpec,
|
||||
}
|
||||
testDeploymentList :=
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "DeploymentList",
|
||||
"items": []interface{}{
|
||||
testDeploymentA,
|
||||
testDeploymentB,
|
||||
},
|
||||
}
|
||||
|
||||
func TestMoreRNodesFromBytes(t *testing.T) {
|
||||
type expected struct {
|
||||
out []map[string]interface{}
|
||||
out []string
|
||||
isErr bool
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
input []byte
|
||||
exp expected
|
||||
@@ -465,16 +417,16 @@ func TestSliceFromBytesMore(t *testing.T) {
|
||||
},
|
||||
"noBytes": {
|
||||
input: []byte{},
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{},
|
||||
},
|
||||
exp: expected{},
|
||||
},
|
||||
"goodJson": {
|
||||
input: []byte(`
|
||||
{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie"}}
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{testConfigMap},
|
||||
out: []string{
|
||||
`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
"goodYaml1": {
|
||||
@@ -485,7 +437,12 @@ metadata:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{testConfigMap},
|
||||
out: []string{`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`},
|
||||
},
|
||||
},
|
||||
"goodYaml2": {
|
||||
@@ -501,26 +458,17 @@ metadata:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{testConfigMap, testConfigMap},
|
||||
},
|
||||
},
|
||||
"localConfigYaml": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie-skip
|
||||
annotations:
|
||||
# this annotation causes the Resource to be ignored by kustomize
|
||||
config.kubernetes.io/local-config: ""
|
||||
---
|
||||
out: []string{`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{testConfigMap},
|
||||
`, `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`},
|
||||
},
|
||||
},
|
||||
"garbageInOneOfTwoObjects": {
|
||||
@@ -545,7 +493,7 @@ WOOOOOOOOOOOOOOOOOOOOOOOOT: woot
|
||||
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{},
|
||||
out: []string{},
|
||||
},
|
||||
},
|
||||
"Missing .metadata.name in object": {
|
||||
@@ -591,9 +539,18 @@ items:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{
|
||||
testConfigMap,
|
||||
testConfigMap},
|
||||
out: []string{`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`, `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
"ConfigMapList": {
|
||||
@@ -611,9 +568,9 @@ items:
|
||||
name: winnie
|
||||
`),
|
||||
exp: expected{
|
||||
out: []map[string]interface{}{
|
||||
testConfigMap,
|
||||
testConfigMap,
|
||||
out: []string{
|
||||
`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`,
|
||||
`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -626,7 +583,7 @@ items:
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-a
|
||||
spec: &hostAliases
|
||||
spec: &foo
|
||||
template:
|
||||
spec:
|
||||
hostAliases:
|
||||
@@ -638,23 +595,39 @@ items:
|
||||
metadata:
|
||||
name: deployment-b
|
||||
spec:
|
||||
<<: *hostAliases
|
||||
*foo
|
||||
`),
|
||||
exp: expected{
|
||||
// TODO(3271): This should work.
|
||||
// https://github.com/kubernetes-sigs/kustomize/issues/3271
|
||||
// json.Marshal(obj) fails on the 2nd list item.
|
||||
// The value of the 1st list item's first spec field is
|
||||
// map[string]interface{}
|
||||
// The value of the 2nd list item's first spec field is
|
||||
// map[interface{}]interface{}
|
||||
// which causes a encoding/json.UnsupportedTypeError.
|
||||
isErr: true,
|
||||
out: []map[string]interface{}{testDeploymentList},
|
||||
out: []string{
|
||||
`{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "deployment-a"}, ` +
|
||||
`"spec": {"template": {"spec": {"hostAliases": [{"hostnames": ["a.example.com"], "ip": "8.8.8.8"}]}}}}`,
|
||||
`{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "deployment-b"}, ` +
|
||||
`"spec": {"template": {"spec": {"hostAliases": [{"hostnames": ["a.example.com"], "ip": "8.8.8.8"}]}}}}`},
|
||||
},
|
||||
},
|
||||
"simpleAnchor": {
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: wildcard
|
||||
data:
|
||||
color: &color-used blue
|
||||
feeling: *color-used
|
||||
`),
|
||||
exp: expected{
|
||||
out: []string{`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: wildcard
|
||||
data:
|
||||
color: blue
|
||||
feeling: blue
|
||||
`},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for n := range testCases {
|
||||
tc := testCases[n]
|
||||
t.Run(n, func(t *testing.T) {
|
||||
@@ -666,15 +639,100 @@ items:
|
||||
assert.False(t, tc.exp.isErr)
|
||||
assert.Equal(t, len(tc.exp.out), len(rs))
|
||||
for i := range rs {
|
||||
rsMap, err := rs[i].Map()
|
||||
actual, err := rs[i].String()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(
|
||||
t, fmt.Sprintf("%v", tc.exp.out[i]), fmt.Sprintf("%v", rsMap))
|
||||
m, _ := rs[i].Map()
|
||||
if !reflect.DeepEqual(tc.exp.out[i], m) {
|
||||
t.Fatalf("%s:\nexpected: %v\n actual: %v",
|
||||
n, tc.exp.out[i], m)
|
||||
}
|
||||
t, strings.TrimSpace(tc.exp.out[i]), strings.TrimSpace(actual))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropLocalNodes(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input []byte
|
||||
expected []byte
|
||||
}{
|
||||
"localConfigUnset": {
|
||||
input: []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
expected: []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
},
|
||||
"localConfigSet": {
|
||||
input: []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie-skip
|
||||
annotations:
|
||||
# this annotation causes the Resource to be ignored by kustomize
|
||||
config.kubernetes.io/local-config: ""
|
||||
`),
|
||||
expected: nil,
|
||||
},
|
||||
"localConfigSetToTrue": {
|
||||
input: []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie-skip
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
`),
|
||||
expected: nil,
|
||||
},
|
||||
"localConfigSetToFalse": {
|
||||
input: []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "false"
|
||||
`),
|
||||
expected: []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "false"
|
||||
name: winnie
|
||||
`),
|
||||
},
|
||||
"localConfigMultiInput": {
|
||||
input: []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie-skip
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
`),
|
||||
expected: []byte(`apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
},
|
||||
}
|
||||
for n := range testCases {
|
||||
tc := testCases[n]
|
||||
t.Run(n, func(t *testing.T) {
|
||||
nin, _ := kio.FromBytes(tc.input)
|
||||
res, err := factory.DropLocalNodes(nin)
|
||||
assert.NoError(t, err)
|
||||
if tc.expected == nil {
|
||||
assert.Equal(t, 0, len(res))
|
||||
} else {
|
||||
actual, _ := res[0].AsYAML()
|
||||
assert.Equal(t, tc.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ var BuildAnnotations = []string{
|
||||
kioutil.PathAnnotation,
|
||||
kioutil.IndexAnnotation,
|
||||
kioutil.SeqIndentAnnotation,
|
||||
|
||||
kioutil.LegacyPathAnnotation,
|
||||
kioutil.LegacyIndexAnnotation,
|
||||
}
|
||||
|
||||
func (r *Resource) ResetRNode(incoming *Resource) {
|
||||
|
||||
6
cmd/config/OWNERS
Normal file
6
cmd/config/OWNERS
Normal file
@@ -0,0 +1,6 @@
|
||||
# See https://github.com/kubernetes/community/blob/master/community-membership.md
|
||||
approvers:
|
||||
- kyaml-approvers
|
||||
|
||||
reviewers:
|
||||
- kyaml-reviewers
|
||||
@@ -16,5 +16,5 @@ require (
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/inf.v0 v0.9.1
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0
|
||||
)
|
||||
|
||||
@@ -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/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
||||
@@ -164,6 +164,8 @@ metadata:
|
||||
a: 'b'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -175,6 +177,8 @@ metadata:
|
||||
a: 'b'
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -191,6 +195,8 @@ metadata:
|
||||
a: 'b'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -206,6 +212,8 @@ metadata:
|
||||
a: 'b'
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: foo
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -222,6 +230,8 @@ metadata:
|
||||
c: 'd'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -234,6 +244,8 @@ metadata:
|
||||
c: 'd'
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -251,6 +263,8 @@ metadata:
|
||||
c: 'd'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -267,6 +281,8 @@ metadata:
|
||||
c: 'd'
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: foo
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -281,6 +297,8 @@ metadata:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -292,6 +310,8 @@ metadata:
|
||||
a: 'b'
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -307,6 +327,8 @@ metadata:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -321,6 +343,8 @@ metadata:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: foo
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -335,6 +359,8 @@ metadata:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -345,6 +371,8 @@ metadata:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -361,6 +389,8 @@ metadata:
|
||||
a: 'b'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -375,6 +405,8 @@ metadata:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: foo
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -389,6 +421,8 @@ metadata:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -399,6 +433,8 @@ metadata:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -414,6 +450,8 @@ metadata:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -429,6 +467,8 @@ metadata:
|
||||
a: 'b'
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: foo
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -443,6 +483,8 @@ metadata:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -453,6 +495,8 @@ metadata:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -469,6 +513,8 @@ metadata:
|
||||
a: 'b'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -483,6 +529,8 @@ metadata:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
namespace: foo
|
||||
spec:
|
||||
replicas: 3
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -182,7 +183,7 @@ func (r *CatRunner) out(w io.Writer) ([]kio.Writer, error) {
|
||||
|
||||
// remove this annotation explicitly, the ByteWriter won't clear it by
|
||||
// default because it doesn't set it
|
||||
clear := []string{"config.kubernetes.io/path"}
|
||||
clear := []string{kioutil.LegacyPathAnnotation, kioutil.PathAnnotation}
|
||||
if r.KeepAnnotations {
|
||||
clear = nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -47,6 +48,7 @@ Environment Variables:
|
||||
|
||||
`,
|
||||
RunE: r.runE,
|
||||
PreRunE: r.preRunE,
|
||||
SilenceUsage: true,
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
@@ -78,6 +80,12 @@ func WrapCommand() *cobra.Command {
|
||||
return GetWrapRunner().Command
|
||||
}
|
||||
|
||||
func (r *WrapRunner) preRunE(_ *cobra.Command, _ []string) error {
|
||||
_, err := fmt.Fprintln(os.Stderr, `Command "wrap" is deprecated, this will no longer be available in kustomize v5.
|
||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *WrapRunner) runE(c *cobra.Command, args []string) error {
|
||||
if r.getEnv == nil {
|
||||
r.getEnv = os.Getenv
|
||||
|
||||
@@ -81,6 +81,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/test_deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
|
||||
spec:
|
||||
replicas: 11
|
||||
selector:
|
||||
@@ -112,6 +114,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/test_service.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/test_service.yaml'
|
||||
spec:
|
||||
selector:
|
||||
name: test
|
||||
@@ -136,6 +140,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/test_deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
|
||||
spec:
|
||||
replicas: 11
|
||||
selector:
|
||||
@@ -164,6 +170,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/test_service.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/test_service.yaml'
|
||||
spec:
|
||||
selector:
|
||||
name: test
|
||||
@@ -186,6 +194,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
@@ -201,6 +211,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml'
|
||||
spec:
|
||||
replicas: 4
|
||||
template:
|
||||
@@ -216,6 +228,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml'
|
||||
spec:
|
||||
replicas: 4
|
||||
template:
|
||||
@@ -233,6 +247,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/test_deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
|
||||
spec:
|
||||
replicas: 11
|
||||
selector:
|
||||
@@ -264,6 +280,8 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'config/test_service.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'config/test_service.yaml'
|
||||
spec:
|
||||
selector:
|
||||
name: test
|
||||
|
||||
@@ -60,6 +60,7 @@ $ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml
|
||||
|
||||
`,
|
||||
RunE: r.runE,
|
||||
PreRunE: r.preRunE,
|
||||
SilenceUsage: true,
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
@@ -84,6 +85,12 @@ func XArgsCommand() *cobra.Command {
|
||||
return GetXArgsRunner().Command
|
||||
}
|
||||
|
||||
func (r *XArgsRunner) preRunE(_ *cobra.Command, _ []string) error {
|
||||
_, err := fmt.Fprintln(os.Stderr, `Command "xargs" is deprecated, this will no longer be available in kustomize v5.
|
||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error {
|
||||
if len(r.Args) == 0 {
|
||||
r.Args = os.Args
|
||||
|
||||
@@ -79,6 +79,8 @@ metadata:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -89,6 +91,8 @@ metadata:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -146,6 +150,7 @@ metadata:
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -155,6 +160,7 @@ metadata:
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -295,6 +301,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'deployment.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
@@ -314,6 +322,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'deployment.yaml'
|
||||
spec:
|
||||
replicas: 4
|
||||
template:
|
||||
@@ -339,6 +349,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'deployment.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
@@ -364,6 +376,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'deployment.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'deployment.yaml'
|
||||
spec:
|
||||
replicas: 4
|
||||
template:
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
||||
@@ -20,6 +23,7 @@ func GetSinkRunner(name string) *SinkRunner {
|
||||
Long: commands.SinkLong,
|
||||
Example: commands.SinkExamples,
|
||||
RunE: r.runE,
|
||||
PreRunE: r.preRunE,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
}
|
||||
runner.FixDocs(name, c)
|
||||
@@ -36,6 +40,12 @@ type SinkRunner struct {
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
func (r *SinkRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
_, err := fmt.Fprintln(os.Stderr, `Command "sink" is deprecated, this will no longer be available in kustomize v5.
|
||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *SinkRunner) runE(c *cobra.Command, args []string) error {
|
||||
var outputs []kio.Writer
|
||||
if len(args) == 1 {
|
||||
@@ -43,7 +53,7 @@ func (r *SinkRunner) runE(c *cobra.Command, args []string) error {
|
||||
} else {
|
||||
outputs = []kio.Writer{&kio.ByteWriter{
|
||||
Writer: c.OutOrStdout(),
|
||||
ClearAnnotations: []string{kioutil.PathAnnotation}},
|
||||
ClearAnnotations: []string{kioutil.PathAnnotation, kioutil.LegacyPathAnnotation}},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
@@ -22,6 +23,7 @@ func GetSourceRunner(name string) *SourceRunner {
|
||||
Long: commands.SourceLong,
|
||||
Example: commands.SourceExamples,
|
||||
RunE: r.runE,
|
||||
PreRunE: r.preRunE,
|
||||
}
|
||||
runner.FixDocs(name, c)
|
||||
c.Flags().StringVar(&r.WrapKind, "wrap-kind", kio.ResourceListKind,
|
||||
@@ -47,6 +49,12 @@ type SourceRunner struct {
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
func (r *SourceRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
_, err := fmt.Fprintln(os.Stderr, `Command "source" is deprecated, this will no longer be available in kustomize v5.
|
||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *SourceRunner) runE(c *cobra.Command, args []string) error {
|
||||
// if there is a function-config specified, emit it
|
||||
var functionConfig *yaml.RNode
|
||||
|
||||
@@ -93,6 +93,8 @@ items:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
@@ -102,6 +104,8 @@ items:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -116,6 +120,8 @@ items:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
- apiVersion: apps/v1
|
||||
@@ -128,6 +134,8 @@ items:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
`, b.String()) {
|
||||
@@ -194,8 +202,8 @@ func TestSourceCommandJSON(t *testing.T) {
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}}
|
||||
- {"apiVersion": "v1", "kind": "Abstraction", "metadata": {"name": "foo", "annotations": {"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n", "config.kubernetes.io/local-config": "true", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f2.json'}}, "spec": {"replicas": 3}}
|
||||
- {"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}}
|
||||
- {"apiVersion": "v1", "kind": "Abstraction", "metadata": {"name": "foo", "annotations": {"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n", "config.kubernetes.io/local-config": "true", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f2.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f2.json'}}, "spec": {"replicas": 3}}
|
||||
`, b.String()) {
|
||||
return
|
||||
}
|
||||
@@ -249,6 +257,7 @@ items:
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
@@ -257,6 +266,7 @@ items:
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -302,7 +312,7 @@ func TestSourceCommandJSON_Stdin(t *testing.T) {
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", 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}}
|
||||
`, out.String()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -278,12 +278,13 @@ func (gr *Runner) CheckoutReleaseBranch(
|
||||
return nil
|
||||
}
|
||||
gr.comment("creating branch")
|
||||
// The branch doesn't exist. Create it.
|
||||
out, err := gr.run(noHarmDone, "checkout", "-b", branch)
|
||||
// The branch doesn't exist remotely. Create or reset it locally.
|
||||
out, err := gr.run(noHarmDone, "checkout", "-B", branch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.Contains(out, "Switched to a new branch ") {
|
||||
// Expected strings: "Switched to a new branch" or "Switched to and reset branch"
|
||||
if !strings.Contains(out, "Switched to") {
|
||||
return fmt.Errorf("unexpected branch creation output: %q", out)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -6,6 +6,6 @@ require (
|
||||
github.com/rakyll/statik v0.1.7
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
sigs.k8s.io/kustomize/api v0.9.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||
sigs.k8s.io/kustomize/api v0.10.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0
|
||||
)
|
||||
|
||||
@@ -228,10 +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/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
sigs.k8s.io/kustomize/api v0.9.0 h1:yGQnm/2GBbHBLSVbM0CsqgPmqK/NSDbhSGbXuhuVN7s=
|
||||
sigs.k8s.io/kustomize/api v0.9.0/go.mod h1:bOF7z4DcRIXcOCeSbVq5o9JhMRnNzWqrRSSBFtz05A4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
sigs.k8s.io/kustomize/api v0.10.0 h1:HK5gVSlVV24AmZ2fTHUIchZ6osaYNegK1jAdx7lJ/mU=
|
||||
sigs.k8s.io/kustomize/api v0.10.0/go.mod h1:syysqD8Oews9oghLfCitMCuCPxxu4MErSJ6uw8ge9jk=
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
||||
@@ -212,7 +212,7 @@ func (mf *kustomizationFile) parseCommentedFields(content []byte) error {
|
||||
if matched {
|
||||
mf.originalFields = append(mf.originalFields, &commentedField{field: field, comment: squash(comments)})
|
||||
comments = [][]byte{}
|
||||
} else if len(comments) > 0 {
|
||||
} else if len(comments) > 0 && len(mf.originalFields) > 0 {
|
||||
mf.originalFields[len(mf.originalFields)-1].appendComment(squash(comments))
|
||||
comments = [][]byte{}
|
||||
}
|
||||
|
||||
@@ -356,6 +356,53 @@ kind: Kustomization
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommentsWithDocumentSeperatorAtBeginning(t *testing.T) {
|
||||
kustomizationContentWithComments := []byte(`
|
||||
|
||||
|
||||
# Some comments
|
||||
# This is some comment we should preserve
|
||||
# don't delete it
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: mynamespace
|
||||
`)
|
||||
|
||||
expected := []byte(`
|
||||
|
||||
|
||||
# Some comments
|
||||
# This is some comment we should preserve
|
||||
# don't delete it
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: mynamespace
|
||||
`)
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
testutils_test.WriteTestKustomizationWith(
|
||||
fSys, kustomizationContentWithComments)
|
||||
mf, err := NewKustomizationFile(fSys)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
|
||||
kustomization, err := mf.Read()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
if err = mf.Write(kustomization); err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
bytes, _ := fSys.ReadFile(mf.path)
|
||||
|
||||
if diff := cmp.Diff(expected, bytes); diff != "" {
|
||||
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnknownFieldInKustomization(t *testing.T) {
|
||||
kContent := []byte(`
|
||||
foo:
|
||||
|
||||
@@ -8,9 +8,9 @@ require (
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
sigs.k8s.io/kustomize/api v0.9.0
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||
sigs.k8s.io/kustomize/api v0.10.0
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
|
||||
@@ -253,12 +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/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
sigs.k8s.io/kustomize/api v0.9.0 h1:yGQnm/2GBbHBLSVbM0CsqgPmqK/NSDbhSGbXuhuVN7s=
|
||||
sigs.k8s.io/kustomize/api v0.9.0/go.mod h1:bOF7z4DcRIXcOCeSbVq5o9JhMRnNzWqrRSSBFtz05A4=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.0 h1:6XPkaOu9eFdvrcqHpfmYAKoBvE01JdvsTVvtDuAFM+8=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.0/go.mod h1:XS4NFKucjk0/+nPKJwSMoZYCjey2PB0ipNoZabXUFPU=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
sigs.k8s.io/kustomize/api v0.10.0 h1:HK5gVSlVV24AmZ2fTHUIchZ6osaYNegK1jAdx7lJ/mU=
|
||||
sigs.k8s.io/kustomize/api v0.10.0/go.mod h1:syysqD8Oews9oghLfCitMCuCPxxu4MErSJ6uw8ge9jk=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.1 h1:eqpN9eUSn3XIfvPabit8lpIqUbWKS7f4lOB4D2cr5CQ=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.10.1/go.mod h1:9W5pDv3cgDfMjOXEga4yC9lUpkgAaecW+lZmHOMeX2I=
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
||||
6
kyaml/OWNERS
Normal file
6
kyaml/OWNERS
Normal file
@@ -0,0 +1,6 @@
|
||||
# See https://github.com/kubernetes/community/blob/master/community-membership.md
|
||||
approvers:
|
||||
- kyaml-approvers
|
||||
|
||||
reviewers:
|
||||
- kyaml-reviewers
|
||||
@@ -11,10 +11,12 @@ import (
|
||||
|
||||
// CopyComments recursively copies the comments on fields in from to fields in to
|
||||
func CopyComments(from, to *yaml.RNode) error {
|
||||
copy(from, to)
|
||||
// from node should not be modified, it should be just used as a reference
|
||||
fromCopy := from.Copy()
|
||||
copyFieldComments(fromCopy, to)
|
||||
// walk the fields copying comments
|
||||
_, err := walk.Walker{
|
||||
Sources: []*yaml.RNode{from, to},
|
||||
Sources: []*yaml.RNode{fromCopy, to},
|
||||
Visitor: &copier{},
|
||||
VisitKeysAsScalars: true}.Walk()
|
||||
return err
|
||||
@@ -25,7 +27,7 @@ func CopyComments(from, to *yaml.RNode) error {
|
||||
type copier struct{}
|
||||
|
||||
func (c *copier) VisitMap(s walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
copy(s.Dest(), s.Origin())
|
||||
copyFieldComments(s.Dest(), s.Origin())
|
||||
return s.Dest(), nil
|
||||
}
|
||||
|
||||
@@ -39,13 +41,13 @@ func (c *copier) VisitScalar(s walk.Sources, _ *openapi.ResourceSchema) (*yaml.R
|
||||
to.Document().Style = yaml.DoubleQuotedStyle
|
||||
}
|
||||
|
||||
copy(s.Dest(), to)
|
||||
copyFieldComments(s.Dest(), to)
|
||||
return s.Dest(), nil
|
||||
}
|
||||
|
||||
func (c *copier) VisitList(s walk.Sources, _ *openapi.ResourceSchema, _ walk.ListKind) (
|
||||
*yaml.RNode, error) {
|
||||
copy(s.Dest(), s.Origin())
|
||||
copyFieldComments(s.Dest(), s.Origin())
|
||||
destItems := s.Dest().Content()
|
||||
originItems := s.Origin().Content()
|
||||
|
||||
@@ -64,8 +66,8 @@ func (c *copier) VisitList(s walk.Sources, _ *openapi.ResourceSchema, _ walk.Lis
|
||||
return s.Dest(), nil
|
||||
}
|
||||
|
||||
// copy copies the comment from one field to another
|
||||
func copy(from, to *yaml.RNode) {
|
||||
// copyFieldComments copies the comment from one field to another
|
||||
func copyFieldComments(from, to *yaml.RNode) {
|
||||
if from == nil || to == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -351,6 +351,30 @@ apiVersion: v1
|
||||
kind: ConfigMap
|
||||
data:
|
||||
somekey: "012345678901234567890123456789012345678901234567890123456789012345678901234" # x
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "sort fields with null value",
|
||||
from: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: workspaces.app.terraform.io
|
||||
creationTimestamp: null # this field is null
|
||||
namespace: staging
|
||||
`,
|
||||
to: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: workspaces.app.terraform.io
|
||||
creationTimestamp: null
|
||||
namespace: staging
|
||||
`,
|
||||
expected: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: workspaces.app.terraform.io
|
||||
creationTimestamp: null # this field is null
|
||||
namespace: staging
|
||||
`,
|
||||
},
|
||||
}
|
||||
@@ -378,9 +402,18 @@ data:
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualFrom, err := from.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(actual)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, strings.TrimSpace(tc.from), strings.TrimSpace(actualFrom)) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ metadata:
|
||||
annotations:
|
||||
key: foo-a
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
labels:
|
||||
key: foo-l
|
||||
`
|
||||
@@ -141,6 +142,7 @@ metadata:
|
||||
annotations:
|
||||
key: bar-a
|
||||
config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
labels:
|
||||
key: bar-l
|
||||
`
|
||||
|
||||
@@ -194,6 +194,8 @@ metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'statefulset_deployment-foo.yaml'
|
||||
config.kubernetes.io/path: 'statefulset_deployment-foo.yaml'
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -202,6 +204,8 @@ metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`, b.String()) {
|
||||
t.FailNow()
|
||||
|
||||
@@ -43,6 +43,7 @@ kind: StatefulSet
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'statefulset_deployment-foo.yaml'
|
||||
config.kubernetes.io/path: 'statefulset_deployment-foo.yaml'
|
||||
`,
|
||||
`apiVersion: v1
|
||||
@@ -50,6 +51,7 @@ kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`,
|
||||
},
|
||||
|
||||
@@ -66,9 +66,14 @@ func (c *FunctionFilter) getFunctionScope() (string, error) {
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err)
|
||||
}
|
||||
p, found := m.Annotations[kioutil.PathAnnotation]
|
||||
var p string
|
||||
var found bool
|
||||
p, found = m.Annotations[kioutil.PathAnnotation]
|
||||
if !found {
|
||||
return "", nil
|
||||
p, found = m.Annotations[kioutil.LegacyPathAnnotation]
|
||||
if !found {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
functionDir := path.Clean(path.Dir(p))
|
||||
@@ -101,12 +106,17 @@ func (c *FunctionFilter) scope(dir string, nodes []*yaml.RNode) ([]*yaml.RNode,
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
p, found := m.Annotations[kioutil.PathAnnotation]
|
||||
var p string
|
||||
var found bool
|
||||
p, found = m.Annotations[kioutil.PathAnnotation]
|
||||
if !found {
|
||||
// this Resource isn't scoped under the function -- don't know where it came from
|
||||
// consider it out of scope
|
||||
saved = append(saved, nodes[i])
|
||||
continue
|
||||
p, found = m.Annotations[kioutil.LegacyPathAnnotation]
|
||||
if !found {
|
||||
// this Resource isn't scoped under the function -- don't know where it came from
|
||||
// consider it out of scope
|
||||
saved = append(saved, nodes[i])
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
resourceDir := path.Clean(path.Dir(p))
|
||||
@@ -193,8 +203,6 @@ func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return append(output, saved...), nil
|
||||
}
|
||||
|
||||
const idAnnotation = "config.k8s.io/id"
|
||||
|
||||
func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
|
||||
// set the id on each node to map inputs to outputs
|
||||
var id int
|
||||
@@ -202,7 +210,11 @@ func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
|
||||
for i := range nodes {
|
||||
id++
|
||||
idStr := fmt.Sprintf("%v", id)
|
||||
err := nodes[i].PipeE(yaml.SetAnnotation(idAnnotation, idStr))
|
||||
err := nodes[i].PipeE(yaml.SetAnnotation(kioutil.IdAnnotation, idStr))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
err = nodes[i].PipeE(yaml.SetAnnotation(kioutil.LegacyIdAnnotation, idStr))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
@@ -214,12 +226,18 @@ func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
|
||||
func (c *FunctionFilter) copyCommentsAndSyncOrder(nodes []*yaml.RNode) error {
|
||||
for i := range nodes {
|
||||
node := nodes[i]
|
||||
anID, err := node.Pipe(yaml.GetAnnotation(idAnnotation))
|
||||
anID, err := node.Pipe(yaml.GetAnnotation(kioutil.IdAnnotation))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if anID == nil {
|
||||
continue
|
||||
anID, err = node.Pipe(yaml.GetAnnotation(kioutil.LegacyIdAnnotation))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if anID == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
var in *yaml.RNode
|
||||
@@ -233,7 +251,10 @@ func (c *FunctionFilter) copyCommentsAndSyncOrder(nodes []*yaml.RNode) error {
|
||||
if err := order.SyncOrder(in, node); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if err := node.PipeE(yaml.ClearAnnotation(idAnnotation)); err != nil {
|
||||
if err := node.PipeE(yaml.ClearAnnotation(kioutil.IdAnnotation)); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if err := node.PipeE(yaml.ClearAnnotation(kioutil.LegacyIdAnnotation)); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
`,
|
||||
`
|
||||
@@ -90,6 +91,7 @@ kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`,
|
||||
},
|
||||
@@ -123,6 +125,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
`,
|
||||
`
|
||||
@@ -132,6 +135,7 @@ metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo.yaml'
|
||||
internal.config.kubernetes.io/path: 'foo.yaml'
|
||||
`,
|
||||
},
|
||||
},
|
||||
@@ -180,6 +184,7 @@ metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo.yaml'
|
||||
internal.config.kubernetes.io/path: 'foo.yaml'
|
||||
`,
|
||||
`
|
||||
apiVersion: v1
|
||||
@@ -188,6 +193,7 @@ metadata:
|
||||
name: configmap-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo.yaml'
|
||||
internal.config.kubernetes.io/path: 'foo.yaml'
|
||||
`,
|
||||
},
|
||||
},
|
||||
@@ -235,6 +241,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
`, `
|
||||
apiVersion: v1
|
||||
@@ -242,6 +249,7 @@ kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`,
|
||||
},
|
||||
@@ -393,6 +401,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
`, `
|
||||
apiVersion: v1
|
||||
@@ -400,6 +409,7 @@ kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`,
|
||||
},
|
||||
@@ -486,6 +496,7 @@ items:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
config.k8s.io/id: '1'
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
@@ -505,7 +516,7 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
new: annotation
|
||||
config.k8s.io/id: '1'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
kind: Example
|
||||
@@ -551,6 +562,7 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
new: annotation
|
||||
internal.config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
`, `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -575,6 +587,7 @@ items:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
config.k8s.io/id: '1'
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
@@ -594,7 +607,7 @@ items:
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
new: annotation
|
||||
config.k8s.io/id: '1'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
kind: Example
|
||||
@@ -638,6 +651,7 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
new: annotation
|
||||
internal.config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
`, `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -661,6 +675,7 @@ items:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
config.k8s.io/id: '1'
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
@@ -668,6 +683,7 @@ items:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
internal.config.kubernetes.io/id: '2'
|
||||
config.k8s.io/id: '2'
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
@@ -686,7 +702,7 @@ items:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||
config.k8s.io/id: '1'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
@@ -737,6 +753,7 @@ metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||
internal.config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||
`, `
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -745,6 +762,7 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
new: annotation
|
||||
internal.config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
`,
|
||||
},
|
||||
},
|
||||
@@ -831,6 +849,7 @@ items:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
config.k8s.io/id: '1'
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
@@ -849,8 +868,9 @@ items:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
config.k8s.io/id: '1'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
new: annotation
|
||||
internal.config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
kind: Example
|
||||
@@ -896,6 +916,7 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
new: annotation
|
||||
internal.config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||
`, `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -919,6 +940,7 @@ items:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/b.yaml'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
config.k8s.io/id: '1'
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
@@ -926,6 +948,7 @@ items:
|
||||
name: service-foo # name comment
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/a.yaml'
|
||||
internal.config.kubernetes.io/id: '2'
|
||||
config.k8s.io/id: '2'
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
@@ -945,14 +968,14 @@ items:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/b.yaml'
|
||||
config.k8s.io/id: '1'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/a.yaml'
|
||||
config.k8s.io/id: '2'
|
||||
internal.config.kubernetes.io/id: '2'
|
||||
new: annotation
|
||||
functionConfig:
|
||||
apiVersion: example.com/v1
|
||||
@@ -996,6 +1019,7 @@ metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/b.yaml'
|
||||
internal.config.kubernetes.io/path: 'foo/b.yaml'
|
||||
`, `
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -1004,6 +1028,7 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/a.yaml'
|
||||
new: annotation
|
||||
internal.config.kubernetes.io/path: 'foo/a.yaml'
|
||||
`,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -76,6 +77,7 @@ run(ctx.resource_list["items"])
|
||||
// name: deployment-1
|
||||
// annotations:
|
||||
// foo: bar
|
||||
// internal.config.kubernetes.io/path: 'deployment_deployment-1.yaml'
|
||||
// config.kubernetes.io/path: 'deployment_deployment-1.yaml'
|
||||
// spec:
|
||||
// template:
|
||||
@@ -90,6 +92,7 @@ run(ctx.resource_list["items"])
|
||||
// name: deployment-2
|
||||
// annotations:
|
||||
// foo: bar
|
||||
// internal.config.kubernetes.io/path: 'deployment_deployment-2.yaml'
|
||||
// config.kubernetes.io/path: 'deployment_deployment-2.yaml'
|
||||
// spec:
|
||||
// template:
|
||||
@@ -168,6 +171,7 @@ run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]["spec"]["val
|
||||
// name: deployment-1
|
||||
// annotations:
|
||||
// foo: hello world
|
||||
// internal.config.kubernetes.io/path: 'deployment_deployment-1.yaml'
|
||||
// config.kubernetes.io/path: 'deployment_deployment-1.yaml'
|
||||
// spec:
|
||||
// template:
|
||||
@@ -182,6 +186,7 @@ run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]["spec"]["val
|
||||
// name: deployment-2
|
||||
// annotations:
|
||||
// foo: hello world
|
||||
// internal.config.kubernetes.io/path: 'deployment_deployment-2.yaml'
|
||||
// config.kubernetes.io/path: 'deployment_deployment-2.yaml'
|
||||
// spec:
|
||||
// template:
|
||||
@@ -257,8 +262,11 @@ run(ctx.resource_list["items"])
|
||||
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: d}},
|
||||
Filters: []kio.Filter{fltr},
|
||||
Outputs: []kio.Writer{&kio.ByteWriter{
|
||||
Writer: output,
|
||||
ClearAnnotations: []string{"config.kubernetes.io/path"},
|
||||
Writer: output,
|
||||
ClearAnnotations: []string{
|
||||
kioutil.PathAnnotation,
|
||||
kioutil.LegacyPathAnnotation,
|
||||
},
|
||||
}}}.Execute()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
@@ -56,6 +56,7 @@ metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: bar
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -96,6 +97,7 @@ metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: annotation-value
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -135,6 +137,7 @@ metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: Deployment enables declarative updates for Pods and ReplicaSets.
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -177,6 +180,7 @@ metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: bar
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -218,6 +222,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -272,6 +277,7 @@ metadata:
|
||||
name: nginx-deployment-1
|
||||
annotations:
|
||||
foo: bar
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -287,6 +293,7 @@ metadata:
|
||||
name: nginx-deployment-2
|
||||
annotations:
|
||||
foo: bar
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment-2.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment-2.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -329,6 +336,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -343,6 +351,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment-2.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment-2.yaml'
|
||||
`,
|
||||
},
|
||||
@@ -370,6 +379,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||
`,
|
||||
},
|
||||
@@ -409,6 +419,7 @@ metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: hello world
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
spec:
|
||||
template:
|
||||
@@ -462,6 +473,7 @@ metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: hello world
|
||||
internal.config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||
spec:
|
||||
template:
|
||||
|
||||
@@ -51,7 +51,15 @@ func WrapErrorWithFile(err error, meta yaml.ResourceMeta) error {
|
||||
if err == nil {
|
||||
return err
|
||||
}
|
||||
path := meta.Annotations[kioutil.PathAnnotation]
|
||||
index := meta.Annotations[kioutil.IndexAnnotation]
|
||||
if path == "" {
|
||||
path = meta.Annotations[kioutil.LegacyPathAnnotation]
|
||||
}
|
||||
if index == "" {
|
||||
index = meta.Annotations[kioutil.LegacyPathAnnotation]
|
||||
}
|
||||
return errors.WrapPrefixf(err, "%s [%s]",
|
||||
meta.Annotations[kioutil.PathAnnotation],
|
||||
meta.Annotations[kioutil.IndexAnnotation])
|
||||
meta.Annotations[path],
|
||||
meta.Annotations[index])
|
||||
}
|
||||
|
||||
@@ -43,6 +43,13 @@ type ByteReadWriter struct {
|
||||
// Style is a style that is set on the Resource Node Document.
|
||||
Style yaml.Style
|
||||
|
||||
// WrapBareSeqNode wraps the bare sequence node document with map node,
|
||||
// kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this option enables wrapping such bare
|
||||
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
|
||||
// note that this wrapping is different and not related to ResourceList wrapping
|
||||
WrapBareSeqNode bool
|
||||
|
||||
FunctionConfig *yaml.RNode
|
||||
|
||||
Results *yaml.RNode
|
||||
@@ -57,6 +64,7 @@ func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
Reader: rw.Reader,
|
||||
OmitReaderAnnotations: rw.OmitReaderAnnotations,
|
||||
PreserveSeqIndent: rw.PreserveSeqIndent,
|
||||
WrapBareSeqNode: rw.WrapBareSeqNode,
|
||||
}
|
||||
val, err := b.Read()
|
||||
if rw.FunctionConfig == nil {
|
||||
@@ -94,6 +102,7 @@ func ParseAll(inputs ...string) ([]*yaml.RNode, error) {
|
||||
func FromBytes(bs []byte) ([]*yaml.RNode, error) {
|
||||
return (&ByteReader{
|
||||
OmitReaderAnnotations: true,
|
||||
AnchorsAweigh: true,
|
||||
Reader: bytes.NewBuffer(bs),
|
||||
}).Read()
|
||||
}
|
||||
@@ -137,6 +146,17 @@ type ByteReader struct {
|
||||
// WrappingKind is set by Read(), and is the kind of the object that
|
||||
// the read objects were originally wrapped in.
|
||||
WrappingKind string
|
||||
|
||||
// WrapBareSeqNode wraps the bare sequence node document with map node,
|
||||
// kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this option enables wrapping such bare
|
||||
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
|
||||
// note that this wrapping is different and not related to ResourceList wrapping
|
||||
WrapBareSeqNode bool
|
||||
|
||||
// AnchorsAweigh set to true attempts to replace all YAML anchor aliases
|
||||
// with their definitions (anchor values) immediately after the read.
|
||||
AnchorsAweigh bool
|
||||
}
|
||||
|
||||
var _ Reader = &ByteReader{}
|
||||
@@ -254,6 +274,13 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
|
||||
// increment the index annotation value
|
||||
index++
|
||||
}
|
||||
if r.AnchorsAweigh {
|
||||
for _, n := range output {
|
||||
if err = n.DeAnchor(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
@@ -275,11 +302,22 @@ func (r *ByteReader) decode(originalYAML string, index int, decoder *yaml.Decode
|
||||
// sort the annotations by key so the output Resources is consistent (otherwise the
|
||||
// annotations will be in a random order)
|
||||
n := yaml.NewRNode(node)
|
||||
// check if it is a bare sequence node and wrap it with a yaml.BareSeqNodeWrappingKey
|
||||
if r.WrapBareSeqNode && node.Kind == yaml.DocumentNode && len(node.Content) > 0 &&
|
||||
node.Content[0] != nil && node.Content[0].Kind == yaml.SequenceNode {
|
||||
wrappedNode := yaml.NewRNode(&yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
})
|
||||
wrappedNode.PipeE(yaml.SetField(yaml.BareSeqNodeWrappingKey, n))
|
||||
n = wrappedNode
|
||||
}
|
||||
|
||||
if r.SetAnnotations == nil {
|
||||
r.SetAnnotations = map[string]string{}
|
||||
}
|
||||
if !r.OmitReaderAnnotations {
|
||||
r.SetAnnotations[kioutil.IndexAnnotation] = fmt.Sprintf("%d", index)
|
||||
r.SetAnnotations[kioutil.LegacyIndexAnnotation] = fmt.Sprintf("%d", index)
|
||||
|
||||
if r.PreserveSeqIndent {
|
||||
// derive and add the seqindent annotation
|
||||
@@ -300,5 +338,5 @@ func (r *ByteReader) decode(originalYAML string, index int, decoder *yaml.Decode
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return yaml.NewRNode(node), nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ c: d
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
`,
|
||||
`# second resource
|
||||
e: f
|
||||
@@ -190,11 +191,13 @@ g:
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
`,
|
||||
`i: j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '2'
|
||||
internal.config.kubernetes.io/index: '2'
|
||||
`,
|
||||
},
|
||||
},
|
||||
@@ -260,6 +263,7 @@ c: d
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
`,
|
||||
`
|
||||
# second resource
|
||||
@@ -269,12 +273,14 @@ g:
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
`,
|
||||
`
|
||||
i: j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '2'
|
||||
internal.config.kubernetes.io/index: '2'
|
||||
`,
|
||||
},
|
||||
instance: ByteReader{},
|
||||
@@ -369,7 +375,7 @@ metadata:
|
||||
`,
|
||||
expectedItems: []string{
|
||||
`
|
||||
{"a": "b", "c": [1, 2], metadata: {annotations: {config.kubernetes.io/index: '0'}}}
|
||||
{"a": "b", "c": [1, 2], metadata: {annotations: {config.kubernetes.io/index: '0', internal.config.kubernetes.io/index: '0'}}}
|
||||
`,
|
||||
},
|
||||
instance: ByteReader{},
|
||||
@@ -758,7 +764,7 @@ items:
|
||||
metadata:
|
||||
name: deployment-b
|
||||
spec:
|
||||
<<: *hostAliases
|
||||
*hostAliases
|
||||
`),
|
||||
exp: expected{
|
||||
sOut: []string{`
|
||||
@@ -769,7 +775,7 @@ items:
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-a
|
||||
spec: &hostAliases
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
hostAliases:
|
||||
@@ -781,7 +787,12 @@ items:
|
||||
metadata:
|
||||
name: deployment-b
|
||||
spec:
|
||||
!!merge <<: *hostAliases
|
||||
template:
|
||||
spec:
|
||||
hostAliases:
|
||||
- hostnames:
|
||||
- a.example.com
|
||||
ip: 8.8.8.8
|
||||
`},
|
||||
},
|
||||
},
|
||||
@@ -808,27 +819,21 @@ items:
|
||||
}
|
||||
}
|
||||
|
||||
// This test shows the lower level (go-yaml) representation of a small doc
|
||||
// with an anchor. The anchor structure is there, in the sense that an
|
||||
// alias pointer is readily available when a node's kind is an AliasNode.
|
||||
// I.e. the anchor mapping name -> object was noted during unmarshalling.
|
||||
// However, at the time of writing github.com/go-yaml/yaml/encoder.go
|
||||
// doesn't appear to have an option to perform anchor replacements when
|
||||
// encoding. It emits anchor definitions and references (aliases) intact.
|
||||
func TestByteReader_AnchorBehavior(t *testing.T) {
|
||||
// Show the low level (go-yaml) representation of a small doc with a
|
||||
// YAML anchor and alias after reading it with anchor expansion on or off.
|
||||
func TestByteReader_AnchorsAweigh(t *testing.T) {
|
||||
const input = `
|
||||
data:
|
||||
color: &color-used blue
|
||||
feeling: *color-used
|
||||
`
|
||||
expected := strings.TrimSpace(`
|
||||
data:
|
||||
color: &color-used blue
|
||||
feeling: *color-used
|
||||
`)
|
||||
var rNode *yaml.RNode
|
||||
{
|
||||
rNodes, err := FromBytes([]byte(input))
|
||||
rNodes, err := (&ByteReader{
|
||||
OmitReaderAnnotations: true,
|
||||
AnchorsAweigh: false,
|
||||
Reader: bytes.NewBuffer([]byte(input)),
|
||||
}).Read()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(rNodes))
|
||||
rNode = rNodes[0]
|
||||
@@ -857,7 +862,7 @@ data:
|
||||
assert.Equal(t, yaml.ScalarNode, yNodes[0].Kind)
|
||||
assert.Equal(t, yaml.NodeTagString, yNodes[0].Tag)
|
||||
assert.Equal(t, "color", yNodes[0].Value)
|
||||
assert.Equal(t, "", yNodes[0].Anchor)
|
||||
assert.Empty(t, yNodes[0].Anchor)
|
||||
assert.Nil(t, yNodes[0].Alias)
|
||||
|
||||
assert.Equal(t, yaml.ScalarNode, yNodes[1].Kind)
|
||||
@@ -869,19 +874,82 @@ data:
|
||||
assert.Equal(t, yaml.ScalarNode, yNodes[2].Kind)
|
||||
assert.Equal(t, yaml.NodeTagString, yNodes[2].Tag)
|
||||
assert.Equal(t, "feeling", yNodes[2].Value)
|
||||
assert.Equal(t, "", yNodes[2].Anchor)
|
||||
assert.Empty(t, yNodes[2].Anchor)
|
||||
assert.Nil(t, yNodes[2].Alias)
|
||||
|
||||
assert.Equal(t, yaml.AliasNode, yNodes[3].Kind)
|
||||
assert.Equal(t, "", yNodes[3].Tag)
|
||||
assert.Empty(t, yNodes[3].Tag)
|
||||
assert.Equal(t, "color-used", yNodes[3].Value)
|
||||
assert.Equal(t, "", yNodes[3].Anchor)
|
||||
assert.Empty(t, yNodes[3].Anchor)
|
||||
assert.NotNil(t, yNodes[3].Alias)
|
||||
}
|
||||
|
||||
yaml, err := rNode.String()
|
||||
str, err := rNode.String()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, strings.TrimSpace(yaml))
|
||||
// The string version matches the input (it still has anchors and aliases).
|
||||
assert.Equal(t, strings.TrimSpace(input), strings.TrimSpace(str))
|
||||
|
||||
// Now do same thing again, but this time set AnchorsAweigh = true.
|
||||
{
|
||||
rNodes, err := (&ByteReader{
|
||||
OmitReaderAnnotations: true,
|
||||
AnchorsAweigh: true,
|
||||
Reader: bytes.NewBuffer([]byte(input)),
|
||||
}).Read()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(rNodes))
|
||||
rNode = rNodes[0]
|
||||
}
|
||||
// Again make assertions on the internals.
|
||||
{
|
||||
yNode := rNode.YNode()
|
||||
|
||||
assert.Equal(t, yaml.NodeTagMap, yNode.Tag)
|
||||
|
||||
yNodes := yNode.Content
|
||||
assert.Equal(t, 2, len(yNodes))
|
||||
|
||||
assert.Equal(t, yaml.NodeTagString, yNodes[0].Tag)
|
||||
assert.Equal(t, "data", yNodes[0].Value)
|
||||
|
||||
assert.Equal(t, yaml.NodeTagMap, yNodes[1].Tag)
|
||||
|
||||
yNodes = yNodes[1].Content
|
||||
assert.Equal(t, 4, len(yNodes))
|
||||
|
||||
assert.Equal(t, yaml.ScalarNode, yNodes[0].Kind)
|
||||
assert.Equal(t, yaml.NodeTagString, yNodes[0].Tag)
|
||||
assert.Equal(t, "color", yNodes[0].Value)
|
||||
assert.Empty(t, yNodes[0].Anchor)
|
||||
assert.Nil(t, yNodes[0].Alias)
|
||||
|
||||
assert.Equal(t, yaml.ScalarNode, yNodes[1].Kind)
|
||||
assert.Equal(t, yaml.NodeTagString, yNodes[1].Tag)
|
||||
assert.Equal(t, "blue", yNodes[1].Value)
|
||||
assert.Empty(t, yNodes[1].Anchor)
|
||||
assert.Nil(t, yNodes[1].Alias)
|
||||
|
||||
assert.Equal(t, yaml.ScalarNode, yNodes[2].Kind)
|
||||
assert.Equal(t, yaml.NodeTagString, yNodes[2].Tag)
|
||||
assert.Equal(t, "feeling", yNodes[2].Value)
|
||||
assert.Empty(t, yNodes[2].Anchor)
|
||||
assert.Nil(t, yNodes[2].Alias)
|
||||
|
||||
assert.Equal(t, yaml.ScalarNode, yNodes[3].Kind)
|
||||
assert.Equal(t, yaml.NodeTagString, yNodes[3].Tag)
|
||||
assert.Equal(t, "blue", yNodes[3].Value)
|
||||
assert.Empty(t, yNodes[3].Anchor)
|
||||
assert.Nil(t, yNodes[3].Alias)
|
||||
}
|
||||
|
||||
str, err = rNode.String()
|
||||
assert.NoError(t, err)
|
||||
// This time, the alias has been replaced with the anchor definition.
|
||||
assert.Equal(t, strings.TrimSpace(`
|
||||
data:
|
||||
color: blue
|
||||
feeling: blue
|
||||
`), strings.TrimSpace(str))
|
||||
}
|
||||
|
||||
// TestByteReader_AddSeqIndentAnnotation tests if the internal.config.kubernetes.io/seqindent
|
||||
|
||||
@@ -216,6 +216,7 @@ spec:
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
---
|
||||
kind: Service
|
||||
spec:
|
||||
@@ -224,6 +225,7 @@ spec:
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
`,
|
||||
instance: kio.ByteReadWriter{KeepReaderAnnotations: true},
|
||||
},
|
||||
@@ -589,3 +591,159 @@ env:
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteReadWriter_WrapBareSeqNode(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
readerErr string
|
||||
writerErr string
|
||||
input string
|
||||
wrapBareSeqNode bool
|
||||
expectedOutput string
|
||||
instance kio.ByteReadWriter
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "round_trip bare seq node simple",
|
||||
wrapBareSeqNode: true,
|
||||
input: `
|
||||
- foo
|
||||
- bar
|
||||
`,
|
||||
expectedOutput: `
|
||||
- foo
|
||||
- bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "round_trip bare seq node",
|
||||
wrapBareSeqNode: true,
|
||||
input: `# Use the old CRD because of the quantity validation issue:
|
||||
# https://github.com/kubeflow/kubeflow/issues/5722
|
||||
- op: replace
|
||||
path: /spec
|
||||
value:
|
||||
group: kubeflow.org
|
||||
names:
|
||||
kind: Notebook
|
||||
plural: notebooks
|
||||
singular: notebook
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: false
|
||||
`,
|
||||
expectedOutput: `# Use the old CRD because of the quantity validation issue:
|
||||
# https://github.com/kubeflow/kubeflow/issues/5722
|
||||
- op: replace
|
||||
path: /spec
|
||||
value:
|
||||
group: kubeflow.org
|
||||
names:
|
||||
kind: Notebook
|
||||
plural: notebooks
|
||||
singular: notebook
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: false
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "error round_trip bare seq node simple",
|
||||
wrapBareSeqNode: false,
|
||||
input: `
|
||||
- foo
|
||||
- bar
|
||||
`,
|
||||
readerErr: "wrong Node Kind for expected: MappingNode was SequenceNode",
|
||||
},
|
||||
{
|
||||
name: "error round_trip bare seq node",
|
||||
wrapBareSeqNode: false,
|
||||
input: `# Use the old CRD because of the quantity validation issue:
|
||||
# https://github.com/kubeflow/kubeflow/issues/5722
|
||||
- op: replace
|
||||
path: /spec
|
||||
value:
|
||||
group: kubeflow.org
|
||||
names:
|
||||
kind: Notebook
|
||||
plural: notebooks
|
||||
singular: notebook
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: false
|
||||
`,
|
||||
readerErr: "wrong Node Kind for expected: MappingNode was SequenceNode",
|
||||
},
|
||||
{
|
||||
name: "round_trip bare seq node json",
|
||||
wrapBareSeqNode: true,
|
||||
input: `[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--namespaced"}]`,
|
||||
expectedOutput: `[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--namespaced"}]`,
|
||||
},
|
||||
{
|
||||
name: "error round_trip invalid yaml node",
|
||||
wrapBareSeqNode: false,
|
||||
input: "I am not valid",
|
||||
readerErr: "wrong Node Kind for expected: MappingNode was ScalarNode",
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var in, out bytes.Buffer
|
||||
in.WriteString(tc.input)
|
||||
w := tc.instance
|
||||
w.Writer = &out
|
||||
w.Reader = &in
|
||||
w.PreserveSeqIndent = true
|
||||
w.WrapBareSeqNode = tc.wrapBareSeqNode
|
||||
|
||||
nodes, err := w.Read()
|
||||
if tc.readerErr != "" {
|
||||
if !assert.Error(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Contains(t, err.Error(), tc.readerErr) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.WrappingKind = ""
|
||||
err = w.Write(nodes)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if tc.writerErr != "" {
|
||||
if !assert.Error(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Contains(t, err.Error(), tc.writerErr) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
@@ -75,6 +76,10 @@ func (w ByteWriter) Write(inputNodes []*yaml.RNode) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
_, err = nodes[i].Pipe(yaml.ClearAnnotation(kioutil.LegacyIndexAnnotation))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
_, err = nodes[i].Pipe(yaml.ClearAnnotation(kioutil.SeqIndentAnnotation))
|
||||
if err != nil {
|
||||
@@ -113,7 +118,7 @@ func (w ByteWriter) Write(inputNodes []*yaml.RNode) error {
|
||||
} else {
|
||||
encoder.CompactSeqIndent()
|
||||
}
|
||||
if err := encoder.Encode(nodes[i].Document()); err != nil {
|
||||
if err := encoder.Encode(upWrapBareSequenceNode(nodes[i].Document())); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
@@ -181,3 +186,13 @@ func (w ByteWriter) shouldJSONEncodeSingleBareNode(nodes []*yaml.RNode) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// upWrapBareSequenceNode unwraps the bare sequence nodes wrapped by yaml.BareSeqNodeWrappingKey
|
||||
func upWrapBareSequenceNode(node *yaml.Node) *yaml.Node {
|
||||
rNode := yaml.NewRNode(node)
|
||||
seqNode, err := rNode.Pipe(yaml.Lookup(yaml.BareSeqNodeWrappingKey))
|
||||
if err == nil && !seqNode.IsNilOrEmpty() {
|
||||
return seqNode.YNode()
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
@@ -222,8 +222,8 @@ spec:
|
||||
`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 0
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`,
|
||||
`e: f
|
||||
g:
|
||||
@@ -232,28 +232,32 @@ g:
|
||||
- j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
internal.config.kubernetes.io/index: 0
|
||||
internal.config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 1
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`,
|
||||
},
|
||||
|
||||
expectedOutput: `a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 0
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
config.kubernetes.io/path: 'a/b/a_test.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
---
|
||||
c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 1
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
config.kubernetes.io/path: 'a/b/a_test.yaml'
|
||||
config.kubernetes.io/index: '1'
|
||||
---
|
||||
e: f
|
||||
g:
|
||||
@@ -262,8 +266,10 @@ g:
|
||||
- j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
internal.config.kubernetes.io/index: 0
|
||||
internal.config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
config.kubernetes.io/path: 'a/b/b_test.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
`,
|
||||
},
|
||||
|
||||
@@ -277,13 +283,13 @@ metadata:
|
||||
`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 1
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`,
|
||||
`e: f
|
||||
g:
|
||||
@@ -293,21 +299,23 @@ g:
|
||||
`,
|
||||
},
|
||||
|
||||
expectedOutput: `e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
- j
|
||||
---
|
||||
a: b #first
|
||||
expectedOutput: `a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
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'
|
||||
---
|
||||
c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
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'
|
||||
---
|
||||
e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
- j
|
||||
`,
|
||||
},
|
||||
|
||||
@@ -321,8 +329,8 @@ metadata:
|
||||
`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 0
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: "compact"
|
||||
`,
|
||||
`e: f
|
||||
@@ -332,15 +340,15 @@ g:
|
||||
- j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
internal.config.kubernetes.io/index: 0
|
||||
internal.config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
internal.config.kubernetes.io/seqindent: "wide"
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 1
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/seqindent: "compact"
|
||||
`,
|
||||
},
|
||||
@@ -348,8 +356,8 @@ metadata:
|
||||
expectedOutput: `a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 0
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: "compact"
|
||||
---
|
||||
e: f
|
||||
@@ -359,15 +367,15 @@ g:
|
||||
- j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
internal.config.kubernetes.io/index: 0
|
||||
internal.config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
internal.config.kubernetes.io/seqindent: "wide"
|
||||
---
|
||||
c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/index: 1
|
||||
internal.config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/seqindent: "compact"
|
||||
`,
|
||||
},
|
||||
@@ -382,7 +390,7 @@ metadata:
|
||||
"a": "a long string that would certainly see a newline introduced by the YAML marshaller abcd123",
|
||||
metadata: {
|
||||
annotations: {
|
||||
config.kubernetes.io/path: test.json
|
||||
internal.config.kubernetes.io/path: test.json
|
||||
}
|
||||
}
|
||||
}`,
|
||||
@@ -392,7 +400,8 @@ metadata:
|
||||
"a": "a long string that would certainly see a newline introduced by the YAML marshaller abcd123",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"config.kubernetes.io/path": "test.json"
|
||||
"config.kubernetes.io/path": "test.json",
|
||||
"internal.config.kubernetes.io/path": "test.json"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
@@ -409,8 +418,8 @@ metadata:
|
||||
metadata: {
|
||||
annotations: {
|
||||
"internal.config.kubernetes.io/seqindent": "compact",
|
||||
"config.kubernetes.io/index": "0",
|
||||
"config.kubernetes.io/path": "test.json"
|
||||
"internal.config.kubernetes.io/index": "0",
|
||||
"internal.config.kubernetes.io/path": "test.json"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
@@ -420,7 +429,8 @@ metadata:
|
||||
"a": "a long string that would certainly see a newline introduced by the YAML marshaller abcd123",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"config.kubernetes.io/path": "test.json"
|
||||
"config.kubernetes.io/path": "test.json",
|
||||
"internal.config.kubernetes.io/path": "test.json"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
@@ -432,14 +442,15 @@ metadata:
|
||||
{
|
||||
name: "encode_unformatted_valid_json",
|
||||
items: []string{
|
||||
`{ "a": "b", metadata: { annotations: { config.kubernetes.io/path: test.json } } }`,
|
||||
`{ "a": "b", metadata: { annotations: { internal.config.kubernetes.io/path: test.json } } }`,
|
||||
},
|
||||
|
||||
expectedOutput: `{
|
||||
"a": "b",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"config.kubernetes.io/path": "test.json"
|
||||
"config.kubernetes.io/path": "test.json",
|
||||
"internal.config.kubernetes.io/path": "test.json"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
@@ -460,7 +471,7 @@ metadata:
|
||||
"a": "b",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"config.kubernetes.io/path": "test.json"
|
||||
"internal.config.kubernetes.io/path": "test.json"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
@@ -469,7 +480,7 @@ metadata:
|
||||
expectedOutput: `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- {"a": "b", "metadata": {"annotations": {"config.kubernetes.io/path": "test.json"}}}
|
||||
- {"a": "b", "metadata": {"annotations": {"internal.config.kubernetes.io/path": "test.json"}}}
|
||||
`,
|
||||
},
|
||||
|
||||
@@ -483,7 +494,7 @@ items:
|
||||
"a": "b",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"config.kubernetes.io/path": "test-1.json"
|
||||
"internal.config.kubernetes.io/path": "test-1.json"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
@@ -491,16 +502,16 @@ items:
|
||||
"c": "d",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"config.kubernetes.io/path": "test-2.json"
|
||||
"internal.config.kubernetes.io/path": "test-2.json"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
|
||||
expectedOutput: `
|
||||
{"a": "b", "metadata": {"annotations": {"config.kubernetes.io/path": "test-1.json"}}}
|
||||
{"a": "b", "metadata": {"annotations": {"internal.config.kubernetes.io/path": "test-1.json"}}}
|
||||
---
|
||||
{"c": "d", "metadata": {"annotations": {"config.kubernetes.io/path": "test-2.json"}}}
|
||||
{"c": "d", "metadata": {"annotations": {"internal.config.kubernetes.io/path": "test-2.json"}}}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -165,6 +165,10 @@ func (f *FileSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
|
||||
resources := map[string][]*yaml.RNode{}
|
||||
for i := range input {
|
||||
if err := kioutil.CopyLegacyAnnotations(input[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m, err := input[i].GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -178,6 +182,9 @@ func (f *FileSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
if _, err := input[i].Pipe(yaml.SetAnnotation(kioutil.PathAnnotation, file)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := input[i].Pipe(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, file)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
resources[file] = append(resources[file], input[i])
|
||||
}
|
||||
@@ -192,6 +199,10 @@ func (f *FileSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
yaml.SetAnnotation(kioutil.IndexAnnotation, fmt.Sprintf("%d", j))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := resources[i][j].Pipe(
|
||||
yaml.SetAnnotation(kioutil.LegacyIndexAnnotation, fmt.Sprintf("%d", j))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output = append(output, resources[i][j])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ metadata:
|
||||
name: foo1
|
||||
namespace: bar
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo1_deployment.yaml'
|
||||
config.kubernetes.io/path: 'foo1_deployment.yaml'
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -60,6 +61,7 @@ kind: Service
|
||||
metadata:
|
||||
name: foo1
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo1_service.yaml'
|
||||
config.kubernetes.io/path: 'foo1_service.yaml'
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
@@ -67,6 +69,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: foo2
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo2_deployment.yaml'
|
||||
config.kubernetes.io/path: 'foo2_deployment.yaml'
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -75,6 +78,7 @@ metadata:
|
||||
name: foo2
|
||||
namespace: bar
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo2_service.yaml'
|
||||
config.kubernetes.io/path: 'foo2_service.yaml'
|
||||
`, out.String())
|
||||
}
|
||||
@@ -97,6 +101,7 @@ kind: Service
|
||||
metadata:
|
||||
name: foo1
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo1__service.yaml'
|
||||
config.kubernetes.io/path: 'foo1__service.yaml'
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
@@ -105,6 +110,7 @@ metadata:
|
||||
name: foo1
|
||||
namespace: bar
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo1_bar_deployment.yaml'
|
||||
config.kubernetes.io/path: 'foo1_bar_deployment.yaml'
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
@@ -112,6 +118,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: foo2
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo2__deployment.yaml'
|
||||
config.kubernetes.io/path: 'foo2__deployment.yaml'
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -120,6 +127,7 @@ metadata:
|
||||
name: foo2
|
||||
namespace: bar
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo2_bar_service.yaml'
|
||||
config.kubernetes.io/path: 'foo2_bar_service.yaml'
|
||||
`, out.String())
|
||||
}
|
||||
@@ -143,6 +151,7 @@ metadata:
|
||||
name: foo1
|
||||
namespace: bar
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'resource.yaml'
|
||||
config.kubernetes.io/path: 'resource.yaml'
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
@@ -150,6 +159,7 @@ kind: Deployment
|
||||
metadata:
|
||||
name: foo2
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'resource.yaml'
|
||||
config.kubernetes.io/path: 'resource.yaml'
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -158,6 +168,7 @@ metadata:
|
||||
name: foo2
|
||||
namespace: bar
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'resource.yaml'
|
||||
config.kubernetes.io/path: 'resource.yaml'
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -165,6 +176,7 @@ kind: Service
|
||||
metadata:
|
||||
name: foo1
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'resource.yaml'
|
||||
config.kubernetes.io/path: 'resource.yaml'
|
||||
`, out.String())
|
||||
}
|
||||
|
||||
@@ -173,6 +173,12 @@ func (dm *DefaultGVKNNMatcher) IsSameResource(node1, node2 *yaml.RNode) bool {
|
||||
if node1 == nil || node2 == nil {
|
||||
return false
|
||||
}
|
||||
if err := kioutil.CopyLegacyAnnotations(node1); err != nil {
|
||||
return false
|
||||
}
|
||||
if err := kioutil.CopyLegacyAnnotations(node2); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
meta1, err := node1.GetMeta()
|
||||
if err != nil {
|
||||
|
||||
211
kyaml/kio/kio.go
211
kyaml/kio/kio.go
@@ -6,7 +6,10 @@
|
||||
package kio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -113,6 +116,14 @@ func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) erro
|
||||
// apply operations
|
||||
var err error
|
||||
for i := range p.Filters {
|
||||
// Not all RNodes passed through kio.Pipeline have metadata nor should
|
||||
// they all be required to.
|
||||
var nodeAnnos map[string]map[string]string
|
||||
nodeAnnos, err = storeInternalAnnotations(result)
|
||||
if err != nil && err != yaml.ErrMissingMetadata {
|
||||
return err
|
||||
}
|
||||
|
||||
op := p.Filters[i]
|
||||
if callback != nil {
|
||||
callback(op)
|
||||
@@ -124,6 +135,13 @@ func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) erro
|
||||
if len(result) == 0 && !p.ContinueOnEmptyResult || err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// If either the internal annotations for path, index, and id OR the legacy
|
||||
// annotations for path, index, and id are changed, we have to update the other.
|
||||
err = reconcileInternalAnnotations(result, nodeAnnos)
|
||||
if err != nil && err != yaml.ErrMissingMetadata {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// write to the outputs
|
||||
@@ -147,3 +165,196 @@ func FilterAll(filter yaml.Filter) Filter {
|
||||
return nodes, nil
|
||||
})
|
||||
}
|
||||
|
||||
// Store the original path, index, and id annotations so that we can reconcile
|
||||
// it later. This is necessary because currently both internal-prefixed annotations
|
||||
// and legacy annotations are currently supported, and a change to one must be
|
||||
// reflected in the other.
|
||||
func storeInternalAnnotations(result []*yaml.RNode) (map[string]map[string]string, error) {
|
||||
nodeAnnosMap := make(map[string]map[string]string)
|
||||
|
||||
for i := range result {
|
||||
if err := kioutil.CopyLegacyAnnotations(result[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta, err := result[i].GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := meta.Annotations[kioutil.PathAnnotation]
|
||||
index := meta.Annotations[kioutil.IndexAnnotation]
|
||||
id := meta.Annotations[kioutil.IdAnnotation]
|
||||
|
||||
if _, ok := nodeAnnosMap[path]; !ok {
|
||||
nodeAnnosMap[path] = make(map[string]string)
|
||||
}
|
||||
nodeAnnosMap[path][index] = id
|
||||
}
|
||||
return nodeAnnosMap, nil
|
||||
}
|
||||
|
||||
type nodeAnnotations struct {
|
||||
path string
|
||||
index string
|
||||
id string
|
||||
}
|
||||
|
||||
func reconcileInternalAnnotations(result []*yaml.RNode, nodeAnnosMap map[string]map[string]string) error {
|
||||
for _, node := range result {
|
||||
meta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if only one annotation is set, set the other.
|
||||
err = missingInternalOrLegacyAnnotations(node, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we must check to see if the function changed either the new internal annotations
|
||||
// or the old legacy annotations. If one is changed, the change must be reflected
|
||||
// in the other.
|
||||
err = checkAnnotationsAltered(node, meta, nodeAnnosMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if the annotations are still somehow out of sync, prefer the internal annotations
|
||||
// and copy them to the legacy ones
|
||||
err = kioutil.CopyLegacyAnnotations(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func missingInternalOrLegacyAnnotations(rn *yaml.RNode, meta yaml.ResourceMeta) error {
|
||||
if err := missingInternalOrLegacyAnnotation(rn, meta, kioutil.PathAnnotation, kioutil.LegacyPathAnnotation); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := missingInternalOrLegacyAnnotation(rn, meta, kioutil.IndexAnnotation, kioutil.LegacyIndexAnnotation); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := missingInternalOrLegacyAnnotation(rn, meta, kioutil.IdAnnotation, kioutil.LegacyIdAnnotation); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func missingInternalOrLegacyAnnotation(rn *yaml.RNode, meta yaml.ResourceMeta, newKey string, legacyKey string) error {
|
||||
value := meta.Annotations[newKey]
|
||||
legacyValue := meta.Annotations[legacyKey]
|
||||
|
||||
if value == "" && legacyValue == "" {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
// new key is not set, copy from legacy key
|
||||
if err := rn.PipeE(yaml.SetAnnotation(newKey, legacyValue)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if legacyValue == "" {
|
||||
// legacy key is not set, copy from new key
|
||||
if err := rn.PipeE(yaml.SetAnnotation(legacyKey, value)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkAnnotationsAltered(rn *yaml.RNode, meta yaml.ResourceMeta, nodeAnnosMap map[string]map[string]string) error {
|
||||
// get the resource's current path, index, and ids from the new annotations
|
||||
internal := nodeAnnotations{
|
||||
path: meta.Annotations[kioutil.PathAnnotation],
|
||||
index: meta.Annotations[kioutil.IndexAnnotation],
|
||||
id: meta.Annotations[kioutil.IdAnnotation],
|
||||
}
|
||||
|
||||
// get the resource's current path, index, and ids from the legacy annotations
|
||||
legacy := nodeAnnotations{
|
||||
path: meta.Annotations[kioutil.LegacyPathAnnotation],
|
||||
index: meta.Annotations[kioutil.LegacyIndexAnnotation],
|
||||
id: meta.Annotations[kioutil.LegacyIdAnnotation],
|
||||
}
|
||||
|
||||
if internal.path == legacy.path &&
|
||||
internal.index == legacy.index &&
|
||||
internal.id == legacy.id {
|
||||
// none of the annotations differ, so no reconciliation is needed
|
||||
return nil
|
||||
}
|
||||
|
||||
// nodeAnnosMap is a map of structure path -> index -> id that stores
|
||||
// all of the resources' path/index/id annotations prior to the functions
|
||||
// being run. We use that to check whether the legacy or new internal
|
||||
// annotations have been changed, and make sure the change is reflected
|
||||
// in the other.
|
||||
|
||||
// first, check if the internal annotations are found in nodeAnnosMap
|
||||
if indexIdMap, ok := nodeAnnosMap[internal.path]; ok {
|
||||
if id, ok := indexIdMap[internal.index]; ok {
|
||||
if id == internal.id {
|
||||
// the internal annotations of the resource match the ones stored in
|
||||
// nodeAnnosMap, so we should copy the legacy annotations to the
|
||||
// internal ones
|
||||
if err := updateAnnotations(rn, meta,
|
||||
[]string{
|
||||
kioutil.PathAnnotation,
|
||||
kioutil.IndexAnnotation,
|
||||
kioutil.IdAnnotation,
|
||||
},
|
||||
[]string{
|
||||
legacy.path,
|
||||
legacy.index,
|
||||
legacy.id,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check the opposite, to see if the legacy annotations are in nodeAnnosMap
|
||||
if indexIdMap, ok := nodeAnnosMap[legacy.path]; ok {
|
||||
if id, ok := indexIdMap[legacy.index]; ok {
|
||||
if id == legacy.id {
|
||||
// the legacy annotations of the resource match the ones stored in
|
||||
// nodeAnnosMap, so we should copy the internal annotations to the
|
||||
// legacy ones
|
||||
if err := updateAnnotations(rn, meta,
|
||||
[]string{
|
||||
kioutil.LegacyPathAnnotation,
|
||||
kioutil.LegacyIndexAnnotation,
|
||||
kioutil.LegacyIdAnnotation,
|
||||
},
|
||||
[]string{
|
||||
internal.path,
|
||||
internal.index,
|
||||
internal.id,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateAnnotations(rn *yaml.RNode, meta yaml.ResourceMeta, keys []string, values []string) error {
|
||||
if len(keys) != len(values) {
|
||||
return fmt.Errorf("keys is not same length as values")
|
||||
}
|
||||
for i := range keys {
|
||||
_, ok := meta.Annotations[keys[i]]
|
||||
if values[i] == "" && !ok {
|
||||
// don't set "" if annotation is not already there
|
||||
continue
|
||||
}
|
||||
if err := rn.PipeE(yaml.SetAnnotation(keys[i], values[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
|
||||
. "sigs.k8s.io/kustomize/kyaml/kio"
|
||||
@@ -187,3 +187,347 @@ func TestContinueOnEmptyBehavior(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyAnnotationReconciliation(t *testing.T) {
|
||||
noopFilter1 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return nodes, nil
|
||||
}
|
||||
noopFilter2 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return nodes, nil
|
||||
}
|
||||
changeInternalAnnos := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for _, rn := range nodes {
|
||||
if err := rn.PipeE(yaml.SetAnnotation(kioutil.PathAnnotation, "new")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rn.PipeE(yaml.SetAnnotation(kioutil.IndexAnnotation, "new")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
changeLegacyAnnos := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for _, rn := range nodes {
|
||||
if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, "new")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyIndexAnnotation, "new")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
changeLegacyId := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for _, rn := range nodes {
|
||||
if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyIdAnnotation, "new")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
changeInternalId := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for _, rn := range nodes {
|
||||
if err := rn.PipeE(yaml.SetAnnotation(kioutil.IdAnnotation, "new")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
noops := []Filter{
|
||||
FilterFunc(noopFilter1),
|
||||
FilterFunc(noopFilter2),
|
||||
}
|
||||
internal := []Filter{FilterFunc(changeInternalAnnos)}
|
||||
legacy := []Filter{FilterFunc(changeLegacyAnnos)}
|
||||
legacyId := []Filter{FilterFunc(changeLegacyId)}
|
||||
internalId := []Filter{FilterFunc(changeInternalId)}
|
||||
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
filters []Filter
|
||||
expected string
|
||||
}{
|
||||
// the orchestrator should copy the legacy annotations to the new
|
||||
// annotations
|
||||
"legacy annotations only": {
|
||||
input: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
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: noops,
|
||||
expected: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'configmap.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-to
|
||||
annotations:
|
||||
config.kubernetes.io/path: "configmap.yaml"
|
||||
config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'configmap.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
data:
|
||||
grpcPort: 8081
|
||||
`,
|
||||
},
|
||||
// the orchestrator should copy the new annotations to the
|
||||
// legacy annotations
|
||||
"new annotations only": {
|
||||
input: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'configmap.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-to
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: "configmap.yaml"
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
data:
|
||||
grpcPort: 8081
|
||||
`,
|
||||
filters: noops,
|
||||
expected: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'configmap.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-to
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: "configmap.yaml"
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '1'
|
||||
data:
|
||||
grpcPort: 8081
|
||||
`,
|
||||
},
|
||||
// the orchestrator should detect that the legacy annotations
|
||||
// have been changed by the function, and should update the
|
||||
// new internal annotations to reflect the same change
|
||||
"change only legacy annotations": {
|
||||
input: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
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: legacy,
|
||||
expected: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'new'
|
||||
config.kubernetes.io/index: 'new'
|
||||
internal.config.kubernetes.io/path: 'new'
|
||||
internal.config.kubernetes.io/index: 'new'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-to
|
||||
annotations:
|
||||
config.kubernetes.io/path: "new"
|
||||
config.kubernetes.io/index: 'new'
|
||||
internal.config.kubernetes.io/path: 'new'
|
||||
internal.config.kubernetes.io/index: 'new'
|
||||
data:
|
||||
grpcPort: 8081
|
||||
`,
|
||||
},
|
||||
// the orchestrator should detect that the new internal annotations
|
||||
// have been changed by the function, and should update the
|
||||
// legacy annotations to reflect the same change
|
||||
"change only internal annotations": {
|
||||
input: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
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: internal,
|
||||
expected: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'new'
|
||||
config.kubernetes.io/index: 'new'
|
||||
internal.config.kubernetes.io/path: 'new'
|
||||
internal.config.kubernetes.io/index: 'new'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-to
|
||||
annotations:
|
||||
config.kubernetes.io/path: "new"
|
||||
config.kubernetes.io/index: 'new'
|
||||
internal.config.kubernetes.io/path: 'new'
|
||||
internal.config.kubernetes.io/index: 'new'
|
||||
data:
|
||||
grpcPort: 8081
|
||||
`,
|
||||
},
|
||||
// the orchestrator should detect that the new internal id annotation
|
||||
// has been changed, and copy it over to the legacy one, and also
|
||||
// copy the path and index legacy annotations to the new internal
|
||||
// ones
|
||||
"change only internal id when original legacy set": {
|
||||
input: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.k8s.io/id: '1'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
`,
|
||||
filters: internalId,
|
||||
expected: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.k8s.io/id: 'new'
|
||||
internal.config.kubernetes.io/path: 'configmap.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/id: 'new'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
`,
|
||||
},
|
||||
// the orchestrator should detect that the legacy id annotation
|
||||
// has been changed, and copy it over to the internal one, and also
|
||||
// copy the path and index internal annotations to the legacy
|
||||
// ones
|
||||
"change only legacy id when internal legacy set": {
|
||||
input: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'configmap.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/id: '1'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
`,
|
||||
filters: legacyId,
|
||||
expected: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: ports-from
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'configmap.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/id: 'new'
|
||||
config.kubernetes.io/path: 'configmap.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
config.k8s.io/id: 'new'
|
||||
data:
|
||||
grpcPort: 8080
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
var out bytes.Buffer
|
||||
input := ByteReadWriter{
|
||||
Reader: bytes.NewBufferString(tc.input),
|
||||
Writer: &out,
|
||||
OmitReaderAnnotations: true,
|
||||
KeepReaderAnnotations: true,
|
||||
}
|
||||
p := Pipeline{
|
||||
Inputs: []Reader{&input},
|
||||
Filters: tc.filters,
|
||||
Outputs: []Writer{&input},
|
||||
}
|
||||
assert.NoError(t, p.Execute())
|
||||
assert.Equal(t, tc.expected, out.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,16 +18,31 @@ type AnnotationKey = string
|
||||
|
||||
const (
|
||||
// IndexAnnotation records the index of a specific resource in a file or input stream.
|
||||
IndexAnnotation AnnotationKey = "config.kubernetes.io/index"
|
||||
IndexAnnotation AnnotationKey = "internal.config.kubernetes.io/index"
|
||||
|
||||
// PathAnnotation records the path to the file the Resource was read from
|
||||
PathAnnotation AnnotationKey = "config.kubernetes.io/path"
|
||||
PathAnnotation AnnotationKey = "internal.config.kubernetes.io/path"
|
||||
|
||||
// SeqIndentAnnotation records the sequence nodes indentation of the input resource
|
||||
SeqIndentAnnotation AnnotationKey = "internal.config.kubernetes.io/seqindent"
|
||||
|
||||
// IdAnnotation records the id of the resource to map inputs to outputs
|
||||
IdAnnotation = "internal.config.kubernetes.io/id"
|
||||
|
||||
// LegacyIndexAnnotation is the deprecated annotation key for resource index
|
||||
LegacyIndexAnnotation AnnotationKey = "config.kubernetes.io/index"
|
||||
|
||||
// LegacyPathAnnotation is the deprecated annotation key for resource path
|
||||
LegacyPathAnnotation AnnotationKey = "config.kubernetes.io/path"
|
||||
|
||||
// LegacyIdAnnotation is the deprecated annotation key for resource ids
|
||||
LegacyIdAnnotation = "config.k8s.io/id"
|
||||
)
|
||||
|
||||
func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
|
||||
if err := CopyLegacyAnnotations(rn); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
meta, err := rn.GetMeta()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
@@ -37,6 +52,40 @@ func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
|
||||
return path, index, nil
|
||||
}
|
||||
|
||||
func CopyLegacyAnnotations(rn *yaml.RNode) error {
|
||||
meta, err := rn.GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copyAnnotations(meta, rn, LegacyPathAnnotation, PathAnnotation); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copyAnnotations(meta, rn, LegacyIndexAnnotation, IndexAnnotation); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copyAnnotations(meta, rn, LegacyIdAnnotation, IdAnnotation); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyAnnotations(meta yaml.ResourceMeta, rn *yaml.RNode, legacyKey string, newKey string) error {
|
||||
newValue := meta.Annotations[newKey]
|
||||
if newValue != "" {
|
||||
if err := rn.PipeE(yaml.SetAnnotation(legacyKey, newValue)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
legacyValue := meta.Annotations[legacyKey]
|
||||
if legacyValue != "" {
|
||||
if err := rn.PipeE(yaml.SetAnnotation(newKey, legacyValue)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrorIfMissingAnnotation validates the provided annotations are present on the given resources
|
||||
func ErrorIfMissingAnnotation(nodes []*yaml.RNode, keys ...AnnotationKey) error {
|
||||
for _, key := range keys {
|
||||
@@ -67,6 +116,9 @@ func DefaultPathAndIndexAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
|
||||
// check each node for the path annotation
|
||||
for i := range nodes {
|
||||
if err := CopyLegacyAnnotations(nodes[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -91,6 +143,9 @@ func DefaultPathAndIndexAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := nodes[i].PipeE(yaml.SetAnnotation(LegacyPathAnnotation, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set the index annotations
|
||||
@@ -113,6 +168,10 @@ func DefaultPathAndIndexAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
yaml.SetAnnotation(IndexAnnotation, fmt.Sprintf("%d", c))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := nodes[i].PipeE(
|
||||
yaml.SetAnnotation(LegacyIndexAnnotation, fmt.Sprintf("%d", c))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -122,6 +181,9 @@ func DefaultPathAndIndexAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
func DefaultPathAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
// check each node for the path annotation
|
||||
for i := range nodes {
|
||||
if err := CopyLegacyAnnotations(nodes[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -137,6 +199,9 @@ func DefaultPathAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := nodes[i].PipeE(yaml.SetAnnotation(LegacyPathAnnotation, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -185,6 +250,12 @@ func SortNodes(nodes []*yaml.RNode) error {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if err := CopyLegacyAnnotations(nodes[i]); err != nil {
|
||||
return false
|
||||
}
|
||||
if err := CopyLegacyAnnotations(nodes[j]); err != nil {
|
||||
return false
|
||||
}
|
||||
var iMeta, jMeta yaml.ResourceMeta
|
||||
if iMeta, _ = nodes[i].GetMeta(); err != nil {
|
||||
return false
|
||||
|
||||
@@ -98,6 +98,7 @@ metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo/b/bar_a.yaml'
|
||||
config.kubernetes.io/path: 'foo/b/bar_a.yaml'
|
||||
`, `with namespace`},
|
||||
{
|
||||
@@ -112,6 +113,7 @@ kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo/bar_a.yaml'
|
||||
config.kubernetes.io/path: 'foo/bar_a.yaml'
|
||||
`, `without namespace`},
|
||||
|
||||
@@ -129,6 +131,7 @@ metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'b/bar_a.yaml'
|
||||
config.kubernetes.io/path: 'b/bar_a.yaml'
|
||||
`, `without dir`},
|
||||
{
|
||||
@@ -139,6 +142,7 @@ metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'a/b.yaml'
|
||||
config.kubernetes.io/path: 'a/b.yaml'
|
||||
`,
|
||||
`apiVersion: v1
|
||||
@@ -147,6 +151,7 @@ metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'a/b.yaml'
|
||||
config.kubernetes.io/path: 'a/b.yaml'
|
||||
`, `skip`},
|
||||
}
|
||||
@@ -184,7 +189,9 @@ metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo/b/bar_a.yaml'
|
||||
config.kubernetes.io/path: 'foo/b/bar_a.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/index: '0'
|
||||
`, `with namespace`},
|
||||
{
|
||||
@@ -199,7 +206,9 @@ kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'foo/bar_a.yaml'
|
||||
config.kubernetes.io/path: 'foo/bar_a.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/index: '0'
|
||||
`, `without namespace`},
|
||||
|
||||
@@ -217,7 +226,9 @@ metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'b/bar_a.yaml'
|
||||
config.kubernetes.io/path: 'b/bar_a.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/index: '0'
|
||||
`, `without dir`},
|
||||
{
|
||||
@@ -228,7 +239,9 @@ metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'a/b.yaml'
|
||||
config.kubernetes.io/path: 'a/b.yaml'
|
||||
internal.config.kubernetes.io/index: '5'
|
||||
config.kubernetes.io/index: '5'
|
||||
`,
|
||||
`apiVersion: v1
|
||||
@@ -237,7 +250,9 @@ metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
internal.config.kubernetes.io/path: 'a/b.yaml'
|
||||
config.kubernetes.io/path: 'a/b.yaml'
|
||||
internal.config.kubernetes.io/index: '5'
|
||||
config.kubernetes.io/index: '5'
|
||||
`, `skip`},
|
||||
}
|
||||
|
||||
@@ -82,6 +82,13 @@ type LocalPackageReadWriter struct {
|
||||
|
||||
// FileSystem can be used to mock the disk file system.
|
||||
FileSystem filesys.FileSystemOrOnDisk
|
||||
|
||||
// WrapBareSeqNode wraps the bare sequence node document with map node,
|
||||
// kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this option enables wrapping such bare
|
||||
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
|
||||
// note that this wrapping is different and not related to ResourceList wrapping
|
||||
WrapBareSeqNode bool
|
||||
}
|
||||
|
||||
func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
@@ -95,6 +102,7 @@ func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
FileSkipFunc: r.FileSkipFunc,
|
||||
PreserveSeqIndent: r.PreserveSeqIndent,
|
||||
FileSystem: r.FileSystem,
|
||||
WrapBareSeqNode: r.WrapBareSeqNode,
|
||||
}.Read()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
@@ -193,6 +201,13 @@ type LocalPackageReader struct {
|
||||
|
||||
// FileSystem can be used to mock the disk file system.
|
||||
FileSystem filesys.FileSystemOrOnDisk
|
||||
|
||||
// WrapBareSeqNode wraps the bare sequence node document with map node,
|
||||
// kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this option enables wrapping such bare
|
||||
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
|
||||
// note that this wrapping is different and not related to ResourceList wrapping
|
||||
WrapBareSeqNode bool
|
||||
}
|
||||
|
||||
var _ Reader = LocalPackageReader{}
|
||||
@@ -287,6 +302,7 @@ func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode
|
||||
OmitReaderAnnotations: r.OmitReaderAnnotations,
|
||||
SetAnnotations: r.SetAnnotations,
|
||||
PreserveSeqIndent: r.PreserveSeqIndent,
|
||||
WrapBareSeqNode: r.WrapBareSeqNode,
|
||||
}
|
||||
return rr.Read()
|
||||
}
|
||||
@@ -320,6 +336,7 @@ func (r *LocalPackageReader) initReaderAnnotations(path string, _ os.FileInfo) {
|
||||
}
|
||||
if !r.OmitReaderAnnotations {
|
||||
r.SetAnnotations[kioutil.PathAnnotation] = path
|
||||
r.SetAnnotations[kioutil.LegacyPathAnnotation] = path
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,13 @@ a: b #forth
|
||||
metadata:
|
||||
`)
|
||||
|
||||
var readFileE = []byte(`---
|
||||
a: b #first
|
||||
---
|
||||
- foo # second
|
||||
- bar
|
||||
`)
|
||||
|
||||
var pkgFile = []byte(``)
|
||||
|
||||
func TestLocalPackageReader_Read_empty(t *testing.T) {
|
||||
@@ -71,12 +78,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
`# second thing
|
||||
e: f
|
||||
@@ -88,18 +99,24 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'b_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'b_test.yaml'
|
||||
`,
|
||||
`a: b #third
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'c_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'c_test.yaml'
|
||||
`,
|
||||
`a: b #forth
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'd_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'd_test.yaml'
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
@@ -133,12 +150,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
`# second thing
|
||||
e: f
|
||||
@@ -150,12 +171,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'b_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'b_test.yaml'
|
||||
`,
|
||||
`a: b #third
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'c_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'c_test.yaml'
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
@@ -191,9 +216,9 @@ func TestLocalPackageReader_Read_JSON(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, nodes, 2)
|
||||
expected := []string{
|
||||
`{"a": "b", metadata: {annotations: {config.kubernetes.io/index: '0', config.kubernetes.io/path: 'a_test.json'}}}
|
||||
`{"a": "b", metadata: {annotations: {config.kubernetes.io/index: '0', config.kubernetes.io/path: 'a_test.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'a_test.json'}}}
|
||||
`,
|
||||
`{"e": "f", "g": {"h": ["i", "j"]}, metadata: {annotations: {config.kubernetes.io/index: '0', config.kubernetes.io/path: 'b_test.json'}}}
|
||||
`{"e": "f", "g": {"h": ["i", "j"]}, metadata: {annotations: {config.kubernetes.io/index: '0', config.kubernetes.io/path: 'b_test.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'b_test.json'}}}
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
@@ -224,12 +249,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
@@ -297,6 +326,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/seqindent: 'compact'
|
||||
`,
|
||||
`c: d # second
|
||||
@@ -304,6 +335,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'a_test.yaml'
|
||||
internal.config.kubernetes.io/seqindent: 'compact'
|
||||
`,
|
||||
`# second thing
|
||||
@@ -316,6 +349,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'b_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'b_test.yaml'
|
||||
internal.config.kubernetes.io/seqindent: 'compact'
|
||||
`,
|
||||
}
|
||||
@@ -347,12 +382,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`# second thing
|
||||
e: f
|
||||
@@ -364,6 +403,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}b_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}b_test.yaml'
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
@@ -397,12 +438,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -438,12 +483,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -480,12 +529,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`# second thing
|
||||
e: f
|
||||
@@ -497,6 +550,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'a${SEP}c${SEP}c_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'a${SEP}c${SEP}c_test.yaml'
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -555,3 +610,45 @@ func testOnDiskAndOnMem(t *testing.T, files []mockFile, f func(t *testing.T, pat
|
||||
f(t, "/", fs)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalPackageReader_ReadBareSeqNodes(t *testing.T) {
|
||||
testOnDiskAndOnMem(t, []mockFile{
|
||||
{path: "a/b"},
|
||||
{path: "a/c"},
|
||||
{path: "e_test.yaml", content: readFileE},
|
||||
}, func(t *testing.T, path string, mockFS filesys.FileSystem) {
|
||||
rfr := LocalPackageReader{
|
||||
PackagePath: path,
|
||||
FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS},
|
||||
WrapBareSeqNode: true,
|
||||
}
|
||||
nodes, err := rfr.Read()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, nodes, 2)
|
||||
expected := []string{
|
||||
`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'e_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'e_test.yaml'
|
||||
`,
|
||||
`bareSeqNodeWrappingKey:
|
||||
- foo # second
|
||||
- bar
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'e_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'e_test.yaml'
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
val, err := nodes[i].String()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected[i], val)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
|
||||
|
||||
if !r.KeepReaderAnnotations {
|
||||
r.ClearAnnotations = append(r.ClearAnnotations, kioutil.PathAnnotation)
|
||||
r.ClearAnnotations = append(r.ClearAnnotations, kioutil.LegacyPathAnnotation)
|
||||
}
|
||||
|
||||
// validate outputs before writing any
|
||||
|
||||
@@ -73,14 +73,18 @@ func TestLocalPackageWriter_Write_keepReaderAnnotations(t *testing.T) {
|
||||
require.Equal(t, `a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/index: "0"
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/path: 'a/b/a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
---
|
||||
c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/index: "1"
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
internal.config.kubernetes.io/path: 'a/b/a_test.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
`, string(b))
|
||||
|
||||
b, err = fs.ReadFile(filepath.Join(d, "a", "b", "b_test.yaml"))
|
||||
@@ -92,8 +96,10 @@ g:
|
||||
- j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/index: "0"
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
internal.config.kubernetes.io/path: 'a/b/b_test.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
`, string(b))
|
||||
})
|
||||
}
|
||||
@@ -268,7 +274,7 @@ g:
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: a/
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/index: "0"
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -294,14 +300,14 @@ func getWriterInputs(t *testing.T, mockFS filesys.FileSystem) (string, *yaml.RNo
|
||||
node1, err := yaml.Parse(`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/index: "0"
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
node2, err := yaml.Parse(`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/index: "1"
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
@@ -312,7 +318,7 @@ g:
|
||||
- j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/index: "0"
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -32,7 +32,7 @@ var GraphStructures = []string{string(TreeStructureGraph), string(TreeStructureP
|
||||
|
||||
// TreeWriter prints the package structured as a tree.
|
||||
// TODO(pwittrock): test this package better. it is lower-risk since it is only
|
||||
// used for printing rather than updating or editing.
|
||||
// used for printing rather than updating or editing.
|
||||
type TreeWriter struct {
|
||||
Writer io.Writer
|
||||
Root string
|
||||
@@ -49,6 +49,11 @@ type TreeWriterField struct {
|
||||
}
|
||||
|
||||
func (p TreeWriter) packageStructure(nodes []*yaml.RNode) error {
|
||||
for i := range nodes {
|
||||
if err := kioutil.CopyLegacyAnnotations(nodes[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
indexByPackage := p.index(nodes)
|
||||
|
||||
// create the new tree
|
||||
|
||||
@@ -39,6 +39,12 @@ The above command will update the [OpenAPI schema] and the [Kustomization schema
|
||||
create a directory kubernetesapi/v1212 and store the resulting
|
||||
swagger.json and swagger.go files there.
|
||||
|
||||
#### Precomputations
|
||||
|
||||
To avoid expensive schema lookups, some functions have precomputed results based on the schema. Unit tests
|
||||
ensure these are kept in sync with the schema; if these tests fail you will need to follow the suggested diff
|
||||
to update the precomputed results.
|
||||
|
||||
### Run all tests
|
||||
|
||||
At the top of the repository, run the tests.
|
||||
|
||||
@@ -50,6 +50,95 @@ type openapiData struct {
|
||||
schemaInit bool
|
||||
}
|
||||
|
||||
// precomputedIsNamespaceScoped precomputes IsNamespaceScoped for known types. This avoids Schema creation,
|
||||
// which is expensive
|
||||
// The test output from TestIsNamespaceScopedPrecompute shows the expected map in go syntax,and can be copy and pasted
|
||||
// from the failure if it changes.
|
||||
var precomputedIsNamespaceScoped = map[yaml.TypeMeta]bool{
|
||||
{APIVersion: "admissionregistration.k8s.io/v1", Kind: "MutatingWebhookConfiguration"}: false,
|
||||
{APIVersion: "admissionregistration.k8s.io/v1", Kind: "ValidatingWebhookConfiguration"}: false,
|
||||
{APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration"}: false,
|
||||
{APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "ValidatingWebhookConfiguration"}: false,
|
||||
{APIVersion: "apiextensions.k8s.io/v1", Kind: "CustomResourceDefinition"}: false,
|
||||
{APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition"}: false,
|
||||
{APIVersion: "apiregistration.k8s.io/v1", Kind: "APIService"}: false,
|
||||
{APIVersion: "apiregistration.k8s.io/v1beta1", Kind: "APIService"}: false,
|
||||
{APIVersion: "apps/v1", Kind: "ControllerRevision"}: true,
|
||||
{APIVersion: "apps/v1", Kind: "DaemonSet"}: true,
|
||||
{APIVersion: "apps/v1", Kind: "Deployment"}: true,
|
||||
{APIVersion: "apps/v1", Kind: "ReplicaSet"}: true,
|
||||
{APIVersion: "apps/v1", Kind: "StatefulSet"}: true,
|
||||
{APIVersion: "autoscaling/v1", Kind: "HorizontalPodAutoscaler"}: true,
|
||||
{APIVersion: "autoscaling/v1", Kind: "Scale"}: true,
|
||||
{APIVersion: "autoscaling/v2beta1", Kind: "HorizontalPodAutoscaler"}: true,
|
||||
{APIVersion: "autoscaling/v2beta2", Kind: "HorizontalPodAutoscaler"}: true,
|
||||
{APIVersion: "batch/v1", Kind: "CronJob"}: true,
|
||||
{APIVersion: "batch/v1", Kind: "Job"}: true,
|
||||
{APIVersion: "batch/v1beta1", Kind: "CronJob"}: true,
|
||||
{APIVersion: "certificates.k8s.io/v1", Kind: "CertificateSigningRequest"}: false,
|
||||
{APIVersion: "certificates.k8s.io/v1beta1", Kind: "CertificateSigningRequest"}: false,
|
||||
{APIVersion: "coordination.k8s.io/v1", Kind: "Lease"}: true,
|
||||
{APIVersion: "coordination.k8s.io/v1beta1", Kind: "Lease"}: true,
|
||||
{APIVersion: "discovery.k8s.io/v1", Kind: "EndpointSlice"}: true,
|
||||
{APIVersion: "discovery.k8s.io/v1beta1", Kind: "EndpointSlice"}: true,
|
||||
{APIVersion: "events.k8s.io/v1", Kind: "Event"}: true,
|
||||
{APIVersion: "events.k8s.io/v1beta1", Kind: "Event"}: true,
|
||||
{APIVersion: "extensions/v1beta1", Kind: "Ingress"}: true,
|
||||
{APIVersion: "flowcontrol.apiserver.k8s.io/v1beta1", Kind: "FlowSchema"}: false,
|
||||
{APIVersion: "flowcontrol.apiserver.k8s.io/v1beta1", Kind: "PriorityLevelConfiguration"}: false,
|
||||
{APIVersion: "networking.k8s.io/v1", Kind: "Ingress"}: true,
|
||||
{APIVersion: "networking.k8s.io/v1", Kind: "IngressClass"}: false,
|
||||
{APIVersion: "networking.k8s.io/v1", Kind: "NetworkPolicy"}: true,
|
||||
{APIVersion: "networking.k8s.io/v1beta1", Kind: "Ingress"}: true,
|
||||
{APIVersion: "networking.k8s.io/v1beta1", Kind: "IngressClass"}: false,
|
||||
{APIVersion: "node.k8s.io/v1", Kind: "RuntimeClass"}: false,
|
||||
{APIVersion: "node.k8s.io/v1beta1", Kind: "RuntimeClass"}: false,
|
||||
{APIVersion: "policy/v1", Kind: "PodDisruptionBudget"}: true,
|
||||
{APIVersion: "policy/v1beta1", Kind: "PodDisruptionBudget"}: true,
|
||||
{APIVersion: "policy/v1beta1", Kind: "PodSecurityPolicy"}: false,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"}: false,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRoleBinding"}: false,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"}: true,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "RoleBinding"}: true,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "ClusterRole"}: false,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "ClusterRoleBinding"}: false,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "Role"}: true,
|
||||
{APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "RoleBinding"}: true,
|
||||
{APIVersion: "scheduling.k8s.io/v1", Kind: "PriorityClass"}: false,
|
||||
{APIVersion: "scheduling.k8s.io/v1beta1", Kind: "PriorityClass"}: false,
|
||||
{APIVersion: "storage.k8s.io/v1", Kind: "CSIDriver"}: false,
|
||||
{APIVersion: "storage.k8s.io/v1", Kind: "CSINode"}: false,
|
||||
{APIVersion: "storage.k8s.io/v1", Kind: "StorageClass"}: false,
|
||||
{APIVersion: "storage.k8s.io/v1", Kind: "VolumeAttachment"}: false,
|
||||
{APIVersion: "storage.k8s.io/v1beta1", Kind: "CSIDriver"}: false,
|
||||
{APIVersion: "storage.k8s.io/v1beta1", Kind: "CSINode"}: false,
|
||||
{APIVersion: "storage.k8s.io/v1beta1", Kind: "CSIStorageCapacity"}: true,
|
||||
{APIVersion: "storage.k8s.io/v1beta1", Kind: "StorageClass"}: false,
|
||||
{APIVersion: "storage.k8s.io/v1beta1", Kind: "VolumeAttachment"}: false,
|
||||
{APIVersion: "v1", Kind: "ComponentStatus"}: false,
|
||||
{APIVersion: "v1", Kind: "ConfigMap"}: true,
|
||||
{APIVersion: "v1", Kind: "Endpoints"}: true,
|
||||
{APIVersion: "v1", Kind: "Event"}: true,
|
||||
{APIVersion: "v1", Kind: "LimitRange"}: true,
|
||||
{APIVersion: "v1", Kind: "Namespace"}: false,
|
||||
{APIVersion: "v1", Kind: "Node"}: false,
|
||||
{APIVersion: "v1", Kind: "NodeProxyOptions"}: false,
|
||||
{APIVersion: "v1", Kind: "PersistentVolume"}: false,
|
||||
{APIVersion: "v1", Kind: "PersistentVolumeClaim"}: true,
|
||||
{APIVersion: "v1", Kind: "Pod"}: true,
|
||||
{APIVersion: "v1", Kind: "PodAttachOptions"}: true,
|
||||
{APIVersion: "v1", Kind: "PodExecOptions"}: true,
|
||||
{APIVersion: "v1", Kind: "PodPortForwardOptions"}: true,
|
||||
{APIVersion: "v1", Kind: "PodProxyOptions"}: true,
|
||||
{APIVersion: "v1", Kind: "PodTemplate"}: true,
|
||||
{APIVersion: "v1", Kind: "ReplicationController"}: true,
|
||||
{APIVersion: "v1", Kind: "ResourceQuota"}: true,
|
||||
{APIVersion: "v1", Kind: "Secret"}: true,
|
||||
{APIVersion: "v1", Kind: "Service"}: true,
|
||||
{APIVersion: "v1", Kind: "ServiceAccount"}: true,
|
||||
{APIVersion: "v1", Kind: "ServiceProxyOptions"}: true,
|
||||
}
|
||||
|
||||
// ResourceSchema wraps the OpenAPI Schema.
|
||||
type ResourceSchema struct {
|
||||
// Schema is the OpenAPI schema for a Resource or field
|
||||
@@ -207,15 +296,17 @@ func AddDefinitions(definitions spec.Definitions) {
|
||||
}
|
||||
// cast the extension to a []map[string]string
|
||||
exts, ok := gvk.([]interface{})
|
||||
if !ok || len(exts) != 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
typeMeta, ok := toTypeMeta(exts[0])
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
globalSchema.schemaByResourceType[typeMeta] = &d
|
||||
|
||||
for i := range exts {
|
||||
typeMeta, ok := toTypeMeta(exts[i])
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
globalSchema.schemaByResourceType[typeMeta] = &d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,10 +356,17 @@ func GetSchema(s string, schema *spec.Schema) (*ResourceSchema, error) {
|
||||
// cluster-scoped by looking at the information in the openapi schema.
|
||||
// The second return value tells whether the provided type could be found
|
||||
// in the openapi schema. If the value is false here, the scope of the
|
||||
// resource is not known. If the type if found, the first return value will
|
||||
// resource is not known. If the type is found, the first return value will
|
||||
// be true if the resource is namespace-scoped, and false if the type is
|
||||
// cluster-scoped.
|
||||
func IsNamespaceScoped(typeMeta yaml.TypeMeta) (bool, bool) {
|
||||
if res, f := precomputedIsNamespaceScoped[typeMeta]; f {
|
||||
return res, true
|
||||
}
|
||||
return isNamespaceScopedFromSchema(typeMeta)
|
||||
}
|
||||
|
||||
func isNamespaceScopedFromSchema(typeMeta yaml.TypeMeta) (bool, bool) {
|
||||
initSchema()
|
||||
isNamespaceScoped, found := globalSchema.namespaceabilityByResourceType[typeMeta]
|
||||
return isNamespaceScoped, found
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -271,6 +272,14 @@ func TestIsNamespaceScoped_builtin(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsNamespaceScopedPrecompute checks that the precomputed result meets the actual result
|
||||
func TestIsNamespaceScopedPrecompute(t *testing.T) {
|
||||
initSchema()
|
||||
if diff := cmp.Diff(globalSchema.namespaceabilityByResourceType, precomputedIsNamespaceScoped); diff != "" {
|
||||
t.Fatalf(diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNamespaceScoped_custom(t *testing.T) {
|
||||
SuppressBuiltInSchemaUse()
|
||||
err := AddSchema([]byte(`
|
||||
|
||||
@@ -13,7 +13,9 @@ import (
|
||||
// Field order might be altered due to round-tripping in arbitrary functions.
|
||||
// This functionality helps to retain the original order of fields to avoid unnecessary diffs.
|
||||
func SyncOrder(from, to *yaml.RNode) error {
|
||||
if err := syncOrder(from, to); err != nil {
|
||||
// from node should not be modified, it should be just used as a reference
|
||||
fromCopy := from.Copy()
|
||||
if err := syncOrder(fromCopy, to); err != nil {
|
||||
return errors.Errorf("failed to sync field order: %q", err.Error())
|
||||
}
|
||||
rearrangeHeadCommentOfSeqNode(to.YNode())
|
||||
|
||||
@@ -5,6 +5,7 @@ package order
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -413,6 +414,15 @@ status:
|
||||
if !assert.Equal(t, tc.expected, out.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualFrom, err := from.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, strings.TrimSpace(tc.from), strings.TrimSpace(actualFrom)) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +343,12 @@ func sortFns(buff *kio.PackageBuffer) error {
|
||||
// sort the nodes so that we traverse them depth first
|
||||
// functions deeper in the file system tree should be run first
|
||||
sort.Slice(buff.Nodes, func(i, j int) bool {
|
||||
if err := kioutil.CopyLegacyAnnotations(buff.Nodes[i]); err != nil {
|
||||
return false
|
||||
}
|
||||
if err := kioutil.CopyLegacyAnnotations(buff.Nodes[j]); err != nil {
|
||||
return false
|
||||
}
|
||||
mi, _ := buff.Nodes[i].GetMeta()
|
||||
pi := filepath.ToSlash(mi.Annotations[kioutil.PathAnnotation])
|
||||
|
||||
@@ -487,7 +493,12 @@ func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode, currentUser
|
||||
|
||||
var p string
|
||||
if spec.Starlark.Path != "" {
|
||||
p = filepath.ToSlash(path.Clean(m.Annotations[kioutil.PathAnnotation]))
|
||||
pathAnno := m.Annotations[kioutil.PathAnnotation]
|
||||
if pathAnno == "" {
|
||||
pathAnno = m.Annotations[kioutil.LegacyPathAnnotation]
|
||||
}
|
||||
p = filepath.ToSlash(path.Clean(pathAnno))
|
||||
|
||||
spec.Starlark.Path = filepath.ToSlash(path.Clean(spec.Starlark.Path))
|
||||
if filepath.IsAbs(spec.Starlark.Path) || path.IsAbs(spec.Starlark.Path) {
|
||||
return nil, errors.Errorf(
|
||||
|
||||
@@ -862,6 +862,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'cluster.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'cluster.yaml'
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`, `
|
||||
@@ -872,6 +874,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'cluster.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'cluster.yaml'
|
||||
spec:
|
||||
replicas: 10
|
||||
`},
|
||||
@@ -883,6 +887,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'cluster.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'cluster.yaml'
|
||||
spec:
|
||||
replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`, `
|
||||
@@ -893,6 +899,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'cluster.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'cluster.yaml'
|
||||
spec:
|
||||
replicas: 10
|
||||
`},
|
||||
@@ -917,6 +925,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'cluster.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'cluster.yaml'
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`, `
|
||||
@@ -927,6 +937,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'another_cluster.yaml'
|
||||
internal.config.kubernetes.io/index: '1'
|
||||
internal.config.kubernetes.io/path: 'another_cluster.yaml'
|
||||
spec:
|
||||
replicas: 10
|
||||
`},
|
||||
@@ -938,6 +950,8 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'cluster.yaml'
|
||||
internal.config.kubernetes.io/index: '0'
|
||||
internal.config.kubernetes.io/path: 'cluster.yaml'
|
||||
spec:
|
||||
replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`},
|
||||
|
||||
@@ -14,6 +14,10 @@ const (
|
||||
WideSequenceStyle SequenceIndentStyle = "wide"
|
||||
CompactSequenceStyle SequenceIndentStyle = "compact"
|
||||
DefaultIndent = 2
|
||||
// BareSeqNodeWrappingKey kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this key is used to wrap such bare
|
||||
// sequence nodes into map node, byteio_writer unwraps it while writing back
|
||||
BareSeqNodeWrappingKey = "bareSeqNodeWrappingKey"
|
||||
)
|
||||
|
||||
// SeqIndentType holds the indentation style for sequence nodes
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.16
|
||||
|
||||
require (
|
||||
sigs.k8s.io/kustomize/api v0.8.9
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.16
|
||||
|
||||
require (
|
||||
sigs.k8s.io/kustomize/api v0.8.9
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.12.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: configmap_some-cm.yaml
|
||||
internal.config.kubernetes.io/path: configmap_some-cm.yaml
|
||||
modified-by: mixer-instance
|
||||
name: some-cm
|
||||
---
|
||||
@@ -55,6 +56,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: configmap_some-cm-copy.yaml
|
||||
internal.config.kubernetes.io/path: configmap_some-cm-copy.yaml
|
||||
name: some-cm-copy
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -62,6 +64,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: configmap_net-new.yaml
|
||||
internal.config.kubernetes.io/path: configmap_net-new.yaml
|
||||
name: net-new
|
||||
`)
|
||||
}
|
||||
|
||||
166
proposals/00-00-template.md
Normal file
166
proposals/00-00-template.md
Normal file
@@ -0,0 +1,166 @@
|
||||
<!--
|
||||
**Note:** When your proposal is complete, all of these comment blocks should be removed.
|
||||
|
||||
To get started with this template:
|
||||
|
||||
- [ ] **Make a copy of this file.**
|
||||
Name it `YY-MM-short-descriptive-title.md` (where `YY-MM` is the current year and month).
|
||||
- [ ] **Fill out this file as best you can.**
|
||||
At minimum, you should fill in the "Summary" and "Motivation" sections.
|
||||
- [ ] **Create a PR.**
|
||||
Ping `@kubernetes-sigs/kustomize-admins` and `@kubernetes-sigs/kustomize-maintainers`.
|
||||
-->
|
||||
|
||||
# Your short, descriptive title
|
||||
|
||||
**Authors**: <!-- Your github handle -->
|
||||
|
||||
**Reviewers**: <!-- List at least one Kustomize approver (https://github.com/kubernetes-sigs/kustomize/blob/master/OWNERS#L2) -->
|
||||
|
||||
**Status**: implementable
|
||||
<!--
|
||||
In general, all proposals made should be merged for the record, whether or not they are accepted.
|
||||
Use the status field to record the results of the latest review:
|
||||
- implementable: The default for this repo. If the proposal is merged, you can start working on it.
|
||||
- deferred: The proposal may be accepted in the future, but it has been shelved for the time being.
|
||||
A new PR must be opened to update the proposal and gain reviewer consensus before work can begin.
|
||||
- withdrawn: The author changed their mind and no longer wants to pursue the proposal.
|
||||
A new PR must be opened to update the proposal and gain reviewer consensus before work can begin.
|
||||
- rejected: This proposal should not be implemented.
|
||||
- replaced: If you submit a new proposal that supersedes an older one,
|
||||
update the older one's status to "replaced by <link>".
|
||||
-->
|
||||
|
||||
## Summary
|
||||
|
||||
<!--
|
||||
In one short paragraph, summarize why this change is important to Kustomize users.
|
||||
-->
|
||||
|
||||
## Motivation
|
||||
|
||||
<!--
|
||||
If this proposal is an expansion of an existing GitHub issue, link to it here.
|
||||
-->
|
||||
|
||||
**Goals:**
|
||||
<!--
|
||||
List the specific goals of the proposal. What is it trying to achieve? How will we
|
||||
know that this has succeeded?
|
||||
-->
|
||||
1. A goal
|
||||
1. Another goal
|
||||
|
||||
|
||||
**Non-goals:**
|
||||
<!--
|
||||
What is out of scope for this proposal? Listing non-goals helps to focus discussion
|
||||
and make progress.
|
||||
-->
|
||||
1. A non-goal
|
||||
1. Another non-goal
|
||||
|
||||
## Proposal
|
||||
|
||||
<!--
|
||||
This is where we get down to the specifics of what the proposal actually is.
|
||||
Include enough information to illustrate your proposal, but try not to
|
||||
overwhelm reviewers with details. Focus on APIs and interfaces rather than implementation details,
|
||||
e.g.:
|
||||
- Does this proposal require new kinds, fields or CLI flags?
|
||||
- Will this feature require extending the public interface of Kustomize's Go packages?
|
||||
(it's ok if you're not sure yet)
|
||||
|
||||
A proof of concept PR is NOT required but is preferable to including large amounts of code
|
||||
inline here, if you feel such implementation details are required to adequately explain your design.
|
||||
If you have a PR, link to it at the top of this section.
|
||||
-->
|
||||
|
||||
### User Stories
|
||||
<!--
|
||||
Describe what people will be able to do if this KEP is implemented. If different user personas
|
||||
will use the feature differently, consider writing separate stories for each.
|
||||
Include as much detail as possible so that people can understand the "how" of the system.
|
||||
The goal here is to make this feel real for users without getting bogged down.
|
||||
-->
|
||||
|
||||
#### Story 1
|
||||
|
||||
Scenario summary: As a [end user, extension developer, ...], I want to [...]
|
||||
<!--
|
||||
A walkthrough of what it will look like for a user to take advantage of the new feature.
|
||||
Include the the steps the user will take and samples of the commands they'll run
|
||||
and config they'll use.
|
||||
-->
|
||||
|
||||
#### Story 2
|
||||
|
||||
Scenario summary: As a [end user, extension developer, ...], I want to [...]
|
||||
<!--
|
||||
A walkthrough of what it will look like for a user to take advantage of the new feature.
|
||||
Include the the steps the user will take and samples of the commands they'll run
|
||||
and config they'll use.
|
||||
-->
|
||||
|
||||
### Risks and Mitigations
|
||||
<!--
|
||||
What are the risks of this proposal, and how do we mitigate? Think broadly.
|
||||
For example, consider both security, end-user privacy, and how this will
|
||||
impact the larger Kubernetes ecosystem.
|
||||
-->
|
||||
|
||||
### Dependencies
|
||||
<!--
|
||||
Kustomize tightly controls its Go dependencies in order to remain approved for
|
||||
integration into kubectl. It cannot depend directly on kubectl or apimachinery code.
|
||||
Identify any new Go dependencies this proposal will require Kustomize to pull in.
|
||||
If any of them are large, is there another option?
|
||||
-->
|
||||
|
||||
### Scalability
|
||||
<!--
|
||||
Is this feature expected to have a performance impact?
|
||||
Explain to what extent and under what conditions.
|
||||
-->
|
||||
|
||||
## Drawbacks
|
||||
<!--
|
||||
Why should this proposal _not_ be implemented?
|
||||
-->
|
||||
|
||||
## Alternatives
|
||||
<!--
|
||||
What other approaches did you consider, and why did you rule them out? Be concise,
|
||||
but do include enough information to express the idea and why it was not acceptable.
|
||||
-->
|
||||
|
||||
## Rollout Plan
|
||||
<!--
|
||||
Depending on the scope of the features and the risks enabling it implies,
|
||||
you may need to use a formal graduation process. If you don't think this is
|
||||
necessary, explain why here, and delete the alpha/beta/GA headings below.
|
||||
-->
|
||||
|
||||
### Alpha
|
||||
<!--
|
||||
New Kinds should be introduced with an alpha group version.
|
||||
New major features should often be gated by an alpha flag at first.
|
||||
New transformers can be introduced for use in the generators/validators/transformers fields
|
||||
before they get their own top-level field in Kustomization.
|
||||
-->
|
||||
|
||||
- Will the feature be gated by an "alpha" flag? Which one?
|
||||
- Will the feature be available in `kubectl kustomize` during alpha? Why or why not?
|
||||
|
||||
### Beta
|
||||
<!--
|
||||
If the alpha was not available in `kubectl kustomize`, you need a beta phase where it is.
|
||||
Full parity with `kubectl kustomize` is required at this stage.
|
||||
-->
|
||||
|
||||
### GA
|
||||
<!--
|
||||
You should generally wait at least two `kubectl` release cycles before promotion to GA,
|
||||
to ensure that the broader user base has time to try the feature and provide feedback.
|
||||
For example, if your feature first appears in kubectl 1.23, promote it in 1.25 or later.
|
||||
-->
|
||||
48
proposals/README.md
Normal file
48
proposals/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Kustomize Enhancement Proposal Processes
|
||||
|
||||
So you want to propose an enhancement to Kustomize—awesome! Choose the option below that best fits the scope of your idea. Before you get started, it's a good idea to review the list of [eschewed features](https://kubectl.docs.kubernetes.io/faq/kustomize/eschewedfeatures).
|
||||
|
||||
[SIG CLI]: https://github.com/kubernetes/community/tree/master/sig-cli
|
||||
[Enhancements repo]: https://github.com/kubernetes/enhancements
|
||||
|
||||
### Option 1: Github issue
|
||||
|
||||
Small, straightforward enhancements can be proposed in regular GitHub issues. As a rule of thumb, the enhancement should be resolvable in a single PR that is at most size L.
|
||||
|
||||
**Example features**:
|
||||
- a new Kustomization field that does something very straightforward, like annotating resources
|
||||
- a new option for an existing built-in transformer
|
||||
|
||||
**Instructions**: [Open an issue](https://github.com/kubernetes-sigs/kustomize/issues/new?labels=kind%2Ffeature&template=feature_request.md)
|
||||
|
||||
### Option 2: Mini (In-Repo) Enhancement Proposal
|
||||
|
||||
If your feature may be controversial or has a lot of details to explain, you should write it up as a mini enhancement proposal on this repo. This process is still relatively lightweight, but allows for more in-depth discussion of multiple details. Because it is submitted as a PR, reviewers can comment on individual lines of the proposal, facilitating discussion. The PR will be merged whether the feature is accepted or rejected, creating a record of the decisions.
|
||||
|
||||
Since the proposal template is a subset of what's required for the full KEP process, this can also be a good option if you're unsure whether your proposal is of general interest to [SIG-CLI]. You can use it to get preliminary feedback from the Kustomize team before bringing a full-fledged KEP to the SIG.
|
||||
|
||||
**Example features**:
|
||||
- a new built-in transformer with several configurable options
|
||||
- a feature that will bring in a significant new dependency
|
||||
- a feature that introduces a new class of behavior, such as manipulating data within an opaque resource field
|
||||
- a new build process that does not affect `kubectl kustomize`
|
||||
|
||||
**Instructions**:
|
||||
1. Make a copy of [00-00-template.md](00-00-template.md) and rename it with the current date (e.g. 21-08) and a succinct title.
|
||||
1. Fill out the template.
|
||||
1. Submit it for review as a PR.
|
||||
1. (Optional) Present your proposal at a biweekly [SIG-CLI] meeting. This can be a good way to get more traction for your proposal. Your presentation should be a quick summary to help folks understand whether the proposal is relevant to them.
|
||||
|
||||
### Option 3: Kubernetes Enhancement Proposal (KEP)
|
||||
|
||||
If your feature changes behaviour in a way that has significance for `kubectl kustomize`, particularly in terms of security, you will need to follow the full KEP process and get buy-in from [SIG-CLI] leadership in addition to Kustomize maintainers. Note that you can still submit a mini (in-repo) enhancement proposal as a first step to get preliminary feedback, including on whether a full KEP is required.
|
||||
|
||||
**Example features**:
|
||||
- a feature with significant privacy or security implications to work out
|
||||
- a feature that is not purely localized to the Kustomize binary on the end user's machine (e.g. downloads something remote, executes something external)
|
||||
|
||||
**Instructions**:
|
||||
1. Follow the process on the [Enhancements repo]. Be sure to put your KEP in the directory for SIG-CLI.
|
||||
1. (Strongly recommended) Send a link to your KEP to the [SIG-CLI] mailing list.
|
||||
1. (Strongly recommended) Present your KEP at a biweekly [SIG-CLI] meeting. Your presentation should be a quick summary to help folks understand whether the KEP is relevant to them.
|
||||
1. After your KEP is accepted, remember to update its metadata as your feature proceeds through the release process.
|
||||
@@ -15,6 +15,20 @@
|
||||
This document describes how to perform a [semver release]
|
||||
of one of the several [Go modules] in this repository.
|
||||
|
||||
#### semver review
|
||||
|
||||
Go's [semver]-compatible version tags take the form `v{major}.{minor}.{patch}`:
|
||||
|
||||
| major | minor | patch |
|
||||
| :---: | :---: | :---: |
|
||||
| API change | enhancements | bug fixes |
|
||||
| manual update | maybe auto-update | auto-update encouraged |
|
||||
|
||||
- If there are only bug fixes or refactors, increment `patch` from whatever it is now.
|
||||
- If there are new features, increment `minor`.
|
||||
- If there's an API change (either the Go API or the CLI behavior
|
||||
with respect to CLI arguments and flags), increment `major`.
|
||||
|
||||
## Release sequence
|
||||
|
||||
The dependencies determine the release order:
|
||||
@@ -28,10 +42,17 @@ The dependencies determine the release order:
|
||||
|
||||
Thus, do `kyaml` first, then `cmd/config`, etc.
|
||||
|
||||
## Prep work
|
||||
|
||||
#### Prepare your source directory
|
||||
|
||||
The release scripts expect Kustomize code to be cloned at a path ending in `sigs.k8s.io/kustomize`. Run all commands from that directory unless otherwise specified.
|
||||
|
||||
#### Consider fetching new OpenAPI data
|
||||
The Kubernetes OpenAPI data changes no more frequently than once per quarter.
|
||||
You can check the current builtin versions that kustomize is using with the
|
||||
following command.
|
||||
|
||||
```
|
||||
kustomize openapi info
|
||||
```
|
||||
@@ -39,46 +60,10 @@ kustomize openapi info
|
||||
Instructions on how to get a new OpenAPI sample can be found in the
|
||||
[OpenAPI Readme].
|
||||
|
||||
## Prep work
|
||||
|
||||
#### Make some helper functions
|
||||
#### Load some helper functions
|
||||
|
||||
```
|
||||
function createBranch {
|
||||
branch=$1
|
||||
echo "Making branch $branch : \"$title\""
|
||||
git branch -D $branch # delete if it exists
|
||||
git checkout -b $branch
|
||||
git commit -a -m "$title"
|
||||
git push -f origin $branch
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
function createPr {
|
||||
gh pr create --title "$title" --body "ALLOW_MODULE_SPAN" --base master
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
function refreshMaster {
|
||||
git checkout master
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
function testKustomizeRepo {
|
||||
make prow-presubmit-check >& /tmp/k.txt
|
||||
local code=$?
|
||||
if [ $code -ne 0 ]; then
|
||||
echo "**** FAILURE ******************"
|
||||
tail /tmp/k.txt
|
||||
else
|
||||
echo "LGTM"
|
||||
fi
|
||||
}
|
||||
source releasing/helpers.sh
|
||||
```
|
||||
|
||||
#### Install the release tool
|
||||
@@ -101,18 +86,21 @@ echo $GITHUB_TOKEN | gh auth login --scopes repo --with-token
|
||||
#### Establish clean state
|
||||
|
||||
```
|
||||
cd ~/gopath/src/sigs.k8s.io/kustomize
|
||||
refreshMaster
|
||||
testKustomizeRepo
|
||||
```
|
||||
|
||||
While you're waiting for the tests, review the commit log. Based on the changes to be included in this release, decide whether a patch, minor or major version bump is needed: [semver review].
|
||||
|
||||
kyaml has no intra-repo deps, so if the tests pass,
|
||||
it can just be released.
|
||||
|
||||
Release it:
|
||||
#### Release it
|
||||
|
||||
The default increment is a new patch version.
|
||||
|
||||
```
|
||||
gorepomod release kyaml --doIt
|
||||
gorepomod release kyaml [patch|minor|major] --doIt
|
||||
```
|
||||
|
||||
Note the version:
|
||||
@@ -120,26 +108,27 @@ Note the version:
|
||||
versionKyaml=v0.10.20 # EDIT THIS!
|
||||
```
|
||||
|
||||
Undraft the release on the [kustomize repo release page],
|
||||
make sure the version number is what you expect.
|
||||
Undraft the release on the [kustomize repo release page]:
|
||||
* 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).
|
||||
* Make sure each commit left in the release notes includes a PR reference.
|
||||
|
||||
|
||||
## Release `cmd/config`
|
||||
|
||||
```
|
||||
cd ../kustomize
|
||||
```
|
||||
|
||||
Pin to the most recent kyaml.
|
||||
#### Pin to the most recent kyaml
|
||||
|
||||
```
|
||||
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/replicacounttransformer/go.mod
|
||||
|
||||
```
|
||||
|
||||
Create the PR:
|
||||
```
|
||||
title="Pin to kyaml $versionKyaml"
|
||||
createBranch pinToKyaml
|
||||
createBranch pinToKyaml $title
|
||||
createPr
|
||||
```
|
||||
|
||||
@@ -160,9 +149,12 @@ refreshMaster
|
||||
testKustomizeRepo
|
||||
```
|
||||
|
||||
Release it:
|
||||
While you're waiting for the tests, review the commit log. Based on the changes to be included in this release, decide whether a patch, minor or major version bump is needed: [semver review].
|
||||
|
||||
#### Release it
|
||||
|
||||
```
|
||||
gorepomod release cmd/config --doIt
|
||||
gorepomod release cmd/config [patch|minor|major] --doIt
|
||||
```
|
||||
|
||||
Note the version:
|
||||
@@ -170,8 +162,10 @@ Note the version:
|
||||
versionCmdConfig=v0.9.12 # EDIT THIS!
|
||||
```
|
||||
|
||||
Undraft the release on the [kustomize repo release page],
|
||||
make sure the version number is what you expect.
|
||||
Undraft the release on the [kustomize repo release page]:
|
||||
* 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).
|
||||
* Make sure each commit left in the release notes includes a PR reference.
|
||||
|
||||
|
||||
## Release `api`
|
||||
@@ -179,7 +173,7 @@ make sure the version number is what you expect.
|
||||
This is the kustomize API, used by the kustomize CLI.
|
||||
|
||||
|
||||
Pin to the new cmd/config:
|
||||
#### Pin to the new cmd/config
|
||||
|
||||
```
|
||||
gorepomod pin cmd/config --doIt
|
||||
@@ -188,7 +182,7 @@ gorepomod pin cmd/config --doIt
|
||||
Create the PR:
|
||||
```
|
||||
title="Pin to cmd/config $versionCmdConfig"
|
||||
createBranch pinToCmdConfig
|
||||
createBranch pinToCmdConfig $title
|
||||
createPr
|
||||
```
|
||||
|
||||
@@ -209,9 +203,12 @@ refreshMaster
|
||||
testKustomizeRepo
|
||||
```
|
||||
|
||||
Release it:
|
||||
While you're waiting for the tests, review the commit log. Based on the changes to be included in this release, decide whether a patch, minor or major version bump is needed: [semver review].
|
||||
|
||||
#### Release it
|
||||
|
||||
```
|
||||
gorepomod release api --doIt
|
||||
gorepomod release api [patch|minor|major] --doIt
|
||||
```
|
||||
|
||||
Note the version:
|
||||
@@ -219,13 +216,16 @@ Note the version:
|
||||
versionApi=v0.8.10 # EDIT THIS!
|
||||
```
|
||||
|
||||
Undraft the release on the [kustomize repo release page],
|
||||
make sure the version number is what you expect.
|
||||
Undraft the release on the [kustomize repo release page]:
|
||||
* 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).
|
||||
* Make sure each commit left in the release notes includes a PR reference.
|
||||
|
||||
|
||||
## Release the kustomize CLI
|
||||
|
||||
Pin to the new API:
|
||||
#### Pin to the new API
|
||||
|
||||
```
|
||||
gorepomod pin api --doIt
|
||||
```
|
||||
@@ -233,7 +233,7 @@ gorepomod pin api --doIt
|
||||
Create the PR:
|
||||
```
|
||||
title="Pin to api $versionApi"
|
||||
createBranch pinToApi
|
||||
createBranch pinToApi $title
|
||||
createPr
|
||||
```
|
||||
|
||||
@@ -254,12 +254,18 @@ refreshMaster
|
||||
testKustomizeRepo
|
||||
```
|
||||
|
||||
Release it:
|
||||
While you're waiting for the tests, review the commit log. Based on the changes to be included in this release, decide whether a patch, minor or major version bump is needed: [semver review].
|
||||
|
||||
#### Release it
|
||||
|
||||
```
|
||||
gorepomod release kustomize --doIt
|
||||
gorepomod release kustomize [patch|minor|major] --doIt
|
||||
```
|
||||
|
||||
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.
|
||||
* 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).
|
||||
* Make sure each commit left in the release notes includes a PR reference.
|
||||
|
||||
## Confirm the kustomize binary is correct
|
||||
|
||||
@@ -308,10 +314,31 @@ refreshMaster
|
||||
testKustomizeRepo
|
||||
```
|
||||
|
||||
### Publish Official Docker Image
|
||||
## Update example test target
|
||||
|
||||
[Makefile]: https://github.com/kubernetes-sigs/kustomize/blob/master/Makefile
|
||||
|
||||
Edit the `prow-presubmit-target` in the [Makefile]
|
||||
to test examples against your new release.
|
||||
|
||||
```
|
||||
sed -i "" "s/LATEST_V4_RELEASE=.*/LATEST_V4_RELEASE=v4.3.0/" Makefile
|
||||
title="Test examples against latest release"
|
||||
createBranch updateProwExamplesTarget $title
|
||||
createPr
|
||||
```
|
||||
|
||||
Wait for tests to pass, then merge the PR:
|
||||
```
|
||||
gh pr status # rinse, repeat
|
||||
gh pr merge -m
|
||||
```
|
||||
|
||||
|
||||
## Publish Official Docker Image
|
||||
|
||||
[k8s.io]: https://github.com/kubernetes/k8s.io
|
||||
[k8s-staging-kustomize]: https://pantheon.corp.google.com/gcr/images/k8s-staging-kustomize?project=k8s-staging-kustomize
|
||||
[k8s-staging-kustomize]: https://console.cloud.google.com/gcr/images/k8s-staging-kustomize?project=k8s-staging-kustomize
|
||||
|
||||
Fork and clone the [k8s.io] repo.
|
||||
|
||||
@@ -326,13 +353,6 @@ project [k8s-staging-kustomize].
|
||||
Commit and push your changes. Then create a PR to [k8s.io] to promote
|
||||
new images. Assign the PR to @monopole and @Shell32-natsu.
|
||||
|
||||
### Finally
|
||||
|
||||
[Makefile]: https://github.com/kubernetes-sigs/kustomize/blob/master/Makefile
|
||||
|
||||
Edit the `prow-presubmit-target` in the [Makefile]
|
||||
to test examples against your new release.
|
||||
|
||||
----
|
||||
|
||||
----
|
||||
@@ -462,21 +482,6 @@ Set the version you want:
|
||||
major=0; minor=1; patch=0
|
||||
```
|
||||
|
||||
#### semver review
|
||||
|
||||
Go's [semver]-compatible version tags take the form `v{major}.{minor}.{patch}`:
|
||||
|
||||
| major | minor | patch |
|
||||
| :---: | :---: | :---: |
|
||||
| API change | enhancements | bug fixes |
|
||||
| manual update | maybe auto-update | auto-update encouraged |
|
||||
|
||||
- If there are only bug fixes or refactors, increment `patch` from whatever it is now.
|
||||
- If there are new features, increment `minor`.
|
||||
- If there's an API change (either the Go API or the CLI behavior
|
||||
with respect to CLI arguments and flags), increment `major`.
|
||||
|
||||
|
||||
|
||||
### Create the release branch
|
||||
|
||||
|
||||
@@ -57,6 +57,6 @@ timeout: 14m
|
||||
# The base64 of that is shown below. It's decrypted by cloud build
|
||||
# and provided back to goreleaser.
|
||||
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:
|
||||
GITHUB_TOKEN: CiQAwfbOkSP4tJf3ZJZMjzHaRPZ2RxiQhORZ3xxlVtpoy8631uQSUACk6WMKjtkpsRkRl+uxWUVvN29M5qveyXjaDDO094/qwsSc8RiYlHYt7Ii1bWkkz3P1kG0nHfG7Fd46A+GJ6R5NhmNfingd/nu9iKrNwLXK
|
||||
GITHUB_TOKEN: CiQAJ+XRL07Aror04bf6N0PpMDxRpxzs1PXVVDztB+HNu3fW7FESUQA2EggaBGI1cpFJC1YT93h9r50WzyLMD28LDDBTO8QJxZsU6UEToBfpVDr0ohnSazBTbvCcy5NJK0ooKyDifFsKzkT5ym3LPyHzPIXiejCmAg==
|
||||
|
||||
32
releasing/helpers.sh
Normal file
32
releasing/helpers.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function createBranch {
|
||||
branch=$1
|
||||
title=$2
|
||||
echo "Making branch $branch : \"$title\""
|
||||
git branch -D $branch # delete if it exists
|
||||
git checkout -b $branch
|
||||
git commit -a -m "$title"
|
||||
git push -f origin $branch
|
||||
}
|
||||
|
||||
function createPr {
|
||||
gh pr create --title "$title" --body "ALLOW_MODULE_SPAN" --base master
|
||||
}
|
||||
|
||||
function refreshMaster {
|
||||
git checkout master
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
}
|
||||
|
||||
function testKustomizeRepo {
|
||||
make prow-presubmit-check >& /tmp/k.txt
|
||||
local code=$?
|
||||
if [ $code -ne 0 ]; then
|
||||
echo "**** FAILURE ******************"
|
||||
tail /tmp/k.txt
|
||||
else
|
||||
echo "LGTM"
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user