Skip to content

Commit 7632241

Browse files
committed
Preserve deployment and template metadata during reconcile
When running `kubectl rollout restart deployment`, Kubernetes adds the `kubectl.kubernetes.io/restartedAt` annotation to the deployment's pod template to trigger a rolling restart. However, Knative's reconciler was removing this annotation by completely replacing the deployment spec, causing new pods to be immediately terminated instead of completing the rollout. This change preserves externally-added metadata at both the deployment level and template level during reconciliation: - Deployment-level labels (was already the case) and annotations (new) - Pod template labels and annotations (e.g., kubectl.kubernetes.io/restartedAt) The fix uses kmeta.UnionMaps to merge desired state with existing state, allowing external annotations/labels to take precedence while still permitting Knative to manage its own metadata. Changes: - cruds.go: Added preservation of deployment and template-level metadata - table_test.go: Added test case to verify external metadata preservation
1 parent 910ae9e commit 7632241

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

pkg/reconciler/revision/cruds.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,13 @@ func (c *Reconciler) checkAndUpdateDeployment(ctx context.Context, rev *v1.Revis
7171
desiredDeployment := have.DeepCopy()
7272
desiredDeployment.Spec = deployment.Spec
7373

74-
// Carry over new labels.
74+
// Carry over new labels and annotations at deployment level.
7575
desiredDeployment.Labels = kmeta.UnionMaps(deployment.Labels, desiredDeployment.Labels)
76+
desiredDeployment.Annotations = kmeta.UnionMaps(deployment.Annotations, desiredDeployment.Annotations)
77+
78+
// Carry over template-level labels and annotations (e.g., kubectl.kubernetes.io/restartedAt from rollout restart).
79+
desiredDeployment.Spec.Template.Labels = kmeta.UnionMaps(deployment.Spec.Template.Labels, have.Spec.Template.Labels)
80+
desiredDeployment.Spec.Template.Annotations = kmeta.UnionMaps(deployment.Spec.Template.Annotations, have.Spec.Template.Annotations)
7681

7782
d, err := c.kubeclient.AppsV1().Deployments(deployment.Namespace).Update(ctx, desiredDeployment, metav1.UpdateOptions{})
7883
if err != nil {

pkg/reconciler/revision/table_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,19 @@ func TestReconcile(t *testing.T) {
209209
Object: deploy(t, "foo", "fix-containers"),
210210
}},
211211
Key: "foo/fix-containers",
212+
}, {
213+
Name: "preserve external deployment and template annotations and labels",
214+
Objects: []runtime.Object{
215+
Revision("foo", "preserve-annotations",
216+
WithLogURL, allUnknownConditions, withDefaultContainerStatuses(), WithRevisionObservedGeneration(1)),
217+
pa("foo", "preserve-annotations", WithReachabilityUnknown),
218+
addExternalMetadata(deploy(t, "foo", "preserve-annotations")),
219+
image("foo", "preserve-annotations"),
220+
},
221+
WantUpdates: []clientgotesting.UpdateActionImpl{{
222+
Object: addExternalMetadata(deploy(t, "foo", "preserve-annotations")),
223+
}},
224+
Key: "foo/preserve-annotations",
212225
}, {
213226
Name: "failure updating deployment",
214227
// Test that we handle an error updating the deployment properly.
@@ -878,6 +891,32 @@ func changeContainers(deploy *appsv1.Deployment) *appsv1.Deployment {
878891
return deploy
879892
}
880893

894+
func addExternalMetadata(deploy *appsv1.Deployment) *appsv1.Deployment {
895+
// Deployment-level metadata
896+
if deploy.Annotations == nil {
897+
deploy.Annotations = make(map[string]string)
898+
}
899+
deploy.Annotations["external.io/annotation"] = "external-annotation"
900+
901+
if deploy.Labels == nil {
902+
deploy.Labels = make(map[string]string)
903+
}
904+
deploy.Labels["external.io/label"] = "external-label"
905+
906+
// Template-level metadata (e.g. from kubectl restart)
907+
if deploy.Spec.Template.ObjectMeta.Annotations == nil {
908+
deploy.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
909+
}
910+
deploy.Spec.Template.ObjectMeta.Annotations["external.io/template-annotation"] = "external-annotation"
911+
912+
if deploy.Spec.Template.ObjectMeta.Labels == nil {
913+
deploy.Spec.Template.ObjectMeta.Labels = make(map[string]string)
914+
}
915+
deploy.Spec.Template.ObjectMeta.Labels["external.io/template-label"] = "external-label"
916+
917+
return deploy
918+
}
919+
881920
func withDefaultContainerStatuses() RevisionOption {
882921
return func(r *v1.Revision) {
883922
r.Status.ContainerStatuses = []v1.ContainerStatus{{

0 commit comments

Comments
 (0)