Skip to content

Commit

Permalink
remove the creationTimestamp from metadata when using ssa apply manif…
Browse files Browse the repository at this point in the history
…ests

Signed-off-by: Wei Liu <liuweixa@redhat.com>
  • Loading branch information
skeeey committed Sep 5, 2024
1 parent 1fc820a commit fa4be1a
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 0 deletions.
32 changes: 32 additions & 0 deletions pkg/work/spoke/apply/server_side_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"

workapiv1 "open-cluster-management.io/api/work/v1"

Expand Down Expand Up @@ -52,6 +53,13 @@ func (c *ServerSideApply) Apply(
}
}

// Currently, if the required object has zero creationTime in metadata, it will cause
// kube-apiserver to increment generation even if nothing else changes. more details see:
// https://github.com/kubernetes/kubernetes/issues/67610
//
// TODO Remove this after the above issue fixed in Kubernetes
removeCreationTimeFromMetadata(required.Object)

obj, err := c.client.
Resource(gvr).
Namespace(required.GetNamespace()).
Expand All @@ -72,3 +80,27 @@ func (c *ServerSideApply) Apply(

return obj, err
}

func removeCreationTimeFromMetadata(obj map[string]interface{}) {
if metadata, found := obj["metadata"]; found {
if metaObj, ok := metadata.(map[string]interface{}); ok {
klog.V(4).Infof("remove `metadata.creationTimestamp`")
unstructured.RemoveNestedField(metaObj, "creationTimestamp")
}
}

for k, v := range obj {
switch val := v.(type) {
case map[string]interface{}:
klog.V(4).Infof("remove `metadata.creationTimestamp` from %s", k)
removeCreationTimeFromMetadata(val)
case []interface{}:
for index, item := range val {
klog.V(4).Infof("remove `metadata.creationTimestamp` from %s[%d]", k, index)
if itemMap, ok := item.(map[string]interface{}); ok {
removeCreationTimeFromMetadata(itemMap)
}
}
}
}
}
140 changes: 140 additions & 0 deletions pkg/work/spoke/apply/server_side_apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"testing"

"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -139,3 +141,141 @@ func (r *reactor) React(action clienttesting.Action) (handled bool, ret runtime.

return true, nil, fmt.Errorf("PatchType is not supported")
}

func TestRemoveCreationTime(t *testing.T) {
cases := []struct {
name string
required *unstructured.Unstructured
validateFunc func(t *testing.T, obj *unstructured.Unstructured)
}{
{
name: "remove creationTimestamp from a kube object",
required: newDeployment(),
validateFunc: func(t *testing.T, obj *unstructured.Unstructured) {
_, existing, err := unstructured.NestedFieldCopy(obj.Object, "metadata", "creationTimestamp")
if err != nil {
t.Fatal(err)
}
if existing {
t.Errorf("unexpected creationTimestamp in `metadata.creationTimestamp`")
}
_, existing, err = unstructured.NestedFieldCopy(obj.Object, "spec", "template", "metadata", "creationTimestamp")
if err != nil {
t.Fatal(err)
}
if existing {
t.Errorf("unexpected creationTimestamp in `spec.template.metadata.creationTimestamp`")
}
},
},
{
name: "remove creationTimestamp from a manifestwork",
required: newManifestWork(),
validateFunc: func(t *testing.T, obj *unstructured.Unstructured) {
_, existing, err := unstructured.NestedFieldCopy(obj.Object, "metadata", "creationTimestamp")
if err != nil {
t.Fatal(err)
}
if existing {
t.Errorf("unexpected creationTimestamp in `metadata.creationTimestamp`")
}

manifests, existing, err := unstructured.NestedSlice(obj.Object, "spec", "workload", "manifests")
if err != nil {
t.Fatal(err)
}
if !existing {
t.Fatalf("no manifests")
}

_, existing, err = unstructured.NestedFieldCopy(manifests[0].(map[string]interface{}), "metadata", "creationTimestamp")
if err != nil {
t.Fatal(err)
}
if existing {
t.Errorf("unexpected creationTimestamp in `spec.workload.manifests[0].metadata.creationTimestamp`")
}
},
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
removeCreationTimeFromMetadata(c.required.Object)
c.validateFunc(t, c.required)
})
}
}

func newDeployment() *unstructured.Unstructured {
var replicas int32 = 3
deploy := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test",
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"test": "test"},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "test",
Image: "test",
},
},
},
},
},
}

obj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(deploy)
return &unstructured.Unstructured{Object: obj}
}

func newManifestWork() *unstructured.Unstructured {
cm := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "cm-test",
Namespace: "default",
},
Data: map[string]string{
"some": "data",
},
}

cmObj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(cm)
raw, _ := (&unstructured.Unstructured{Object: cmObj}).MarshalJSON()
manifest := workapiv1.Manifest{}
manifest.Raw = raw

work := &workapiv1.ManifestWork{
TypeMeta: metav1.TypeMeta{
APIVersion: "work.open-cluster-management.io/v1",
Kind: "ManifestWork",
},
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test",
},
Spec: workapiv1.ManifestWorkSpec{
Workload: workapiv1.ManifestsTemplate{
Manifests: []workapiv1.Manifest{manifest},
},
},
}

obj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(work)
return &unstructured.Unstructured{Object: obj}
}

0 comments on commit fa4be1a

Please sign in to comment.