From fbe5ebff9e83a427e80b804e768d21aa0b8cf8ff Mon Sep 17 00:00:00 2001 From: Timofei Larkin Date: Thu, 18 Apr 2024 01:06:45 +0300 Subject: [PATCH] Factor out StrategicMerge into a generic function and add a test --- internal/controller/factory/statefulset.go | 20 ++------------ internal/k8sutils/strategicmerge.go | 32 ++++++++++++++++++++++ internal/k8sutils/strategicmerge_test.go | 19 +++++++++++++ internal/k8sutils/suite_test.go | 13 +++++++++ 4 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 internal/k8sutils/strategicmerge.go create mode 100644 internal/k8sutils/strategicmerge_test.go create mode 100644 internal/k8sutils/suite_test.go diff --git a/internal/controller/factory/statefulset.go b/internal/controller/factory/statefulset.go index fcc7d370..0a23af7c 100644 --- a/internal/controller/factory/statefulset.go +++ b/internal/controller/factory/statefulset.go @@ -18,7 +18,6 @@ package factory import ( "context" - "encoding/json" "fmt" appsv1 "k8s.io/api/apps/v1" @@ -26,11 +25,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/strategicpatch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" etcdaenixiov1alpha1 "github.com/aenix-io/etcd-operator/api/v1alpha1" + "github.com/aenix-io/etcd-operator/internal/k8sutils" ) const ( @@ -75,25 +74,12 @@ func CreateOrUpdateStatefulSet( Containers: []corev1.Container{generateContainer(cluster)}, Volumes: volumes, } - basePodSpecBytes, err := json.Marshal(basePodSpec) - if err != nil { - return fmt.Errorf("cannot marshal generated base pod spec to JSON: %w", err) - } if cluster.Spec.PodTemplate.Spec.Containers == nil { cluster.Spec.PodTemplate.Spec.Containers = make([]corev1.Container, 0) } - overridePodSpecBytes, err := json.Marshal(cluster.Spec.PodTemplate.Spec) - if err != nil { - return fmt.Errorf("cannot marshal field podTemplate.spec to JSON: %w", err) - } - finalPodSpecBytes, err := strategicpatch.StrategicMergePatch(basePodSpecBytes, overridePodSpecBytes, &corev1.PodSpec{}) - if err != nil { - return fmt.Errorf("cannot patch base pod spec with podTemplate.spec: %w", err) - } - finalPodSpec := corev1.PodSpec{} - err = json.Unmarshal(finalPodSpecBytes, &finalPodSpec) + finalPodSpec, err := k8sutils.StrategicMerge(basePodSpec, cluster.Spec.PodTemplate.Spec) if err != nil { - return fmt.Errorf("cannot unmarshal finalized podspec from JSON: %w", err) + return fmt.Errorf("cannot strategic-merge base podspec with podTemplate.spec: %w", err) } statefulSet := &appsv1.StatefulSet{ diff --git a/internal/k8sutils/strategicmerge.go b/internal/k8sutils/strategicmerge.go new file mode 100644 index 00000000..a0f6f801 --- /dev/null +++ b/internal/k8sutils/strategicmerge.go @@ -0,0 +1,32 @@ +package k8sutils + +import ( + "encoding/json" + "fmt" + + "k8s.io/apimachinery/pkg/util/strategicpatch" +) + +func StrategicMerge[K any](base, patch K) (merged K, err error) { + baseBytes, err := json.Marshal(base) + if err != nil { + return merged, fmt.Errorf("cannot marshal base object to JSON: %w", err) + } + + patchBytes, err := json.Marshal(patch) + if err != nil { + return merged, fmt.Errorf("cannot marshal patch object to JSON: %w", err) + } + + mergedBytes, err := strategicpatch.StrategicMergePatch(baseBytes, patchBytes, &merged) + if err != nil { + return merged, fmt.Errorf("cannot patch base pod spec with podTemplate.spec: %w", err) + } + + err = json.Unmarshal(mergedBytes, &merged) + if err != nil { + return merged, fmt.Errorf("cannot unmarshal merged object from JSON: %w", err) + } + + return merged, nil +} diff --git a/internal/k8sutils/strategicmerge_test.go b/internal/k8sutils/strategicmerge_test.go new file mode 100644 index 00000000..c8ee897b --- /dev/null +++ b/internal/k8sutils/strategicmerge_test.go @@ -0,0 +1,19 @@ +package k8sutils + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" +) + +var _ = Describe("StrategicMerge handler", func() { + When("Merging two podspecs", func() { + It("Should merge two empty podspecs into an empty podspec", func() { + p1 := corev1.PodSpec{} + p2 := corev1.PodSpec{} + p, err := StrategicMerge(p1, p2) + Expect(err).NotTo(HaveOccurred()) + Expect(p).To(Equal(p1)) + }) + }) +}) diff --git a/internal/k8sutils/suite_test.go b/internal/k8sutils/suite_test.go new file mode 100644 index 00000000..5a1fd363 --- /dev/null +++ b/internal/k8sutils/suite_test.go @@ -0,0 +1,13 @@ +package k8sutils + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestK8SUtils(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "K8SUtils Suite") +}