diff --git a/pkg/diff/diff.go b/pkg/diff/diff.go index 6e40534c7..8513eb03d 100644 --- a/pkg/diff/diff.go +++ b/pkg/diff/diff.go @@ -183,6 +183,9 @@ func serverSideDiff(config, live *unstructured.Unstructured, opts ...Option) (*D } } + // Remarshal predictedLive to ensure it receives the same normalization as live. + predictedLive = remarshal(predictedLive, o) + Normalize(predictedLive, opts...) unstructured.RemoveNestedField(predictedLive.Object, "metadata", "managedFields") diff --git a/pkg/diff/diff_test.go b/pkg/diff/diff_test.go index 81ac0eab8..69d0b1d58 100644 --- a/pkg/diff/diff_test.go +++ b/pkg/diff/diff_test.go @@ -1609,6 +1609,68 @@ metadata: assert.False(t, ok) } +func TestRemarshalStatefulSetCreationTimestamp(t *testing.T) { + manifest := []byte(` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: test-sts + creationTimestamp: "2025-11-06T19:35:31Z" +spec: + serviceName: test + selector: + matchLabels: + app: test + template: + metadata: + creationTimestamp: null + labels: + app: test + spec: + containers: + - name: test + image: nginx + volumeClaimTemplates: + - metadata: + name: data + creationTimestamp: null + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +`) + var un unstructured.Unstructured + require.NoError(t, yaml.Unmarshal(manifest, &un)) + + // Verify creationTimestamp exists in nested metadata before remarshal + spec := un.Object["spec"].(map[string]any) + templateMetadata := spec["template"].(map[string]any)["metadata"].(map[string]any) + _, ok := templateMetadata["creationTimestamp"] + assert.True(t, ok, "creationTimestamp should exist in template.metadata before remarshal") + + volumeClaimTemplates := spec["volumeClaimTemplates"].([]any) + vctMetadata := volumeClaimTemplates[0].(map[string]any)["metadata"].(map[string]any) + _, ok = vctMetadata["creationTimestamp"] + assert.True(t, ok, "creationTimestamp should exist in volumeClaimTemplates[0].metadata before remarshal") + + // Remarshal + newUn := remarshal(&un, applyOptions(diffOptionsForTest())) + + // Verify creationTimestamp is removed from nested metadata after remarshal + // (top-level metadata.creationTimestamp is preserved as it's part of the resource identity) + spec = newUn.Object["spec"].(map[string]any) + templateMetadata = spec["template"].(map[string]any)["metadata"].(map[string]any) + _, ok = templateMetadata["creationTimestamp"] + assert.False(t, ok, "creationTimestamp should be removed from template.metadata after remarshal") + + volumeClaimTemplates = spec["volumeClaimTemplates"].([]any) + vctMetadata = volumeClaimTemplates[0].(map[string]any)["metadata"].(map[string]any) + _, ok = vctMetadata["creationTimestamp"] + assert.False(t, ok, "creationTimestamp should be removed from volumeClaimTemplates[0].metadata after remarshal") +} + func TestRemarshalResources(t *testing.T) { getRequests := func(un *unstructured.Unstructured) map[string]any { return un.Object["spec"].(map[string]any)["containers"].([]any)[0].(map[string]any)["resources"].(map[string]any)["requests"].(map[string]any)