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:
Natasha Sarkar
2021-12-23 10:40:29 -08:00
committed by GitHub
parent 233f1a3c2a
commit 964bb38ba2
8 changed files with 717 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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