提交 2c5e8fda 编写于 作者: T Thomas Stromberg

Add detailed JSON & status codes for all error paths

上级 a8b40a70
......@@ -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)
}
},
}
......
......@@ -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)
}
},
}
......
......@@ -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)
}
}
},
}
......
......@@ -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`")
}
}
......
......@@ -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:
......
......@@ -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})
},
}
......
......@@ -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})
},
}
......
......@@ -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)
}
}
}
......
......@@ -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})
......
......@@ -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)
}
}
......
......@@ -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)
}
},
}
......
......@@ -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)
}
},
}
......
......@@ -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
......
......@@ -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})
}
}
......
......@@ -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)
}
}
}
......
......@@ -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)
}
},
}
......
......@@ -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})
},
}
......
......@@ -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)
}
},
}
......
......@@ -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")
}
......
......@@ -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]")
},
}
......@@ -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})
},
}
......
......@@ -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})
},
}
......
......@@ -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()
......
......@@ -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})
},
}
......
......@@ -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})
},
}
......
......@@ -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, ", ")})
}
}
......
......@@ -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)
}
},
}
......
......@@ -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.
......
......@@ -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)
}
}
}
......@@ -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
......
......@@ -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)
}
},
}
......
此差异已折叠。
......@@ -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")
......
......@@ -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))
......
......@@ -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
......
......@@ -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
},
......
......@@ -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, ", ")})
}
},
}
......
......@@ -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())
......
......@@ -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})
}
},
}
......@@ -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'")
}
},
}
......
......@@ -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)
......
......@@ -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)
......
......@@ -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
}
......@@ -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]})
}
}
......
......@@ -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)
}
......@@ -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("")
......
......@@ -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
}
}
......
......@@ -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.
......
......@@ -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
......
......@@ -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
......
......@@ -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 {
......
......@@ -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")
......
......@@ -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)
}
......@@ -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 {
......
......@@ -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
......
......@@ -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...)
......
......@@ -20,65 +20,49 @@ 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)
}
// Message outputs a templated fatal error message and exits with the supplied error code.
func Message(r reason.Kind, format string, args ...out.V) {
if r.ID == "" {
glog.Errorf("supplied reason has no ID: %+v", r)
}
// 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)
}
if r.Style == style.None {
r.Style = style.Failure
}
// 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 r.ExitCode == 0 {
r.ExitCode = reason.ExProgramError
}
if p != nil {
if out.JSON {
p.DisplayJSON(Config)
os.Exit(Config)
}
WithProblem(msg, err, p)
os.Exit(Config)
if len(args) == 0 {
args = append(args, out.V{})
}
out.DisplayError(msg, err)
os.Exit(Software)
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)
}
// 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...)
}
// 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")
// Error outputs an error and exits.
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())
}
......@@ -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())
}
}
......
......@@ -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
}
}
......@@ -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())
}
......@@ -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)
}
......
......@@ -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
......
......@@ -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) {
......
......@@ -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! ({{.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.
......
......@@ -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)
......
......@@ -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)
}
......@@ -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")
}
}
......@@ -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")
}
......
......@@ -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)
}
}
......@@ -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
......
......@@ -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,7 +114,7 @@ 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
}
......@@ -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})
}
}
......
......@@ -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: ")
}
......
......@@ -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.
......@@ -243,12 +237,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)
}
}
}
......@@ -261,9 +255,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...)
}
/*
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) > 0 {
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("")
}
/*
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)
}
})
}
}
/*
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...)
}
......@@ -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)
......
......@@ -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
......
......@@ -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
}
......
......@@ -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"
......
......@@ -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
......
此差异已折叠。
/*
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
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -5,7 +5,6 @@ description: >
---
## minikube addons
Enable or disable a minikube addon
......
......@@ -5,7 +5,6 @@ description: >
---
## minikube cache
Add, delete, or push a local image into minikube
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册