mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +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(
|
||||
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
|
||||
var result []resmap.Generator
|
||||
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) (
|
||||
result []*resmap.GeneratorWithProperties, err error) {
|
||||
for _, res := range rm.Resources() {
|
||||
g, err := l.LoadGenerator(ldr, v, res)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
// KustTarget encapsulates the entirety of a kustomization build.
|
||||
type KustTarget struct {
|
||||
kustomization *types.Kustomization
|
||||
kustFileName string
|
||||
ldr ifc.Loader
|
||||
validator ifc.Validator
|
||||
rFactory *resmap.Factory
|
||||
@@ -53,7 +54,7 @@ func NewKustTarget(
|
||||
|
||||
// Load attempts to load the target's kustomization file.
|
||||
func (kt *KustTarget) Load() error {
|
||||
content, err := loadKustFile(kt.ldr)
|
||||
content, kustFileName, err := loadKustFile(kt.ldr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -74,6 +75,7 @@ func (kt *KustTarget) Load() error {
|
||||
strings.Join(errs, "\n"), kt.ldr.Root())
|
||||
}
|
||||
kt.kustomization = &k
|
||||
kt.kustFileName = kustFileName
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -85,23 +87,25 @@ func (kt *KustTarget) Kustomization() types.Kustomization {
|
||||
return result
|
||||
}
|
||||
|
||||
func loadKustFile(ldr ifc.Loader) ([]byte, error) {
|
||||
func loadKustFile(ldr ifc.Loader) ([]byte, string, error) {
|
||||
var content []byte
|
||||
match := 0
|
||||
var kustFileName string
|
||||
for _, kf := range konfig.RecognizedKustomizationFileNames() {
|
||||
c, err := ldr.Load(kf)
|
||||
if err == nil {
|
||||
match += 1
|
||||
content = c
|
||||
kustFileName = kf
|
||||
}
|
||||
}
|
||||
switch match {
|
||||
case 0:
|
||||
return nil, NewErrMissingKustomization(ldr.Root())
|
||||
return nil, "", NewErrMissingKustomization(ldr.Root())
|
||||
case 1:
|
||||
return content, nil
|
||||
return content, kustFileName, nil
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
return nil, "", fmt.Errorf(
|
||||
"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(
|
||||
err, "merging CRDs %v", crdTc)
|
||||
}
|
||||
err = kt.runGenerators(ra)
|
||||
err = kt.runGenerators(ra, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -244,23 +248,31 @@ func (kt *KustTarget) IgnoreLocal(ra *accumulator.ResAccumulator) error {
|
||||
}
|
||||
|
||||
func (kt *KustTarget) runGenerators(
|
||||
ra *accumulator.ResAccumulator) error {
|
||||
var generators []resmap.Generator
|
||||
gs, err := kt.configureBuiltinGenerators()
|
||||
ra *accumulator.ResAccumulator, origin *resource.Origin) error {
|
||||
var generators []*resmap.GeneratorWithProperties
|
||||
gs, err := kt.configureBuiltinGenerators(origin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
generators = append(generators, gs...)
|
||||
gs, err = kt.configureExternalGenerators()
|
||||
|
||||
gs, err = kt.configureExternalGenerators(origin)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "loading generator plugins")
|
||||
}
|
||||
generators = append(generators, gs...)
|
||||
for _, g := range generators {
|
||||
|
||||
for i, g := range generators {
|
||||
resMap, err := g.Generate()
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "merging from generator %v", g)
|
||||
@@ -269,7 +281,8 @@ func (kt *KustTarget) runGenerators(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error) {
|
||||
func (kt *KustTarget) configureExternalGenerators(origin *resource.Origin) (
|
||||
[]*resmap.GeneratorWithProperties, error) {
|
||||
ra := accumulator.MakeEmptyAccumulator()
|
||||
var generatorPaths []string
|
||||
for _, p := range kt.kustomization.Generators {
|
||||
@@ -279,10 +292,19 @@ func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error)
|
||||
// not an inline config
|
||||
generatorPaths = append(generatorPaths, p)
|
||||
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, err := kt.accumulateResources(ra, generatorPaths, &resource.Origin{})
|
||||
ra, err := kt.accumulateResources(ra, generatorPaths, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,11 +5,14 @@ package target
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Functions dedicated to configuring the builtin
|
||||
@@ -27,8 +30,8 @@ import (
|
||||
// image tag transforms. In these cases, we'll need
|
||||
// N plugin instances with differing configurations.
|
||||
|
||||
func (kt *KustTarget) configureBuiltinGenerators() (
|
||||
result []resmap.Generator, err error) {
|
||||
func (kt *KustTarget) configureBuiltinGenerators(origin *resource.Origin) (
|
||||
result []*resmap.GeneratorWithProperties, err error) {
|
||||
for _, bpt := range []builtinhelpers.BuiltinPluginType{
|
||||
builtinhelpers.ConfigMapGenerator,
|
||||
builtinhelpers.SecretGenerator,
|
||||
@@ -39,7 +42,25 @@ func (kt *KustTarget) configureBuiltinGenerators() (
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,10 +4,16 @@
|
||||
package krusty_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"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"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
func TestAnnoOriginLocalFiles(t *testing.T) {
|
||||
@@ -263,3 +269,546 @@ metadata:
|
||||
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)
|
||||
}
|
||||
|
||||
// A GeneratorWithProperties contains a Generator and stores
|
||||
// some of its properties
|
||||
type GeneratorWithProperties struct {
|
||||
Generator
|
||||
Origin *resource.Origin
|
||||
}
|
||||
|
||||
// Something that's configurable accepts an
|
||||
// instance of PluginHelpers and a raw config
|
||||
// object (YAML in []byte form).
|
||||
@@ -136,6 +143,11 @@ type ResMap interface {
|
||||
// self, then its behavior _cannot_ be merge or replace.
|
||||
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
|
||||
// the provided key value pair.
|
||||
AnnotateAll(key string, value string) error
|
||||
|
||||
@@ -484,6 +484,25 @@ func (m *resWrangler) AbsorbAll(other ResMap) error {
|
||||
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 {
|
||||
id := res.CurId()
|
||||
matches := m.GetMatchingResourcesByAnyId(id.Equals)
|
||||
@@ -510,9 +529,18 @@ func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
|
||||
case types.BehaviorReplace:
|
||||
res.CopyMergeMetaDataFieldsFrom(old)
|
||||
case types.BehaviorMerge:
|
||||
// ensure the origin annotation doesn't get overwritten
|
||||
orig, err := old.GetOrigin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res.CopyMergeMetaDataFieldsFrom(old)
|
||||
res.MergeDataMapFrom(old)
|
||||
res.MergeBinaryDataMapFrom(old)
|
||||
if orig != nil {
|
||||
res.SetOrigin(orig)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"id %#v exists; behavior must be merge or replace", id)
|
||||
|
||||
@@ -14,8 +14,9 @@ import (
|
||||
// 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 is the path to the resource. If a local resource, this path is
|
||||
// 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"`
|
||||
|
||||
// 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
|
||||
// if it is not from a local file
|
||||
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
|
||||
@@ -55,3 +66,29 @@ func (origin *Origin) String() (string, error) {
|
||||
anno, err := kyaml.Marshal(origin)
|
||||
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())
|
||||
}
|
||||
|
||||
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
|
||||
// kept kustomize in the context of each Resource object.
|
||||
// Currently mainly the name prefix and name suffix are added.
|
||||
|
||||
Reference in New Issue
Block a user