diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 98a2142c79..55325fee80 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -116,6 +116,7 @@ const ( hostDNSResolver = "host-dns-resolver" waitUntilHealthy = "wait" force = "force" + dryRun = "dry-run" interactive = "interactive" waitTimeout = "wait-timeout" nativeSSH = "native-ssh" @@ -158,6 +159,7 @@ func initMinikubeFlags() { startCmd.Flags().Bool(force, false, "Force minikube to perform possibly dangerous operations") startCmd.Flags().Bool(interactive, true, "Allow user prompts for more information") + startCmd.Flags().Bool(dryRun, false, "dry-run mode. Validates configuration, but does does not mutate system state") startCmd.Flags().Int(cpus, 2, "Number of CPUs allocated to the minikube VM.") startCmd.Flags().String(memory, defaultMemorySize, "Amount of RAM allocated to the minikube VM (format: [], where unit = b, k, m or g).") @@ -310,7 +312,9 @@ func runStart(cmd *cobra.Command, args []string) { validateUser(driverName) // Download & update the driver, even in --download-only mode - updateDriver(driverName) + if !viper.GetBool(dryRun) { + updateDriver(driverName) + } k8sVersion, isUpgrade := getKubernetesVersion(existing) config, err := generateCfgFromFlags(cmd, k8sVersion, driverName) @@ -318,6 +322,12 @@ func runStart(cmd *cobra.Command, args []string) { exit.WithError("Failed to generate config", err) } + // This is about as far as we can go without overwriting config files + if viper.GetBool(dryRun) { + out.T(out.DryRun, `dry-run validation complete!`) + return + } + cacheISO(&config, driverName) if viper.GetBool(nativeSSH) { @@ -767,7 +777,7 @@ func validateDiskSize() { func validateMemorySize() { memorySizeMB := pkgutil.CalculateSizeInMB(viper.GetString(memory)) if memorySizeMB < pkgutil.CalculateSizeInMB(minimumMemorySize) && !viper.GetBool(force) { - exit.UsageT("Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}", out.V{"requested_size": memorySizeMB, "minimum_size": pkgutil.CalculateSizeInMB(minimumMemorySize)}) + exit.WithCodeT(exit.Config, "Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}", out.V{"requested_size": memorySizeMB, "minimum_size": pkgutil.CalculateSizeInMB(minimumMemorySize)}) } if memorySizeMB < pkgutil.CalculateSizeInMB(defaultMemorySize) && !viper.GetBool(force) { out.T(out.Notice, "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.", diff --git a/pkg/minikube/out/style.go b/pkg/minikube/out/style.go index b14c9d5883..3695ed4167 100644 --- a/pkg/minikube/out/style.go +++ b/pkg/minikube/out/style.go @@ -113,6 +113,7 @@ var styles = map[StyleEnum]style{ Unmount: {Prefix: "🔥 "}, MountOptions: {Prefix: "💾 "}, Fileserver: {Prefix: "🚀 ", OmitNewline: true}, + DryRun: {Prefix: "🏜️ "}, } // Add a prefix to a string diff --git a/pkg/minikube/out/style_enum.go b/pkg/minikube/out/style_enum.go index 0bc8ac1822..81e232b141 100644 --- a/pkg/minikube/out/style_enum.go +++ b/pkg/minikube/out/style_enum.go @@ -84,4 +84,5 @@ const ( Empty Workaround Sparkle + DryRun ) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index 2d41198dbd..1d20afa56e 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -92,6 +92,7 @@ func TestFunctional(t *testing.T) { {"ConfigCmd", validateConfigCmd}, {"DashboardCmd", validateDashboardCmd}, {"DNS", validateDNS}, + {"DryRun", validateDryRun}, {"StatusCmd", validateStatusCmd}, {"LogsCmd", validateLogsCmd}, {"MountCmd", validateMountCmd}, @@ -309,6 +310,32 @@ func validateDNS(ctx context.Context, t *testing.T, profile string) { } } +// validateDryRun asserts that the dry-run mode quickly exits with the right code +func validateDryRun(ctx context.Context, t *testing.T, profile string) { + // dry-run mode should always be able to finish quickly + mctx, cancel := context.WithTimeout(ctx, 2*time.Second) + defer cancel() + + // Too little memory! + startArgs := append([]string{"start", "-p", profile, "--dry-run", "--memory", "250MB"}, StartArgs()...) + c := exec.CommandContext(mctx, Target(), startArgs...) + rr, err := Run(t, c) + + wantCode := 78 // exit.Config + if rr.ExitCode != wantCode { + t.Errorf("dry-run(250MB) exit code = %d, wanted = %d: %v", rr.ExitCode, wantCode, err) + } + + dctx, cancel := context.WithTimeout(ctx, 2*time.Second) + defer cancel() + startArgs = append([]string{"start", "-p", profile, "--dry-run"}, StartArgs()...) + c = exec.CommandContext(dctx, Target(), startArgs...) + rr, err = Run(t, c) + if rr.ExitCode != 0 || err != nil { + t.Errorf("dry-run exit code = %d, wanted = %d: %v", rr.ExitCode, 0, err) + } +} + // validateCacheCmd tests functionality of cache command (cache add, delete, list) func validateCacheCmd(ctx context.Context, t *testing.T, profile string) { if NoneDriver() {