diff --git a/cmd/minikube/cmd/stop.go b/cmd/minikube/cmd/stop.go index 4cce5d09fa..4fd0fadabc 100644 --- a/cmd/minikube/cmd/stop.go +++ b/cmd/minikube/cmd/stop.go @@ -46,6 +46,7 @@ var ( stopAll bool keepActive bool scheduledStopDuration time.Duration + cancelScheduledStop bool ) // stopCmd represents the stop command @@ -60,6 +61,8 @@ func init() { stopCmd.Flags().BoolVar(&stopAll, "all", false, "Set flag to stop all profiles (clusters)") stopCmd.Flags().BoolVar(&keepActive, "keep-context-active", false, "keep the kube-context active after cluster is stopped. Defaults to false.") stopCmd.Flags().DurationVar(&scheduledStopDuration, "schedule", 0*time.Second, "Set flag to stop cluster after a set amount of time (e.g. --schedule=5m)") + stopCmd.Flags().BoolVar(&cancelScheduledStop, "cancel-scheduled", false, "cancel any existing scheduled stop requests") + if err := stopCmd.Flags().MarkHidden("schedule"); err != nil { klog.Info("unable to mark --schedule flag as hidden") } @@ -97,6 +100,11 @@ func runStop(cmd *cobra.Command, args []string) { // Kill any existing scheduled stops schedule.KillExisting(profilesToStop) + if cancelScheduledStop { + register.Reg.SetStep(register.Done) + out.Step(style.Stopped, `All existing scheduled stops cancelled`) + return + } if scheduledStopDuration != 0 { if err := schedule.Daemonize(profilesToStop, scheduledStopDuration); err != nil { diff --git a/site/content/en/docs/commands/stop.md b/site/content/en/docs/commands/stop.md index 9f54204ab1..8b96e3de89 100644 --- a/site/content/en/docs/commands/stop.md +++ b/site/content/en/docs/commands/stop.md @@ -21,6 +21,7 @@ minikube stop [flags] ``` --all Set flag to stop all profiles (clusters) + --cancel-scheduled cancel any existing scheduled stop requests --keep-context-active keep the kube-context active after cluster is stopped. Defaults to false. -o, --output string Format to print stdout in. Options include: [text,json] (default "text") ``` diff --git a/test/integration/scheduled_stop_test.go b/test/integration/scheduled_stop_test.go index a9d833a22b..508a385999 100644 --- a/test/integration/scheduled_stop_test.go +++ b/test/integration/scheduled_stop_test.go @@ -50,7 +50,7 @@ func TestScheduledStopWindows(t *testing.T) { startMinikube(ctx, t, profile) // schedule a stop for 5m from now - scheduledStopMinikube(ctx, t, profile, "5m") + stopMinikube(ctx, t, profile, []string{"--schedule", "5m"}) // make sure the systemd service is running rr, err := Run(t, exec.CommandContext(ctx, Target(), []string{"ssh", "-p", profile, "--", "sudo", "systemctl", "show", constants.ScheduledStopSystemdService, "--no-page"}...)) @@ -62,12 +62,12 @@ func TestScheduledStopWindows(t *testing.T) { } // reschedule stop for 5 seconds from now - scheduledStopMinikube(ctx, t, profile, "5s") + stopMinikube(ctx, t, profile, []string{"--schedule", "5s"}) // sleep for 5 seconds time.Sleep(5 * time.Second) // make sure minikube status is "Stopped" - ensureMinikubeStatusStopped(ctx, t, profile) + ensureMinikubeStatus(ctx, t, profile, state.Stopped.String()) } func TestScheduledStopUnix(t *testing.T) { @@ -83,20 +83,31 @@ func TestScheduledStopUnix(t *testing.T) { startMinikube(ctx, t, profile) // schedule a stop for 5 min from now and make sure PID is created - scheduledStopMinikube(ctx, t, profile, "5m") + stopMinikube(ctx, t, profile, []string{"--schedule", "5m"}) pid := checkPID(t, profile) if !processRunning(t, pid) { t.Fatalf("process %v is not running", pid) } - // redo scheduled stop to be in 10s - scheduledStopMinikube(ctx, t, profile, "10s") + // schedule a second stop which should cancel the first scheduled stop + stopMinikube(ctx, t, profile, []string{"--schedule", "8s"}) if processRunning(t, pid) { t.Fatalf("process %v running but should have been killed on reschedule of stop", pid) } checkPID(t, profile) - // make sure minikube status is "Stopped" - ensureMinikubeStatusStopped(ctx, t, profile) + + // cancel the shutdown and make sure minikube is still running after 8 seconds + // sleep 12 just to be safe + stopMinikube(ctx, t, profile, []string{"--cancel-scheduled"}) + time.Sleep(12 * time.Second) + ensureMinikubeStatus(ctx, t, profile, state.Running.String()) + + // schedule another stop, make sure minikube status is "Stopped" + stopMinikube(ctx, t, profile, []string{"--schedule", "5s"}) + if processRunning(t, pid) { + t.Fatalf("process %v running but should have been killed on reschedule of stop", pid) + } + ensureMinikubeStatus(ctx, t, profile, state.Stopped.String()) } func startMinikube(ctx context.Context, t *testing.T, profile string) { @@ -107,8 +118,9 @@ func startMinikube(ctx context.Context, t *testing.T, profile string) { } } -func scheduledStopMinikube(ctx context.Context, t *testing.T, profile string, stop string) { - args := []string{"stop", "-p", profile, "--schedule", stop} +func stopMinikube(ctx context.Context, t *testing.T, profile string, additionalArgs []string) { + args := []string{"stop", "-p", profile} + args = append(args, additionalArgs...) rr, err := Run(t, exec.CommandContext(ctx, Target(), args...)) if err != nil { t.Fatalf("starting minikube: %v\n%s", err, rr.Output()) @@ -144,14 +156,13 @@ func processRunning(t *testing.T, pid string) bool { t.Log("signal error was: ", err) return err == nil } - -func ensureMinikubeStatusStopped(ctx context.Context, t *testing.T, profile string) { +func ensureMinikubeStatus(ctx context.Context, t *testing.T, profile, wantStatus string) { // wait allotted time to make sure minikube status is "Stopped" checkStatus := func() error { ctx, cancel := context.WithDeadline(ctx, time.Now().Add(10*time.Second)) defer cancel() got := Status(ctx, t, Target(), profile, "Host", profile) - if got != state.Stopped.String() { + if got != wantStatus { return fmt.Errorf("expected post-stop host status to be -%q- but got *%q*", state.Stopped, got) } return nil