Skip to content

Commit

Permalink
Versioned explicit reference now consumes existing buildObjRefReplace…
Browse files Browse the repository at this point in the history
…mentFunc
  • Loading branch information
100mik committed Sep 27, 2021
1 parent fa88a3a commit 22cdaad
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 92 deletions.
72 changes: 25 additions & 47 deletions pkg/kapp/diff/explicit_versioned_ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,54 @@ package diff

import (
"fmt"
"reflect"

ctlres "github.com/k14s/kapp/pkg/kapp/resources"
"gopkg.in/yaml.v2"
)

type VersionedRefDesc struct {
Namespace string `yaml:"namespace"`
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Name string `yaml:"name"`
}

type ExplicitVersionedRef struct {
Resource VersionedResource
Annotation string
AnnotationKey string
Annotation string
}

const (
explicitReferenceKey = "kapp.k14s.io/versioned-explicit-ref"
explicitReferenceKeyPrefix = "kapp.k14s.io/versioned-explicit-ref."
)

func NewExplicitVersionedRef(res VersionedResource, annotation string) *ExplicitVersionedRef {
return &ExplicitVersionedRef{res, annotation}
func NewExplicitVersionedRef(annotationKey string, annotation string) *ExplicitVersionedRef {
return &ExplicitVersionedRef{annotationKey, annotation}
}

// Returns true if the resource is referenced by the annotation
func (e *ExplicitVersionedRef) IsReferenced() (bool, error) {
reference := VersionedRefDesc{}
err := yaml.Unmarshal([]byte(e.Annotation), &reference)
func (e *ExplicitVersionedRef) AsObjectRef() (map[string]interface{}, error) {
var objectRef map[string]interface{}
err := yaml.Unmarshal([]byte(e.Annotation), &objectRef)
if err != nil {
return false, fmt.Errorf("Error unmarshalling versioned reference: %s", err)
return nil, fmt.Errorf("Parsing versioned explicit reference from annotation '%s': %s", e.AnnotationKey, err)
}

if reference.APIVersion == "" || reference.Kind == "" || reference.Name == "" {
return false, fmt.Errorf("Explicit reference error: apiVersion, kind and name are required values in an explicit versioned reference")
}
_, hasAPIVersionKey := objectRef["apiVersion"]
_, hasKindKey := objectRef["kind"]
_, hasNameKey := objectRef["name"]

baseName, _ := e.Resource.BaseNameAndVersion()
versionedResourceDesc := VersionedRefDesc{
Namespace: e.Resource.res.Namespace(),
APIVersion: e.Resource.res.APIVersion(),
Kind: e.Resource.res.Kind(),
Name: baseName,
if !(hasAPIVersionKey && hasKindKey && hasNameKey) {
return nil, fmt.Errorf("Expected versioned explicit reference to specify non-empty apiVersion, kind and name keys")
}

return reflect.DeepEqual(versionedResourceDesc, reference), nil
return objectRef, nil
}

func (e *ExplicitVersionedRef) VersionedReference() (string, error) {
reference := VersionedRefDesc{}
err := yaml.Unmarshal([]byte(e.Annotation), &reference)
func (e *ExplicitVersionedRef) AnnotationMod(objectRef map[string]interface{}) (ctlres.StringMapAppendMod, error) {
value, err := yaml.Marshal(objectRef)
if err != nil {
return "", fmt.Errorf("Error unmarshalling versioned reference: %s", err)
return ctlres.StringMapAppendMod{}, fmt.Errorf("Marshalling explicit reference: %s", err)
}

reference.Name = e.Resource.res.Name()
versionedReference, err := yaml.Marshal(reference)
if err != nil {
return "", fmt.Errorf("Error marshalling versioned reference: %s", err)
}

return string(versionedReference), nil
return ctlres.StringMapAppendMod{
ResourceMatcher: ctlres.AllMatcher{},
Path: ctlres.NewPathFromStrings([]string{"metadata", "annotations"}),
KVs: map[string]string{
e.AnnotationKey: string(value),
},
}, nil
}

/*
Annotation with a list of references in JSON format:
kapp.k14s.io/versioned-explicit-ref: '{ "references": [ { "namespace": <resource-namespace>, "apiGroup": <resource-apiGroup>, "kind" : <resource-kind>, "name": <resource-name> } ] }'
"namespace" need not be assigned a value for cluster-scoped resources.
"apiGroup" need not be assigned a value for resources belonging to "core" API group.
*/
33 changes: 15 additions & 18 deletions pkg/kapp/diff/versioned_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,26 @@ func (d VersionedResource) updateAffected(rule ctlconf.TemplateRule, rs []ctlres
for _, res := range rs {
for k, v := range res.Annotations() {
if k == explicitReferenceKey || strings.HasPrefix(k, explicitReferenceKeyPrefix) {
explicitRef := NewExplicitVersionedRef(d, v)
isReferenced, err := explicitRef.IsReferenced()
explicitRef := NewExplicitVersionedRef(k, v)
objectRef, err := explicitRef.AsObjectRef()
if err != nil {
return err
}

if isReferenced {
versionedReference, err := explicitRef.VersionedReference()
if err != nil {
return err
}
// Passing empty TemplateAffectedObjRef as explicit references do not have a special name key
err = d.buildObjRefReplacementFunc(ctlconf.TemplateAffectedObjRef{})(objectRef)
if err != nil {
return fmt.Errorf("Error updating explicit versioned reference: %s", err)
}

d.annotationMod(k, versionedReference).Apply(res)
annotationMod, err := explicitRef.AnnotationMod(objectRef)
if err != nil {
return err
}

err = annotationMod.Apply(res)
if err != nil {
return err
}
}
}
Expand All @@ -115,16 +122,6 @@ func (d VersionedResource) updateAffected(rule ctlconf.TemplateRule, rs []ctlres
return nil
}

func (d VersionedResource) annotationMod(annotation string, value string) ctlres.StringMapAppendMod {
return ctlres.StringMapAppendMod{
ResourceMatcher: ctlres.AllMatcher{},
Path: ctlres.NewPathFromStrings([]string{"metadata", "annotations"}),
KVs: map[string]string{
annotation: value,
},
}
}

func (d VersionedResource) buildObjRefReplacementFunc(
affectedObjRef ctlconf.TemplateAffectedObjRef) func(map[string]interface{}) error {

Expand Down
171 changes: 144 additions & 27 deletions test/e2e/versioned_explicit_reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,33 @@ metadata:
name: config-2
annotations:
kapp.k14s.io/versioned-explicit-ref: |
namespace: kapp-test
apiVersion: v1
kind: ConfigMap
name: config-1
data:
foo: bar
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-3
annotations:
kapp.k14s.io/versioned-explicit-ref.match: |
apiVersion: v1
kind: ConfigMap
name: config-1
data:
foo: bar
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-4
annotations:
kapp.k14s.io/versioned-explicit-ref.nomatch: |
apiVersion: v1
kind: ConfigMap
name: config-2
data:
foo: bar
`
Expand All @@ -58,12 +81,35 @@ metadata:
name: config-2
annotations:
kapp.k14s.io/versioned-explicit-ref: |
namespace: kapp-test
apiVersion: v1
kind: ConfigMap
name: config-1
data:
foo: bar
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-3
annotations:
kapp.k14s.io/versioned-explicit-ref.match: |
apiVersion: v1
kind: ConfigMap
name: config-1
data:
foo: bar
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-4
annotations:
kapp.k14s.io/versioned-explicit-ref.nomatch: |
apiVersion: v1
kind: ConfigMap
name: config-2
data:
foo: bar
`

name := "test-versioned-explicit-references"
Expand All @@ -75,41 +121,112 @@ data:
defer cleanUp()

logger.Section("deploy initial", func() {
kapp.RunWithOpts([]string{"deploy", "-f", "-", "-a", name},
out, _ := kapp.RunWithOpts([]string{"deploy", "-f", "-", "-a", name, "--json"},
RunOpts{IntoNs: true, StdinReader: strings.NewReader(yaml1)})
resp := uitest.JSONUIFromBytes(t, []byte(out))

expected := []map[string]string{
{
"age": "",
"conditions": "",
"kind": "ConfigMap",
"name": "config-1-ver-1",
"namespace": "kapp-test",
"op": "create",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "",
"wait_to": "reconcile",
},
{
"age": "",
"conditions": "",
"kind": "ConfigMap",
"name": "config-2",
"namespace": "kapp-test",
"op": "create",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "",
"wait_to": "reconcile",
},
{
"age": "",
"conditions": "",
"kind": "ConfigMap",
"name": "config-3",
"namespace": "kapp-test",
"op": "create",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "",
"wait_to": "reconcile",
},
{
"age": "",
"conditions": "",
"kind": "ConfigMap",
"name": "config-4",
"namespace": "kapp-test",
"op": "create",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "",
"wait_to": "reconcile",
},
}

if !reflect.DeepEqual(resp.Tables[0].Rows, expected) {
t.Fatalf("Expected to see correct changes but recieved >>%s<<", out)
}
})

logger.Section("update versioned resource", func() {
out, _ := kapp.RunWithOpts([]string{"deploy", "-f", "-", "-a", name, "--json"},
RunOpts{IntoNs: true, StdinReader: strings.NewReader(yaml2)})
resp := uitest.JSONUIFromBytes(t, []byte(out))

expected := []map[string]string{{
"age": "",
"conditions": "",
"kind": "ConfigMap",
"name": "config-1-ver-2",
"namespace": "kapp-test",
"op": "create",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "",
"wait_to": "reconcile",
}, {
"age": "<replaced>",
"conditions": "",
"kind": "ConfigMap",
"name": "config-2",
"namespace": "kapp-test",
"op": "update",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "ok",
"wait_to": "reconcile",
}}
expected := []map[string]string{
{
"age": "",
"conditions": "",
"kind": "ConfigMap",
"name": "config-1-ver-2",
"namespace": "kapp-test",
"op": "create",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "",
"wait_to": "reconcile",
},
{
"age": "<replaced>",
"conditions": "",
"kind": "ConfigMap",
"name": "config-2",
"namespace": "kapp-test",
"op": "update",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "ok",
"wait_to": "reconcile",
},
{
"age": "<replaced>",
"conditions": "",
"kind": "ConfigMap",
"name": "config-3",
"namespace": "kapp-test",
"op": "update",
"op_strategy": "",
"reconcile_info": "",
"reconcile_state": "ok",
"wait_to": "reconcile",
},
}

if !reflect.DeepEqual(replaceAge(resp.Tables[0].Rows), expected) {
t.Fatalf("Expected to see correct changes, but did not: '%s'", out)
t.Fatalf("Expected to see correct changes but recieved >>%s<<", out)
}
})
}

0 comments on commit 22cdaad

Please sign in to comment.