diff --git a/cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go b/cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go index a387ff7cac6..ba9e2049729 100644 --- a/cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go +++ b/cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go @@ -63,40 +63,55 @@ func runCleanupNode(c workflow.RunData) error { klog.Warningln("[reset] The kubelet service could not be stopped by kubeadm. Unable to detect a supported init system!") klog.Warningln("[reset] Please ensure kubelet is stopped manually") } else { - fmt.Println("[reset] Stopping the kubelet service") - if err := initSystem.ServiceStop("kubelet"); err != nil { - klog.Warningf("[reset] The kubelet service could not be stopped by kubeadm: [%v]\n", err) - klog.Warningln("[reset] Please ensure kubelet is stopped manually") + if !r.DryRun() { + fmt.Println("[reset] Stopping the kubelet service") + if err := initSystem.ServiceStop("kubelet"); err != nil { + klog.Warningf("[reset] The kubelet service could not be stopped by kubeadm: [%v]\n", err) + klog.Warningln("[reset] Please ensure kubelet is stopped manually") + } + } else { + fmt.Println("[reset] Would stop the kubelet service") } } - // Try to unmount mounted directories under kubeadmconstants.KubeletRunDirectory in order to be able to remove the kubeadmconstants.KubeletRunDirectory directory later - fmt.Printf("[reset] Unmounting mounted directories in %q\n", kubeadmconstants.KubeletRunDirectory) - // In case KubeletRunDirectory holds a symbolic link, evaluate it - kubeletRunDir, err := absoluteKubeletRunDirectory() - if err == nil { - // Only clean absoluteKubeletRunDirectory if umountDirsCmd passed without error - r.AddDirsToClean(kubeletRunDir) + if !r.DryRun() { + // Try to unmount mounted directories under kubeadmconstants.KubeletRunDirectory in order to be able to remove the kubeadmconstants.KubeletRunDirectory directory later + fmt.Printf("[reset] Unmounting mounted directories in %q\n", kubeadmconstants.KubeletRunDirectory) + // In case KubeletRunDirectory holds a symbolic link, evaluate it + kubeletRunDir, err := absoluteKubeletRunDirectory() + if err == nil { + // Only clean absoluteKubeletRunDirectory if umountDirsCmd passed without error + r.AddDirsToClean(kubeletRunDir) + } + } else { + fmt.Printf("[reset] Would unmount mounted directories in %q\n", kubeadmconstants.KubeletRunDirectory) } - klog.V(1).Info("[reset] Removing Kubernetes-managed containers") - if err := removeContainers(utilsexec.New(), r.CRISocketPath()); err != nil { - klog.Warningf("[reset] Failed to remove containers: %v\n", err) + if !r.DryRun() { + klog.V(1).Info("[reset] Removing Kubernetes-managed containers") + if err := removeContainers(utilsexec.New(), r.CRISocketPath()); err != nil { + klog.Warningf("[reset] Failed to remove containers: %v\n", err) + } + } else { + fmt.Println("[reset] Would remove Kubernetes-managed containers") } r.AddDirsToClean("/var/lib/dockershim", "/var/run/kubernetes", "/var/lib/cni") // Remove contents from the config and pki directories - klog.V(1).Infoln("[reset] Removing contents from the config and pki directories") if certsDir != kubeadmapiv1.DefaultCertificatesDir { klog.Warningf("[reset] WARNING: Cleaning a non-default certificates directory: %q\n", certsDir) } - resetConfigDir(kubeadmconstants.KubernetesDir, certsDir) + resetConfigDir(kubeadmconstants.KubernetesDir, certsDir, r.DryRun()) if r.Cfg() != nil && features.Enabled(r.Cfg().FeatureGates, features.RootlessControlPlane) { - klog.V(1).Infoln("[reset] Removing users and groups created for rootless control-plane") - if err := users.RemoveUsersAndGroups(); err != nil { - klog.Warningf("[reset] Failed to remove users and groups: %v\n", err) + if !r.DryRun() { + klog.V(1).Infoln("[reset] Removing users and groups created for rootless control-plane") + if err := users.RemoveUsersAndGroups(); err != nil { + klog.Warningf("[reset] Failed to remove users and groups: %v\n", err) + } + } else { + fmt.Println("[reset] Would remove users and groups created for rootless control-plane") } } @@ -130,16 +145,20 @@ func removeContainers(execer utilsexec.Interface, criSocketPath string) error { } // resetConfigDir is used to cleanup the files kubeadm writes in /etc/kubernetes/. -func resetConfigDir(configPathDir, pkiPathDir string) { +func resetConfigDir(configPathDir, pkiPathDir string, isDryRun bool) { dirsToClean := []string{ filepath.Join(configPathDir, kubeadmconstants.ManifestsSubDirName), pkiPathDir, } - fmt.Printf("[reset] Deleting contents of config directories: %v\n", dirsToClean) - for _, dir := range dirsToClean { - if err := CleanDir(dir); err != nil { - klog.Warningf("[reset] Failed to delete contents of %q directory: %v", dir, err) + if !isDryRun { + fmt.Printf("[reset] Deleting contents of directories: %v\n", dirsToClean) + for _, dir := range dirsToClean { + if err := CleanDir(dir); err != nil { + klog.Warningf("[reset] Failed to delete contents of %q directory: %v", dir, err) + } } + } else { + fmt.Printf("[reset] Would delete contents of directories: %v\n", dirsToClean) } filesToClean := []string{ @@ -149,11 +168,16 @@ func resetConfigDir(configPathDir, pkiPathDir string) { filepath.Join(configPathDir, kubeadmconstants.ControllerManagerKubeConfigFileName), filepath.Join(configPathDir, kubeadmconstants.SchedulerKubeConfigFileName), } - fmt.Printf("[reset] Deleting files: %v\n", filesToClean) - for _, path := range filesToClean { - if err := os.RemoveAll(path); err != nil { - klog.Warningf("[reset] Failed to remove file: %q [%v]\n", path, err) + + if !isDryRun { + fmt.Printf("[reset] Deleting files: %v\n", filesToClean) + for _, path := range filesToClean { + if err := os.RemoveAll(path); err != nil { + klog.Warningf("[reset] Failed to remove file: %q [%v]\n", path, err) + } } + } else { + fmt.Printf("[reset] Would delete files: %v\n", filesToClean) } } diff --git a/cmd/kubeadm/app/cmd/phases/reset/cleanupnode_test.go b/cmd/kubeadm/app/cmd/phases/reset/cleanupnode_test.go index 0f5fd1397d9..16a17713159 100644 --- a/cmd/kubeadm/app/cmd/phases/reset/cleanupnode_test.go +++ b/cmd/kubeadm/app/cmd/phases/reset/cleanupnode_test.go @@ -174,7 +174,7 @@ func TestConfigDirCleaner(t *testing.T) { if test.resetDir == "" { test.resetDir = "pki" } - resetConfigDir(tmpDir, filepath.Join(tmpDir, test.resetDir)) + resetConfigDir(tmpDir, filepath.Join(tmpDir, test.resetDir), false) // Verify the files we cleanup implicitly in every test: assertExists(t, tmpDir) diff --git a/cmd/kubeadm/app/cmd/phases/reset/data.go b/cmd/kubeadm/app/cmd/phases/reset/data.go index c9913267fcc..9862b2f262b 100644 --- a/cmd/kubeadm/app/cmd/phases/reset/data.go +++ b/cmd/kubeadm/app/cmd/phases/reset/data.go @@ -32,6 +32,7 @@ type resetData interface { InputReader() io.Reader IgnorePreflightErrors() sets.String Cfg() *kubeadmapi.InitConfiguration + DryRun() bool Client() clientset.Interface AddDirsToClean(dirs ...string) CertificatesDir() string diff --git a/cmd/kubeadm/app/cmd/phases/reset/removeetcdmember.go b/cmd/kubeadm/app/cmd/phases/reset/removeetcdmember.go index a1b01d374bd..7eae4f2f8fe 100644 --- a/cmd/kubeadm/app/cmd/phases/reset/removeetcdmember.go +++ b/cmd/kubeadm/app/cmd/phases/reset/removeetcdmember.go @@ -58,8 +58,12 @@ func runRemoveETCDMemberPhase(c workflow.RunData) error { if err == nil { r.AddDirsToClean(etcdDataDir) if cfg != nil { - if err := etcdphase.RemoveStackedEtcdMemberFromCluster(r.Client(), cfg); err != nil { - klog.Warningf("[reset] failed to remove etcd member: %v, please manually remove this etcd member using etcdctl", err) + if !r.DryRun() { + if err := etcdphase.RemoveStackedEtcdMemberFromCluster(r.Client(), cfg); err != nil { + klog.Warningf("[reset] Failed to remove etcd member: %v, please manually remove this etcd member using etcdctl", err) + } + } else { + fmt.Println("[reset] Would remove the etcd member on this node from the etcd cluster") } } } else { diff --git a/cmd/kubeadm/app/cmd/reset.go b/cmd/kubeadm/app/cmd/reset.go index 5d6221e2561..aaccbd56cec 100644 --- a/cmd/kubeadm/app/cmd/reset.go +++ b/cmd/kubeadm/app/cmd/reset.go @@ -64,6 +64,7 @@ type resetOptions struct { forceReset bool ignorePreflightErrors []string kubeconfigPath string + dryRun bool } // resetData defines all the runtime information used when running the kubeadm reset workflow; @@ -78,6 +79,7 @@ type resetData struct { outputWriter io.Writer cfg *kubeadmapi.InitConfiguration dirsToClean []string + dryRun bool } // newResetOptions returns a struct ready for being used for creating cmd join flags. @@ -134,6 +136,7 @@ func newResetData(cmd *cobra.Command, options *resetOptions, in io.Reader, out i inputReader: in, outputWriter: out, cfg: cfg, + dryRun: options.dryRun, }, nil } @@ -154,6 +157,10 @@ func AddResetFlags(flagSet *flag.FlagSet, resetOptions *resetOptions) { &resetOptions.forceReset, options.ForceReset, "f", false, "Reset the node without prompting for confirmation.", ) + flagSet.BoolVar( + &resetOptions.dryRun, options.DryRun, resetOptions.dryRun, + "Don't apply any changes; just output what would be done.", + ) options.AddKubeConfigFlag(flagSet, &resetOptions.kubeconfigPath) options.AddIgnorePreflightErrorsFlag(flagSet, &resetOptions.ignorePreflightErrors) @@ -214,6 +221,11 @@ func newCmdReset(in io.Reader, out io.Writer, resetOptions *resetOptions) *cobra } func cleanDirs(data *resetData) { + if data.DryRun() { + fmt.Printf("[reset] Would delete contents of stateful directories: %v\n", data.dirsToClean) + return + } + fmt.Printf("[reset] Deleting contents of stateful directories: %v\n", data.dirsToClean) for _, dir := range data.dirsToClean { klog.V(1).Infof("[reset] Deleting contents of %s", dir) @@ -228,6 +240,11 @@ func (r *resetData) Cfg() *kubeadmapi.InitConfiguration { return r.cfg } +// DryRun returns the DryRun flag. +func (r *resetData) DryRun() bool { + return r.dryRun +} + // CertificatesDir returns the CertificatesDir. func (r *resetData) CertificatesDir() string { return r.certificatesDir