upgrade kustomize-in-kubectl to v4.4.1

This commit is contained in:
natasha41575 2021-11-12 10:57:32 -08:00
parent 763916aa2d
commit 5991d3ded0
67 changed files with 7605 additions and 1696 deletions

8
go.mod
View File

@ -526,10 +526,10 @@ replace (
rsc.io/pdf => rsc.io/pdf v0.1.1
sigs.k8s.io/apiserver-network-proxy/konnectivity-client => sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25
sigs.k8s.io/json => sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6
sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.8.11
sigs.k8s.io/kustomize/cmd/config => sigs.k8s.io/kustomize/cmd/config v0.9.13
sigs.k8s.io/kustomize/kustomize/v4 => sigs.k8s.io/kustomize/kustomize/v4 v4.2.0
sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.11.0
sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.10.1
sigs.k8s.io/kustomize/cmd/config => sigs.k8s.io/kustomize/cmd/config v0.10.2
sigs.k8s.io/kustomize/kustomize/v4 => sigs.k8s.io/kustomize/kustomize/v4 v4.4.1
sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/structured-merge-diff/v4 => sigs.k8s.io/structured-merge-diff/v4 v4.2.0
sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.2.0
)

14
go.sum
View File

@ -606,13 +606,13 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25 h1:DEQ12ZRxJjsgl
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
sigs.k8s.io/kustomize/api v0.8.11 h1:LzQzlq6Z023b+mBtc6v72N2mSHYmN8x7ssgbf/hv0H8=
sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g=
sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs=
sigs.k8s.io/kustomize/kustomize/v4 v4.2.0 h1:RKgbyHgzuHQZ35sBDzWcbnR3HBlJSYdSN0H+sx3tUkk=
sigs.k8s.io/kustomize/kustomize/v4 v4.2.0/go.mod h1:MOkR6fmhwG7hEDRXBYELTi5GSFcLwfqwzTRHW3kv5go=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/kustomize/api v0.10.1 h1:KgU7hfYoscuqag84kxtzKdEC3mKMb99DPI3a0eaV1d0=
sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=
sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ=
sigs.k8s.io/kustomize/kustomize/v4 v4.4.1 h1:6hgMEo3Gt0XmhDt4vo0FJ0LRDMc4i8JC6SUW24D4hQM=
sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io=
sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=

View File

@ -22,8 +22,8 @@ require (
k8s.io/apimachinery v0.0.0
k8s.io/client-go v0.0.0
k8s.io/kube-openapi v0.0.0-20211105084753-ee342a809c29
sigs.k8s.io/kustomize/api v0.8.11
sigs.k8s.io/kustomize/kyaml v0.11.0
sigs.k8s.io/kustomize/api v0.10.1
sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/yaml v1.2.0
)

View File

@ -810,10 +810,10 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
sigs.k8s.io/kustomize/api v0.8.11 h1:LzQzlq6Z023b+mBtc6v72N2mSHYmN8x7ssgbf/hv0H8=
sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/kustomize/api v0.10.1 h1:KgU7hfYoscuqag84kxtzKdEC3mKMb99DPI3a0eaV1d0=
sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=
sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=

View File

@ -41,8 +41,8 @@ require (
k8s.io/kube-openapi v0.0.0-20211105084753-ee342a809c29
k8s.io/metrics v0.0.0
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b
sigs.k8s.io/kustomize/kustomize/v4 v4.2.0
sigs.k8s.io/kustomize/kyaml v0.11.0
sigs.k8s.io/kustomize/kustomize/v4 v4.4.1
sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/yaml v1.2.0
)

View File

@ -926,13 +926,13 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
sigs.k8s.io/kustomize/api v0.8.11 h1:LzQzlq6Z023b+mBtc6v72N2mSHYmN8x7ssgbf/hv0H8=
sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g=
sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs=
sigs.k8s.io/kustomize/kustomize/v4 v4.2.0 h1:RKgbyHgzuHQZ35sBDzWcbnR3HBlJSYdSN0H+sx3tUkk=
sigs.k8s.io/kustomize/kustomize/v4 v4.2.0/go.mod h1:MOkR6fmhwG7hEDRXBYELTi5GSFcLwfqwzTRHW3kv5go=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/kustomize/api v0.10.1 h1:KgU7hfYoscuqag84kxtzKdEC3mKMb99DPI3a0eaV1d0=
sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=
sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ=
sigs.k8s.io/kustomize/kustomize/v4 v4.4.1 h1:6hgMEo3Gt0XmhDt4vo0FJ0LRDMc4i8JC6SUW24D4hQM=
sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io=
sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=

View File

@ -810,10 +810,10 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
sigs.k8s.io/kustomize/api v0.8.11 h1:LzQzlq6Z023b+mBtc6v72N2mSHYmN8x7ssgbf/hv0H8=
sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/kustomize/api v0.10.1 h1:KgU7hfYoscuqag84kxtzKdEC3mKMb99DPI3a0eaV1d0=
sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=
sigs.k8s.io/kustomize/kyaml v0.13.0 h1:9c+ETyNfSrVhxvphs+K2dzT3dh5oVPPEqPOE/cUpScY=
sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=

18
vendor/modules.txt vendored
View File

@ -2356,7 +2356,7 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client
# sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 => sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6
sigs.k8s.io/json
sigs.k8s.io/json/internal/golang/encoding/json
# sigs.k8s.io/kustomize/api v0.8.11 => sigs.k8s.io/kustomize/api v0.8.11
# sigs.k8s.io/kustomize/api v0.10.1 => sigs.k8s.io/kustomize/api v0.10.1
sigs.k8s.io/kustomize/api/builtins
sigs.k8s.io/kustomize/api/filters/annotations
sigs.k8s.io/kustomize/api/filters/fieldspec
@ -2400,9 +2400,9 @@ sigs.k8s.io/kustomize/api/provider
sigs.k8s.io/kustomize/api/resmap
sigs.k8s.io/kustomize/api/resource
sigs.k8s.io/kustomize/api/types
# sigs.k8s.io/kustomize/kustomize/v4 v4.2.0 => sigs.k8s.io/kustomize/kustomize/v4 v4.2.0
# sigs.k8s.io/kustomize/kustomize/v4 v4.4.1 => sigs.k8s.io/kustomize/kustomize/v4 v4.4.1
sigs.k8s.io/kustomize/kustomize/v4/commands/build
# sigs.k8s.io/kustomize/kyaml v0.11.0 => sigs.k8s.io/kustomize/kyaml v0.11.0
# sigs.k8s.io/kustomize/kyaml v0.13.0 => sigs.k8s.io/kustomize/kyaml v0.13.0
sigs.k8s.io/kustomize/kyaml/comments
sigs.k8s.io/kustomize/kyaml/errors
sigs.k8s.io/kustomize/kyaml/ext
@ -2419,11 +2419,13 @@ sigs.k8s.io/kustomize/kyaml/kio/filters
sigs.k8s.io/kustomize/kyaml/kio/kioutil
sigs.k8s.io/kustomize/kyaml/openapi
sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi
sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/v1204
sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/v1212
sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi
sigs.k8s.io/kustomize/kyaml/order
sigs.k8s.io/kustomize/kyaml/resid
sigs.k8s.io/kustomize/kyaml/runfn
sigs.k8s.io/kustomize/kyaml/sets
sigs.k8s.io/kustomize/kyaml/sliceutil
sigs.k8s.io/kustomize/kyaml/yaml
sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/labels
sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/selection
@ -2832,9 +2834,9 @@ sigs.k8s.io/yaml
# rsc.io/pdf => rsc.io/pdf v0.1.1
# sigs.k8s.io/apiserver-network-proxy/konnectivity-client => sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25
# sigs.k8s.io/json => sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6
# sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.8.11
# sigs.k8s.io/kustomize/cmd/config => sigs.k8s.io/kustomize/cmd/config v0.9.13
# sigs.k8s.io/kustomize/kustomize/v4 => sigs.k8s.io/kustomize/kustomize/v4 v4.2.0
# sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.11.0
# sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.10.1
# sigs.k8s.io/kustomize/cmd/config => sigs.k8s.io/kustomize/cmd/config v0.10.2
# sigs.k8s.io/kustomize/kustomize/v4 => sigs.k8s.io/kustomize/kustomize/v4 v4.4.1
# sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.13.0
# sigs.k8s.io/structured-merge-diff/v4 => sigs.k8s.io/structured-merge-diff/v4 v4.2.0
# sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.2.0

View File

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

View File

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

View File

@ -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,

View File

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

View File

@ -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) {

View File

@ -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
}

View File

@ -125,7 +125,7 @@ func parseGitUrl(n string) (
index := strings.Index(n, gitSuffix)
orgRepo = n[0:index]
n = n[index+len(gitSuffix):]
if n[0] == '/' {
if len(n) > 0 && n[0] == '/' {
n = n[1:]
}
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)

View File

@ -79,6 +79,7 @@ func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin {
StorageMounts: toStorageMounts(o.Mounts),
Env: o.Env,
AsCurrentUser: o.AsCurrentUser,
WorkingDir: o.WorkingDir,
},
}
}

View File

@ -47,6 +47,11 @@ func (l *Loader) Config() *types.PluginConfig {
return l.pc
}
// SetWorkDir sets the working directory for this loader's plugins
func (l *Loader) SetWorkDir(wd string) {
l.pc.FnpLoadingOptions.WorkingDir = wd
}
func (l *Loader) LoadGenerators(
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
var result []resmap.Generator

View File

@ -231,10 +231,10 @@ func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {
if err := r.SetAnnotations(annotations); err != nil {
return nil, err
}
r.SetOptions(types.NewGenArgs(
&types.GeneratorArgs{
Behavior: behavior,
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
if needsHash {
r.EnableHashSuffix()
}
r.SetBehavior(types.NewGenerationBehavior(behavior))
}
return rm, nil
}

View File

@ -10,14 +10,17 @@ 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"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/yaml"
@ -38,11 +41,13 @@ func NewKustTarget(
validator ifc.Validator,
rFactory *resmap.Factory,
pLdr *loader.Loader) *KustTarget {
pLdrCopy := *pLdr
pLdrCopy.SetWorkDir(ldr.Root())
return &KustTarget{
ldr: ldr,
validator: validator,
rFactory: rFactory,
pLdr: pLdr,
pLdr: &pLdrCopy,
}
}
@ -108,7 +113,7 @@ func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
}
func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) {
ra, err := kt.AccumulateTarget()
ra, err := kt.AccumulateTarget(&resource.Origin{})
if err != nil {
return nil, err
}
@ -151,20 +156,29 @@ func (kt *KustTarget) addHashesToNames(
// holding customized resources and the data/rules used
// to do so. The name back references and vars are
// not yet fixed.
func (kt *KustTarget) AccumulateTarget() (
// The origin parameter is used through the recursive calls
// to annotate each resource with information about where
// the resource came from, e.g. the file and/or the repository
// it originated from.
// As an entrypoint, one can pass an empty resource.Origin object to
// AccumulateTarget. As AccumulateTarget moves recursively
// through kustomization directories, it updates `origin.path`
// accordingly. When a remote base is found, it updates `origin.repo`
// and `origin.ref` accordingly.
func (kt *KustTarget) AccumulateTarget(origin *resource.Origin) (
ra *accumulator.ResAccumulator, err error) {
return kt.accumulateTarget(accumulator.MakeEmptyAccumulator())
return kt.accumulateTarget(accumulator.MakeEmptyAccumulator(), origin)
}
// ra should be empty when this KustTarget is a Kustomization, or the ra of the parent if this KustTarget is a Component
// (or empty if the Component does not have a parent).
func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator, origin *resource.Origin) (
resRa *accumulator.ResAccumulator, err error) {
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources)
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources, origin)
if err != nil {
return nil, errors.Wrap(err, "accumulating resources")
}
ra, err = kt.accumulateComponents(ra, kt.kustomization.Components)
ra, err = kt.accumulateComponents(ra, kt.kustomization.Components, origin)
if err != nil {
return nil, errors.Wrap(err, "accumulating components")
}
@ -205,9 +219,26 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
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
@ -247,7 +278,7 @@ func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error)
}
ra.AppendAll(rm)
}
ra, err := kt.accumulateResources(ra, generatorPaths)
ra, err := kt.accumulateResources(ra, generatorPaths, &resource.Origin{})
if err != nil {
return nil, err
}
@ -283,8 +314,7 @@ func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]re
}
ra.AppendAll(rm)
}
ra, err := kt.accumulateResources(ra, transformerPaths)
ra, err := kt.accumulateResources(ra, transformerPaths, &resource.Origin{})
if err != nil {
return nil, err
}
@ -332,16 +362,16 @@ func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) error {
// accumulateResources fills the given resourceAccumulator
// with resources read from the given list of paths.
func (kt *KustTarget) accumulateResources(
ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) {
ra *accumulator.ResAccumulator, paths []string, origin *resource.Origin) (*accumulator.ResAccumulator, error) {
for _, path := range paths {
// try loading resource as file then as base (directory or git repository)
if errF := kt.accumulateFile(ra, path); errF != nil {
if errF := kt.accumulateFile(ra, path, origin); errF != nil {
ldr, err := kt.ldr.New(path)
if err != nil {
return nil, errors.Wrapf(
err, "accumulation err='%s'", errF.Error())
}
ra, err = kt.accumulateDirectory(ra, ldr, false)
ra, err = kt.accumulateDirectory(ra, ldr, origin.Append(path), false)
if err != nil {
return nil, errors.Wrapf(
err, "accumulation err='%s'", errF.Error())
@ -354,7 +384,7 @@ func (kt *KustTarget) accumulateResources(
// accumulateResources fills the given resourceAccumulator
// with resources read from the given list of paths.
func (kt *KustTarget) accumulateComponents(
ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) {
ra *accumulator.ResAccumulator, paths []string, origin *resource.Origin) (*accumulator.ResAccumulator, error) {
for _, path := range paths {
// Components always refer to directories
ldr, errL := kt.ldr.New(path)
@ -362,7 +392,8 @@ func (kt *KustTarget) accumulateComponents(
return nil, fmt.Errorf("loader.New %q", errL)
}
var errD error
ra, errD = kt.accumulateDirectory(ra, ldr, true)
origin.Path = filepath.Join(origin.Path, path)
ra, errD = kt.accumulateDirectory(ra, ldr, origin, true)
if errD != nil {
return nil, fmt.Errorf("accumulateDirectory: %q", errD)
}
@ -371,7 +402,7 @@ func (kt *KustTarget) accumulateComponents(
}
func (kt *KustTarget) accumulateDirectory(
ra *accumulator.ResAccumulator, ldr ifc.Loader, isComponent bool) (*accumulator.ResAccumulator, error) {
ra *accumulator.ResAccumulator, ldr ifc.Loader, origin *resource.Origin, isComponent bool) (*accumulator.ResAccumulator, error) {
defer ldr.Cleanup()
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
err := subKt.Load()
@ -379,6 +410,7 @@ func (kt *KustTarget) accumulateDirectory(
return nil, errors.Wrapf(
err, "couldn't make target for path '%s'", ldr.Root())
}
subKt.kustomization.BuildMetadata = kt.kustomization.BuildMetadata
var bytes []byte
path := ldr.Root()
if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists {
@ -402,12 +434,12 @@ func (kt *KustTarget) accumulateDirectory(
var subRa *accumulator.ResAccumulator
if isComponent {
// Components don't create a new accumulator: the kustomization directives are added to the current accumulator
subRa, err = subKt.accumulateTarget(ra)
subRa, err = subKt.accumulateTarget(ra, origin)
ra = accumulator.MakeEmptyAccumulator()
} else {
// Child Kustomizations create a new accumulator which resolves their kustomization directives, which will later
// be merged into the current accumulator.
subRa, err = subKt.AccumulateTarget()
subRa, err = subKt.AccumulateTarget(origin)
}
if err != nil {
return nil, errors.Wrapf(
@ -422,11 +454,18 @@ func (kt *KustTarget) accumulateDirectory(
}
func (kt *KustTarget) accumulateFile(
ra *accumulator.ResAccumulator, path string) error {
ra *accumulator.ResAccumulator, path string, origin *resource.Origin) error {
resources, err := kt.rFactory.FromFile(kt.ldr, path)
if err != nil {
return errors.Wrapf(err, "accumulating resources from '%s'", path)
}
if utils.StringSliceContains(kt.kustomization.BuildMetadata, "originAnnotations") {
origin = origin.Append(path)
err = resources.AnnotateAll(utils.OriginAnnotation, origin.String())
if err != nil {
return errors.Wrapf(err, "cannot add path annotation for '%s'", path)
}
}
err = ra.AppendAll(resources)
if err != nil {
return errors.Wrapf(err, "merging resources from '%s'", path)

View File

@ -0,0 +1,23 @@
package utils
import "sigs.k8s.io/kustomize/api/konfig"
const (
BuildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds"
BuildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames"
BuildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
BuildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
BuildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces"
BuildAnnotationsRefBy = konfig.ConfigAnnoDomain + "/refBy"
BuildAnnotationsGenBehavior = konfig.ConfigAnnoDomain + "/generatorBehavior"
BuildAnnotationsGenAddHashSuffix = konfig.ConfigAnnoDomain + "/needsHashSuffix"
// the following are only for patches, to specify whether they can change names
// and kinds of their targets
BuildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange"
BuildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange"
OriginAnnotation = "config.kubernetes.io/origin"
Enabled = "enabled"
)

View File

@ -4,25 +4,10 @@ import (
"fmt"
"strings"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
const (
BuildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds"
BuildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames"
BuildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
BuildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
BuildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces"
// the following are only for patches, to specify whether they can change names
// and kinds of their targets
BuildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange"
BuildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange"
Allowed = "allowed"
)
// MakeResIds returns all of an RNode's current and previous Ids
func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) {
var result []resid.ResId

View File

@ -31,11 +31,12 @@ const (
// A program name, for use in help, finding the XDG_CONFIG_DIR, etc.
ProgramName = "kustomize"
// ConfigAnnoDomain is configuration-related annotation namespace.
ConfigAnnoDomain = "config.kubernetes.io"
// ConfigAnnoDomain is internal configuration-related annotation namespace.
// See https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md.
ConfigAnnoDomain = "internal.config.kubernetes.io"
// If a resource has this annotation, kustomize will drop it.
IgnoredByKustomizeAnnotation = ConfigAnnoDomain + "/local-config"
IgnoredByKustomizeAnnotation = "config.kubernetes.io/local-config"
// Label key that indicates the resources are built from Kustomize
ManagedbyLabelKey = "app.kubernetes.io/managed-by"

View File

@ -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

View File

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

View File

@ -136,6 +136,10 @@ type ResMap interface {
// self, then its behavior _cannot_ be merge or replace.
AbsorbAll(ResMap) error
// AnnotateAll annotates all resources in the ResMap with
// the provided key value pair.
AnnotateAll(key string, value string) error
// AsYaml returns the yaml form of resources.
AsYaml() ([]byte, error)
@ -210,6 +214,35 @@ type ResMap interface {
// namespaces. Cluster wide objects are never excluded.
SubsetThatCouldBeReferencedByResource(*resource.Resource) ResMap
// DeAnchor replaces YAML aliases with structured data copied from anchors.
// This cannot be undone; if desired, call DeepCopy first.
// Subsequent marshalling to YAML will no longer have anchor
// definitions ('&') or aliases ('*').
//
// Anchors are not expected to work across YAML 'documents'.
// If three resources are loaded from one file containing three YAML docs:
//
// {resourceA}
// ---
// {resourceB}
// ---
// {resourceC}
//
// then anchors defined in A cannot be seen from B and C and vice versa.
// OTOH, cross-resource links (a field in B referencing fields in A) will
// work if the resources are gathered in a ResourceList:
//
// apiVersion: config.kubernetes.io/v1
// kind: ResourceList
// metadata:
// name: someList
// items:
// - {resourceA}
// - {resourceB}
// - {resourceC}
//
DeAnchor() error
// DeepCopy copies the ResMap and underlying resources.
DeepCopy() ResMap

View File

@ -9,6 +9,7 @@ import (
"reflect"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/annotations"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
@ -531,6 +532,19 @@ func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
}
}
// AnnotateAll implements ResMap
func (m *resWrangler) AnnotateAll(key string, value string) error {
return m.ApplyFilter(annotations.Filter{
Annotations: map[string]string{
key: value,
},
FsSlice: []types.FieldSpec{{
Path: "metadata/annotations",
CreateIfNotPresent: true,
}},
})
}
// Select returns a list of resources that
// are selected by a Selector
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
@ -593,6 +607,16 @@ func (m *resWrangler) ToRNodeSlice() []*kyaml.RNode {
return result
}
// DeAnchor implements ResMap.
func (m *resWrangler) DeAnchor() (err error) {
for i := range m.rList {
if err = m.rList[i].DeAnchor(); err != nil {
return err
}
}
return nil
}
// ApplySmPatch applies the patch, and errors on Id collisions.
func (m *resWrangler) ApplySmPatch(
selectedSet *resource.IdSet, patch *resource.Resource) error {

View File

@ -64,18 +64,23 @@ func (rf *Factory) FromMapAndOption(
// TODO: return err instead of log.
log.Fatal(err)
}
return rf.makeOne(n, types.NewGenArgs(args))
return rf.makeOne(n, args)
}
// makeOne returns a new instance of Resource.
func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GenArgs) *Resource {
func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GeneratorArgs) *Resource {
if rn == nil {
log.Fatal("RNode must not be null")
}
if o == nil {
o = types.NewGenArgs(nil)
resource := &Resource{RNode: *rn}
if o != nil {
if o.Options == nil || !o.Options.DisableNameSuffixHash {
resource.EnableHashSuffix()
}
resource.SetBehavior(types.NewGenerationBehavior(o.Behavior))
}
return &Resource{RNode: *rn, options: o}
return resource
}
// SliceFromPatches returns a slice of resources given a patch path
@ -119,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.
@ -138,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
@ -147,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)
@ -159,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 {
@ -211,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) {
@ -265,7 +280,7 @@ func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs)
if err != nil {
return nil, err
}
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
return rf.makeOne(rn, &args.GeneratorArgs), nil
}
// MakeSecret makes an instance of Resource for Secret
@ -274,5 +289,5 @@ func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Reso
if err != nil {
return nil, err
}
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
return rf.makeOne(rn, &args.GeneratorArgs), nil
}

60
vendor/sigs.k8s.io/kustomize/api/resource/origin.go generated vendored Normal file
View File

@ -0,0 +1,60 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resource
import (
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/api/internal/git"
)
// Origin retains information about where resources in the output
// of `kustomize build` originated from
type Origin struct {
// Path is the path to the resource, rooted from the directory upon
// which `kustomize build` was invoked
Path string
// Repo is the remote repository that the resource originated from if it is
// not from a local file
Repo string
// Ref is the ref of the remote repository that the resource originated from
// if it is not from a local file
Ref string
}
// Copy returns a copy of origin
func (origin *Origin) Copy() Origin {
return *origin
}
// Append returns a copy of origin with a path appended to it
func (origin *Origin) Append(path string) *Origin {
originCopy := origin.Copy()
repoSpec, err := git.NewRepoSpecFromUrl(path)
if err == nil {
originCopy.Repo = repoSpec.Host + repoSpec.OrgRepo
absPath := repoSpec.AbsPath()
path = absPath[strings.Index(absPath[1:], "/")+1:][1:]
originCopy.Path = ""
originCopy.Ref = repoSpec.Ref
}
originCopy.Path = filepath.Join(originCopy.Path, path)
return &originCopy
}
// String returns a string version of origin
func (origin *Origin) String() string {
var anno string
anno = anno + "path: " + origin.Path + "\n"
if origin.Repo != "" {
anno = anno + "repo: " + origin.Repo + "\n"
}
if origin.Ref != "" {
anno = anno + "ref: " + origin.Ref + "\n"
}
return anno
}

View File

@ -13,6 +13,7 @@ import (
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/resid"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml"
@ -22,11 +23,10 @@ import (
// paired with metadata used by kustomize.
type Resource struct {
kyaml.RNode
options *types.GenArgs
refBy []resid.ResId
refVarNames []string
}
// nolint
var BuildAnnotations = []string{
utils.BuildAnnotationPreviousKinds,
utils.BuildAnnotationPreviousNames,
@ -35,6 +35,16 @@ var BuildAnnotations = []string{
utils.BuildAnnotationPreviousNamespaces,
utils.BuildAnnotationAllowNameChange,
utils.BuildAnnotationAllowKindChange,
utils.BuildAnnotationsRefBy,
utils.BuildAnnotationsGenBehavior,
utils.BuildAnnotationsGenAddHashSuffix,
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
kioutil.SeqIndentAnnotation,
kioutil.LegacyPathAnnotation,
kioutil.LegacyIndexAnnotation,
}
func (r *Resource) ResetRNode(incoming *Resource) {
@ -80,6 +90,8 @@ func (r *Resource) DeepCopy() *Resource {
// CopyMergeMetaDataFieldsFrom copies everything but the non-metadata in
// the resource.
// TODO: move to RNode, use GetMeta to improve performance.
// TODO: make a version of mergeStringMaps that is build-annotation aware
// to avoid repeatedly setting refby and genargs annotations
// Must remove the kustomize bit at the end.
func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
if err := r.SetLabels(
@ -87,7 +99,7 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
return fmt.Errorf("copyMerge cannot set labels - %w", err)
}
if err := r.SetAnnotations(
mergeStringMaps(other.GetAnnotations(), r.GetAnnotations())); err != nil {
mergeStringMapsWithBuildAnnotations(other.GetAnnotations(), r.GetAnnotations())); err != nil {
return fmt.Errorf("copyMerge cannot set annotations - %w", err)
}
if err := r.SetName(other.GetName()); err != nil {
@ -101,8 +113,6 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
}
func (r *Resource) copyKustomizeSpecificFields(other *Resource) {
r.options = other.options
r.refBy = other.copyRefBy()
r.refVarNames = copyStringSlice(other.refVarNames)
}
@ -144,10 +154,10 @@ func (r *Resource) ErrIfNotEquals(o *Resource) error {
func (r *Resource) ReferencesEqual(other *Resource) bool {
setSelf := make(map[resid.ResId]bool)
setOther := make(map[resid.ResId]bool)
for _, ref := range other.refBy {
for _, ref := range other.GetRefBy() {
setOther[ref] = true
}
for _, ref := range r.refBy {
for _, ref := range r.GetRefBy() {
if _, ok := setOther[ref]; !ok {
return false
}
@ -156,15 +166,6 @@ func (r *Resource) ReferencesEqual(other *Resource) bool {
return len(setSelf) == len(setOther)
}
func (r *Resource) copyRefBy() []resid.ResId {
if r.refBy == nil {
return nil
}
s := make([]resid.ResId, len(r.refBy))
copy(s, r.refBy)
return s
}
func copyStringSlice(s []string) []string {
if s == nil {
return nil
@ -250,41 +251,45 @@ func (r *Resource) setPreviousId(ns string, n string, k string) *Resource {
// AllowNameChange allows name changes to the resource.
func (r *Resource) AllowNameChange() {
annotations := r.GetAnnotations()
annotations[utils.BuildAnnotationAllowNameChange] = utils.Allowed
if err := r.SetAnnotations(annotations); err != nil {
panic(err)
}
r.enable(utils.BuildAnnotationAllowNameChange)
}
// NameChangeAllowed checks if a patch resource is allowed to change another resource's name.
func (r *Resource) NameChangeAllowed() bool {
annotations := r.GetAnnotations()
v, ok := annotations[utils.BuildAnnotationAllowNameChange]
return ok && v == utils.Allowed
return r.isEnabled(utils.BuildAnnotationAllowNameChange)
}
// AllowKindChange allows kind changes to the resource.
func (r *Resource) AllowKindChange() {
r.enable(utils.BuildAnnotationAllowKindChange)
}
// KindChangeAllowed checks if a patch resource is allowed to change another resource's kind.
func (r *Resource) KindChangeAllowed() bool {
return r.isEnabled(utils.BuildAnnotationAllowKindChange)
}
func (r *Resource) isEnabled(annoKey string) bool {
annotations := r.GetAnnotations()
annotations[utils.BuildAnnotationAllowKindChange] = utils.Allowed
v, ok := annotations[annoKey]
return ok && v == utils.Enabled
}
func (r *Resource) enable(annoKey string) {
annotations := r.GetAnnotations()
annotations[annoKey] = utils.Enabled
if err := r.SetAnnotations(annotations); err != nil {
panic(err)
}
}
func (r *Resource) KindChangeAllowed() bool {
annotations := r.GetAnnotations()
v, ok := annotations[utils.BuildAnnotationAllowKindChange]
return ok && v == utils.Allowed
}
// String returns resource as JSON.
func (r *Resource) String() string {
bs, err := r.MarshalJSON()
if err != nil {
return "<" + err.Error() + ">"
}
return strings.TrimSpace(string(bs)) + r.options.String()
return strings.TrimSpace(string(bs))
}
// AsYAML returns the resource in Yaml form.
@ -306,20 +311,34 @@ func (r *Resource) MustYaml() string {
return string(yml)
}
// SetOptions updates the generator options for the resource.
func (r *Resource) SetOptions(o *types.GenArgs) {
r.options = o
}
// Behavior returns the behavior for the resource.
func (r *Resource) Behavior() types.GenerationBehavior {
return r.options.Behavior()
annotations := r.GetAnnotations()
if v, ok := annotations[utils.BuildAnnotationsGenBehavior]; ok {
return types.NewGenerationBehavior(v)
}
return types.NewGenerationBehavior("")
}
// SetBehavior sets the behavior for the resource.
func (r *Resource) SetBehavior(behavior types.GenerationBehavior) {
annotations := r.GetAnnotations()
annotations[utils.BuildAnnotationsGenBehavior] = behavior.String()
if err := r.SetAnnotations(annotations); err != nil {
panic(err)
}
}
// NeedHashSuffix returns true if a resource content
// hash should be appended to the name of the resource.
func (r *Resource) NeedHashSuffix() bool {
return r.options != nil && r.options.ShouldAddHashSuffixToName()
return r.isEnabled(utils.BuildAnnotationsGenAddHashSuffix)
}
// EnableHashSuffix marks the resource as needing a content
// hash to be appended to the name of the resource.
func (r *Resource) EnableHashSuffix() {
r.enable(utils.BuildAnnotationsGenAddHashSuffix)
}
// OrgId returns the original, immutable ResId for the resource.
@ -363,12 +382,18 @@ func (r *Resource) CurId() resid.ResId {
// GetRefBy returns the ResIds that referred to current resource
func (r *Resource) GetRefBy() []resid.ResId {
return r.refBy
var resIds []resid.ResId
asStrings := r.getCsvAnnotation(utils.BuildAnnotationsRefBy)
for _, s := range asStrings {
resIds = append(resIds, resid.FromString(s))
}
return resIds
}
// AppendRefBy appends a ResId into the refBy list
func (r *Resource) AppendRefBy(id resid.ResId) {
r.refBy = append(r.refBy, id)
// Using any type except fmt.Stringer here results in a compilation error
func (r *Resource) AppendRefBy(id fmt.Stringer) {
r.appendCsvAnnotation(utils.BuildAnnotationsRefBy, id.String())
}
// GetRefVarNames returns vars that refer to current resource
@ -424,3 +449,17 @@ func mergeStringMaps(maps ...map[string]string) map[string]string {
}
return result
}
func mergeStringMapsWithBuildAnnotations(maps ...map[string]string) map[string]string {
result := mergeStringMaps(maps...)
for i := range BuildAnnotations {
if len(maps) > 0 {
if v, ok := maps[0][BuildAnnotations[i]]; ok {
result[BuildAnnotations[i]] = v
continue
}
}
delete(result, BuildAnnotations[i])
}
return result
}

View File

@ -1,46 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
import (
"strconv"
"strings"
)
// GenArgs is a facade over GeneratorArgs, exposing a few readonly properties.
type GenArgs struct {
args *GeneratorArgs
}
// NewGenArgs returns a new instance of GenArgs.
func NewGenArgs(args *GeneratorArgs) *GenArgs {
return &GenArgs{args: args}
}
func (g *GenArgs) String() string {
if g == nil {
return "{nilGenArgs}"
}
return "{" +
strings.Join([]string{
"nsfx:" + strconv.FormatBool(g.ShouldAddHashSuffixToName()),
"beh:" + g.Behavior().String()},
",") +
"}"
}
// ShouldAddHashSuffixToName returns true if a resource
// content hash should be appended to the name of the resource.
func (g *GenArgs) ShouldAddHashSuffixToName() bool {
return g.args != nil &&
(g.args.Options == nil || !g.args.Options.DisableNameSuffixHash)
}
// Behavior returns Behavior field of GeneratorArgs
func (g *GenArgs) Behavior() GenerationBehavior {
if g == nil || g.args == nil {
return BehaviorUnspecified
}
return NewGenerationBehavior(g.args.Behavior)
}

View File

@ -161,6 +161,9 @@ type Kustomization struct {
// Inventory appends an object that contains the record
// of all other objects, which can be used in apply, prune and delete
Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"`
// BuildMetadata is a list of strings used to toggle different build options
BuildMetadata []string `json:"buildMetadata,omitempty" yaml:"buildMetadata,omitempty"`
}
// FixKustomizationPostUnmarshalling fixes things

View File

@ -57,4 +57,6 @@ type FnPluginLoadingOptions struct {
Env []string
// Run as uid and gid of the command executor
AsCurrentUser bool
// Run in this working directory
WorkingDir string
}

View File

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

View File

@ -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
}

View File

@ -123,11 +123,11 @@ func (n *fsNode) addFile(name string, c []byte) (result *fsNode, err error) {
if result.offset != nil {
return nil, fmt.Errorf("cannot add already opened file '%s'", n.Path())
}
result.content = c
result.content = append(result.content[:0], c...)
return result, nil
}
result = &fsNode{
content: c,
content: append([]byte(nil), c...),
parent: parent,
}
parent.dir[fileName] = result
@ -612,6 +612,7 @@ func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
// This is how /bin/ls behaves.
func (n *fsNode) Glob(pattern string) ([]string, error) {
var result []string
var allFiles []string
err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
@ -622,7 +623,7 @@ func (n *fsNode) Glob(pattern string) ([]string, error) {
return err
}
if match {
result = append(result, path)
allFiles = append(allFiles, path)
}
}
return nil
@ -630,6 +631,11 @@ func (n *fsNode) Glob(pattern string) ([]string, error) {
if err != nil {
return nil, err
}
if IsHiddenFilePath(pattern) {
result = allFiles
} else {
result = RemoveHiddenFiles(allFiles)
}
sort.Strings(result)
return result, nil
}

View File

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

View File

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

View File

@ -5,6 +5,7 @@ package container
import (
"fmt"
"os"
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
@ -139,19 +140,27 @@ func (c Filter) GetExit() error {
}
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
c.setupExec()
if err := c.setupExec(); err != nil {
return nil, err
}
return c.Exec.Filter(nodes)
}
func (c *Filter) setupExec() {
func (c *Filter) setupExec() error {
// don't init 2x
if c.Exec.Path != "" {
return
return nil
}
wd, err := os.Getwd()
if err != nil {
return err
}
c.Exec.WorkingDir = wd
path, args := c.getCommand()
c.Exec.Path = path
c.Exec.Args = args
return nil
}
// getArgs returns the command + args to run to spawn the container

View File

@ -7,7 +7,9 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@ -19,6 +21,10 @@ type Filter struct {
// Args are the arguments to the executable
Args []string `yaml:"args,omitempty"`
// WorkingDir is the working directory that the executable
// should run in
WorkingDir string
runtimeutil.FunctionFilter
}
@ -32,5 +38,17 @@ func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
cmd.Stdin = reader
cmd.Stdout = writer
cmd.Stderr = os.Stderr
if c.WorkingDir == "" {
return errors.Errorf("no working directory set for exec function")
}
if !filepath.IsAbs(c.WorkingDir) {
return errors.Errorf(
"relative working directory %s not allowed", c.WorkingDir)
}
if c.WorkingDir == "/" {
return errors.Errorf(
"root working directory '/' not allowed")
}
cmd.Dir = c.WorkingDir
return cmd.Run()
}

View File

@ -15,6 +15,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/order"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@ -65,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))
@ -100,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))
@ -169,8 +180,8 @@ func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return nil, err
}
// copy the comments from the inputs to the outputs
if err := c.setComments(output); err != nil {
// copy the comments and sync the order of fields from the inputs to the outputs
if err := c.copyCommentsAndSyncOrder(output); err != nil {
return nil, err
}
@ -192,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
@ -201,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)
}
@ -210,15 +223,21 @@ func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
return nil
}
func (c *FunctionFilter) setComments(nodes []*yaml.RNode) error {
func (c *FunctionFilter) copyCommentsAndSyncOrder(nodes []*yaml.RNode) error {
for i := range nodes {
node := nodes[i]
anID, err := node.Pipe(yaml.GetAnnotation(idAnnotation))
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
@ -229,7 +248,13 @@ func (c *FunctionFilter) setComments(nodes []*yaml.RNode) error {
if err := comments.CopyComments(in, node); err != nil {
return errors.Wrap(err)
}
if err := node.PipeE(yaml.ClearAnnotation(idAnnotation)); err != nil {
if err := order.SyncOrder(in, node); err != nil {
return errors.Wrap(err)
}
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)
}
}

View File

@ -7,6 +7,7 @@ import (
"bytes"
"fmt"
"io"
"regexp"
"sort"
"strings"
@ -17,7 +18,7 @@ import (
const (
ResourceListKind = "ResourceList"
ResourceListAPIVersion = "config.kubernetes.io/v1alpha1"
ResourceListAPIVersion = "config.kubernetes.io/v1"
)
// ByteReadWriter reads from an input and writes to an output.
@ -36,9 +37,19 @@ type ByteReadWriter struct {
// the Resources, otherwise they will be cleared.
KeepReaderAnnotations bool
// PreserveSeqIndent if true adds kioutil.SeqIndentAnnotation to each resource
PreserveSeqIndent bool
// 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
@ -52,14 +63,16 @@ func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
b := &ByteReader{
Reader: rw.Reader,
OmitReaderAnnotations: rw.OmitReaderAnnotations,
PreserveSeqIndent: rw.PreserveSeqIndent,
WrapBareSeqNode: rw.WrapBareSeqNode,
}
val, err := b.Read()
rw.Results = b.Results
if rw.FunctionConfig == nil {
rw.FunctionConfig = b.FunctionConfig
}
rw.Results = b.Results
if !rw.NoWrap {
if !rw.NoWrap && rw.WrappingKind == "" {
rw.WrappingAPIVersion = b.WrappingAPIVersion
rw.WrappingKind = b.WrappingKind
}
@ -67,15 +80,18 @@ func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
}
func (rw *ByteReadWriter) Write(nodes []*yaml.RNode) error {
return ByteWriter{
w := ByteWriter{
Writer: rw.Writer,
KeepReaderAnnotations: rw.KeepReaderAnnotations,
Style: rw.Style,
FunctionConfig: rw.FunctionConfig,
Results: rw.Results,
WrappingAPIVersion: rw.WrappingAPIVersion,
WrappingKind: rw.WrappingKind,
}.Write(nodes)
}
if !rw.NoWrap {
w.WrappingAPIVersion = rw.WrappingAPIVersion
w.WrappingKind = rw.WrappingKind
}
return w.Write(nodes)
}
// ParseAll reads all of the inputs into resources
@ -89,6 +105,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()
}
@ -108,9 +125,12 @@ type ByteReader struct {
Reader io.Reader
// OmitReaderAnnotations will configures Read to skip setting the config.kubernetes.io/index
// annotation on Resources as they are Read.
// and internal.config.kubernetes.io/seqindent annotations on Resources as they are Read.
OmitReaderAnnotations bool
// PreserveSeqIndent if true adds kioutil.SeqIndentAnnotation to each resource
PreserveSeqIndent bool
// SetAnnotations is a map of caller specified annotations to set on resources as they are read
// These are independent of the annotations controlled by OmitReaderAnnotations
SetAnnotations map[string]string
@ -129,11 +149,57 @@ 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{}
// splitDocuments returns a slice of all documents contained in a YAML string. Multiple documents can be divided by the
// YAML document separator (---). It allows for white space and comments to be after the separator on the same line,
// but will return an error if anything else is on the line.
func splitDocuments(s string) ([]string, error) {
docs := make([]string, 0)
if len(s) > 0 {
// The YAML document separator is any line that starts with ---
yamlSeparatorRegexp := regexp.MustCompile(`\n---.*\n`)
// Find all separators, check them for invalid content, and append each document to docs
separatorLocations := yamlSeparatorRegexp.FindAllStringIndex(s, -1)
prev := 0
for i := range separatorLocations {
loc := separatorLocations[i]
separator := s[loc[0]:loc[1]]
// If the next non-whitespace character on the line following the separator is not a comment, return an error
trimmedContentAfterSeparator := strings.TrimSpace(separator[4:])
if len(trimmedContentAfterSeparator) > 0 && trimmedContentAfterSeparator[0] != '#' {
return nil, errors.Errorf("invalid document separator: %s", strings.TrimSpace(separator))
}
docs = append(docs, s[prev:loc[0]])
prev = loc[1]
}
docs = append(docs, s[prev:])
}
return docs, nil
}
func (r *ByteReader) Read() ([]*yaml.RNode, error) {
if r.PreserveSeqIndent && r.OmitReaderAnnotations {
return nil, errors.Errorf(`"PreserveSeqIndent" option adds a reader annotation, please set "OmitReaderAnnotations" to false`)
}
output := ResourceNodeSlice{}
// by manually splitting resources -- otherwise the decoder will get the Resource
@ -144,8 +210,12 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
return nil, errors.Wrap(err)
}
// replace the ending \r\n (line ending used in windows) with \n and then separate by \n---\n
values := strings.Split(strings.ReplaceAll(input.String(), "\r\n", "\n"), "\n---\n")
// Replace the ending \r\n (line ending used in windows) with \n and then split it into multiple YAML documents
// if it contains document separators (---)
values, err := splitDocuments(strings.ReplaceAll(input.String(), "\r\n", "\n"))
if err != nil {
return nil, errors.Wrap(err)
}
index := 0
for i := range values {
@ -155,10 +225,11 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
values[i] += "\n"
}
decoder := yaml.NewDecoder(bytes.NewBufferString(values[i]))
node, err := r.decode(index, decoder)
node, err := r.decode(values[i], index, decoder)
if err == io.EOF {
continue
}
if err != nil {
return nil, errors.Wrap(err)
}
@ -206,10 +277,17 @@ 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
}
func (r *ByteReader) decode(index int, decoder *yaml.Decoder) (*yaml.RNode, error) {
func (r *ByteReader) decode(originalYAML string, index int, decoder *yaml.Decoder) (*yaml.RNode, error) {
node := &yaml.Node{}
err := decoder.Decode(node)
if err == io.EOF {
@ -227,11 +305,34 @@ func (r *ByteReader) decode(index int, decoder *yaml.Decoder) (*yaml.RNode, erro
// 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 {
err := kioutil.CopyLegacyAnnotations(n)
if err != nil {
return nil, err
}
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
seqIndentStyle := yaml.DeriveSeqIndentStyle(originalYAML)
if seqIndentStyle != "" {
r.SetAnnotations[kioutil.SeqIndentAnnotation] = fmt.Sprintf("%s", seqIndentStyle)
}
}
}
var keys []string
for k := range r.SetAnnotations {
@ -244,5 +345,5 @@ func (r *ByteReader) decode(index int, decoder *yaml.Decoder) (*yaml.RNode, erro
return nil, errors.Wrap(err)
}
}
return yaml.NewRNode(node), nil
return n, nil
}

View File

@ -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"
@ -62,6 +63,12 @@ func (w ByteWriter) Write(inputNodes []*yaml.RNode) error {
// Even though we use the this value further down we must check this before removing annotations
jsonEncodeSingleBareNode := w.shouldJSONEncodeSingleBareNode(nodes)
// store seqindent annotation value for each node in order to set the encoder indentation
var seqIndentsForNodes []string
for i := range nodes {
seqIndentsForNodes = append(seqIndentsForNodes, nodes[i].GetAnnotations()[kioutil.SeqIndentAnnotation])
}
for i := range nodes {
// clean resources by removing annotations set by the Reader
if !w.KeepReaderAnnotations {
@ -69,6 +76,15 @@ 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 {
return errors.Wrap(err)
}
}
for _, a := range w.ClearAnnotations {
_, err := nodes[i].Pipe(yaml.ClearAnnotation(a))
@ -97,8 +113,13 @@ func (w ByteWriter) Write(inputNodes []*yaml.RNode) error {
// don't wrap the elements
if w.WrappingKind == "" {
for i := range nodes {
if err := encoder.Encode(nodes[i].Document()); err != nil {
return err
if seqIndentsForNodes[i] == string(yaml.WideSequenceStyle) {
encoder.DefaultSeqIndent()
} else {
encoder.CompactSeqIndent()
}
if err := encoder.Encode(upWrapBareSequenceNode(nodes[i].Document())); err != nil {
return errors.Wrap(err)
}
}
return nil
@ -165,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
}

View File

@ -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])
}
}

View File

@ -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 {

View File

@ -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,226 @@ 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
}
if err := checkMismatchedAnnos(meta); 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
}
func checkMismatchedAnnos(meta yaml.ResourceMeta) error {
path := meta.Annotations[kioutil.PathAnnotation]
index := meta.Annotations[kioutil.IndexAnnotation]
id := meta.Annotations[kioutil.IdAnnotation]
legacyPath := meta.Annotations[kioutil.LegacyPathAnnotation]
legacyIndex := meta.Annotations[kioutil.LegacyIndexAnnotation]
legacyId := meta.Annotations[kioutil.LegacyIdAnnotation]
// if prior to running the functions, the legacy and internal annotations differ,
// throw an error as we cannot infer the user's intent.
if path != legacyPath {
return fmt.Errorf("resource input to function has mismatched legacy and internal path annotations")
}
if index != legacyIndex {
return fmt.Errorf("resource input to function has mismatched legacy and internal index annotations")
}
if id != legacyId {
return fmt.Errorf("resource input to function has mismatched legacy and internal id annotations")
}
return nil
}
type nodeAnnotations struct {
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, throw an error
meta, err = node.GetMeta()
if err != nil {
return err
}
err = checkMismatchedAnnos(meta)
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
}

View File

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

View File

@ -9,6 +9,7 @@ import (
"path/filepath"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/sets"
"sigs.k8s.io/kustomize/kyaml/yaml"
@ -40,6 +41,9 @@ type LocalPackageReadWriter struct {
KeepReaderAnnotations bool `yaml:"keepReaderAnnotations,omitempty"`
// PreserveSeqIndent if true adds kioutil.SeqIndentAnnotation to each resource
PreserveSeqIndent bool
// PackagePath is the path to the package directory.
PackagePath string `yaml:"path,omitempty"`
@ -75,6 +79,16 @@ type LocalPackageReadWriter struct {
// FileSkipFunc is a function which returns true if reader should ignore
// the file
FileSkipFunc LocalPackageSkipFileFunc
// 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) {
@ -86,6 +100,9 @@ func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
SetAnnotations: r.SetAnnotations,
PackageFileName: r.PackageFileName,
FileSkipFunc: r.FileSkipFunc,
PreserveSeqIndent: r.PreserveSeqIndent,
FileSystem: r.FileSystem,
WrapBareSeqNode: r.WrapBareSeqNode,
}.Read()
if err != nil {
return nil, errors.Wrap(err)
@ -113,13 +130,14 @@ func (r *LocalPackageReadWriter) Write(nodes []*yaml.RNode) error {
PackagePath: r.PackagePath,
ClearAnnotations: clear,
KeepReaderAnnotations: r.KeepReaderAnnotations,
FileSystem: r.FileSystem,
}.Write(nodes)
if err != nil {
return errors.Wrap(err)
}
deleteFiles := r.files.Difference(newFiles)
for f := range deleteFiles {
if err = os.Remove(filepath.Join(r.PackagePath, f)); err != nil {
if err = r.FileSystem.RemoveAll(filepath.Join(r.PackagePath, f)); err != nil {
return errors.Wrap(err)
}
}
@ -177,6 +195,19 @@ type LocalPackageReader struct {
// FileSkipFunc is a function which returns true if reader should ignore
// the file
FileSkipFunc LocalPackageSkipFileFunc
// PreserveSeqIndent if true adds kioutil.SeqIndentAnnotation to each resource
PreserveSeqIndent bool
// 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{}
@ -200,12 +231,15 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
var operand ResourceNodeSlice
var pathRelativeTo string
var err error
ignoreFilesMatcher := &ignoreFilesMatcher{}
r.PackagePath, err = filepath.Abs(r.PackagePath)
ignoreFilesMatcher := &ignoreFilesMatcher{
fs: r.FileSystem,
}
dir, file, err := r.FileSystem.CleanedAbs(r.PackagePath)
if err != nil {
return nil, errors.Wrap(err)
}
err = filepath.Walk(r.PackagePath, func(
r.PackagePath = filepath.Join(string(dir), file)
err = r.FileSystem.Walk(r.PackagePath, func(
path string, info os.FileInfo, err error) error {
if err != nil {
return errors.Wrap(err)
@ -256,7 +290,7 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
// readFile reads the ResourceNodes from a file
func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode, error) {
f, err := os.Open(path)
f, err := r.FileSystem.Open(path)
if err != nil {
return nil, err
}
@ -267,6 +301,8 @@ func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode
Reader: f,
OmitReaderAnnotations: r.OmitReaderAnnotations,
SetAnnotations: r.SetAnnotations,
PreserveSeqIndent: r.PreserveSeqIndent,
WrapBareSeqNode: r.WrapBareSeqNode,
}
return rr.Read()
}
@ -300,6 +336,7 @@ func (r *LocalPackageReader) initReaderAnnotations(path string, _ os.FileInfo) {
}
if !r.OmitReaderAnnotations {
r.SetAnnotations[kioutil.PathAnnotation] = path
r.SetAnnotations[kioutil.LegacyPathAnnotation] = path
}
}
@ -313,11 +350,8 @@ func (r *LocalPackageReader) shouldSkipDir(path string, matcher *ignoreFilesMatc
return nil
}
// check if this is a subpackage
_, err := os.Stat(filepath.Join(path, r.PackageFileName))
if os.IsNotExist(err) {
if !r.FileSystem.Exists(filepath.Join(path, r.PackageFileName)) {
return nil
} else if err != nil {
return errors.Wrap(err)
}
if !r.IncludeSubpackages {
return filepath.SkipDir

View File

@ -4,12 +4,14 @@
package kio
import (
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@ -26,6 +28,9 @@ type LocalPackageWriter struct {
// ClearAnnotations will clear annotations before writing the resources
ClearAnnotations []string `yaml:"clearAnnotations,omitempty"`
// FileSystem can be used to mock the disk file system.
FileSystem filesys.FileSystemOrOnDisk
}
var _ Writer = LocalPackageWriter{}
@ -36,9 +41,10 @@ func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
return err
}
if s, err := os.Stat(r.PackagePath); err != nil {
return err
} else if !s.IsDir() {
if !r.FileSystem.Exists(r.PackagePath) {
return errors.WrapPrefixf(os.ErrNotExist, "could not write to %q", r.PackagePath)
}
if !r.FileSystem.IsDir(r.PackagePath) {
// if the user specified input isn't a directory, the package is the directory of the
// target
r.PackagePath = filepath.Dir(r.PackagePath)
@ -60,50 +66,42 @@ 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
for path := range outputFiles {
outputPath := filepath.Join(r.PackagePath, path)
if st, err := os.Stat(outputPath); !os.IsNotExist(err) {
if err != nil {
return errors.Wrap(err)
}
if st.IsDir() {
return fmt.Errorf("config.kubernetes.io/path cannot be a directory: %s", path)
}
if r.FileSystem.IsDir(outputPath) {
return fmt.Errorf("config.kubernetes.io/path cannot be a directory: %s", path)
}
err = os.MkdirAll(filepath.Dir(outputPath), 0700)
err = r.FileSystem.MkdirAll(filepath.Dir(outputPath))
if err != nil {
return errors.Wrap(err)
}
}
// write files
buf := bytes.NewBuffer(nil)
for path := range outputFiles {
outputPath := filepath.Join(r.PackagePath, path)
err = os.MkdirAll(filepath.Dir(filepath.Join(r.PackagePath, path)), 0700)
err = r.FileSystem.MkdirAll(filepath.Dir(filepath.Join(r.PackagePath, path)))
if err != nil {
return errors.Wrap(err)
}
f, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0600))
if err != nil {
buf.Reset()
w := ByteWriter{
Writer: buf,
KeepReaderAnnotations: r.KeepReaderAnnotations,
ClearAnnotations: r.ClearAnnotations,
}
if err = w.Write(outputFiles[path]); err != nil {
return errors.Wrap(err)
}
if err := func() error {
defer f.Close()
w := ByteWriter{
Writer: f,
KeepReaderAnnotations: r.KeepReaderAnnotations,
ClearAnnotations: r.ClearAnnotations,
}
if err = w.Write(outputFiles[path]); err != nil {
return errors.Wrap(err)
}
return nil
}(); err != nil {
if err := r.FileSystem.WriteFile(outputPath, buf.Bytes()); err != nil {
return errors.Wrap(err)
}
}

View File

@ -9,7 +9,7 @@ import (
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Setup creates directories and files for testing
@ -21,18 +21,12 @@ type Setup struct {
// setupDirectories creates directories for reading test configuration from
func SetupDirectories(t *testing.T, dirs ...string) Setup {
d, err := ioutil.TempDir("", "kyaml-test")
if !assert.NoError(t, err) {
assert.FailNow(t, err.Error())
}
require.NoError(t, err)
err = os.Chdir(d)
if !assert.NoError(t, err) {
assert.FailNow(t, err.Error())
}
require.NoError(t, err)
for _, s := range dirs {
err = os.MkdirAll(s, 0700)
if !assert.NoError(t, err) {
assert.FailNow(t, err.Error())
}
require.NoError(t, err)
}
return Setup{Root: d}
}
@ -40,13 +34,9 @@ func SetupDirectories(t *testing.T, dirs ...string) Setup {
// writeFile writes a file under the test directory
func (s Setup) WriteFile(t *testing.T, path string, value []byte) {
err := os.MkdirAll(filepath.Dir(filepath.Join(s.Root, path)), 0700)
if !assert.NoError(t, err) {
assert.FailNow(t, err.Error())
}
require.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(s.Root, path), value, 0600)
if !assert.NoError(t, err) {
assert.FailNow(t, err.Error())
}
require.NoError(t, err)
}
// clean deletes the test config

View File

@ -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

View File

@ -5,7 +5,8 @@ MYGOBIN = $(shell go env GOBIN)
ifeq ($(MYGOBIN),)
MYGOBIN = $(shell go env GOPATH)/bin
endif
API_VERSION := "v1.19.1"
API_VERSION := "v1.21.2"
KIND_VERSION := "v0.11.1"
.PHONY: all
all: \
@ -33,15 +34,12 @@ $(MYGOBIN)/kind:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
wget -O ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(shell uname)-amd64; \
wget -O ./kind https://github.com/kubernetes-sigs/kind/releases/download/$(KIND_VERSION)/kind-$(shell uname)-amd64; \
chmod +x ./kind; \
mv ./kind $(MYGOBIN); \
rm -rf $$d; \
)
$(MYGOBIN)/kpt:
../../hack/install_kpt.sh 0.34.0 $(MYGOBIN)
kustomizationapi/swagger.go: $(MYGOBIN)/go-bindata kustomizationapi/swagger.json
$(MYGOBIN)/go-bindata \
--pkg kustomizationapi \
@ -53,9 +51,12 @@ kubernetesapi/openapiinfo.go:
./scripts/makeOpenApiInfoDotGo.sh
.PHONY: kubernetesapi/swagger.json
kubernetesapi/swagger.json: $(MYGOBIN)/kind $(MYGOBIN)/kpt
kubernetesapi/swagger.json: $(MYGOBIN)/kind $(MYGOBIN)/kustomize
./scripts/fetchSchemaFromCluster.sh $(API_VERSION)
.PHONY: kubernetesapi/swagger.go
kubernetesapi/swagger.go: $(MYGOBIN)/go-bindata kubernetesapi/swagger.json
./scripts/generateSwaggerDotGo.sh $(API_VERSION)
$(MYGOBIN)/kustomize:
$(shell cd ../.. && MYGOBIN=$(MYGOBIN) make $(MYGOBIN)/kustomize)

View File

@ -1,57 +1,49 @@
# Sampling New OpenAPI Data
[OpenAPI schema]: ./kubernetesapi/
[Kustomization schema]: ./kustomizationapi/
[kind]: https://hub.docker.com/r/kindest/node/tags
This document describes how to fetch OpenAPI data from a
live kubernetes API server, e.g. an instance of [kind].
live kubernetes API server.
The scripts used will create a clean [kind] instance for this purpose.
## Replacing the default openapi schema version
### Delete all currently built-in schema
This will remove both the Kustomization and Kubernetes schemas:
```
make nuke
```
### Add a new built-in schema
### Choose the new version to use
The compiled-in schema version should maximize API availability with respect to all actively supported Kubernetes versions. For example, while 1.20, 1.21 and 1.22 are the actively supported versions, 1.21 is the best choice. This is because 1.21 introduces at least one new API and does not remove any, while 1.22 removes a large set of long-deprecated APIs that are still supported in 1.20/1.21.
### Update the built-in schema to a new version
In the Makefile in this directory, update the `API_VERSION` to your desired version.
You may need to update the version of Kind these scripts use by changing `KIND_VERSION` in the Makefile in this directory. You can find compatibility information in the [kind release notes](https://github.com/kubernetes-sigs/kind/releases).
In this directory, fetch the openapi schema and generate the
corresponding swagger.go for the kubernetes api:
```
make kubernetesapi/swagger.go
make all
```
To fetch the schema without generating the swagger.go, you can
run:
The above command will update the [OpenAPI schema] and the [Kustomization schema]. It will
create a directory kubernetesapi/v1212 and store the resulting
swagger.json and swagger.go files there.
```
make nuke
make kubernetesapi/swagger.json
```
#### Precomputations
Note that generating the swagger.go will re-fetch the schema.
You can specify a specific version with the "API_VERSION"
parameter. The default version is v1.19.1. Here is an
example for generating swagger.go for v1.14.1.
```
make kubernetesapi/swagger.go API_VERSION=v1.14.1
```
This will update the [OpenAPI schema]. The above command will
create a directory kubernetesapi/v1141 and store the resulting
swagger.json and swagger.go files there.
### Make the schema available for use
While the above commands generate the swagger.go files, they
do not make them available for use nor do they update the
info field reported by `kustomize openapi info`. To make the
newly fetched schema and swagger.go available:
```
make kubernetesapi/openapiinfo.go
```
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
@ -59,5 +51,44 @@ At the top of the repository, run the tests.
```
make prow-presubmit-check >& /tmp/k.txt; echo $?
# The exit code should be zero; if not examine /tmp/k.txt
```
The exit code should be zero; if not, examine `/tmp/k.txt`.
## Generating additional schemas
Instead of replacing the default version, you can specify a desired version as part of the make invocation:
```
rm kubernetesapi/swagger.go
make kubernetesapi/swagger.go API_VERSION=v1.21.2
```
While the above commands generate the swagger.go files, they
do not make them available for use nor do they update the
info field reported by `kustomize openapi info`. To make the
newly fetched schema and swagger.go available:
```
rm kubernetesapi/openapiinfo.go
make kubernetesapi/openapiinfo.go
```
## Partial regeneration
You can also regenerate the kubernetes api schemas specifically with:
```
rm kubernetesapi/swagger.go
make kubernetesapi/swagger.go
```
To fetch the schema without generating the swagger.go, you can
run:
```
rm kubernetesapi/swagger.json
make kubernetesapi/swagger.json
```
Note that generating the swagger.go will re-fetch the schema.

View File

@ -6,13 +6,13 @@
package kubernetesapi
import (
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/v1204"
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/v1212"
)
const Info = "{title:Kubernetes,version:v1.20.4}"
const Info = "{title:Kubernetes,version:v1.21.2}"
var OpenAPIMustAsset = map[string]func(string) []byte{
"v1204": v1204.MustAsset,
"v1212": v1212.MustAsset,
}
const DefaultOpenAPI = "v1204"
const DefaultOpenAPI = "v1212"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -16,6 +16,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi"
"sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
k8syaml "sigs.k8s.io/yaml"
)
// globalSchema contains global state information about the openapi
@ -49,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
@ -206,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
}
}
}
@ -264,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
@ -456,6 +555,8 @@ func SetSchema(openAPIField map[string]string, schema []byte, reset bool) error
if schema != nil { // use custom schema
customSchema = schema
kubernetesOpenAPIVersion = "custom"
// if the schema is changed, initSchema should parse the new schema
globalSchema.schemaInit = false
return nil
}
@ -468,6 +569,8 @@ func SetSchema(openAPIField map[string]string, schema []byte, reset bool) error
return fmt.Errorf("the specified OpenAPI version is not built in")
}
customSchema = nil
// if the schema is changed, initSchema should parse the new schema
globalSchema.schemaInit = false
return nil
}
@ -536,7 +639,14 @@ func parseBuiltinSchema(version string) {
// parse parses and indexes a single json schema
func parse(b []byte) error {
var swagger spec.Swagger
s := string(b)
if len(s) > 0 && s[0] != '{' {
var err error
b, err = k8syaml.YAMLToJSON(b)
if err != nil {
return errors.Wrap(err)
}
}
if err := swagger.UnmarshalJSON(b); err != nil {
return errors.Wrap(err)
}

124
vendor/sigs.k8s.io/kustomize/kyaml/order/syncorder.go generated vendored Normal file
View File

@ -0,0 +1,124 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package order
import (
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// SyncOrder recursively sorts the map node keys in 'to' node to match the order of
// map node keys in 'from' node at same tree depth, additional keys are moved to the end
// 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 {
// 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())
return nil
}
func syncOrder(from, to *yaml.RNode) error {
if from.IsNilOrEmpty() || to.IsNilOrEmpty() {
return nil
}
switch from.YNode().Kind {
case yaml.DocumentNode:
// Traverse the child of the documents
return syncOrder(yaml.NewRNode(from.YNode()), yaml.NewRNode(to.YNode()))
case yaml.MappingNode:
return VisitFields(from, to, func(fNode, tNode *yaml.MapNode) error {
// Traverse each field value
if fNode == nil || tNode == nil {
return nil
}
return syncOrder(fNode.Value, tNode.Value)
})
case yaml.SequenceNode:
return VisitElements(from, to, func(fNode, tNode *yaml.RNode) error {
// Traverse each list element
return syncOrder(fNode, tNode)
})
}
return nil
}
// VisitElements calls fn for each element in a SequenceNode.
// Returns an error for non-SequenceNodes
func VisitElements(from, to *yaml.RNode, fn func(fNode, tNode *yaml.RNode) error) error {
fElements, err := from.Elements()
if err != nil {
return errors.Wrap(err)
}
tElements, err := to.Elements()
if err != nil {
return errors.Wrap(err)
}
for i := range fElements {
if i >= len(tElements) {
return nil
}
if err := fn(fElements[i], tElements[i]); err != nil {
return errors.Wrap(err)
}
}
return nil
}
// VisitFields calls fn for each field in the RNode.
// Returns an error for non-MappingNodes.
func VisitFields(from, to *yaml.RNode, fn func(fNode, tNode *yaml.MapNode) error) error {
srcFieldNames, err := from.Fields()
if err != nil {
return nil
}
yaml.SyncMapNodesOrder(from, to)
// visit each field
for _, fieldName := range srcFieldNames {
if err := fn(from.Field(fieldName), to.Field(fieldName)); err != nil {
return errors.Wrap(err)
}
}
return nil
}
// rearrangeHeadCommentOfSeqNode addresses a remote corner case due to moving a
// map node in a sequence node with a head comment to the top
func rearrangeHeadCommentOfSeqNode(node *yaml.Node) {
if node == nil {
return
}
switch node.Kind {
case yaml.DocumentNode:
for _, node := range node.Content {
rearrangeHeadCommentOfSeqNode(node)
}
case yaml.MappingNode:
for _, node := range node.Content {
rearrangeHeadCommentOfSeqNode(node)
}
case yaml.SequenceNode:
for _, node := range node.Content {
// for each child mapping node, transfer the head comment of it's
// first child scalar node to the head comment of itself
if len(node.Content) > 0 && node.Content[0].Kind == yaml.ScalarNode {
if node.HeadComment == "" {
node.HeadComment = node.Content[0].HeadComment
continue
}
if node.Content[0].HeadComment != "" {
node.HeadComment += "\n" + node.Content[0].HeadComment
node.Content[0].HeadComment = ""
}
}
}
}
}

View File

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

View File

@ -101,6 +101,9 @@ type RunFns struct {
// If it is true, the empty result will be provided as input to the next
// function in the list.
ContinueOnEmptyResult bool
// WorkingDir specifies which working directory an exec function should run in.
WorkingDir string
}
// Execute runs the command
@ -340,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])
@ -484,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(
@ -507,7 +521,10 @@ func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode, currentUser
}
if r.EnableExec && spec.Exec.Path != "" {
ef := &exec.Filter{Path: spec.Exec.Path}
ef := &exec.Filter{
Path: spec.Exec.Path,
WorkingDir: r.WorkingDir,
}
ef.FunctionConfig = api
ef.GlobalScope = r.GlobalScope

25
vendor/sigs.k8s.io/kustomize/kyaml/sliceutil/slice.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package sliceutil
// Contains return true if string e is present in slice s
func Contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
// Remove removes the first occurrence of r in slice s
// and returns remaining slice
func Remove(s []string, r string) []string {
for i, v := range s {
if v == r {
return append(s[:i], s[i+1:]...)
}
}
return s
}

View File

@ -10,14 +10,25 @@ import (
"sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml"
)
const CompactSequenceStyle = "compact"
const WideSequenceStyle = "wide"
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"
)
const DefaultIndent = 2
const DefaultSequenceStyle = CompactSequenceStyle
// SeqIndentType holds the indentation style for sequence nodes
type SequenceIndentStyle string
var sequenceIndentationStyle = DefaultSequenceStyle
var indent = DefaultIndent
// EncoderOptions are options that can be used to configure the encoder,
// do not expose new options without considerable justification
type EncoderOptions struct {
// SeqIndent is the indentation style for YAML Sequence nodes
SeqIndent SequenceIndentStyle
}
// Expose the yaml.v3 functions so this package can be used as a replacement
@ -43,13 +54,33 @@ var Unmarshal = yaml.Unmarshal
var NewDecoder = yaml.NewDecoder
var NewEncoder = func(w io.Writer) *yaml.Encoder {
e := yaml.NewEncoder(w)
e.SetIndent(indent)
if sequenceIndentationStyle == CompactSequenceStyle {
e.CompactSeqIndent()
}
e.SetIndent(DefaultIndent)
e.CompactSeqIndent()
return e
}
// MarshalWithOptions marshals the input interface with provided options
func MarshalWithOptions(in interface{}, opts *EncoderOptions) ([]byte, error) {
var buf bytes.Buffer
err := NewEncoderWithOptions(&buf, opts).Encode(in)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// NewEncoderWithOptions returns the encoder with provided options
func NewEncoderWithOptions(w io.Writer, opts *EncoderOptions) *yaml.Encoder {
encoder := NewEncoder(w)
encoder.SetIndent(DefaultIndent)
if opts.SeqIndent == WideSequenceStyle {
encoder.DefaultSeqIndent()
} else {
encoder.CompactSeqIndent()
}
return encoder
}
var AliasNode yaml.Kind = yaml.AliasNode
var DocumentNode yaml.Kind = yaml.DocumentNode
var MappingNode yaml.Kind = yaml.MappingNode

View File

@ -424,12 +424,46 @@ func Lookup(path ...string) PathGetter {
return PathGetter{Path: path}
}
// Lookup returns a PathGetter to lookup a field by its path and create it if it doesn't already
// LookupCreate returns a PathGetter to lookup a field by its path and create it if it doesn't already
// exist.
func LookupCreate(kind yaml.Kind, path ...string) PathGetter {
return PathGetter{Path: path, Create: kind}
}
// ConventionalContainerPaths is a list of paths at which containers typically appear in workload APIs.
// It is intended for use with LookupFirstMatch.
var ConventionalContainerPaths = [][]string{
// e.g. Deployment, ReplicaSet, DaemonSet, Job, StatefulSet
{"spec", "template", "spec", "containers"},
// e.g. CronJob
{"spec", "jobTemplate", "spec", "template", "spec", "containers"},
// e.g. Pod
{"spec", "containers"},
// e.g. PodTemplate
{"template", "spec", "containers"},
}
// LookupFirstMatch returns a Filter for locating a value that may exist at one of several possible paths.
// For example, it can be used with ConventionalContainerPaths to find the containers field in a standard workload resource.
// If more than one of the paths exists in the resource, the first will be returned. If none exist,
// nil will be returned. If an error is encountered during lookup, it will be returned.
func LookupFirstMatch(paths [][]string) Filter {
return FilterFunc(func(object *RNode) (*RNode, error) {
var result *RNode
var err error
for _, path := range paths {
result, err = object.Pipe(PathGetter{Path: path})
if err != nil {
return nil, errors.Wrap(err)
}
if result != nil {
return result, nil
}
}
return nil, nil
})
}
// PathGetter returns the RNode under Path.
type PathGetter struct {
Kind string `yaml:"kind,omitempty"`

View File

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

View File

@ -14,6 +14,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml"
"sigs.k8s.io/kustomize/kyaml/sliceutil"
"sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/labels"
)
@ -146,6 +147,50 @@ func NewMapRNode(values *map[string]string) *RNode {
return m
}
// SyncMapNodesOrder sorts the map node keys in 'to' node to match the order of
// map node keys in 'from' node, additional keys are moved to the end
func SyncMapNodesOrder(from, to *RNode) {
to.Copy()
res := &RNode{value: &yaml.Node{
Kind: to.YNode().Kind,
Style: to.YNode().Style,
Tag: to.YNode().Tag,
Anchor: to.YNode().Anchor,
Alias: to.YNode().Alias,
HeadComment: to.YNode().HeadComment,
LineComment: to.YNode().LineComment,
FootComment: to.YNode().FootComment,
Line: to.YNode().Line,
Column: to.YNode().Column,
}}
fromFieldNames, err := from.Fields()
if err != nil {
return
}
toFieldNames, err := to.Fields()
if err != nil {
return
}
for _, fieldName := range fromFieldNames {
if !sliceutil.Contains(toFieldNames, fieldName) {
continue
}
// append the common nodes in the order defined in 'from' node
res.value.Content = append(res.value.Content, to.Field(fieldName).Key.YNode(), to.Field(fieldName).Value.YNode())
toFieldNames = sliceutil.Remove(toFieldNames, fieldName)
}
for _, fieldName := range toFieldNames {
// append the residual nodes which are not present in 'from' node
res.value.Content = append(res.value.Content, to.Field(fieldName).Key.YNode(), to.Field(fieldName).Value.YNode())
}
to.SetYNode(res.YNode())
}
// NewRNode returns a new RNode pointer containing the provided Node.
func NewRNode(value *yaml.Node) *RNode {
return &RNode{value: value}
@ -858,6 +903,48 @@ func (rn *RNode) UnmarshalJSON(b []byte) error {
return nil
}
// DeAnchor inflates all YAML aliases with their anchor values.
// All YAML anchor data is permanently removed (feel free to call Copy first).
func (rn *RNode) DeAnchor() (err error) {
rn.value, err = deAnchor(rn.value)
return
}
// deAnchor removes all AliasNodes from the yaml.Node's tree, replacing
// them with what they point to. All Anchor fields (these are used to mark
// anchor definitions) are cleared.
func deAnchor(yn *yaml.Node) (res *yaml.Node, err error) {
if yn == nil {
return nil, nil
}
if yn.Anchor != "" {
// This node defines an anchor. Clear the field so that it
// doesn't show up when marshalling.
if yn.Kind == yaml.AliasNode {
// Maybe this is OK, but for now treating it as a bug.
return nil, fmt.Errorf(
"anchor %q defined using alias %v", yn.Anchor, yn.Alias)
}
yn.Anchor = ""
}
switch yn.Kind {
case yaml.ScalarNode:
return yn, nil
case yaml.AliasNode:
return deAnchor(yn.Alias)
case yaml.DocumentNode, yaml.MappingNode, yaml.SequenceNode:
for i := range yn.Content {
yn.Content[i], err = deAnchor(yn.Content[i])
if err != nil {
return nil, err
}
}
return yn, nil
default:
return nil, fmt.Errorf("cannot deAnchor kind %q", yn.Kind)
}
}
// GetValidatedMetadata returns metadata after subjecting it to some tests.
func (rn *RNode) GetValidatedMetadata() (ResourceMeta, error) {
m, err := rn.GetMeta()

View File

@ -139,16 +139,16 @@ type NameMeta struct {
type ResourceMeta struct {
TypeMeta `json:",inline" yaml:",inline"`
// ObjectMeta is the metadata field of a Resource
ObjectMeta `yaml:"metadata,omitempty"`
ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
}
// ObjectMeta contains metadata about a Resource
type ObjectMeta struct {
NameMeta `json:",inline" yaml:",inline"`
// Labels is the metadata.labels field of a Resource
Labels map[string]string `yaml:"labels,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// Annotations is the metadata.annotations field of a Resource.
Annotations map[string]string `yaml:"annotations,omitempty"`
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
}
// GetIdentifier returns a ResourceIdentifier that includes

71
vendor/sigs.k8s.io/kustomize/kyaml/yaml/util.go generated vendored Normal file
View File

@ -0,0 +1,71 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml
import (
"strings"
)
// DeriveSeqIndentStyle derives the sequence indentation annotation value for the resource,
// originalYAML is the input yaml string,
// the style is decided by deriving the existing sequence indentation of first sequence node
func DeriveSeqIndentStyle(originalYAML string) string {
lines := strings.Split(originalYAML, "\n")
for i, line := range lines {
elems := strings.SplitN(line, "- ", 2)
if len(elems) != 2 {
continue
}
// prefix of "- " must be sequence of spaces
if strings.Trim(elems[0], " ") != "" {
continue
}
numSpacesBeforeSeqElem := len(elems[0])
// keyLine is the line before the first sequence element
keyLine := keyLineBeforeSeqElem(lines, i)
if keyLine == "" {
// there is no keyLine for this sequence node
// all of those lines are comments
continue
}
numSpacesBeforeKeyElem := len(keyLine) - len(strings.TrimLeft(keyLine, " "))
trimmedKeyLine := strings.Trim(keyLine, " ")
if strings.Count(trimmedKeyLine, ":") != 1 || !strings.HasSuffix(trimmedKeyLine, ":") {
// if the key line doesn't contain only one : that too at the end,
// this is not a sequence node, it is a wrapped sequence node string
// ignore it
continue
}
if numSpacesBeforeSeqElem == numSpacesBeforeKeyElem {
return string(CompactSequenceStyle)
}
if numSpacesBeforeSeqElem-numSpacesBeforeKeyElem == 2 {
return string(WideSequenceStyle)
}
}
return string(CompactSequenceStyle)
}
// keyLineBeforeSeqElem iterates through the lines before the first seqElement
// and tries to find the non-comment key line for the sequence node
func keyLineBeforeSeqElem(lines []string, seqElemIndex int) string {
// start with the previous line of sequence element
i := seqElemIndex - 1
for ; i >= 0; i-- {
line := lines[i]
trimmedLine := strings.Trim(line, " ")
if strings.HasPrefix(trimmedLine, "#") { // commented line
continue
}
// we have a non-commented line which can have a trailing comment
parts := strings.SplitN(line, "#", 2)
return parts[0] // throw away the trailing comment part
}
return ""
}