From c72d4565fbdb0ad363d405ff23ade04f10320af5 Mon Sep 17 00:00:00 2001 From: olalekan odukoya Date: Tue, 17 Feb 2026 03:05:25 +0100 Subject: [PATCH] add warning when kubectl rollout undo is used on apply-managed resources Signed-off-by: olalekan odukoya Kubernetes-commit: fbe6f7f9f25a8198199af9bd75aba9d45e994b36 --- pkg/cmd/cmd.go | 2 +- pkg/cmd/rollout/rollout.go | 4 ++-- pkg/cmd/rollout/rollout_undo.go | 21 ++++++++++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 16c417b01..665959d5e 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -275,7 +275,7 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command { { Message: "Deploy Commands:", Commands: []*cobra.Command{ - rollout.NewCmdRollout(f, o.IOStreams), + rollout.NewCmdRollout("kubectl", f, o.IOStreams), scale.NewCmdScale(f, o.IOStreams), autoscale.NewCmdAutoscale(f, o.IOStreams), }, diff --git a/pkg/cmd/rollout/rollout.go b/pkg/cmd/rollout/rollout.go index 2ecbe4829..a9f4f6c0c 100644 --- a/pkg/cmd/rollout/rollout.go +++ b/pkg/cmd/rollout/rollout.go @@ -54,7 +54,7 @@ var ( ) // NewCmdRollout returns a Command instance for 'rollout' sub command -func NewCmdRollout(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { +func NewCmdRollout(parent string, f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "rollout SUBCOMMAND", DisableFlagsInUseLine: true, @@ -67,7 +67,7 @@ func NewCmdRollout(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra cmd.AddCommand(NewCmdRolloutHistory(f, streams)) cmd.AddCommand(NewCmdRolloutPause(f, streams)) cmd.AddCommand(NewCmdRolloutResume(f, streams)) - cmd.AddCommand(NewCmdRolloutUndo(f, streams)) + cmd.AddCommand(NewCmdRolloutUndo(parent, f, streams)) cmd.AddCommand(NewCmdRolloutStatus(f, streams)) cmd.AddCommand(NewCmdRolloutRestart(f, streams)) diff --git a/pkg/cmd/rollout/rollout_undo.go b/pkg/cmd/rollout/rollout_undo.go index 30ebc99b6..d3e03bff0 100644 --- a/pkg/cmd/rollout/rollout_undo.go +++ b/pkg/cmd/rollout/rollout_undo.go @@ -21,6 +21,8 @@ import ( "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/cli-runtime/pkg/printers" @@ -47,6 +49,7 @@ type UndoOptions struct { LabelSelector string EnforceNamespace bool RESTClientGetter genericclioptions.RESTClientGetter + CmdBaseName string resource.FilenameOptions genericiooptions.IOStreams @@ -65,6 +68,10 @@ var ( # Roll back to the previous deployment with dry-run kubectl rollout undo --dry-run=server deployment/abc`) + + warningRollbackApplyManagedResource = "Warning: resource %[1]s was previously managed with '%[3]s apply'. " + + "Rolling back will not update the %[2]s annotation, which may cause unexpected behavior on future '%[3]s apply' operations. " + + "Consider using '%[3]s apply' with your previous configuration file instead.\n" ) // NewRolloutUndoOptions returns an initialized UndoOptions instance @@ -77,8 +84,9 @@ func NewRolloutUndoOptions(streams genericiooptions.IOStreams) *UndoOptions { } // NewCmdRolloutUndo returns a Command instance for the 'rollout undo' sub command -func NewCmdRolloutUndo(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { +func NewCmdRolloutUndo(parent string, f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { o := NewRolloutUndoOptions(streams) + o.CmdBaseName = parent validArgs := []string{"deployment", "daemonset", "statefulset"} @@ -157,6 +165,17 @@ func (o *UndoOptions) RunUndo() error { if err != nil { return err } + + metadata, err := meta.Accessor(info.Object) + if err != nil { + return err + } + + annotationMap := metadata.GetAnnotations() + if _, ok := annotationMap[corev1.LastAppliedConfigAnnotation]; ok { + fmt.Fprintf(o.ErrOut, warningRollbackApplyManagedResource, info.ObjectName(), corev1.LastAppliedConfigAnnotation, o.CmdBaseName) //nolint:errcheck + } + rollbacker, err := polymorphichelpers.RollbackerFn(o.RESTClientGetter, info.ResourceMapping()) if err != nil { return err