mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-13 10:00:56 +00:00
add origin annotation for resources generated by generators (#4341)
* add origin annotation for resources generated by builtin and custom generators * decouple origin data from generator data and account for inline generators
This commit is contained in:
@@ -53,14 +53,18 @@ func (l *Loader) SetWorkDir(wd string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loader) LoadGenerators(
|
func (l *Loader) LoadGenerators(
|
||||||
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
|
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) (
|
||||||
var result []resmap.Generator
|
result []*resmap.GeneratorWithProperties, err error) {
|
||||||
for _, res := range rm.Resources() {
|
for _, res := range rm.Resources() {
|
||||||
g, err := l.LoadGenerator(ldr, v, res)
|
g, err := l.LoadGenerator(ldr, v, res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
result = append(result, g)
|
generatorOrigin, err := resource.OriginFromCustomPlugin(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, &resmap.GeneratorWithProperties{Generator: g, Origin: generatorOrigin})
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
// KustTarget encapsulates the entirety of a kustomization build.
|
// KustTarget encapsulates the entirety of a kustomization build.
|
||||||
type KustTarget struct {
|
type KustTarget struct {
|
||||||
kustomization *types.Kustomization
|
kustomization *types.Kustomization
|
||||||
|
kustFileName string
|
||||||
ldr ifc.Loader
|
ldr ifc.Loader
|
||||||
validator ifc.Validator
|
validator ifc.Validator
|
||||||
rFactory *resmap.Factory
|
rFactory *resmap.Factory
|
||||||
@@ -53,7 +54,7 @@ func NewKustTarget(
|
|||||||
|
|
||||||
// Load attempts to load the target's kustomization file.
|
// Load attempts to load the target's kustomization file.
|
||||||
func (kt *KustTarget) Load() error {
|
func (kt *KustTarget) Load() error {
|
||||||
content, err := loadKustFile(kt.ldr)
|
content, kustFileName, err := loadKustFile(kt.ldr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -74,6 +75,7 @@ func (kt *KustTarget) Load() error {
|
|||||||
strings.Join(errs, "\n"), kt.ldr.Root())
|
strings.Join(errs, "\n"), kt.ldr.Root())
|
||||||
}
|
}
|
||||||
kt.kustomization = &k
|
kt.kustomization = &k
|
||||||
|
kt.kustFileName = kustFileName
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,23 +87,25 @@ func (kt *KustTarget) Kustomization() types.Kustomization {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadKustFile(ldr ifc.Loader) ([]byte, error) {
|
func loadKustFile(ldr ifc.Loader) ([]byte, string, error) {
|
||||||
var content []byte
|
var content []byte
|
||||||
match := 0
|
match := 0
|
||||||
|
var kustFileName string
|
||||||
for _, kf := range konfig.RecognizedKustomizationFileNames() {
|
for _, kf := range konfig.RecognizedKustomizationFileNames() {
|
||||||
c, err := ldr.Load(kf)
|
c, err := ldr.Load(kf)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
match += 1
|
match += 1
|
||||||
content = c
|
content = c
|
||||||
|
kustFileName = kf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch match {
|
switch match {
|
||||||
case 0:
|
case 0:
|
||||||
return nil, NewErrMissingKustomization(ldr.Root())
|
return nil, "", NewErrMissingKustomization(ldr.Root())
|
||||||
case 1:
|
case 1:
|
||||||
return content, nil
|
return content, kustFileName, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf(
|
return nil, "", fmt.Errorf(
|
||||||
"Found multiple kustomization files under: %s\n", ldr.Root())
|
"Found multiple kustomization files under: %s\n", ldr.Root())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,7 +210,7 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator, origin *r
|
|||||||
return nil, errors.Wrapf(
|
return nil, errors.Wrapf(
|
||||||
err, "merging CRDs %v", crdTc)
|
err, "merging CRDs %v", crdTc)
|
||||||
}
|
}
|
||||||
err = kt.runGenerators(ra)
|
err = kt.runGenerators(ra, origin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -244,23 +248,31 @@ func (kt *KustTarget) IgnoreLocal(ra *accumulator.ResAccumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) runGenerators(
|
func (kt *KustTarget) runGenerators(
|
||||||
ra *accumulator.ResAccumulator) error {
|
ra *accumulator.ResAccumulator, origin *resource.Origin) error {
|
||||||
var generators []resmap.Generator
|
var generators []*resmap.GeneratorWithProperties
|
||||||
gs, err := kt.configureBuiltinGenerators()
|
gs, err := kt.configureBuiltinGenerators(origin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
generators = append(generators, gs...)
|
generators = append(generators, gs...)
|
||||||
gs, err = kt.configureExternalGenerators()
|
|
||||||
|
gs, err = kt.configureExternalGenerators(origin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "loading generator plugins")
|
return errors.Wrap(err, "loading generator plugins")
|
||||||
}
|
}
|
||||||
generators = append(generators, gs...)
|
generators = append(generators, gs...)
|
||||||
for _, g := range generators {
|
|
||||||
|
for i, g := range generators {
|
||||||
resMap, err := g.Generate()
|
resMap, err := g.Generate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if resMap != nil {
|
||||||
|
err = resMap.AddOriginAnnotation(generators[i].Origin)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "adding origin annotations for generator %v", g)
|
||||||
|
}
|
||||||
|
}
|
||||||
err = ra.AbsorbAll(resMap)
|
err = ra.AbsorbAll(resMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "merging from generator %v", g)
|
return errors.Wrapf(err, "merging from generator %v", g)
|
||||||
@@ -269,7 +281,8 @@ func (kt *KustTarget) runGenerators(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error) {
|
func (kt *KustTarget) configureExternalGenerators(origin *resource.Origin) (
|
||||||
|
[]*resmap.GeneratorWithProperties, error) {
|
||||||
ra := accumulator.MakeEmptyAccumulator()
|
ra := accumulator.MakeEmptyAccumulator()
|
||||||
var generatorPaths []string
|
var generatorPaths []string
|
||||||
for _, p := range kt.kustomization.Generators {
|
for _, p := range kt.kustomization.Generators {
|
||||||
@@ -279,10 +292,19 @@ func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error)
|
|||||||
// not an inline config
|
// not an inline config
|
||||||
generatorPaths = append(generatorPaths, p)
|
generatorPaths = append(generatorPaths, p)
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
// inline config, track the origin
|
||||||
|
if origin != nil {
|
||||||
|
resources := rm.Resources()
|
||||||
|
for _, r := range resources {
|
||||||
|
r.SetOrigin(origin.Append(kt.kustFileName))
|
||||||
|
rm.Replace(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ra.AppendAll(rm)
|
ra.AppendAll(rm)
|
||||||
}
|
}
|
||||||
ra, err := kt.accumulateResources(ra, generatorPaths, &resource.Origin{})
|
ra, err := kt.accumulateResources(ra, generatorPaths, origin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ package target
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Functions dedicated to configuring the builtin
|
// Functions dedicated to configuring the builtin
|
||||||
@@ -27,8 +30,8 @@ import (
|
|||||||
// image tag transforms. In these cases, we'll need
|
// image tag transforms. In these cases, we'll need
|
||||||
// N plugin instances with differing configurations.
|
// N plugin instances with differing configurations.
|
||||||
|
|
||||||
func (kt *KustTarget) configureBuiltinGenerators() (
|
func (kt *KustTarget) configureBuiltinGenerators(origin *resource.Origin) (
|
||||||
result []resmap.Generator, err error) {
|
result []*resmap.GeneratorWithProperties, err error) {
|
||||||
for _, bpt := range []builtinhelpers.BuiltinPluginType{
|
for _, bpt := range []builtinhelpers.BuiltinPluginType{
|
||||||
builtinhelpers.ConfigMapGenerator,
|
builtinhelpers.ConfigMapGenerator,
|
||||||
builtinhelpers.SecretGenerator,
|
builtinhelpers.SecretGenerator,
|
||||||
@@ -39,7 +42,25 @@ func (kt *KustTarget) configureBuiltinGenerators() (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
result = append(result, r...)
|
|
||||||
|
var generatorOrigin *resource.Origin
|
||||||
|
if origin != nil {
|
||||||
|
generatorOrigin = &resource.Origin{
|
||||||
|
Repo: origin.Repo,
|
||||||
|
Ref: origin.Ref,
|
||||||
|
ConfiguredIn: filepath.Join(origin.Path, kt.kustFileName),
|
||||||
|
ConfiguredBy: yaml.ResourceIdentifier{
|
||||||
|
TypeMeta: yaml.TypeMeta{
|
||||||
|
APIVersion: "builtin",
|
||||||
|
Kind: bpt.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range r {
|
||||||
|
result = append(result, &resmap.GeneratorWithProperties{Generator: r[i], Origin: generatorOrigin})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,16 @@
|
|||||||
package krusty_test
|
package krusty_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||||
|
"sigs.k8s.io/kustomize/api/krusty"
|
||||||
_ "sigs.k8s.io/kustomize/api/krusty"
|
_ "sigs.k8s.io/kustomize/api/krusty"
|
||||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAnnoOriginLocalFiles(t *testing.T) {
|
func TestAnnoOriginLocalFiles(t *testing.T) {
|
||||||
@@ -263,3 +269,546 @@ metadata:
|
|||||||
type: Opaque
|
type: Opaque
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginLocalBuiltinGenerator(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK(".", `
|
||||||
|
resources:
|
||||||
|
- service.yaml
|
||||||
|
configMapGenerator:
|
||||||
|
- name: bob
|
||||||
|
literals:
|
||||||
|
- fruit=Indian Gooseberry
|
||||||
|
- year=2020
|
||||||
|
- crisis=true
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
|
||||||
|
`)
|
||||||
|
th.WriteF("service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: demo
|
||||||
|
spec:
|
||||||
|
clusterIP: None
|
||||||
|
`)
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(
|
||||||
|
m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: service.yaml
|
||||||
|
name: demo
|
||||||
|
spec:
|
||||||
|
clusterIP: None
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
crisis: "true"
|
||||||
|
fruit: Indian Gooseberry
|
||||||
|
year: "2020"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
configuredIn: kustomization.yaml
|
||||||
|
configuredBy:
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: ConfigMapGenerator
|
||||||
|
name: bob-79t79mt227
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginConfigMapGeneratorMerge(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK("base", `
|
||||||
|
configMapGenerator:
|
||||||
|
- name: bob
|
||||||
|
literals:
|
||||||
|
- fruit=Indian Gooseberry
|
||||||
|
- year=2020
|
||||||
|
- crisis=true
|
||||||
|
`)
|
||||||
|
th.WriteK("overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
configMapGenerator:
|
||||||
|
- name: bob
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- month=12
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)
|
||||||
|
m := th.Run("overlay", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `apiVersion: v1
|
||||||
|
data:
|
||||||
|
crisis: "true"
|
||||||
|
fruit: Indian Gooseberry
|
||||||
|
month: "12"
|
||||||
|
year: "2020"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
configuredIn: ../base/kustomization.yaml
|
||||||
|
configuredBy:
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: ConfigMapGenerator
|
||||||
|
name: bob-bk46gm59c6
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginConfigMapGeneratorReplace(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK("base", `
|
||||||
|
configMapGenerator:
|
||||||
|
- name: bob
|
||||||
|
literals:
|
||||||
|
- fruit=Indian Gooseberry
|
||||||
|
- year=2020
|
||||||
|
- crisis=true
|
||||||
|
`)
|
||||||
|
th.WriteK("overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
configMapGenerator:
|
||||||
|
- name: bob
|
||||||
|
behavior: replace
|
||||||
|
literals:
|
||||||
|
- month=12
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)
|
||||||
|
m := th.Run("overlay", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `apiVersion: v1
|
||||||
|
data:
|
||||||
|
month: "12"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
configuredIn: kustomization.yaml
|
||||||
|
configuredBy:
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: ConfigMapGenerator
|
||||||
|
name: bob-f8t5fhtbhc
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginCustomExecGenerator(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
|
||||||
|
th := kusttest_test.MakeHarnessWithFs(t, fSys)
|
||||||
|
o := th.MakeOptionsPluginsEnabled()
|
||||||
|
o.PluginConfig.FnpLoadingOptions.EnableExec = true
|
||||||
|
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
th.WriteK(tmpDir.String(), `
|
||||||
|
resources:
|
||||||
|
- short_secret.yaml
|
||||||
|
generators:
|
||||||
|
- gener.yaml
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Create some additional resource just to make sure everything is added
|
||||||
|
th.WriteF(filepath.Join(tmpDir.String(), "short_secret.yaml"),
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
`)
|
||||||
|
th.WriteF(filepath.Join(tmpDir.String(), "generateDeployment.sh"), generateDeploymentDotSh)
|
||||||
|
|
||||||
|
assert.NoError(t, os.Chmod(filepath.Join(tmpDir.String(), "generateDeployment.sh"), 0777))
|
||||||
|
th.WriteF(filepath.Join(tmpDir.String(), "gener.yaml"), `
|
||||||
|
kind: executable
|
||||||
|
metadata:
|
||||||
|
name: demo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: ./generateDeployment.sh
|
||||||
|
spec:
|
||||||
|
`)
|
||||||
|
|
||||||
|
m := th.Run(tmpDir.String(), o)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: short_secret.yaml
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
configuredIn: gener.yaml
|
||||||
|
configuredBy:
|
||||||
|
kind: executable
|
||||||
|
name: demo
|
||||||
|
tshirt-size: small
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginCustomInlineExecGenerator(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
|
||||||
|
th := kusttest_test.MakeHarnessWithFs(t, fSys)
|
||||||
|
o := th.MakeOptionsPluginsEnabled()
|
||||||
|
o.PluginConfig.FnpLoadingOptions.EnableExec = true
|
||||||
|
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
th.WriteK(tmpDir.String(), `
|
||||||
|
resources:
|
||||||
|
- short_secret.yaml
|
||||||
|
generators:
|
||||||
|
- |-
|
||||||
|
kind: executable
|
||||||
|
metadata:
|
||||||
|
name: demo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: ./generateDeployment.sh
|
||||||
|
spec:
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Create some additional resource just to make sure everything is added
|
||||||
|
th.WriteF(filepath.Join(tmpDir.String(), "short_secret.yaml"),
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
`)
|
||||||
|
th.WriteF(filepath.Join(tmpDir.String(), "generateDeployment.sh"), generateDeploymentDotSh)
|
||||||
|
assert.NoError(t, os.Chmod(filepath.Join(tmpDir.String(), "generateDeployment.sh"), 0777))
|
||||||
|
m := th.Run(tmpDir.String(), o)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: short_secret.yaml
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
configuredIn: kustomization.yaml
|
||||||
|
configuredBy:
|
||||||
|
kind: executable
|
||||||
|
name: demo
|
||||||
|
tshirt-size: small
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginCustomExecGeneratorWithOverlay(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
|
||||||
|
th := kusttest_test.MakeHarnessWithFs(t, fSys)
|
||||||
|
o := th.MakeOptionsPluginsEnabled()
|
||||||
|
o.PluginConfig.FnpLoadingOptions.EnableExec = true
|
||||||
|
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
base := filepath.Join(tmpDir.String(), "base")
|
||||||
|
prod := filepath.Join(tmpDir.String(), "prod")
|
||||||
|
assert.NoError(t, fSys.Mkdir(base))
|
||||||
|
assert.NoError(t, fSys.Mkdir(prod))
|
||||||
|
th.WriteK(base, `
|
||||||
|
resources:
|
||||||
|
- short_secret.yaml
|
||||||
|
generators:
|
||||||
|
- gener.yaml
|
||||||
|
`)
|
||||||
|
th.WriteK(prod, `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)
|
||||||
|
th.WriteF(filepath.Join(base, "short_secret.yaml"),
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
`)
|
||||||
|
th.WriteF(filepath.Join(base, "generateDeployment.sh"), generateDeploymentDotSh)
|
||||||
|
|
||||||
|
assert.NoError(t, os.Chmod(filepath.Join(base, "generateDeployment.sh"), 0777))
|
||||||
|
th.WriteF(filepath.Join(base, "gener.yaml"), `
|
||||||
|
kind: executable
|
||||||
|
metadata:
|
||||||
|
name: demo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: ./generateDeployment.sh
|
||||||
|
spec:
|
||||||
|
`)
|
||||||
|
|
||||||
|
m := th.Run(prod, o)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: ../base/short_secret.yaml
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
configuredIn: ../base/gener.yaml
|
||||||
|
configuredBy:
|
||||||
|
kind: executable
|
||||||
|
name: demo
|
||||||
|
tshirt-size: small
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginCustomInlineExecGeneratorWithOverlay(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
|
||||||
|
th := kusttest_test.MakeHarnessWithFs(t, fSys)
|
||||||
|
o := th.MakeOptionsPluginsEnabled()
|
||||||
|
o.PluginConfig.FnpLoadingOptions.EnableExec = true
|
||||||
|
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
base := filepath.Join(tmpDir.String(), "base")
|
||||||
|
prod := filepath.Join(tmpDir.String(), "prod")
|
||||||
|
assert.NoError(t, fSys.Mkdir(base))
|
||||||
|
assert.NoError(t, fSys.Mkdir(prod))
|
||||||
|
th.WriteK(base, `
|
||||||
|
resources:
|
||||||
|
- short_secret.yaml
|
||||||
|
generators:
|
||||||
|
- |-
|
||||||
|
kind: executable
|
||||||
|
metadata:
|
||||||
|
name: demo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: ./generateDeployment.sh
|
||||||
|
spec:
|
||||||
|
`)
|
||||||
|
th.WriteK(prod, `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)
|
||||||
|
th.WriteF(filepath.Join(base, "short_secret.yaml"),
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
`)
|
||||||
|
th.WriteF(filepath.Join(base, "generateDeployment.sh"), generateDeploymentDotSh)
|
||||||
|
assert.NoError(t, os.Chmod(filepath.Join(base, "generateDeployment.sh"), 0777))
|
||||||
|
m := th.Run(prod, o)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
path: ../base/short_secret.yaml
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: "true"
|
||||||
|
name: node1-bmc-secret
|
||||||
|
stringData:
|
||||||
|
userData: |
|
||||||
|
bootcmd:
|
||||||
|
- mkdir /mnt/vda
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
configuredIn: ../base/kustomization.yaml
|
||||||
|
configuredBy:
|
||||||
|
kind: executable
|
||||||
|
name: demo
|
||||||
|
tshirt-size: small
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
`, string(yml))
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnoOriginRemoteBuiltinGenerator(t *testing.T) {
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
|
||||||
|
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(`
|
||||||
|
resources:
|
||||||
|
- github.com/kubernetes-sigs/kustomize/examples/ldap/base/?ref=v1.0.6
|
||||||
|
buildMetadata: [originAnnotations]
|
||||||
|
`)))
|
||||||
|
m, err := b.Run(
|
||||||
|
fSys,
|
||||||
|
tmpDir.String())
|
||||||
|
if utils.IsErrTimeout(err) {
|
||||||
|
// Don't fail on timeouts.
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
yml, err := m.AsYaml()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, string(yml), `kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/origin: |
|
||||||
|
repo: https://github.com/kubernetes-sigs/kustomize
|
||||||
|
ref: v1.0.6
|
||||||
|
configuredIn: examples/ldap/base/kustomization.yaml
|
||||||
|
configuredBy:
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: ConfigMapGenerator
|
||||||
|
name: ldap-configmap-4d7m6k5b42`)
|
||||||
|
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ type Generator interface {
|
|||||||
Generate() (ResMap, error)
|
Generate() (ResMap, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A GeneratorWithProperties contains a Generator and stores
|
||||||
|
// some of its properties
|
||||||
|
type GeneratorWithProperties struct {
|
||||||
|
Generator
|
||||||
|
Origin *resource.Origin
|
||||||
|
}
|
||||||
|
|
||||||
// Something that's configurable accepts an
|
// Something that's configurable accepts an
|
||||||
// instance of PluginHelpers and a raw config
|
// instance of PluginHelpers and a raw config
|
||||||
// object (YAML in []byte form).
|
// object (YAML in []byte form).
|
||||||
@@ -136,6 +143,11 @@ type ResMap interface {
|
|||||||
// self, then its behavior _cannot_ be merge or replace.
|
// self, then its behavior _cannot_ be merge or replace.
|
||||||
AbsorbAll(ResMap) error
|
AbsorbAll(ResMap) error
|
||||||
|
|
||||||
|
// AddOriginAnnotation will add the provided origin as
|
||||||
|
// an annotation to all resources in the Resmap, if
|
||||||
|
// the origin is not nil.
|
||||||
|
AddOriginAnnotation(origin *resource.Origin) error
|
||||||
|
|
||||||
// AnnotateAll annotates all resources in the ResMap with
|
// AnnotateAll annotates all resources in the ResMap with
|
||||||
// the provided key value pair.
|
// the provided key value pair.
|
||||||
AnnotateAll(key string, value string) error
|
AnnotateAll(key string, value string) error
|
||||||
|
|||||||
@@ -484,6 +484,25 @@ func (m *resWrangler) AbsorbAll(other ResMap) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddOriginAnnotation implements ResMap.
|
||||||
|
func (m *resWrangler) AddOriginAnnotation(origin *resource.Origin) error {
|
||||||
|
if origin == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, res := range m.rList {
|
||||||
|
or, err := res.GetOrigin()
|
||||||
|
if or != nil || err != nil {
|
||||||
|
// if any resources already have an origin annotation,
|
||||||
|
// skip it
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := res.SetOrigin(origin); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
|
func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
|
||||||
id := res.CurId()
|
id := res.CurId()
|
||||||
matches := m.GetMatchingResourcesByAnyId(id.Equals)
|
matches := m.GetMatchingResourcesByAnyId(id.Equals)
|
||||||
@@ -510,9 +529,18 @@ func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
|
|||||||
case types.BehaviorReplace:
|
case types.BehaviorReplace:
|
||||||
res.CopyMergeMetaDataFieldsFrom(old)
|
res.CopyMergeMetaDataFieldsFrom(old)
|
||||||
case types.BehaviorMerge:
|
case types.BehaviorMerge:
|
||||||
|
// ensure the origin annotation doesn't get overwritten
|
||||||
|
orig, err := old.GetOrigin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
res.CopyMergeMetaDataFieldsFrom(old)
|
res.CopyMergeMetaDataFieldsFrom(old)
|
||||||
res.MergeDataMapFrom(old)
|
res.MergeDataMapFrom(old)
|
||||||
res.MergeBinaryDataMapFrom(old)
|
res.MergeBinaryDataMapFrom(old)
|
||||||
|
if orig != nil {
|
||||||
|
res.SetOrigin(orig)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"id %#v exists; behavior must be merge or replace", id)
|
"id %#v exists; behavior must be merge or replace", id)
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ import (
|
|||||||
// Origin retains information about where resources in the output
|
// Origin retains information about where resources in the output
|
||||||
// of `kustomize build` originated from
|
// of `kustomize build` originated from
|
||||||
type Origin struct {
|
type Origin struct {
|
||||||
// Path is the path to the resource, rooted from the directory upon
|
// Path is the path to the resource. If a local resource, this path is
|
||||||
// which `kustomize build` was invoked
|
// rooted from the directory upon which `kustomize build` was invoked. If a
|
||||||
|
// remote resource, this path is rooted from the root of the remote repo.
|
||||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
|
||||||
// Repo is the remote repository that the resource originated from if it is
|
// Repo is the remote repository that the resource originated from if it is
|
||||||
@@ -25,6 +26,16 @@ type Origin struct {
|
|||||||
// Ref is the ref of the remote repository that the resource originated from
|
// Ref is the ref of the remote repository that the resource originated from
|
||||||
// if it is not from a local file
|
// if it is not from a local file
|
||||||
Ref string `json:"ref,omitempty" yaml:"ref,omitempty"`
|
Ref string `json:"ref,omitempty" yaml:"ref,omitempty"`
|
||||||
|
|
||||||
|
// The following fields only apply to resources that have been
|
||||||
|
// generated by fields other than the `resources` field.
|
||||||
|
|
||||||
|
// ConfiguredIn is the file path to the generator config that created the
|
||||||
|
// resource
|
||||||
|
ConfiguredIn string `json:"configuredIn,omitempty" yaml:"configuredIn,omitempty"`
|
||||||
|
|
||||||
|
// ConfiguredBy is the ObjectReference of the generator config
|
||||||
|
ConfiguredBy kyaml.ResourceIdentifier `json:"configuredBy,omitempty" yaml:"configuredBy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of origin
|
// Copy returns a copy of origin
|
||||||
@@ -55,3 +66,29 @@ func (origin *Origin) String() (string, error) {
|
|||||||
anno, err := kyaml.Marshal(origin)
|
anno, err := kyaml.Marshal(origin)
|
||||||
return string(anno), err
|
return string(anno), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OriginFromCustomPlugin(res *Resource) (*Origin, error) {
|
||||||
|
origin, err := res.GetOrigin()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result *Origin
|
||||||
|
if origin != nil {
|
||||||
|
result = &Origin{
|
||||||
|
Repo: origin.Repo,
|
||||||
|
Ref: origin.Ref,
|
||||||
|
ConfiguredIn: origin.Path,
|
||||||
|
ConfiguredBy: kyaml.ResourceIdentifier{
|
||||||
|
TypeMeta: kyaml.TypeMeta{
|
||||||
|
APIVersion: res.GetApiVersion(),
|
||||||
|
Kind: res.GetKind(),
|
||||||
|
},
|
||||||
|
NameMeta: kyaml.NameMeta{
|
||||||
|
Name: res.GetName(),
|
||||||
|
Namespace: res.GetNamespace(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,6 +67,29 @@ func (r *Resource) SetGvk(gvk resid.Gvk) {
|
|||||||
r.SetApiVersion(gvk.ApiVersion())
|
r.SetApiVersion(gvk.ApiVersion())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Resource) GetOrigin() (*Origin, error) {
|
||||||
|
annotations := r.GetAnnotations()
|
||||||
|
originAnnotations, ok := annotations[utils.OriginAnnotation]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var origin Origin
|
||||||
|
if err := yaml.Unmarshal([]byte(originAnnotations), &origin); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &origin, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resource) SetOrigin(origin *Origin) error {
|
||||||
|
annotations := r.GetAnnotations()
|
||||||
|
originStr, err := origin.String()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
annotations[utils.OriginAnnotation] = originStr
|
||||||
|
return r.SetAnnotations(annotations)
|
||||||
|
}
|
||||||
|
|
||||||
// ResCtx is an interface describing the contextual added
|
// ResCtx is an interface describing the contextual added
|
||||||
// kept kustomize in the context of each Resource object.
|
// kept kustomize in the context of each Resource object.
|
||||||
// Currently mainly the name prefix and name suffix are added.
|
// Currently mainly the name prefix and name suffix are added.
|
||||||
|
|||||||
Reference in New Issue
Block a user