[node operator] patching util functions

Signed-off-by: Malte Poll <mp@edgeless.systems>
This commit is contained in:
Malte Poll 2022-06-28 09:55:05 +02:00 committed by Malte Poll
parent 614447495d
commit 3932581f2a
6 changed files with 212 additions and 5 deletions

View File

@ -6,7 +6,7 @@ require (
github.com/medik8s/node-maintenance-operator v0.13.0
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.18.1
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.7.5
k8s.io/api v0.24.0
k8s.io/apimachinery v0.24.0
k8s.io/client-go v0.24.0
@ -14,6 +14,8 @@ require (
sigs.k8s.io/controller-runtime v0.12.1
)
require github.com/pmezard/go-difflib v1.0.0 // indirect
require (
cloud.google.com/go v0.81.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
@ -53,7 +55,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
@ -75,7 +76,7 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.24.0 // indirect
k8s.io/component-base v0.24.0 // indirect
k8s.io/klog/v2 v2.60.1 // indirect

View File

@ -485,13 +485,16 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -968,8 +971,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -0,0 +1,26 @@
package patch
import "sigs.k8s.io/controller-runtime/pkg/client"
// SetAnnotations creates a patch for a client.Object by merging annotations with existing annotations.
func SetAnnotations(original, patched client.Object, annotations map[string]string) client.Patch {
mergedAnnotations := patched.GetAnnotations()
if mergedAnnotations == nil {
mergedAnnotations = make(map[string]string, len(annotations))
}
for annotationKey, annotationValue := range annotations {
mergedAnnotations[annotationKey] = annotationValue
}
patched.SetAnnotations(mergedAnnotations)
return client.StrategicMergeFrom(original, client.MergeFromWithOptimisticLock{})
}
// UnsetAnnotations creates a patch for a client.Object by deleting annotations from the object.
func UnsetAnnotations(original, patched client.Object, annotationKeys []string) client.Patch {
mergedAnnotations := patched.GetAnnotations()
for _, annotationKey := range annotationKeys {
delete(mergedAnnotations, annotationKey)
}
patched.SetAnnotations(mergedAnnotations)
return client.StrategicMergeFrom(original, client.MergeFromWithOptimisticLock{})
}

View File

@ -0,0 +1,99 @@
package patch
import (
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var annotationsTestObject = corev1.Node{
TypeMeta: v1.TypeMeta{
Kind: "Node",
APIVersion: "v1",
},
ObjectMeta: v1.ObjectMeta{
ResourceVersion: "0",
},
}
func TestSetAnnotations(t *testing.T) {
testCases := map[string]struct {
oldAnnotations map[string]string
newAnnotations map[string]string
wantPatch []byte
}{
"empty patch only contains resource version": {
wantPatch: []byte(`{"metadata":{"resourceVersion":"0"}}`),
},
"patch on node without existing annotations": {
newAnnotations: map[string]string{"key": "value"},
wantPatch: []byte(`{"metadata":{"annotations":{"key":"value"},"resourceVersion":"0"}}`),
},
"patch on node with same existing annotations": {
oldAnnotations: map[string]string{"key": "value"},
newAnnotations: map[string]string{"key": "value"},
wantPatch: []byte(`{"metadata":{"resourceVersion":"0"}}`),
},
"patch on node with same key but different value": {
oldAnnotations: map[string]string{"key": "oldvalue"},
newAnnotations: map[string]string{"key": "newvalue"},
wantPatch: []byte(`{"metadata":{"annotations":{"key":"newvalue"},"resourceVersion":"0"}}`),
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
original := annotationsTestObject.DeepCopy()
original.SetAnnotations(tc.oldAnnotations)
patched := original.DeepCopy()
patch := SetAnnotations(original, patched, tc.newAnnotations)
data, err := patch.Data(patched)
assert.NoError(err)
assert.Equal(tc.wantPatch, data)
})
}
}
func TestUnsetAnnotations(t *testing.T) {
testCases := map[string]struct {
oldAnnotations map[string]string
annotationKeys []string
wantPatch []byte
}{
"empty patch only contains resource version": {
wantPatch: []byte(`{"metadata":{"resourceVersion":"0"}}`),
},
"patch on node without existing annotations": {
annotationKeys: []string{"key"},
wantPatch: []byte(`{"metadata":{"resourceVersion":"0"}}`),
},
"patch on node with existing annotations": {
oldAnnotations: map[string]string{"key": "value"},
annotationKeys: []string{"key"},
wantPatch: []byte(`{"metadata":{"annotations":null,"resourceVersion":"0"}}`),
},
"patch on node with existing annotations delete one of multiple": {
oldAnnotations: map[string]string{"key": "value", "otherkey": "othervalue"},
annotationKeys: []string{"key"},
wantPatch: []byte(`{"metadata":{"annotations":{"key":null},"resourceVersion":"0"}}`),
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
original := annotationsTestObject.DeepCopy()
original.SetAnnotations(tc.oldAnnotations)
patched := original.DeepCopy()
patch := UnsetAnnotations(original, patched, tc.annotationKeys)
data, err := patch.Data(patched)
assert.NoError(err)
assert.Equal(tc.wantPatch, data)
})
}
}

View File

@ -0,0 +1,18 @@
package patch
import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
// SetLabels creates a patch for a client.Object by merging labels with existing labels.
func SetLabels(original, patched client.Object, labels map[string]string) client.Patch {
mergedLabels := patched.GetLabels()
if mergedLabels == nil {
mergedLabels = make(map[string]string, len(labels))
}
for labelKey, labelValue := range labels {
mergedLabels[labelKey] = labelValue
}
patched.SetLabels(mergedLabels)
return client.StrategicMergeFrom(original, client.MergeFromWithOptimisticLock{})
}

View File

@ -0,0 +1,59 @@
package patch
import (
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var labelsTestObject = corev1.Node{
TypeMeta: v1.TypeMeta{
Kind: "Node",
APIVersion: "v1",
},
ObjectMeta: v1.ObjectMeta{
ResourceVersion: "0",
},
}
func TestSetLabels(t *testing.T) {
testCases := map[string]struct {
oldLabels map[string]string
newLabels map[string]string
wantPatch []byte
}{
"empty patch only contains resource version": {
wantPatch: []byte(`{"metadata":{"resourceVersion":"0"}}`),
},
"patch on node without existing labels": {
newLabels: map[string]string{"key": "value"},
wantPatch: []byte(`{"metadata":{"labels":{"key":"value"},"resourceVersion":"0"}}`),
},
"patch on node with same existing labels": {
oldLabels: map[string]string{"key": "value"},
newLabels: map[string]string{"key": "value"},
wantPatch: []byte(`{"metadata":{"resourceVersion":"0"}}`),
},
"patch on node with same key but different value": {
oldLabels: map[string]string{"key": "oldvalue"},
newLabels: map[string]string{"key": "newvalue"},
wantPatch: []byte(`{"metadata":{"labels":{"key":"newvalue"},"resourceVersion":"0"}}`),
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
original := labelsTestObject.DeepCopy()
original.SetLabels(tc.oldLabels)
patched := original.DeepCopy()
patch := SetLabels(original, patched, tc.newLabels)
data, err := patch.Data(patched)
assert.NoError(err)
assert.Equal(tc.wantPatch, data)
})
}
}