Merge pull request #9126 from tstromberg/error-code-for-everyone

Add a machine readable reason to all error paths
pull/9139/head^2
Thomas Strömberg 2020-08-31 22:34:24 -07:00 committed by GitHub
commit 51a3155cfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
134 changed files with 2947 additions and 1970 deletions

View File

@ -23,6 +23,7 @@ import (
"k8s.io/minikube/pkg/minikube/image"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/reason"
)
// cacheImageConfigKey is the config field name used to store which images we have previously cached
@ -43,11 +44,11 @@ var addCacheCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
// Cache and load images into docker daemon
if err := machine.CacheAndLoadImages(args); err != nil {
exit.WithError("Failed to cache and load images", err)
exit.Error(reason.InternalCacheLoad, "Failed to cache and load images", err)
}
// Add images to config file
if err := cmdConfig.AddToConfigMap(cacheImageConfigKey, args); err != nil {
exit.WithError("Failed to update config", err)
exit.Error(reason.InternalAddConfig, "Failed to update config", err)
}
},
}
@ -60,11 +61,11 @@ var deleteCacheCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
// Delete images from config file
if err := cmdConfig.DeleteFromConfigMap(cacheImageConfigKey, args); err != nil {
exit.WithError("Failed to delete images from config", err)
exit.Error(reason.InternalDelConfig, "Failed to delete images from config", err)
}
// Delete images from cache/images directory
if err := image.DeleteFromCacheDir(args); err != nil {
exit.WithError("Failed to delete images", err)
exit.Error(reason.HostDelCache, "Failed to delete images", err)
}
},
}
@ -77,7 +78,7 @@ var reloadCacheCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
err := node.CacheAndLoadImagesInConfig()
if err != nil {
exit.WithError("Failed to reload cached images", err)
exit.Error(reason.GuestCacheLoad, "Failed to reload cached images", err)
}
},
}

View File

@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
cmdConfig "k8s.io/minikube/cmd/minikube/cmd/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/reason"
)
const defaultCacheListFormat = "{{.CacheImage}}\n"
@ -42,10 +43,10 @@ var listCacheCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
images, err := cmdConfig.ListConfigMap(cacheImageConfigKey)
if err != nil {
exit.WithError("Failed to get image map", err)
exit.Error(reason.InternalListConfig, "Failed to get image map", err)
}
if err := cacheList(images); err != nil {
exit.WithError("Failed to list cached images", err)
exit.Error(reason.InternalCacheList, "Failed to list cached images", err)
}
},
}

View File

@ -25,6 +25,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
)
const longDescription = `
@ -73,27 +74,26 @@ var completionCmd = &cobra.Command{
Long: longDescription,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
exit.UsageT("Usage: minikube completion SHELL")
exit.Message(reason.Usage, "Usage: minikube completion SHELL")
}
if args[0] != "bash" && args[0] != "zsh" && args[0] != "fish" {
exit.UsageT("Sorry, completion support is not yet implemented for {{.name}}", out.V{"name": args[0]})
exit.Message(reason.Usage, "Sorry, completion support is not yet implemented for {{.name}}", out.V{"name": args[0]})
} else if args[0] == "bash" {
err := GenerateBashCompletion(os.Stdout, cmd.Parent())
if err != nil {
exit.WithError("bash completion failed", err)
exit.Error(reason.InternalCompletion, "bash completion failed", err)
}
} else if args[0] == "zsh" {
err := GenerateZshCompletion(os.Stdout, cmd.Parent())
if err != nil {
exit.WithError("zsh completion failed", err)
exit.Error(reason.InternalCompletion, "zsh completion failed", err)
}
} else {
err := GenerateFishCompletion(os.Stdout, cmd.Parent())
if err != nil {
exit.WithError("fish completion failed", err)
exit.Error(reason.InternalCompletion, "fish completion failed", err)
}
}
},
}

View File

@ -31,6 +31,8 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var addonListOutput string
@ -47,7 +49,7 @@ var addonsListCmd = &cobra.Command{
Long: "Lists all available minikube addons as well as their current statuses (enabled/disabled)",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 0 {
exit.UsageT("usage: minikube addons list")
exit.Message(reason.Usage, "usage: minikube addons list")
}
_, cc := mustload.Partial(ClusterFlagValue())
@ -57,7 +59,7 @@ var addonsListCmd = &cobra.Command{
case "json":
printAddonsJSON(cc)
default:
exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'list', 'json'", addonListOutput))
exit.Message(reason.Usage, fmt.Sprintf("invalid output format: %s. Valid values: 'list', 'json'", addonListOutput))
}
},
}
@ -115,7 +117,7 @@ var printAddonsList = func(cc *config.ClusterConfig) {
glog.Errorf("list profiles returned error: %v", err)
}
if len(v) > 1 {
out.T(out.Tip, "To see addons list for other profiles use: `minikube addons -p name list`")
out.T(style.Tip, "To see addons list for other profiles use: `minikube addons -p name list`")
}
}

View File

@ -25,7 +25,9 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/service"
"k8s.io/minikube/pkg/minikube/style"
)
var addonsConfigureCmd = &cobra.Command{
@ -34,7 +36,7 @@ var addonsConfigureCmd = &cobra.Command{
Long: "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list ",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
exit.UsageT("usage: minikube addons configure ADDON_NAME")
exit.Message(reason.Usage, "usage: minikube addons configure ADDON_NAME")
}
addon := args[0]
@ -123,7 +125,6 @@ var addonsConfigureCmd = &cobra.Command{
"cloud": "ecr",
"kubernetes.io/minikube-addons": "registry-creds",
})
if err != nil {
out.FailureT("ERROR creating `registry-creds-ecr` secret: {{.error}}", out.V{"error": err})
}
@ -204,7 +205,7 @@ var addonsConfigureCmd = &cobra.Command{
}
if err := config.SaveProfile(profile, cfg); err != nil {
out.ErrT(out.FatalType, "Failed to save config {{.profile}}", out.V{"profile": profile})
out.ErrT(style.Fatal, "Failed to save config {{.profile}}", out.V{"profile": profile})
}
default:

View File

@ -21,6 +21,8 @@ import (
"k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var addonsDisableCmd = &cobra.Command{
@ -29,18 +31,18 @@ var addonsDisableCmd = &cobra.Command{
Long: "Disables the addon w/ADDON_NAME within minikube (example: minikube addons disable dashboard). For a list of available addons use: minikube addons list ",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
exit.UsageT("usage: minikube addons disable ADDON_NAME")
exit.Message(reason.Usage, "usage: minikube addons disable ADDON_NAME")
}
addon := args[0]
if addon == "heapster" {
exit.WithCodeT(exit.Unavailable, "The heapster addon is depreciated. please try to disable metrics-server instead")
exit.Message(reason.AddonUnsupported, "The heapster addon is depreciated. please try to disable metrics-server instead")
}
err := addons.SetAndSave(ClusterFlagValue(), addon, "false")
if err != nil {
exit.WithError("disable failed", err)
exit.Error(reason.InternalDisable, "disable failed", err)
}
out.T(out.AddonDisable, `"The '{{.minikube_addon}}' addon is disabled`, out.V{"minikube_addon": addon})
out.T(style.AddonDisable, `"The '{{.minikube_addon}}' addon is disabled`, out.V{"minikube_addon": addon})
},
}

View File

@ -24,6 +24,8 @@ import (
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var addonsEnableCmd = &cobra.Command{
@ -32,24 +34,24 @@ var addonsEnableCmd = &cobra.Command{
Long: "Enables the addon w/ADDON_NAME within minikube (example: minikube addons enable dashboard). For a list of available addons use: minikube addons list ",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
exit.UsageT("usage: minikube addons enable ADDON_NAME")
exit.Message(reason.Usage, "usage: minikube addons enable ADDON_NAME")
}
addon := args[0]
// replace heapster as metrics-server because heapster is deprecated
if addon == "heapster" {
out.T(out.Waiting, "enable metrics-server addon instead of heapster addon because heapster is deprecated")
out.T(style.Waiting, "enable metrics-server addon instead of heapster addon because heapster is deprecated")
addon = "metrics-server"
}
err := addons.SetAndSave(ClusterFlagValue(), addon, "true")
if err != nil {
exit.WithError("enable failed", err)
exit.Error(reason.InternalEnable, "enable failed", err)
}
if addon == "dashboard" {
tipProfileArg := ""
if ClusterFlagValue() != constants.DefaultClusterName {
tipProfileArg = fmt.Sprintf(" -p %s", ClusterFlagValue())
}
out.T(out.Tip, `Some dashboard features require the metrics-server addon. To enable all features please run:
out.T(style.Tip, `Some dashboard features require the metrics-server addon. To enable all features please run:
minikube{{.profileArg}} addons enable metrics-server
@ -57,7 +59,7 @@ var addonsEnableCmd = &cobra.Command{
}
out.T(out.AddonEnable, "The '{{.addonName}}' addon is enabled", out.V{"addonName": addon})
out.T(style.AddonEnable, "The '{{.addonName}}' addon is enabled", out.V{"addonName": addon})
},
}

View File

@ -26,7 +26,9 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/service"
"k8s.io/minikube/pkg/minikube/style"
)
var (
@ -47,13 +49,13 @@ var addonsOpenCmd = &cobra.Command{
PreRun: func(cmd *cobra.Command, args []string) {
t, err := template.New("addonsURL").Parse(addonsURLFormat)
if err != nil {
exit.UsageT("The value passed to --format is invalid: {{.error}}", out.V{"error": err})
exit.Message(reason.Usage, "The value passed to --format is invalid: {{.error}}", out.V{"error": err})
}
addonsURLTemplate = t
},
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
exit.UsageT("usage: minikube addons open ADDON_NAME")
exit.Message(reason.Usage, "usage: minikube addons open ADDON_NAME")
}
addonName := args[0]
@ -62,14 +64,14 @@ var addonsOpenCmd = &cobra.Command{
addon, ok := assets.Addons[addonName] // validate addon input
if !ok {
exit.WithCodeT(exit.Data, `addon '{{.name}}' is not a valid addon packaged with minikube.
exit.Message(reason.Usage, `addon '{{.name}}' is not a valid addon packaged with minikube.
To see the list of available addons run:
minikube addons list`, out.V{"name": addonName})
}
enabled := addon.IsEnabled(co.Config)
if !enabled {
exit.WithCodeT(exit.Unavailable, `addon '{{.name}}' is currently not enabled.
exit.Message(reason.AddonNotEnabled, `addon '{{.name}}' is currently not enabled.
To enable this addon run:
minikube addons enable {{.name}}`, out.V{"name": addonName})
}
@ -79,10 +81,10 @@ minikube addons enable {{.name}}`, out.V{"name": addonName})
serviceList, err := service.GetServiceListByLabel(cname, namespace, key, addonName)
if err != nil {
exit.WithCodeT(exit.Unavailable, "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}", out.V{"namespace": namespace, "labelName": key, "addonName": addonName, "error": err})
exit.Message(reason.SvcList, "Error getting service with namespace: {{.namespace}} and labels {{.labelName}}:{{.addonName}}: {{.error}}", out.V{"namespace": namespace, "labelName": key, "addonName": addonName, "error": err})
}
if len(serviceList.Items) == 0 {
exit.WithCodeT(exit.Config, `This addon does not have an endpoint defined for the 'addons open' command.
exit.Message(reason.SvcNotFound, `This addon does not have an endpoint defined for the 'addons open' command.
You can add one by annotating a service with the label {{.labelName}}:{{.addonName}}`, out.V{"labelName": key, "addonName": addonName})
}
for i := range serviceList.Items {
@ -90,14 +92,14 @@ You can add one by annotating a service with the label {{.labelName}}:{{.addonNa
var urlString []string
if urlString, err = service.WaitForService(co.API, co.Config.Name, namespace, svc, addonsURLTemplate, addonsURLMode, https, wait, interval); err != nil {
exit.WithCodeT(exit.Unavailable, "Wait failed: {{.error}}", out.V{"error": err})
exit.Message(reason.SvcTimeout, "Wait failed: {{.error}}", out.V{"error": err})
}
if len(urlString) != 0 {
out.T(out.Celebrate, "Opening Kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": svc})
out.T(style.Celebrate, "Opening Kubernetes service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": svc})
for _, url := range urlString {
if err := browser.OpenURL(url); err != nil {
exit.WithError(fmt.Sprintf("browser failed to open url %s", url), err)
exit.Error(reason.HostBrowser, fmt.Sprintf("browser failed to open url %s", url), err)
}
}
}

View File

@ -25,6 +25,8 @@ import (
"k8s.io/minikube/pkg/minikube/kubeconfig"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
// ProfileCmd represents the profile command
@ -35,26 +37,26 @@ var ProfileCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
profile := ClusterFlagValue()
out.T(out.Empty, profile)
out.T(style.Empty, profile)
os.Exit(0)
}
if len(args) > 1 {
exit.UsageT("usage: minikube profile [MINIKUBE_PROFILE_NAME]")
exit.Message(reason.Usage, "usage: minikube profile [MINIKUBE_PROFILE_NAME]")
}
profile := args[0]
// Check whether the profile name is container friendly
if !config.ProfileNameValid(profile) {
out.WarningT("Profile name '{{.profilename}}' is not valid", out.V{"profilename": profile})
exit.UsageT("Only alphanumeric and dashes '-' are permitted. Minimum 1 character, starting with alphanumeric.")
exit.Message(reason.Usage, "Only alphanumeric and dashes '-' are permitted. Minimum 1 character, starting with alphanumeric.")
}
/**
we need to add code over here to check whether the profile
name is in the list of reserved keywords
*/
if config.ProfileNameInReservedKeywords(profile) {
exit.WithCodeT(exit.Config, `Profile name "{{.profilename}}" is reserved keyword. To delete this profile, run: "{{.cmd}}"`, out.V{"profilename": profile, "cmd": mustload.ExampleCmd(profile, "delete")})
exit.Message(reason.InternalReservedProfile, `Profile name "{{.profilename}}" is reserved keyword. To delete this profile, run: "{{.cmd}}"`, out.V{"profilename": profile, "cmd": mustload.ExampleCmd(profile, "delete")})
}
if profile == "default" {
@ -68,18 +70,18 @@ var ProfileCmd = &cobra.Command{
}
if !config.ProfileExists(profile) {
out.ErrT(out.Tip, `if you want to create a profile you can by this command: minikube start -p {{.profile_name}}`, out.V{"profile_name": profile})
out.ErrT(style.Tip, `if you want to create a profile you can by this command: minikube start -p {{.profile_name}}`, out.V{"profile_name": profile})
os.Exit(0)
}
err := Set(config.ProfileName, profile)
if err != nil {
exit.WithError("Setting profile failed", err)
exit.Error(reason.InternalConfigSet, "Setting profile failed", err)
}
cc, err := config.Load(profile)
// might err when loading older version of cfg file that doesn't have KeepContext field
if err != nil && !config.IsNotExist(err) {
out.ErrT(out.Sad, `Error loading profile config: {{.error}}`, out.V{"error": err})
out.ErrT(style.Sad, `Error loading profile config: {{.error}}`, out.V{"error": err})
}
if err == nil {
if cc.KeepContext {
@ -88,7 +90,7 @@ var ProfileCmd = &cobra.Command{
} else {
err := kubeconfig.SetCurrentContext(profile, kubeconfig.PathFromEnv())
if err != nil {
out.ErrT(out.Sad, `Error while setting kubectl current context : {{.error}}`, out.V{"error": err})
out.ErrT(style.Sad, `Error while setting kubectl current context : {{.error}}`, out.V{"error": err})
}
}
out.SuccessT("minikube profile was successfully set to {{.profile_name}}", out.V{"profile_name": profile})

View File

@ -28,36 +28,33 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
"github.com/golang/glog"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
)
var (
output string
)
var output string
var profileListCmd = &cobra.Command{
Use: "list",
Short: "Lists all minikube profiles.",
Long: "Lists all valid minikube profiles and detects all possible invalid profiles.",
Run: func(cmd *cobra.Command, args []string) {
switch strings.ToLower(output) {
case "json":
printProfilesJSON()
case "table":
printProfilesTable()
default:
exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'table', 'json'", output))
exit.Message(reason.Usage, fmt.Sprintf("invalid output format: %s. Valid values: 'table', 'json'", output))
}
},
}
var printProfilesTable = func() {
var validData [][]string
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Profile", "VM Driver", "Runtime", "IP", "Port", "Version", "Status"})
@ -67,7 +64,7 @@ var printProfilesTable = func() {
validProfiles, invalidProfiles, err := config.ListProfiles()
if len(validProfiles) == 0 || err != nil {
exit.UsageT("No minikube profile was found. You can create one using `minikube start`.")
exit.Message(reason.Usage, "No minikube profile was found. You can create one using `minikube start`.")
}
api, err := machine.NewAPIClient()
if err != nil {
@ -78,7 +75,7 @@ var printProfilesTable = func() {
for _, p := range validProfiles {
cp, err := config.PrimaryControlPlane(p.Config)
if err != nil {
exit.WithError("error getting primary control plane", err)
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
}
p.Status, err = machine.Status(api, driver.MachineName(*p.Config, cp))
if err != nil {
@ -93,9 +90,9 @@ var printProfilesTable = func() {
if invalidProfiles != nil {
out.WarningT("Found {{.number}} invalid profile(s) ! ", out.V{"number": len(invalidProfiles)})
for _, p := range invalidProfiles {
out.ErrT(out.Empty, "\t "+p.Name)
out.ErrT(style.Empty, "\t "+p.Name)
}
out.ErrT(out.Tip, "You can delete them using the following command(s): ")
out.ErrT(style.Tip, "You can delete them using the following command(s): ")
for _, p := range invalidProfiles {
out.Err(fmt.Sprintf("\t $ minikube delete -p %s \n", p.Name))
}
@ -105,7 +102,6 @@ var printProfilesTable = func() {
if err != nil {
glog.Warningf("error loading profiles: %v", err)
}
}
var printProfilesJSON = func() {
@ -119,7 +115,7 @@ var printProfilesJSON = func() {
for _, v := range validProfiles {
cp, err := config.PrimaryControlPlane(v.Config)
if err != nil {
exit.WithError("error getting primary control plane", err)
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
}
status, err := machine.Status(api, driver.MachineName(*v.Config, cp))
if err != nil {
@ -143,7 +139,7 @@ var printProfilesJSON = func() {
invalid = []*config.Profile{}
}
var body = map[string]interface{}{}
body := map[string]interface{}{}
if err == nil || config.IsNotExist(err) {
body["valid"] = valid
@ -154,7 +150,7 @@ var printProfilesJSON = func() {
body["error"] = err
jsonString, _ := json.Marshal(body)
out.String(string(jsonString))
os.Exit(exit.Failure)
os.Exit(reason.ExGuestError)
}
}

View File

@ -23,6 +23,7 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
)
var configSetCmd = &cobra.Command{
@ -32,14 +33,14 @@ var configSetCmd = &cobra.Command{
These values can be overwritten by flags or environment variables at runtime.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 2 {
exit.UsageT("not enough arguments ({{.ArgCount}}).\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE", out.V{"ArgCount": len(args)})
exit.Message(reason.Usage, "not enough arguments ({{.ArgCount}}).\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE", out.V{"ArgCount": len(args)})
}
if len(args) > 2 {
exit.UsageT("toom any arguments ({{.ArgCount}}).\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE", out.V{"ArgCount": len(args)})
exit.Message(reason.Usage, "toom any arguments ({{.ArgCount}}).\nusage: minikube config set PROPERTY_NAME PROPERTY_VALUE", out.V{"ArgCount": len(args)})
}
err := Set(args[0], args[1])
if err != nil {
exit.WithError("Set failed", err)
exit.Error(reason.InternalConfigSet, "Set failed", err)
}
},
}

View File

@ -21,6 +21,7 @@ import (
config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/reason"
)
var configUnsetCmd = &cobra.Command{
@ -29,11 +30,11 @@ var configUnsetCmd = &cobra.Command{
Long: "unsets PROPERTY_NAME from the minikube config file. Can be overwritten by flags or environmental variables",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
exit.UsageT("usage: minikube config unset PROPERTY_NAME")
exit.Message(reason.Usage, "usage: minikube config unset PROPERTY_NAME")
}
err := Unset(args[0])
if err != nil {
exit.WithError("unset failed", err)
exit.Error(reason.InternalConfigUnset, "unset failed", err)
}
},
}

View File

@ -24,6 +24,7 @@ import (
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/reason"
)
const defaultConfigViewFormat = "- {{.ConfigKey}}: {{.ConfigValue}}\n"
@ -43,7 +44,7 @@ var configViewCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
err := View()
if err != nil {
exit.WithError("config view failed", err)
exit.Error(reason.InternalConfigView, "config view failed", err)
}
},
}
@ -64,12 +65,12 @@ func View() error {
for k, v := range cfg {
tmpl, err := template.New("view").Parse(viewFormat)
if err != nil {
exit.WithError("Error creating view template", err)
exit.Error(reason.InternalViewTmpl, "Error creating view template", err)
}
viewTmplt := ViewTemplate{k, v}
err = tmpl.Execute(os.Stdout, viewTmplt)
if err != nil {
exit.WithError("Error executing view template", err)
exit.Error(reason.InternalViewExec, "Error executing view template", err)
}
}
return nil

View File

@ -31,12 +31,14 @@ import (
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/browser"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/proxy"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/service"
"k8s.io/minikube/pkg/util/retry"
)
@ -72,47 +74,47 @@ var dashboardCmd = &cobra.Command{
if !enabled {
// Send status messages to stderr for folks re-using this output.
out.ErrT(out.Enabling, "Enabling dashboard ...")
out.ErrT(style.Enabling, "Enabling dashboard ...")
// Enable the dashboard add-on
err = addons.SetAndSave(cname, "dashboard", "true")
if err != nil {
exit.WithError("Unable to enable dashboard", err)
exit.Error(reason.InternalAddonEnable, "Unable to enable dashboard", err)
}
}
ns := "kubernetes-dashboard"
svc := "kubernetes-dashboard"
out.ErrT(out.Verifying, "Verifying dashboard health ...")
out.ErrT(style.Verifying, "Verifying dashboard health ...")
checkSVC := func() error { return service.CheckService(cname, ns, svc) }
// for slow machines or parallels in CI to avoid #7503
if err = retry.Expo(checkSVC, 100*time.Microsecond, time.Minute*10); err != nil {
exit.WithCodeT(exit.Unavailable, "dashboard service is not running: {{.error}}", out.V{"error": err})
exit.Message(reason.SvcCheckTimeout, "dashboard service is not running: {{.error}}", out.V{"error": err})
}
out.ErrT(out.Launch, "Launching proxy ...")
out.ErrT(style.Launch, "Launching proxy ...")
p, hostPort, err := kubectlProxy(kubectlVersion, cname)
if err != nil {
exit.WithError("kubectl proxy", err)
exit.Error(reason.HostKubectlProxy, "kubectl proxy", err)
}
url := dashboardURL(hostPort, ns, svc)
out.ErrT(out.Verifying, "Verifying proxy health ...")
out.ErrT(style.Verifying, "Verifying proxy health ...")
chkURL := func() error { return checkURL(url) }
if err = retry.Expo(chkURL, 100*time.Microsecond, 10*time.Minute); err != nil {
exit.WithCodeT(exit.Unavailable, "{{.url}} is not accessible: {{.error}}", out.V{"url": url, "error": err})
exit.Message(reason.SvcURLTimeout, "{{.url}} is not accessible: {{.error}}", out.V{"url": url, "error": err})
}
//check if current user is root
// check if current user is root
user, err := user.Current()
if err != nil {
exit.WithError("Unable to get current user", err)
exit.Error(reason.HostCurrentUser, "Unable to get current user", err)
}
if dashboardURLMode || user.Uid == "0" {
out.Ln(url)
} else {
out.T(out.Celebrate, "Opening {{.url}} in your default browser...", out.V{"url": url})
out.T(style.Celebrate, "Opening {{.url}} in your default browser...", out.V{"url": url})
if err = browser.OpenURL(url); err != nil {
exit.WithCodeT(exit.Software, "failed to open browser: {{.error}}", out.V{"error": err})
exit.Message(reason.HostBrowser, "failed to open browser: {{.error}}", out.V{"error": err})
}
}

View File

@ -45,10 +45,14 @@ import (
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var deleteAll bool
var purge bool
var (
deleteAll bool
purge bool
)
// deleteCmd represents the delete command
var deleteCmd = &cobra.Command{
@ -85,7 +89,7 @@ func init() {
deleteCmd.Flags().BoolVar(&purge, "purge", false, "Set this flag to delete the '.minikube' folder from your user directory.")
if err := viper.BindPFlags(deleteCmd.Flags()); err != nil {
exit.WithError("unable to bind flags", err)
exit.Error(reason.InternalBindFlags, "unable to bind flags", err)
}
RootCmd.AddCommand(deleteCmd)
}
@ -124,9 +128,9 @@ func deleteContainersAndVolumes(ociBin string) {
// runDelete handles the executes the flow of "minikube delete"
func runDelete(cmd *cobra.Command, args []string) {
if len(args) > 0 {
exit.UsageT("Usage: minikube delete")
exit.Message(reason.Usage, "Usage: minikube delete")
}
//register.SetEventLogPath(localpath.EventLog(ClusterFlagValue()))
// register.SetEventLogPath(localpath.EventLog(ClusterFlagValue()))
register.Reg.SetStep(register.Deleting)
validProfiles, invalidProfiles, err := config.ListProfiles()
@ -137,11 +141,11 @@ func runDelete(cmd *cobra.Command, args []string) {
// in the case user has more than 1 profile and runs --purge
// to prevent abandoned VMs/containers, force user to run with delete --all
if purge && len(profilesToDelete) > 1 && !deleteAll {
out.ErrT(out.Notice, "Multiple minikube profiles were found - ")
out.ErrT(style.Notice, "Multiple minikube profiles were found - ")
for _, p := range profilesToDelete {
out.T(out.Notice, " - {{.profile}}", out.V{"profile": p.Name})
out.T(style.Notice, " - {{.profile}}", out.V{"profile": p.Name})
}
exit.UsageT("Usage: minikube delete --all --purge")
exit.Message(reason.Usage, "Usage: minikube delete --all --purge")
}
if deleteAll {
@ -154,11 +158,11 @@ func runDelete(cmd *cobra.Command, args []string) {
if len(errs) > 0 {
HandleDeletionErrors(errs)
} else {
out.T(out.DeletingHost, "Successfully deleted all profiles")
out.T(style.DeletingHost, "Successfully deleted all profiles")
}
} else {
if len(args) > 0 {
exit.UsageT("usage: minikube delete")
exit.Message(reason.Usage, "usage: minikube delete")
}
cname := ClusterFlagValue()
@ -166,7 +170,7 @@ func runDelete(cmd *cobra.Command, args []string) {
orphan := false
if err != nil {
out.ErrT(out.Meh, `"{{.name}}" profile does not exist, trying anyways.`, out.V{"name": cname})
out.ErrT(style.Meh, `"{{.name}}" profile does not exist, trying anyways.`, out.V{"name": cname})
orphan = true
}
@ -193,9 +197,9 @@ func runDelete(cmd *cobra.Command, args []string) {
func purgeMinikubeDirectory() {
glog.Infof("Purging the '.minikube' directory located at %s", localpath.MiniPath())
if err := os.RemoveAll(localpath.MiniPath()); err != nil {
exit.WithError("unable to delete minikube config folder", err)
exit.Error(reason.HostPurge, "unable to delete minikube config folder", err)
}
out.T(out.Deleted, "Successfully purged minikube directory located at - [{{.minikubeDirectory}}]", out.V{"minikubeDirectory": localpath.MiniPath()})
out.T(style.Deleted, "Successfully purged minikube directory located at - [{{.minikubeDirectory}}]", out.V{"minikubeDirectory": localpath.MiniPath()})
}
// DeleteProfiles deletes one or more profiles
@ -204,7 +208,6 @@ func DeleteProfiles(profiles []*config.Profile) []error {
var errs []error
for _, profile := range profiles {
err := deleteProfile(profile)
if err != nil {
mm, loadErr := machine.LoadMachine(profile.Name)
@ -244,7 +247,7 @@ func deletePossibleKicLeftOver(cname string, driverName string) {
cs, err := oci.ListContainersByLabel(bin, delLabel)
if err == nil && len(cs) > 0 {
for _, c := range cs {
out.T(out.DeletingHost, `Deleting container "{{.name}}" ...`, out.V{"name": cname})
out.T(style.DeletingHost, `Deleting container "{{.name}}" ...`, out.V{"name": cname})
err := oci.DeleteContainer(bin, c)
if err != nil { // it will error if there is no container to delete
glog.Errorf("error deleting container %q. You may want to delete it manually :\n%v", cname, err)
@ -279,7 +282,7 @@ func deleteProfile(profile *config.Profile) error {
// if driver is oci driver, delete containers and volumes
if driver.IsKIC(profile.Config.Driver) {
out.T(out.DeletingHost, `Deleting "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": profile.Name, "driver_name": profile.Config.Driver})
out.T(style.DeletingHost, `Deleting "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": profile.Name, "driver_name": profile.Config.Driver})
for _, n := range profile.Config.Nodes {
machineName := driver.MachineName(*profile.Config, n)
deletePossibleKicLeftOver(machineName, profile.Config.Driver)
@ -330,7 +333,7 @@ func deleteProfile(profile *config.Profile) error {
if err := deleteContext(profile.Name); err != nil {
return err
}
out.T(out.Deleted, `Removed all traces of the "{{.name}}" cluster.`, out.V{"name": profile.Name})
out.T(style.Deleted, `Removed all traces of the "{{.name}}" cluster.`, out.V{"name": profile.Name})
return nil
}
@ -346,7 +349,7 @@ func deleteHosts(api libmachine.API, cc *config.ClusterConfig) {
glog.Infof("Host %s does not exist. Proceeding ahead with cleanup.", machineName)
default:
out.FailureT("Failed to delete cluster: {{.error}}", out.V{"error": err})
out.T(out.Notice, `You may need to manually remove the "{{.name}}" VM from your hypervisor`, out.V{"name": machineName})
out.T(style.Notice, `You may need to manually remove the "{{.name}}" VM from your hypervisor`, out.V{"name": machineName})
}
}
}
@ -377,7 +380,7 @@ func deleteContext(machineName string) error {
}
func deleteInvalidProfile(profile *config.Profile) []error {
out.T(out.DeletingHost, "Trying to delete invalid profile {{.profile}}", out.V{"profile": profile.Name})
out.T(style.DeletingHost, "Trying to delete invalid profile {{.profile}}", out.V{"profile": profile.Name})
var errs []error
pathToProfile := config.ProfileFolderPath(profile.Name, localpath.MiniPath())
@ -403,7 +406,7 @@ func profileDeletionErr(cname string, additionalInfo string) error {
}
func uninstallKubernetes(api libmachine.API, cc config.ClusterConfig, n config.Node, bsName string) error {
out.T(out.Resetting, "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...", out.V{"kubernetes_version": cc.KubernetesConfig.KubernetesVersion, "bootstrapper_name": bsName})
out.T(style.Resetting, "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...", out.V{"kubernetes_version": cc.KubernetesConfig.KubernetesVersion, "bootstrapper_name": bsName})
host, err := machine.LoadHost(api, driver.MachineName(cc, n))
if err != nil {
return DeletionError{Err: fmt.Errorf("unable to load host: %v", err), Errtype: MissingCluster}
@ -453,19 +456,19 @@ func handleSingleDeletionError(err error) {
case Fatal:
out.FatalT(deletionError.Error())
case MissingProfile:
out.ErrT(out.Sad, deletionError.Error())
out.ErrT(style.Sad, deletionError.Error())
case MissingCluster:
out.ErrT(out.Meh, deletionError.Error())
out.ErrT(style.Meh, deletionError.Error())
default:
out.FatalT(deletionError.Error())
}
} else {
exit.WithError("Could not process error from failed deletion", err)
exit.Error(reason.GuestDeletion, "Could not process error from failed deletion", err)
}
}
func handleMultipleDeletionErrors(errors []error) {
out.ErrT(out.Sad, "Multiple errors deleting profiles")
out.ErrT(style.Sad, "Multiple errors deleting profiles")
for _, err := range errors {
deletionError, ok := err.(DeletionError)
@ -473,7 +476,7 @@ func handleMultipleDeletionErrors(errors []error) {
if ok {
glog.Errorln(deletionError.Error())
} else {
exit.WithError("Could not process errors from failed deletion", err)
exit.Error(reason.GuestDeletion, "Could not process errors from failed deletion", err)
}
}
}
@ -481,10 +484,10 @@ func handleMultipleDeletionErrors(errors []error) {
func deleteProfileDirectory(profile string) {
machineDir := filepath.Join(localpath.MiniPath(), "machines", profile)
if _, err := os.Stat(machineDir); err == nil {
out.T(out.DeletingHost, `Removing {{.directory}} ...`, out.V{"directory": machineDir})
out.T(style.DeletingHost, `Removing {{.directory}} ...`, out.V{"directory": machineDir})
err := os.RemoveAll(machineDir)
if err != nil {
exit.WithError("Unable to remove machine directory", err)
exit.Error(reason.GuestProfileDeletion, "Unable to remove machine directory", err)
}
}
}

View File

@ -38,6 +38,7 @@ import (
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/shell"
"k8s.io/minikube/pkg/minikube/sysinit"
)
@ -73,7 +74,7 @@ type EnvNoProxyGetter struct{}
func dockerShellCfgSet(ec DockerEnvConfig, envMap map[string]string) *DockerShellConfig {
profile := ec.profile
const usgPlz = "To point your shell to minikube's docker-daemon, run:"
var usgCmd = fmt.Sprintf("minikube -p %s docker-env", profile)
usgCmd := fmt.Sprintf("minikube -p %s docker-env", profile)
s := &DockerShellConfig{
Config: *shell.CfgSet(ec.EnvConfig, usgPlz, usgCmd),
}
@ -123,7 +124,7 @@ func isDockerActive(r command.Runner) bool {
func mustRestartDocker(name string, runner command.Runner) {
if err := sysinit.New(runner).Restart("docker"); err != nil {
exit.WithCodeT(exit.Unavailable, `The Docker service within '{{.name}}' is not active`, out.V{"name": name})
exit.Message(reason.RuntimeRestart, `The Docker service within '{{.name}}' is not active`, out.V{"name": name})
}
}
@ -139,7 +140,7 @@ var dockerEnvCmd = &cobra.Command{
if dockerUnset {
if err := dockerUnsetScript(DockerEnvConfig{EnvConfig: sh}, os.Stdout); err != nil {
exit.WithError("Error generating unset output", err)
exit.Error(reason.InternalEnvScript, "Error generating unset output", err)
}
return
}
@ -149,15 +150,15 @@ var dockerEnvCmd = &cobra.Command{
driverName := co.CP.Host.DriverName
if driverName == driver.None {
exit.UsageT(`'none' driver does not support 'minikube docker-env' command`)
exit.Message(reason.EnvDriverConflict, `'none' driver does not support 'minikube docker-env' command`)
}
if len(co.Config.Nodes) > 1 {
exit.WithCodeT(exit.BadUsage, `The docker-env command is incompatible with multi-node clusters. Use the 'registry' add-on: https://minikube.sigs.k8s.io/docs/handbook/registry/`)
exit.Message(reason.EnvMultiConflict, `The docker-env command is incompatible with multi-node clusters. Use the 'registry' add-on: https://minikube.sigs.k8s.io/docs/handbook/registry/`)
}
if co.Config.KubernetesConfig.ContainerRuntime != "docker" {
exit.WithCodeT(exit.BadUsage, `The docker-env command is only compatible with the "docker" runtime, but this cluster was configured to use the "{{.runtime}}" runtime.`,
exit.Message(reason.Usage, `The docker-env command is only compatible with the "docker" runtime, but this cluster was configured to use the "{{.runtime}}" runtime.`,
out.V{"runtime": co.Config.KubernetesConfig.ContainerRuntime})
}
@ -171,7 +172,7 @@ var dockerEnvCmd = &cobra.Command{
if driver.NeedsPortForward(driverName) {
port, err = oci.ForwardedPort(driverName, cname, port)
if err != nil {
exit.WithCodeT(exit.Failure, "Error getting port binding for '{{.driver_name}} driver: {{.error}}", out.V{"driver_name": driverName, "error": err})
exit.Message(reason.DrvPortForward, "Error getting port binding for '{{.driver_name}} driver: {{.error}}", out.V{"driver_name": driverName, "error": err})
}
}
@ -188,7 +189,7 @@ var dockerEnvCmd = &cobra.Command{
if ec.Shell == "" {
ec.Shell, err = shell.Detect()
if err != nil {
exit.WithError("Error detecting shell", err)
exit.Error(reason.InternalShellDetect, "Error detecting shell", err)
}
}
@ -208,7 +209,7 @@ var dockerEnvCmd = &cobra.Command{
}
if err := dockerSetScript(ec, os.Stdout); err != nil {
exit.WithError("Error generating set output", err)
exit.Error(reason.InternalDockerScript, "Error generating set output", err)
}
},
}

View File

@ -23,6 +23,8 @@ import (
"k8s.io/minikube/pkg/generate"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var path string
@ -35,18 +37,17 @@ var generateDocs = &cobra.Command{
Example: "minikube generate-docs --path <FOLDER_PATH>",
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
// if directory does not exist
docsPath, err := os.Stat(path)
if err != nil || !docsPath.IsDir() {
exit.UsageT("Unable to generate the documentation. Please ensure that the path specified is a directory, exists & you have permission to write to it.")
exit.Message(reason.Usage, "Unable to generate the documentation. Please ensure that the path specified is a directory, exists & you have permission to write to it.")
}
// generate docs
if err := generate.Docs(RootCmd, path); err != nil {
exit.WithError("Unable to generate docs", err)
exit.Error(reason.InternalGenerateDocs, "Unable to generate docs", err)
}
out.T(out.Documentation, "Docs have been saved at - {{.path}}", out.V{"path": path})
out.T(style.Documentation, "Docs have been saved at - {{.path}}", out.V{"path": path})
},
}

View File

@ -28,6 +28,7 @@ import (
"k8s.io/minikube/pkg/minikube/logs"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
)
const (
@ -55,17 +56,17 @@ var logsCmd = &cobra.Command{
bs, err := cluster.Bootstrapper(co.API, viper.GetString(cmdcfg.Bootstrapper), *co.Config, co.CP.Runner)
if err != nil {
exit.WithError("Error getting cluster bootstrapper", err)
exit.Error(reason.InternalBootstrapper, "Error getting cluster bootstrapper", err)
}
cr, err := cruntime.New(cruntime.Config{Type: co.Config.KubernetesConfig.ContainerRuntime, Runner: co.CP.Runner})
if err != nil {
exit.WithError("Unable to get runtime", err)
exit.Error(reason.InternalNewRuntime, "Unable to get runtime", err)
}
if followLogs {
err := logs.Follow(cr, bs, *co.Config, co.CP.Runner)
if err != nil {
exit.WithError("Follow", err)
exit.Error(reason.InternalLogFollow, "Follow", err)
}
return
}
@ -77,9 +78,9 @@ var logsCmd = &cobra.Command{
err = logs.Output(cr, bs, *co.Config, co.CP.Runner, numberOfLines)
if err != nil {
out.Ln("")
// Avoid exit.WithError, since it outputs the issue URL
// Avoid exit.Error, since it outputs the issue URL
out.WarningT("{{.error}}", out.V{"error": err})
os.Exit(exit.Unavailable)
os.Exit(reason.ExSvcError)
}
},
}

View File

@ -35,6 +35,8 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/third_party/go9p/ufs"
)
@ -46,15 +48,17 @@ const (
)
// placeholders for flag values
var mountIP string
var mountVersion string
var mountType string
var isKill bool
var uid string
var gid string
var mSize int
var options []string
var mode uint
var (
mountIP string
mountVersion string
mountType string
isKill bool
uid string
gid string
mSize int
options []string
mode uint
)
// supportedFilesystems is a map of filesystem types to not warn against.
var supportedFilesystems = map[string]bool{nineP: true}
@ -67,31 +71,31 @@ var mountCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
if isKill {
if err := killMountProcess(); err != nil {
exit.WithError("Error killing mount process", err)
exit.Error(reason.HostKillMountProc, "Error killing mount process", err)
}
os.Exit(0)
}
if len(args) != 1 {
exit.UsageT(`Please specify the directory to be mounted:
exit.Message(reason.Usage, `Please specify the directory to be mounted:
minikube mount <source directory>:<target directory> (example: "/host-home:/vm-home")`)
}
mountString := args[0]
idx := strings.LastIndex(mountString, ":")
if idx == -1 { // no ":" was present
exit.UsageT(`mount argument "{{.value}}" must be in form: <source directory>:<target directory>`, out.V{"value": mountString})
exit.Message(reason.Usage, `mount argument "{{.value}}" must be in form: <source directory>:<target directory>`, out.V{"value": mountString})
}
hostPath := mountString[:idx]
vmPath := mountString[idx+1:]
if _, err := os.Stat(hostPath); err != nil {
if os.IsNotExist(err) {
exit.WithCodeT(exit.NoInput, "Cannot find directory {{.path}} for mount", out.V{"path": hostPath})
exit.Message(reason.HostPathMissing, "Cannot find directory {{.path}} for mount", out.V{"path": hostPath})
} else {
exit.WithError("stat failed", err)
exit.Error(reason.HostPathStat, "stat failed", err)
}
}
if len(vmPath) == 0 || !strings.HasPrefix(vmPath, "/") {
exit.UsageT("Target directory {{.path}} must be an absolute path", out.V{"path": vmPath})
exit.Message(reason.Usage, "Target directory {{.path}} must be an absolute path", out.V{"path": vmPath})
}
var debugVal int
if glog.V(1) {
@ -100,7 +104,7 @@ var mountCmd = &cobra.Command{
co := mustload.Running(ClusterFlagValue())
if co.CP.Host.Driver.DriverName() == driver.None {
exit.UsageT(`'none' driver does not support 'minikube mount' command`)
exit.Message(reason.Usage, `'none' driver does not support 'minikube mount' command`)
}
var ip net.IP
@ -108,17 +112,17 @@ var mountCmd = &cobra.Command{
if mountIP == "" {
ip, err = cluster.HostIP(co.CP.Host)
if err != nil {
exit.WithError("Error getting the host IP address to use from within the VM", err)
exit.Error(reason.IfHostIP, "Error getting the host IP address to use from within the VM", err)
}
} else {
ip = net.ParseIP(mountIP)
if ip == nil {
exit.WithCodeT(exit.Data, "error parsing the input ip address for mount")
exit.Message(reason.IfMountIP, "error parsing the input ip address for mount")
}
}
port, err := getPort()
if err != nil {
exit.WithError("Error finding port for mount", err)
exit.Error(reason.IfMountPort, "Error finding port for mount", err)
}
cfg := &cluster.MountConfig{
@ -150,7 +154,7 @@ var mountCmd = &cobra.Command{
if driver.IsKIC(co.CP.Host.Driver.DriverName()) && runtime.GOOS != "linux" {
bindIP = "127.0.0.1"
}
out.T(out.Mounting, "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...", out.V{"sourcePath": hostPath, "destinationPath": vmPath})
out.T(style.Mounting, "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...", out.V{"sourcePath": hostPath, "destinationPath": vmPath})
out.Infof("Mount type: {{.name}}", out.V{"type": cfg.Type})
out.Infof("User ID: {{.userID}}", out.V{"userID": cfg.UID})
out.Infof("Group ID: {{.groupID}}", out.V{"groupID": cfg.GID})
@ -164,9 +168,9 @@ var mountCmd = &cobra.Command{
if cfg.Type == nineP {
wg.Add(1)
go func() {
out.T(out.Fileserver, "Userspace file server: ")
out.T(style.Fileserver, "Userspace file server: ")
ufs.StartServer(net.JoinHostPort(bindIP, strconv.Itoa(port)), debugVal, hostPath)
out.T(out.Stopped, "Userspace file server is shutdown")
out.T(style.Stopped, "Userspace file server is shutdown")
wg.Done()
}()
}
@ -176,22 +180,22 @@ var mountCmd = &cobra.Command{
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
for sig := range c {
out.T(out.Unmount, "Unmounting {{.path}} ...", out.V{"path": vmPath})
out.T(style.Unmount, "Unmounting {{.path}} ...", out.V{"path": vmPath})
err := cluster.Unmount(co.CP.Runner, vmPath)
if err != nil {
out.FailureT("Failed unmount: {{.error}}", out.V{"error": err})
}
exit.WithCodeT(exit.Interrupted, "Received {{.name}} signal", out.V{"name": sig})
exit.Message(reason.Interrupted, "Received {{.name}} signal", out.V{"name": sig})
}
}()
err = cluster.Mount(co.CP.Runner, ip.String(), vmPath, cfg)
if err != nil {
exit.WithError("mount failed", err)
exit.Error(reason.GuestMount, "mount failed", err)
}
out.T(out.SuccessType, "Successfully mounted {{.sourcePath}} to {{.destinationPath}}", out.V{"sourcePath": hostPath, "destinationPath": vmPath})
out.T(style.Success, "Successfully mounted {{.sourcePath}} to {{.destinationPath}}", out.V{"sourcePath": hostPath, "destinationPath": vmPath})
out.Ln("")
out.T(out.Notice, "NOTE: This process must stay alive for the mount to be accessible ...")
out.T(style.Notice, "NOTE: This process must stay alive for the mount to be accessible ...")
wg.Wait()
},
}
@ -203,7 +207,7 @@ func init() {
mountCmd.Flags().BoolVar(&isKill, "kill", false, "Kill the mount process spawned by minikube start")
mountCmd.Flags().StringVar(&uid, "uid", "docker", "Default user id used for the mount")
mountCmd.Flags().StringVar(&gid, "gid", "docker", "Default group id used for the mount")
mountCmd.Flags().UintVar(&mode, "mode", 0755, "File permissions used for the mount")
mountCmd.Flags().UintVar(&mode, "mode", 0o755, "File permissions used for the mount")
mountCmd.Flags().StringSliceVar(&options, "options", []string{}, "Additional mount options, such as cache=fscache")
mountCmd.Flags().IntVar(&mSize, "msize", defaultMsize, "The number of bytes to use for 9p packet payload")
}

View File

@ -19,6 +19,7 @@ package cmd
import (
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/reason"
)
// nodeCmd represents the set of node subcommands
@ -27,6 +28,6 @@ var nodeCmd = &cobra.Command{
Short: "Add, remove, or list additional nodes",
Long: "Operations on nodes",
Run: func(cmd *cobra.Command, args []string) {
exit.UsageT("Usage: minikube node [add|start|stop|delete|list]")
exit.Message(reason.Usage, "Usage: minikube node [add|start|stop|delete|list]")
},
}

View File

@ -25,12 +25,15 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var (
cp bool
worker bool
)
var nodeAddCmd = &cobra.Command{
Use: "add",
Short: "Adds a node to the given cluster.",
@ -45,7 +48,7 @@ var nodeAddCmd = &cobra.Command{
name := node.Name(len(cc.Nodes) + 1)
out.T(out.Happy, "Adding node {{.name}} to cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
out.T(style.Happy, "Adding node {{.name}} to cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
// TODO: Deal with parameters better. Ideally we should be able to acceot any node-specific minikube start params here.
n := config.Node{
@ -66,15 +69,15 @@ var nodeAddCmd = &cobra.Command{
if err := node.Add(cc, n, false); err != nil {
_, err := maybeDeleteAndRetry(cmd, *cc, n, nil, err)
if err != nil {
exit.WithError("failed to add node", err)
exit.Error(reason.GuestNodeAdd, "failed to add node", err)
}
}
if err := config.SaveProfile(cc.Name, cc); err != nil {
exit.WithError("failed to save config", err)
exit.Error(reason.HostSaveProfile, "failed to save config", err)
}
out.T(out.Ready, "Successfully added {{.name}} to {{.cluster}}!", out.V{"name": name, "cluster": cc.Name})
out.T(style.Ready, "Successfully added {{.name}} to {{.cluster}}!", out.V{"name": name, "cluster": cc.Name})
},
}

View File

@ -23,6 +23,8 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var nodeDeleteCmd = &cobra.Command{
@ -30,18 +32,17 @@ var nodeDeleteCmd = &cobra.Command{
Short: "Deletes a node from a cluster.",
Long: "Deletes a node from a cluster.",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
exit.UsageT("Usage: minikube node delete [name]")
exit.Message(reason.Usage, "Usage: minikube node delete [name]")
}
name := args[0]
co := mustload.Healthy(ClusterFlagValue())
out.T(out.DeletingHost, "Deleting node {{.name}} from cluster {{.cluster}}", out.V{"name": name, "cluster": co.Config.Name})
out.T(style.DeletingHost, "Deleting node {{.name}} from cluster {{.cluster}}", out.V{"name": name, "cluster": co.Config.Name})
n, err := node.Delete(*co.Config, name)
if err != nil {
exit.WithError("deleting node", err)
exit.Error(reason.GuestNodeDelete, "deleting node", err)
}
if driver.IsKIC(co.Config.Driver) {
@ -49,7 +50,7 @@ var nodeDeleteCmd = &cobra.Command{
deletePossibleKicLeftOver(machineName, co.Config.Driver)
}
out.T(out.Deleted, "Node {{.name}} was successfully deleted.", out.V{"name": name})
out.T(style.Deleted, "Node {{.name}} was successfully deleted.", out.V{"name": name})
},
}

View File

@ -25,6 +25,7 @@ import (
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/reason"
)
var nodeListCmd = &cobra.Command{
@ -33,7 +34,7 @@ var nodeListCmd = &cobra.Command{
Long: "List existing minikube nodes.",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 0 {
exit.UsageT("Usage: minikube node list")
exit.Message(reason.Usage, "Usage: minikube node list")
}
cname := ClusterFlagValue()

View File

@ -27,6 +27,8 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var nodeStartCmd = &cobra.Command{
@ -35,7 +37,7 @@ var nodeStartCmd = &cobra.Command{
Long: "Starts an existing stopped node in a cluster.",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
exit.UsageT("Usage: minikube node start [name]")
exit.Message(reason.Usage, "Usage: minikube node start [name]")
}
api, cc := mustload.Partial(ClusterFlagValue())
@ -43,18 +45,18 @@ var nodeStartCmd = &cobra.Command{
n, _, err := node.Retrieve(*cc, name)
if err != nil {
exit.WithError("retrieving node", err)
exit.Error(reason.GuestNodeRetrieve, "retrieving node", err)
}
machineName := driver.MachineName(*cc, *n)
if machine.IsRunning(api, machineName) {
out.T(out.Check, "{{.name}} is already running", out.V{"name": name})
out.T(style.Check, "{{.name}} is already running", out.V{"name": name})
os.Exit(0)
}
r, p, m, h, err := node.Provision(cc, n, false, viper.GetBool(deleteOnFailure))
if err != nil {
exit.WithError("provisioning host for node", err)
exit.Error(reason.GuestNodeProvision, "provisioning host for node", err)
}
s := node.Starter{
@ -71,11 +73,11 @@ var nodeStartCmd = &cobra.Command{
if err != nil {
_, err := maybeDeleteAndRetry(cmd, *cc, *n, nil, err)
if err != nil {
node.MaybeExitWithAdvice(err)
exit.WithError("failed to start node", err)
node.ExitIfFatal(err)
exit.Error(reason.GuestNodeStart, "failed to start node", err)
}
}
out.T(out.Happy, "Successfully started node {{.name}}!", out.V{"name": machineName})
out.T(style.Happy, "Successfully started node {{.name}}!", out.V{"name": machineName})
},
}

View File

@ -24,6 +24,8 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var nodeStopCmd = &cobra.Command{
@ -32,7 +34,7 @@ var nodeStopCmd = &cobra.Command{
Long: "Stops a node in a cluster.",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
exit.UsageT("Usage: minikube node stop [name]")
exit.Message(reason.Usage, "Usage: minikube node stop [name]")
}
name := args[0]
@ -40,7 +42,7 @@ var nodeStopCmd = &cobra.Command{
n, _, err := node.Retrieve(*cc, name)
if err != nil {
exit.WithError("retrieving node", err)
exit.Error(reason.GuestNodeRetrieve, "retrieving node", err)
}
machineName := driver.MachineName(*cc, *n)
@ -49,7 +51,7 @@ var nodeStopCmd = &cobra.Command{
if err != nil {
out.FatalT("Failed to stop node {{.name}}", out.V{"name": name})
}
out.T(out.Stopped, "Successfully stopped node {{.name}}", out.V{"name": machineName})
out.T(style.Stopped, "Successfully stopped node {{.name}}", out.V{"name": machineName})
},
}

View File

@ -33,6 +33,8 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
var (
@ -54,9 +56,9 @@ func runPause(cmd *cobra.Command, args []string) {
glog.Infof("namespaces: %v keys: %v", namespaces, viper.AllSettings())
if allNamespaces {
namespaces = nil //all
namespaces = nil // all
} else if len(namespaces) == 0 {
exit.WithCodeT(exit.BadUsage, "Use -A to specify all namespaces")
exit.Message(reason.Usage, "Use -A to specify all namespaces")
}
ids := []string{}
@ -68,35 +70,35 @@ func runPause(cmd *cobra.Command, args []string) {
name = co.Config.Name
}
out.T(out.Pause, "Pausing node {{.name}} ... ", out.V{"name": name})
out.T(style.Pause, "Pausing node {{.name}} ... ", out.V{"name": name})
host, err := machine.LoadHost(co.API, driver.MachineName(*co.Config, n))
if err != nil {
exit.WithError("Error getting host", err)
exit.Error(reason.GuestLoadHost, "Error getting host", err)
}
r, err := machine.CommandRunner(host)
if err != nil {
exit.WithError("Failed to get command runner", err)
exit.Error(reason.InternalCommandRunner, "Failed to get command runner", err)
}
cr, err := cruntime.New(cruntime.Config{Type: co.Config.KubernetesConfig.ContainerRuntime, Runner: r})
if err != nil {
exit.WithError("Failed runtime", err)
exit.Error(reason.InternalNewRuntime, "Failed runtime", err)
}
uids, err := cluster.Pause(cr, r, namespaces)
if err != nil {
exit.WithError("Pause", err)
exit.Error(reason.GuestPause, "Pause", err)
}
ids = append(ids, uids...)
}
register.Reg.SetStep(register.Done)
if namespaces == nil {
out.T(out.Unpause, "Paused {{.count}} containers", out.V{"count": len(ids)})
out.T(style.Unpause, "Paused {{.count}} containers", out.V{"count": len(ids)})
} else {
out.T(out.Unpause, "Paused {{.count}} containers in: {{.namespaces}}", out.V{"count": len(ids), "namespaces": strings.Join(namespaces, ", ")})
out.T(style.Unpause, "Paused {{.count}} containers in: {{.namespaces}}", out.V{"count": len(ids), "namespaces": strings.Join(namespaces, ", ")})
}
}

View File

@ -35,6 +35,7 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/shell"
)
@ -47,15 +48,13 @@ type PodmanShellConfig struct {
MinikubePodmanProfile string
}
var (
podmanUnset bool
)
var podmanUnset bool
// podmanShellCfgSet generates context variables for "podman-env"
func podmanShellCfgSet(ec PodmanEnvConfig, envMap map[string]string) *PodmanShellConfig {
profile := ec.profile
const usgPlz = "To point your shell to minikube's podman service, run:"
var usgCmd = fmt.Sprintf("minikube -p %s podman-env", profile)
usgCmd := fmt.Sprintf("minikube -p %s podman-env", profile)
s := &PodmanShellConfig{
Config: *shell.CfgSet(ec.EnvConfig, usgPlz, usgCmd),
}
@ -114,7 +113,7 @@ var podmanEnvCmd = &cobra.Command{
if podmanUnset {
if err := podmanUnsetScript(PodmanEnvConfig{EnvConfig: sh}, os.Stdout); err != nil {
exit.WithError("Error generating unset output", err)
exit.Error(reason.InternalEnvScript, "Error generating unset output", err)
}
return
}
@ -124,20 +123,20 @@ var podmanEnvCmd = &cobra.Command{
driverName := co.CP.Host.DriverName
if driverName == driver.None {
exit.UsageT(`'none' driver does not support 'minikube podman-env' command`)
exit.Message(reason.Usage, `'none' driver does not support 'minikube podman-env' command`)
}
if len(co.Config.Nodes) > 1 {
exit.WithCodeT(exit.BadUsage, `The podman-env command is incompatible with multi-node clusters. Use the 'registry' add-on: https://minikube.sigs.k8s.io/docs/handbook/registry/`)
exit.Message(reason.Usage, `The podman-env command is incompatible with multi-node clusters. Use the 'registry' add-on: https://minikube.sigs.k8s.io/docs/handbook/registry/`)
}
if ok := isPodmanAvailable(co.CP.Runner); !ok {
exit.WithCodeT(exit.Unavailable, `The podman service within '{{.cluster}}' is not active`, out.V{"cluster": cname})
exit.Message(reason.EnvPodmanUnavailable, `The podman service within '{{.cluster}}' is not active`, out.V{"cluster": cname})
}
client, err := createExternalSSHClient(co.CP.Host.Driver)
if err != nil {
exit.WithError("Error getting ssh client", err)
exit.Error(reason.IfSSHClient, "Error getting ssh client", err)
}
ec := PodmanEnvConfig{
@ -150,12 +149,12 @@ var podmanEnvCmd = &cobra.Command{
if ec.Shell == "" {
ec.Shell, err = shell.Detect()
if err != nil {
exit.WithError("Error detecting shell", err)
exit.Error(reason.InternalShellDetect, "Error detecting shell", err)
}
}
if err := podmanSetScript(ec, os.Stdout); err != nil {
exit.WithError("Error generating set output", err)
exit.Error(reason.InternalEnvScript, "Error generating set output", err)
}
},
}

View File

@ -34,6 +34,7 @@ import (
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/translate"
)
@ -56,15 +57,15 @@ var RootCmd = &cobra.Command{
Long: `minikube provisions and manages local Kubernetes clusters optimized for development workflows.`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
for _, path := range dirs {
if err := os.MkdirAll(path, 0777); err != nil {
exit.WithError("Error creating minikube directory", err)
if err := os.MkdirAll(path, 0o777); err != nil {
exit.Error(reason.HostHomeMkdir, "Error creating minikube directory", err)
}
}
logDir := pflag.Lookup("log_dir")
if !logDir.Changed {
if err := logDir.Value.Set(localpath.MakeMiniPath("logs")); err != nil {
exit.WithError("logdir set failed", err)
exit.Error(reason.InternalFlagSet, "logdir set failed", err)
}
}
},
@ -105,7 +106,7 @@ func Execute() {
if err := RootCmd.Execute(); err != nil {
// Cobra already outputs the error, typically because the user provided an unknown command.
os.Exit(exit.BadUsage)
os.Exit(reason.ExProgramUsage)
}
}
@ -143,7 +144,7 @@ func usageTemplate() string {
// by setting them directly, using values from viper when not passed in as args
func setFlagsUsingViper() {
for _, config := range []string{"alsologtostderr", "log_dir", "v"} {
var a = pflag.Lookup(config)
a := pflag.Lookup(config)
viper.SetDefault(a.Name, a.DefValue)
// If the flag is set, override viper value
if a.Changed {
@ -152,7 +153,7 @@ func setFlagsUsingViper() {
// Viper will give precedence first to calls to the Set command,
// then to values from the config.yml
if err := a.Value.Set(viper.GetString(a.Name)); err != nil {
exit.WithError(fmt.Sprintf("failed to set value for %q", a.Name), err)
exit.Error(reason.InternalFlagSet, fmt.Sprintf("failed to set value for %q", a.Name), err)
}
a.Changed = true
}
@ -229,10 +230,9 @@ func init() {
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
if err := viper.BindPFlags(RootCmd.PersistentFlags()); err != nil {
exit.WithError("Unable to bind flags", err)
exit.Error(reason.InternalBindFlags, "Unable to bind flags", err)
}
cobra.OnInitialize(initConfig)
}
// initConfig reads in config file and ENV variables if set.

View File

@ -40,7 +40,9 @@ import (
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/service"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/tunnel/kic"
)
@ -64,7 +66,7 @@ var serviceCmd = &cobra.Command{
PersistentPreRun: func(cmd *cobra.Command, args []string) {
t, err := template.New("serviceURL").Parse(serviceURLFormat)
if err != nil {
exit.WithError("The value passed to --format is invalid", err)
exit.Error(reason.InternalFormatUsage, "The value passed to --format is invalid", err)
}
serviceURLTemplate = t
@ -72,7 +74,7 @@ var serviceCmd = &cobra.Command{
},
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 || len(args) > 1 {
exit.UsageT("You must specify a service name")
exit.Message(reason.Usage, "You must specify a service name")
}
svc := args[0]
@ -84,10 +86,10 @@ var serviceCmd = &cobra.Command{
if err != nil {
var s *service.SVCNotFoundError
if errors.As(err, &s) {
exit.WithCodeT(exit.Data, `Service '{{.service}}' was not found in '{{.namespace}}' namespace.
exit.Message(reason.SvcNotFound, `Service '{{.service}}' was not found in '{{.namespace}}' namespace.
You may select another namespace by using 'minikube service {{.service}} -n <namespace>'. Or list out all the services using 'minikube service list'`, out.V{"service": svc, "namespace": namespace})
}
exit.WithError("Error opening service", err)
exit.Error(reason.SvcTimeout, "Error opening service", err)
}
if driver.NeedsPortForward(co.Config.Driver) {
@ -107,7 +109,6 @@ func init() {
serviceCmd.Flags().IntVar(&interval, "interval", service.DefaultInterval, "The initial time interval for each check that wait performs in seconds")
serviceCmd.PersistentFlags().StringVar(&serviceURLFormat, "format", defaultServiceFormatTemplate, "Format to output service URL in. This format will be applied to each url individually and they will be printed one at a time.")
}
func startKicServiceTunnel(svc, configName string) {
@ -116,12 +117,12 @@ func startKicServiceTunnel(svc, configName string) {
clientset, err := kapi.Client(configName)
if err != nil {
exit.WithError("error creating clientset", err)
exit.Error(reason.InternalKubernetesClient, "error creating clientset", err)
}
port, err := oci.ForwardedPort(oci.Docker, configName, 22)
if err != nil {
exit.WithError("error getting ssh port", err)
exit.Error(reason.DrvPortForward, "error getting ssh port", err)
}
sshPort := strconv.Itoa(port)
sshKey := filepath.Join(localpath.MiniPath(), "machines", configName, "id_rsa")
@ -129,7 +130,7 @@ func startKicServiceTunnel(svc, configName string) {
serviceTunnel := kic.NewServiceTunnel(sshPort, sshKey, clientset.CoreV1())
urls, err := serviceTunnel.Start(svc, namespace)
if err != nil {
exit.WithError("error starting tunnel", err)
exit.Error(reason.SvcTunnelStart, "error starting tunnel", err)
}
// wait for tunnel to come up
@ -145,7 +146,7 @@ func startKicServiceTunnel(svc, configName string) {
err = serviceTunnel.Stop()
if err != nil {
exit.WithError("error stopping tunnel", err)
exit.Error(reason.SvcTunnelStop, "error stopping tunnel", err)
}
}
@ -163,9 +164,9 @@ func openURLs(svc string, urls []string) {
continue
}
out.T(out.Celebrate, "Opening service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": svc})
out.T(style.Celebrate, "Opening service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": svc})
if err := browser.OpenURL(u); err != nil {
exit.WithError(fmt.Sprintf("open url failed: %s", u), err)
exit.Error(reason.HostBrowser, fmt.Sprintf("open url failed: %s", u), err)
}
}
}

View File

@ -24,10 +24,11 @@ import (
"github.com/spf13/cobra"
core "k8s.io/api/core/v1"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/service"
"k8s.io/minikube/pkg/minikube/style"
)
var serviceListNamespace string
@ -43,8 +44,8 @@ var serviceListCmd = &cobra.Command{
serviceURLs, err := service.GetServiceURLs(co.API, co.Config.Name, serviceListNamespace, serviceURLTemplate)
if err != nil {
out.FatalT("Failed to get service URL: {{.error}}", out.V{"error": err})
out.ErrT(out.Notice, "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.")
os.Exit(exit.Unavailable)
out.ErrT(style.Notice, "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.")
os.Exit(reason.ExSvcUnavailable)
}
var data [][]string

View File

@ -28,11 +28,10 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
)
var (
nativeSSHClient bool
)
var nativeSSHClient bool
// sshCmd represents the docker-ssh command
var sshCmd = &cobra.Command{
@ -43,7 +42,7 @@ var sshCmd = &cobra.Command{
cname := ClusterFlagValue()
co := mustload.Running(cname)
if co.CP.Host.DriverName == driver.None {
exit.UsageT("'none' driver does not support 'minikube ssh' command")
exit.Message(reason.Usage, "'none' driver does not support 'minikube ssh' command")
}
var err error
@ -53,7 +52,7 @@ var sshCmd = &cobra.Command{
} else {
n, _, err = node.Retrieve(*co.Config, nodeName)
if err != nil {
exit.WithCodeT(exit.Unavailable, "Node {{.nodeName}} does not exist.", out.V{"nodeName": nodeName})
exit.Message(reason.GuestNodeRetrieve, "Node {{.nodeName}} does not exist.", out.V{"nodeName": nodeName})
}
}
@ -62,7 +61,7 @@ var sshCmd = &cobra.Command{
// This is typically due to a non-zero exit code, so no need for flourish.
out.ErrLn("ssh: %v", err)
// It'd be nice if we could pass up the correct error code here :(
os.Exit(exit.Failure)
os.Exit(1)
}
},
}

View File

@ -57,6 +57,8 @@ import (
"k8s.io/minikube/pkg/minikube/notify"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/registry"
"k8s.io/minikube/pkg/minikube/translate"
@ -77,7 +79,7 @@ func init() {
initDriverFlags()
initNetworkingFlags()
if err := viper.BindPFlags(startCmd.Flags()); err != nil {
exit.WithError("unable to bind flags", err)
exit.Error(reason.InternalBindFlags, "unable to bind flags", err)
}
}
@ -152,11 +154,11 @@ func runStart(cmd *cobra.Command, args []string) {
if !config.ProfileNameValid(ClusterFlagValue()) {
out.WarningT("Profile name '{{.name}}' is not valid", out.V{"name": ClusterFlagValue()})
exit.UsageT("Only alphanumeric and dashes '-' are permitted. Minimum 1 character, starting with alphanumeric.")
exit.Message(reason.Usage, "Only alphanumeric and dashes '-' are permitted. Minimum 1 character, starting with alphanumeric.")
}
existing, err := config.Load(ClusterFlagValue())
if err != nil && !config.IsNotExist(err) {
exit.WithCodeT(exit.Data, "Unable to load config: {{.error}}", out.V{"error": err})
exit.Message(reason.HostConfigLoad, "Unable to load config: {{.error}}", out.V{"error": err})
}
if existing != nil {
@ -168,11 +170,11 @@ func runStart(cmd *cobra.Command, args []string) {
ds, alts, specified := selectDriver(existing)
starter, err := provisionWithDriver(cmd, ds, existing)
if err != nil {
node.MaybeExitWithAdvice(err)
node.ExitIfFatal(err)
machine.MaybeDisplayAdvice(err, ds.Name)
if specified {
// If the user specified a driver, don't fallback to anything else
exit.WithError("error provisioning host", err)
exit.Error(reason.GuestProvision, "error provisioning host", err)
} else {
success := false
// Walk down the rest of the options
@ -199,7 +201,7 @@ func runStart(cmd *cobra.Command, args []string) {
}
}
if !success {
exit.WithError("error provisioning host", err)
exit.Error(reason.GuestProvision, "error provisioning host", err)
}
}
}
@ -211,20 +213,19 @@ func runStart(cmd *cobra.Command, args []string) {
stopProfile(existing.Name)
starter, err = provisionWithDriver(cmd, ds, existing)
if err != nil {
exit.WithError("error provisioning host", err)
exit.Error(reason.GuestProvision, "error provisioning host", err)
}
}
kubeconfig, err := startWithDriver(cmd, starter, existing)
if err != nil {
node.MaybeExitWithAdvice(err)
exit.WithError("failed to start node", err)
node.ExitIfFatal(err)
exit.Error(reason.GuestStart, "failed to start node", err)
}
if err := showKubectlInfo(kubeconfig, starter.Node.KubernetesVersion, starter.Cfg.Name); err != nil {
glog.Errorf("kubectl info: %v", err)
}
}
func provisionWithDriver(cmd *cobra.Command, ds registry.DriverState, existing *config.ClusterConfig) (node.Starter, error) {
@ -252,7 +253,7 @@ func provisionWithDriver(cmd *cobra.Command, ds registry.DriverState, existing *
// 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!`)
out.T(style.DryRun, `dry-run validation complete!`)
os.Exit(0)
}
@ -313,7 +314,7 @@ func startWithDriver(cmd *cobra.Command, starter node.Starter, existing *config.
}
if numNodes > 1 {
if driver.BareMetal(starter.Cfg.Driver) {
exit.WithCodeT(exit.Config, "The none driver is not compatible with multi-node clusters.")
exit.Message(reason.DrvUnsupportedMulti, "The none driver is not compatible with multi-node clusters.")
} else {
// Only warn users on first start.
if existing == nil {
@ -352,7 +353,7 @@ func startWithDriver(cmd *cobra.Command, starter node.Starter, existing *config.
func warnAboutMultiNode() {
out.WarningT("Multi-node clusters are currently experimental and might exhibit unintended behavior.")
out.T(out.Documentation, "To track progress on multi-node clusters, see https://github.com/kubernetes/minikube/issues/7538.")
out.T(style.Documentation, "To track progress on multi-node clusters, see https://github.com/kubernetes/minikube/issues/7538.")
}
func updateDriver(driverName string) {
@ -371,7 +372,7 @@ func displayVersion(version string) {
}
register.Reg.SetStep(register.InitialSetup)
out.T(out.Happy, "{{.prefix}}minikube {{.version}} on {{.platform}}", out.V{"prefix": prefix, "version": version, "platform": platform()})
out.T(style.Happy, "{{.prefix}}minikube {{.version}} on {{.platform}}", out.V{"prefix": prefix, "version": version, "platform": platform()})
}
// displayEnviron makes the user aware of environment variables that will affect how minikube operates
@ -389,16 +390,16 @@ func displayEnviron(env []string) {
func showKubectlInfo(kcs *kubeconfig.Settings, k8sVersion string, machineName string) error {
register.Reg.SetStep(register.Done)
if kcs.KeepContext {
out.T(out.Kubectl, "To connect to this cluster, use: kubectl --context={{.name}}", out.V{"name": kcs.ClusterName})
out.T(style.Kubectl, "To connect to this cluster, use: kubectl --context={{.name}}", out.V{"name": kcs.ClusterName})
} else {
out.T(out.Ready, `Done! kubectl is now configured to use "{{.name}}"`, out.V{"name": machineName})
out.T(style.Ready, `Done! kubectl is now configured to use "{{.name}}"`, out.V{"name": machineName})
}
path, err := exec.LookPath("kubectl")
if err != nil {
out.ErrT(out.Kubectl, "Kubectl not found in your path")
out.ErrT(out.Workaround, "You can use kubectl inside minikube. For more information, visit https://minikube.sigs.k8s.io/docs/handbook/kubectl/")
out.ErrT(out.Tip, "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/")
out.ErrT(style.Kubectl, "Kubectl not found in your path")
out.ErrT(style.Workaround, "You can use kubectl inside minikube. For more information, visit https://minikube.sigs.k8s.io/docs/handbook/kubectl/")
out.ErrT(style.Tip, "For best results, install kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/")
return nil
}
@ -420,7 +421,7 @@ func showKubectlInfo(kcs *kubeconfig.Settings, k8sVersion string, machineName st
out.Ln("")
out.WarningT("{{.path}} is version {{.client_version}}, which may be incompatible with Kubernetes {{.cluster_version}}.",
out.V{"path": path, "client_version": client, "cluster_version": cluster})
out.ErrT(out.Tip, "You can also use 'minikube kubectl -- get pods' to invoke a matching version",
out.ErrT(style.Tip, "You can also use 'minikube kubectl -- get pods' to invoke a matching version",
out.V{"path": path, "client_version": client})
}
return nil
@ -432,7 +433,7 @@ func maybeDeleteAndRetry(cmd *cobra.Command, existing config.ClusterConfig, n co
// Start failed, delete the cluster and try again
profile, err := config.LoadProfile(existing.Name)
if err != nil {
out.ErrT(out.Meh, `"{{.name}}" profile does not exist, trying anyways.`, out.V{"name": existing.Name})
out.ErrT(style.Meh, `"{{.name}}" profile does not exist, trying anyways.`, out.V{"name": existing.Name})
}
err = deleteProfile(profile)
@ -471,7 +472,7 @@ func maybeDeleteAndRetry(cmd *cobra.Command, existing config.ClusterConfig, n co
return kubeconfig, nil
}
// Don't delete the cluster unless they ask
return nil, errors.Wrap(originalErr, "startup failed")
return nil, originalErr
}
func kubectlVersion(path string) (string, error) {
@ -507,7 +508,7 @@ func selectDriver(existing *config.ClusterConfig) (registry.DriverState, []regis
if existing != nil {
old := hostDriver(existing)
ds := driver.Status(old)
out.T(out.Sparkle, `Using the {{.driver}} driver based on existing profile`, out.V{"driver": ds.String()})
out.T(style.Sparkle, `Using the {{.driver}} driver based on existing profile`, out.V{"driver": ds.String()})
return ds, nil, true
}
@ -525,9 +526,9 @@ func selectDriver(existing *config.ClusterConfig) (registry.DriverState, []regis
}
ds := driver.Status(d)
if ds.Name == "" {
exit.WithCodeT(exit.Unavailable, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": d, "os": runtime.GOOS})
exit.Message(reason.DrvUnsupportedOS, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": d, "os": runtime.GOOS})
}
out.T(out.Sparkle, `Using the {{.driver}} driver based on user configuration`, out.V{"driver": ds.String()})
out.T(style.Sparkle, `Using the {{.driver}} driver based on user configuration`, out.V{"driver": ds.String()})
return ds, nil, true
}
@ -535,21 +536,20 @@ func selectDriver(existing *config.ClusterConfig) (registry.DriverState, []regis
if d := viper.GetString("vm-driver"); d != "" {
ds := driver.Status(viper.GetString("vm-driver"))
if ds.Name == "" {
exit.WithCodeT(exit.Unavailable, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": d, "os": runtime.GOOS})
exit.Message(reason.DrvUnsupportedOS, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": d, "os": runtime.GOOS})
}
out.T(out.Sparkle, `Using the {{.driver}} driver based on user configuration`, out.V{"driver": ds.String()})
out.T(style.Sparkle, `Using the {{.driver}} driver based on user configuration`, out.V{"driver": ds.String()})
return ds, nil, true
}
choices := driver.Choices(viper.GetBool("vm"))
pick, alts, rejects := driver.Suggest(choices)
if pick.Name == "" {
out.T(out.ThumbsDown, "Unable to pick a default driver. Here is what was considered, in preference order:")
out.T(style.ThumbsDown, "Unable to pick a default driver. Here is what was considered, in preference order:")
for _, r := range rejects {
out.Infof("{{ .name }}: {{ .rejection }}", out.V{"name": r.Name, "rejection": r.Rejection})
}
out.T(out.Workaround, "Try specifying a --driver, or see https://minikube.sigs.k8s.io/docs/start/")
os.Exit(exit.Unavailable)
exit.Message(reason.DrvNotDetected, "No possible driver was detected. Try specifying --driver, or see https://minikube.sigs.k8s.io/docs/start/")
}
if len(alts) > 1 {
@ -557,9 +557,9 @@ func selectDriver(existing *config.ClusterConfig) (registry.DriverState, []regis
for _, a := range alts {
altNames = append(altNames, a.String())
}
out.T(out.Sparkle, `Automatically selected the {{.driver}} driver. Other choices: {{.alternates}}`, out.V{"driver": pick.Name, "alternates": strings.Join(altNames, ", ")})
out.T(style.Sparkle, `Automatically selected the {{.driver}} driver. Other choices: {{.alternates}}`, out.V{"driver": pick.Name, "alternates": strings.Join(altNames, ", ")})
} else {
out.T(out.Sparkle, `Automatically selected the {{.driver}} driver`, out.V{"driver": pick.String()})
out.T(style.Sparkle, `Automatically selected the {{.driver}} driver`, out.V{"driver": pick.String()})
}
return pick, alts, false
}
@ -617,19 +617,18 @@ func validateSpecifiedDriver(existing *config.ClusterConfig) {
return
}
out.ErrT(out.Conflict, `The existing "{{.name}}" VM was created using the "{{.old}}" driver, and is incompatible with the "{{.new}}" driver.`,
out.V{"name": existing.Name, "new": requested, "old": old})
out.ErrT(out.Workaround, `To proceed, either:
1) Delete the existing "{{.name}}" cluster using: '{{.delcommand}}'
* or *
2) Start the existing "{{.name}}" cluster using: '{{.command}} --driver={{.old}}'
`, out.V{"command": mustload.ExampleCmd(existing.Name, "start"), "delcommand": mustload.ExampleCmd(existing.Name, "delete"), "old": old, "name": existing.Name})
exit.WithCodeT(exit.Config, "Exiting.")
exit.Advice(
reason.GuestDrvMismatch,
`The existing "{{.name}}" cluster was created using the "{{.old}}" driver, which is incompatible with requested "{{.new}}" driver.`,
"Delete the existing '{{.name}}' cluster using: '{{.delcommand}}', or start the existing '{{.name}}' cluster using: '{{.command}} --driver={{.old}}'",
out.V{
"name": existing.Name,
"new": requested,
"old": old,
"command": mustload.ExampleCmd(existing.Name, "start"),
"delcommand": mustload.ExampleCmd(existing.Name, "delete"),
},
)
}
// validateDriver validates that the selected driver appears sane, exits if not
@ -637,7 +636,7 @@ func validateDriver(ds registry.DriverState, existing *config.ClusterConfig) {
name := ds.Name
glog.Infof("validating driver %q against %+v", name, existing)
if !driver.Supported(name) {
exit.WithCodeT(exit.Unavailable, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": name, "os": runtime.GOOS})
exit.Message(reason.DrvUnsupportedOS, "The driver '{{.driver}}' is not supported on {{.os}}", out.V{"driver": name, "os": runtime.GOOS})
}
// if we are only downloading artifacts for a driver, we can stop validation here
@ -648,33 +647,43 @@ func validateDriver(ds registry.DriverState, existing *config.ClusterConfig) {
st := ds.State
glog.Infof("status for %s: %+v", name, st)
if st.NeedsImprovement { // warn but don't exit
out.ErrLn("")
out.WarningT("'{{.driver}}' driver reported a issue that could affect the performance.", out.V{"driver": name})
out.ErrT(out.Tip, "Suggestion: {{.fix}}", out.V{"fix": translate.T(st.Fix)})
out.ErrLn("")
if st.NeedsImprovement {
out.WarnReason(reason.Kind{
ID: fmt.Sprintf("PROVIDER_%s_IMPROVEMENT", strings.ToUpper(name)),
Advice: translate.T(st.Fix),
Style: style.Improvement,
}, `The '{{.driver}}' driver reported a performance issue`, out.V{"driver": name})
}
if st.Error != nil {
out.ErrLn("")
out.WarningT("'{{.driver}}' driver reported an issue: {{.error}}", out.V{"driver": name, "error": st.Error})
out.ErrT(out.Tip, "Suggestion: {{.fix}}", out.V{"fix": translate.T(st.Fix)})
if st.Doc != "" {
out.ErrT(out.Documentation, "Documentation: {{.url}}", out.V{"url": st.Doc})
}
out.ErrLn("")
if !st.Installed {
if existing != nil {
if old := hostDriver(existing); name == old {
exit.WithCodeT(exit.Unavailable, "{{.driver}} does not appear to be installed, but is specified by an existing profile. Please run 'minikube delete' or install {{.driver}}", out.V{"driver": name})
}
}
exit.WithCodeT(exit.Unavailable, "{{.driver}} does not appear to be installed", out.V{"driver": name})
}
exitIfNotForced(exit.Unavailable, "Failed to validate '{{.driver}}' driver", out.V{"driver": name})
if st.Error == nil {
return
}
if !st.Installed {
exit.Message(reason.Kind{
ID: fmt.Sprintf("PROVIDER_%s_NOT_FOUND", strings.ToUpper(name)),
Advice: translate.T(st.Fix),
ExitCode: reason.ExProviderNotFound,
URL: st.Doc,
Style: style.Shrug,
}, `The '{{.driver}}' provider was not found: {{.error}}`, out.V{"driver": name, "error": st.Error})
}
id := fmt.Sprintf("PROVIDER_%s_ERROR", strings.ToUpper(name))
code := reason.ExProviderUnavailable
if !st.Running {
id = fmt.Sprintf("PROVIDER_%s_NOT_RUNNING", strings.ToUpper(name))
code = reason.ExProviderNotRunning
}
exitIfNotForced(reason.Kind{
ID: id,
Advice: translate.T(st.Fix),
ExitCode: code,
URL: st.Doc,
Style: style.Fatal,
}, st.Error.Error())
}
func selectImageRepository(mirrorCountry string, v semver.Version) (bool, string, error) {
@ -739,31 +748,30 @@ func validateUser(drvName string) {
useForce := viper.GetBool(force)
if driver.NeedsRoot(drvName) && u.Uid != "0" && !useForce {
exit.WithCodeT(exit.Permissions, `The "{{.driver_name}}" driver requires root privileges. Please run minikube using 'sudo -E minikube start --driver={{.driver_name}}'.`, out.V{"driver_name": drvName})
exit.Message(reason.DrvNeedsRoot, `The "{{.driver_name}}" driver requires root privileges. Please run minikube using 'sudo -E minikube start --driver={{.driver_name}}'.`, out.V{"driver_name": drvName})
}
// If root is required, or we are not root, exit early
if driver.NeedsRoot(drvName) || u.Uid != "0" {
return
}
out.ErrT(out.Stopped, `The "{{.driver_name}}" driver should not be used with root privileges.`, out.V{"driver_name": drvName})
out.ErrT(out.Tip, "If you are running minikube within a VM, consider using --driver=none:")
out.ErrT(out.Documentation, " https://minikube.sigs.k8s.io/docs/reference/drivers/none/")
out.ErrT(style.Stopped, `The "{{.driver_name}}" driver should not be used with root privileges.`, out.V{"driver_name": drvName})
out.ErrT(style.Tip, "If you are running minikube within a VM, consider using --driver=none:")
out.ErrT(style.Documentation, " https://minikube.sigs.k8s.io/docs/reference/drivers/none/")
if !useForce {
os.Exit(exit.Permissions)
}
cname := ClusterFlagValue()
_, err = config.Load(cname)
if err == nil || !config.IsNotExist(err) {
out.ErrT(out.Tip, "Tip: To remove this root owned cluster, run: sudo {{.cmd}}", out.V{"cmd": mustload.ExampleCmd(cname, "delete")})
out.ErrT(style.Tip, "Tip: To remove this root owned cluster, run: sudo {{.cmd}}", out.V{"cmd": mustload.ExampleCmd(cname, "delete")})
}
if !useForce {
exit.WithCodeT(exit.Permissions, "Exiting")
exit.Message(reason.DrvAsRoot, `The "{{.driver_name}}" driver should not be used with root privileges.`, out.V{"driver_name": drvName})
}
}
// memoryLimits returns the amount of memory allocated to the system and hypervisor , the return value is in MB
// memoryLimits returns the amount of memory allocated to the system and hypervisor, the return value is in MiB
func memoryLimits(drvName string) (int, int, error) {
info, cpuErr, memErr, diskErr := machine.CachedHostInfo()
if cpuErr != nil {
@ -791,7 +799,7 @@ func memoryLimits(drvName string) (int, int, error) {
return sysLimit, containerLimit, nil
}
// suggestMemoryAllocation calculates the default memory footprint in MB
// suggestMemoryAllocation calculates the default memory footprint in MiB
func suggestMemoryAllocation(sysLimit int, containerLimit int, nodes int) int {
if mem := viper.GetInt(memory); mem != 0 {
return mem
@ -829,76 +837,60 @@ func suggestMemoryAllocation(sysLimit int, containerLimit int, nodes int) int {
return suggested
}
// validateMemoryHardLimit checks if the user system has enough memory at all !
func validateMemoryHardLimit(drvName string) {
s, c, err := memoryLimits(drvName)
if err != nil {
glog.Warningf("Unable to query memory limits: %v", err)
out.WarningT("Failed to verify system memory limits.")
return
}
if s < 2200 {
out.WarningT("Your system has only {{.memory_amount}}MB memory. This might not work minimum required is 2000MB.", out.V{"memory_amount": s})
return
}
if driver.IsDockerDesktop(drvName) {
// in Docker Desktop if you allocate 2 GB the docker info shows: Total Memory: 1.945GiB which becomes 1991 when we calculate the MBs
// thats why it is not same number as other drivers which is 2 GB
if c < 1991 {
out.WarningT(`Increase Docker for Desktop memory to at least 2.5GB or more:
Docker for Desktop > Settings > Resources > Memory
`)
}
}
}
// validateMemorySize validates the memory size matches the minimum recommended
func validateMemorySize(req int, drvName string) {
// validateRequestedMemorySize validates the memory size matches the minimum recommended
func validateRequestedMemorySize(req int, drvName string) {
// TODO: Fix MB vs MiB confusion
sysLimit, containerLimit, err := memoryLimits(drvName)
if err != nil {
glog.Warningf("Unable to query memory limits: %v", err)
}
// maximm percent of their ram they could allocate to minikube to prevent #8708
maxAdvised := 0.79 * float64(sysLimit)
// a more sane alternative to their high memory 80%
minAdvised := 0.50 * float64(sysLimit)
// Detect if their system doesn't have enough memory to work with.
if driver.IsKIC(drvName) && containerLimit < minUsableMem {
if driver.IsDockerDesktop(drvName) {
if runtime.GOOS == "darwin" {
exitIfNotForced(reason.RsrcInsufficientDarwinDockerMemory, "Docker Desktop only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes", out.V{"size": containerLimit, "req": minUsableMem, "recommend": "2.25 GB"})
} else {
exitIfNotForced(reason.RsrcInsufficientWindowsDockerMemory, "Docker Desktop only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes", out.V{"size": containerLimit, "req": minUsableMem, "recommend": "2.25 GB"})
}
}
exitIfNotForced(reason.RsrcInsufficientContainerMemory, "{{.driver}} only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes", out.V{"size": containerLimit, "driver": drvName, "req": minUsableMem})
}
if sysLimit < minUsableMem {
exitIfNotForced(reason.RsrcInsufficientSysMemory, "System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes", out.V{"size": containerLimit, "driver": drvName, "req": minUsableMem})
}
if req < minUsableMem {
exitIfNotForced(exit.Config, "Requested memory allocation {{.requested}}MB is less than the usable minimum of {{.minimum_memory}}MB", out.V{"requested": req, "minimum_memory": minUsableMem})
exitIfNotForced(reason.RsrcInsufficientReqMemory, "Requested memory allocation {{.requested}}MiB is less than the usable minimum of {{.minimum_memory}}MB", out.V{"requested": req, "minimum_memory": minUsableMem})
}
if req < minRecommendedMem {
out.WarningT("Requested memory allocation ({{.requested}}MB) is less than the recommended minimum {{.recommended}}MB. Kubernetes may crash unexpectedly.",
out.V{"requested": req, "recommended": minRecommendedMem})
out.WarnReason(reason.RsrcInsufficientReqMemory, "Requested memory allocation ({{.requested}}MB) is less than the recommended minimum {{.recommend}}MB. Deployments may fail.", out.V{"requested": req, "recommend": minRecommendedMem})
}
if driver.IsDockerDesktop(drvName) && containerLimit < 2997 && sysLimit > 8000 { // for users with more than 8 GB advice 3 GB
out.WarningT(`Your system has {{.system_limit}}MB memory but Docker has only {{.container_limit}}MB. For a better performance increase to at least 3GB.
Docker for Desktop > Settings > Resources > Memory
`, out.V{"container_limit": containerLimit, "system_limit": sysLimit})
r := reason.RsrcInsufficientDarwinDockerMemory
if runtime.GOOS == "Windows" {
r = reason.RsrcInsufficientWindowsDockerMemory
}
r.Style = style.Improvement
out.WarnReason(r, "Docker Desktop has access to only {{.size}}MiB of the {{.sys}}MiB in available system memory. Consider increasing this for improved performance.", out.V{"size": containerLimit, "sys": sysLimit, "recommend": "3 GB"})
}
advised := suggestMemoryAllocation(sysLimit, containerLimit, viper.GetInt(nodes))
if req > sysLimit {
message := `Requested memory allocation {{.requested}}MB is more than your system limit {{.system_limit}}MB. Try specifying a lower memory:
minikube start --memory={{.min_advised}}mb
`
exitIfNotForced(exit.Config, message, out.V{"requested": req, "system_limit": sysLimit, "max_advised": int32(maxAdvised), "min_advised": minAdvised})
exitIfNotForced(reason.Kind{ID: "RSRC_OVER_ALLOC_MEM", Advice: "Start minikube with less memory allocated: 'minikube start --memory={{.advised}}mb'"},
`Requested memory allocation {{.requested}}MB is more than your system limit {{.system_limit}}MB.`,
out.V{"requested": req, "system_limit": sysLimit, "advised": advised})
}
if float64(req) > maxAdvised {
out.WarningT(`You are allocating {{.requested}}MB to memory and your system only has {{.system_limit}}MB. You might face issues. try specifying a lower memory:
minikube start --memory={{.min_advised}}mb
`, out.V{"requested": req, "system_limit": sysLimit, "min_advised": minAdvised})
// Recommend 1GB to handle OS/VM overhead
maxAdvised := sysLimit - 1024
if req > maxAdvised {
out.WarnReason(reason.Kind{ID: "RSRC_OVER_ALLOC_MEM", Advice: "Start minikube with less memory allocated: 'minikube start --memory={{.advised}}mb'"},
`The requested memory allocation of {{.requested}}MiB does not leave room for system overhead (total system memory: {{.system_limit}}MiB). You may face stability issues.`,
out.V{"requested": req, "system_limit": sysLimit, "advised": advised})
}
}
// validateCPUCount validates the cpu count matches the minimum recommended
@ -915,45 +907,49 @@ func validateCPUCount(drvName string) {
} else {
cpuCount = viper.GetInt(cpus)
}
if cpuCount < minimumCPUS {
exitIfNotForced(exit.BadUsage, "Requested cpu count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}", out.V{"requested_cpus": cpuCount, "minimum_cpus": minimumCPUS})
exitIfNotForced(reason.RsrcInsufficientCores, "Requested cpu count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}", out.V{"requested_cpus": cpuCount, "minimum_cpus": minimumCPUS})
}
if driver.IsKIC((drvName)) {
si, err := oci.CachedDaemonInfo(drvName)
if err != nil {
out.T(out.Confused, "Failed to verify '{{.driver_name}} info' will try again ...", out.V{"driver_name": drvName})
si, err = oci.DaemonInfo(drvName)
if err != nil {
exit.UsageT("Ensure your {{.driver_name}} is running and is healthy.", out.V{"driver_name": driver.FullName(drvName)})
}
if !driver.IsKIC((drvName)) {
return
}
si, err := oci.CachedDaemonInfo(drvName)
if err != nil {
out.T(style.Confused, "Failed to verify '{{.driver_name}} info' will try again ...", out.V{"driver_name": drvName})
si, err = oci.DaemonInfo(drvName)
if err != nil {
exit.Message(reason.Usage, "Ensure your {{.driver_name}} is running and is healthy.", out.V{"driver_name": driver.FullName(drvName)})
}
if si.CPUs < 2 {
if drvName == oci.Docker {
out.T(out.Conflict, `Your Docker Desktop has less than 2 CPUs. Increase CPUs for Docker Desktop.
Docker icon > Settings > Resources > CPUs
`)
}
out.T(out.Documentation, "https://docs.docker.com/config/containers/resource_constraints/")
exitIfNotForced(exit.BadUsage, "Ensure your {{.driver_name}} system has enough CPUs. The minimum allowed is 2 CPUs.", out.V{"driver_name": driver.FullName(viper.GetString("driver"))})
}
}
// looks good
if si.CPUs >= 2 {
return
}
if drvName == oci.Docker && runtime.GOOS == "darwin" {
exitIfNotForced(reason.RsrcInsufficientDarwinDockerCores, "Docker Desktop has less than 2 CPUs configured, but Kubernetes requires at least 2 to be available")
} else if drvName == oci.Docker && runtime.GOOS == "windows" {
exitIfNotForced(reason.RsrcInsufficientWindowsDockerCores, "Docker Desktop has less than 2 CPUs configured, but Kubernetes requires at least 2 to be available")
} else {
exitIfNotForced(reason.RsrcInsufficientCores, "{{.driver_name}} has less than 2 CPUs available, but Kubernetes requires at least 2 to be available", out.V{"driver_name": driver.FullName(viper.GetString("driver"))})
}
}
// validateFlags validates the supplied flags against known bad combinations
func validateFlags(cmd *cobra.Command, drvName string) {
if cmd.Flags().Changed(humanReadableDiskSize) {
diskSizeMB, err := util.CalculateSizeInMB(viper.GetString(humanReadableDiskSize))
if err != nil {
exitIfNotForced(exit.Config, "Validation unable to parse disk size '{{.diskSize}}': {{.error}}", out.V{"diskSize": viper.GetString(humanReadableDiskSize), "error": err})
exitIfNotForced(reason.Usage, "Validation unable to parse disk size '{{.diskSize}}': {{.error}}", out.V{"diskSize": viper.GetString(humanReadableDiskSize), "error": err})
}
if diskSizeMB < minimumDiskSize {
exitIfNotForced(exit.Config, "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}", out.V{"requested_size": diskSizeMB, "minimum_size": minimumDiskSize})
exitIfNotForced(reason.RsrcInsufficientStorage, "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}", out.V{"requested_size": diskSizeMB, "minimum_size": minimumDiskSize})
}
}
@ -962,8 +958,8 @@ func validateFlags(cmd *cobra.Command, drvName string) {
out.WarningT("The '{{.name}}' driver does not respect the --cpus flag", out.V{"name": drvName})
}
}
validateCPUCount(drvName)
validateMemoryHardLimit(drvName)
if cmd.Flags().Changed(memory) {
if !driver.HasResourceLimits(drvName) {
@ -971,9 +967,9 @@ func validateFlags(cmd *cobra.Command, drvName string) {
}
req, err := util.CalculateSizeInMB(viper.GetString(memory))
if err != nil {
exitIfNotForced(exit.Config, "Unable to parse memory '{{.memory}}': {{.error}}", out.V{"memory": viper.GetString(memory), "error": err})
exitIfNotForced(reason.Usage, "Unable to parse memory '{{.memory}}': {{.error}}", out.V{"memory": viper.GetString(memory), "error": err})
}
validateMemorySize(req, drvName)
validateRequestedMemorySize(req, drvName)
}
if cmd.Flags().Changed(containerRuntime) {
@ -996,13 +992,13 @@ func validateFlags(cmd *cobra.Command, drvName string) {
}
if !validRuntime {
exit.UsageT(`Invalid Container Runtime: "{{.runtime}}". Valid runtimes are: {{.validOptions}}`, out.V{"runtime": runtime, "validOptions": strings.Join(cruntime.ValidRuntimes(), ", ")})
exit.Message(reason.Usage, `Invalid Container Runtime: "{{.runtime}}". Valid runtimes are: {{.validOptions}}`, out.V{"runtime": runtime, "validOptions": strings.Join(cruntime.ValidRuntimes(), ", ")})
}
}
if driver.BareMetal(drvName) {
if ClusterFlagValue() != constants.DefaultClusterName {
exit.WithCodeT(exit.Config, "The '{{.name}} driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/", out.V{"name": drvName})
exit.Message(reason.DrvUnsupportedProfile, "The '{{.name}} driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/", out.V{"name": drvName})
}
runtime := viper.GetString(containerRuntime)
@ -1014,20 +1010,19 @@ func validateFlags(cmd *cobra.Command, drvName string) {
version, _ := util.ParseKubernetesVersion(getKubernetesVersion(nil))
if version.GTE(semver.MustParse("1.18.0-beta.1")) {
if _, err := exec.LookPath("conntrack"); err != nil {
exit.WithCodeT(exit.Config, "Sorry, Kubernetes {{.k8sVersion}} requires conntrack to be installed in root's path", out.V{"k8sVersion": version.String()})
exit.Message(reason.GuestMissingConntrack, "Sorry, Kubernetes {{.k8sVersion}} requires conntrack to be installed in root's path", out.V{"k8sVersion": version.String()})
}
}
}
// validate kubeadm extra args
if invalidOpts := bsutil.FindInvalidExtraConfigFlags(config.ExtraOptions); len(invalidOpts) > 0 {
out.ErrT(
out.Warning,
out.WarningT(
"These --extra-config parameters are invalid: {{.invalid_extra_opts}}",
out.V{"invalid_extra_opts": invalidOpts},
)
exit.WithCodeT(
exit.Config,
exit.Message(
reason.Usage,
"Valid components are: {{.valid_extra_opts}}",
out.V{"valid_extra_opts": bsutil.KubeadmExtraConfigOpts},
)
@ -1037,12 +1032,12 @@ func validateFlags(cmd *cobra.Command, drvName string) {
for param := range config.ExtraOptions.AsMap().Get(bsutil.Kubeadm) {
if !config.ContainsParam(bsutil.KubeadmExtraArgsAllowed[bsutil.KubeadmCmdParam], param) &&
!config.ContainsParam(bsutil.KubeadmExtraArgsAllowed[bsutil.KubeadmConfigParam], param) {
exit.UsageT("Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config", out.V{"parameter_name": param})
exit.Message(reason.Usage, "Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config", out.V{"parameter_name": param})
}
}
if s := viper.GetString(startOutput); s != "text" && s != "json" {
exit.UsageT("Sorry, please set the --output flag to one of the following valid options: [text,json]")
exit.Message(reason.Usage, "Sorry, please set the --output flag to one of the following valid options: [text,json]")
}
validateRegistryMirror()
@ -1051,7 +1046,6 @@ func validateFlags(cmd *cobra.Command, drvName string) {
// This function validates if the --registry-mirror
// args match the format of http://localhost
func validateRegistryMirror() {
if len(registryMirror) > 0 {
for _, loc := range registryMirror {
URL, err := url.Parse(loc)
@ -1059,7 +1053,7 @@ func validateRegistryMirror() {
glog.Errorln("Error Parsing URL: ", err)
}
if (URL.Scheme != "http" && URL.Scheme != "https") || URL.Path != "" {
exit.UsageT("Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}", out.V{"url": loc})
exit.Message(reason.Usage, "Sorry, the url provided with the --registry-mirror flag is invalid: {{.url}}", out.V{"url": loc})
}
}
@ -1140,11 +1134,11 @@ func validateKubernetesVersion(old *config.ClusterConfig) {
oldestVersion, err := semver.Make(strings.TrimPrefix(constants.OldestKubernetesVersion, version.VersionPrefix))
if err != nil {
exit.WithCodeT(exit.Data, "Unable to parse oldest Kubernetes version from constants: {{.error}}", out.V{"error": err})
exit.Message(reason.InternalSemverParse, "Unable to parse oldest Kubernetes version from constants: {{.error}}", out.V{"error": err})
}
defaultVersion, err := semver.Make(strings.TrimPrefix(constants.DefaultKubernetesVersion, version.VersionPrefix))
if err != nil {
exit.WithCodeT(exit.Data, "Unable to parse default Kubernetes version from constants: {{.error}}", out.V{"error": err})
exit.Message(reason.InternalSemverParse, "Unable to parse default Kubernetes version from constants: {{.error}}", out.V{"error": err})
}
if nvs.LT(oldestVersion) {
@ -1152,7 +1146,7 @@ func validateKubernetesVersion(old *config.ClusterConfig) {
if !viper.GetBool(force) {
out.WarningT("You can force an unsupported Kubernetes version via the --force flag")
}
exitIfNotForced(exit.Data, "Kubernetes {{.version}} is not supported by this release of minikube", out.V{"version": nvs})
exitIfNotForced(reason.KubernetesTooOld, "Kubernetes {{.version}} is not supported by this release of minikube", out.V{"version": nvs})
}
if old == nil || old.KubernetesConfig.KubernetesVersion == "" {
@ -1171,26 +1165,12 @@ func validateKubernetesVersion(old *config.ClusterConfig) {
}
suggestedName := old.Name + "2"
out.T(out.Conflict, "You have selected Kubernetes {{.new}}, but the existing cluster is running Kubernetes {{.old}}", out.V{"new": nvs, "old": ovs, "profile": profileArg})
exit.WithCodeT(exit.Config, `Non-destructive downgrades are not supported, but you can proceed with one of the following options:
1) Recreate the cluster with Kubernetes {{.new}}, by running:
minikube delete{{.profile}}
minikube start{{.profile}} --kubernetes-version={{.prefix}}{{.new}}
2) Create a second cluster with Kubernetes {{.new}}, by running:
minikube start -p {{.suggestedName}} --kubernetes-version={{.prefix}}{{.new}}
3) Use the existing cluster at version Kubernetes {{.old}}, by running:
minikube start{{.profile}} --kubernetes-version={{.prefix}}{{.old}}
`, out.V{"prefix": version.VersionPrefix, "new": nvs, "old": ovs, "profile": profileArg, "suggestedName": suggestedName})
exit.Message(reason.KubernetesDowngrade, "Unable to safely downgrade existing Kubernetes v{{.old}} cluster to v{{.new}}",
out.V{"prefix": version.VersionPrefix, "new": nvs, "old": ovs, "profile": profileArg, "suggestedName": suggestedName})
}
if defaultVersion.GT(nvs) {
out.T(out.New, "Kubernetes {{.new}} is now available. If you would like to upgrade, specify: --kubernetes-version={{.prefix}}{{.new}}", out.V{"prefix": version.VersionPrefix, "new": defaultVersion})
out.T(style.New, "Kubernetes {{.new}} is now available. If you would like to upgrade, specify: --kubernetes-version={{.prefix}}{{.new}}", out.V{"prefix": version.VersionPrefix, "new": defaultVersion})
}
}
@ -1210,15 +1190,15 @@ func getKubernetesVersion(old *config.ClusterConfig) string {
nvs, err := semver.Make(strings.TrimPrefix(paramVersion, version.VersionPrefix))
if err != nil {
exit.WithCodeT(exit.Data, `Unable to parse "{{.kubernetes_version}}": {{.error}}`, out.V{"kubernetes_version": paramVersion, "error": err})
exit.Message(reason.Usage, `Unable to parse "{{.kubernetes_version}}": {{.error}}`, out.V{"kubernetes_version": paramVersion, "error": err})
}
return version.VersionPrefix + nvs.String()
}
func exitIfNotForced(code int, message string, v out.V) {
func exitIfNotForced(r reason.Kind, message string, v ...out.V) {
if !viper.GetBool(force) {
exit.WithCodeT(code, message, v)
exit.Message(r, message, v...)
}
out.WarningT(message, v)
out.Error(r, message, v...)
}

View File

@ -38,6 +38,8 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/proxy"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
pkgutil "k8s.io/minikube/pkg/util"
"k8s.io/minikube/pkg/version"
)
@ -92,8 +94,8 @@ const (
interactive = "interactive"
waitTimeout = "wait-timeout"
nativeSSH = "native-ssh"
minUsableMem = 1024 // Kubernetes will not start with less than 1GB
minRecommendedMem = 2000 // Warn at no lower than existing configurations
minUsableMem = 1024 // In MiB: Kubernetes will not start with less than 1GiB
minRecommendedMem = 2000 // In MiB: Warn at no lower than existing configurations
minimumCPUS = 2
minimumDiskSize = 2000
autoUpdate = "auto-update-drivers"
@ -230,19 +232,19 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k
var err error
mem, err = pkgutil.CalculateSizeInMB(viper.GetString(memory))
if err != nil {
exit.WithCodeT(exit.Config, "Generate unable to parse memory '{{.memory}}': {{.error}}", out.V{"memory": viper.GetString(memory), "error": err})
exit.Message(reason.Usage, "Generate unable to parse memory '{{.memory}}': {{.error}}", out.V{"memory": viper.GetString(memory), "error": err})
}
if driver.IsKIC(drvName) && mem > containerLimit {
exit.UsageT("{{.driver_name}} has only {{.container_limit}}MB memory but you specified {{.specified_memory}}MB", out.V{"container_limit": containerLimit, "specified_memory": mem, "driver_name": driver.FullName(drvName)})
exit.Message(reason.Usage, "{{.driver_name}} has only {{.container_limit}}MB memory but you specified {{.specified_memory}}MB", out.V{"container_limit": containerLimit, "specified_memory": mem, "driver_name": driver.FullName(drvName)})
}
} else {
validateMemorySize(mem, drvName)
validateRequestedMemorySize(mem, drvName)
glog.Infof("Using suggested %dMB memory alloc based on sys=%dMB, container=%dMB", mem, sysLimit, containerLimit)
}
diskSize, err := pkgutil.CalculateSizeInMB(viper.GetString(humanReadableDiskSize))
if err != nil {
exit.WithCodeT(exit.Config, "Generate unable to parse disk size '{{.diskSize}}': {{.error}}", out.V{"diskSize": viper.GetString(humanReadableDiskSize), "error": err})
exit.Message(reason.Usage, "Generate unable to parse disk size '{{.diskSize}}': {{.error}}", out.V{"diskSize": viper.GetString(humanReadableDiskSize), "error": err})
}
repository := viper.GetString(imageRepository)
@ -250,12 +252,12 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k
if strings.ToLower(repository) == "auto" || (mirrorCountry != "" && repository == "") {
found, autoSelectedRepository, err := selectImageRepository(mirrorCountry, semver.MustParse(strings.TrimPrefix(k8sVersion, version.VersionPrefix)))
if err != nil {
exit.WithError("Failed to check main repository and mirrors for images", err)
exit.Error(reason.InetRepo, "Failed to check main repository and mirrors for images", err)
}
if !found {
if autoSelectedRepository == "" {
exit.WithCodeT(exit.Failure, "None of the known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag")
exit.Message(reason.InetReposUnavailable, "None of the known repositories are accessible. Consider specifying an alternative image repository with --image-repository flag")
} else {
out.WarningT("None of the known repositories in your location are accessible. Using {{.image_repository_name}} as fallback.", out.V{"image_repository_name": autoSelectedRepository})
}
@ -265,7 +267,7 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k
}
if cmd.Flags().Changed(imageRepository) || cmd.Flags().Changed(imageMirrorCountry) {
out.T(out.SuccessType, "Using image repository {{.name}}", out.V{"name": repository})
out.T(style.Success, "Using image repository {{.name}}", out.V{"name": repository})
}
// Backwards compatibility with --enable-default-cni
@ -428,7 +430,7 @@ func updateExistingConfigFromFlags(cmd *cobra.Command, existing *config.ClusterC
}
// validate the memory size in case user changed their system memory limits (example change docker desktop or upgraded memory.)
validateMemorySize(cc.Memory, cc.Driver)
validateRequestedMemorySize(cc.Memory, cc.Driver)
if cc.CPUs == 0 {
glog.Info("Existing config file was missing cpu. (could be an old minikube config), will use the default value")

View File

@ -45,12 +45,15 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/version"
)
var statusFormat string
var output string
var layout string
var (
statusFormat string
output string
layout string
)
const (
// Additional legacy states:
@ -182,9 +185,8 @@ var statusCmd = &cobra.Command{
Exit status contains the status of minikube's VM, cluster and Kubernetes encoded on it's bits in this order from right to left.
Eg: 7 meaning: 1 (for minikube NOK) + 2 (for cluster NOK) + 4 (for Kubernetes NOK)`,
Run: func(cmd *cobra.Command, args []string) {
if output != "text" && statusFormat != defaultStatusFormat {
exit.UsageT("Cannot use both --output and --format options")
exit.Message(reason.Usage, "Cannot use both --output and --format options")
}
cname := ClusterFlagValue()
@ -195,7 +197,7 @@ var statusCmd = &cobra.Command{
if nodeName != "" || statusFormat != defaultStatusFormat && len(cc.Nodes) > 1 {
n, _, err := node.Retrieve(*cc, nodeName)
if err != nil {
exit.WithError("retrieving node", err)
exit.Error(reason.GuestNodeRetrieve, "retrieving node", err)
}
st, err := nodeStatus(api, *cc, *n)
@ -224,22 +226,22 @@ var statusCmd = &cobra.Command{
case "text":
for _, st := range statuses {
if err := statusText(st, os.Stdout); err != nil {
exit.WithError("status text failure", err)
exit.Error(reason.InternalStatusText, "status text failure", err)
}
}
case "json":
// Layout is currently only supported for JSON mode
if layout == "cluster" {
if err := clusterStatusJSON(statuses, os.Stdout); err != nil {
exit.WithError("status json failure", err)
exit.Error(reason.InternalStatusJSON, "status json failure", err)
}
} else {
if err := statusJSON(statuses, os.Stdout); err != nil {
exit.WithError("status json failure", err)
exit.Error(reason.InternalStatusJSON, "status json failure", err)
}
}
default:
exit.WithCodeT(exit.BadUsage, fmt.Sprintf("invalid output format: %s. Valid values: 'text', 'json'", output))
exit.Message(reason.Usage, fmt.Sprintf("invalid output format: %s. Valid values: 'text', 'json'", output))
}
os.Exit(exitCode(statuses))

View File

@ -34,11 +34,15 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/retry"
)
var stopAll bool
var keepActive bool
var (
stopAll bool
keepActive bool
)
// stopCmd represents the stop command
var stopCmd = &cobra.Command{
@ -50,12 +54,11 @@ itself, leaving all files intact. The cluster can be started again with the "sta
}
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.")
if err := viper.GetViper().BindPFlags(stopCmd.Flags()); err != nil {
exit.WithError("unable to bind flags", err)
exit.Error(reason.InternalFlagsBind, "unable to bind flags", err)
}
RootCmd.AddCommand(stopCmd)
@ -88,7 +91,7 @@ func runStop(cmd *cobra.Command, args []string) {
register.Reg.SetStep(register.Done)
if stoppedNodes > 0 {
out.T(out.Stopped, `{{.count}} nodes stopped.`, out.V{"count": stoppedNodes})
out.T(style.Stopped, `{{.count}} nodes stopped.`, out.V{"count": stoppedNodes})
}
}
@ -115,7 +118,7 @@ func stopProfile(profile string) int {
if !keepActive {
if err := kubeconfig.UnsetCurrentContext(profile, kubeconfig.PathFromEnv()); err != nil {
exit.WithError("update config", err)
exit.Error(reason.HostKubeconfigUnset, "update config", err)
}
}
@ -134,7 +137,7 @@ func stop(api libmachine.API, machineName string) bool {
switch err := errors.Cause(err).(type) {
case mcnerror.ErrHostDoesNotExist:
out.T(out.Meh, `"{{.machineName}}" does not exist, nothing to stop`, out.V{"machineName": machineName})
out.T(style.Meh, `"{{.machineName}}" does not exist, nothing to stop`, out.V{"machineName": machineName})
nonexistent = true
return nil
default:
@ -143,7 +146,7 @@ func stop(api libmachine.API, machineName string) bool {
}
if err := retry.Expo(tryStop, 1*time.Second, 120*time.Second, 5); err != nil {
exit.WithError("Unable to stop VM", err)
exit.Error(reason.GuestStopTimeout, "Unable to stop VM", err)
}
return nonexistent

View File

@ -33,6 +33,7 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/tunnel"
"k8s.io/minikube/pkg/minikube/tunnel/kic"
)
@ -65,7 +66,7 @@ var tunnelCmd = &cobra.Command{
// doesn't hang on the API server call during startup and shutdown time or if there is a temporary error.
clientset, err := kapi.Client(cname)
if err != nil {
exit.WithError("error creating clientset", err)
exit.Error(reason.InternalKubernetesClient, "error creating clientset", err)
}
ctrlC := make(chan os.Signal, 1)
@ -80,7 +81,7 @@ var tunnelCmd = &cobra.Command{
port, err := oci.ForwardedPort(oci.Docker, cname, 22)
if err != nil {
exit.WithError("error getting ssh port", err)
exit.Error(reason.DrvPortForward, "error getting ssh port", err)
}
sshPort := strconv.Itoa(port)
sshKey := filepath.Join(localpath.MiniPath(), "machines", cname, "id_rsa")
@ -88,7 +89,7 @@ var tunnelCmd = &cobra.Command{
kicSSHTunnel := kic.NewSSHTunnel(ctx, sshPort, sshKey, clientset.CoreV1())
err = kicSSHTunnel.Start()
if err != nil {
exit.WithError("error starting tunnel", err)
exit.Error(reason.SvcTunnelStart, "error starting tunnel", err)
}
return
@ -96,7 +97,7 @@ var tunnelCmd = &cobra.Command{
done, err := manager.StartTunnel(ctx, cname, co.API, config.DefaultLoader, clientset.CoreV1())
if err != nil {
exit.WithError("error starting tunnel", err)
exit.Error(reason.SvcTunnelStart, "error starting tunnel", err)
}
<-done
},

View File

@ -33,6 +33,8 @@ import (
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
// unpauseCmd represents the docker-pause command
@ -48,10 +50,10 @@ var unpauseCmd = &cobra.Command{
glog.Infof("namespaces: %v keys: %v", namespaces, viper.AllSettings())
if allNamespaces {
namespaces = nil //all
namespaces = nil // all
} else {
if len(namespaces) == 0 {
exit.WithCodeT(exit.BadUsage, "Use -A to specify all namespaces")
exit.Message(reason.Usage, "Use -A to specify all namespaces")
}
}
@ -66,27 +68,27 @@ var unpauseCmd = &cobra.Command{
name = co.Config.Name
}
out.T(out.Pause, "Unpausing node {{.name}} ... ", out.V{"name": name})
out.T(style.Pause, "Unpausing node {{.name}} ... ", out.V{"name": name})
machineName := driver.MachineName(*co.Config, n)
host, err := machine.LoadHost(co.API, machineName)
if err != nil {
exit.WithError("Error getting host", err)
exit.Error(reason.GuestLoadHost, "Error getting host", err)
}
r, err := machine.CommandRunner(host)
if err != nil {
exit.WithError("Failed to get command runner", err)
exit.Error(reason.InternalCommandRunner, "Failed to get command runner", err)
}
cr, err := cruntime.New(cruntime.Config{Type: co.Config.KubernetesConfig.ContainerRuntime, Runner: r})
if err != nil {
exit.WithError("Failed runtime", err)
exit.Error(reason.InternalNewRuntime, "Failed runtime", err)
}
uids, err := cluster.Unpause(cr, r, namespaces)
if err != nil {
exit.WithError("Pause", err)
exit.Error(reason.GuestUnpause, "Pause", err)
}
ids = append(ids, uids...)
}
@ -94,9 +96,9 @@ var unpauseCmd = &cobra.Command{
register.Reg.SetStep(register.Done)
if namespaces == nil {
out.T(out.Pause, "Unpaused {{.count}} containers", out.V{"count": len(ids)})
out.T(style.Pause, "Unpaused {{.count}} containers", out.V{"count": len(ids)})
} else {
out.T(out.Pause, "Unpaused {{.count}} containers in: {{.namespaces}}", out.V{"count": len(ids), "namespaces": strings.Join(namespaces, ", ")})
out.T(style.Pause, "Unpaused {{.count}} containers in: {{.namespaces}}", out.V{"count": len(ids), "namespaces": strings.Join(namespaces, ", ")})
}
},
}

View File

@ -21,6 +21,7 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/notify"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/version"
)
@ -32,11 +33,11 @@ var updateCheckCmd = &cobra.Command{
url := notify.GithubMinikubeReleasesURL
r, err := notify.GetAllVersionsFromURL(url)
if err != nil {
exit.WithError("Unable to fetch latest version info", err)
exit.Error(reason.InetVersionUnavailable, "Unable to fetch latest version info", err)
}
if len(r) < 1 {
exit.WithCodeT(exit.Data, "Update server returned an empty list")
exit.Message(reason.InetVersionEmpty, "Update server returned an empty list")
}
out.Ln("CurrentVersion: %s", version.GetVersion())

View File

@ -22,6 +22,8 @@ import (
"k8s.io/minikube/pkg/minikube/kubeconfig"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
// updateContextCmd represents the update-context command
@ -36,13 +38,12 @@ var updateContextCmd = &cobra.Command{
updated, err := kubeconfig.UpdateEndpoint(cname, co.CP.Hostname, co.CP.Port, kubeconfig.PathFromEnv())
if err != nil {
exit.WithError("update config", err)
exit.Error(reason.HostKubeconfigUpdate, "update config", err)
}
if updated {
out.T(out.Celebrate, `"{{.context}}" context has been updated to point to {{.hostname}}:{{.port}}`, out.V{"context": cname, "hostname": co.CP.Hostname, "port": co.CP.Port})
out.T(style.Celebrate, `"{{.context}}" context has been updated to point to {{.hostname}}:{{.port}}`, out.V{"context": cname, "hostname": co.CP.Hostname, "port": co.CP.Port})
} else {
out.T(out.Meh, `No changes required for the "{{.context}}" context`, out.V{"context": cname})
out.T(style.Meh, `No changes required for the "{{.context}}" context`, out.V{"context": cname})
}
},
}

View File

@ -23,6 +23,7 @@ import (
"gopkg.in/yaml.v2"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/version"
)
@ -51,17 +52,17 @@ var versionCmd = &cobra.Command{
case "json":
json, err := json.Marshal(data)
if err != nil {
exit.WithError("version json failure", err)
exit.Error(reason.InternalJSONMarshal, "version json failure", err)
}
out.Ln(string(json))
case "yaml":
yaml, err := yaml.Marshal(data)
if err != nil {
exit.WithError("version yaml failure", err)
exit.Error(reason.InternalYamlMarshal, "version yaml failure", err)
}
out.Ln(string(yaml))
default:
exit.WithCodeT(exit.BadUsage, "error: --output must be 'yaml' or 'json'")
exit.Message(reason.InternalOutputUsage, "error: --output must be 'yaml' or 'json'")
}
},
}

View File

@ -46,7 +46,7 @@ func getSHAFromURL(url string) (string, error) {
return hex.EncodeToString(b[:]), nil
}
func TestReleasesJson(t *testing.T) {
func TestReleasesJSON(t *testing.T) {
releases, err := notify.GetAllVersionsFromURL(notify.GithubMinikubeReleasesURL)
if err != nil {
t.Fatalf("Error getting releases.json: %v", err)

View File

@ -41,7 +41,9 @@ import (
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/storageclass"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/retry"
)
@ -144,7 +146,7 @@ func enableOrDisableAddon(cc *config.ClusterConfig, name string, val string) err
// to match both ingress and ingress-dns addons
if strings.HasPrefix(name, "ingress") && enable {
if driver.IsKIC(cc.Driver) && runtime.GOOS != "linux" {
exit.UsageT(`Due to networking limitations of driver {{.driver_name}} on {{.os_name}}, {{.addon_name}} addon is not supported.
exit.Message(reason.Usage, `Due to networking limitations of driver {{.driver_name}} on {{.os_name}}, {{.addon_name}} addon is not supported.
Alternatively to use this addon you can use a vm-based driver:
'minikube start --vm=true'
@ -152,7 +154,7 @@ Alternatively to use this addon you can use a vm-based driver:
To track the update on this work in progress feature please check:
https://github.com/kubernetes/minikube/issues/7332`, out.V{"driver_name": cc.Driver, "os_name": runtime.GOOS, "addon_name": name})
} else if driver.BareMetal(cc.Driver) {
exit.UsageT(`Due to networking limitations of driver {{.driver_name}}, {{.addon_name}} addon is not supported. Try using a different driver.`,
exit.Message(reason.Usage, `Due to networking limitations of driver {{.driver_name}}, {{.addon_name}} addon is not supported. Try using a different driver.`,
out.V{"driver_name": cc.Driver, "addon_name": name})
}
}
@ -177,7 +179,7 @@ https://github.com/kubernetes/minikube/issues/7332`, out.V{"driver_name": cc.Dri
cp, err := config.PrimaryControlPlane(cc)
if err != nil {
exit.WithError("Error getting primary control plane", err)
exit.Error(reason.GuestCpConfig, "Error getting primary control plane", err)
}
mName := driver.MachineName(*cc, cp)
@ -193,8 +195,8 @@ https://github.com/kubernetes/minikube/issues/7332`, out.V{"driver_name": cc.Dri
if err != nil {
return errors.Wrap(err, "registry port")
}
out.T(out.Tip, `Registry addon on with {{.driver}} uses {{.port}} please use that instead of default 5000`, out.V{"driver": cc.Driver, "port": port})
out.T(out.Documentation, `For more information see: https://minikube.sigs.k8s.io/docs/drivers/{{.driver}}`, out.V{"driver": cc.Driver})
out.T(style.Tip, `Registry addon on with {{.driver}} uses {{.port}} please use that instead of default 5000`, out.V{"driver": cc.Driver, "port": port})
out.T(style.Documentation, `For more information see: https://minikube.sigs.k8s.io/docs/drivers/{{.driver}}`, out.V{"driver": cc.Driver})
}
}
@ -334,7 +336,7 @@ func verifyAddonStatusInternal(cc *config.ClusterConfig, name string, val string
label, ok := addonPodLabels[name]
if ok && enable {
out.T(out.HealthCheck, "Verifying {{.addon_name}} addon...", out.V{"addon_name": name})
out.T(style.HealthCheck, "Verifying {{.addon_name}} addon...", out.V{"addon_name": name})
client, err := kapi.Client(viper.GetString(config.ProfileName))
if err != nil {
return errors.Wrapf(err, "get kube-client to validate %s addon: %v", name, err)
@ -395,7 +397,7 @@ func Start(wg *sync.WaitGroup, cc *config.ClusterConfig, toEnable map[string]boo
defer func() { // making it show after verifications( not perfect till #7613 is closed)
register.Reg.SetStep(register.EnablingAddons)
out.T(out.AddonEnable, "Enabled addons: {{.addons}}", out.V{"addons": strings.Join(toEnableList, ", ")})
out.T(style.AddonEnable, "Enabled addons: {{.addons}}", out.V{"addons": strings.Join(toEnableList, ", ")})
}()
for _, a := range toEnableList {
awg.Add(1)

View File

@ -29,6 +29,8 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
const (
@ -46,7 +48,6 @@ func EnableOrDisable(cfg *config.ClusterConfig, name string, val string) error {
return enableAddon(cfg)
}
return disableAddon(cfg)
}
func enableAddon(cfg *config.ClusterConfig) error {
@ -58,7 +59,7 @@ func enableAddon(cfg *config.ClusterConfig) error {
ctx := context.Background()
creds, err := google.FindDefaultCredentials(ctx)
if err != nil {
exit.WithCodeT(exit.Failure, "Could not find any GCP credentials. Either run `gcloud auth login` or set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your credentials file.")
exit.Message(reason.InternalCredsNotFound, "Could not find any GCP credentials. Either run `gcloud auth login` or set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your credentials file.")
}
f := assets.NewMemoryAssetTarget(creds.JSON, credentialsPath, "0444")
@ -83,7 +84,7 @@ func enableAddon(cfg *config.ClusterConfig) error {
}
out.WarningT("Could not determine a Google Cloud project, which might be ok.")
out.T(out.Tip, `To set your Google Cloud project, run:
out.T(style.Tip, `To set your Google Cloud project, run:
gcloud config set project <project name>
@ -122,8 +123,8 @@ func DisplayAddonMessage(cfg *config.ClusterConfig, name string, val string) err
return errors.Wrapf(err, "parsing bool: %s", name)
}
if enable {
out.T(out.Notice, "Your GCP credentials will now be mounted into every pod created in the {{.name}} cluster.", out.V{"name": cfg.Name})
out.T(out.Notice, "If you don't want your credentials mounted into a specific pod, add a label with the `gcp-auth-skip-secret` key to your pod configuration.")
out.T(style.Notice, "Your GCP credentials will now be mounted into every pod created in the {{.name}} cluster.", out.V{"name": cfg.Name})
out.T(style.Notice, "If you don't want your credentials mounted into a specific pod, add a label with the `gcp-auth-skip-secret` key to your pod configuration.")
}
return nil
}

View File

@ -28,6 +28,7 @@ import (
"github.com/golang/glog"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
// RunResult holds the results of a Runner
@ -100,7 +101,7 @@ func runCmd(cmd *exec.Cmd, warnSlow ...bool) (*RunResult, error) {
if warn { // convert exec.Command to with context
cmdWithCtx := exec.CommandContext(ctx, cmd.Args[0], cmd.Args[1:]...)
cmdWithCtx.Stdout = cmd.Stdout //copying the original command
cmdWithCtx.Stdout = cmd.Stdout // copying the original command
cmdWithCtx.Stderr = cmd.Stderr
cmd = cmdWithCtx
}
@ -134,7 +135,7 @@ func runCmd(cmd *exec.Cmd, warnSlow ...bool) (*RunResult, error) {
out.WarningT(`Executing "{{.command}}" took an unusually long time: {{.duration}}`, out.V{"command": rr.Command(), "duration": elapsed})
// Don't show any restarting hint, when running podman locally (on linux, with sudo). Only when having a service.
if cmd.Args[0] != "sudo" {
out.ErrT(out.Tip, `Restarting the {{.name}} service may improve performance.`, out.V{"name": cmd.Args[0]})
out.ErrT(style.Tip, `Restarting the {{.name}} service may improve performance.`, out.V{"name": cmd.Args[0]})
}
}

View File

@ -124,7 +124,7 @@ func writeSubcommands(command *cobra.Command, w io.Writer) error {
func generateTitle(command *cobra.Command, w io.Writer) error {
date := time.Now().Format("2006-01-02")
title := out.ApplyTemplateFormatting(9999, false, title, out.V{"Command": command.Name(), "Description": command.Short, "Date": date})
title := out.Fmt(title, out.V{"Command": command.Name(), "Description": command.Short, "Date": date})
_, err := w.Write([]byte(title))
return err
}
@ -134,5 +134,5 @@ func saveDocForCommand(command *cobra.Command, contents []byte, path string) err
if err := os.Remove(fp); err != nil {
glog.Warningf("error removing %s", fp)
}
return ioutil.WriteFile(fp, contents, 0644)
return ioutil.WriteFile(fp, contents, 0o644)
}

View File

@ -18,20 +18,18 @@ package kubeadm
import (
"context"
"fmt"
"net"
"os/exec"
"path"
"runtime"
"sync"
"fmt"
"net"
// WARNING: Do not use path/filepath in this package unless you want bizarre Windows paths
"strconv"
"strings"
"sync"
"time"
// WARNING: Do not use path/filepath in this package unless you want bizarre Windows paths
"github.com/blang/semver"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/state"
@ -56,6 +54,7 @@ import (
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/sysinit"
"k8s.io/minikube/pkg/minikube/vmpath"
"k8s.io/minikube/pkg/util"
@ -217,7 +216,6 @@ func (k *Bootstrapper) init(cfg config.ClusterConfig) error {
if driver.IsKIC(cfg.Driver) { // to bypass this error: /proc/sys/net/bridge/bridge-nf-call-iptables does not exist
ignore = append(ignore, "FileContent--proc-sys-net-bridge-bridge-nf-call-iptables")
}
if err := k.clearStaleConfigs(cfg); err != nil {
@ -275,7 +273,6 @@ func (k *Bootstrapper) init(cfg config.ClusterConfig) error {
// applyCNI applies CNI to a cluster. Needs to be done every time a VM is powered up.
func (k *Bootstrapper) applyCNI(cfg config.ClusterConfig) error {
cnm, err := cni.New(cfg)
if err != nil {
return errors.Wrap(err, "cni config")
@ -285,7 +282,7 @@ func (k *Bootstrapper) applyCNI(cfg config.ClusterConfig) error {
return nil
}
out.T(out.CNI, "Configuring {{.name}} (Container Networking Interface) ...", out.V{"name": cnm.String()})
out.T(style.CNI, "Configuring {{.name}} (Container Networking Interface) ...", out.V{"name": cnm.String()})
if err := cnm.Apply(k.c); err != nil {
return errors.Wrap(err, "cni apply")
@ -302,7 +299,6 @@ func (k *Bootstrapper) applyCNI(cfg config.ClusterConfig) error {
// unpause unpauses any Kubernetes backplane components
func (k *Bootstrapper) unpause(cfg config.ClusterConfig) error {
cr, err := cruntime.New(cruntime.Config{Type: cfg.KubernetesConfig.ContainerRuntime, Runner: k.c})
if err != nil {
return err
@ -341,7 +337,7 @@ func (k *Bootstrapper) StartCluster(cfg config.ClusterConfig) error {
return nil
}
out.ErrT(out.Embarrassed, "Unable to restart cluster, will reset it: {{.error}}", out.V{"error": rerr})
out.ErrT(style.Embarrassed, "Unable to restart cluster, will reset it: {{.error}}", out.V{"error": rerr})
if err := k.DeleteCluster(cfg.KubernetesConfig); err != nil {
glog.Warningf("delete failed: %v", err)
}
@ -360,7 +356,7 @@ func (k *Bootstrapper) StartCluster(cfg config.ClusterConfig) error {
// retry again if it is not a fail fast error
if _, ff := err.(*FailFastError); !ff {
out.ErrT(out.Conflict, "initialization failed, will try again: {{.error}}", out.V{"error": err})
out.ErrT(style.Conflict, "initialization failed, will try again: {{.error}}", out.V{"error": err})
if err := k.DeleteCluster(cfg.KubernetesConfig); err != nil {
glog.Warningf("delete failed: %v", err)
}
@ -397,7 +393,7 @@ func (k *Bootstrapper) WaitForNode(cfg config.ClusterConfig, n config.Node, time
start := time.Now()
register.Reg.SetStep(register.VerifyingKubernetes)
out.T(out.HealthCheck, "Verifying Kubernetes components...")
out.T(style.HealthCheck, "Verifying Kubernetes components...")
// TODO: #7706: for better performance we could use k.client inside minikube to avoid asking for external IP:PORT
cp, err := config.PrimaryControlPlane(&cfg)
@ -747,14 +743,16 @@ func (k *Bootstrapper) UpdateCluster(cfg config.ClusterConfig) error {
return errors.Wrap(err, "kubeadm images")
}
r, err := cruntime.New(cruntime.Config{Type: cfg.KubernetesConfig.ContainerRuntime,
Runner: k.c, Socket: cfg.KubernetesConfig.CRISocket})
r, err := cruntime.New(cruntime.Config{
Type: cfg.KubernetesConfig.ContainerRuntime,
Runner: k.c, Socket: cfg.KubernetesConfig.CRISocket,
})
if err != nil {
return errors.Wrap(err, "runtime")
}
if err := r.Preload(cfg.KubernetesConfig); err != nil {
glog.Infof("prelaoding failed, will try to load cached images: %v", err)
glog.Infof("preload failed, will try to load cached images: %v", err)
}
if cfg.KubernetesConfig.ShouldLoadCachedImages {
@ -942,16 +940,16 @@ func adviseNodePressure(err error, name string, drv string) {
glog.Warning(diskErr)
out.WarningT("The node {{.name}} has ran out of disk space.", out.V{"name": name})
// generic advice for all drivers
out.T(out.Tip, "Please free up disk or prune images.")
out.T(style.Tip, "Please free up disk or prune images.")
if driver.IsVM(drv) {
out.T(out.Stopped, "Please create a cluster with bigger disk size: `minikube start --disk SIZE_MB` ")
out.T(style.Stopped, "Please create a cluster with bigger disk size: `minikube start --disk SIZE_MB` ")
} else if drv == oci.Docker && runtime.GOOS != "linux" {
out.T(out.Stopped, "Please increse Desktop's disk size.")
out.T(style.Stopped, "Please increse Desktop's disk size.")
if runtime.GOOS == "darwin" {
out.T(out.Documentation, "Documentation: {{.url}}", out.V{"url": "https://docs.docker.com/docker-for-mac/space/"})
out.T(style.Documentation, "Documentation: {{.url}}", out.V{"url": "https://docs.docker.com/docker-for-mac/space/"})
}
if runtime.GOOS == "windows" {
out.T(out.Documentation, "Documentation: {{.url}}", out.V{"url": "https://docs.docker.com/docker-for-windows/"})
out.T(style.Documentation, "Documentation: {{.url}}", out.V{"url": "https://docs.docker.com/docker-for-windows/"})
}
}
out.ErrLn("")
@ -962,16 +960,16 @@ func adviseNodePressure(err error, name string, drv string) {
out.ErrLn("")
glog.Warning(memErr)
out.WarningT("The node {{.name}} has ran out of memory.", out.V{"name": name})
out.T(out.Tip, "Check if you have unnecessary pods running by running 'kubectl get po -A")
out.T(style.Tip, "Check if you have unnecessary pods running by running 'kubectl get po -A")
if driver.IsVM(drv) {
out.T(out.Stopped, "Consider creating a cluster with larger memory size using `minikube start --memory SIZE_MB` ")
out.T(style.Stopped, "Consider creating a cluster with larger memory size using `minikube start --memory SIZE_MB` ")
} else if drv == oci.Docker && runtime.GOOS != "linux" {
out.T(out.Stopped, "Consider increasing Docker Desktop's memory size.")
out.T(style.Stopped, "Consider increasing Docker Desktop's memory size.")
if runtime.GOOS == "darwin" {
out.T(out.Documentation, "Documentation: {{.url}}", out.V{"url": "https://docs.docker.com/docker-for-mac/space/"})
out.T(style.Documentation, "Documentation: {{.url}}", out.V{"url": "https://docs.docker.com/docker-for-mac/space/"})
}
if runtime.GOOS == "windows" {
out.T(out.Documentation, "Documentation: {{.url}}", out.V{"url": "https://docs.docker.com/docker-for-windows/"})
out.T(style.Documentation, "Documentation: {{.url}}", out.V{"url": "https://docs.docker.com/docker-for-windows/"})
}
}
out.ErrLn("")

View File

@ -22,6 +22,7 @@ import (
"github.com/pkg/browser"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
// OpenURL opens a new browser window pointing to URL.
@ -29,7 +30,7 @@ func OpenURL(url string) error {
if runtime.GOOS == "linux" {
_, err := exec.LookPath("xdg-open")
if err != nil {
out.T(out.URL, url)
out.T(style.URL, url)
return nil
}
}

View File

@ -31,6 +31,7 @@ import (
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/reason"
)
// This init function is used to set the logtostderr variable to false so that INFO level log info does not clutter the CLI
@ -38,7 +39,7 @@ import (
// see: https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/util/logs/logs.go#L32-L34
func init() {
if err := flag.Set("logtostderr", "false"); err != nil {
exit.WithError("unable to set logtostderr", err)
exit.Error(reason.InternalFlagSet, "unable to set logtostderr", err)
}
// Setting the default client to native gives much better performance.

View File

@ -35,7 +35,7 @@ import (
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/download"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/sysinit"
)
@ -130,8 +130,8 @@ func (r *Containerd) Name() string {
}
// Style is the console style for containerd
func (r *Containerd) Style() out.StyleEnum {
return out.Containerd
func (r *Containerd) Style() style.Enum {
return style.Containerd
}
// Version retrieves the current version of this runtime

View File

@ -33,7 +33,7 @@ import (
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/download"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/sysinit"
)
@ -69,8 +69,8 @@ func (r *CRIO) Name() string {
}
// Style is the console style for CRIO
func (r *CRIO) Style() out.StyleEnum {
return out.CRIO
func (r *CRIO) Style() style.Enum {
return style.CRIO
}
// Version retrieves the current version of this runtime

View File

@ -27,7 +27,7 @@ import (
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/sysinit"
)
@ -76,7 +76,7 @@ type Manager interface {
// Available returns an error if it is not possible to use this runtime on a host
Available() error
// Style is an associated StyleEnum for Name()
Style() out.StyleEnum
Style() style.Enum
// CGroupDriver returns cgroup driver ("cgroupfs" or "systemd")
CGroupDriver() (string, error)
@ -175,7 +175,6 @@ func ContainerStatusCommand() string {
// disableOthers disables all other runtimes except for me.
func disableOthers(me Manager, cr CommandRunner) error {
// valid values returned by manager.Name()
runtimes := []string{"containerd", "crio", "docker"}
for _, name := range runtimes {

View File

@ -31,7 +31,7 @@ import (
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/docker"
"k8s.io/minikube/pkg/minikube/download"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/sysinit"
)
@ -49,6 +49,7 @@ func NewErrISOFeature(missing string) *ErrISOFeature {
missing: missing,
}
}
func (e *ErrISOFeature) Error() string {
return e.missing
}
@ -66,8 +67,8 @@ func (r *Docker) Name() string {
}
// Style is the console style for Docker
func (r *Docker) Style() out.StyleEnum {
return out.Docker
func (r *Docker) Style() style.Enum {
return style.Docker
}
// Version retrieves the current version of this runtime
@ -154,7 +155,6 @@ func (r *Docker) LoadImage(path string) error {
return errors.Wrap(err, "loadimage docker.")
}
return nil
}
// CGroupDriver returns cgroup driver ("cgroupfs" or "systemd")

View File

@ -23,6 +23,7 @@ import (
"github.com/blang/semver"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
func driverWithChecksumURL(name string, v semver.Version) string {
@ -32,11 +33,11 @@ func driverWithChecksumURL(name string, v semver.Version) string {
// Driver downloads an arbitrary driver
func Driver(name string, destination string, v semver.Version) error {
out.T(out.FileDownload, "Downloading driver {{.driver}}:", out.V{"driver": name})
out.T(style.FileDownload, "Downloading driver {{.driver}}:", out.V{"driver": name})
if err := download(driverWithChecksumURL(name, v), destination); err != nil {
return errors.Wrap(err, "download")
}
// Give downloaded drivers a baseline decent file permission
return os.Chmod(destination, 0755)
return os.Chmod(destination, 0o755)
}

View File

@ -30,6 +30,7 @@ import (
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/lock"
"k8s.io/minikube/pkg/version"
)
@ -126,7 +127,7 @@ func downloadISO(isoURL string, skipChecksum bool) error {
return nil
}
out.T(out.ISODownload, "Downloading VM boot image ...")
out.T(style.ISODownload, "Downloading VM boot image ...")
urlWithChecksum := isoURL + "?checksum=file:" + isoURL + ".sha256"
if skipChecksum {

View File

@ -34,6 +34,7 @@ import (
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
const (
@ -86,7 +87,6 @@ func remoteTarballURL(k8sVersion, containerRuntime string) string {
// PreloadExists returns true if there is a preloaded tarball that can be used
func PreloadExists(k8sVersion, containerRuntime string, forcePreload ...bool) bool {
// TODO (#8166): Get rid of the need for this and viper at all
force := false
if len(forcePreload) > 0 {
@ -138,7 +138,7 @@ func Preload(k8sVersion, containerRuntime string) error {
return nil
}
out.T(out.FileDownload, "Downloading Kubernetes {{.version}} preload ...", out.V{"version": k8sVersion})
out.T(style.FileDownload, "Downloading Kubernetes {{.version}} preload ...", out.V{"version": k8sVersion})
url := remoteTarballURL(k8sVersion, containerRuntime)
if err := download(url, targetPath); err != nil {
@ -168,7 +168,7 @@ func saveChecksumFile(k8sVersion, containerRuntime string) error {
return errors.Wrap(err, "getting storage object")
}
checksum := attrs.MD5
return ioutil.WriteFile(PreloadChecksumPath(k8sVersion, containerRuntime), checksum, 0644)
return ioutil.WriteFile(PreloadChecksumPath(k8sVersion, containerRuntime), checksum, 0o644)
}
// verifyChecksum returns true if the checksum of the local binary matches

View File

@ -32,6 +32,7 @@ import (
"k8s.io/minikube/pkg/minikube/download"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/lock"
)
@ -90,7 +91,7 @@ func fixDriverPermissions(name string, path string, interactive bool) error {
example.WriteString(fmt.Sprintf(" $ %s \n", strings.Join(c.Args, " ")))
}
out.T(out.Permissions, "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\n\n{{ .example }}\n", out.V{"driver": name, "example": example.String()})
out.T(style.Permissions, "The '{{.driver}}' driver requires elevated permissions. The following commands will be executed:\n\n{{ .example }}\n", out.V{"driver": name, "example": example.String()})
for _, c := range cmds {
testArgs := append([]string{"-n"}, c.Args[1:]...)
test := exec.Command("sudo", testArgs...)

View File

@ -20,65 +20,56 @@ package exit
import (
"os"
"runtime"
"runtime/debug"
"github.com/golang/glog"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/problem"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
// Exit codes based on sysexits(3)
const (
Failure = 1 // Failure represents a general failure code
Interrupted = 2 // Ctrl-C (SIGINT)
BadUsage = 64 // Usage represents an incorrect command line
Data = 65 // Data represents incorrect data supplied by the user
NoInput = 66 // NoInput represents that the input file did not exist or was not readable
Unavailable = 69 // Unavailable represents when a service was unavailable
Software = 70 // Software represents an internal software error.
IO = 74 // IO represents an I/O error
Permissions = 77 // Permissions represents a permissions error
Config = 78 // Config represents an unconfigured or misconfigured state
InsufficientStorage = 507 // InsufficientStorage represents insufficient storage in the VM/container
)
// UsageT outputs a templated usage error and exits with error code 64
func UsageT(format string, a ...out.V) {
out.ErrWithExitCode(out.Usage, format, BadUsage, a...)
os.Exit(BadUsage)
}
// WithCodeT outputs a templated fatal error message and exits with the supplied error code.
func WithCodeT(code int, format string, a ...out.V) {
out.ErrWithExitCode(out.FatalType, format, code, a...)
os.Exit(code)
}
// WithError outputs an error and exits.
func WithError(msg string, err error) {
glog.Infof("WithError(%s)=%v called from:\n%s", msg, err, debug.Stack())
p := problem.FromError(err, runtime.GOOS)
if p != nil {
if out.JSON {
p.DisplayJSON(Config)
os.Exit(Config)
}
WithProblem(msg, err, p)
os.Exit(Config)
// Message outputs a templated message and exits without interpretation
func Message(r reason.Kind, format string, args ...out.V) {
if r.ID == "" {
glog.Errorf("supplied reason has no ID: %+v", r)
}
out.DisplayError(msg, err)
os.Exit(Software)
if r.Style == style.None {
r.Style = style.Failure
}
if r.ExitCode == 0 {
r.ExitCode = reason.ExProgramError
}
if len(args) == 0 {
args = append(args, out.V{})
}
// No need to manipulate the message for JSON output
if out.JSON {
out.Error(r, format, args...)
} else {
args[0]["fatal_msg"] = out.Fmt(format, args...)
args[0]["fatal_code"] = r.ID
out.Error(r, "Exiting due to {{.fatal_code}}: {{.fatal_msg}}", args...)
}
os.Exit(r.ExitCode)
}
// WithProblem outputs info related to a known problem and exits.
func WithProblem(msg string, err error, p *problem.Problem) {
out.ErrT(out.Empty, "")
out.FailureT("[{{.id}}] {{.msg}} {{.error}}", out.V{"msg": msg, "id": p.ID, "error": p.Err})
p.Display()
if p.ShowIssueLink {
out.ErrT(out.Empty, "")
out.ErrT(out.Sad, "If the above advice does not help, please let us know: ")
out.ErrT(out.URL, "https://github.com/kubernetes/minikube/issues/new/choose")
}
// Advice is syntactic sugar to output a message with dynamically generated advice
func Advice(r reason.Kind, msg string, advice string, a ...out.V) {
r.Advice = out.Fmt(advice, a...)
Message(r, msg, a...)
}
// Error takes a fatal error, matches it against known issues, and outputs the best message for it
func Error(r reason.Kind, msg string, err error) {
ki := reason.MatchKnownIssue(r, err, runtime.GOOS)
if ki != nil {
Message(*ki, err.Error())
}
// By default, unmatched errors should show a link
r.NewIssueLink = true
Message(r, err.Error())
}

View File

@ -56,10 +56,7 @@ func SetCurrentContext(name string, configPath ...string) error {
return errors.Wrap(err, "Error getting kubeconfig status")
}
kcfg.CurrentContext = name
if err := writeToFile(kcfg, fPath); err != nil {
return errors.Wrap(err, "writing kubeconfig")
}
return nil
return writeToFile(kcfg, fPath)
}
// DeleteContext deletes the specified machine's kubeconfig context

View File

@ -34,6 +34,7 @@ import (
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/cruntime"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
// rootCauses are regular expressions that match known failures
@ -148,7 +149,7 @@ func OutputProblems(problems map[string][]string, maxLines int) {
lines = lines[len(lines)-maxLines:]
}
for _, l := range lines {
out.T(out.LogEntry, l)
out.T(style.LogEntry, l)
}
}
}
@ -167,9 +168,9 @@ func Output(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.Cluster
failed := []string{}
for i, name := range names {
if i > 0 {
out.T(out.Empty, "")
out.T(style.Empty, "")
}
out.T(out.Empty, "==> {{.name}} <==", out.V{"name": name})
out.T(style.Empty, "==> {{.name}} <==", out.V{"name": name})
var b bytes.Buffer
c := exec.Command("/bin/bash", "-c", cmds[name])
c.Stdout = &b
@ -181,7 +182,7 @@ func Output(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.Cluster
}
scanner := bufio.NewScanner(&b)
for scanner.Scan() {
out.T(out.Empty, scanner.Text())
out.T(style.Empty, scanner.Text())
}
}

View File

@ -22,43 +22,43 @@ import (
"github.com/pkg/errors"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
// MaybeDisplayAdvice will provide advice without exiting, so minikube has a chance to try the failover
func MaybeDisplayAdvice(err error, driver string) {
if errors.Is(err, oci.ErrDaemonInfo) {
out.ErrLn("")
out.ErrT(out.Conflict, "{{.driver_name}} couldn't proceed because {{.driver_name}} service is not healthy.", out.V{"driver_name": driver})
out.ErrT(style.Conflict, "{{.driver_name}} couldn't proceed because {{.driver_name}} service is not healthy.", out.V{"driver_name": driver})
}
if errors.Is(err, oci.ErrExitedUnexpectedly) {
out.ErrLn("")
out.ErrT(out.Conflict, "The minikube {{.driver_name}} container exited unexpectedly.", out.V{"driver_name": driver})
out.ErrT(style.Conflict, "The minikube {{.driver_name}} container exited unexpectedly.", out.V{"driver_name": driver})
}
if errors.Is(err, oci.ErrExitedUnexpectedly) || errors.Is(err, oci.ErrDaemonInfo) {
out.T(out.Tip, "If you are still interested to make {{.driver_name}} driver work. The following suggestions might help you get passed this issue:", out.V{"driver_name": driver})
out.T(out.Empty, `
out.T(style.Tip, "If you are still interested to make {{.driver_name}} driver work. The following suggestions might help you get passed this issue:", out.V{"driver_name": driver})
out.T(style.Empty, `
- Prune unused {{.driver_name}} images, volumes and abandoned containers.`, out.V{"driver_name": driver})
out.T(out.Empty, `
out.T(style.Empty, `
- Restart your {{.driver_name}} service`, out.V{"driver_name": driver})
if runtime.GOOS != "linux" {
out.T(out.Empty, `
out.T(style.Empty, `
- Ensure your {{.driver_name}} daemon has access to enough CPU/memory resources. `, out.V{"driver_name": driver})
if runtime.GOOS == "darwin" && driver == oci.Docker {
out.T(out.Empty, `
out.T(style.Empty, `
- Docs https://docs.docker.com/docker-for-mac/#resources`, out.V{"driver_name": driver})
}
if runtime.GOOS == "windows" && driver == oci.Docker {
out.T(out.Empty, `
out.T(style.Empty, `
- Docs https://docs.docker.com/docker-for-windows/#resources`, out.V{"driver_name": driver})
}
}
out.T(out.Empty, `
out.T(style.Empty, `
- Delete and recreate minikube cluster
minikube delete
minikube start --driver={{.driver_name}}`, out.V{"driver_name": driver})
// TODO #8348: maybe advice user if to set the --force-systemd https://github.com/kubernetes/minikube/issues/8348
}
}

View File

@ -47,6 +47,7 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/registry"
)
@ -220,7 +221,6 @@ func (api *LocalClient) Create(h *host.Host) error {
}
for _, step := range steps {
if err := step.f(); err != nil {
return errors.Wrap(err, step.name)
}
@ -281,7 +281,7 @@ func (cg *CertGenerator) ValidateCertificate(addr string, authOptions *auth.Opti
func registerDriver(drvName string) {
def := registry.Driver(drvName)
if def.Empty() {
exit.UsageT("unsupported or missing driver: {{.name}}", out.V{"name": drvName})
exit.Message(reason.Usage, "unsupported or missing driver: {{.name}}", out.V{"name": drvName})
}
plugin.RegisterDriver(def.Init())
}

View File

@ -31,6 +31,7 @@ import (
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
// deleteOrphanedKIC attempts to delete an orphaned docker instance for machines without a config file
@ -95,7 +96,7 @@ func DeleteHost(api libmachine.API, machineName string, deleteAbandoned ...bool)
time.Sleep(1 * time.Second)
}
out.T(out.DeletingHost, `Deleting "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": machineName, "driver_name": host.DriverName})
out.T(style.DeletingHost, `Deleting "{{.profile_name}}" in {{.driver_name}} ...`, out.V{"profile_name": machineName, "driver_name": host.DriverName})
return delete(api, host, machineName)
}

View File

@ -34,6 +34,7 @@ import (
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
)
// hostRunner is a minimal host.Host based interface for running commands
@ -112,7 +113,7 @@ func recreateIfNeeded(api libmachine.API, cc *config.ClusterConfig, n *config.No
}
if !me || err == constants.ErrMachineMissing {
out.T(out.Shrug, `{{.driver_name}} "{{.cluster}}" {{.machine_type}} is missing, will recreate.`, out.V{"driver_name": cc.Driver, "cluster": machineName, "machine_type": machineType})
out.T(style.Shrug, `{{.driver_name}} "{{.cluster}}" {{.machine_type}} is missing, will recreate.`, out.V{"driver_name": cc.Driver, "cluster": machineName, "machine_type": machineType})
demolish(api, *cc, *n, h)
glog.Infof("Sleeping 1 second for extra luck!")
@ -134,13 +135,13 @@ func recreateIfNeeded(api libmachine.API, cc *config.ClusterConfig, n *config.No
if s == state.Running {
if !recreated {
out.T(out.Running, `Updating the running {{.driver_name}} "{{.cluster}}" {{.machine_type}} ...`, out.V{"driver_name": cc.Driver, "cluster": machineName, "machine_type": machineType})
out.T(style.Running, `Updating the running {{.driver_name}} "{{.cluster}}" {{.machine_type}} ...`, out.V{"driver_name": cc.Driver, "cluster": machineName, "machine_type": machineType})
}
return h, nil
}
if !recreated {
out.T(out.Restarting, `Restarting existing {{.driver_name}} {{.machine_type}} for "{{.cluster}}" ...`, out.V{"driver_name": cc.Driver, "cluster": machineName, "machine_type": machineType})
out.T(style.Restarting, `Restarting existing {{.driver_name}} {{.machine_type}} for "{{.cluster}}" ...`, out.V{"driver_name": cc.Driver, "cluster": machineName, "machine_type": machineType})
}
if err := h.Driver.Start(); err != nil {
MaybeDisplayAdvice(err, h.DriverName)
@ -160,7 +161,7 @@ func maybeWarnAboutEvalEnv(drver string, name string) {
return
}
if os.Getenv(constants.MinikubeActiveDockerdEnv) != "" {
out.T(out.Notice, "Noticed you have an activated docker-env on {{.driver_name}} driver in this terminal:", out.V{"driver_name": drver})
out.T(style.Notice, "Noticed you have an activated docker-env on {{.driver_name}} driver in this terminal:", out.V{"driver_name": drver})
// TODO: refactor docker-env package to generate only eval command per shell. https://github.com/kubernetes/minikube/issues/6887
out.WarningT(`Please re-eval your docker-env, To ensure your environment variables have updated ports:
@ -169,7 +170,7 @@ func maybeWarnAboutEvalEnv(drver string, name string) {
`, out.V{"profile_name": name})
}
if os.Getenv(constants.MinikubeActivePodmanEnv) != "" {
out.T(out.Notice, "Noticed you have an activated podman-env on {{.driver_name}} driver in this terminal:", out.V{"driver_name": drver})
out.T(style.Notice, "Noticed you have an activated podman-env on {{.driver_name}} driver in this terminal:", out.V{"driver_name": drver})
// TODO: refactor podman-env package to generate only eval command per shell. https://github.com/kubernetes/minikube/issues/6887
out.WarningT(`Please re-eval your podman-env, To ensure your environment variables have updated ports:
@ -177,7 +178,6 @@ func maybeWarnAboutEvalEnv(drver string, name string) {
`, out.V{"profile_name": name})
}
}
// ensureGuestClockSync ensures that the guest system clock is relatively in-sync

View File

@ -28,6 +28,7 @@ import (
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/style"
)
// HostInfo holds information on the user's machine
@ -80,7 +81,7 @@ func showLocalOsRelease() {
}
register.Reg.SetStep(register.LocalOSRelease)
out.T(out.Provisioner, "OS release is {{.pretty_name}}", out.V{"pretty_name": osReleaseInfo.PrettyName})
out.T(style.Provisioner, "OS release is {{.pretty_name}}", out.V{"pretty_name": osReleaseInfo.PrettyName})
}
// logRemoteOsRelease shows systemd information about the current linux distribution, on the remote VM
@ -99,8 +100,10 @@ func logRemoteOsRelease(r command.Runner) {
glog.Infof("Remote host: %s", osReleaseInfo.PrettyName)
}
var cachedSystemMemoryLimit *mem.VirtualMemoryStat
var cachedSystemMemoryErr *error
var (
cachedSystemMemoryLimit *mem.VirtualMemoryStat
cachedSystemMemoryErr *error
)
// cachedSysMemLimit will return a cached limit for the system's virtual memory.
func cachedSysMemLimit() (*mem.VirtualMemoryStat, error) {
@ -115,8 +118,10 @@ func cachedSysMemLimit() (*mem.VirtualMemoryStat, error) {
return cachedSystemMemoryLimit, *cachedSystemMemoryErr
}
var cachedDisk *disk.UsageStat
var cachedDiskInfoErr *error
var (
cachedDisk *disk.UsageStat
cachedDiskInfoErr *error
)
// cachedDiskInfo will return a cached disk usage info
func cachedDiskInfo() (disk.UsageStat, error) {
@ -131,8 +136,10 @@ func cachedDiskInfo() (disk.UsageStat, error) {
return *cachedDisk, *cachedDiskInfoErr
}
var cachedCPU *[]cpu.InfoStat
var cachedCPUErr *error
var (
cachedCPU *[]cpu.InfoStat
cachedCPUErr *error
)
// cachedCPUInfo will return a cached cpu info
func cachedCPUInfo() ([]cpu.InfoStat, error) {

View File

@ -45,26 +45,26 @@ import (
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/proxy"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/registry"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/vmpath"
"k8s.io/minikube/pkg/util/lock"
)
var (
// requiredDirectories are directories to create on the host during setup
requiredDirectories = []string{
vmpath.GuestAddonsDir,
vmpath.GuestManifestsDir,
vmpath.GuestEphemeralDir,
vmpath.GuestPersistentDir,
vmpath.GuestKubernetesCertsDir,
path.Join(vmpath.GuestPersistentDir, "images"),
path.Join(vmpath.GuestPersistentDir, "binaries"),
vmpath.GuestGvisorDir,
vmpath.GuestCertAuthDir,
vmpath.GuestCertStoreDir,
}
)
// requiredDirectories are directories to create on the host during setup
var requiredDirectories = []string{
vmpath.GuestAddonsDir,
vmpath.GuestManifestsDir,
vmpath.GuestEphemeralDir,
vmpath.GuestPersistentDir,
vmpath.GuestKubernetesCertsDir,
path.Join(vmpath.GuestPersistentDir, "images"),
path.Join(vmpath.GuestPersistentDir, "binaries"),
vmpath.GuestGvisorDir,
vmpath.GuestCertAuthDir,
vmpath.GuestCertStoreDir,
}
// StartHost starts a host VM.
func StartHost(api libmachine.API, cfg *config.ClusterConfig, n *config.Node) (*host.Host, bool, error) {
@ -217,17 +217,18 @@ func postStartValidations(h *host.Host, drvName string) {
if err != nil {
glog.Warningf("error getting command runner: %v", err)
}
// make sure /var isn't full, otherwise warn
// make sure /var isn't full, as pod deployments will fail if it is
percentageFull, err := DiskUsed(r, "/var")
if err != nil {
glog.Warningf("error getting percentage of /var that is free: %v", err)
}
if percentageFull >= 99 {
exit.WithCodeT(exit.InsufficientStorage, "docker daemon out of memory. No space left on device")
exit.Message(reason.RsrcInsufficientDockerStorage, `Docker is out of disk space! (/var is at {{.p}}% of capacity)`, out.V{"p": percentageFull})
}
if percentageFull > 80 {
out.ErrT(out.Tip, "The docker daemon is almost out of memory, run 'docker system prune' to free up space")
if percentageFull >= 85 {
out.WarnReason(reason.RsrcInsufficientDockerStorage, `Docker is nearly out of disk space, which may cause deployments to fail! ({{.p}}% of capacity)`, out.V{"p": percentageFull})
}
}
@ -292,7 +293,6 @@ func acquireMachinesLock(name string, drv string) (mutex.Releaser, error) {
spec.Timeout = 13 * time.Minute
if driver.IsKIC(drv) {
spec.Timeout = 10 * time.Minute
}
glog.Infof("acquiring machines lock for %s: %+v", name, spec)
@ -311,17 +311,17 @@ func showHostInfo(cfg config.ClusterConfig) {
info, cpuErr, memErr, DiskErr := CachedHostInfo()
if cpuErr == nil && memErr == nil && DiskErr == nil {
register.Reg.SetStep(register.RunningLocalhost)
out.T(out.StartingNone, "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...", out.V{"number_of_cpus": info.CPUs, "memory_size": info.Memory, "disk_size": info.DiskSize})
out.T(style.StartingNone, "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...", out.V{"number_of_cpus": info.CPUs, "memory_size": info.Memory, "disk_size": info.DiskSize})
}
return
}
if driver.IsKIC(cfg.Driver) { // TODO:medyagh add free disk space on docker machine
register.Reg.SetStep(register.CreatingContainer)
out.T(out.StartingVM, "Creating {{.driver_name}} {{.machine_type}} (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB) ...", out.V{"driver_name": cfg.Driver, "number_of_cpus": cfg.CPUs, "memory_size": cfg.Memory, "machine_type": machineType})
out.T(style.StartingVM, "Creating {{.driver_name}} {{.machine_type}} (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB) ...", out.V{"driver_name": cfg.Driver, "number_of_cpus": cfg.CPUs, "memory_size": cfg.Memory, "machine_type": machineType})
return
}
register.Reg.SetStep(register.CreatingVM)
out.T(out.StartingVM, "Creating {{.driver_name}} {{.machine_type}} (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...", out.V{"driver_name": cfg.Driver, "number_of_cpus": cfg.CPUs, "memory_size": cfg.Memory, "disk_size": cfg.DiskSize, "machine_type": machineType})
out.T(style.StartingVM, "Creating {{.driver_name}} {{.machine_type}} (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...", out.V{"driver_name": cfg.Driver, "number_of_cpus": cfg.CPUs, "memory_size": cfg.Memory, "disk_size": cfg.DiskSize, "machine_type": machineType})
}
// AddHostAlias makes fine adjustments to pod resources that aren't possible via kubeadm config.

View File

@ -29,6 +29,7 @@ import (
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/retry"
)
@ -41,7 +42,7 @@ func StopHost(api libmachine.API, machineName string) error {
return errors.Wrapf(err, "load")
}
out.T(out.Stopping, `Stopping node "{{.name}}" ...`, out.V{"name": machineName})
out.T(style.Stopping, `Stopping node "{{.name}}" ...`, out.V{"name": machineName})
return stop(h)
}
@ -79,7 +80,7 @@ func trySSHPowerOff(h *host.Host) error {
return nil
}
out.T(out.Shutdown, `Powering off "{{.profile_name}}" via SSH ...`, out.V{"profile_name": h.Name})
out.T(style.Shutdown, `Powering off "{{.profile_name}}" via SSH ...`, out.V{"profile_name": h.Name})
// differnet for kic because RunSSHCommand is not implemented by kic
if driver.IsKIC(h.DriverName) {
err := oci.ShutDown(h.DriverName, h.Name)

View File

@ -34,6 +34,8 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
// ClusterController holds all the needed information for a minikube cluster
@ -64,15 +66,16 @@ func Partial(name string, miniHome ...string) (libmachine.API, *config.ClusterCo
glog.Infof("Loading cluster: %s", name)
api, err := machine.NewAPIClient(miniHome...)
if err != nil {
exit.WithError("libmachine failed", err)
exit.Error(reason.NewAPIClient, "libmachine failed", err)
}
cc, err := config.Load(name, miniHome...)
if err != nil {
if config.IsNotExist(err) {
out.T(out.Shrug, `There is no local cluster named "{{.cluster}}"`, out.V{"cluster": name})
exitTip("start", name, exit.Data)
out.T(style.Shrug, `There is no local cluster named "{{.cluster}}"`, out.V{"cluster": name})
exitTip("start", name, reason.ExGuestNotFound)
}
exit.WithError("Error getting cluster config", err)
exit.Error(reason.HostConfigLoad, "Error getting cluster config", err)
}
return api, cc
@ -84,43 +87,43 @@ func Running(name string) ClusterController {
cp, err := config.PrimaryControlPlane(cc)
if err != nil {
exit.WithError("Unable to find control plane", err)
exit.Error(reason.GuestCpConfig, "Unable to find control plane", err)
}
machineName := driver.MachineName(*cc, cp)
hs, err := machine.Status(api, machineName)
if err != nil {
exit.WithError("Unable to get machine status", err)
exit.Error(reason.GuestStatus, "Unable to get machine status", err)
}
if hs == state.None.String() {
out.T(out.Shrug, `The control plane node "{{.name}}" does not exist.`, out.V{"name": cp.Name})
exitTip("start", name, exit.Unavailable)
out.T(style.Shrug, `The control plane node "{{.name}}" does not exist.`, out.V{"name": cp.Name})
exitTip("start", name, reason.ExGuestNotFound)
}
if hs == state.Stopped.String() {
out.T(out.Shrug, `The control plane node must be running for this command`)
exitTip("start", name, exit.Unavailable)
out.T(style.Shrug, `The control plane node must be running for this command`)
exitTip("start", name, reason.ExGuestUnavailable)
}
if hs != state.Running.String() {
out.T(out.Shrug, `The control plane node is not running (state={{.state}})`, out.V{"name": cp.Name, "state": hs})
exitTip("start", name, exit.Unavailable)
out.T(style.Shrug, `The control plane node is not running (state={{.state}})`, out.V{"name": cp.Name, "state": hs})
exitTip("start", name, reason.ExSvcUnavailable)
}
host, err := machine.LoadHost(api, name)
if err != nil {
exit.WithError("Unable to load host", err)
exit.Error(reason.GuestLoadHost, "Unable to load host", err)
}
cr, err := machine.CommandRunner(host)
if err != nil {
exit.WithError("Unable to get command runner", err)
exit.Error(reason.InternalCommandRunner, "Unable to get command runner", err)
}
hostname, ip, port, err := driver.ControlPlaneEndpoint(cc, &cp, host.DriverName)
if err != nil {
exit.WithError("Unable to get forwarded endpoint", err)
exit.Error(reason.DrvCPEndpoint, "Unable to get forwarded endpoint", err)
}
return ClusterController{
@ -144,18 +147,18 @@ func Healthy(name string) ClusterController {
as, err := kverify.APIServerStatus(co.CP.Runner, co.CP.Hostname, co.CP.Port)
if err != nil {
out.FailureT(`Unable to get control plane status: {{.error}}`, out.V{"error": err})
exitTip("delete", name, exit.Unavailable)
exitTip("delete", name, reason.ExSvcError)
}
if as == state.Paused {
out.T(out.Shrug, `The control plane for "{{.name}}" is paused!`, out.V{"name": name})
exitTip("unpause", name, exit.Unavailable)
out.T(style.Shrug, `The control plane for "{{.name}}" is paused!`, out.V{"name": name})
exitTip("unpause", name, reason.ExSvcConfig)
}
if as != state.Running {
out.T(out.Shrug, `This control plane is not running! (state={{.state}})`, out.V{"state": as.String()})
out.T(style.Shrug, `This control plane is not running! (state={{.state}})`, out.V{"state": as.String()})
out.WarningT(`This is unusual - you may want to investigate using "{{.command}}"`, out.V{"command": ExampleCmd(name, "logs")})
exitTip("start", name, exit.Unavailable)
exitTip("start", name, reason.ExSvcUnavailable)
}
return co
}
@ -171,6 +174,6 @@ func ExampleCmd(cname string, action string) string {
// exitTip returns an action tip and exits
func exitTip(action string, profile string, code int) {
command := ExampleCmd(profile, action)
out.T(out.Workaround, `To fix this, run: "{{.command}}"`, out.V{"command": command})
out.T(style.Workaround, `To fix this, run: "{{.command}}"`, out.V{"command": command})
os.Exit(code)
}

View File

@ -20,56 +20,46 @@ import (
"runtime"
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
// MaybeExitWithAdvice before exiting will try to check for different error types and provide advice if we know for sure what the error is
func MaybeExitWithAdvice(err error) {
// ExitIfFatal before exiting will try to check for different error types and provide advice if we know for sure what the error is
func ExitIfFatal(err error) {
if err == nil {
return
}
if errors.Is(err, oci.ErrWindowsContainers) {
out.ErrLn("")
out.ErrT(out.Conflict, "Your Docker Desktop container OS type is Windows but Linux is required.")
out.T(out.Warning, "Please change Docker settings to use Linux containers instead of Windows containers.")
out.T(out.Documentation, "https://minikube.sigs.k8s.io/docs/drivers/docker/#verify-docker-container-type-is-linux")
exit.UsageT(`You can verify your Docker container type by running:
{{.command}}
`, out.V{"command": "docker info --format '{{.OSType}}'"})
exit.Message(reason.Kind{
ID: "PROVIDER_DOCKER_CONTAINER_OS",
ExitCode: reason.ExProviderConflict,
Style: style.Conflict,
URL: "https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers",
Advice: "From the Docker Desktop menu, select 'Switch to Linux containers'",
}, "Docker Desktop is configured for Windows containers, but Linux containers are required for minikube")
}
if errors.Is(err, oci.ErrCPUCountLimit) {
out.ErrLn("")
out.ErrT(out.Conflict, "{{.name}} doesn't have enough CPUs. ", out.V{"name": driver.FullName(viper.GetString("driver"))})
if runtime.GOOS != "linux" && viper.GetString("driver") == "docker" {
out.T(out.Warning, "Please consider changing your Docker Desktop's resources.")
out.T(out.Documentation, "https://docs.docker.com/config/containers/resource_constraints/")
} else {
cpuCount := viper.GetInt(cpus)
if cpuCount == 2 {
out.T(out.Tip, "Please ensure your system has {{.cpu_counts}} CPU cores.", out.V{"cpu_counts": viper.GetInt(cpus)})
} else {
out.T(out.Tip, "Please ensure your {{.driver_name}} system has access to {{.cpu_counts}} CPU cores or reduce the number of the specified CPUs", out.V{"driver_name": driver.FullName(viper.GetString("driver")), "cpu_counts": viper.GetInt(cpus)})
}
if runtime.GOOS == "darwin" {
exit.Message(reason.RsrcInsufficientDarwinDockerCores, "Docker Desktop has less than 2 CPUs configured, but Kubernetes requires at least 2 to be available")
}
exit.UsageT("Ensure your {{.driver_name}} system has enough CPUs. The minimum allowed is 2 CPUs.", out.V{"driver_name": viper.GetString("driver")})
if runtime.GOOS == "windows" {
exit.Message(reason.RsrcInsufficientWindowsDockerCores, "Docker Desktop has less than 2 CPUs configured, but Kubernetes requires at least 2 to be available")
}
exit.Message(reason.RsrcInsufficientCores, "Docker has less than 2 CPUs available, but Kubernetes requires at least 2 to be available")
}
if errors.Is(err, kubeadm.ErrNoExecLinux) {
out.ErrLn("")
out.ErrT(out.Conflict, "kubeadm binary is not executable !")
out.T(out.Documentation, "Try the solution in this link: https://github.com/kubernetes/minikube/issues/8327#issuecomment-651288459")
exit.UsageT(`Ensure the binaries are not mounted with "noexec" option. To check run:
$ findmnt
`)
exit.Message(reason.Kind{
ID: "PROVIDER_DOCKER_NOEXEC",
ExitCode: reason.ExProviderPermission,
Style: style.Permissions,
Issues: []int{8327},
Advice: "Ensure that your Docker mountpoints do not have the 'noexec' flag set",
}, "The kubeadm binary within the Docker container is not executable")
}
}

View File

@ -37,6 +37,8 @@ import (
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
const (
@ -73,17 +75,17 @@ func handleDownloadOnly(cacheGroup, kicGroup *errgroup.Group, k8sVersion string)
return
}
if err := doCacheBinaries(k8sVersion); err != nil {
exit.WithError("Failed to cache binaries", err)
exit.Error(reason.InetCacheBinaries, "Failed to cache binaries", err)
}
if _, err := CacheKubectlBinary(k8sVersion); err != nil {
exit.WithError("Failed to cache kubectl", err)
exit.Error(reason.InetCacheKubectl, "Failed to cache kubectl", err)
}
waitCacheRequiredImages(cacheGroup)
waitDownloadKicBaseImage(kicGroup)
if err := saveImagesToTarFromConfig(); err != nil {
exit.WithError("Failed to cache images to tar", err)
exit.Error(reason.InetCacheTar, "Failed to cache images to tar", err)
}
out.T(out.Check, "Download complete!")
out.T(style.Check, "Download complete!")
os.Exit(0)
}
@ -115,7 +117,7 @@ func beginDownloadKicBaseImage(g *errgroup.Group, cc *config.ClusterConfig, down
}
glog.Infof("Beginning downloading kic base image for %s with %s", cc.Driver, cc.KubernetesConfig.ContainerRuntime)
out.T(out.Pulling, "Pulling base image ...")
out.T(style.Pulling, "Pulling base image ...")
g.Go(func() error {
baseImg := cc.KicBaseImage
if baseImg == kic.BaseImage && len(cc.KubernetesConfig.ImageRepository) != 0 {
@ -163,20 +165,19 @@ func waitDownloadKicBaseImage(g *errgroup.Group) {
if err != nil {
if errors.Is(err, image.ErrGithubNeedsLogin) {
glog.Warningf("Error downloading kic artifacts: %v", err)
out.ErrT(out.Connectivity, "Unfortunately, could not download the base image {{.image_name}} ", out.V{"image_name": strings.Split(kic.BaseImage, "@")[0]})
out.ErrT(style.Connectivity, "Unfortunately, could not download the base image {{.image_name}} ", out.V{"image_name": strings.Split(kic.BaseImage, "@")[0]})
out.WarningT("In order to use the fall back image, you need to log in to the github packages registry")
out.T(out.Documentation, `Please visit the following link for documentation around this:
out.T(style.Documentation, `Please visit the following link for documentation around this:
https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages#authenticating-to-github-packages
`)
}
if errors.Is(err, image.ErrGithubNeedsLogin) || errors.Is(err, image.ErrNeedsLogin) {
exit.UsageT(`Please either authenticate to the registry or use --base-image flag to use a different registry.`)
exit.Message(reason.Usage, `Please either authenticate to the registry or use --base-image flag to use a different registry.`)
} else {
glog.Errorln("Error downloading kic artifacts: ", err)
}
}
}
glog.Info("Successfully downloaded all kic artifacts")
}

View File

@ -33,6 +33,8 @@ import (
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/lock"
)
@ -57,7 +59,7 @@ func configureMounts(wg *sync.WaitGroup) {
return
}
out.T(out.Mounting, "Creating mount {{.name}} ...", out.V{"name": viper.GetString(mountString)})
out.T(style.Mounting, "Creating mount {{.name}} ...", out.V{"name": viper.GetString(mountString)})
path := os.Args[0]
mountDebugVal := 0
if glog.V(8) {
@ -70,9 +72,9 @@ func configureMounts(wg *sync.WaitGroup) {
mountCmd.Stderr = os.Stderr
}
if err := mountCmd.Start(); err != nil {
exit.WithError("Error starting mount", err)
exit.Error(reason.GuestMount, "Error starting mount", err)
}
if err := lock.WriteFile(filepath.Join(localpath.MiniPath(), constants.MountProcessFileName), []byte(strconv.Itoa(mountCmd.Process.Pid)), 0644); err != nil {
exit.WithError("Error writing mount pid", err)
if err := lock.WriteFile(filepath.Join(localpath.MiniPath(), constants.MountProcessFileName), []byte(strconv.Itoa(mountCmd.Process.Pid)), 0o644); err != nil {
exit.Error(reason.HostMountPid, "Error writing mount pid", err)
}
}

View File

@ -34,7 +34,6 @@ import (
const (
mountString = "mount-string"
createMount = "mount"
cpus = "cpus"
)
// Add adds a new node config to an existing cluster.
@ -116,7 +115,6 @@ func Delete(cc config.ClusterConfig, name string) (*config.Node, error) {
// Retrieve finds the node by name in the given cluster
func Retrieve(cc config.ClusterConfig, name string) (*config.Node, int, error) {
for i, n := range cc.Nodes {
if n.Name == name {
return &n, i, nil

View File

@ -55,6 +55,8 @@ import (
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/proxy"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util"
"k8s.io/minikube/pkg/util/retry"
)
@ -112,14 +114,14 @@ func Start(starter Starter, apiServer bool) (*kubeconfig.Settings, error) {
bs = setupKubeAdm(starter.MachineAPI, *starter.Cfg, *starter.Node, starter.Runner)
err = bs.StartCluster(*starter.Cfg)
if err != nil {
MaybeExitWithAdvice(err)
ExitIfFatal(err)
out.LogEntries("Error starting cluster", err, logs.FindProblems(cr, bs, *starter.Cfg, starter.Runner))
return nil, err
}
// write the kubeconfig to the file system after everything required (like certs) are created by the bootstrapper
if err := kubeconfig.Update(kcs); err != nil {
return nil, errors.Wrap(err, "Failed to update kubeconfig file.")
return nil, errors.Wrap(err, "Failed kubeconfig update")
}
} else {
bs, err = cluster.Bootstrapper(starter.MachineAPI, viper.GetString(cmdcfg.Bootstrapper), *starter.Cfg, starter.Runner)
@ -161,7 +163,6 @@ func Start(starter Starter, apiServer bool) (*kubeconfig.Settings, error) {
if starter.Cfg.Driver == driver.None && len(starter.Cfg.Nodes) == 1 {
prepareNone()
}
} else {
if err := bs.UpdateNode(*starter.Cfg, *starter.Node, cr); err != nil {
return nil, errors.Wrap(err, "update node")
@ -209,9 +210,9 @@ func Provision(cc *config.ClusterConfig, n *config.Node, apiServer bool, delOnFa
register.Reg.SetStep(register.StartingNode)
name := driver.MachineName(*cc, *n)
if apiServer {
out.T(out.ThumbsUp, "Starting control plane node {{.name}} in cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
out.T(style.ThumbsUp, "Starting control plane node {{.name}} in cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
} else {
out.T(out.ThumbsUp, "Starting node {{.name}} in cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
out.T(style.ThumbsUp, "Starting node {{.name}} in cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
}
if driver.IsKIC(cc.Driver) {
@ -232,7 +233,6 @@ func Provision(cc *config.ClusterConfig, n *config.Node, apiServer bool, delOnFa
waitDownloadKicBaseImage(&kicGroup)
return startMachine(cc, n, delOnFail)
}
// ConfigureRuntimes does what needs to happen to get a runtime going.
@ -245,7 +245,7 @@ func configureRuntimes(runner cruntime.CommandRunner, cc config.ClusterConfig, k
}
cr, err := cruntime.New(co)
if err != nil {
exit.WithError("Failed runtime", err)
exit.Error(reason.InternalRuntime, "Failed runtime", err)
}
disableOthers := true
@ -259,20 +259,20 @@ func configureRuntimes(runner cruntime.CommandRunner, cc config.ClusterConfig, k
if err := cr.Preload(cc.KubernetesConfig); err != nil {
switch err.(type) {
case *cruntime.ErrISOFeature:
out.ErrT(out.Tip, "Existing disk is missing new features ({{.error}}). To upgrade, run 'minikube delete'", out.V{"error": err})
out.ErrT(style.Tip, "Existing disk is missing new features ({{.error}}). To upgrade, run 'minikube delete'", out.V{"error": err})
default:
glog.Warningf("%s preload failed: %v, falling back to caching images", cr.Name(), err)
}
if err := machine.CacheImagesForBootstrapper(cc.KubernetesConfig.ImageRepository, cc.KubernetesConfig.KubernetesVersion, viper.GetString(cmdcfg.Bootstrapper)); err != nil {
exit.WithError("Failed to cache images", err)
exit.Error(reason.RuntimeCache, "Failed to cache images", err)
}
}
}
err = cr.Enable(disableOthers, forceSystemd())
if err != nil {
exit.WithError("Failed to enable container runtime", err)
exit.Error(reason.RuntimeEnable, "Failed to enable container runtime", err)
}
return cr
@ -286,7 +286,7 @@ func forceSystemd() bool {
func setupKubeAdm(mAPI libmachine.API, cfg config.ClusterConfig, n config.Node, r command.Runner) bootstrapper.Bootstrapper {
bs, err := cluster.Bootstrapper(mAPI, viper.GetString(cmdcfg.Bootstrapper), cfg, r)
if err != nil {
exit.WithError("Failed to get bootstrapper", err)
exit.Error(reason.InternalBootstrapper, "Failed to get bootstrapper", err)
}
for _, eo := range config.ExtraOptions {
out.Infof("{{.extra_option_component_name}}.{{.key}}={{.value}}", out.V{"extra_option_component_name": eo.Component, "key": eo.Key, "value": eo.Value})
@ -295,11 +295,11 @@ func setupKubeAdm(mAPI libmachine.API, cfg config.ClusterConfig, n config.Node,
// update cluster and set up certs
if err := bs.UpdateCluster(cfg); err != nil {
exit.WithError("Failed to update cluster", err)
exit.Error(reason.KubernetesInstallFailed, "Failed to update cluster", err)
}
if err := bs.SetupCerts(cfg.KubernetesConfig, n); err != nil {
exit.WithError("Failed to setup certs", err)
exit.Error(reason.GuestCert, "Failed to setup certs", err)
}
return bs
@ -308,7 +308,7 @@ func setupKubeAdm(mAPI libmachine.API, cfg config.ClusterConfig, n config.Node,
func setupKubeconfig(h *host.Host, cc *config.ClusterConfig, n *config.Node, clusterName string) *kubeconfig.Settings {
addr, err := apiServerURL(*h, *cc, *n)
if err != nil {
exit.WithError("Failed to get API Server URL", err)
exit.Error(reason.DrvCPEndpoint, "Failed to get API Server URL", err)
}
if cc.KubernetesConfig.APIServerName != constants.APIServerName {
@ -385,7 +385,7 @@ func startHost(api libmachine.API, cc *config.ClusterConfig, n *config.Node, del
return host, exists, err
}
out.ErrT(out.Embarrassed, "StartHost failed, but will try again: {{.error}}", out.V{"error": err})
out.ErrT(style.Embarrassed, "StartHost failed, but will try again: {{.error}}", out.V{"error": err})
glog.Info("Will try again in 5 seconds ...")
// Try again, but just once to avoid making the logs overly confusing
time.Sleep(5 * time.Second)
@ -406,7 +406,7 @@ func startHost(api libmachine.API, cc *config.ClusterConfig, n *config.Node, del
// Don't use host.Driver to avoid nil pointer deref
drv := cc.Driver
out.ErrT(out.Sad, `Failed to start {{.driver}} {{.driver_type}}. "{{.cmd}}" may fix it: {{.error}}`, out.V{"driver": drv, "driver_type": driver.MachineType(drv), "cmd": mustload.ExampleCmd(cc.Name, "start"), "error": err})
out.ErrT(style.Sad, `Failed to start {{.driver}} {{.driver_type}}. "{{.cmd}}" may fix it: {{.error}}`, out.V{"driver": drv, "driver_type": driver.MachineType(drv), "cmd": mustload.ExampleCmd(cc.Name, "start"), "error": err})
return host, exists, err
}
@ -422,7 +422,7 @@ func validateNetwork(h *host.Host, r command.Runner, imageRepository string) (st
for _, k := range proxy.EnvVars {
if v := os.Getenv(k); v != "" {
if !optSeen {
out.T(out.Internet, "Found network options:")
out.T(style.Internet, "Found network options:")
optSeen = true
}
out.Infof("{{.key}}={{.value}}", out.V{"key": k, "value": v})
@ -430,7 +430,7 @@ func validateNetwork(h *host.Host, r command.Runner, imageRepository string) (st
k = strings.ToUpper(k) // for http_proxy & https_proxy
if (k == "HTTP_PROXY" || k == "HTTPS_PROXY") && !ipExcluded && !warnedOnce {
out.WarningT("You appear to be using a proxy, but your NO_PROXY environment does not include the minikube IP ({{.ip_address}}).", out.V{"ip_address": ip})
out.T(out.Documentation, "Please see {{.documentation_url}} for more details", out.V{"documentation_url": "https://minikube.sigs.k8s.io/docs/handbook/vpn_and_proxy/"})
out.T(style.Documentation, "Please see {{.documentation_url}} for more details", out.V{"documentation_url": "https://minikube.sigs.k8s.io/docs/handbook/vpn_and_proxy/"})
warnedOnce = true
}
}
@ -467,7 +467,7 @@ func trySSH(h *host.Host, ip string) error {
err := retry.Expo(dial, time.Second, 13*time.Second)
if err != nil {
out.ErrT(out.FailureType, `minikube is unable to connect to the VM: {{.error}}
out.ErrT(style.Failure, `minikube is unable to connect to the VM: {{.error}}
This is likely due to one of two reasons:
@ -505,20 +505,20 @@ func tryRegistry(r command.Runner, driverName string, imageRepository string) {
if rr, err := r.RunCmd(exec.Command("curl", opts...)); err != nil {
glog.Warningf("%s failed: %v", rr.Args, err)
out.WarningT("This {{.type}} is having trouble accessing https://{{.repository}}", out.V{"repository": imageRepository, "type": driver.MachineType(driverName)})
out.ErrT(out.Tip, "To pull new external images, you may need to configure a proxy: https://minikube.sigs.k8s.io/docs/reference/networking/proxy/")
out.ErrT(style.Tip, "To pull new external images, you may need to configure a proxy: https://minikube.sigs.k8s.io/docs/reference/networking/proxy/")
}
}
// prepareNone prepares the user and host for the joy of the "none" driver
func prepareNone() {
register.Reg.SetStep(register.ConfiguringLHEnv)
out.T(out.StartingNone, "Configuring local host environment ...")
out.T(style.StartingNone, "Configuring local host environment ...")
if viper.GetBool(config.WantNoneDriverWarning) {
out.ErrT(out.Empty, "")
out.ErrT(style.Empty, "")
out.WarningT("The 'none' driver is designed for experts who need to integrate with an existing VM")
out.ErrT(out.Tip, "Most users should use the newer 'docker' driver instead, which does not require root!")
out.ErrT(out.Documentation, "For more information, see: https://minikube.sigs.k8s.io/docs/reference/drivers/none/")
out.ErrT(out.Empty, "")
out.ErrT(style.Tip, "Most users should use the newer 'docker' driver instead, which does not require root!")
out.ErrT(style.Documentation, "For more information, see: https://minikube.sigs.k8s.io/docs/reference/drivers/none/")
out.ErrT(style.Empty, "")
}
if os.Getenv("CHANGE_MINIKUBE_NONE_USER") == "" {
@ -526,16 +526,16 @@ func prepareNone() {
out.WarningT("kubectl and minikube configuration will be stored in {{.home_folder}}", out.V{"home_folder": home})
out.WarningT("To use kubectl or minikube commands as your own user, you may need to relocate them. For example, to overwrite your own settings, run:")
out.ErrT(out.Empty, "")
out.ErrT(out.Command, "sudo mv {{.home_folder}}/.kube {{.home_folder}}/.minikube $HOME", out.V{"home_folder": home})
out.ErrT(out.Command, "sudo chown -R $USER $HOME/.kube $HOME/.minikube")
out.ErrT(out.Empty, "")
out.ErrT(style.Empty, "")
out.ErrT(style.Command, "sudo mv {{.home_folder}}/.kube {{.home_folder}}/.minikube $HOME", out.V{"home_folder": home})
out.ErrT(style.Command, "sudo chown -R $USER $HOME/.kube $HOME/.minikube")
out.ErrT(style.Empty, "")
out.ErrT(out.Tip, "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true")
out.ErrT(style.Tip, "This can also be done automatically by setting the env var CHANGE_MINIKUBE_NONE_USER=true")
}
if err := util.MaybeChownDirRecursiveToMinikubeUser(localpath.MiniPath()); err != nil {
exit.WithCodeT(exit.Permissions, "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}", out.V{"minikube_dir_path": localpath.MiniPath(), "error": err})
exit.Message(reason.HostHomeChown, "Failed to change permissions for {{.minikube_dir_path}}: {{.error}}", out.V{"minikube_dir_path": localpath.MiniPath(), "error": err})
}
}

View File

@ -32,6 +32,7 @@ import (
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/lock"
"k8s.io/minikube/pkg/version"
)
@ -66,8 +67,8 @@ func MaybePrintUpdateText(url string, lastUpdatePath string) bool {
glog.Errorf("write time failed: %v", err)
}
url := "https://github.com/kubernetes/minikube/releases/tag/v" + latestVersion.String()
out.ErrT(out.Celebrate, `minikube {{.version}} is available! Download it: {{.url}}`, out.V{"version": latestVersion, "url": url})
out.ErrT(out.Tip, "To disable this notice, run: 'minikube config set WantUpdateNotification false'\n")
out.ErrT(style.Celebrate, `minikube {{.version}} is available! Download it: {{.url}}`, out.V{"version": latestVersion, "url": url})
out.ErrT(style.Tip, "To disable this notice, run: 'minikube config set WantUpdateNotification false'\n")
return true
}
return false
@ -133,7 +134,7 @@ func GetAllVersionsFromURL(url string) (Releases, error) {
}
func writeTimeToFile(path string, inputTime time.Time) error {
err := lock.WriteFile(path, []byte(inputTime.Format(timeLayout)), 0644)
err := lock.WriteFile(path, []byte(inputTime.Format(timeLayout)), 0o644)
if err != nil {
return errors.Wrap(err, "Error writing current update time to file: ")
}

View File

@ -18,7 +18,10 @@ limitations under the License.
package out
import (
"bytes"
"fmt"
"html"
"html/template"
"io"
"os"
"strconv"
@ -27,6 +30,7 @@ import (
"github.com/golang/glog"
isatty "github.com/mattn/go-isatty"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/translate"
)
@ -35,7 +39,7 @@ import (
//
// out.SetOutFile(os.Stdout)
// out.String("Starting up!")
// out.T(out.StatusChange, "Configuring things")
// out.T(style.StatusChange, "Configuring things")
// out.SetErrFile(os.Stderr)
// out.Fatal("Oh no, everything failed.")
@ -68,12 +72,12 @@ type fdWriter interface {
type V map[string]interface{}
// T writes a stylized and templated message to stdout
func T(style StyleEnum, format string, a ...V) {
if style == Option {
func T(st style.Enum, format string, a ...V) {
if st == style.Option {
Infof(format, a...)
return
}
outStyled := ApplyTemplateFormatting(style, useColor, format, a...)
outStyled := stylized(st, useColor, format, a...)
if JSON {
register.PrintStep(outStyled)
return
@ -84,7 +88,7 @@ func T(style StyleEnum, format string, a ...V) {
// Infof is used for informational logs (options, env variables, etc)
func Infof(format string, a ...V) {
outStyled := ApplyTemplateFormatting(Option, useColor, format, a...)
outStyled := stylized(style.Option, useColor, format, a...)
if JSON {
register.PrintInfo(outStyled)
return
@ -119,19 +123,9 @@ func Ln(format string, a ...interface{}) {
String(format+"\n", a...)
}
// ErrWithExitCode includes the exit code in JSON output
func ErrWithExitCode(style StyleEnum, format string, exitcode int, a ...V) {
if JSON {
errStyled := ApplyTemplateFormatting(style, useColor, format, a...)
register.PrintErrorExitCode(errStyled, exitcode)
return
}
ErrT(style, format, a...)
}
// ErrT writes a stylized and templated error message to stderr
func ErrT(style StyleEnum, format string, a ...V) {
errStyled := ApplyTemplateFormatting(style, useColor, format, a...)
func ErrT(st style.Enum, format string, a ...V) {
errStyled := stylized(st, useColor, format, a...)
Err(errStyled)
}
@ -163,26 +157,26 @@ func ErrLn(format string, a ...interface{}) {
// SuccessT is a shortcut for writing a templated success message to stdout
func SuccessT(format string, a ...V) {
T(SuccessType, format, a...)
T(style.Success, format, a...)
}
// FatalT is a shortcut for writing a templated fatal message to stderr
func FatalT(format string, a ...V) {
ErrT(FatalType, format, a...)
ErrT(style.Fatal, format, a...)
}
// WarningT is a shortcut for writing a templated warning message to stderr
func WarningT(format string, a ...V) {
if JSON {
register.PrintWarning(ApplyTemplateFormatting(Warning, useColor, format, a...))
register.PrintWarning(stylized(style.Warning, useColor, format, a...))
return
}
ErrT(Warning, format, a...)
ErrT(style.Warning, format, a...)
}
// FailureT is a shortcut for writing a templated failure message to stderr
func FailureT(format string, a ...V) {
ErrT(FailureType, format, a...)
ErrT(style.Failure, format, a...)
}
// SetOutFile configures which writer standard output goes to.
@ -248,12 +242,12 @@ func LogEntries(msg string, err error, entries map[string][]string) {
DisplayError(msg, err)
for name, lines := range entries {
T(FailureType, "Problems detected in {{.entry}}:", V{"entry": name})
T(style.Failure, "Problems detected in {{.entry}}:", V{"entry": name})
if len(lines) > MaxLogEntries {
lines = lines[:MaxLogEntries]
}
for _, l := range lines {
T(LogEntry, l)
T(style.LogEntry, l)
}
}
}
@ -266,9 +260,46 @@ func DisplayError(msg string, err error) {
return
}
// use Warning because Error will display a duplicate message to stderr
ErrT(Empty, "")
ErrT(style.Empty, "")
FatalT("{{.msg}}: {{.err}}", V{"msg": translate.T(msg), "err": err})
ErrT(Empty, "")
ErrT(Sad, "minikube is exiting due to an error. If the above message is not useful, open an issue:")
ErrT(URL, "https://github.com/kubernetes/minikube/issues/new/choose")
ErrT(style.Empty, "")
ErrT(style.Sad, "minikube is exiting due to an error. If the above message is not useful, open an issue:")
ErrT(style.URL, "https://github.com/kubernetes/minikube/issues/new/choose")
}
// applyTmpl applies formatting
func applyTmpl(format string, a ...V) string {
if len(a) == 0 {
glog.Warningf("no arguments passed for %q - returning raw string", format)
return format
}
var buf bytes.Buffer
t, err := template.New(format).Parse(format)
if err != nil {
glog.Errorf("unable to parse %q: %v - returning raw string.", format, err)
return format
}
err = t.Execute(&buf, a[0])
if err != nil {
glog.Errorf("unable to execute %s: %v - returning raw string.", format, err)
return format
}
out := buf.String()
// Return quotes back to normal
out = html.UnescapeString(out)
// escape any outstanding '%' signs so that they don't get interpreted
// as a formatting directive down the line
out = strings.Replace(out, "%", "%%", -1)
// avoid doubling up in case this function is called multiple times
out = strings.Replace(out, "%%%%", "%%", -1)
return out
}
// Fmt applies formatting and translation
func Fmt(format string, a ...V) string {
format = translate.T(format)
return applyTmpl(format, a...)
}

View File

@ -0,0 +1,120 @@
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY knd, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package out provides a mechanism for sending localized, stylized output to the console.
package out
import (
"strings"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
)
// Error shows an an error reason
func Error(k reason.Kind, format string, a ...V) {
if JSON {
msg := Fmt(format, a...)
register.PrintErrorExitCode(strings.TrimSpace(msg), k.ExitCode, map[string]string{
"name": k.ID,
"advice": k.Advice,
"url": k.URL,
"issues": strings.Join(k.IssueURLs(), ","),
})
} else {
displayText(k, format, a...)
}
}
// WarnReason shows a warning reason
func WarnReason(k reason.Kind, format string, a ...V) {
if JSON {
msg := Fmt(format, a...)
register.PrintWarning(msg)
} else {
displayText(k, format, a...)
}
}
// indentMultiLine indents a message if it contains multiple lines
func indentMultiLine(s string) string {
if !strings.Contains(s, "\n") {
return s
}
cleaned := []string{"\n"}
for _, sn := range strings.Split(s, "\n") {
cleaned = append(cleaned, style.Indented+strings.TrimSpace(sn))
}
return strings.Join(cleaned, "\n")
}
func displayText(k reason.Kind, format string, a ...V) {
Ln("")
st := k.Style
if st == style.None {
st = style.KnownIssue
}
ErrT(st, format, a...)
if k.Advice != "" {
advice := indentMultiLine(Fmt(k.Advice, a...))
ErrT(style.Tip, Fmt("Suggestion: {{.advice}}", V{"advice": advice}))
}
if k.URL != "" {
ErrT(style.Documentation, "Documentation: {{.url}}", V{"url": k.URL})
}
issueURLs := k.IssueURLs()
if len(issueURLs) == 1 {
ErrT(style.Issues, "Related issue: {{.url}}", V{"url": issueURLs[0]})
}
if len(issueURLs) > 1 {
ErrT(style.Issues, "Related issues:")
for _, i := range issueURLs {
ErrT(style.Issue, "{{.url}}", V{"url": i})
}
}
if k.NewIssueLink {
ErrT(style.Empty, "")
ErrT(style.Sad, "If the above advice does not help, please let us know: ")
ErrT(style.URL, "https://github.com/kubernetes/minikube/issues/new/choose")
}
Ln("")
}

View File

@ -0,0 +1,128 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package out
import (
"bytes"
"os"
"strings"
"testing"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/reason"
)
type buffFd struct {
bytes.Buffer
uptr uintptr
}
func (b buffFd) Fd() uintptr { return b.uptr }
func TestDisplayProblem(t *testing.T) {
buffErr := buffFd{}
SetErrFile(&buffErr)
tests := []struct {
description string
issue reason.Kind
expected string
}{
{
issue: reason.Kind{
ID: "example",
URL: "example.com",
},
description: "url, id and err",
expected: `X Something failed
* Documentation: example.com
`,
},
{
issue: reason.Kind{ID: "example", URL: "example.com", Issues: []int{0, 1}, Advice: "you need a hug"},
description: "with 2 issues and suggestion",
expected: `X Something failed
* Suggestion: you need a hug
* Documentation: example.com
* Related issues:
- https://github.com/kubernetes/minikube/issues/0
- https://github.com/kubernetes/minikube/issues/1
`,
},
{
issue: reason.Kind{ID: "example", URL: "example.com", Issues: []int{0, 1}},
description: "with 2 issues",
expected: `X Something failed
* Documentation: example.com
* Related issues:
- https://github.com/kubernetes/minikube/issues/0
- https://github.com/kubernetes/minikube/issues/1
`,
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
buffErr.Truncate(0)
JSON = false
Error(tc.issue, "Something failed")
errStr := buffErr.String()
if strings.TrimSpace(errStr) != strings.TrimSpace(tc.expected) {
t.Fatalf("Expected errString:\n%v\ngot:\n%v\n", tc.expected, errStr)
}
})
}
}
func TestDisplayJSON(t *testing.T) {
defer SetJSON(false)
SetJSON(true)
tcs := []struct {
k *reason.Kind
expected string
}{
{
k: &reason.Kind{
ID: "BUG",
ExitCode: 4,
Advice: "fix me!",
Issues: []int{1, 2},
URL: "url",
},
expected: `{"data":{"advice":"fix me!","exitcode":"4","issues":"https://github.com/kubernetes/minikube/issues/1,https://github.com/kubernetes/minikube/issues/2","message":"my error","name":"BUG","url":"url"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.error"}
`,
},
}
for _, tc := range tcs {
t.Run(tc.k.ID, func(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
register.SetOutputFile(buf)
defer func() { register.SetOutputFile(os.Stdout) }()
register.GetUUID = func() string {
return "random-id"
}
JSON = true
Error(*tc.k, "my error")
actual := buf.String()
if actual != tc.expected {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", tc.expected, actual)
}
})
}
}

View File

@ -0,0 +1,60 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package out provides a mechanism for sending localized, stylized output to the console.
package out
import (
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/translate"
)
// Add a prefix to a string
func applyPrefix(prefix, format string) string {
if prefix == "" {
return format
}
return prefix + format
}
// applyStyle translates the given string if necessary then adds any appropriate style prefix.
func applyStyle(st style.Enum, useColor bool, format string) string {
format = translate.T(format)
s, ok := style.Config[st]
if !s.OmitNewline {
format += "\n"
}
// Similar to CSS styles, if no style matches, output an unformatted string.
if !ok || JSON {
return format
}
if !useColor {
return applyPrefix(style.LowPrefix(s), format)
}
return applyPrefix(s.Prefix, format)
}
// stylized applies formatting to the provided template
func stylized(st style.Enum, useColor bool, format string, a ...V) string {
if a == nil {
a = []V{{}}
}
format = applyStyle(st, useColor, format)
return Fmt(format, a...)
}

View File

@ -20,11 +20,12 @@ import (
"fmt"
"strings"
"testing"
"k8s.io/minikube/pkg/minikube/style"
)
func TestApplyPrefix(t *testing.T) {
var tests = []struct {
tests := []struct {
prefix, format, expected, description string
}{
{
@ -50,56 +51,18 @@ func TestApplyPrefix(t *testing.T) {
}
}
func TestLowPrefix(t *testing.T) {
var tests = []struct {
expected string
description string
style style
}{
{
expected: lowBullet,
description: "empty prefix",
},
{
expected: "bar",
style: style{LowPrefix: "bar"},
description: "lowPrefix",
},
{
expected: lowBullet,
style: style{Prefix: "foo"},
description: "prefix without spaces",
},
{
expected: lowIndent,
style: style{Prefix: " foo"},
description: "prefix with spaces",
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := lowPrefix(test.style)
if got != test.expected {
t.Errorf("Expected %v but got %v", test.expected, got)
}
})
}
}
func TestApplyStyle(t *testing.T) {
var tests = []struct {
tests := []struct {
expected string
description string
styleEnum StyleEnum
styleEnum style.Enum
format string
useColor bool
}{
{
expected: fmt.Sprintf("%sbar", lowBullet),
expected: fmt.Sprintf("%sbar", style.LowBullet),
description: "format bar, empty style, color off",
styleEnum: Empty,
styleEnum: style.Empty,
useColor: false,
format: "bar",
},
@ -111,9 +74,9 @@ func TestApplyStyle(t *testing.T) {
format: "bar",
},
{
expected: fmt.Sprintf("%sfoo", styles[Ready].Prefix),
expected: fmt.Sprintf("%sfoo", style.Config[style.Ready].Prefix),
description: "format foo, ready style, color on",
styleEnum: Ready,
styleEnum: style.Ready,
useColor: true,
format: "foo",
},
@ -130,19 +93,18 @@ func TestApplyStyle(t *testing.T) {
}
func TestApplyTemplateFormating(t *testing.T) {
var tests = []struct {
tests := []struct {
expected string
description string
styleEnum StyleEnum
styleEnum style.Enum
format string
useColor bool
a []V
}{
{
expected: fmt.Sprintf("%sbar", lowBullet),
expected: fmt.Sprintf("%sbar", style.LowBullet),
description: "format bar, empty style, color off",
styleEnum: Empty,
styleEnum: style.Empty,
useColor: false,
format: "bar",
},
@ -154,30 +116,30 @@ func TestApplyTemplateFormating(t *testing.T) {
format: "bar",
},
{
expected: fmt.Sprintf("%sfoo", styles[Ready].Prefix),
expected: fmt.Sprintf("%sfoo", style.Config[style.Ready].Prefix),
description: "format foo, ready style, color on, a nil",
styleEnum: Ready,
styleEnum: style.Ready,
useColor: true,
format: "foo",
},
{
expected: fmt.Sprintf("%sfoo", styles[Ready].Prefix),
expected: fmt.Sprintf("%sfoo", style.Config[style.Ready].Prefix),
description: "format foo, ready style, color on",
styleEnum: Ready,
styleEnum: style.Ready,
useColor: true,
format: "foo",
},
{
expected: fmt.Sprintf("%s{{ a }}", styles[Ready].Prefix),
expected: fmt.Sprintf("%s{{ a }}", style.Config[style.Ready].Prefix),
description: "bad format",
styleEnum: Ready,
styleEnum: style.Ready,
useColor: true,
format: "{{ a }}",
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
rawGot := ApplyTemplateFormatting(test.styleEnum, test.useColor, test.format, test.a...)
rawGot := stylized(test.styleEnum, test.useColor, test.format, test.a...)
got := strings.TrimSpace(rawGot)
if got != test.expected {
t.Errorf("Expected '%v' but got '%v'", test.expected, got)

View File

@ -22,6 +22,7 @@ import (
"strconv"
"testing"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/tests"
"k8s.io/minikube/pkg/minikube/translate"
)
@ -35,20 +36,20 @@ func TestOutT(t *testing.T) {
"Installing Kubernetes version {{.version}} ...": "... {{.version}} تثبيت Kubernetes الإصدار",
}
var testCases = []struct {
style StyleEnum
testCases := []struct {
style style.Enum
message string
params V
want string
wantASCII string
}{
{Happy, "Happy", nil, "😄 Happy\n", "* Happy\n"},
{Option, "Option", nil, " ▪ Option\n", " - Option\n"},
{Warning, "Warning", nil, "❗ Warning\n", "! Warning\n"},
{FatalType, "Fatal: {{.error}}", V{"error": "ugh"}, "💣 Fatal: ugh\n", "X Fatal: ugh\n"},
{Issue, "http://i/{{.number}}", V{"number": 10000}, " ▪ http://i/10000\n", " - http://i/10000\n"},
{Usage, "raw: {{.one}} {{.two}}", V{"one": "'%'", "two": "%d"}, "💡 raw: '%' %d\n", "* raw: '%' %d\n"},
{Running, "Installing Kubernetes version {{.version}} ...", V{"version": "v1.13"}, "🏃 ... v1.13 تثبيت Kubernetes الإصدار\n", "* ... v1.13 تثبيت Kubernetes الإصدار\n"},
{style.Happy, "Happy", nil, "😄 Happy\n", "* Happy\n"},
{style.Option, "Option", nil, " ▪ Option\n", " - Option\n"},
{style.Warning, "Warning", nil, "❗ Warning\n", "! Warning\n"},
{style.Fatal, "Fatal: {{.error}}", V{"error": "ugh"}, "💣 Fatal: ugh\n", "X Fatal: ugh\n"},
{style.Issue, "http://i/{{.number}}", V{"number": 10000}, " ▪ http://i/10000\n", " - http://i/10000\n"},
{style.Usage, "raw: {{.one}} {{.two}}", V{"one": "'%'", "two": "%d"}, "💡 raw: '%' %d\n", "* raw: '%' %d\n"},
{style.Running, "Installing Kubernetes version {{.version}} ...", V{"version": "v1.13"}, "🏃 ... v1.13 تثبيت Kubernetes الإصدار\n", "* ... v1.13 تثبيت Kubernetes الإصدار\n"},
}
for _, tc := range testCases {
for _, override := range []bool{true, false} {
@ -74,7 +75,7 @@ func TestOutT(t *testing.T) {
func TestOut(t *testing.T) {
os.Setenv(OverrideEnv, "")
var testCases = []struct {
testCases := []struct {
format string
arg interface{}
want string

View File

@ -46,7 +46,7 @@ func SetOutputFile(w io.Writer) {
// SetEventLogPath sets the path of an event log file
func SetEventLogPath(path string) {
if _, err := os.Stat(filepath.Dir(path)); err != nil {
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
if err := os.MkdirAll(filepath.Dir(path), 0o777); err != nil {
glog.Errorf("Error creating profile directory: %v", err)
return
}

View File

@ -104,6 +104,7 @@ func TestErrorExitCode(t *testing.T) {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", expected, actual)
}
}
func TestWarning(t *testing.T) {
expected := `{"data":{"message":"warning"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.warning"}`
expected += "\n"

View File

@ -16,7 +16,10 @@ limitations under the License.
package register
import "fmt"
import (
"fmt"
"strings"
)
// Log represents the different types of logs that can be output as JSON
// This includes: Step, Download, DownloadProgress, Warning, Info, Error
@ -39,7 +42,7 @@ func NewStep(message string) *Step {
return &Step{data: map[string]string{
"totalsteps": Reg.totalSteps(),
"currentstep": Reg.currentStep(),
"message": message,
"message": strings.TrimSpace(message),
"name": string(Reg.current),
}}
}
@ -92,7 +95,7 @@ type Warning struct {
func NewWarning(warning string) *Warning {
return &Warning{
map[string]string{
"message": warning,
"message": strings.TrimSpace(warning),
},
}
}
@ -115,7 +118,7 @@ func (s *Info) Type() string {
func NewInfo(message string) *Info {
return &Info{
map[string]string{
"message": message,
"message": strings.TrimSpace(message),
},
}
}
@ -128,18 +131,18 @@ type Error struct {
func NewError(err string) *Error {
return &Error{
map[string]string{
"message": err,
"message": strings.TrimSpace(err),
},
}
}
// NewErrorExitCode returns an error that has an associated exit code
func NewErrorExitCode(err string, exitcode int, additionalData ...map[string]string) *Error {
e := NewError(err)
e := NewError(strings.TrimSpace(err))
e.data["exitcode"] = fmt.Sprintf("%v", exitcode)
for _, a := range additionalData {
for k, v := range a {
e.data[k] = v
e.data[k] = strings.TrimSpace(v)
}
}
return e

View File

@ -1,595 +0,0 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package problem
import "regexp"
// re is a shortcut around regexp.MustCompile
func re(s string) *regexp.Regexp {
return regexp.MustCompile(s)
}
// vmProblems are VM related problems
var vmProblems = map[string]match{
// Generic VM driver
"DRIVER_CORRUPT": {
Regexp: re(`Error attempting to get plugin server address for RPC`),
Advice: "The VM driver exited with an error, and may be corrupt. Run 'minikube start' with --alsologtostderr -v=8 to see the error",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/",
ShowIssueLink: true,
},
"DRIVER_EXITED": {
Regexp: re(`Unable to start VM: start: exit status 1`),
Advice: "The VM driver crashed. Run 'minikube start --alsologtostderr -v=8' to see the VM driver error message",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/#troubleshooting",
ShowIssueLink: true,
},
"DRIVER_NOT_FOUND": {
Regexp: re(`registry: driver not found`),
Advice: "Your minikube config refers to an unsupported driver. Erase ~/.minikube, and try again.",
Issues: []int{5295},
},
"DRIVER_MISSING_ADDRESS": {
Regexp: re(`new host: dial tcp: missing address`),
Advice: "The machine-driver specified is failing to start. Try running 'docker-machine-driver-<type> version'",
Issues: []int{6023, 4679},
ShowIssueLink: true,
},
"PRECREATE_EXIT_1": {
Regexp: re(`precreate: exit status 1`),
Advice: "The hypervisor does not appear to be configured properly. Run 'minikube start --alsologtostderr -v=1' and inspect the error code",
Issues: []int{6098},
ShowIssueLink: true,
},
"FILE_IN_USE": {
Regexp: re(`The process cannot access the file because it is being used by another process`),
Advice: "Another program is using a file required by minikube. If you are using Hyper-V, try stopping the minikube VM from within the Hyper-V manager",
URL: "https://docs.docker.com/machine/drivers/hyper-v/",
GOOS: []string{"windows"},
Issues: []int{7300},
},
"CREATE_TIMEOUT": {
Regexp: re(`create host timed out in \d`),
Advice: "Try 'minikube delete', and disable any conflicting VPN or firewall software",
Issues: []int{7072},
},
"IMAGE_ARCH": {
Regexp: re(`Error: incompatible image architecture`),
Advice: "This driver does not yet work on your architecture. Maybe try --driver=none",
GOOS: []string{"linux"},
Issues: []int{7071},
},
// Docker
"DOCKER_WSL2_MOUNT": {
Regexp: re(`cannot find cgroup mount destination: unknown`),
Advice: "Run: 'sudo mkdir /sys/fs/cgroup/systemd && sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd'",
URL: "https://github.com/microsoft/WSL/issues/4189",
Issues: []int{5392},
GOOS: []string{"linux"},
},
"DOCKER_READONLY": {
Regexp: re(`mkdir /var/lib/docker/volumes.*: read-only file system`),
Advice: "Restart Docker",
Issues: []int{6825},
},
"DOCKER_CHROMEOS": {
Regexp: re(`Container.*is not running.*chown docker:docker`),
Advice: "minikube is not yet compatible with ChromeOS",
Issues: []int{6411},
},
"DOCKER_PROVISION_STUCK_CONTAINER": {
Regexp: re(`executing "" at <index (index .NetworkSettings.Ports "22/tcp") 0>`),
Advice: "Restart Docker, Ensure docker is running and then run: 'minikube delete' and then 'minikube start' again",
URL: "https://github.com/kubernetes/minikube/issues/8163#issuecomment-652627436",
Issues: []int{8163},
},
// Hyperkit
"HYPERKIT_NO_IP": {
Regexp: re(`IP address never found in dhcp leases file Temporary Error: Could not find an IP address for`),
Advice: "Install the latest hyperkit binary, and run 'minikube delete'",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/",
Issues: []int{1926, 4206},
GOOS: []string{"darwin"},
},
"HYPERKIT_NOT_FOUND": {
Regexp: re(`Driver "hyperkit" not found.`),
Advice: "Please install the minikube hyperkit VM driver, or select an alternative --driver",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/",
GOOS: []string{"darwin"},
},
"HYPERKIT_VMNET_FRAMEWORK": {
Regexp: re(`error from vmnet.framework: -1`),
Advice: "Hyperkit networking is broken. Upgrade to the latest hyperkit version and/or Docker for Desktop. Alternatively, you may choose an alternate --driver",
Issues: []int{6028, 5594},
GOOS: []string{"darwin"},
},
"HYPERKIT_CRASHED": {
Regexp: re(`hyperkit crashed!`),
Advice: "Hyperkit is broken. Upgrade to the latest hyperkit version and/or Docker for Desktop. Alternatively, you may choose an alternate --driver",
Issues: []int{6079, 5780},
GOOS: []string{"darwin"},
},
// Hyper-V
"HYPERV_NO_VSWITCH": {
Regexp: re(`no External vswitch found. A valid vswitch must be available for this command to run.`),
Advice: "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=<switch-name>` to `minikube start`",
URL: "https://docs.docker.com/machine/drivers/hyper-v/",
GOOS: []string{"windows"},
},
"HYPERV_VSWITCH_NOT_FOUND": {
Regexp: re(`precreate: vswitch.*not found`),
Advice: "Confirm that you have supplied the correct value to --hyperv-virtual-switch using the 'Get-VMSwitch' command",
URL: "https://docs.docker.com/machine/drivers/hyper-v/",
GOOS: []string{"windows"},
},
"HYPERV_POWERSHELL_NOT_FOUND": {
Regexp: re(`Powershell was not found in the path`),
Advice: "To start minikube with Hyper-V, Powershell must be in your PATH`",
URL: "https://docs.docker.com/machine/drivers/hyper-v/",
GOOS: []string{"windows"},
},
"HYPERV_AS_ADMIN": {
Regexp: re(`Hyper-v commands have to be run as an Administrator`),
Advice: "Right-click the PowerShell icon and select Run as Administrator to open PowerShell in elevated mode.",
URL: "https://rominirani.com/docker-machine-windows-10-hyper-v-troubleshooting-tips-367c1ea73c24",
Issues: []int{4511},
GOOS: []string{"windows"},
},
"HYPERV_NEEDS_ESC": {
Regexp: re(`The requested operation requires elevation.`),
Advice: "Right-click the PowerShell icon and select Run as Administrator to open PowerShell in elevated mode.",
Issues: []int{7347},
GOOS: []string{"windows"},
},
"HYPERV_FILE_DELETE_FAILURE": {
Regexp: re(`Unable to remove machine directory`),
Advice: "You may need to stop the Hyper-V Manager and run `minikube delete` again.",
Issues: []int{6804},
GOOS: []string{"windows"},
},
// KVM
"KVM2_NOT_FOUND": {
Regexp: re(`Driver "kvm2" not found. Do you have the plugin binary .* accessible in your PATH`),
Advice: "Please install the minikube kvm2 VM driver, or select an alternative --driver",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
GOOS: []string{"linux"},
},
"KVM2_NO_DOMAIN": {
Regexp: re(`no domain with matching name`),
Advice: "The VM that minikube is configured for no longer exists. Run 'minikube delete'",
Issues: []int{3636},
GOOS: []string{"linux"},
},
"KVM_CREATE_CONFLICT": {
Regexp: re(`KVM_CREATE_VM.* failed:.* Device or resource busy`),
Advice: "Another hypervisor, such as VirtualBox, is conflicting with KVM. Please stop the other hypervisor, or use --driver to switch to it.",
Issues: []int{4913},
GOOS: []string{"linux"},
},
"KVM2_RESTART_NO_IP": {
Regexp: re(`Error starting stopped host: Machine didn't return an IP after \d+ seconds`),
Advice: "The KVM driver is unable to resurrect this old VM. Please run `minikube delete` to delete it and try again.",
Issues: []int{3901, 3434},
},
"KVM2_START_NO_IP": {
Regexp: re(`Error in driver during machine creation: Machine didn't return an IP after \d+ seconds`),
Advice: "Check your firewall rules for interference, and run 'virt-host-validate' to check for KVM configuration issues. If you are running minikube within a VM, consider using --driver=none",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
Issues: []int{4249, 3566},
GOOS: []string{"linux"},
},
"KVM2_NETWORK_DEFINE_XML": {
Regexp: re(`not supported by the connection driver: virNetworkDefineXML`),
Advice: "Rebuild libvirt with virt-network support",
URL: "https://forums.gentoo.org/viewtopic-t-981692-start-0.html",
Issues: []int{4195},
GOOS: []string{"linux"},
},
"KVM2_FAILED_MSR": {
Regexp: re(`qemu unexpectedly closed the monitor.*failed to set MSR`),
Advice: "Upgrade to QEMU v3.1.0+, run 'virt-host-validate', or ensure that you are not running in a nested VM environment.",
Issues: []int{4277},
GOOS: []string{"linux"},
},
"KVM_UNAVAILABLE": {
Regexp: re(`invalid argument: could not find capabilities for domaintype=kvm`),
Advice: "Your host does not support KVM virtualization. Ensure that qemu-kvm is installed, and run 'virt-host-validate' to debug the problem",
URL: "http://mikko.repolainen.fi/documents/virtualization-with-kvm",
Issues: []int{2991},
GOOS: []string{"linux"},
},
"KVM_CONNECTION_ERROR": {
Regexp: re(`error connecting to libvirt socket`),
Advice: "Have you set up libvirt correctly?",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
GOOS: []string{"linux"},
},
"KVM_ISO_PERMISSION": {
Regexp: re(`boot2docker.iso.*Permission denied`),
Advice: "Ensure that the user listed in /etc/libvirt/qemu.conf has access to your home directory",
GOOS: []string{"linux"},
Issues: []int{5950},
},
"KVM_OOM": {
Regexp: re(`cannot set up guest memory.*Cannot allocate memory`),
Advice: "Choose a smaller value for --memory, such as 2000",
GOOS: []string{"linux"},
Issues: []int{6366},
},
// None
"NONE_APISERVER_MISSING": {
Regexp: re(`apiserver process never appeared`),
Advice: "Check that SELinux is disabled, and that the provided apiserver flags are valid",
Issues: []int{6014, 4536},
GOOS: []string{"linux"},
},
"NONE_DOCKER_EXIT_1": {
Regexp: re(`sudo systemctl start docker: exit status 1`),
Advice: "Either systemctl is not installed, or Docker is broken. Run 'sudo systemctl start docker' and 'journalctl -u docker'",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/none",
Issues: []int{4498},
GOOS: []string{"linux"},
},
"NONE_DOCKER_EXIT_5": {
Regexp: re(`sudo systemctl start docker: exit status 5`),
Advice: "Ensure that Docker is installed and healthy: Run 'sudo systemctl start docker' and 'journalctl -u docker'. Alternatively, select another value for --driver",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/none",
Issues: []int{5532},
GOOS: []string{"linux"},
},
"NONE_CRIO_EXIT_5": {
Regexp: re(`sudo systemctl restart crio: exit status 5`),
Advice: "Ensure that CRI-O is installed and healthy: Run 'sudo systemctl start crio' and 'journalctl -u crio'. Alternatively, use --container-runtime=docker",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/none",
Issues: []int{5532},
GOOS: []string{"linux"},
},
"NONE_PORT_IN_USE": {
Regexp: re(`ERROR Port-.*is in use`),
Advice: "kubeadm detected a TCP port conflict with another process: probably another local Kubernetes installation. Run lsof -p<port> to find the process and kill it",
Issues: []int{5484},
GOOS: []string{"linux"},
},
"NONE_KUBELET": {
Regexp: re(`The kubelet is not running`),
Advice: "Check output of 'journalctl -xeu kubelet', try passing --extra-config=kubelet.cgroup-driver=systemd to minikube start",
Issues: []int{4172},
GOOS: []string{"linux"},
},
"NONE_DEFAULT_ROUTE": {
Regexp: re(`(No|from) default routes`),
Advice: "Configure a default route on this Linux host, or use another --driver that does not require it",
Issues: []int{6083, 5636},
GOOS: []string{"linux"},
},
// VirtualBox
"VBOX_BLOCKED": {
Regexp: re(`NS_ERROR_FAILURE.*0x80004005`),
Advice: "Reinstall VirtualBox and verify that it is not blocked: System Preferences -> Security & Privacy -> General -> Some system software was blocked from loading",
Issues: []int{4107},
GOOS: []string{"darwin"},
},
"VBOX_DRV_NOT_LOADED": {
Regexp: re(`vboxdrv kernel module is not loaded`),
Advice: "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
Issues: []int{4043, 4711},
},
"VBOX_DEVICE_MISSING": {
Regexp: re(`vboxdrv does not exist`),
Advice: "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
Issues: []int{3974},
},
"VBOX_HARDENING": {
Regexp: re(`terminated unexpectedly.*VBoxHardening`),
Advice: "VirtualBox is broken. Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.",
Issues: []int{3859, 3910},
URL: "https://forums.virtualbox.org/viewtopic.php?f=25&t=82106",
GOOS: []string{"windows"},
},
"VBOX_NS_ERRROR": {
Regexp: re(`terminated unexpectedly.*NS_ERROR_FAILURE.*0x80004005`),
Advice: "VirtualBox is broken. Reinstall VirtualBox, reboot, and run 'minikube delete'.",
Issues: []int{5227},
GOOS: []string{"linux"},
},
"VBOX_HOST_ADAPTER": {
Regexp: re(`The host-only adapter we just created is not visible`),
Advice: "Reboot to complete VirtualBox installation, verify that VirtualBox is not blocked by your system, and/or use another hypervisor",
Issues: []int{3614, 4222, 5817},
URL: "https://stackoverflow.com/questions/52277019/how-to-fix-vm-issue-with-minikube-start",
},
"VBOX_IP_CONFLICT": {
Regexp: re(`VirtualBox is configured with multiple host-only adapters with the same IP`),
Advice: "Use VirtualBox to remove the conflicting VM and/or network interfaces",
URL: "https://stackoverflow.com/questions/55573426/virtualbox-is-configured-with-multiple-host-only-adapters-with-the-same-ip-whe",
Issues: []int{3584},
},
"VBOX_HYPERV_64_BOOT": {
Regexp: re(`VirtualBox won't boot a 64bits VM when Hyper-V is activated`),
Advice: "VirtualBox and Hyper-V are having a conflict. Use '--driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'",
Issues: []int{4051, 4783},
},
"VBOX_HYPERV_NEM_VM": {
Regexp: re(`vrc=VERR_NEM_VM_CREATE_FAILED`),
Advice: "VirtualBox and Hyper-V are having a conflict. Use '--driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'",
Issues: []int{4587},
},
"VBOX_NOT_FOUND": {
Regexp: re(`VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path`),
Advice: "Install VirtualBox and ensure it is in the path, or select an alternative value for --driver",
URL: "https://minikube.sigs.k8s.io/docs/start/",
Issues: []int{3784},
},
"VBOX_NO_VM": {
Regexp: re(`Could not find a registered machine named`),
Advice: "The VM that minikube is configured for no longer exists. Run 'minikube delete'",
Issues: []int{4694},
},
"VBOX_VTX_DISABLED": {
Regexp: re(`This computer doesn't have VT-X/AMD-v enabled`),
Advice: "Virtualization support is disabled on your computer. If you are running minikube within a VM, try '--driver=docker'. Otherwise, consult your systems BIOS manual for how to enable virtualization.",
Issues: []int{3900, 4730},
},
"VERR_VERR_VMX_DISABLED": {
Regexp: re(`VT-x is disabled.*VERR_VMX_MSR_ALL_VMX_DISABLED`),
Advice: "Virtualization support is disabled on your computer. If you are running minikube within a VM, try '--driver=docker'. Otherwise, consult your systems BIOS manual for how to enable virtualization.",
Issues: []int{5282, 5456},
},
"VBOX_VERR_VMX_NO_VMX": {
Regexp: re(`VT-x is not available.*VERR_VMX_NO_VMX`),
Advice: "Your host does not support virtualization. If you are running minikube within a VM, try '--driver=docker'. Otherwise, enable virtualization in your BIOS",
Issues: []int{1994, 5326},
},
"VERR_SVM_DISABLED": {
Regexp: re(`VERR_SVM_DISABLED`),
Advice: "Your host does not support virtualization. If you are running minikube within a VM, try '--driver=docker'. Otherwise, enable virtualization in your BIOS",
Issues: []int{7074},
},
"VBOX_HOST_NETWORK": {
Regexp: re(`Error setting up host only network on machine start.*Unspecified error`),
Advice: "VirtualBox cannot create a network, probably because it conflicts with an existing network that minikube no longer knows about. Try running 'minikube delete'",
Issues: []int{5260},
},
"VBOX_INTERFACE_NOT_FOUND": {
Regexp: re(`ERR_INTNET_FLT_IF_NOT_FOUND`),
Advice: "VirtualBox is unable to find its network interface. Try upgrading to the latest release and rebooting.",
Issues: []int{6036},
},
}
// proxyDoc is the URL to proxy documentation
const proxyDoc = "https://minikube.sigs.k8s.io/docs/handbook/vpn_and_proxy/"
const vpnDoc = "https://minikube.sigs.k8s.io/docs/handbook/vpn_and_proxy/"
// netProblems are network related problems.
var netProblems = map[string]match{
"GCR_UNAVAILABLE": {
Regexp: re(`gcr.io.*443: connect: invalid argument`),
Advice: "minikube is unable to access the Google Container Registry. You may need to configure it to use a HTTP proxy.",
URL: proxyDoc,
Issues: []int{3860},
},
"DOWNLOAD_RESET_BY_PEER": {
Regexp: re(`Error downloading .*connection reset by peer`),
Advice: "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.",
URL: proxyDoc,
Issues: []int{3909},
},
"DOWNLOAD_IO_TIMEOUT": {
Regexp: re(`Error downloading .*timeout`),
Advice: "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.",
URL: proxyDoc,
Issues: []int{3846},
},
"DOWNLOAD_TLS_OVERSIZED": {
Regexp: re(`tls: oversized record received with length`),
Advice: "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.",
URL: proxyDoc,
Issues: []int{3857, 3759, 4252},
},
"DOWNLOAD_BLOCKED": {
Regexp: re(`iso: failed to download|download.*host has failed to respond`),
Advice: "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.",
URL: proxyDoc,
Issues: []int{3922, 6109, 6123},
},
"PULL_TIMEOUT_EXCEEDED": {
Regexp: re(`ImagePull.*Timeout exceeded while awaiting headers`),
Advice: "A firewall is blocking Docker the minikube VM from reaching the image repository. You may need to select --image-repository, or use a proxy.",
URL: proxyDoc,
Issues: []int{3898, 6070},
},
"SSH_AUTH_FAILURE": {
Regexp: re(`ssh: handshake failed: ssh: unable to authenticate.*, no supported methods remain`),
Advice: "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.",
URL: vpnDoc,
Issues: []int{3930},
},
"SSH_TCP_FAILURE": {
Regexp: re(`dial tcp .*:22: connectex: A connection attempt failed because the connected party did not properly respond`),
Advice: "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.",
URL: vpnDoc,
Issues: []int{3388},
},
"INVALID_PROXY_HOSTNAME": {
Regexp: re(`dial tcp: lookup.*: no such host`),
Advice: "Verify that your HTTP_PROXY and HTTPS_PROXY environment variables are set correctly.",
URL: proxyDoc,
},
"HOST_CIDR_CONFLICT": {
Regexp: re(`host-only cidr conflicts with the network address of a host interface`),
Advice: "Specify an alternate --host-only-cidr value, such as 172.16.0.1/24",
Issues: []int{3594},
},
"HTTP_HTTPS_RESPONSE": {
Regexp: re(`http: server gave HTTP response to HTTPS client`),
Advice: "Ensure that your value for HTTPS_PROXY points to an HTTPS proxy rather than an HTTP proxy",
Issues: []int{6107},
URL: proxyDoc,
},
"NOT_A_TLS_HANDSHAKE": {
Regexp: re(`tls: first record does not look like a TLS handshake`),
Advice: "Ensure that your value for HTTPS_PROXY points to an HTTPS proxy rather than an HTTP proxy",
Issues: []int{7286},
URL: proxyDoc,
},
}
// deployProblems are Kubernetes deployment problems.
var deployProblems = map[string]match{
"DOCKER_UNAVAILABLE": {
Regexp: re(`Error configuring auth on host: OS type not recognized`),
Advice: "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.",
Issues: []int{3952},
},
"INVALID_KUBERNETES_VERSION": {
Regexp: re(`No Major.Minor.Patch elements found`),
Advice: "Specify --kubernetes-version in v<major>.<minor.<build> form. example: 'v1.1.14'",
},
"KUBERNETES_VERSION_MISSING_V": {
Regexp: re(`strconv.ParseUint: parsing "": invalid syntax`),
Advice: "Check that your --kubernetes-version has a leading 'v'. For example: 'v1.1.14'",
},
"APISERVER_MISSING": {
Regexp: re(`apiserver process never appeared`),
Advice: "Check that the provided apiserver flags are valid, and that SELinux is disabled",
Issues: []int{4536, 6014},
},
"APISERVER_TIMEOUT": {
Regexp: re(`apiserver: timed out waiting for the condition`),
Advice: "A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/",
URL: vpnDoc,
Issues: []int{4302},
},
"DNS_TIMEOUT": {
Regexp: re(`dns: timed out waiting for the condition`),
Advice: "Run 'kubectl describe pod coredns -n kube-system' and check for a firewall or DNS conflict",
URL: vpnDoc,
},
"SERVICE_NOT_FOUND": {
Regexp: re(`Could not find finalized endpoint being pointed to by`),
Advice: "Please make sure the service you are looking for is deployed or is in the correct namespace.",
Issues: []int{4599},
},
"OPEN_SERVICE_NOT_FOUND": {
Regexp: re(`Error opening service.*not found`),
Advice: "Use 'kubect get po -A' to find the correct and namespace name",
Issues: []int{5836},
},
"OOM_KILL_SSH": {
Regexp: re(`Process exited with status 137 from signal KILL`),
Advice: "Disable dynamic memory in your VM manager, or pass in a larger --memory value",
Issues: []int{1766},
},
"OOM_KILL_SCP": {
Regexp: re(`An existing connection was forcibly closed by the remote host`),
Advice: "Disable dynamic memory in your VM manager, or pass in a larger --memory value",
Issues: []int{1766},
},
"PROXY_UNEXPECTED_503": {
Regexp: re(`proxy.*unexpected response code: 503`),
Advice: "Confirm that you have a working internet connection and that your VM has not run out of resources by using: 'minikube logs'",
Issues: []int{4749},
},
"CERT_NOT_SIGNED_BY_CA": {
Regexp: re(`not signed by CA certificate ca: crypto/rsa: verification error`),
Advice: "Try 'minikube delete' to force new SSL certificates to be installed",
Issues: []int{6596},
},
"DOCKER_RESTART_FAILED": {
Regexp: re(`systemctl -f restart docker`),
Advice: "Remove the incompatible --docker-opt flag if one was provided",
Issues: []int{7070},
},
"WAITING_FOR_SSH": {
Regexp: re(`waiting for SSH to be available`),
Advice: "Try 'minikube delete', and disable any conflicting VPN or firewall software",
Issues: []int{4617},
},
}
// osProblems are operating-system specific issues
var osProblems = map[string]match{
"NON_C_DRIVE": {
Regexp: re(`.iso: The system cannot find the path specified.`),
Advice: "Run minikube from the C: drive.",
Issues: []int{1574},
},
"SYSTEMCTL_EXIT_1": {
Regexp: re(`Failed to enable container runtime: .*sudo systemctl start docker: exit status 1`),
Advice: "If using the none driver, ensure that systemctl is installed",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/none/",
Issues: []int{2704},
},
"KUBECONFIG_WRITE_FAIL": {
Regexp: re(`Failed to setup kubeconfig: writing kubeconfig`),
Advice: "Unset the KUBECONFIG environment variable, or verify that it does not point to an empty or otherwise invalid path",
Issues: []int{5268, 4100, 5207},
},
"KUBECONFIG_DENIED": {
Regexp: re(`.kube/config: permission denied`),
Advice: "Run: 'chmod 600 $HOME/.kube/config'",
GOOS: []string{"darwin", "linux"},
Issues: []int{5714},
},
"JUJU_LOCK_DENIED": {
Regexp: re(`unable to open /tmp/juju.*: permission denied`),
Advice: "Run 'sudo sysctl fs.protected_regular=0', or try a driver which does not require root, such as '--driver=docker'",
GOOS: []string{"linux"},
Issues: []int{6391},
},
}
// stateProblems are issues relating to local state
var stateProblems = map[string]match{
"MACHINE_DOES_NOT_EXIST": {
Regexp: re(`machine does not exist`),
Advice: "Run 'minikube delete' to delete the stale VM, or and ensure that minikube is running as the same user you are issuing this command with",
Issues: []int{3864, 6087},
},
"MACHINE_NOT_FOUND": {
Regexp: re(`Machine does not exist for api.Exists`),
Advice: "Your minikube vm is not running, try minikube start.",
Issues: []int{4889},
},
"IP_NOT_FOUND": {
Regexp: re(`Error getting ssh host name for driver: IP not found`),
Advice: "The minikube VM is offline. Please run 'minikube start' to start it again.",
Issues: []int{3849, 3648},
},
"DASHBOARD_ROLE_REF": {
Regexp: re(`dashboard.*cannot change roleRef`),
Advice: "Run: 'kubectl delete clusterrolebinding kubernetes-dashboard'",
Issues: []int{7256},
},
}
// dockerProblems are issues relating to issues with the docker driver
var dockerProblems = map[string]match{
"NO_SPACE_ON_DEVICE": {
Regexp: re(`.*docker.*No space left on device.*`),
Advice: `Try at least one of the following to free up space on the device:
1. Run "docker system prune" to remove unused docker data
2. Increase the amount of memory allocated to Docker for Desktop via
Docker icon > Preferences > Resources > Disk Image Size
3. Run "minikube ssh -- docker system prune" if using the docker container runtime
`,
Issues: []int{9024},
},
}

View File

@ -1,154 +0,0 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package problem helps deliver actionable feedback to a user based on an error message.
package problem
import (
"fmt"
"regexp"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/translate"
)
const issueBase = "https://github.com/kubernetes/minikube/issues"
// Problem represents a known problem in minikube.
type Problem struct {
// ID is an arbitrary unique and stable string describing this issue
ID string
// Err is the original error
Err error
// Advice is actionable text that the user should follow
Advice string
// URL is a reference URL for more information
URL string
// Issues are a list of related issues to this problem
Issues []int
// Hide the new issue link: it isn't our problem, and we won't be able to suggest additional assistance.
ShowIssueLink bool
}
// match maps a regular expression to problem metadata.
type match struct {
Regexp *regexp.Regexp
Advice string
URL string
Issues []int
// GOOS is what platforms this problem may be specific to, when disambiguation is necessary.
GOOS []string
// Hide the new issue link: it isn't our problem, and we won't be able to suggest additional assistance.
ShowIssueLink bool
}
// Display problem metadata to the console
func (p *Problem) Display() {
out.ErrT(out.Tip, "Suggestion: {{.advice}}", out.V{"advice": translate.T(p.Advice)})
if p.URL != "" {
out.ErrT(out.Documentation, "Documentation: {{.url}}", out.V{"url": p.URL})
}
if len(p.Issues) == 0 {
return
}
if len(p.Issues) == 1 {
out.ErrT(out.Issues, "Related issue: {{.url}}", out.V{"url": fmt.Sprintf("%s/%d", issueBase, p.Issues[0])})
return
}
out.ErrT(out.Issues, "Related issues:")
issues := p.Issues
if len(issues) > 3 {
issues = issues[0:3]
}
for _, i := range issues {
out.ErrT(out.Issue, "{{.url}}", out.V{"url": fmt.Sprintf("%s/%d", issueBase, i)})
}
}
// DisplayJSON displays problem metadata in JSON format
func (p *Problem) DisplayJSON(exitcode int) {
var issues string
for _, i := range p.Issues {
issues += fmt.Sprintf("https://github.com/kubernetes/minikube/issues/%v,", i)
}
extraArgs := map[string]string{
"name": p.ID,
"advice": p.Advice,
"url": p.URL,
"issues": issues,
}
register.PrintErrorExitCode(p.Err.Error(), exitcode, extraArgs)
}
// FromError returns a known problem from an error on an OS
func FromError(err error, goos string) *Problem {
maps := []map[string]match{
osProblems,
vmProblems,
netProblems,
deployProblems,
stateProblems,
dockerProblems,
}
var osMatch *Problem
var genericMatch *Problem
for _, m := range maps {
for id, match := range m {
if !match.Regexp.MatchString(err.Error()) {
continue
}
// Does this match require an OS matchup?
if len(match.GOOS) > 0 {
foundOS := false
for _, o := range match.GOOS {
if o == goos {
foundOS = true
}
}
if !foundOS {
continue
}
}
p := &Problem{
Err: err,
Advice: match.Advice,
URL: match.URL,
ID: id,
Issues: match.Issues,
ShowIssueLink: match.ShowIssueLink,
}
if len(match.GOOS) > 0 {
osMatch = p
} else {
genericMatch = p
}
}
}
// Prioritize operating-system specific matches over general ones
if osMatch != nil {
return osMatch
}
return genericMatch
}

View File

@ -1,206 +0,0 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package problem
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
)
type buffFd struct {
bytes.Buffer
uptr uintptr
}
func (b buffFd) Fd() uintptr { return b.uptr }
func TestDisplay(t *testing.T) {
buffErr := buffFd{}
out.SetErrFile(&buffErr)
var tests = []struct {
description string
problem Problem
expected string
}{
{
problem: Problem{ID: "example", URL: "example.com", Err: fmt.Errorf("test")},
description: "url, id and err",
expected: `
* Suggestion:
* Documentation: example.com
`,
},
{
problem: Problem{ID: "example", URL: "example.com", Err: fmt.Errorf("test"), Issues: []int{0, 1}, Advice: "you need a hug"},
description: "with 2 issues and suggestion",
expected: `
* Suggestion: you need a hug
* Documentation: example.com
* Related issues:
- https://github.com/kubernetes/minikube/issues/0
- https://github.com/kubernetes/minikube/issues/1
`,
},
{
problem: Problem{ID: "example", URL: "example.com", Err: fmt.Errorf("test"), Issues: []int{0, 1}},
description: "with 2 issues",
expected: `
* Suggestion:
* Documentation: example.com
* Related issues:
- https://github.com/kubernetes/minikube/issues/0
- https://github.com/kubernetes/minikube/issues/1
`,
},
// 6 issues should be trimmed to 3
{
problem: Problem{ID: "example", URL: "example.com", Err: fmt.Errorf("test"), Issues: []int{0, 1, 2, 3, 4, 5}},
description: "with 6 issues",
expected: `
* Suggestion:
* Documentation: example.com
* Related issues:
- https://github.com/kubernetes/minikube/issues/0
- https://github.com/kubernetes/minikube/issues/1
- https://github.com/kubernetes/minikube/issues/2
`,
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
buffErr.Truncate(0)
tc.problem.Display()
errStr := buffErr.String()
if strings.TrimSpace(errStr) != strings.TrimSpace(tc.expected) {
t.Fatalf("Expected errString:\n%v\ngot:\n%v\n", tc.expected, errStr)
}
})
}
}
func TestDisplayJSON(t *testing.T) {
defer out.SetJSON(false)
out.SetJSON(true)
tcs := []struct {
p *Problem
expected string
}{
{
p: &Problem{
Err: fmt.Errorf("my error"),
Advice: "fix me!",
Issues: []int{1, 2},
URL: "url",
ID: "BUG",
},
expected: `{"data":{"advice":"fix me!","exitcode":"4","issues":"https://github.com/kubernetes/minikube/issues/1,https://github.com/kubernetes/minikube/issues/2,","message":"my error","name":"BUG","url":"url"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.error"}
`,
},
}
for _, tc := range tcs {
t.Run(tc.p.ID, func(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
register.SetOutputFile(buf)
defer func() { register.SetOutputFile(os.Stdout) }()
register.GetUUID = func() string {
return "random-id"
}
tc.p.DisplayJSON(4)
actual := buf.String()
if actual != tc.expected {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", tc.expected, actual)
}
})
}
}
func TestFromError(t *testing.T) {
var tests = []struct {
issue int
os string
want string
err string
}{
{0, "", "", "this is just a lame error message with no matches."},
{2991, "linux", "KVM_UNAVAILABLE", "Unable to start VM: create: Error creating machine: Error in driver during machine creation: creating domain: Error defining domain xml:\n\n: virError(Code=8, Domain=44, Message='invalid argument: could not find capabilities for domaintype=kvm ')"},
{3594, "", "HOST_CIDR_CONFLICT", "Error starting host: Error starting stopped host: Error setting up host only network on machine start: host-only cidr conflicts with the network address of a host interface."},
{3614, "", "VBOX_HOST_ADAPTER", "Error starting host: Error starting stopped host: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is supposed to fix this issue"},
{3784, "", "VBOX_NOT_FOUND", "create: precreate: VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path"},
{3849, "", "IP_NOT_FOUND", "bootstrapper: Error creating new ssh host from driver: Error getting ssh host name for driver: IP not found"},
{3859, "windows", "VBOX_HARDENING", `Unable to start VM: create: creating: Unable to start the VM: C:\Program Files\Oracle\VirtualBox\VBoxManage.exe startvm minikube --type headless failed:
VBoxManage.exe: error: The virtual machine 'minikube' has terminated unexpectedly during startup with exit code -1073741819 (0xc0000005). More details may be available in 'C:\Users\pabitra_b.minikube\machines\minikube\minikube\Logs\VBoxHardening.log'
VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component MachineWrap, interface IMachine`},
{3922, "", "DOWNLOAD_BLOCKED", `unable to cache ISO: https://storage.googleapis.com/minikube/iso/minikube-v0.35.0.iso: failed to download: failed to download to temp file: download failed: 5 error(s) occurred:
* Temporary download error: Get https://storage.googleapis.com/minikube/iso/minikube-v0.35.0.iso: dial tcp 216.58.207.144:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.`},
{4107, "darwin", "VBOX_BLOCKED", "Result Code: NS_ERROR_FAILURE (0x80004005)"},
{4302, "", "APISERVER_TIMEOUT", "apiserver: timed out waiting for the condition"},
{4252, "", "DOWNLOAD_TLS_OVERSIZED", "Failed to update cluster: downloading binaries: downloading kubeadm: Error downloading kubeadm v1.14.1: failed to download: failed to download to temp file: download failed: 5 error(s) occurred:\n\nTemporary download error: Get https://storage.googleapis.com/kubernetes-release/release/v1.14.1/bin/linux/amd64/kubeadm: proxyconnect tcp: tls: oversized record received with length 20527"},
{4222, "", "VBOX_HOST_ADAPTER", "Unable to start VM: create: creating: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is supposed to fix this issue"},
{6014, "linux", "NONE_APISERVER_MISSING", "Error restarting cluster: waiting for apiserver: apiserver process never appeared"},
{5836, "", "OPEN_SERVICE_NOT_FOUND", `Error opening service: Service newservice was not found in "unknown" namespace. You may select another namespace by using 'minikube service newservice -n : Temporary Error: Error getting service newservice: services "newservice" not found`},
{6087, "", "MACHINE_DOES_NOT_EXIST", `Error getting machine status: state: machine does not exist`},
{5714, "darwin", "KUBECONFIG_DENIED", `Failed to setup kubeconfig: writing kubeconfig: Error writing file /Users/matthewgleich/.kube/config: error writing file /Users/matthewgleich/.kube/config: open /Users/matthewgleich/.kube/config: permission denied`},
{5532, "linux", "NONE_DOCKER_EXIT_5", `Failed to enable container runtime: running command: sudo systemctl start docker: exit status 5`},
{5532, "linux", "NONE_CRIO_EXIT_5", `Failed to enable container runtime: running command: sudo systemctl restart crio: exit status 5`},
{5484, "linux", "NONE_PORT_IN_USE", `[ERROR Port-10252]: Port 10252 is in use`},
{4913, "linux", "KVM_CREATE_CONFLICT", `Unable to start VM: create: Error creating machine: Error in driver during machine creation: error creating VM: virError(Code=1, Domain=10, Message='internal error: process exited while connecting to monitor: ioctl(KVM_CREATE_VM) failed: 16 Device or resource busy`},
{5950, "linux", "KVM_ISO_PERMISSION", `Retriable failure: create: Error creating machine: Error in driver during machine creation: error creating VM: virError(Code=1, Domain=10, Message='internal error: qemu unexpectedly closed the monitor: 2019-11-19T16:08:16.757609Z qemu-kvm: -drive file=/home/lnicotra/.minikube/machines/minikube/boot2docker.iso,format=raw,if=none,id=drive-scsi0-0-0-2,readonly=on: could not open disk image /home/lnicotra/.minikube/machines/minikube/boot2docker.iso: Could not open '/home/lnicotra/.minikube/machines/minikube/boot2docker.iso': Permission denied'`},
{5836, "", "OPEN_SERVICE_NOT_FOUND", `Error opening service: Service kubernetes-bootcamp was not found in "default" namespace. You may select another namespace by using 'minikube service kubernetes-bootcamp -n : Temporary Error: Error getting service kubernetes-bootcamp: services "kubernetes-bootcamp" not found`},
{3898, "", "PULL_TIMEOUT_EXCEEDED", `[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-controller-manager:v1.17.0: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)`},
{6079, "darwin", "HYPERKIT_CRASHED", `Error creating machine: Error in driver during machine creation: hyperkit crashed! command line:`},
{5636, "linux", "NONE_DEFAULT_ROUTE", `Unable to get VM IP address: unable to select an IP from default routes.`},
{6087, "", "MACHINE_DOES_NOT_EXIST", `Error getting host status: state: machine does not exist`},
{6098, "windows", "PRECREATE_EXIT_1", `Retriable failure: create: precreate: exit status 1`},
{6107, "", "HTTP_HTTPS_RESPONSE", `http: server gave HTTP response to HTTPS client`},
{6109, "", "DOWNLOAD_BLOCKED", `Failed to update cluster: downloading binaries: downloading kubelet: Error downloading kubelet v1.16.2: failed to download: failed to download to temp file: failed to copy contents: read tcp 192.168.0.106:61314->172.217.166.176:443: wsarecv: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.`},
{6109, "", "DOWNLOAD_BLOCKED", `Failed to update cluster: downloading binaries: downloading kubeadm: Error downloading kubeadm v1.17.0: failed to download: failed to download to temp file: failed to copy contents: read tcp [2606:a000:81c5:1e00:349a:26c0:7ea6:bbf1]:55317->[2607:f8b0:4004:815::2010]:443: wsarecv: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.`},
{4277, "linux", "KVM2_FAILED_MSR", `Unable to start VM: start: Error creating VM: virError(Code=1, Domain=10, Message='internal error: qemu unexpectedly closed the monitor: 2019-05-17T02:20:07.980140Z qemu-system-x86_64: error: failed to set MSR 0x38d to 0x0 qemu-system-x86_64: /build/qemu-lXHhGe/qemu-2.11+dfsg/target/i386/kvm.c:1807: kvm_put_msrs: Assertion ret == cpu->kvm_msr_buf->nmsrs failed.`},
}
for _, tc := range tests {
t.Run(tc.want, func(t *testing.T) {
got := FromError(fmt.Errorf(tc.err), tc.os)
if got == nil {
if tc.want != "" {
t.Errorf("FromError(%q)=nil, want %s", tc.err, tc.want)
}
return
}
if got.ID != tc.want {
t.Errorf("FromError(%q)=%s, want %s", tc.err, got.ID, tc.want)
}
found := false
for _, i := range got.Issues {
if i == tc.issue {
found = true
}
}
if !found {
t.Errorf("Issue %d is not listed in %+v", tc.issue, got.Issues)
}
})
}
}

View File

@ -0,0 +1,162 @@
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Copyright 2019 TheExSvc Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package reason
const (
// Reserved UNIX exit codes
ExFailure = 1 // Failure represents a general failure code
ExInterrupted = 2 // Ctrl-C (SIGINT)
// 3-7 are reserved for crazy legacy codes returned by "minikube status"
// How to assign new minikube exit codes:
//
// * Each error source is indexed from 10 onward, in general, it follows the dependency stack
// * For each error source, we roughly try to follow sysexits(3) for backwards compatibility
//
// errorOff = 0 // (~EX_SOFTWARE)
// conflictOff = 1 // (~EX_OSERR)
// timeoutOff = 2 // (~EX_INTERRUPTED)
// notRunningOff = 3 // custom
// usageOff = 4 // (~EX_USAGE)
// notFoundOff = 5 // (~EX_DATAERR)
// unsupportedOff = 6 // (~EX_PROTOCOL)
// permissionOff = 7 // (~EX_NOPERM)
// configOff = 8 // (~EX_CONFIG)
// navailableOff = 9 // (~EX_UNAVAILABLE)
// Error codes specific to the minikube program
ExProgramError = 10 // generic error
ExProgramUsage = 14 // bad command-line options
ExProgramConflict = 11 // can't do what you want because of existing data
ExProgramNotFound = 15 // something was not found
ExProgramUnsupported = 16 // unsupported features
ExProgramConfig = 18 // bad configuration specified
// Error codes specific to resource limits (exit code layout follows no rules)
ExResourceError = 20
ExInsufficientMemory = 23
ExInsufficientStorage = 26
ExInsufficientPermission = 27
ExInsufficientCores = 29
// Error codes specific to the host
ExHostError = 30
ExHostConflict = 31
ExHostTimeout = 32
ExHostUsage = 34
ExHostNotFound = 35
ExHostUnsupported = 38
ExHostPermission = 37
ExHostConfig = 38
// Error codes specific to remote networking
ExInternetError = 40
ExInternetConflict = 41
ExInternetTimeout = 42
ExInternetNotFound = 45
ExInternetConfig = 48
ExInternetUnavailable = 49
// Error codes specific to the libmachine driver
ExDriverError = 50
ExDriverConflict = 51
ExDriverTimeout = 52
ExDriverUsage = 54
ExDriverNotFound = 55
ExDriverUnsupported = 56
ExDriverPermission = 57
ExDriverConfig = 58
ExDriverUnavailable = 59
// Error codes specific to the driver provider
ExProviderError = 60
ExProviderConflict = 61
ExProviderTimeout = 62
ExProviderNotRunning = 63
// Reserve 64 for the moment as it used to be usage
ExProviderNotFound = 65
ExProviderUnsupported = 66
ExProviderPermission = 67
ExProviderConfig = 68
ExProviderUnavailable = 69 // In common use
// Error codes specific to local networking
ExLocalNetworkError = 70
ExLocalNetworkConflict = 71
ExLocalNetworkTimeout = 72
ExLocalNetworkNotFound = 75
ExLocalNetworkPermission = 77
ExLocalNetworkConfig = 78
ExLocalNetworkUnavailable = 79
// Error codes specific to the guest host
ExGuestError = 80
ExGuestConflict = 81
ExGuestTimeout = 82
ExGuestNotRunning = 83
ExGuestNotFound = 85
ExGuestUnsupported = 86
ExGuestPermission = 87
ExGuestConfig = 88
ExGuestUnavailable = 89
// Error codes specific to the container runtime
ExRuntimeError = 90
ExRuntimeNotRunning = 93
ExRuntimeNotFound = 95
ExRuntimeUnavailable = 99
// Error codes specific to the Kubernetes control plane
ExControlPlaneError = 100
ExControlPlaneConflict = 101
ExControlPlaneTimeout = 102
ExControlPlaneNotRunning = 103
ExControlPlaneNotFound = 105
ExControlPlaneUnsupported = 106
ExControlPlaneConfig = 108
ExControlPlaneUnavailable = 109
// Error codes specific to a Kubernetes service
ExSvcError = 110
ExSvcConflict = 111
ExSvcTimeout = 112
ExSvcNotRunning = 113
ExSvcNotFound = 115
ExSvcUnsupported = 116
ExSvcPermission = 117
ExSvcConfig = 118
ExSvcUnavailable = 119
// Reserve 128+ for OS signal based exit codes
)

View File

@ -0,0 +1,985 @@
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY matchND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package reason
import (
"regexp"
"k8s.io/minikube/pkg/minikube/style"
)
// links used by multiple known issues
const (
proxyDoc = "https://minikube.sigs.k8s.io/docs/handbook/vpn_and_proxy/"
vpnDoc = "https://minikube.sigs.k8s.io/docs/handbook/vpn_and_proxy/"
)
// re is a shortcut around regexp.MustCompile
func re(s string) *regexp.Regexp {
return regexp.MustCompile(s)
}
// programIssues are issues with the minikube binary
var programIssues = []match{
{
Kind: Kind{
ID: "MK_KVERSION_USAGE",
ExitCode: ExProgramUsage,
Advice: "Specify --kubernetes-version in v<major>.<minor.<build> form. example: 'v1.1.14'",
},
Regexp: re(`No Major.Minor.Patch elements found`),
},
}
// resourceIssues are failures due to resource constraints
var resourceIssues = []match{
{
Kind: Kind{
ID: "RSRC_KVM_OOM",
ExitCode: ExInsufficientMemory,
Advice: "Choose a smaller value for --memory, such as 2000",
Issues: []int{6366},
},
Regexp: re(`cannot set up guest memory.*Cannot allocate memory`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "RSRC_SSH_OOM",
ExitCode: ExInsufficientMemory,
Advice: "Disable dynamic memory in your VM manager, or pass in a larger --memory value",
Issues: []int{1766},
},
Regexp: re(`Process exited with status 137 from signal matchLL`),
},
{
Kind: Kind{
ID: "RSRC_SCP_OOM",
ExitCode: ExInsufficientMemory,
Advice: "Disable dynamic memory in your VM manager, or pass in a larger --memory value",
Issues: []int{1766},
},
Regexp: re(`An existing connection was forcibly closed by the remote host`),
},
}
// hostIssues are related to the host operating system or BIOS
var hostIssues = []match{
{
Kind: Kind{
ID: "HOST_VIRT_UNAVAILABLE",
ExitCode: ExHostConfig,
Advice: "Virtualization support is disabled on your computer. If you are running minikube within a VM, try '--driver=docker'. Otherwise, consult your systems BIOS manual for how to enable virtualization.",
Issues: []int{3900, 4730},
},
Regexp: re(`This computer doesn't have VT-X/AMD-v enabled`),
},
{
Kind: Kind{
ID: "HOST_VTX_DISABLED",
ExitCode: ExHostConfig,
Advice: "Virtualization support is disabled on your computer. If you are running minikube within a VM, try '--driver=docker'. Otherwise, consult your systems BIOS manual for how to enable virtualization.",
Issues: []int{5282, 5456},
},
Regexp: re(`VT-x is disabled.*VERR_VMX_MSR_ALL_VMX_DISABLED`),
},
{
Kind: Kind{
ID: "HOST_VTX_UNAVAILABLE",
ExitCode: ExHostConfig,
Advice: "Your host does not support virtualization. If you are running minikube within a VM, try '--driver=docker'. Otherwise, enable virtualization in your BIOS",
Issues: []int{1994, 5326},
},
Regexp: re(`VT-x is not available.*VERR_VMX_NO_VMX`),
},
{
Kind: Kind{
ID: "HOST_SVM_DISABLED",
ExitCode: ExHostConfig,
Advice: "Your host does not support virtualization. If you are running minikube within a VM, try '--driver=docker'. Otherwise, enable virtualization in your BIOS",
Issues: []int{7074},
},
Regexp: re(`VERR_SVM_DISABLED`),
},
{
Kind: Kind{
ID: "HOST_NON_C_DRIVE",
ExitCode: ExHostUsage,
Advice: "Run minikube from the C: drive.",
Issues: []int{1574},
},
Regexp: re(`.iso: The system cannot find the path specified.`),
},
{
Kind: Kind{
ID: "HOST_KUBECONFIG_WRITE",
ExitCode: ExHostPermission,
Advice: "Unset the KUBECONFIG environment variable, or verify that it does not point to an empty or otherwise invalid path",
Issues: []int{5268, 4100, 5207},
},
Regexp: re(`Failed to setup kubeconfig: writing kubeconfig`),
},
{
Kind: Kind{
ID: "HOST_KUBECONFIG_PERMISSION",
ExitCode: ExHostPermission,
Advice: "Run: 'sudo chown $USER $HOME/.kube/config && chmod 600 $HOME/.kube/config'",
Issues: []int{5714},
Style: style.NotAllowed,
},
Regexp: re(`.kube/config: permission denied`),
GOOS: []string{"darwin", "linux"},
},
{
Kind: Kind{
ID: "HOST_JUJU_LOCK_PERMISSION",
ExitCode: ExHostPermission,
Advice: "Run 'sudo sysctl fs.protected_regular=0', or try a driver which does not require root, such as '--driver=docker'",
Issues: []int{6391},
},
Regexp: re(`unable to open /tmp/juju.*: permission denied`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "HOST_DOCKER_CHROMEOS",
ExitCode: ExHostUnsupported,
Advice: "ChromeOS is missing the kernel support necessary for running Kubernetes",
Issues: []int{6411},
},
Regexp: re(`Container.*is not running.*chown docker:docker`),
},
}
// providerIssues are failures relating to a driver provider
var providerIssues = []match{
// General
{
Kind: Kind{
ID: "PR_PRECREATE_EXIT_1",
ExitCode: ExProviderError,
Advice: "The hypervisor does not appear to be configured properly. Run 'minikube start --alsologtostderr -v=1' and inspect the error code",
Issues: []int{6098},
},
Regexp: re(`precreate: exit status 1`),
},
// Docker environment
{
Kind: Kind{
ID: "PR_DOCKER_CGROUP_MOUNT",
ExitCode: ExProviderError,
Advice: "Run: 'sudo mkdir /sys/fs/cgroup/systemd && sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd'",
URL: "https://github.com/microsoft/WSL/issues/4189",
Issues: []int{5392},
},
Regexp: re(`cannot find cgroup mount destination: unknown`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "PR_DOCKER_READONLY_VOL",
ExitCode: ExProviderError,
Advice: "Restart Docker",
Issues: []int{6825},
},
Regexp: re(`mkdir /var/lib/docker/volumes.*: read-only file system`),
},
{
Kind: Kind{
ID: "PR_DOCKER_NO_SSH",
ExitCode: ExProviderTimeout,
Advice: "Restart Docker, Ensure docker is running and then run: 'minikube delete' and then 'minikube start' again",
URL: "https://github.com/kubernetes/minikube/issues/8163#issuecomment-652627436",
Issues: []int{8163},
},
Regexp: re(`executing "" at <index (index .NetworkSettings.Ports "22/tcp") 0>`),
},
// Hyperkit hypervisor
{
Kind: Kind{
ID: "PR_HYPERKIT_NO_IP",
ExitCode: ExProviderError,
Advice: "Install the latest hyperkit binary, and run 'minikube delete'",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/",
Issues: []int{1926, 4206},
},
Regexp: re(`IP address never found in dhcp leases file Temporary Error: Could not find an IP address for`),
GOOS: []string{"darwin"},
},
{
Kind: Kind{
ID: "PR_HYPERKIT_NOT_FOUND",
ExitCode: ExProviderNotFound,
Advice: "Please install the minikube hyperkit VM driver, or select an alternative --driver",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/",
},
Regexp: re(`Driver "hyperkit" not found.`),
GOOS: []string{"darwin"},
},
{
Kind: Kind{
ID: "PR_HYPERKIT_VMNET_FRAMEWORK",
ExitCode: ExProviderError,
Advice: "Hyperkit networking is broken. Upgrade to the latest hyperkit version and/or Docker for Desktop. Alternatively, you may choose an alternate --driver",
Issues: []int{6028, 5594},
},
Regexp: re(`error from vmnet.framework: -1`),
GOOS: []string{"darwin"},
},
{
Kind: Kind{
ID: "PR_HYPERKIT_CRASHED",
ExitCode: ExProviderError,
Advice: "Hyperkit is broken. Upgrade to the latest hyperkit version and/or Docker for Desktop. Alternatively, you may choose an alternate --driver",
Issues: []int{6079, 5780},
},
Regexp: re(`hyperkit crashed!`),
GOOS: []string{"darwin"},
},
// Hyper-V hypervisor
{
Kind: Kind{
ID: "PR_HYPERV_AS_ADMIN",
ExitCode: ExProviderPermission,
Advice: "Right-click the PowerShell icon and select Run as Administrator to open PowerShell in elevated mode.",
URL: "https://rominirani.com/docker-machine-windows-10-hyper-v-troubleshooting-tips-367c1ea73c24",
Issues: []int{4511},
},
Regexp: re(`Hyper-v commands have to be run as an Administrator`),
GOOS: []string{"windows"},
},
{
Kind: Kind{
ID: "PR_HYPERV_NEEDS_ESC",
ExitCode: ExProviderPermission,
Advice: "Right-click the PowerShell icon and select Run as Administrator to open PowerShell in elevated mode.",
Issues: []int{7347},
},
Regexp: re(`The requested operation requires elevation.`),
GOOS: []string{"windows"},
},
// KVM hypervisor
{
Kind: Kind{
ID: "PR_KVM_CAPABILITIES",
ExitCode: ExProviderUnavailable,
Advice: "Your host does not support KVM virtualization. Ensure that qemu-kvm is installed, and run 'virt-host-validate' to debug the problem",
URL: "http://mikko.repolainen.fi/documents/virtualization-with-kvm",
Issues: []int{2991},
},
Regexp: re(`invalid argument: could not find capabilities for domaintype=kvm`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "PR_KVM_SOCKET",
ExitCode: ExProviderUnavailable,
Advice: "Check that libvirt is setup properly",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
},
Regexp: re(`error connecting to libvirt socket`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "PR_KVM_ISO_PERMISSION",
ExitCode: ExProviderPermission,
Advice: "Ensure that the user listed in /etc/libvirt/qemu.conf has access to your home directory",
Issues: []int{5950},
},
Regexp: re(`boot2docker.iso.*Permission denied`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "PR_KVM_NET_XML",
ExitCode: ExProviderConfig,
Advice: "Rebuild libvirt with virt-network support",
URL: "https://forums.gentoo.org/viewtopic-t-981692-start-0.html",
Issues: []int{4195},
},
Regexp: re(`not supported by the connection driver: virNetworkDefineXML`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "PR_KVM_MSR",
ExitCode: ExProviderError,
Advice: "Upgrade to QEMU v3.1.0+, run 'virt-host-validate', or ensure that you are not running in a nested VM environment.",
Issues: []int{4277},
},
Regexp: re(`qemu unexpectedly closed the monitor.*failed to set MSR`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "PR_KVM_CREATE_BUSY",
ExitCode: ExDriverConflict,
Advice: "Another hypervisor, such as VirtualBox, is conflicting with KVM. Please stop the other hypervisor, or use --driver to switch to it.",
Issues: []int{4913},
},
Regexp: re(`KVM_CREATE_VM.* failed:.* Device or resource busy`),
GOOS: []string{"linux"},
},
// VirtualBox provider
{
Kind: Kind{
ID: "PR_VBOX_BLOCKED",
ExitCode: ExProviderPermission,
Advice: "Reinstall VirtualBox and verify that it is not blocked: System Preferences -> Security & Privacy -> General -> Some system software was blocked from loading",
Issues: []int{4107},
},
Regexp: re(`NS_ERROR.*0x80004005`),
GOOS: []string{"darwin"},
},
{
Kind: Kind{
ID: "PR_VBOX_MODULE",
ExitCode: ExProviderNotRunning,
Advice: "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
Issues: []int{4043, 4711},
},
Regexp: re(`vboxdrv kernel module is not loaded`),
},
{
Kind: Kind{
ID: "PR_VBOX_DEVICE_MISSING",
ExitCode: ExProviderNotRunning,
Advice: "Reinstall VirtualBox and reboot. Alternatively, try the kvm2 driver: https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
Issues: []int{3974},
},
Regexp: re(`vboxdrv does not exist`),
},
{
Kind: Kind{
ID: "PR_VBOX_HARDENING",
ExitCode: ExProviderConflict,
Advice: "VirtualBox is broken. Disable real-time anti-virus software, reboot, and reinstall VirtualBox if the problem continues.",
Issues: []int{3859, 3910},
URL: "https://forums.virtualbox.org/viewtopic.php?f=25&t=82106",
},
Regexp: re(`terminated unexpectedly.*VBoxHardening`),
GOOS: []string{"windows"},
},
{
Kind: Kind{
ID: "PR_VBOX_80004005",
ExitCode: ExProviderError,
Advice: "VirtualBox is broken. Reinstall VirtualBox, reboot, and run 'minikube delete'.",
Issues: []int{5227},
},
Regexp: re(`terminated unexpectedly.*NS_ERROR.*0x80004005`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "PR_VBOX_HYPERV_64_BOOT",
ExitCode: ExProviderConflict,
Advice: "VirtualBox and Hyper-V are having a conflict. Use '--driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'",
Issues: []int{4051, 4783},
},
Regexp: re(`VirtualBox won't boot a 64bits VM when Hyper-V is activated`),
},
{
Kind: Kind{
ID: "PR_VBOX_HYPERV_CONFLICT",
ExitCode: ExProviderConflict,
Advice: "VirtualBox and Hyper-V are having a conflict. Use '--driver=hyperv' or disable Hyper-V using: 'bcdedit /set hypervisorlaunchtype off'",
Issues: []int{4587},
},
Regexp: re(`vrc=VERR_NEM_VM_CREATE`),
},
{
Kind: Kind{
ID: "PR_VBOXMANAGE_NOT_FOUND",
ExitCode: ExProviderNotFound,
Advice: "Install VirtualBox and ensure it is in the path, or select an alternative value for --driver",
URL: "https://minikube.sigs.k8s.io/docs/start/",
Issues: []int{3784},
},
Regexp: re(`VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path`),
},
}
// driverIssues are specific to a libmachine driver
var driverIssues = []match{
// Generic VM driver
{
Kind: Kind{
ID: "DRV_CORRUPT",
ExitCode: ExDriverError,
Advice: "The VM driver exited with an error, and may be corrupt. Run 'minikube start' with --alsologtostderr -v=8 to see the error",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/",
NewIssueLink: true,
},
Regexp: re(`Error attempting to get plugin server address for RPC`),
},
{
Kind: Kind{
ID: "DRV_EXITED_1",
ExitCode: ExDriverError,
Advice: "The VM driver crashed. Run 'minikube start --alsologtostderr -v=8' to see the VM driver error message",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/#troubleshooting",
NewIssueLink: true,
},
Regexp: re(`Unable to start VM: start: exit status 1`),
},
{
Kind: Kind{
ID: "DRV_REGISTRY_NOT_FOUND",
ExitCode: ExDriverUnsupported,
Advice: "Your minikube config refers to an unsupported driver. Erase ~/.minikube, and try again.",
Issues: []int{5295},
},
Regexp: re(`registry: driver not found`),
},
{
Kind: Kind{
ID: "DRV_MISSING_ADDRESS",
ExitCode: ExDriverError,
Advice: "The machine-driver specified is failing to start. Try running 'docker-machine-driver-<type> version'",
Issues: []int{6023, 4679},
NewIssueLink: true,
},
Regexp: re(`new host: dial tcp: missing address`),
},
{
Kind: Kind{
ID: "DRV_CREATE_TIMEOUT",
ExitCode: ExDriverTimeout,
Advice: "Try 'minikube delete', and disable any conflicting VPN or firewall software",
Issues: []int{7072},
},
Regexp: re(`create host timed out in \d`),
},
{
Kind: Kind{
ID: "DRV_IMAGE_ARCH_UNSUPPORTED",
ExitCode: ExDriverUnsupported,
Advice: "This driver does not yet work on your architecture. Maybe try --driver=none",
Issues: []int{7071},
},
Regexp: re(`Error: incompatible image architecture`),
GOOS: []string{"linux"},
},
// Hyper-V
{
Kind: Kind{
ID: "DRV_HYPERV_NO_VSWITCH",
ExitCode: ExDriverConfig,
Advice: "Configure an external network switch following the official documentation, then add `--hyperv-virtual-switch=<switch-name>` to `minikube start`",
URL: "https://docs.docker.com/machine/drivers/hyper-v/",
},
Regexp: re(`no External vswitch found. A valid vswitch must be available for this command to run.`),
GOOS: []string{"windows"},
},
{
Kind: Kind{
ID: "DRV_HYPERV_VSWITCH_NOT_FOUND",
ExitCode: ExDriverUsage,
Advice: "Confirm that you have supplied the correct value to --hyperv-virtual-switch using the 'Get-VMSwitch' command",
URL: "https://docs.docker.com/machine/drivers/hyper-v/",
},
Regexp: re(`precreate: vswitch.*not found`),
GOOS: []string{"windows"},
},
{
Kind: Kind{
ID: "DRV_HYPERV_POWERSHELL_NOT_FOUND",
ExitCode: ExDriverUnavailable,
Advice: "To start minikube with Hyper-V, Powershell must be in your PATH`",
URL: "https://docs.docker.com/machine/drivers/hyper-v/",
},
Regexp: re(`Powershell was not found in the path`),
GOOS: []string{"windows"},
},
{
Kind: Kind{
ID: "DRV_HYPERV_FILE_DELETE",
ExitCode: ExDriverConflict,
Advice: "You may need to stop the Hyper-V Manager and run `minikube delete` again.",
Issues: []int{6804},
},
Regexp: re(`Unable to remove machine directory`),
GOOS: []string{"windows"},
},
// KVM
{
Kind: Kind{
ID: "DRV_KVM2_NOT_FOUND",
ExitCode: ExDriverNotFound,
Advice: "Please install the minikube kvm2 VM driver, or select an alternative --driver",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
},
Regexp: re(`Driver "kvm2" not found. Do you have the plugin binary .* accessible in your PATH`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "DRV_RESTART_NO_IP",
ExitCode: ExDriverTimeout,
Advice: "The KVM driver is unable to resurrect this old VM. Please run `minikube delete` to delete it and try again.",
Issues: []int{3901, 3434},
},
Regexp: re(`Error starting stopped host: Machine didn't return an IP after \d+ seconds`),
},
{
Kind: Kind{
ID: "DRV_NO_IP",
ExitCode: ExDriverTimeout,
Advice: "Check your firewall rules for interference, and run 'virt-host-validate' to check for KVM configuration issues. If you are running minikube within a VM, consider using --driver=none",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/",
Issues: []int{4249, 3566},
},
Regexp: re(`Error in driver during machine creation: Machine didn't return an IP after \d+ seconds`),
GOOS: []string{"linux"},
},
}
// localNetworkIssues are errors communicating to the guest
var localNetworkIssues = []match{
{
Kind: Kind{
ID: "IF_SSH_AUTH",
ExitCode: ExLocalNetworkConfig,
Advice: "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.",
URL: vpnDoc,
Issues: []int{3930},
},
Regexp: re(`ssh: handshake failed: ssh: unable to authenticate.*, no supported methods remain`),
},
{
Kind: Kind{
ID: "IF_SSH_NO_RESPONSE",
ExitCode: ExLocalNetworkConfig,
Advice: "Your host is failing to route packets to the minikube VM. If you have VPN software, try turning it off or configuring it so that it does not re-route traffic to the VM IP. If not, check your VM environment routing options.",
URL: vpnDoc,
Issues: []int{3388},
},
Regexp: re(`dial tcp .*:22: connectex: A connection attempt failed because the connected party did not properly respond`),
},
{
Kind: Kind{
ID: "IF_HOST_CIDR_CONFLICT",
ExitCode: ExLocalNetworkConflict,
Advice: "Specify an alternate --host-only-cidr value, such as 172.16.0.1/24",
Issues: []int{3594},
},
Regexp: re(`host-only cidr conflicts with the network address of a host interface`),
},
{
Kind: Kind{
ID: "IF_VBOX_NOT_VISIBLE",
ExitCode: ExLocalNetworkNotFound,
Advice: "Reboot to complete VirtualBox installation, verify that VirtualBox is not blocked by your system, and/or use another hypervisor",
Issues: []int{3614, 4222, 5817},
URL: "https://stackoverflow.com/questions/52277019/how-to-fix-vm-issue-with-minikube-start",
},
Regexp: re(`The host-only adapter we just created is not visible`),
},
{
Kind: Kind{
ID: "IF_VBOX_SAME_IP",
ExitCode: ExLocalNetworkConflict,
Advice: "Use VirtualBox to remove the conflicting VM and/or network interfaces",
URL: "https://stackoverflow.com/questions/55573426/virtualbox-is-configured-with-multiple-host-only-adapters-with-the-same-ip-whe",
Issues: []int{3584},
},
Regexp: re(`VirtualBox is configured with multiple host-only adapters with the same IP`),
},
{
Kind: Kind{
ID: "IF_VBOX_NOT_FOUND",
ExitCode: ExLocalNetworkNotFound,
Advice: "VirtualBox is unable to find its network interface. Try upgrading to the latest release and rebooting.",
Issues: []int{6036},
},
Regexp: re(`ERR_INTNET_FLT_IF_NOT_FOUND`),
},
{
Kind: Kind{
ID: "IF_VBOX_UNSPECIFIED",
ExitCode: ExLocalNetworkConflict,
Advice: "VirtualBox cannot create a network, probably because it conflicts with an existing network that minikube no longer knows about. Try running 'minikube delete'",
Issues: []int{5260},
},
Regexp: re(`Error setting up host only network on machine start.*Unspecified error`),
},
{
Kind: Kind{
ID: "IF_SSH_TIMEOUT",
ExitCode: ExLocalNetworkTimeout,
Advice: "Try 'minikube delete', and disable any conflicting VPN or firewall software",
Issues: []int{4617},
},
Regexp: re(`waiting for SSH to be available`),
},
}
// internetIssues are internet related problems.
var internetIssues = []match{
{
Kind: Kind{
ID: "INET_GCR_UNAVAILABLE",
ExitCode: ExInternetUnavailable,
Advice: "minikube is unable to access the Google Container Registry. You may need to configure it to use a HTTP proxy.",
URL: proxyDoc,
Issues: []int{3860},
},
Regexp: re(`gcr.io.*443: connect: invalid argument`),
},
{
Kind: Kind{
ID: "INET_RESET_BY_PEER",
ExitCode: ExInternetUnavailable,
Advice: "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.",
URL: proxyDoc,
Issues: []int{3909},
},
Regexp: re(`Error downloading .*connection reset by peer`),
},
{
Kind: Kind{
ID: "INET_DOWNLOAD_TIMEOUT",
ExitCode: ExInternetTimeout,
Advice: "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.",
URL: proxyDoc,
Issues: []int{3846},
},
Regexp: re(`Error downloading .*timeout`),
},
{
Kind: Kind{
ID: "INET_TLS_OVERSIZED",
ExitCode: ExInternetConflict,
Advice: "A firewall is interfering with minikube's ability to make outgoing HTTPS requests. You may need to change the value of the HTTPS_PROXY environment variable.",
URL: proxyDoc,
Issues: []int{3857, 3759, 4252},
},
Regexp: re(`tls: oversized record received with length`),
},
{
Kind: Kind{
ID: "INET_DOWNLOAD_BLOCKED",
ExitCode: ExInternetTimeout,
Advice: "A firewall is likely blocking minikube from reaching the internet. You may need to configure minikube to use a proxy.",
URL: proxyDoc,
Issues: []int{3922, 6109, 6123},
},
Regexp: re(`iso: failed to download|download.*host has failed to respond`),
},
{
Kind: Kind{
ID: "INET_PULL_TIMEOUT",
ExitCode: ExInternetTimeout,
Advice: "A firewall is blocking Docker the minikube VM from reaching the image repository. You may need to select --image-repository, or use a proxy.",
URL: proxyDoc,
Issues: []int{3898, 6070},
},
Regexp: re(`ImagePull.*Timeout exceeded while awaiting headers`),
},
{
Kind: Kind{
ID: "INET_LOOKUP_HOST",
ExitCode: ExInternetConfig,
Advice: "Verify that your HTTP_PROXY and HTTPS_PROXY environment variables are set correctly.",
URL: proxyDoc,
},
Regexp: re(`dial tcp: lookup.*: no such host`),
},
{
Kind: Kind{
ID: "INET_PROXY_CONFUSION",
ExitCode: ExInternetConfig,
Advice: "Ensure that your value for HTTPS_PROXY points to an HTTPS proxy rather than an HTTP proxy",
Issues: []int{6107},
URL: proxyDoc,
},
Regexp: re(`http: server gave HTTP response to HTTPS client`),
},
{
Kind: Kind{
ID: "INET_NOT_TLS",
ExitCode: ExInternetConfig,
Advice: "Ensure that your value for HTTPS_PROXY points to an HTTPS proxy rather than an HTTP proxy",
Issues: []int{7286},
URL: proxyDoc,
},
Regexp: re(`tls: first record does not look like a TLS handshake`),
},
{
Kind: Kind{
ID: "INET_PROXY_503",
ExitCode: ExInternetConfig,
Advice: "Confirm that you have a working internet connection and that your VM has not run out of resources by using: 'minikube logs'",
Issues: []int{4749},
},
Regexp: re(`proxy.*unexpected response code: 503`),
},
{
Kind: Kind{
ID: "INET_DEFAULT_ROUTE",
ExitCode: ExInternetNotFound,
Advice: "Configure a default route on this Linux host, or use another --driver that does not require it",
Issues: []int{6083, 5636},
},
Regexp: re(`(No|from) default routes`),
GOOS: []string{"linux"},
},
}
var guestIssues = []match{
{
Kind: Kind{
ID: "GUEST_KVM2_NO_DOMAIN",
ExitCode: ExGuestNotFound,
Advice: "The VM that minikube is configured for no longer exists. Run 'minikube delete'",
Issues: []int{3636},
},
Regexp: re(`no domain with matching name`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "GUEST_PORT_IN_USE",
ExitCode: ExGuestConflict,
Advice: "kubeadm detected a TCP port conflict with another process: probably another local Kubernetes installation. Run lsof -p<port> to find the process and kill it",
Issues: []int{5484},
},
Regexp: re(`ERROR Port-.*is in use`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "GUEST_DOES_NOT_EXIST",
ExitCode: ExGuestNotFound,
Advice: "Run 'minikube delete' to delete the stale VM, or and ensure that minikube is running as the same user you are issuing this command with",
Issues: []int{3864, 6087},
},
Regexp: re(`machine does not exist`),
},
{
Kind: Kind{
ID: "GUEST_NOT_FOUND",
ExitCode: ExGuestNotFound,
Advice: "Your minikube vm is not running, try minikube start.",
Issues: []int{4889},
},
Regexp: re(`Machine does not exist for api.Exists`),
},
{
Kind: Kind{
ID: "GUEST_IP_NOT_FOUND",
ExitCode: ExGuestNotRunning,
Advice: "The minikube VM is offline. Please run 'minikube start' to start it again.",
Issues: []int{3849, 3648},
},
Regexp: re(`Error getting ssh host name for driver: IP not found`),
},
{
Kind: Kind{
ID: "GUEST_UNSIGNED_CERT",
ExitCode: ExGuestConfig,
Advice: "Try 'minikube delete' to force new SSL certificates to be installed",
Issues: []int{6596},
},
Regexp: re(`not signed by CA certificate ca: crypto/rsa: verification error`),
},
{
Kind: Kind{
ID: "GUEST_VBOX_NO_VM",
ExitCode: ExGuestNotFound,
Advice: "The VM that minikube is configured for no longer exists. Run 'minikube delete'",
Issues: []int{4694},
},
Regexp: re(`Could not find a registered machine named`),
},
{
Kind: Kind{
ID: "GUEST_FILE_IN_USE",
ExitCode: ExGuestConflict,
Advice: "Another program is using a file required by minikube. If you are using Hyper-V, try stopping the minikube VM from within the Hyper-V manager",
URL: "https://docs.docker.com/machine/drivers/hyper-v/",
Issues: []int{7300},
},
Regexp: re(`The process cannot access the file because it is being used by another process`),
GOOS: []string{"windows"},
},
}
// runtimeIssues are container runtime issues (containerd, docker, etc)
var runtimeIssues = []match{
{
Kind: Kind{
ID: "RT_DOCKER_RESTART",
ExitCode: ExRuntimeError,
Advice: "Remove the incompatible --docker-opt flag if one was provided",
Issues: []int{7070},
},
Regexp: re(`systemctl -f restart docker`),
},
{
Kind: Kind{
ID: "RT_DOCKER_UNAVAILABLE",
ExitCode: ExRuntimeUnavailable,
Advice: "Docker inside the VM is unavailable. Try running 'minikube delete' to reset the VM.",
Issues: []int{3952},
},
Regexp: re(`Error configuring auth on host: OS type not recognized`),
},
{
Kind: Kind{
ID: "RT_DOCKER_EXIT_1",
ExitCode: ExRuntimeNotFound,
Advice: "Either systemctl is not installed, or Docker is broken. Run 'sudo systemctl start docker' and 'journalctl -u docker'",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/none",
Issues: []int{2704, 4498},
},
Regexp: re(`sudo systemctl start docker: exit status 1`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "RT_DOCKER_EXIT_5",
ExitCode: ExRuntimeUnavailable,
Advice: "Ensure that Docker is installed and healthy: Run 'sudo systemctl start docker' and 'journalctl -u docker'. Alternatively, select another value for --driver",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/none",
Issues: []int{5532},
},
Regexp: re(`sudo systemctl start docker: exit status 5`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "RT_CRIO_EXIT_5",
ExitCode: ExRuntimeUnavailable,
Advice: "Ensure that CRI-O is installed and healthy: Run 'sudo systemctl start crio' and 'journalctl -u crio'. Alternatively, use --container-runtime=docker",
URL: "https://minikube.sigs.k8s.io/docs/reference/drivers/none",
Issues: []int{5532},
},
Regexp: re(`sudo systemctl restart crio: exit status 5`),
GOOS: []string{"linux"},
},
}
// controlPlaneIssues are Kubernetes deployment issues
var controlPlaneIssues = []match{
{
Kind: Kind{
ID: "K8S_APISERVER_MISSING",
ExitCode: ExControlPlaneNotFound,
Advice: "Check that the provided apiserver flags are valid, and that SELinux is disabled",
Issues: []int{4536, 6014},
},
Regexp: re(`apiserver process never appeared`),
},
{
Kind: Kind{
ID: "K8S_APISERVER_TIMEOUT",
ExitCode: ExControlPlaneTimeout,
Advice: "A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/",
URL: vpnDoc,
Issues: []int{4302},
},
Regexp: re(`apiserver: timed out waiting for the condition`),
},
{
Kind: Kind{
ID: "K8S_DNS_TIMEOUT",
ExitCode: ExControlPlaneTimeout,
Advice: "Run 'kubectl describe pod coredns -n kube-system' and check for a firewall or DNS conflict",
URL: vpnDoc,
},
Regexp: re(`dns: timed out waiting for the condition`),
},
{
Kind: Kind{
ID: "K8S_KUBELET_NOT_RUNNING",
ExitCode: ExControlPlaneUnavailable,
Advice: "Check output of 'journalctl -xeu kubelet', try passing --extra-config=kubelet.cgroup-driver=systemd to minikube start",
Issues: []int{4172},
},
Regexp: re(`The kubelet is not running|kubelet isn't running`),
GOOS: []string{"linux"},
},
{
Kind: Kind{
ID: "K8S_INVALID_DNS_DOMAIN",
ExitCode: ExControlPlaneConfig,
Advice: "Select a valid value for --dnsdomain",
},
Regexp: re(`dnsDomain: Invalid`),
},
}
// serviceIssues are issues with services running on top of Kubernetes
var serviceIssues = []match{
{
Kind: Kind{
ID: "SVC_ENDPOINT_NOT_FOUND",
ExitCode: ExSvcNotFound,
Advice: "Please make sure the service you are looking for is deployed or is in the correct namespace.",
Issues: []int{4599},
},
Regexp: re(`Could not find finalized endpoint being pointed to by`),
},
{
Kind: Kind{
ID: "SVC_OPEN_NOT_FOUND",
ExitCode: ExSvcNotFound,
Advice: "Use 'kubect get po -A' to find the correct and namespace name",
Issues: []int{5836},
},
Regexp: re(`Error opening service.*not found`),
},
{
Kind: Kind{
ID: "SVC_DASHBOARD_ROLE_REF",
ExitCode: ExSvcPermission,
Advice: "Run: 'kubectl delete clusterrolebinding kubernetes-dashboard'",
Issues: []int{7256},
},
Regexp: re(`dashboard.*cannot change roleRef`),
},
}

View File

@ -0,0 +1,87 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package reason
import (
"regexp"
"github.com/golang/glog"
)
// match matches a known issue within minikube
type match struct {
// Inherit ID, ExitCode, and Style from reason.Kind
Kind
// Regexp is which regular expression this issue matches
Regexp *regexp.Regexp
// Operating systems this error is specific to
GOOS []string
}
func knownIssues() []match {
ps := []match{}
// This is intentionally in dependency order
ps = append(ps, programIssues...)
ps = append(ps, resourceIssues...)
ps = append(ps, hostIssues...)
ps = append(ps, providerIssues...)
ps = append(ps, driverIssues...)
ps = append(ps, localNetworkIssues...)
ps = append(ps, internetIssues...)
ps = append(ps, guestIssues...)
ps = append(ps, runtimeIssues...)
ps = append(ps, controlPlaneIssues...)
ps = append(ps, serviceIssues...)
return ps
}
// MatchKnownIssue returns a known issue from an error on an OS
func MatchKnownIssue(r Kind, err error, goos string) *Kind {
// The kind passed in has specified that it should not be rematched
if r.NoMatch {
return nil
}
var genericMatch *Kind
for _, ki := range knownIssues() {
ki := ki
if ki.Regexp == nil {
glog.Errorf("known issue has no regexp: %+v", ki)
continue
}
if !ki.Regexp.MatchString(err.Error()) {
continue
}
// Does this match require an OS matchup?
if len(ki.GOOS) > 0 {
for _, o := range ki.GOOS {
if o == goos {
return &ki.Kind
}
}
}
if genericMatch == nil {
genericMatch = &ki.Kind
}
}
return genericMatch
}

View File

@ -0,0 +1,90 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package reason
import (
"fmt"
"testing"
)
func TestFromError(t *testing.T) {
tests := []struct {
issue int
os string
want string
err string
}{
{0, "", "", "this is just a lame error message with no matches."},
{2991, "linux", "PR_KVM_CAPABILITIES", "Unable to start VM: create: Error creating machine: Error in driver during machine creation: creating domain: Error defining domain xml:\n\n: virError(Code=8, Domain=44, Message='invalid argument: could not find capabilities for domaintype=kvm ')"},
{3594, "", "IF_HOST_CIDR_CONFLICT", "Error starting host: Error starting stopped host: Error setting up host only network on machine start: host-only cidr conflicts with the network address of a host interface."},
{3614, "", "IF_VBOX_NOT_VISIBLE", "Error starting host: Error starting stopped host: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is supposed to fix this issue"},
{3784, "", "PR_VBOXMANAGE_NOT_FOUND", "create: precreate: VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path"},
{3849, "", "GUEST_IP_NOT_FOUND", "bootstrapper: Error creating new ssh host from driver: Error getting ssh host name for driver: IP not found"},
{3859, "windows", "PR_VBOX_HARDENING", `Unable to start VM: create: creating: Unable to start the VM: C:\Program Files\Oracle\VirtualBox\VBoxManage.exe startvm minikube --type headless failed:
VBoxManage.exe: error: The virtual machine 'minikube' has terminated unexpectedly during startup with exit code -1073741819 (0xc0000005). More details may be available in 'C:\Users\pabitra_b.minikube\machines\minikube\minikube\Logs\VBoxHardening.log'
VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component MachineWrap, interface IMachine`},
{3922, "", "INET_DOWNLOAD_BLOCKED", `unable to cache ISO: https://storage.googleapis.com/minikube/iso/minikube-v0.35.0.iso: failed to download: failed to download to temp file: download failed: 5 error(s) occurred:
* Temporary download error: Get https://storage.googleapis.com/minikube/iso/minikube-v0.35.0.iso: dial tcp 216.58.207.144:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.`},
{4107, "darwin", "PR_VBOX_BLOCKED", "Result Code: NS_ERROR (0x80004005)"},
{4302, "", "K8S_APISERVER_TIMEOUT", "apiserver: timed out waiting for the condition"},
{4252, "", "INET_TLS_OVERSIZED", "Failed to update cluster: downloading binaries: downloading kubeadm: Error downloading kubeadm v1.14.1: failed to download: failed to download to temp file: download failed: 5 error(s) occurred:\n\nTemporary download error: Get https://storage.googleapis.com/kubernetes-release/release/v1.14.1/bin/linux/amd64/kubeadm: proxyconnect tcp: tls: oversized record received with length 20527"},
{4222, "", "IF_VBOX_NOT_VISIBLE", "Unable to start VM: create: creating: Error setting up host only network on machine start: The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is supposed to fix this issue"},
{6014, "linux", "K8S_APISERVER_MISSING", "Error restarting cluster: waiting for apiserver: apiserver process never appeared"},
{5836, "", "SVC_OPEN_NOT_FOUND", `Error opening service: Service newservice was not found in "unknown" namespace. You may select another namespace by using 'minikube service newservice -n : Temporary Error: Error getting service newservice: services "newservice" not found`},
{6087, "", "GUEST_DOES_NOT_EXIST", `Error getting machine status: state: machine does not exist`},
{5714, "darwin", "HOST_KUBECONFIG_PERMISSION", `Failed to setup kubeconfig: writing kubeconfig: Error writing file /Users/matthewgleich/.kube/config: error writing file /Users/matthewgleich/.kube/config: open /Users/matthewgleich/.kube/config: permission denied`},
{5532, "linux", "RT_DOCKER_EXIT_5", `Failed to enable container runtime: running command: sudo systemctl start docker: exit status 5`},
{5532, "linux", "RT_CRIO_EXIT_5", `Failed to enable container runtime: running command: sudo systemctl restart crio: exit status 5`},
{5484, "linux", "GUEST_PORT_IN_USE", `[ERROR Port-10252]: Port 10252 is in use`},
{4913, "linux", "PR_KVM_CREATE_BUSY", `Unable to start VM: create: Error creating machine: Error in driver during machine creation: error creating VM: virError(Code=1, Domain=10, Message='internal error: process exited while connecting to monitor: ioctl(KVM_CREATE_VM) failed: 16 Device or resource busy`},
{5950, "linux", "PR_KVM_ISO_PERMISSION", `Retriable failure: create: Error creating machine: Error in driver during machine creation: error creating VM: virError(Code=1, Domain=10, Message='internal error: qemu unexpectedly closed the monitor: 2019-11-19T16:08:16.757609Z qemu-kvm: -drive file=/home/lnicotra/.minikube/machines/minikube/boot2docker.iso,format=raw,if=none,id=drive-scsi0-0-0-2,readonly=on: could not open disk image /home/lnicotra/.minikube/machines/minikube/boot2docker.iso: Could not open '/home/lnicotra/.minikube/machines/minikube/boot2docker.iso': Permission denied'`},
{5836, "", "SVC_OPEN_NOT_FOUND", `Error opening service: Service kubernetes-bootcamp was not found in "default" namespace. You may select another namespace by using 'minikube service kubernetes-bootcamp -n : Temporary Error: Error getting service kubernetes-bootcamp: services "kubernetes-bootcamp" not found`},
{3898, "", "INET_PULL_TIMEOUT", `[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-controller-manager:v1.17.0: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)`},
{6079, "darwin", "PR_HYPERKIT_CRASHED", `Error creating machine: Error in driver during machine creation: hyperkit crashed! command line:`},
{5636, "linux", "INET_DEFAULT_ROUTE", `Unable to get VM IP address: unable to select an IP from default routes.`},
{6087, "", "GUEST_DOES_NOT_EXIST", `Error getting host status: state: machine does not exist`},
{6098, "windows", "PR_PRECREATE_EXIT_1", `Retriable failure: create: precreate: exit status 1`},
{6107, "", "INET_PROXY_CONFUSION", `http: server gave HTTP response to HTTPS client`},
{6109, "", "INET_DOWNLOAD_BLOCKED", `Failed to update cluster: downloading binaries: downloading kubelet: Error downloading kubelet v1.16.2: failed to download: failed to download to temp file: failed to copy contents: read tcp 192.168.0.106:61314->172.217.166.176:443: wsarecv: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.`},
{6109, "", "INET_DOWNLOAD_BLOCKED", `Failed to update cluster: downloading binaries: downloading kubeadm: Error downloading kubeadm v1.17.0: failed to download: failed to download to temp file: failed to copy contents: read tcp [2606:a000:81c5:1e00:349a:26c0:7ea6:bbf1]:55317->[2607:f8b0:4004:815::2010]:443: wsarecv: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.`},
{4277, "linux", "PR_KVM_MSR", `Unable to start VM: start: Error creating VM: virError(Code=1, Domain=10, Message='internal error: qemu unexpectedly closed the monitor: 2019-05-17T02:20:07.980140Z qemu-system-x86_64: error: failed to set MSR 0x38d to 0x0 qemu-system-x86_64: /build/qemu-lXHhGe/qemu-2.11+dfsg/target/i386/kvm.c:1807: kvm_put_msrs: Assertion ret == cpu->kvm_msr_buf->nmsrs failed.`},
}
for _, tc := range tests {
t.Run(tc.want, func(t *testing.T) {
got := MatchKnownIssue(Kind{}, fmt.Errorf(tc.err), tc.os)
if got == nil {
if tc.want != "" {
t.Errorf("FromError(%q)=nil, want %s", tc.err, tc.want)
}
return
}
if got.ID != tc.want {
t.Errorf("FromError(%q)=%s, want %s", tc.err, got.ID, tc.want)
}
found := false
for _, i := range got.Issues {
if i == tc.issue {
found = true
}
}
if !found {
t.Errorf("Issue %d is not listed in %+v", tc.issue, got.Issues)
}
})
}
}

View File

@ -0,0 +1,286 @@
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the Kind{ID: "License", ExitCode: });
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an Kind{ID: "AS IS", ExitCode: } BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package reason
import (
"fmt"
"k8s.io/minikube/pkg/minikube/style"
)
const issueBase = "https://github.com/kubernetes/minikube/issues"
// Kind describes reason metadata
type Kind struct {
// ID is an unique and stable string describing a reason
ID string
// ExitCode to be used (defaults to 1)
ExitCode int
// Style is what emoji prefix to use for this reason
Style style.Enum
// Advice is actionable text that the user should follow
Advice string
// URL is a reference URL for more information
URL string
// Issues are a list of related issues to this issue
Issues []int
// Show the new issue link
NewIssueLink bool
// Do not attempt to match this reason to a specific known issue
NoMatch bool
}
func (k *Kind) IssueURLs() []string {
is := []string{}
for _, i := range k.Issues {
is = append(is, fmt.Sprintf("%s/%d", issueBase, i))
}
return is
}
// Sections are ordered roughly by stack dependencies
var (
Usage = Kind{ID: "MK_USAGE", ExitCode: ExProgramUsage}
Interrupted = Kind{ID: "MK_INTERRUPTED", ExitCode: ExProgramConflict}
NewAPIClient = Kind{ID: "MK_NEW_APICLIENT", ExitCode: ExProgramError}
InternalAddonEnable = Kind{ID: "MK_ADDON_ENABLE", ExitCode: ExProgramError}
InternalAddConfig = Kind{ID: "MK_ADD_CONFIG", ExitCode: ExProgramError}
InternalBindFlags = Kind{ID: "MK_BIND_FLAGS", ExitCode: ExProgramError}
InternalBootstrapper = Kind{ID: "MK_BOOTSTRAPPER", ExitCode: ExProgramError}
InternalCacheList = Kind{ID: "MK_CACHE_LIST", ExitCode: ExProgramError}
InternalCacheLoad = Kind{ID: "MK_CACHE_LOAD", ExitCode: ExProgramError}
InternalCommandRunner = Kind{ID: "MK_COMMAND_RUNNER", ExitCode: ExProgramError}
InternalCompletion = Kind{ID: "MK_COMPLETION", ExitCode: ExProgramError}
InternalConfigSet = Kind{ID: "MK_CONFIG_SET", ExitCode: ExProgramError}
InternalConfigUnset = Kind{ID: "MK_CONFIG_UNSET", ExitCode: ExProgramError}
InternalConfigView = Kind{ID: "MK_CONFIG_VIEW", ExitCode: ExProgramError}
InternalDelConfig = Kind{ID: "MK_DEL_CONFIG", ExitCode: ExProgramError}
InternalDisable = Kind{ID: "MK_DISABLE", ExitCode: ExProgramError}
InternalDockerScript = Kind{ID: "MK_DOCKER_SCRIPT", ExitCode: ExProgramError}
InternalEnable = Kind{ID: "MK_ENABLE", ExitCode: ExProgramError}
InternalFlagsBind = Kind{ID: "MK_FLAGS_BIND", ExitCode: ExProgramError}
InternalFlagSet = Kind{ID: "MK_FLAGS_SET", ExitCode: ExProgramError}
InternalFormatUsage = Kind{ID: "MK_FORMAT_USAGE", ExitCode: ExProgramError}
InternalGenerateDocs = Kind{ID: "MK_GENERATE_DOCS", ExitCode: ExProgramError}
InternalJSONMarshal = Kind{ID: "MK_JSON_MARSHAL", ExitCode: ExProgramError}
InternalKubernetesClient = Kind{ID: "MK_K8S_CLIENT", ExitCode: ExControlPlaneUnavailable}
InternalListConfig = Kind{ID: "MK_LIST_CONFIG", ExitCode: ExProgramError}
InternalLogtostderrFlag = Kind{ID: "MK_LOGTOSTDERR_FLAG", ExitCode: ExProgramError}
InternalLogFollow = Kind{ID: "MK_LOG_FOLLOW", ExitCode: ExProgramError}
InternalNewRuntime = Kind{ID: "MK_NEW_RUNTIME", ExitCode: ExProgramError}
InternalOutputUsage = Kind{ID: "MK_OUTPUT_USAGE", ExitCode: ExProgramError}
InternalRuntime = Kind{ID: "MK_RUNTIME", ExitCode: ExProgramError}
InternalReservedProfile = Kind{ID: "MK_RESERVED_PROFILE", ExitCode: ExProgramConflict}
InternalEnvScript = Kind{ID: "MK_ENV_SCRIPT", ExitCode: ExProgramError}
InternalShellDetect = Kind{ID: "MK_SHELL_DETECT", ExitCode: ExProgramError}
InternalStatusJSON = Kind{ID: "MK_STATUS_JSON", ExitCode: ExProgramError}
InternalStatusText = Kind{ID: "MK_STATUS_TEXT", ExitCode: ExProgramError}
InternalUnsetScript = Kind{ID: "MK_UNSET_SCRIPT", ExitCode: ExProgramError}
InternalViewExec = Kind{ID: "MK_VIEW_EXEC", ExitCode: ExProgramError}
InternalViewTmpl = Kind{ID: "MK_VIEW_TMPL", ExitCode: ExProgramError}
InternalYamlMarshal = Kind{ID: "MK_YAML_MARSHAL", ExitCode: ExProgramError}
InternalCredsNotFound = Kind{ID: "MK_CREDENTIALS_NOT_FOUND", ExitCode: ExProgramNotFound, Style: style.Shrug}
InternalSemverParse = Kind{ID: "MK_SEMVER_PARSE", ExitCode: ExProgramError}
RsrcInsufficientCores = Kind{ID: "RSRC_INSUFFICIENT_CORES", ExitCode: ExInsufficientCores, Style: style.UnmetRequirement}
RsrcInsufficientDarwinDockerCores = Kind{
ID: "RSRC_DOCKER_CORES",
ExitCode: ExInsufficientCores,
Advice: `1. Click on "Docker for Desktop" menu icon
2. Click "Preferences"
3. Click "Resources"
4. Increase "CPUs" slider bar to 2 or higher
5. Click "Apply & Restart"`,
Style: style.UnmetRequirement,
URL: "https://docs.docker.com/docker-for-mac/#resources",
}
RsrcInsufficientWindowsDockerCores = Kind{
ID: "RSRC_DOCKER_CORES",
ExitCode: ExInsufficientCores,
Advice: `1. Open the "Docker Desktop" menu by clicking the Docker icon in the system tray
2. Click "Settings"
3. Click "Resources"
4. Increase "CPUs" slider bar to 2 or higher
5. Click "Apply & Restart"`,
URL: "https://docs.docker.com/docker-for-windows/#resources",
Style: style.UnmetRequirement,
}
RsrcInsufficientReqMemory = Kind{ID: "RSRC_INSUFFICIENT_REQ_MEMORY", ExitCode: ExInsufficientMemory, Style: style.UnmetRequirement}
RsrcInsufficientSysMemory = Kind{ID: "RSRC_INSUFFICIENT_SYS_MEMORY", ExitCode: ExInsufficientMemory, Style: style.UnmetRequirement}
RsrcInsufficientContainerMemory = Kind{ID: "RSRC_INSUFFICIENT_CONTAINER_MEMORY", ExitCode: ExInsufficientMemory, Style: style.UnmetRequirement}
RsrcInsufficientWindowsDockerMemory = Kind{
ID: "RSRC_DOCKER_MEMORY",
ExitCode: ExInsufficientMemory,
Advice: `1. Open the "Docker Desktop" menu by clicking the Docker icon in the system tray
2. Click "Settings"
3. Click "Resources"
4. Increase "Memory" slider bar to {{.recommend}} or higher
5. Click "Apply & Restart"`,
URL: "https://docs.docker.com/docker-for-windows/#resources",
Style: style.UnmetRequirement,
}
RsrcInsufficientDarwinDockerMemory = Kind{
ID: "RSRC_DOCKER_MEMORY",
ExitCode: ExInsufficientCores,
Advice: `1. Click on "Docker for Desktop" menu icon
2. Click "Preferences"
3. Click "Resources"
4. Increase "Memory" slider bar to {{.recommend}} or higher
5. Click "Apply & Restart"`,
Style: style.UnmetRequirement,
URL: "https://docs.docker.com/docker-for-mac/#resources",
}
RsrcInsufficientDockerStorage = Kind{
ID: "RSRC_DOCKER_STORAGE",
ExitCode: ExInsufficientStorage,
Advice: `Try at least one of the following to free up space on the device:
1. Run "docker system prune" to remove unused docker data
2. Increase the amount of memory allocated to Docker for Desktop via
Docker icon > Preferences > Resources > Disk Image Size
3. Run "minikube ssh -- docker system prune" if using the docker container runtime`,
Issues: []int{9024},
}
RsrcInsufficientStorage = Kind{ID: "RSRC_INSUFFICIENT_STORAGE", ExitCode: ExInsufficientStorage, Style: style.UnmetRequirement}
HostHomeMkdir = Kind{ID: "HOST_HOME_MKDIR", ExitCode: ExHostPermission}
HostHomeChown = Kind{ID: "HOST_HOME_CHOWN", ExitCode: ExHostPermission}
HostBrowser = Kind{ID: "HOST_BROWSER", ExitCode: ExHostError}
HostConfigLoad = Kind{ID: "HOST_CONFIG_LOAD", ExitCode: ExHostConfig}
HostCurrentUser = Kind{ID: "HOST_CURRENT_USER", ExitCode: ExHostConfig}
HostDelCache = Kind{ID: "HOST_DEL_CACHE", ExitCode: ExHostError}
HostKillMountProc = Kind{ID: "HOST_KILL_MOUNT_PROC", ExitCode: ExHostError}
HostKubeconfigUnset = Kind{ID: "HOST_KUBECNOFIG_UNSET", ExitCode: ExHostConfig}
HostKubeconfigUpdate = Kind{ID: "HOST_KUBECONFIG_UPDATE", ExitCode: ExHostConfig}
HostKubectlProxy = Kind{ID: "HOST_KUBECTL_PROXY", ExitCode: ExHostError}
HostMountPid = Kind{ID: "HOST_MOUNT_PID", ExitCode: ExHostError}
HostPathMissing = Kind{ID: "HOST_PATH_MISSING", ExitCode: ExHostNotFound}
HostPathStat = Kind{ID: "HOST_PATH_STAT", ExitCode: ExHostError}
HostPurge = Kind{ID: "HOST_PURGE", ExitCode: ExHostError}
HostSaveProfile = Kind{ID: "HOST_SAVE_PROFILE", ExitCode: ExHostConfig}
ProviderNotFound = Kind{ID: "PROVIDER_NOT_FOUND", ExitCode: ExProviderNotFound}
ProviderUnavailable = Kind{ID: "PROVIDER_UNAVAILABLE", ExitCode: ExProviderNotFound, Style: style.Shrug}
DrvCPEndpoint = Kind{ID: "DRV_CP_ENDPOINT", ExitCode: ExDriverError}
DrvPortForward = Kind{ID: "DRV_PORT_FORWARD", ExitCode: ExDriverError}
DrvUnsupportedMulti = Kind{ID: "DRV_UNSUPPORTED_MULTINODE", ExitCode: ExDriverConflict}
DrvUnsupportedOS = Kind{ID: "DRV_UNSUPPORTED_OS", ExitCode: ExDriverUnsupported}
DrvUnsupportedProfile = Kind{ID: "DRV_UNSUPPORTED_PROFILE", ExitCode: ExDriverUnsupported}
DrvNotFound = Kind{ID: "DRV_NOT_FOUND", ExitCode: ExDriverNotFound}
DrvNotDetected = Kind{ID: "DRV_NOT_DETECTED", ExitCode: ExDriverNotFound}
DrvAsRoot = Kind{ID: "DRV_AS_ROOT", ExitCode: ExDriverPermission}
DrvNeedsRoot = Kind{ID: "DRV_NEEDS_ROOT", ExitCode: ExDriverPermission}
GuestCacheLoad = Kind{ID: "GUEST_CACHE_LOAD", ExitCode: ExGuestError}
GuestCert = Kind{ID: "GUEST_CERT", ExitCode: ExGuestError}
GuestCpConfig = Kind{ID: "GUEST_CP_CONFIG", ExitCode: ExGuestConfig}
GuestDeletion = Kind{ID: "GUEST_DELETION", ExitCode: ExGuestError}
GuestLoadHost = Kind{ID: "GUEST_LOAD_HOST", ExitCode: ExGuestError}
GuestMount = Kind{ID: "GUEST_MOUNT", ExitCode: ExGuestError}
GuestNodeAdd = Kind{ID: "GUEST_NODE_ADD", ExitCode: ExGuestError}
GuestNodeDelete = Kind{ID: "GUEST_NODE_DELETE", ExitCode: ExGuestError}
GuestNodeProvision = Kind{ID: "GUEST_NODE_PROVISION", ExitCode: ExGuestError}
GuestNodeRetrieve = Kind{ID: "GUEST_NODE_RETRIEVE", ExitCode: ExGuestNotFound}
GuestNodeStart = Kind{ID: "GUEST_NODE_START", ExitCode: ExGuestError}
GuestPause = Kind{ID: "GUEST_PAUSE", ExitCode: ExGuestError}
GuestProfileDeletion = Kind{ID: "GUEST_PROFILE_DELETION", ExitCode: ExGuestError}
GuestProvision = Kind{ID: "GUEST_PROVISION", ExitCode: ExGuestError}
GuestStart = Kind{ID: "GUEST_START", ExitCode: ExGuestError}
GuestStatus = Kind{ID: "GUEST_STATUS", ExitCode: ExGuestError}
GuestStopTimeout = Kind{ID: "GUEST_STOP_TIMEOUT", ExitCode: ExGuestTimeout}
GuestUnpause = Kind{ID: "GUEST_UNPAUSE", ExitCode: ExGuestError}
GuestDrvMismatch = Kind{ID: "GUEST_DRIVER_MISMATCH", ExitCode: ExGuestConflict, Style: style.Conflict}
GuestMissingConntrack = Kind{ID: "GUEST_MISSING_CONNTRACK", ExitCode: ExGuestUnsupported}
IfHostIP = Kind{ID: "IF_HOST_IP", ExitCode: ExLocalNetworkError}
IfMountIP = Kind{ID: "IF_MOUNT_IP", ExitCode: ExLocalNetworkError}
IfMountPort = Kind{ID: "IF_MOUNT_PORT", ExitCode: ExLocalNetworkError}
IfSSHClient = Kind{ID: "IF_SSH_CLIENT", ExitCode: ExLocalNetworkError}
InetCacheBinaries = Kind{ID: "INET_CACHE_BINARIES", ExitCode: ExInternetError}
InetCacheKubectl = Kind{ID: "INET_CACHE_KUBECTL", ExitCode: ExInternetError}
InetCacheTar = Kind{ID: "INET_CACHE_TAR", ExitCode: ExInternetError}
InetGetVersions = Kind{ID: "INET_GET_VERSIONS", ExitCode: ExInternetError}
InetRepo = Kind{ID: "INET_REPO", ExitCode: ExInternetError}
InetReposUnavailable = Kind{ID: "INET_REPOS_UNAVAILABLE", ExitCode: ExInternetError}
InetVersionUnavailable = Kind{ID: "INET_VERSION_UNAVAILABLE", ExitCode: ExInternetUnavailable}
InetVersionEmpty = Kind{ID: "INET_VERSION_EMPTY", ExitCode: ExInternetConfig}
RuntimeEnable = Kind{ID: "RUNTIME_ENABLE", ExitCode: ExRuntimeError}
RuntimeCache = Kind{ID: "RUNTIME_CACHE", ExitCode: ExRuntimeError}
RuntimeRestart = Kind{ID: "RUNTIME_RESTART", ExitCode: ExRuntimeError}
SvcCheckTimeout = Kind{ID: "SVC_CHECK_TIMEOUT", ExitCode: ExSvcTimeout}
SvcTimeout = Kind{ID: "SVC_TIMEOUT", ExitCode: ExSvcTimeout}
SvcList = Kind{ID: "SVC_LIST", ExitCode: ExSvcError}
SvcTunnelStart = Kind{ID: "SVC_TUNNEL_START", ExitCode: ExSvcError}
SvcTunnelStop = Kind{ID: "SVC_TUNNEL_STOP", ExitCode: ExSvcError}
SvcURLTimeout = Kind{ID: "SVC_URL_TIMEOUT", ExitCode: ExSvcTimeout}
SvcNotFound = Kind{ID: "SVC_NOT_FOUND", ExitCode: ExSvcNotFound}
EnvDriverConflict = Kind{ID: "ENV_DRIVER_CONFLICT", ExitCode: ExDriverConflict}
EnvMultiConflict = Kind{ID: "ENV_MULTINODE_CONFLICT", ExitCode: ExGuestConflict}
EnvDockerUnavailable = Kind{ID: "ENV_DOCKER_UNAVAILABLE", ExitCode: ExRuntimeUnavailable}
EnvPodmanUnavailable = Kind{ID: "ENV_PODMAN_UNAVAILABLE", ExitCode: ExRuntimeUnavailable}
AddonUnsupported = Kind{ID: "SVC_ADDON_UNSUPPORTED", ExitCode: ExSvcUnsupported}
AddonNotEnabled = Kind{ID: "SVC_ADDON_NOT_ENABLED", ExitCode: ExProgramConflict}
KubernetesInstallFailed = Kind{ID: "K8S_INSTALL_FAILED", ExitCode: ExControlPlaneError}
KubernetesTooOld = Kind{ID: "K8S_OLD_UNSUPPORTED", ExitCode: ExControlPlaneUnsupported}
KubernetesDowngrade = Kind{
ID: "K8S_DOWNGRADE_UNSUPPORTED",
ExitCode: ExControlPlaneUnsupported,
Advice: `1) Recreate the cluster with Kubernetes {{.new}}, by running:
minikube delete{{.profile}}
minikube start{{.profile}} --kubernetes-version={{.prefix}}{{.new}}
2) Create a second cluster with Kubernetes {{.new}}, by running:
minikube start -p {{.suggestedName}} --kubernetes-version={{.prefix}}{{.new}}
3) Use the existing cluster at version Kubernetes {{.old}}, by running:
minikube start{{.profile}} --kubernetes-version={{.prefix}}{{.old}}
`,
Style: style.SeeNoEvil,
}
)

View File

@ -150,17 +150,17 @@ func checkOverlayMod() registry.State {
// suggestFix matches a stderr with possible fix for the docker driver
func suggestFix(stderr string, err error) registry.State {
if strings.Contains(stderr, "permission denied") && runtime.GOOS == "linux" {
return registry.State{Error: err, Installed: true, Healthy: false, Fix: "Add your user to the 'docker' group: 'sudo usermod -aG docker $USER && newgrp docker'", Doc: "https://docs.docker.com/engine/install/linux-postinstall/"}
return registry.State{Error: err, Installed: true, Running: true, Healthy: false, Fix: "Add your user to the 'docker' group: 'sudo usermod -aG docker $USER && newgrp docker'", Doc: "https://docs.docker.com/engine/install/linux-postinstall/"}
}
if strings.Contains(stderr, "/pipe/docker_engine: The system cannot find the file specified.") && runtime.GOOS == "windows" {
return registry.State{Error: err, Installed: true, Healthy: false, Fix: "Start the Docker service. If Docker is already running, you may need to reset Docker to factory settings with: Settings > Reset.", Doc: "https://github.com/docker/for-win/issues/1825#issuecomment-450501157"}
return registry.State{Error: err, Installed: true, Running: false, Healthy: false, Fix: "Start the Docker service. If Docker is already running, you may need to reset Docker to factory settings with: Settings > Reset.", Doc: "https://github.com/docker/for-win/issues/1825#issuecomment-450501157"}
}
if strings.Contains(stderr, "Cannot connect") || strings.Contains(stderr, "refused") || strings.Contains(stderr, "Is the docker daemon running") || strings.Contains(stderr, "docker daemon is not running") {
return registry.State{Error: err, Installed: true, Healthy: false, Fix: "Start the Docker service", Doc: docURL}
return registry.State{Error: err, Installed: true, Running: false, Healthy: false, Fix: "Start the Docker service", Doc: docURL}
}
// We don't have good advice, but at least we can provide a good error message
return registry.State{Error: err, Installed: true, Healthy: false, Doc: docURL}
return registry.State{Error: err, Installed: true, Running: true, Healthy: false, Doc: docURL}
}

View File

@ -96,7 +96,7 @@ func status() registry.State {
cmd := exec.CommandContext(ctx, path, "-v")
out, err := cmd.CombinedOutput()
if err != nil {
return registry.State{Installed: true, Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), out), Fix: "Run 'brew install hyperkit'", Doc: docURL}
return registry.State{Installed: true, Running: false, Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), out), Fix: "Run 'brew install hyperkit'", Doc: docURL}
}
// Split version from v0.YYYYMMDD-HH-xxxxxxx or 0.YYYYMMDD to YYYYMMDD
@ -105,13 +105,13 @@ func status() registry.State {
// If current hyperkit is not newer than minimumVersion, suggest upgrade information
isNew, err := isNewerVersion(currentVersion, specificVersion)
if err != nil {
return registry.State{Installed: true, Healthy: true, Error: fmt.Errorf("hyperkit version check failed:\n%v", err), Doc: docURL}
return registry.State{Installed: true, Running: true, Healthy: true, Error: fmt.Errorf("hyperkit version check failed:\n%v", err), Doc: docURL}
}
if !isNew {
return registry.State{Installed: true, Healthy: true, Error: fmt.Errorf("the installed hyperkit version (0.%s) is older than the minimum recommended version (%s)", currentVersion, minimumVersion), Fix: "Run 'brew upgrade hyperkit'", Doc: docURL}
return registry.State{Installed: true, Running: true, Healthy: true, Error: fmt.Errorf("the installed hyperkit version (0.%s) is older than the minimum recommended version (%s)", currentVersion, minimumVersion), Fix: "Run 'brew upgrade hyperkit'", Doc: docURL}
}
return registry.State{Installed: true, Healthy: true}
return registry.State{Installed: true, Running: true, Healthy: true}
}
// isNewerVersion checks whether current hyperkit is newer than specific version

View File

@ -95,14 +95,14 @@ func status() registry.State {
if err != nil {
errorMessage := fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), out)
fixMessage := "Start PowerShell as an Administrator"
return registry.State{Installed: false, Error: errorMessage, Fix: fixMessage, Doc: docURL}
return registry.State{Installed: false, Running: true, Error: errorMessage, Fix: fixMessage, Doc: docURL}
}
// Get-Wmiobject does not return an error code for false
if strings.TrimSpace(string(out)) != "True" {
errorMessage := fmt.Errorf("%s returned %q", strings.Join(cmd.Args, " "), out)
fixMessage := "Enable Hyper-V: Start PowerShell as Administrator, and run: 'Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All'"
return registry.State{Installed: false, Error: errorMessage, Fix: fixMessage, Doc: docURL}
return registry.State{Installed: false, Running: false, Error: errorMessage, Fix: fixMessage, Doc: docURL}
}
return registry.State{Installed: true, Healthy: true}

View File

@ -117,6 +117,7 @@ func status() registry.State {
if err != nil {
return registry.State{
Installed: true,
Running: true,
Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), strings.TrimSpace(string(out))),
Fix: "Follow your Linux distribution instructions for configuring KVM",
Doc: docURL,
@ -129,6 +130,7 @@ func status() registry.State {
if err != nil {
return registry.State{
Installed: true,
Running: true,
Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), strings.TrimSpace(string(out))),
Fix: "Check that libvirtd is properly installed and that you are a member of the appropriate libvirt group",
Doc: docURL,

View File

@ -54,16 +54,16 @@ func configure(cc config.ClusterConfig, n config.Node) (interface{}, error) {
func status() registry.State {
_, err := exec.LookPath("iptables")
if err != nil {
return registry.State{Error: err, Fix: "iptables must be installed", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/none/"}
return registry.State{Running: true, Error: err, Fix: "iptables must be installed", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/none/"}
}
if _, err := exec.LookPath("docker"); err != nil {
return registry.State{Error: err, Installed: false, Fix: "Install docker", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/none/"}
return registry.State{Running: true, Error: err, Installed: false, Fix: "Install docker", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/none/"}
}
u, err := user.Current()
if err != nil {
return registry.State{Error: err, Healthy: false, Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/none/"}
return registry.State{Running: true, Error: err, Healthy: false, Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/none/"}
}
if u.Uid != "0" {

View File

@ -59,5 +59,5 @@ func status() registry.State {
if err != nil {
return registry.State{Error: err, Fix: "Install docker-machine-driver-parallels", Doc: "https://minikube.sigs.k8s.io/docs/reference/drivers/parallels/"}
}
return registry.State{Installed: true, Healthy: true}
return registry.State{Installed: true, Healthy: true, Running: true}
}

View File

@ -104,7 +104,7 @@ func status() registry.State {
v, err := semver.Make(output)
if err != nil {
return registry.State{Error: err, Installed: true, Healthy: false, Fix: "Cant verify minimum required version for podman . See podman website for installation guide.", Doc: "https://podman.io/getting-started/installation.html"}
return registry.State{Error: err, Installed: true, Running: true, Healthy: false, Fix: "Cant verify minimum required version for podman . See podman website for installation guide.", Doc: "https://podman.io/getting-started/installation.html"}
}
if v.LT(minReqPodmanVer) {
@ -122,7 +122,7 @@ func status() registry.State {
// Basic timeout
if ctx.Err() == context.DeadlineExceeded {
return registry.State{Error: err, Installed: true, Healthy: false, Fix: "Restart the Podman service", Doc: docURL}
return registry.State{Error: err, Installed: true, Running: false, Healthy: false, Fix: "Restart the Podman service", Doc: docURL}
}
username := "$USER"

View File

@ -73,9 +73,10 @@ func status() registry.State {
path, err := exec.LookPath(tryPath)
if err != nil {
return registry.State{
Error: fmt.Errorf("unable to find VBoxManage in $PATH"),
Fix: "Install VirtualBox",
Doc: docURL,
Error: fmt.Errorf("unable to find VBoxManage in $PATH"),
Fix: "Install VirtualBox",
Installed: false,
Doc: docURL,
}
}
@ -89,7 +90,7 @@ func status() registry.State {
// Basic timeout
if ctx.Err() == context.DeadlineExceeded {
glog.Warningf("%q timed out. ", strings.Join(cmd.Args, " "))
return registry.State{Error: err, Installed: true, Healthy: false, Fix: "Restart VirtualBox", Doc: docURL}
return registry.State{Error: err, Installed: true, Running: false, Healthy: false, Fix: "Restart VirtualBox", Doc: docURL}
}
if exitErr, ok := err.(*exec.ExitError); ok {

View File

@ -74,7 +74,8 @@ type StatusChecker func() State
type State struct {
Installed bool
Healthy bool
NeedsImprovement bool // driver is healthy but could be improved
Running bool // it at least appears to be running
NeedsImprovement bool // healthy but could be improved
Error error
Fix string
Doc string

View File

@ -38,6 +38,7 @@ import (
"k8s.io/minikube/pkg/kapi"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/retry"
)
@ -122,7 +123,7 @@ func GetServiceURLs(api libmachine.API, cname string, namespace string, t *templ
return serviceURLs, nil
}
// GetServiceURLsForService returns a SvcUrl object for a service in a namespace. Supports optional formatting.
// GetServiceURLsForService returns a SvcURL object for a service in a namespace. Supports optional formatting.
func GetServiceURLsForService(api libmachine.API, cname string, namespace, service string, t *template.Template) (SvcURL, error) {
host, err := machine.LoadHost(api, cname)
if err != nil {
@ -286,7 +287,7 @@ func WaitForService(api libmachine.API, cname string, namespace string, service
}
if len(serviceURL.URLs) == 0 {
out.T(out.Sad, "service {{.namespace_name}}/{{.service_name}} has no node port", out.V{"namespace_name": namespace, "service_name": service})
out.T(style.Sad, "service {{.namespace_name}}/{{.service_name}} has no node port", out.V{"namespace_name": namespace, "service_name": service})
return urlList, nil
}

Some files were not shown because too many files have changed in this diff Show More