From ac64299c8af52cc7b4fadb8501056254783858a2 Mon Sep 17 00:00:00 2001 From: john-a-joyce Date: Mon, 18 Oct 2021 06:31:44 -0400 Subject: [PATCH] Adds an Ignore option for Metadata (#44) --- README.md | 6 ++++++ patch/deletenull.go | 31 +++++++++++++++++++++++++++++++ tests/integration_test.go | 28 +++++++++++++++++++++++++++- tests/main_test.go | 3 ++- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c92f402..5e63a12 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ To help in these scenarios there are the following options to be used when calcu - `IgnoreStatusFields` - `IgnoreVolumeClaimTemplateTypeMetaAndStatus` - `IgnorePDBSelector` +- `IgnoreField("field-name-to-ignore")` Example: ``` @@ -91,6 +92,11 @@ This CalculateOption clears volumeClaimTemplate fields from both objects before Checks `selector` fields of PDB objects before comparing and removes them if they match. `reflect.DeepEquals` is used for the equality check. This is required because map fields using `patchStrategy:"replace"` will always diff regardless if they are otherwise equal. +#### IgnoreField("field-name-to-ignore") + +This CalculateOption removes the field provided (as a string) in the call before comparing them. A common usage might be to remove the metadata fields by using the `IgnoreField("metadata")` option. + + ## Contributing If you find this project useful here's how you can help: diff --git a/patch/deletenull.go b/patch/deletenull.go index 0fdeb1b..0f388c8 100644 --- a/patch/deletenull.go +++ b/patch/deletenull.go @@ -41,6 +41,22 @@ func IgnoreStatusFields() CalculateOption { } } +func IgnoreField(field string) CalculateOption { + return func(current, modified []byte) ([]byte, []byte, error) { + current, err := deleteDataField(current, field) + if err != nil { + return []byte{}, []byte{}, errors.Wrap(err, "could not delete the field from current byte sequence") + } + + modified, err = deleteDataField(modified, field) + if err != nil { + return []byte{}, []byte{}, errors.Wrap(err, "could not delete the field from modified byte sequence") + } + + return current, modified, nil + } +} + func IgnoreVolumeClaimTemplateTypeMetaAndStatus() CalculateOption { return func(current, modified []byte) ([]byte, []byte, error) { current, err := deleteVolumeClaimTemplateFields(current) @@ -173,6 +189,21 @@ func deleteNullInSlice(m []interface{}) ([]interface{}, error) { return filteredSlice, nil } +func deleteDataField(obj []byte, fieldName string) ([]byte, error) { + var objectMap map[string]interface{} + err := json.Unmarshal(obj, &objectMap) + if err != nil { + return []byte{}, errors.Wrap(err, "could not unmarshal byte sequence") + } + delete(objectMap, fieldName) + obj, err = json.ConfigCompatibleWithStandardLibrary.Marshal(objectMap) + if err != nil { + return []byte{}, errors.Wrap(err, "could not marshal byte sequence") + } + + return obj, nil +} + func deleteStatusField(obj []byte) ([]byte, error) { var objectMap map[string]interface{} err := json.Unmarshal(obj, &objectMap) diff --git a/tests/integration_test.go b/tests/integration_test.go index ba29dd4..1e6d708 100644 --- a/tests/integration_test.go +++ b/tests/integration_test.go @@ -725,6 +725,27 @@ func TestIntegration(t *testing.T) { n := i.(*appsv1.StatefulSet) n.Spec.Template.ObjectMeta.Labels = map[string]string{"c": "d"} }), + NewTestMatch("Ignore Metadata field", + &v1.Pod{ + + ObjectMeta: standardObjectMeta(), + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "test-container", + Image: "test-image", + }, + }, + }, + }). + withRemoteChange(func(i interface{}) { + pod := i.(*v1.Pod) + pod.Labels = map[string]string{"a": "b"} + }). + withLocalChange(func(i interface{}) { + pod := i.(*v1.Pod) + pod.Labels = map[string]string{"c": "d"} + }), } runAll(t, tests) } @@ -748,7 +769,12 @@ func runAll(t *testing.T, tests []*TestItem) { if testing.Verbose() { log.Printf("> %s", test.name) } - err := testMatchOnObject(test) + var err error + if test.name == "Ignore Metadata field" { + err = testMatchOnObject(test, "metadata") + } else { + err = testMatchOnObject(test, "") + } if err != nil { if *failonerror { t.Fatalf("Test '%s' failed: %s %s", test.name, err, errors.GetDetails(err)) diff --git a/tests/main_test.go b/tests/main_test.go index 23e7a7e..fec1db4 100644 --- a/tests/main_test.go +++ b/tests/main_test.go @@ -186,13 +186,14 @@ func (t *TestItem) withIgnoreVersions(v []string) *TestItem { return t } -func testMatchOnObject(testItem *TestItem) error { +func testMatchOnObject(testItem *TestItem, ignoreField string) error { var existing metav1.Object var err error opts := []patch.CalculateOption{ patch.IgnoreStatusFields(), patch.IgnoreVolumeClaimTemplateTypeMetaAndStatus(), patch.IgnorePDBSelector(), + patch.IgnoreField(ignoreField), } newObject := testItem.object