未验证 提交 ec5107ea 编写于 作者: T Thomas Strömberg 提交者: GitHub

Merge pull request #3638 from tstromberg/console

Initial implementation of 'console' package for stylized & localized console output 😂
......@@ -11,6 +11,14 @@
pruneopts = "NUT"
revision = "d6e3b3328b783f23731bc4d058875b0371ff8109"
[[projects]]
digest = "1:f314424048778dd9cd7bb5c7807a8396b0c6b2a133935f15ad350dcd36c27286"
name = "github.com/Parallels/docker-machine-parallels"
packages = ["."]
pruneopts = "NUT"
revision = "f23cb0b3d3de98df376bbfed9d97bc1f355df864"
version = "v1.3.0"
[[projects]]
digest = "1:aba270497eb2d49f5cba6f4162d524b9a1195a24cbce8be20bf56a0051f47deb"
name = "github.com/blang/semver"
......@@ -249,6 +257,14 @@
pruneopts = "NUT"
revision = "8c5f0ad9360406a3807ce7de6bc73269a91a6e51"
[[projects]]
digest = "1:c7d9de42b661ba85788f5f631cbac165795a2ff7dc1c59a4241d6228b129c3e4"
name = "github.com/hashicorp/go-version"
packages = ["."]
pruneopts = "NUT"
revision = "d40cf49b3a77bba84a7afdbd7f1dc295d114efb1"
version = "v1.1.0"
[[projects]]
digest = "1:475b179287e8afdcd352014b2c2500e67decdf63e66125e2129286873453e1cd"
name = "github.com/hashicorp/golang-lru"
......@@ -360,6 +376,14 @@
pruneopts = "NUT"
revision = "61b492c03cf472e0c6419be5899b8e0dc28b1b88"
[[projects]]
digest = "1:bffa444ca07c69c599ae5876bc18b25bfd5fa85b297ca10a25594d284a7e9c5d"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "NUT"
revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
version = "v0.0.4"
[[projects]]
digest = "1:f2d263faf8641cd0a31bba2d034eb2f9ab9f3c3348b06ac9ad54b9a4ea692dee"
name = "github.com/mattn/go-runewidth"
......@@ -660,12 +684,28 @@
revision = "95c6576299259db960f6c5b9b69ea52422860fce"
[[projects]]
digest = "1:97337ef8cb438f9e3a99ea91a300e916ed9a96fbf3ad50f9a020d30ea9f8692f"
branch = "master"
digest = "1:11d290de3457882172ce8d9ffe930999cfb62929b58e486e1ff1b6adcf3c52bc"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"feature/plural",
"internal",
"internal/catmsg",
"internal/colltab",
"internal/format",
"internal/gen",
"internal/language",
"internal/language/compact",
"internal/number",
"internal/stringset",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"message",
"message/catalog",
"secure/bidirule",
"transform",
"unicode/bidi",
......@@ -674,7 +714,7 @@
"unicode/rangetable",
]
pruneopts = "NUT"
revision = "b19bf474d317b857955b12035d2c5acb57ce8b01"
revision = "e6919f6577db79269a6443b9dc46d18f2238fb5d"
[[projects]]
digest = "1:d37b0ef2944431fe9e8ef35c6fffc8990d9e2ca300588df94a6890f3649ae365"
......@@ -918,6 +958,7 @@
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/Parallels/docker-machine-parallels",
"github.com/blang/semver",
"github.com/docker/go-units",
"github.com/docker/machine/drivers/hyperv",
......@@ -957,6 +998,7 @@
"github.com/johanneswuerbach/nfsexports",
"github.com/libvirt/libvirt-go",
"github.com/machine-drivers/docker-machine-driver-vmware/pkg/drivers/vmware/config",
"github.com/mattn/go-isatty",
"github.com/mitchellh/go-ps",
"github.com/moby/hyperkit/go",
"github.com/olekukonko/tablewriter",
......@@ -978,6 +1020,8 @@
"golang.org/x/sync/errgroup",
"golang.org/x/sync/syncmap",
"golang.org/x/sys/windows/registry",
"golang.org/x/text/language",
"golang.org/x/text/message",
"k8s.io/api/apps/v1",
"k8s.io/api/core/v1",
"k8s.io/api/rbac/v1beta1",
......
......@@ -114,3 +114,7 @@
[[constraint]]
name = "github.com/google/go-cmp"
version = "0.2.0"
[[constraint]]
name = "golang.org/x/text"
branch = "master"
......@@ -17,12 +17,12 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
cmdConfig "k8s.io/minikube/cmd/minikube/cmd/config"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
)
......@@ -42,12 +42,12 @@ 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 {
fmt.Fprintf(os.Stderr, "Error caching and loading images: %v\n", err)
console.Fatal("Failed to cache and load images: %v", err)
os.Exit(1)
}
// Add images to config file
if err := cmdConfig.AddToConfigMap(constants.Cache, args); err != nil {
fmt.Fprintf(os.Stderr, "Error adding cached images to config file: %v\n", err)
console.Fatal("Failed to update config: %v", err)
os.Exit(1)
}
},
......@@ -61,12 +61,12 @@ var deleteCacheCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
// Delete images from config file
if err := cmdConfig.DeleteFromConfigMap(constants.Cache, args); err != nil {
fmt.Fprintf(os.Stderr, "Error deleting images from config file: %v\n", err)
console.Fatal("Failed to delete images from config: %v", err)
os.Exit(1)
}
// Delete images from cache/images directory
if err := machine.DeleteFromImageCacheDir(args); err != nil {
fmt.Fprintf(os.Stderr, "Error deleting images: %v\n", err)
console.Fatal("Failed to delete images: %v", err)
os.Exit(1)
}
},
......
......@@ -17,12 +17,12 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"text/template"
"github.com/spf13/cobra"
cmdConfig "k8s.io/minikube/cmd/minikube/cmd/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
)
......@@ -38,14 +38,13 @@ var listCacheCmd = &cobra.Command{
Short: "List all available images from the local cache.",
Long: "List all available images from the local cache.",
Run: func(cmd *cobra.Command, args []string) {
// list images from config file
images, err := cmdConfig.ListConfigMap(constants.Cache)
if err != nil {
fmt.Fprintf(os.Stderr, "Error listing image entries from config: %v\n", err)
console.Fatal("Failed to get image map: %v", err)
os.Exit(1)
}
if err := cacheList(images); err != nil {
fmt.Fprintf(os.Stderr, "Error listing images: %v\n", err)
console.Fatal("Failed to list cached images: %v", err)
os.Exit(1)
}
},
......@@ -62,13 +61,13 @@ func cacheList(images []string) error {
for _, image := range images {
tmpl, err := template.New("list").Parse(cacheListFormat)
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating list template: %v\n", err)
console.Fatal("Unable to parse template: %v", err)
os.Exit(1)
}
listTmplt := CacheListTemplate{image}
err = tmpl.Execute(os.Stdout, listTmplt)
if err != nil {
fmt.Fprintf(os.Stderr, "Error executing list template: %v\n", err)
console.Fatal("Unable to process template: %v", err)
os.Exit(1)
}
}
......
......@@ -18,13 +18,13 @@ package cmd
import (
"bytes"
"fmt"
"io"
"os"
"github.com/pkg/errors"
"github.com/spf13/cobra"
cmdutil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/console"
)
const longDescription = `
......@@ -70,11 +70,11 @@ var completionCmd = &cobra.Command{
Long: longDescription,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
fmt.Println("Usage: minikube completion SHELL")
console.ErrStyle("usage", "Usage: minikube completion SHELL")
os.Exit(1)
}
if args[0] != "bash" && args[0] != "zsh" {
fmt.Println("Only bash and zsh are supported for minikube completion")
console.Fatal("Sorry, completion support is not yet implemented for %q", args[0])
os.Exit(1)
} else if args[0] == "bash" {
err := GenerateBashCompletion(os.Stdout, cmd.Parent())
......
......@@ -17,7 +17,6 @@ limitations under the License.
package config
import (
"fmt"
"os"
"sort"
"text/template"
......@@ -25,6 +24,7 @@ import (
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
)
......@@ -41,12 +41,12 @@ 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 {
fmt.Fprintln(os.Stderr, "usage: minikube addons list")
console.ErrStyle("usage", "usage: minikube addons list")
os.Exit(1)
}
err := addonList()
if err != nil {
fmt.Fprintln(os.Stderr, err)
console.Fatal("addon list failed: %v", err)
os.Exit(1)
}
},
......
......@@ -17,13 +17,13 @@ limitations under the License.
package config
import (
"fmt"
"os"
"text/template"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
)
......@@ -41,7 +41,7 @@ var configViewCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
err := configView()
if err != nil {
fmt.Fprintln(os.Stderr, err)
console.Fatal("config view failed: %v", err)
os.Exit(1)
}
},
......
......@@ -17,11 +17,11 @@ limitations under the License.
package config
import (
"fmt"
"io/ioutil"
"os"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/service"
)
......@@ -31,7 +31,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 {
fmt.Fprintln(os.Stderr, "usage: minikube addons configure ADDON_NAME")
console.ErrStyle("usage", "usage: minikube addons configure ADDON_NAME")
os.Exit(1)
}
......@@ -78,7 +78,7 @@ var addonsConfigureCmd = &cobra.Command{
dat, err := ioutil.ReadFile(gcrPath)
if err != nil {
fmt.Println("Could not read file for application_default_credentials.json")
console.Failure("Error reading %s: %v", gcrPath, err)
} else {
gcrApplicationDefaultCredentials = string(dat)
}
......@@ -110,7 +110,7 @@ var addonsConfigureCmd = &cobra.Command{
})
if err != nil {
fmt.Println("ERROR creating `registry-creds-ecr` secret")
console.Failure("ERROR creating `registry-creds-ecr` secret: %v", err)
}
// Create GCR Secret
......@@ -128,7 +128,7 @@ var addonsConfigureCmd = &cobra.Command{
})
if err != nil {
fmt.Println("ERROR creating `registry-creds-gcr` secret")
console.Failure("ERROR creating `registry-creds-gcr` secret: %v", err)
}
// Create Docker Secret
......@@ -147,16 +147,14 @@ var addonsConfigureCmd = &cobra.Command{
})
if err != nil {
fmt.Println("ERROR creating `registry-creds-dpr` secret")
console.Warning("ERROR creating `registry-creds-dpr` secret")
}
break
default:
fmt.Fprintln(os.Stdout, fmt.Sprintf("%s has no available configuration options", addon))
console.Failure("%s has no available configuration options", addon)
return
}
fmt.Fprintln(os.Stdout, fmt.Sprintf("%s was successfully configured", addon))
console.Success("%s was successfully configured", addon)
},
}
......
......@@ -17,10 +17,10 @@ limitations under the License.
package config
import (
"fmt"
"os"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/console"
)
var addonsDisableCmd = &cobra.Command{
......@@ -29,17 +29,17 @@ 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 {
fmt.Fprintln(os.Stderr, "usage: minikube addons disable ADDON_NAME")
console.ErrStyle("usage", "usage: minikube addons disable ADDON_NAME")
os.Exit(1)
}
addon := args[0]
err := Set(addon, "false")
if err != nil {
fmt.Fprintln(os.Stdout, err)
console.Fatal("disable failed: %v", err)
os.Exit(1)
}
fmt.Fprintln(os.Stdout, fmt.Sprintf("%s was successfully disabled", addon))
console.Success("%s was successfully disabled", addon)
},
}
......
......@@ -17,10 +17,10 @@ limitations under the License.
package config
import (
"fmt"
"os"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/console"
)
var addonsEnableCmd = &cobra.Command{
......@@ -29,16 +29,16 @@ 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 {
fmt.Fprintln(os.Stderr, "usage: minikube addons enable ADDON_NAME")
console.ErrStyle("usage", "usage: minikube addons enable ADDON_NAME")
os.Exit(1)
}
addon := args[0]
err := Set(addon, "true")
if err != nil {
fmt.Fprintln(os.Stdout, err)
console.Fatal("enable failed: %v", err)
} else {
fmt.Fprintln(os.Stdout, fmt.Sprintf("%s was successfully enabled", addon))
console.Success("%s was successfully enabled", addon)
}
},
}
......
......@@ -19,10 +19,10 @@ package config
import (
"errors"
"fmt"
"os"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
)
var configGetCmd = &cobra.Command{
......@@ -44,7 +44,7 @@ var configGetCmd = &cobra.Command{
return fmt.Errorf("no value for key '%s'", args[0])
}
fmt.Fprintln(os.Stdout, val)
console.OutLn(val)
return nil
},
}
......
......@@ -17,13 +17,13 @@ limitations under the License.
package config
import (
"fmt"
"os"
"text/template"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/service"
......@@ -47,21 +47,21 @@ var addonsOpenCmd = &cobra.Command{
PreRun: func(cmd *cobra.Command, args []string) {
t, err := template.New("addonsURL").Parse(addonsURLFormat)
if err != nil {
fmt.Fprintln(os.Stderr, "The value passed to --format is invalid:\n\n", err)
console.Fatal("The value passed to --format is invalid: %s", err)
os.Exit(1)
}
addonsURLTemplate = t
},
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
fmt.Fprintln(os.Stderr, "usage: minikube addons open ADDON_NAME")
console.ErrStyle("usage", "usage: minikube addons open ADDON_NAME")
os.Exit(1)
}
addonName := args[0]
//TODO(r2d4): config should not reference API, pull this out
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
......@@ -69,20 +69,20 @@ var addonsOpenCmd = &cobra.Command{
cluster.EnsureMinikubeRunningOrExit(api, 1)
addon, ok := assets.Addons[addonName] // validate addon input
if !ok {
fmt.Fprintln(os.Stderr, fmt.Sprintf(`addon '%s' is not a valid addon packaged with minikube.
console.Fatal(`addon '%s' is not a valid addon packaged with minikube.
To see the list of available addons run:
minikube addons list`, addonName))
minikube addons list`, addonName)
os.Exit(1)
}
ok, err = addon.IsEnabled()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
console.Fatal("IsEnabled error: %v", err)
os.Exit(1)
}
if !ok {
fmt.Fprintln(os.Stderr, fmt.Sprintf(`addon '%s' is currently not enabled.
console.ErrStyle("conflict", `addon '%s' is currently not enabled.
To enable this addon run:
minikube addons enable %s`, addonName, addonName))
minikube addons enable %s`, addonName, addonName)
os.Exit(1)
}
......@@ -91,15 +91,15 @@ minikube addons enable %s`, addonName, addonName))
serviceList, err := service.GetServiceListByLabel(namespace, key, addonName)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting service with namespace: %s and labels %s:%s: %v\n", namespace, key, addonName, err)
console.Fatal("Error getting service with namespace: %s and labels %s:%s: %v", namespace, key, addonName, err)
os.Exit(1)
}
if len(serviceList.Items) == 0 {
fmt.Fprintf(os.Stdout, `
console.Fatal(`
This addon does not have an endpoint defined for the 'addons open' command
You can add one by annotating a service with the label %s:%s
`, key, addonName)
os.Exit(0)
os.Exit(1)
}
for i := range serviceList.Items {
svc := serviceList.Items[i].ObjectMeta.Name
......
......@@ -17,11 +17,11 @@ limitations under the License.
package config
import (
"fmt"
"os"
"github.com/spf13/cobra"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
)
var ProfileCmd = &cobra.Command{
......@@ -30,7 +30,7 @@ var ProfileCmd = &cobra.Command{
Long: "profile sets the current minikube profile. This is used to run and manage multiple minikube instance. You can return to the default minikube profile by running `minikube profile default`",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
fmt.Fprintln(os.Stderr, "usage: minikube profile MINIKUBE_PROFILE_NAME")
console.ErrStyle("usage", "usage: minikube profile MINIKUBE_PROFILE_NAME")
os.Exit(1)
}
......@@ -40,9 +40,10 @@ var ProfileCmd = &cobra.Command{
}
err := Set(pkgConfig.MachineProfile, profile)
if err != nil {
fmt.Fprintln(os.Stdout, err)
console.Fatal("set failed: %v", err)
os.Exit(1)
} else {
fmt.Fprintln(os.Stdout, fmt.Sprintf("minikube profile was successfully set to %s", profile))
console.Success("minikube profile was successfully set to %s", profile)
}
},
}
......@@ -18,13 +18,13 @@ package config
import (
"bufio"
"fmt"
"io"
"log"
"os"
"strings"
"golang.org/x/crypto/ssh/terminal"
"k8s.io/minikube/pkg/minikube/console"
)
// AskForYesNoConfirmation asks the user for confirmation. A user must type in "yes" or "no" and
......@@ -35,7 +35,7 @@ func AskForYesNoConfirmation(s string, posResponses, negResponses []string) bool
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("%s [y/n]: ", s)
console.Out("%s [y/n]: ", s)
response, err := reader.ReadString('\n')
if err != nil {
......@@ -49,7 +49,7 @@ func AskForYesNoConfirmation(s string, posResponses, negResponses []string) bool
} else if containsString(negResponses, response) {
return false
} else {
fmt.Println("Please type yes or no:")
console.Err("Please type yes or no:")
return AskForYesNoConfirmation(s, posResponses, negResponses)
}
}
......@@ -64,7 +64,7 @@ func AskForStaticValue(s string) string {
// Can't have zero length
if len(response) == 0 {
fmt.Println("--Error, please enter a value:")
console.Err("--Error, please enter a value:")
continue
}
return response
......@@ -79,7 +79,7 @@ func AskForStaticValueOptional(s string) string {
}
func getStaticValue(reader *bufio.Reader, s string) string {
fmt.Printf("%s", s)
console.Out("%s", s)
response, err := reader.ReadString('\n')
if err != nil {
......@@ -111,7 +111,7 @@ func concealableAskForStaticValue(readWriter io.ReadWriter, promptString string,
}
response = strings.TrimSpace(response)
if len(response) == 0 {
fmt.Println("--Error, please enter a value:")
console.Warning("Please enter a value:")
return concealableAskForStaticValue(readWriter, promptString, hidden)
}
return response, nil
......
......@@ -17,12 +17,11 @@ limitations under the License.
package config
import (
"fmt"
"os"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
"github.com/spf13/cobra"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
)
var configSetCmd = &cobra.Command{
......@@ -32,12 +31,12 @@ 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 {
fmt.Fprintln(os.Stderr, "usage: minikube config set PROPERTY_NAME PROPERTY_VALUE")
console.ErrStyle("usage", "usage: minikube config set PROPERTY_NAME PROPERTY_VALUE")
os.Exit(1)
}
err := Set(args[0], args[1])
if err != nil {
fmt.Fprintln(os.Stderr, err)
console.Fatal("Set failed: %v", err)
os.Exit(1)
}
},
......
......@@ -17,12 +17,11 @@ limitations under the License.
package config
import (
"fmt"
"os"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
"github.com/spf13/cobra"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
)
var configUnsetCmd = &cobra.Command{
......@@ -31,12 +30,12 @@ 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 {
fmt.Fprintln(os.Stdout, "usage: minikube config unset PROPERTY_NAME")
console.ErrStyle("usage", "usage: minikube config unset PROPERTY_NAME")
os.Exit(1)
}
err := unset(args[0])
if err != nil {
fmt.Fprintln(os.Stdout, err)
console.Fatal("unset failed: %v", err)
}
},
}
......
......@@ -26,6 +26,7 @@ import (
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/storageclass"
......@@ -105,7 +106,7 @@ func EnableOrDisableAddon(name string, val string) error {
//TODO(r2d4): config package should not reference API, pull this out
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
......
......@@ -28,6 +28,7 @@ import (
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/cruntime"
)
......@@ -42,7 +43,7 @@ func IsValidDriver(string, driver string) error {
}
func RequiresRestartMsg(string, string) error {
fmt.Fprintln(os.Stdout, "These changes will take effect upon a minikube delete and then a minikube start")
console.OutStyle("warning", "These changes will take effect upon a minikube delete and then a minikube start")
return nil
}
......
......@@ -33,6 +33,7 @@ import (
configcmd "k8s.io/minikube/cmd/minikube/cmd/config"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/service"
"k8s.io/minikube/pkg/util"
......@@ -60,46 +61,47 @@ var dashboardCmd = &cobra.Command{
}()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
cluster.EnsureMinikubeRunningOrExit(api, 1)
fmt.Fprintln(os.Stderr, "Enabling dashboard ...")
// Send status messages to stderr for folks re-using this output.
console.ErrStyle("enabling", "Enabling dashboard ...")
// Enable the dashboard add-on
err = configcmd.Set("dashboard", "true")
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to enable dashboard: %v\n", err)
console.Fatal("Unable to enable dashboard: %v", err)
os.Exit(1)
}
ns := "kube-system"
svc := "kubernetes-dashboard"
fmt.Fprintln(os.Stderr, "Verifying dashboard health ...")
console.ErrStyle("verifying", "Verifying dashboard health ...")
if err = util.RetryAfter(180, func() error { return service.CheckService(ns, svc) }, 1*time.Second); err != nil {
fmt.Fprintf(os.Stderr, "%s:%s is not running: %v\n", ns, svc, err)
console.Fatal("%s:%s is not running: %v", ns, svc, err)
os.Exit(1)
}
fmt.Fprintln(os.Stderr, "Launching proxy ...")
console.ErrStyle("launch", "Launching proxy ...")
p, hostPort, err := kubectlProxy()
if err != nil {
glog.Fatalf("kubectl proxy: %v", err)
}
url := dashboardURL(hostPort, ns, svc)
fmt.Fprintln(os.Stderr, "Verifying proxy health ...")
console.ErrStyle("verifying", "Verifying proxy health ...")
if err = util.RetryAfter(60, func() error { return checkURL(url) }, 1*time.Second); err != nil {
fmt.Fprintf(os.Stderr, "%s is not responding properly: %v\n", url, err)
console.Fatal("%s is not responding properly: %v", url, err)
os.Exit(1)
}
if dashboardURLMode {
fmt.Fprintln(os.Stdout, url)
console.OutLn(url)
} else {
fmt.Fprintln(os.Stdout, fmt.Sprintf("Opening %s in your default browser...", url))
console.ErrStyle("celebrate", "Opening %s in your default browser...", url)
if err = browser.OpenURL(url); err != nil {
fmt.Fprintf(os.Stderr, fmt.Sprintf("failed to open browser: %v", err))
console.Failure("failed to open browser: %v", err)
}
}
......
......@@ -17,14 +17,16 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"github.com/docker/machine/libmachine/mcnerror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmdUtil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
pkg_config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
)
......@@ -37,32 +39,43 @@ var deleteCmd = &cobra.Command{
associated files.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 {
fmt.Fprintln(os.Stderr, "usage: minikube delete")
console.ErrStyle("usage", "usage: minikube delete")
os.Exit(1)
}
fmt.Println("Deleting local Kubernetes cluster...")
profile := viper.GetString(pkg_config.MachineProfile)
console.OutStyle("deleting-vm", "Deleting %q Kubernetes cluster ...", profile)
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
if err = cluster.DeleteHost(api); err != nil {
fmt.Println("Errors occurred deleting machine: ", err)
os.Exit(1)
switch err := errors.Cause(err).(type) {
case mcnerror.ErrHostDoesNotExist:
console.OutStyle("meh", "%q VM does not exist", profile)
default:
console.Fatal("Failed to delete VM: %v", err)
os.Exit(1)
}
} else {
console.OutStyle("crushed", "VM deleted.")
}
fmt.Println("Machine deleted.")
if err := cmdUtil.KillMountProcess(); err != nil {
fmt.Println("Errors occurred deleting mount process: ", err)
console.Fatal("Failed to kill mount process: %v", err)
}
if err := os.Remove(constants.GetProfileFile(viper.GetString(pkg_config.MachineProfile))); err != nil {
fmt.Println("Error deleting machine profile config")
if os.IsNotExist(err) {
console.OutStyle("meh", "%q profile does not exist", profile)
os.Exit(0)
}
console.Fatal("Failed to remove profile: %v", err)
os.Exit(1)
}
console.Success("Removed %q profile!", profile)
},
}
......
......@@ -34,6 +34,7 @@ import (
cmdUtil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
)
......@@ -308,25 +309,24 @@ var dockerEnvCmd = &cobra.Command{
Short: "Sets up docker env variables; similar to '$(docker-machine env)'",
Long: `Sets up docker env variables; similar to '$(docker-machine env)'.`,
Run: func(cmd *cobra.Command, args []string) {
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
host, err := cluster.CheckIfHostExistsAndLoad(api, config.GetMachineName())
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting host: %v\n", err)
console.Fatal("Error getting host: %v", err)
os.Exit(1)
}
if host.Driver.DriverName() == "none" {
fmt.Println(`'none' driver does not support 'minikube docker-env' command`)
os.Exit(0)
console.Fatal(`'none' driver does not support 'minikube docker-env' command`)
os.Exit(1)
}
docker, err := GetDockerActive(host)
if !docker {
fmt.Println(`# The docker service is currently not active`)
console.OutLn(`# The docker service is currently not active`)
os.Exit(1)
}
......
......@@ -17,12 +17,11 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/machine"
)
......@@ -34,21 +33,21 @@ var ipCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
host, err := api.Load(config.GetMachineName())
if err != nil {
glog.Errorln("Error getting IP: ", err)
console.Fatal("Error getting host: %v", err)
os.Exit(1)
}
ip, err := host.Driver.GetIP()
if err != nil {
glog.Errorln("Error getting IP: ", err)
console.Fatal("Error getting IP: %v", err)
os.Exit(1)
}
fmt.Println(ip)
console.OutLn(ip)
},
}
......
......@@ -17,8 +17,6 @@ limitations under the License.
package cmd
import (
"fmt"
"log"
"os"
"github.com/golang/glog"
......@@ -26,6 +24,7 @@ import (
"github.com/spf13/viper"
cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config"
cmdUtil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/machine"
)
......@@ -41,7 +40,7 @@ var logsCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
......@@ -52,7 +51,7 @@ var logsCmd = &cobra.Command{
err = clusterBootstrapper.GetClusterLogsTo(follow, os.Stdout)
if err != nil {
log.Println("Error getting machine logs:", err)
console.Fatal("Error getting machine logs:", err)
cmdUtil.MaybeReportErrorAndExit(err)
}
},
......
......@@ -17,18 +17,17 @@ limitations under the License.
package cmd
import (
"fmt"
"net"
"os"
"sync"
"strings"
"sync"
"github.com/golang/glog"
"github.com/spf13/cobra"
cmdUtil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/third_party/go9p/ufs"
......@@ -49,42 +48,36 @@ var mountCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
if isKill {
if err := cmdUtil.KillMountProcess(); err != nil {
fmt.Println("Errors occurred deleting mount process: ", err)
console.Fatal("Error killing mount process: ", err)
os.Exit(1)
}
os.Exit(0)
}
if len(args) != 1 {
errText := `Please specify the directory to be mounted:
minikube mount HOST_MOUNT_DIRECTORY:VM_MOUNT_DIRECTORY(ex:"/host-home:/vm-home")
`
fmt.Fprintln(os.Stderr, errText)
console.ErrStyle("usage", `Please specify the directory to be mounted:
minikube mount HOST_MOUNT_DIRECTORY:VM_MOUNT_DIRECTORY(ex:"/host-home:/vm-home")`)
os.Exit(1)
}
mountString := args[0]
idx := strings.LastIndex(mountString, ":")
if idx == -1 { // no ":" was present
errText := `Mount directory must be in the form:
HOST_MOUNT_DIRECTORY:VM_MOUNT_DIRECTORY`
fmt.Fprintln(os.Stderr, errText)
console.ErrStyle("usage", `Mount directory must be in the form:
HOST_MOUNT_DIRECTORY:VM_MOUNT_DIRECTORY`)
os.Exit(1)
}
hostPath := mountString[:idx]
vmPath := mountString[idx+1:]
if _, err := os.Stat(hostPath); err != nil {
if os.IsNotExist(err) {
errText := fmt.Sprintf("Cannot find directory %s for mount", hostPath)
fmt.Fprintln(os.Stderr, errText)
console.Fatal("Cannot find directory %s for mount", hostPath)
} else {
errText := fmt.Sprintf("Error accessing directory %s for mount", hostPath)
fmt.Fprintln(os.Stderr, errText)
console.Fatal("Error accessing directory %s for mount", hostPath)
}
os.Exit(1)
}
if len(vmPath) == 0 || !strings.HasPrefix(vmPath, "/") {
errText := fmt.Sprintf("The :VM_MOUNT_DIRECTORY must be an absolute path")
fmt.Fprintln(os.Stderr, errText)
console.ErrStyle("usage", "The :VM_MOUNT_DIRECTORY must be an absolute path")
os.Exit(1)
}
var debugVal int
......@@ -93,17 +86,17 @@ var mountCmd = &cobra.Command{
}
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
host, err := api.Load(config.GetMachineName())
if err != nil {
glog.Errorln("Error loading api: ", err)
console.Fatal("Error loading api: %v", err)
os.Exit(1)
}
if host.Driver.DriverName() == "none" {
fmt.Println(`'none' driver does not support 'minikube mount' command`)
console.Fatal(`'none' driver does not support 'minikube mount' command`)
os.Exit(0)
}
var ip net.IP
......@@ -120,11 +113,11 @@ var mountCmd = &cobra.Command{
os.Exit(1)
}
}
fmt.Printf("Mounting %s into %s on the minikube VM\n", hostPath, vmPath)
fmt.Println("This daemon process needs to stay alive for the mount to still be accessible...")
console.OutStyle("mounting", "Mounting %s into %s on the minikube VM", hostPath, vmPath)
console.OutStyle("notice", "This daemon process needs to stay alive for the mount to be accessible ...")
port, err := cmdUtil.GetPort()
if err != nil {
glog.Errorln("Error finding port for mount: ", err)
console.Fatal("Error finding port for mount: %v", err)
os.Exit(1)
}
var wg sync.WaitGroup
......@@ -135,7 +128,7 @@ var mountCmd = &cobra.Command{
}()
err = cluster.MountHost(api, ip, vmPath, port, mountVersion, uid, gid, msize)
if err != nil {
fmt.Println(err.Error())
console.Fatal(err.Error())
os.Exit(1)
}
wg.Wait()
......
......@@ -17,12 +17,12 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"text/template"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/service"
......@@ -48,7 +48,7 @@ var serviceCmd = &cobra.Command{
PersistentPreRun: func(cmd *cobra.Command, args []string) {
t, err := template.New("serviceURL").Parse(serviceURLFormat)
if err != nil {
fmt.Fprintln(os.Stderr, "The value passed to --format is invalid:\n\n", err)
console.Fatal("The value passed to --format is invalid: %v", err)
os.Exit(1)
}
serviceURLTemplate = t
......@@ -57,15 +57,14 @@ var serviceCmd = &cobra.Command{
},
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 || len(args) > 1 {
errText := "Please specify a service name."
fmt.Fprintln(os.Stderr, errText)
console.Fatal("No service name was specified.")
os.Exit(1)
}
svc := args[0]
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
......@@ -74,7 +73,7 @@ var serviceCmd = &cobra.Command{
err = service.WaitAndMaybeOpenService(api, namespace, svc,
serviceURLTemplate, serviceURLMode, https, wait, interval)
if err != nil {
fmt.Fprintf(os.Stderr, "Error opening service: %v\n", err)
console.Fatal("Error opening service: %v", err)
os.Exit(1)
}
},
......
......@@ -17,14 +17,13 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"strings"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/service"
)
......@@ -39,14 +38,14 @@ var serviceListCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
serviceURLs, err := service.GetServiceURLs(api, serviceListNamespace, serviceURLTemplate)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.")
console.Fatal("Failed to get service URL: %v", err)
console.ErrStyle("notice", "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.")
os.Exit(1)
}
......
......@@ -17,12 +17,11 @@ limitations under the License.
package cmd
import (
"fmt"
"path/filepath"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
)
......@@ -32,7 +31,7 @@ var sshKeyCmd = &cobra.Command{
Short: "Retrieve the ssh identity key path of the specified cluster",
Long: "Retrieve the ssh identity key path of the specified cluster.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(filepath.Join(constants.GetMinipath(), "machines", config.GetMachineName(), "id_rsa"))
console.OutLn(filepath.Join(constants.GetMinipath(), "machines", config.GetMachineName(), "id_rsa"))
},
}
......
......@@ -17,14 +17,12 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"github.com/golang/glog"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/machine"
)
......@@ -36,22 +34,22 @@ var sshCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
host, err := cluster.CheckIfHostExistsAndLoad(api, config.GetMachineName())
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting host: %v\n", err)
console.Fatal("Error getting host: %v", err)
os.Exit(1)
}
if host.Driver.DriverName() == "none" {
fmt.Println(`'none' driver does not support 'minikube ssh' command`)
os.Exit(0)
console.Fatal(`'none' driver does not support 'minikube ssh' command`)
os.Exit(1)
}
err = cluster.CreateSSHShell(api, args)
if err != nil {
glog.Errorln(errors.Wrap(err, "Error attempting to ssh/run-ssh-command"))
console.Fatal("Error creating SSH shell: %v", err)
os.Exit(1)
}
},
......
此差异已折叠。
......@@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"text/template"
......@@ -29,6 +28,7 @@ import (
cmdUtil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/util/kubeconfig"
......@@ -62,7 +62,7 @@ var statusCmd = &cobra.Command{
var returnCode = 0
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(internalErrorCode)
}
defer api.Close()
......
......@@ -17,13 +17,17 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"time"
"github.com/docker/machine/libmachine/mcnerror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmdUtil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
pkg_config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/machine"
pkgutil "k8s.io/minikube/pkg/util"
)
......@@ -35,25 +39,39 @@ var stopCmd = &cobra.Command{
Long: `Stops a local kubernetes cluster running in Virtualbox. This command stops the VM
itself, leaving all files intact. The cluster can be started again with the "start" command.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Stopping local Kubernetes cluster...")
profile := viper.GetString(pkg_config.MachineProfile)
console.OutStyle("stopping", "Stopping %q Kubernetes cluster...", profile)
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
nonexistent := false
stop := func() (err error) {
return cluster.StopHost(api)
err = cluster.StopHost(api)
switch err := errors.Cause(err).(type) {
case mcnerror.ErrHostDoesNotExist:
console.OutStyle("meh", "%q VM does not exist, nothing to stop", profile)
nonexistent = true
return nil
default:
return err
}
}
if err := pkgutil.RetryAfter(5, stop, 1*time.Second); err != nil {
fmt.Println("Error stopping machine: ", err)
console.Fatal("Unable to stop VM: %v", err)
cmdUtil.MaybeReportErrorAndExit(err)
}
fmt.Println("Machine stopped.")
if !nonexistent {
console.OutStyle("stopped", "%q stopped.", profile)
}
if err := cmdUtil.KillMountProcess(); err != nil {
fmt.Println("Errors occurred deleting mount process: ", err)
console.Fatal("Unable to kill mount process: %v", err)
}
},
}
......
......@@ -17,11 +17,10 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/notify"
"k8s.io/minikube/pkg/version"
......@@ -39,17 +38,17 @@ var updateCheckCmd = &cobra.Command{
url := constants.GithubMinikubeReleasesURL
r, err := notify.GetAllVersionsFromURL(url)
if err != nil {
fmt.Fprintf(os.Stderr, "Error fetching latest version from internet")
console.Fatal("Unable to fetch latest version info: %v", err)
os.Exit(1)
}
if len(r) < 1 {
fmt.Fprintf(os.Stderr, "Got empty version list from server")
console.Fatal("Update server returned an empty list")
os.Exit(2)
}
fmt.Println("CurrentVersion:", version.GetVersion())
fmt.Println("LatestVersion:", r[0].Name)
console.OutLn("CurrentVersion: %s", version.GetVersion())
console.OutLn("LatestVersion: %s", r[0].Name)
},
}
......
......@@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"fmt"
"os"
"github.com/golang/glog"
......@@ -25,6 +24,7 @@ import (
cmdUtil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
kcfg "k8s.io/minikube/pkg/util/kubeconfig"
......@@ -39,7 +39,7 @@ var updateContextCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
api, err := machine.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting client: %v\n", err)
console.Fatal("Error getting client: %v", err)
os.Exit(1)
}
defer api.Close()
......@@ -49,15 +49,15 @@ var updateContextCmd = &cobra.Command{
glog.Errorln("Error host driver ip status:", err)
cmdUtil.MaybeReportErrorAndExit(err)
}
ok, err := kcfg.UpdateKubeconfigIP(ip, constants.KubeconfigPath, machineName)
updated, err := kcfg.UpdateKubeconfigIP(ip, constants.KubeconfigPath, machineName)
if err != nil {
glog.Errorln("Error kubeconfig status:", err)
cmdUtil.MaybeReportErrorAndExit(err)
}
if ok {
fmt.Println("Reconfigured kubeconfig IP, now pointing at " + ip.String())
if updated {
console.OutStyle("celebrate", "IP has been updated to point at %s", ip)
} else {
fmt.Println("Kubeconfig IP correctly configured, pointing at " + ip.String())
console.OutStyle("meh", "IP was already correctly configured for %s", ip)
}
},
......
......@@ -17,10 +17,8 @@ limitations under the License.
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/version"
)
......@@ -33,8 +31,7 @@ var versionCmd = &cobra.Command{
enableUpdateNotification = false
},
Run: func(command *cobra.Command, args []string) {
fmt.Println("minikube version:", version.GetVersion())
console.OutLn("minikube version: %v", version.GetVersion())
},
}
......
......@@ -22,6 +22,7 @@ import (
"github.com/golang/glog"
"github.com/pkg/profile"
"k8s.io/minikube/cmd/minikube/cmd"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
_ "k8s.io/minikube/pkg/provision"
......@@ -31,12 +32,13 @@ const minikubeEnableProfile = "MINIKUBE_ENABLE_PROFILING"
func main() {
defer glog.Flush()
if os.Getenv(minikubeEnableProfile) == "1" {
defer profile.Start(profile.TraceProfile).Stop()
}
if os.Getenv(constants.IsMinikubeChildProcess) == "" {
machine.StartDriver()
}
console.SetOutFile(os.Stdout)
console.SetErrFile(os.Stderr)
cmd.Execute()
}
......@@ -28,7 +28,7 @@ import (
func main() {
// Glog requires that /tmp exists.
if err := os.MkdirAll("/tmp", 0755); err != nil {
fmt.Printf("Error creating tmpdir: %v\n", err)
fmt.Fprintf(os.Stderr, "Error creating tmpdir: %v\n", err)
os.Exit(1)
}
flag.Parse()
......
......@@ -39,6 +39,7 @@ import (
"golang.org/x/crypto/ssh/terminal"
minikubeConfig "k8s.io/minikube/cmd/minikube/cmd/config"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/version"
)
......@@ -135,7 +136,7 @@ func MaybeReportErrorAndExitWithCode(errToReport error, returnCode int) {
if viper.GetBool(config.WantReportError) {
err = ReportError(errToReport, constants.ReportingURL)
} else if viper.GetBool(config.WantReportErrorPrompt) {
fmt.Println(
console.Err(
`================================================================================
An error has occurred. Would you like to opt in to sending anonymized crash
information to minikube to help prevent future errors?
......@@ -148,7 +149,7 @@ To disable this prompt, run: 'minikube config set WantReportErrorPrompt false'
err = ReportError(errToReport, constants.ReportingURL)
}
} else {
fmt.Println("Bummer, perhaps next time!")
console.ErrStyle("meh", "Bummer, perhaps next time!")
}
}
......@@ -156,13 +157,12 @@ To disable this prompt, run: 'minikube config set WantReportErrorPrompt false'
if err != nil {
glog.Infof("report error failed: %v", err)
}
fmt.Printf("\n\nminikube failed :( exiting with error code %d\n", returnCode)
os.Exit(returnCode)
}
func getInput(input chan string, r io.Reader) {
reader := bufio.NewReader(r)
fmt.Print("Please enter your response [Y/n]: ")
console.OutLn("Please enter your response [Y/n]: ")
response, err := reader.ReadString('\n')
if err != nil {
glog.Errorf(err.Error())
......@@ -184,11 +184,11 @@ func PromptUserForAccept(r io.Reader) bool {
} else if response == "n" || response == "no" {
return false
} else {
fmt.Println("Invalid response, error reporting remains disabled. Must be in form [Y/n]")
console.Warning("Invalid response, error reporting remains disabled. Must be in form [Y/n]")
return false
}
case <-time.After(30 * time.Second):
fmt.Println("Prompt timed out.")
console.Warning("Prompt timed out.")
return false
}
}
......
......@@ -21,6 +21,7 @@ package hyperkit
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/user"
"path"
......@@ -30,14 +31,11 @@ import (
"syscall"
"time"
"github.com/mitchellh/go-ps"
"io/ioutil"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/state"
"github.com/johanneswuerbach/nfsexports"
"github.com/mitchellh/go-ps"
"github.com/moby/hyperkit/go"
"github.com/pkg/errors"
pkgdrivers "k8s.io/minikube/pkg/drivers"
......@@ -283,7 +281,7 @@ func (d *Driver) recoverFromUncleanShutdown() error {
p, err := ps.FindProcess(pid)
if err != nil {
return errors.Wrapf(err, "trying to find process for PID %s", pid)
return errors.Wrapf(err, "trying to find process for PID %d", pid)
}
if p != nil && !strings.Contains(p.Executable(), "hyperkit") {
......
......@@ -39,6 +39,7 @@ import (
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/bootstrapper"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/cruntime"
"k8s.io/minikube/pkg/minikube/machine"
......@@ -186,6 +187,8 @@ func (k *KubeadmBootstrapper) StartCluster(k8s config.KubernetesConfig) error {
}
}
// NOTE: We have not yet asserted that we can access the apiserver. Now would be a great time to do so.
console.OutStyle("permissions", "Configuring cluster permissions ...")
if err := util.RetryAfter(100, elevateKubeSystemPrivileges, time.Millisecond*500); err != nil {
return errors.Wrap(err, "timed out waiting to elevate kube-system RBAC privileges")
}
......@@ -240,6 +243,9 @@ func (k *KubeadmBootstrapper) RestartCluster(k8s config.KubernetesConfig) error
return errors.Wrapf(err, "running cmd: %s", cmd)
}
}
// NOTE: Perhaps now would be a good time to check apiserver health?
console.OutStyle("waiting", "Restarting kube-proxy ...")
if err := restartKubeProxy(k8s); err != nil {
return errors.Wrap(err, "restarting kube-proxy")
}
......@@ -353,10 +359,10 @@ func (k *KubeadmBootstrapper) UpdateCluster(cfg config.KubernetesConfig) error {
}
f, err := assets.NewFileAsset(path, "/usr/bin", bin, "0641")
if err != nil {
return errors.Wrap(err, "making new file asset")
return errors.Wrap(err, "new file asset")
}
if err := k.c.Copy(f); err != nil {
return errors.Wrapf(err, "transferring kubeadm file: %+v", f)
return errors.Wrapf(err, "copy")
}
return nil
})
......@@ -366,15 +372,14 @@ func (k *KubeadmBootstrapper) UpdateCluster(cfg config.KubernetesConfig) error {
}
if err := addAddons(&files); err != nil {
return errors.Wrap(err, "adding addons to copyable files")
return errors.Wrap(err, "adding addons")
}
for _, f := range files {
if err := k.c.Copy(f); err != nil {
return errors.Wrapf(err, "transferring kubeadm file: %+v", f)
return errors.Wrapf(err, "copy")
}
}
err = k.c.Run(`
sudo systemctl daemon-reload &&
sudo systemctl enable kubelet &&
......@@ -482,10 +487,9 @@ func maybeDownloadAndCache(binary, version string) (string, error) {
options.Checksum = constants.GetKubernetesReleaseURLSha1(binary, version)
options.ChecksumHash = crypto.SHA1
glog.Infof("Downloading %s %s", binary, version)
console.OutStyle("file-download", "Downloading %s %s", binary, version)
if err := download.ToFile(url, targetFilepath, options); err != nil {
return "", errors.Wrapf(err, "Error downloading %s %s", binary, version)
}
glog.Infof("Finished Downloading %s %s", binary, version)
return targetFilepath, nil
}
......@@ -38,8 +38,8 @@ import (
"github.com/golang/glog"
"github.com/pkg/errors"
"github.com/spf13/viper"
cfg "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/registry"
"k8s.io/minikube/pkg/util"
......@@ -64,7 +64,7 @@ func init() {
func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error) {
exists, err := api.Exists(cfg.GetMachineName())
if err != nil {
return nil, errors.Wrapf(err, "Error checking if host exists: %s", cfg.GetMachineName())
return nil, errors.Wrapf(err, "machine name: %s", cfg.GetMachineName())
}
if !exists {
glog.Infoln("Machine does not exist... provisioning new machine")
......@@ -80,7 +80,14 @@ func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error)
}
if h.Driver.DriverName() != config.VMDriver {
fmt.Printf("Skipping %s driver, existing host has %s driver.\n", config.VMDriver, h.Driver.DriverName())
console.Out("\n")
console.Warning("Ignoring --vm-driver=%s, as the existing %q VM was created using the %s driver.",
config.VMDriver, cfg.GetMachineName(), h.Driver.DriverName())
console.Warning("To switch drivers, you may create a new VM using `minikube start -p <name> --vm-driver=%s`", config.VMDriver)
console.Warning("Alternatively, you may delete the existing VM using `minikube delete -p %s`", cfg.GetMachineName())
console.Out("\n")
} else if exists && cfg.GetMachineName() == constants.DefaultMachineName {
console.OutStyle("tip", "Tip: To create a new cluster, use 'minikube start -p <new name>' or use 'minikube delete' to delete this one.")
}
s, err := h.Driver.GetState()
......@@ -91,10 +98,10 @@ func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error)
if s != state.Running {
if err := h.Driver.Start(); err != nil {
return nil, errors.Wrap(err, "Error starting stopped host")
return nil, errors.Wrap(err, "start")
}
if err := api.Save(h); err != nil {
return nil, errors.Wrap(err, "Error saving started host")
return nil, errors.Wrap(err, "save")
}
}
......@@ -122,7 +129,7 @@ func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error)
func StopHost(api libmachine.API) error {
host, err := api.Load(cfg.GetMachineName())
if err != nil {
return errors.Wrapf(err, "Load: %s", cfg.GetMachineName())
return errors.Wrapf(err, "load")
}
if err := host.Stop(); err != nil {
alreadyInStateError, ok := err.(mcnerror.ErrHostAlreadyInState)
......@@ -138,19 +145,22 @@ func StopHost(api libmachine.API) error {
func DeleteHost(api libmachine.API) error {
host, err := api.Load(cfg.GetMachineName())
if err != nil {
return errors.Wrapf(err, "Load: %s", cfg.GetMachineName())
return errors.Wrap(err, "load")
}
if err := host.Driver.Remove(); err != nil {
return errors.Wrap(err, "host remove")
}
if err := api.Remove(cfg.GetMachineName()); err != nil {
return errors.Wrap(err, "api remove")
}
m := util.MultiError{}
m.Collect(host.Driver.Remove())
m.Collect(api.Remove(cfg.GetMachineName()))
return m.ToError()
return nil
}
// GetHostStatus gets the status of the host VM.
func GetHostStatus(api libmachine.API) (string, error) {
exists, err := api.Exists(cfg.GetMachineName())
if err != nil {
return "", errors.Wrapf(err, "Error checking that api exists for: %s", cfg.GetMachineName())
return "", errors.Wrapf(err, "%s exists", cfg.GetMachineName())
}
if !exists {
return state.None.String(), nil
......@@ -158,12 +168,12 @@ func GetHostStatus(api libmachine.API) (string, error) {
host, err := api.Load(cfg.GetMachineName())
if err != nil {
return "", errors.Wrapf(err, "Error loading api for: %s", cfg.GetMachineName())
return "", errors.Wrapf(err, "load")
}
s, err := host.Driver.GetState()
if err != nil {
return "", errors.Wrap(err, "Error getting host state")
return "", errors.Wrap(err, "state")
}
return s.String(), nil
}
......@@ -177,11 +187,11 @@ func GetHostDriverIP(api libmachine.API, machineName string) (net.IP, error) {
ipStr, err := host.Driver.GetIP()
if err != nil {
return nil, errors.Wrap(err, "Error getting IP")
return nil, errors.Wrap(err, "getting IP")
}
ip := net.ParseIP(ipStr)
if ip == nil {
return nil, fmt.Errorf("error parsing IP: %s", ipStr)
return nil, fmt.Errorf("parsing IP: %s", ipStr)
}
return ip, nil
}
......@@ -200,21 +210,21 @@ func preCreateHost(config *cfg.MachineConfig) error {
switch config.VMDriver {
case "kvm":
if viper.GetBool(cfg.ShowDriverDeprecationNotification) {
fmt.Fprintln(os.Stderr, `WARNING: The kvm driver is now deprecated and support for it will be removed in a future release.
console.Warning(`The kvm driver is deprecated and support for it will be removed in a future release.
Please consider switching to the kvm2 driver, which is intended to replace the kvm driver.
See https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm2-driver for more information.
To disable this message, run [minikube config set WantShowDriverDeprecationNotification false]`)
}
case "xhyve":
if viper.GetBool(cfg.ShowDriverDeprecationNotification) {
fmt.Fprintln(os.Stderr, `WARNING: The xhyve driver is now deprecated and support for it will be removed in a future release.
console.Warning(`The xhyve driver is deprecated and support for it will be removed in a future release.
Please consider switching to the hyperkit driver, which is intended to replace the xhyve driver.
See https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#hyperkit-driver for more information.
To disable this message, run [minikube config set WantShowDriverDeprecationNotification false]`)
}
case "vmwarefusion":
if viper.GetBool(cfg.ShowDriverDeprecationNotification) {
fmt.Fprintln(os.Stderr, `WARNING: The vmwarefusion driver is now deprecated and support for it will be removed in a future release.
console.Warning(`The vmwarefusion driver is deprecated and support for it will be removed in a future release.
Please consider switching to the new vmware unified driver, which is intended to replace the vmwarefusion driver.
See https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#vmware-unified-driver for more information.
To disable this message, run [minikube config set WantShowDriverDeprecationNotification false]`)
......@@ -249,12 +259,12 @@ func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error
data, err := json.Marshal(driver)
if err != nil {
return nil, errors.Wrap(err, "Error marshalling json")
return nil, errors.Wrap(err, "marshal")
}
h, err := api.NewHost(config.VMDriver, data)
if err != nil {
return nil, errors.Wrap(err, "Error creating new host")
return nil, errors.Wrap(err, "new host")
}
h.HostOptions.AuthOptions.CertDir = constants.GetMinipath()
......@@ -264,11 +274,11 @@ func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error
if err := api.Create(h); err != nil {
// Wait for all the logs to reach the client
time.Sleep(2 * time.Second)
return nil, errors.Wrap(err, "Error creating host")
return nil, errors.Wrap(err, "create")
}
if err := api.Save(h); err != nil {
return nil, errors.Wrap(err, "Error attempting to save")
return nil, errors.Wrap(err, "save")
}
return h, nil
}
......@@ -310,11 +320,11 @@ func MountHost(api libmachine.API, ip net.IP, path, port, mountVersion string, u
host.RunSSHCommand(GetMountCleanupCommand(path))
mountCmd, err := GetMountCommand(ip, path, port, mountVersion, uid, gid, msize)
if err != nil {
return errors.Wrap(err, "Error getting mount command")
return errors.Wrap(err, "mount command")
}
_, err = host.RunSSHCommand(mountCmd)
if err != nil {
return errors.Wrap(err, "running mount host command")
return errors.Wrap(err, "running mount")
}
return nil
}
......@@ -332,13 +342,13 @@ func GetVMHostIP(host *host.Host) (net.IP, error) {
hypervVirtualSwitch := re.FindStringSubmatch(string(host.RawDriver))[1]
ip, err := getIPForInterface(fmt.Sprintf("vEthernet (%s)", hypervVirtualSwitch))
if err != nil {
return []byte{}, errors.Wrap(err, "Error getting VM/Host IP address")
return []byte{}, errors.Wrap(err, fmt.Sprintf("ip for interface (%s)", hypervVirtualSwitch))
}
return ip, nil
case "virtualbox":
out, err := exec.Command(detectVBoxManageCmd(), "showvminfo", host.Name, "--machinereadable").Output()
if err != nil {
return []byte{}, errors.Wrap(err, "Error running vboxmanage command")
return []byte{}, errors.Wrap(err, "vboxmanage")
}
re := regexp.MustCompile(`hostonlyadapter2="(.*?)"`)
iface := re.FindStringSubmatch(string(out))[1]
......@@ -388,21 +398,21 @@ func CreateSSHShell(api libmachine.API, args []string) error {
machineName := cfg.GetMachineName()
host, err := CheckIfHostExistsAndLoad(api, machineName)
if err != nil {
return errors.Wrap(err, "Error checking if api exist and loading it")
return errors.Wrap(err, "host exists and load")
}
currentState, err := host.Driver.GetState()
if err != nil {
return errors.Wrap(err, "Error getting state of host")
return errors.Wrap(err, "state")
}
if currentState != state.Running {
return errors.Errorf("Error: Cannot run ssh command: Host %q is not running", machineName)
return errors.Errorf("%q is not running", machineName)
}
client, err := host.CreateSSHClient()
if err != nil {
return errors.Wrap(err, "Error creating ssh client")
return errors.Wrap(err, "Creating ssh client")
}
return client.Shell(args...)
}
......@@ -412,11 +422,11 @@ func CreateSSHShell(api libmachine.API, args []string) error {
func EnsureMinikubeRunningOrExit(api libmachine.API, exitStatus int) {
s, err := GetHostStatus(api)
if err != nil {
glog.Errorln("Error getting machine status:", err)
console.Fatal("Error getting machine status:", err)
os.Exit(1)
}
if s != state.Running.String() {
fmt.Fprintln(os.Stderr, "minikube is not currently running so the service cannot be accessed")
console.ErrStyle("conflict", "minikube is not running, so the service cannot be accessed")
os.Exit(exitStatus)
}
}
......
......@@ -18,7 +18,6 @@ package cluster
import (
"os"
"strings"
"testing"
"github.com/docker/machine/libmachine/drivers"
......@@ -256,29 +255,6 @@ func TestDeleteHostErrorDeletingFiles(t *testing.T) {
}
}
func TestDeleteHostMultipleErrors(t *testing.T) {
api := tests.NewMockAPI()
api.RemoveError = true
h, _ := createHost(api, defaultMachineConfig)
d := &tests.MockDriver{RemoveError: true}
h.Driver = d
err := DeleteHost(api)
if err == nil {
t.Fatal("Expected error deleting host, didn't get one.")
}
expectedErrors := []string{"error removing " + config.GetMachineName(), "error deleting machine"}
for _, expectedError := range expectedErrors {
if !strings.Contains(err.Error(), expectedError) {
t.Fatalf("Error %v expected to contain: %s.", err, expectedError)
}
}
}
func TestGetHostStatus(t *testing.T) {
api := tests.NewMockAPI()
......
/*
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 console provides a mechanism for sending localized, stylized output to the console.
package console
import (
"fmt"
"io"
"os"
"strconv"
"strings"
"github.com/golang/glog"
isatty "github.com/mattn/go-isatty"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
// By design, this package uses global references to language and output objects, in preference
// to passing a console object throughout the code base. Typical usage is:
//
// console.SetOutFile(os.Stdout)
// console.Out("Starting up!")
// console.OutStyle("status-change", "Configuring things")
// console.SetErrFile(os.Stderr)
// console.Fatal("Oh no, everything failed.")
// NOTE: If you do not want colorized output, set MINIKUBE_IN_COLOR=false in your environment.
var (
// outFile is where Out* functions send output to. Set using SetOutFile()
outFile fdWriter
// errFile is where Err* functions send output to. Set using SetErrFile()
errFile fdWriter
// preferredLanguage is the default language messages will be output in
preferredLanguage = language.AmericanEnglish
// our default language
defaultLanguage = language.AmericanEnglish
// useColor is whether or not color output should be used, updated by Set*Writer.
useColor = false
// OverrideEnv is the environment variable used to override color/emoji usage
OverrideEnv = "MINIKUBE_IN_COLOR"
)
// fdWriter is the subset of file.File that implements io.Writer and Fd()
type fdWriter interface {
io.Writer
Fd() uintptr
}
// OutStyle writes a stylized and formatted message to stdout
func OutStyle(style, format string, a ...interface{}) error {
OutStyle, err := applyStyle(style, useColor, fmt.Sprintf(format, a...))
if err != nil {
glog.Errorf("applyStyle(%s): %v", style, err)
if oerr := OutLn(format, a...); oerr != nil {
glog.Errorf("Out failed: %v", oerr)
}
return err
}
return Out(OutStyle)
}
// Out writes a basic formatted string to stdout
func Out(format string, a ...interface{}) error {
p := message.NewPrinter(preferredLanguage)
if outFile == nil {
if _, err := p.Fprintf(os.Stdout, "(stdout unset)"+format, a...); err != nil {
return err
}
return fmt.Errorf("no output file has been set")
}
_, err := p.Fprintf(outFile, format, a...)
return err
}
// OutLn writes a basic formatted string with a newline to stdout
func OutLn(format string, a ...interface{}) error {
return Out(format+"\n", a...)
}
// ErrStyle writes a stylized and formatted error message to stderr
func ErrStyle(style, format string, a ...interface{}) error {
format, err := applyStyle(style, useColor, fmt.Sprintf(format, a...))
if err != nil {
glog.Errorf("applyStyle(%s): %v", style, err)
if oerr := ErrLn(format, a...); oerr != nil {
glog.Errorf("Err(%s) failed: %v", format, oerr)
}
return err
}
return Err(format)
}
// Err writes a basic formatted string to stderr
func Err(format string, a ...interface{}) error {
p := message.NewPrinter(preferredLanguage)
if errFile == nil {
if _, err := p.Fprintf(os.Stderr, "(stderr unset)"+format, a...); err != nil {
return err
}
return fmt.Errorf("no error file has been set")
}
_, err := p.Fprintf(errFile, format, a...)
return err
}
// ErrLn writes a basic formatted string with a newline to stderr
func ErrLn(format string, a ...interface{}) error {
return Err(format+"\n", a...)
}
// Success is a shortcut for writing a styled success message to stdout
func Success(format string, a ...interface{}) error {
return OutStyle("success", format, a...)
}
// Fatal is a shortcut for writing a styled fatal message to stderr
func Fatal(format string, a ...interface{}) error {
return ErrStyle("fatal", format, a...)
}
// Warning is a shortcut for writing a styled warning message to stderr
func Warning(format string, a ...interface{}) error {
return ErrStyle("warning", format, a...)
}
// Failure is a shortcut for writing a styled failure message to stderr
func Failure(format string, a ...interface{}) error {
return ErrStyle("failure", format, a...)
}
// SetPreferredLanguageTag configures which language future messages should use.
func SetPreferredLanguageTag(l language.Tag) {
glog.Infof("Setting Language to %s ...", l)
preferredLanguage = l
}
// SetPreferredLanguage configures which language future messages should use, based on a LANG string.
func SetPreferredLanguage(s string) error {
// "C" is commonly used to denote a neutral POSIX locale. See http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_02
if s == "" || s == "C" {
SetPreferredLanguageTag(defaultLanguage)
return nil
}
// Handles "de_DE" or "de_DE.utf8"
// We don't process encodings, since Rob Pike invented utf8 and we're mostly stuck with it.
parts := strings.Split(s, ".")
l, err := language.Parse(parts[0])
if err != nil {
return err
}
SetPreferredLanguageTag(l)
return nil
}
// SetOutFile configures which writer standard output goes to.
func SetOutFile(w fdWriter) {
glog.Infof("Setting OutFile to fd %d ...", w.Fd())
outFile = w
useColor = wantsColor(w.Fd())
}
// SetErrFile configures which writer error output goes to.
func SetErrFile(w fdWriter) {
glog.Infof("Setting ErrFile to fd %d...", w.Fd())
errFile = w
useColor = wantsColor(w.Fd())
}
// wantsColor determines if the user might want colorized output.
func wantsColor(fd uintptr) bool {
// First process the environment: we allow users to force colors on or off.
//
// MINIKUBE_IN_COLOR=[1, T, true, TRUE]
// MINIKUBE_IN_COLOR=[0, f, false, FALSE]
//
// If unset, we try to automatically determine suitability from the environment.
val := os.Getenv(OverrideEnv)
if val != "" {
glog.Infof("%s=%q\n", OverrideEnv, os.Getenv(OverrideEnv))
override, err := strconv.ParseBool(val)
if err != nil {
// That's OK, we will just fall-back to automatic detection.
glog.Errorf("ParseBool(%s): %v", OverrideEnv, err)
} else {
return override
}
}
term := os.Getenv("TERM")
// Example: term-256color
if !strings.Contains(term, "color") {
glog.Infof("TERM=%s, which probably does not support color", term)
return false
}
isT := isatty.IsTerminal(fd)
glog.Infof("isatty.IsTerminal(%d) = %v\n", fd, isT)
return isT
}
/*
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 console
import (
"bytes"
"os"
"testing"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
// fakeFile satisfies fdWriter
type fakeFile struct {
b bytes.Buffer
}
func newFakeFile() *fakeFile {
return &fakeFile{}
}
func (f *fakeFile) Fd() uintptr {
return uintptr(0)
}
func (f *fakeFile) Write(p []byte) (int, error) {
return f.b.Write(p)
}
func (f *fakeFile) String() string {
return f.b.String()
}
func TestOutStyle(t *testing.T) {
os.Setenv(OverrideEnv, "1")
f := newFakeFile()
SetOutFile(f)
if err := OutStyle("happy", "This is a happy message."); err != nil {
t.Errorf("unexpected error: %q", err)
}
got := f.String()
want := "😄 This is a happy message.\n"
if got != want {
t.Errorf("OutStyle() = %q, want %q", got, want)
}
}
func TestOut(t *testing.T) {
os.Setenv(OverrideEnv, "")
// An example translation just to assert that this code path is executed.
err := message.SetString(language.Arabic, "Installing Kubernetes version %s ...", "... %s تثبيت Kubernetes الإصدار")
if err != nil {
t.Fatalf("setstring: %v", err)
}
var tests = []struct {
format string
lang language.Tag
arg string
want string
}{
{format: "xyz123", want: "xyz123"},
{format: "Installing Kubernetes version %s ...", lang: language.Arabic, arg: "v1.13", want: "... v1.13 تثبيت Kubernetes الإصدار"},
{format: "Installing Kubernetes version %s ...", lang: language.AmericanEnglish, arg: "v1.13", want: "Installing Kubernetes version v1.13 ..."},
}
for _, tc := range tests {
t.Run(tc.format, func(t *testing.T) {
SetPreferredLanguageTag(tc.lang)
f := newFakeFile()
SetOutFile(f)
ErrLn("unrelated message")
if err := Out(tc.format, tc.arg); err != nil {
t.Errorf("unexpected error: %q", err)
}
got := f.String()
if got != tc.want {
t.Errorf("Out(%s, %s) = %q, want %q", tc.format, tc.arg, got, tc.want)
}
})
}
}
func TestErr(t *testing.T) {
os.Setenv(OverrideEnv, "0")
f := newFakeFile()
SetErrFile(f)
if err := Err("xyz123\n"); err != nil {
t.Errorf("unexpected error: %q", err)
}
OutLn("unrelated message")
got := f.String()
want := "xyz123\n"
if got != want {
t.Errorf("Err() = %q, want %q", got, want)
}
}
func TestErrStyle(t *testing.T) {
os.Setenv(OverrideEnv, "1")
f := newFakeFile()
SetErrFile(f)
if err := ErrStyle("fatal", "It broke"); err != nil {
t.Errorf("unexpected error: %q", err)
}
got := f.String()
want := "💣 It broke\n"
if got != want {
t.Errorf("ErrStyle() = %q, want %q", got, want)
}
}
func TestSetPreferredLanguage(t *testing.T) {
os.Setenv(OverrideEnv, "0")
var tests = []struct {
input string
want language.Tag
}{
{"", language.AmericanEnglish},
{"C", language.AmericanEnglish},
{"zh", language.Chinese},
{"fr_FR.utf8", language.French},
}
for _, tc := range tests {
t.Run(tc.input, func(t *testing.T) {
// Set something so that we can assert change.
SetPreferredLanguageTag(language.Icelandic)
if err := SetPreferredLanguage(tc.input); err != nil {
t.Errorf("unexpected error: %q", err)
}
// Just compare the bases ("en", "fr"), since I can't seem to refer directly to them
want, _ := tc.want.Base()
got, _ := preferredLanguage.Base()
if got != want {
t.Errorf("SetPreferredLanguage(%s) = %q, want %q", tc.input, got, want)
}
})
}
}
/*
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 console
import (
"fmt"
"golang.org/x/text/message"
)
// style describes how to stylize a message.
type style struct {
// Prefix is a string to place in the beginning of a message
Prefix string
// OmitNewline omits a newline at the end of a message.
OmitNewline bool
}
// styles is a map of style name to style struct
// For consistency, ensure that emojis added render with the same width across platforms.
var styles = map[string]style{
"happy": {Prefix: "😄 "},
"success": {Prefix: "✅ "},
"failure": {Prefix: "❌ "},
"conflict": {Prefix: "💥 "},
"fatal": {Prefix: "💣 "},
"notice": {Prefix: "📌 "},
"ready": {Prefix: "🏄 "},
"restarting": {Prefix: "🔄 "},
"stopping": {Prefix: "✋ "},
"stopped": {Prefix: "🛑 "},
"warning": {Prefix: "⚠️ "},
"waiting": {Prefix: "⌛ "},
"usage": {Prefix: "💡 "},
"launch": {Prefix: "🚀 "},
"thumbs-up": {Prefix: "👍 "},
"option": {Prefix: " ▪ "}, // Indented bullet
"crushed": {Prefix: "💔 "},
// Specialized purpose styles
"iso-download": {Prefix: "💿 "},
"file-download": {Prefix: "💾 "},
"caching": {Prefix: "🤹 "},
"starting-vm": {Prefix: "🔥 "},
"starting-none": {Prefix: "🤹 "},
"deleting-vm": {Prefix: "🔥 "},
"copying": {Prefix: "✨ "},
"connectivity": {Prefix: "📶 "},
"internet": {Prefix: "🌐 "},
"mounting": {Prefix: "📁 "},
"celebrate": {Prefix: "🎉 "},
"container-runtime": {Prefix: "🎁 "},
"Docker": {Prefix: "🐳 "},
"CRIO": {Prefix: "🎁 "}, // This should be a snow-flake, but the emoji has a strange width on macOS
"containerd": {Prefix: "📦 "},
"permissions": {Prefix: "🔑 "},
"enabling": {Prefix: "🔌 "},
"pulling": {Prefix: "🚜 "},
"verifying": {Prefix: "🤔 "},
"verifying-noline": {Prefix: "🤔 ", OmitNewline: true},
"kubectl": {Prefix: "💗 "},
"meh": {Prefix: "🙄 "},
"embarassed": {Prefix: "🤦 "},
"tip": {Prefix: "💡 "},
}
// Add a prefix to a string
func applyPrefix(prefix, format string) string {
if prefix == "" {
return format
}
// TODO(tstromberg): Ensure compatibility with RTL languages.
return prefix + format
}
// Apply styling to a format string
func applyStyle(style string, useColor bool, format string, a ...interface{}) (string, error) {
p := message.NewPrinter(preferredLanguage)
out := p.Sprintf(format, a...)
s, ok := styles[style]
if !s.OmitNewline {
out += "\n"
}
// Similar to CSS styles, if no style matches, output an unformatted string.
if !ok {
return p.Sprintf(format, a...), fmt.Errorf("unknown style: %q", style)
}
prefix := s.Prefix
if !useColor && prefix != "" {
prefix = "-"
}
out = applyPrefix(prefix, out)
return out, nil
}
......@@ -19,7 +19,6 @@ package machine
import (
"crypto/tls"
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
......@@ -120,7 +119,7 @@ func (api *LocalClient) NewHost(driverName string, rawDriver []byte) (*host.Host
func (api *LocalClient) Load(name string) (*host.Host, error) {
h, err := api.Filestore.Load(name)
if err != nil {
return nil, errors.Wrap(err, "Error loading host from store")
return nil, errors.Wrap(err, "filestore")
}
var def registry.DriverDef
......@@ -165,25 +164,25 @@ func (api *LocalClient) Create(h *host.Host) error {
f func() error
}{
{
"Bootstrapping certs.",
"bootstrapping certificates",
func() error { return cert.BootstrapCertificates(h.AuthOptions()) },
},
{
"Running precreate checks.",
"precreate",
h.Driver.PreCreateCheck,
},
{
"Saving driver.",
"saving",
func() error {
return api.Save(h)
},
},
{
"Creating VM.",
"creating",
h.Driver.Create,
},
{
"Waiting for VM to start.",
"waiting",
func() error {
if h.Driver.DriverName() == "none" {
return nil
......@@ -192,7 +191,7 @@ func (api *LocalClient) Create(h *host.Host) error {
},
},
{
"Provisioning VM.",
"provisioning",
func() error {
if h.Driver.DriverName() == "none" {
return nil
......@@ -205,7 +204,7 @@ func (api *LocalClient) Create(h *host.Host) error {
for _, step := range steps {
if err := step.f(); err != nil {
return errors.Wrap(err, fmt.Sprintf("Error executing step: %s\n", step.name))
return errors.Wrap(err, step.name)
}
}
......
......@@ -20,28 +20,25 @@ import (
"bytes"
"fmt"
"net/url"
"os"
"strings"
"text/template"
"time"
"github.com/docker/machine/libmachine"
"github.com/golang/glog"
"github.com/pkg/browser"
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/clientcmd"
"text/template"
"github.com/spf13/viper"
"k8s.io/apimachinery/pkg/labels"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/util"
)
......@@ -242,9 +239,9 @@ func WaitAndMaybeOpenService(api libmachine.API, namespace string, service strin
urlString, isHTTPSchemedURL := OptionallyHTTPSFormattedURLString(bareURLString, https)
if urlMode || !isHTTPSchemedURL {
fmt.Fprintln(os.Stdout, urlString)
console.OutLn(urlString)
} else {
fmt.Fprintln(os.Stderr, "Opening kubernetes service "+namespace+"/"+service+" in default browser...")
console.OutStyle("celebrate", "Opening kubernetes service %s/%s in default browser...", namespace, service)
browser.OpenURL(urlString)
}
}
......@@ -312,7 +309,6 @@ func CreateSecret(namespace, name string, dataValues map[string]string, labels m
_, err = secrets.Create(secretObj)
if err != nil {
fmt.Println("err: ", err)
return &util.RetriableError{Err: err}
}
......
......@@ -18,7 +18,6 @@ package util
import (
"crypto"
"fmt"
"net/url"
"os"
"path/filepath"
......@@ -26,6 +25,7 @@ import (
"github.com/golang/glog"
download "github.com/jimmidyson/go-download"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/constants"
)
......@@ -72,7 +72,7 @@ func (f DefaultDownloader) CacheMinikubeISOFromURL(isoURL string) error {
options.ChecksumHash = crypto.SHA256
}
fmt.Println("Downloading Minikube ISO")
console.OutStyle("iso-download", "Downloading Minikube ISO ...")
if err := download.ToFile(isoURL, f.GetISOCacheFilepath(isoURL), options); err != nil {
return errors.Wrap(err, isoURL)
}
......
......@@ -114,7 +114,7 @@ func RetryAfter(attempts int, callback func() error, d time.Duration) (err error
glog.Infof("non-retriable error: %v", err)
return m.ToError()
}
glog.V(2).Infof("sleeping %s", d)
glog.V(2).Infof("error: %v - sleeping %s", err, d)
time.Sleep(d)
}
return m.ToError()
......
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
MIT License (Expat)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Package isatty implements interface to isatty
package isatty
// +build appengine
package isatty
// IsTerminal returns true if the file descriptor is terminal which
// is always false on on appengine classic which is a sandboxed PaaS.
func IsTerminal(fd uintptr) bool {
return false
}
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}
// +build darwin freebsd openbsd netbsd dragonfly
// +build !appengine
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TIOCGETA
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}
// +build linux
// +build !appengine,!ppc64,!ppc64le
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TCGETS
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}
// +build linux
// +build ppc64 ppc64le
package isatty
import (
"unsafe"
syscall "golang.org/x/sys/unix"
)
const ioctlReadTermios = syscall.TCGETS
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}
// +build !windows
// +build !appengine
package isatty
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}
// +build solaris
// +build !appengine
package isatty
import (
"golang.org/x/sys/unix"
)
// IsTerminal returns true if the given file descriptor is a terminal.
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
func IsTerminal(fd uintptr) bool {
var termio unix.Termio
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
return err == nil
}
// +build windows
// +build !appengine
package isatty
import (
"strings"
"syscall"
"unicode/utf16"
"unsafe"
)
const (
fileNameInfo uintptr = 2
fileTypePipe = 3
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
procGetFileType = kernel32.NewProc("GetFileType")
)
func init() {
// Check if GetFileInformationByHandleEx is available.
if procGetFileInformationByHandleEx.Find() != nil {
procGetFileInformationByHandleEx = nil
}
}
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}
// Check pipe name is used for cygwin/msys2 pty.
// Cygwin/MSYS2 PTY has a name like:
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
func isCygwinPipeName(name string) bool {
token := strings.Split(name, "-")
if len(token) < 5 {
return false
}
if token[0] != `\msys` && token[0] != `\cygwin` {
return false
}
if token[1] == "" {
return false
}
if !strings.HasPrefix(token[2], "pty") {
return false
}
if token[3] != `from` && token[3] != `to` {
return false
}
if token[4] != "master" {
return false
}
return true
}
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal.
func IsCygwinTerminal(fd uintptr) bool {
if procGetFileInformationByHandleEx == nil {
return false
}
// Cygwin/msys's pty is a pipe.
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
if ft != fileTypePipe || e != 0 {
return false
}
var buf [2 + syscall.MAX_PATH]uint16
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
uintptr(len(buf)*2), 0, 0)
if r == 0 || e != 0 {
return false
}
l := *(*uint32)(unsafe.Pointer(&buf))
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
}
此差异已折叠。
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package build
import (
"fmt"
"unicode"
"golang.org/x/text/internal/colltab"
)
const (
defaultSecondary = 0x20
defaultTertiary = 0x2
maxTertiary = 0x1F
)
type rawCE struct {
w []int
ccc uint8
}
func makeRawCE(w []int, ccc uint8) rawCE {
ce := rawCE{w: make([]int, 4), ccc: ccc}
copy(ce.w, w)
return ce
}
// A collation element is represented as an uint32.
// In the typical case, a rune maps to a single collation element. If a rune
// can be the start of a contraction or expands into multiple collation elements,
// then the collation element that is associated with a rune will have a special
// form to represent such m to n mappings. Such special collation elements
// have a value >= 0x80000000.
const (
maxPrimaryBits = 21
maxSecondaryBits = 12
maxTertiaryBits = 8
)
func makeCE(ce rawCE) (uint32, error) {
v, e := colltab.MakeElem(ce.w[0], ce.w[1], ce.w[2], ce.ccc)
return uint32(v), e
}
// For contractions, collation elements are of the form
// 110bbbbb bbbbbbbb iiiiiiii iiiinnnn, where
// - n* is the size of the first node in the contraction trie.
// - i* is the index of the first node in the contraction trie.
// - b* is the offset into the contraction collation element table.
// See contract.go for details on the contraction trie.
const (
contractID = 0xC0000000
maxNBits = 4
maxTrieIndexBits = 12
maxContractOffsetBits = 13
)
func makeContractIndex(h ctHandle, offset int) (uint32, error) {
if h.n >= 1<<maxNBits {
return 0, fmt.Errorf("size of contraction trie node too large: %d >= %d", h.n, 1<<maxNBits)
}
if h.index >= 1<<maxTrieIndexBits {
return 0, fmt.Errorf("size of contraction trie offset too large: %d >= %d", h.index, 1<<maxTrieIndexBits)
}
if offset >= 1<<maxContractOffsetBits {
return 0, fmt.Errorf("contraction offset out of bounds: %x >= %x", offset, 1<<maxContractOffsetBits)
}
ce := uint32(contractID)
ce += uint32(offset << (maxNBits + maxTrieIndexBits))
ce += uint32(h.index << maxNBits)
ce += uint32(h.n)
return ce, nil
}
// For expansions, collation elements are of the form
// 11100000 00000000 bbbbbbbb bbbbbbbb,
// where b* is the index into the expansion sequence table.
const (
expandID = 0xE0000000
maxExpandIndexBits = 16
)
func makeExpandIndex(index int) (uint32, error) {
if index >= 1<<maxExpandIndexBits {
return 0, fmt.Errorf("expansion index out of bounds: %x >= %x", index, 1<<maxExpandIndexBits)
}
return expandID + uint32(index), nil
}
// Each list of collation elements corresponding to an expansion starts with
// a header indicating the length of the sequence.
func makeExpansionHeader(n int) (uint32, error) {
return uint32(n), nil
}
// Some runes can be expanded using NFKD decomposition. Instead of storing the full
// sequence of collation elements, we decompose the rune and lookup the collation
// elements for each rune in the decomposition and modify the tertiary weights.
// The collation element, in this case, is of the form
// 11110000 00000000 wwwwwwww vvvvvvvv, where
// - v* is the replacement tertiary weight for the first rune,
// - w* is the replacement tertiary weight for the second rune,
// Tertiary weights of subsequent runes should be replaced with maxTertiary.
// See https://www.unicode.org/reports/tr10/#Compatibility_Decompositions for more details.
const (
decompID = 0xF0000000
)
func makeDecompose(t1, t2 int) (uint32, error) {
if t1 >= 256 || t1 < 0 {
return 0, fmt.Errorf("first tertiary weight out of bounds: %d >= 256", t1)
}
if t2 >= 256 || t2 < 0 {
return 0, fmt.Errorf("second tertiary weight out of bounds: %d >= 256", t2)
}
return uint32(t2<<8+t1) + decompID, nil
}
const (
// These constants were taken from https://www.unicode.org/versions/Unicode6.0.0/ch12.pdf.
minUnified rune = 0x4E00
maxUnified = 0x9FFF
minCompatibility = 0xF900
maxCompatibility = 0xFAFF
minRare = 0x3400
maxRare = 0x4DBF
)
const (
commonUnifiedOffset = 0x10000
rareUnifiedOffset = 0x20000 // largest rune in common is U+FAFF
otherOffset = 0x50000 // largest rune in rare is U+2FA1D
illegalOffset = otherOffset + int(unicode.MaxRune)
maxPrimary = illegalOffset + 1
)
// implicitPrimary returns the primary weight for the a rune
// for which there is no entry for the rune in the collation table.
// We take a different approach from the one specified in
// https://unicode.org/reports/tr10/#Implicit_Weights,
// but preserve the resulting relative ordering of the runes.
func implicitPrimary(r rune) int {
if unicode.Is(unicode.Ideographic, r) {
if r >= minUnified && r <= maxUnified {
// The most common case for CJK.
return int(r) + commonUnifiedOffset
}
if r >= minCompatibility && r <= maxCompatibility {
// This will typically not hit. The DUCET explicitly specifies mappings
// for all characters that do not decompose.
return int(r) + commonUnifiedOffset
}
return int(r) + rareUnifiedOffset
}
return int(r) + otherOffset
}
// convertLargeWeights converts collation elements with large
// primaries (either double primaries or for illegal runes)
// to our own representation.
// A CJK character C is represented in the DUCET as
// [.FBxx.0020.0002.C][.BBBB.0000.0000.C]
// We will rewrite these characters to a single CE.
// We assume the CJK values start at 0x8000.
// See https://unicode.org/reports/tr10/#Implicit_Weights
func convertLargeWeights(elems []rawCE) (res []rawCE, err error) {
const (
cjkPrimaryStart = 0xFB40
rarePrimaryStart = 0xFB80
otherPrimaryStart = 0xFBC0
illegalPrimary = 0xFFFE
highBitsMask = 0x3F
lowBitsMask = 0x7FFF
lowBitsFlag = 0x8000
shiftBits = 15
)
for i := 0; i < len(elems); i++ {
ce := elems[i].w
p := ce[0]
if p < cjkPrimaryStart {
continue
}
if p > 0xFFFF {
return elems, fmt.Errorf("found primary weight %X; should be <= 0xFFFF", p)
}
if p >= illegalPrimary {
ce[0] = illegalOffset + p - illegalPrimary
} else {
if i+1 >= len(elems) {
return elems, fmt.Errorf("second part of double primary weight missing: %v", elems)
}
if elems[i+1].w[0]&lowBitsFlag == 0 {
return elems, fmt.Errorf("malformed second part of double primary weight: %v", elems)
}
np := ((p & highBitsMask) << shiftBits) + elems[i+1].w[0]&lowBitsMask
switch {
case p < rarePrimaryStart:
np += commonUnifiedOffset
case p < otherPrimaryStart:
np += rareUnifiedOffset
default:
p += otherOffset
}
ce[0] = np
for j := i + 1; j+1 < len(elems); j++ {
elems[j] = elems[j+1]
}
elems = elems[:len(elems)-1]
}
}
return elems, nil
}
// nextWeight computes the first possible collation weights following elems
// for the given level.
func nextWeight(level colltab.Level, elems []rawCE) []rawCE {
if level == colltab.Identity {
next := make([]rawCE, len(elems))
copy(next, elems)
return next
}
next := []rawCE{makeRawCE(elems[0].w, elems[0].ccc)}
next[0].w[level]++
if level < colltab.Secondary {
next[0].w[colltab.Secondary] = defaultSecondary
}
if level < colltab.Tertiary {
next[0].w[colltab.Tertiary] = defaultTertiary
}
// Filter entries that cannot influence ordering.
for _, ce := range elems[1:] {
skip := true
for i := colltab.Primary; i < level; i++ {
skip = skip && ce.w[i] == 0
}
if !skip {
next = append(next, ce)
}
}
return next
}
func nextVal(elems []rawCE, i int, level colltab.Level) (index, value int) {
for ; i < len(elems) && elems[i].w[level] == 0; i++ {
}
if i < len(elems) {
return i, elems[i].w[level]
}
return i, 0
}
// compareWeights returns -1 if a < b, 1 if a > b, or 0 otherwise.
// It also returns the collation level at which the difference is found.
func compareWeights(a, b []rawCE) (result int, level colltab.Level) {
for level := colltab.Primary; level < colltab.Identity; level++ {
var va, vb int
for ia, ib := 0, 0; ia < len(a) || ib < len(b); ia, ib = ia+1, ib+1 {
ia, va = nextVal(a, ia, level)
ib, vb = nextVal(b, ib, level)
if va != vb {
if va < vb {
return -1, level
} else {
return 1, level
}
}
}
}
return 0, colltab.Identity
}
func equalCE(a, b rawCE) bool {
for i := 0; i < 3; i++ {
if b.w[i] != a.w[i] {
return false
}
}
return true
}
func equalCEArrays(a, b []rawCE) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if !equalCE(a[i], b[i]) {
return false
}
}
return true
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package build
import (
"fmt"
"io"
"reflect"
"sort"
"strings"
"golang.org/x/text/internal/colltab"
)
// This file contains code for detecting contractions and generating
// the necessary tables.
// Any Unicode Collation Algorithm (UCA) table entry that has more than
// one rune one the left-hand side is called a contraction.
// See https://www.unicode.org/reports/tr10/#Contractions for more details.
//
// We define the following terms:
// initial: a rune that appears as the first rune in a contraction.
// suffix: a sequence of runes succeeding the initial rune
// in a given contraction.
// non-initial: a rune that appears in a suffix.
//
// A rune may be both an initial and a non-initial and may be so in
// many contractions. An initial may typically also appear by itself.
// In case of ambiguities, the UCA requires we match the longest
// contraction.
//
// Many contraction rules share the same set of possible suffixes.
// We store sets of suffixes in a trie that associates an index with
// each suffix in the set. This index can be used to look up a
// collation element associated with the (starter rune, suffix) pair.
//
// The trie is defined on a UTF-8 byte sequence.
// The overall trie is represented as an array of ctEntries. Each node of the trie
// is represented as a subsequence of ctEntries, where each entry corresponds to
// a possible match of a next character in the search string. An entry
// also includes the length and offset to the next sequence of entries
// to check in case of a match.
const (
final = 0
noIndex = 0xFF
)
// ctEntry associates to a matching byte an offset and/or next sequence of
// bytes to check. A ctEntry c is called final if a match means that the
// longest suffix has been found. An entry c is final if c.N == 0.
// A single final entry can match a range of characters to an offset.
// A non-final entry always matches a single byte. Note that a non-final
// entry might still resemble a completed suffix.
// Examples:
// The suffix strings "ab" and "ac" can be represented as:
// []ctEntry{
// {'a', 1, 1, noIndex}, // 'a' by itself does not match, so i is 0xFF.
// {'b', 'c', 0, 1}, // "ab" -> 1, "ac" -> 2
// }
//
// The suffix strings "ab", "abc", "abd", and "abcd" can be represented as:
// []ctEntry{
// {'a', 1, 1, noIndex}, // 'a' must be followed by 'b'.
// {'b', 1, 2, 1}, // "ab" -> 1, may be followed by 'c' or 'd'.
// {'d', 'd', final, 3}, // "abd" -> 3
// {'c', 4, 1, 2}, // "abc" -> 2, may be followed by 'd'.
// {'d', 'd', final, 4}, // "abcd" -> 4
// }
// See genStateTests in contract_test.go for more examples.
type ctEntry struct {
L uint8 // non-final: byte value to match; final: lowest match in range.
H uint8 // non-final: relative index to next block; final: highest match in range.
N uint8 // non-final: length of next block; final: final
I uint8 // result offset. Will be noIndex if more bytes are needed to complete.
}
// contractTrieSet holds a set of contraction tries. The tries are stored
// consecutively in the entry field.
type contractTrieSet []struct{ l, h, n, i uint8 }
// ctHandle is used to identify a trie in the trie set, consisting in an offset
// in the array and the size of the first node.
type ctHandle struct {
index, n int
}
// appendTrie adds a new trie for the given suffixes to the trie set and returns
// a handle to it. The handle will be invalid on error.
func appendTrie(ct *colltab.ContractTrieSet, suffixes []string) (ctHandle, error) {
es := make([]stridx, len(suffixes))
for i, s := range suffixes {
es[i].str = s
}
sort.Sort(offsetSort(es))
for i := range es {
es[i].index = i + 1
}
sort.Sort(genidxSort(es))
i := len(*ct)
n, err := genStates(ct, es)
if err != nil {
*ct = (*ct)[:i]
return ctHandle{}, err
}
return ctHandle{i, n}, nil
}
// genStates generates ctEntries for a given suffix set and returns
// the number of entries for the first node.
func genStates(ct *colltab.ContractTrieSet, sis []stridx) (int, error) {
if len(sis) == 0 {
return 0, fmt.Errorf("genStates: list of suffices must be non-empty")
}
start := len(*ct)
// create entries for differing first bytes.
for _, si := range sis {
s := si.str
if len(s) == 0 {
continue
}
added := false
c := s[0]
if len(s) > 1 {
for j := len(*ct) - 1; j >= start; j-- {
if (*ct)[j].L == c {
added = true
break
}
}
if !added {
*ct = append(*ct, ctEntry{L: c, I: noIndex})
}
} else {
for j := len(*ct) - 1; j >= start; j-- {
// Update the offset for longer suffixes with the same byte.
if (*ct)[j].L == c {
(*ct)[j].I = uint8(si.index)
added = true
}
// Extend range of final ctEntry, if possible.
if (*ct)[j].H+1 == c {
(*ct)[j].H = c
added = true
}
}
if !added {
*ct = append(*ct, ctEntry{L: c, H: c, N: final, I: uint8(si.index)})
}
}
}
n := len(*ct) - start
// Append nodes for the remainder of the suffixes for each ctEntry.
sp := 0
for i, end := start, len(*ct); i < end; i++ {
fe := (*ct)[i]
if fe.H == 0 { // uninitialized non-final
ln := len(*ct) - start - n
if ln > 0xFF {
return 0, fmt.Errorf("genStates: relative block offset too large: %d > 255", ln)
}
fe.H = uint8(ln)
// Find first non-final strings with same byte as current entry.
for ; sis[sp].str[0] != fe.L; sp++ {
}
se := sp + 1
for ; se < len(sis) && len(sis[se].str) > 1 && sis[se].str[0] == fe.L; se++ {
}
sl := sis[sp:se]
sp = se
for i, si := range sl {
sl[i].str = si.str[1:]
}
nn, err := genStates(ct, sl)
if err != nil {
return 0, err
}
fe.N = uint8(nn)
(*ct)[i] = fe
}
}
sort.Sort(entrySort((*ct)[start : start+n]))
return n, nil
}
// There may be both a final and non-final entry for a byte if the byte
// is implied in a range of matches in the final entry.
// We need to ensure that the non-final entry comes first in that case.
type entrySort colltab.ContractTrieSet
func (fe entrySort) Len() int { return len(fe) }
func (fe entrySort) Swap(i, j int) { fe[i], fe[j] = fe[j], fe[i] }
func (fe entrySort) Less(i, j int) bool {
return fe[i].L > fe[j].L
}
// stridx is used for sorting suffixes and their associated offsets.
type stridx struct {
str string
index int
}
// For computing the offsets, we first sort by size, and then by string.
// This ensures that strings that only differ in the last byte by 1
// are sorted consecutively in increasing order such that they can
// be packed as a range in a final ctEntry.
type offsetSort []stridx
func (si offsetSort) Len() int { return len(si) }
func (si offsetSort) Swap(i, j int) { si[i], si[j] = si[j], si[i] }
func (si offsetSort) Less(i, j int) bool {
if len(si[i].str) != len(si[j].str) {
return len(si[i].str) > len(si[j].str)
}
return si[i].str < si[j].str
}
// For indexing, we want to ensure that strings are sorted in string order, where
// for strings with the same prefix, we put longer strings before shorter ones.
type genidxSort []stridx
func (si genidxSort) Len() int { return len(si) }
func (si genidxSort) Swap(i, j int) { si[i], si[j] = si[j], si[i] }
func (si genidxSort) Less(i, j int) bool {
if strings.HasPrefix(si[j].str, si[i].str) {
return false
}
if strings.HasPrefix(si[i].str, si[j].str) {
return true
}
return si[i].str < si[j].str
}
// lookup matches the longest suffix in str and returns the associated offset
// and the number of bytes consumed.
func lookup(ct *colltab.ContractTrieSet, h ctHandle, str []byte) (index, ns int) {
states := (*ct)[h.index:]
p := 0
n := h.n
for i := 0; i < n && p < len(str); {
e := states[i]
c := str[p]
if c >= e.L {
if e.L == c {
p++
if e.I != noIndex {
index, ns = int(e.I), p
}
if e.N != final {
// set to new state
i, states, n = 0, states[int(e.H)+n:], int(e.N)
} else {
return
}
continue
} else if e.N == final && c <= e.H {
p++
return int(c-e.L) + int(e.I), p
}
}
i++
}
return
}
// print writes the contractTrieSet t as compilable Go code to w. It returns
// the total number of bytes written and the size of the resulting data structure in bytes.
func print(t *colltab.ContractTrieSet, w io.Writer, name string) (n, size int, err error) {
update3 := func(nn, sz int, e error) {
n += nn
if err == nil {
err = e
}
size += sz
}
update2 := func(nn int, e error) { update3(nn, 0, e) }
update3(printArray(*t, w, name))
update2(fmt.Fprintf(w, "var %sContractTrieSet = ", name))
update3(printStruct(*t, w, name))
update2(fmt.Fprintln(w))
return
}
func printArray(ct colltab.ContractTrieSet, w io.Writer, name string) (n, size int, err error) {
p := func(f string, a ...interface{}) {
nn, e := fmt.Fprintf(w, f, a...)
n += nn
if err == nil {
err = e
}
}
size = len(ct) * 4
p("// %sCTEntries: %d entries, %d bytes\n", name, len(ct), size)
p("var %sCTEntries = [%d]struct{L,H,N,I uint8}{\n", name, len(ct))
for _, fe := range ct {
p("\t{0x%X, 0x%X, %d, %d},\n", fe.L, fe.H, fe.N, fe.I)
}
p("}\n")
return
}
func printStruct(ct colltab.ContractTrieSet, w io.Writer, name string) (n, size int, err error) {
n, err = fmt.Fprintf(w, "colltab.ContractTrieSet( %sCTEntries[:] )", name)
size = int(reflect.TypeOf(ct).Size())
return
}
此差异已折叠。
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package build
import (
"fmt"
"io"
"reflect"
"golang.org/x/text/internal/colltab"
)
// table is an intermediate structure that roughly resembles the table in collate.
type table struct {
colltab.Table
trie trie
root *trieHandle
}
// print writes the table as Go compilable code to w. It prefixes the
// variable names with name. It returns the number of bytes written
// and the size of the resulting table.
func (t *table) fprint(w io.Writer, name string) (n, size int, err error) {
update := func(nn, sz int, e error) {
n += nn
if err == nil {
err = e
}
size += sz
}
// Write arrays needed for the structure.
update(printColElems(w, t.ExpandElem, name+"ExpandElem"))
update(printColElems(w, t.ContractElem, name+"ContractElem"))
update(t.trie.printArrays(w, name))
update(printArray(t.ContractTries, w, name))
nn, e := fmt.Fprintf(w, "// Total size of %sTable is %d bytes\n", name, size)
update(nn, 0, e)
return
}
func (t *table) fprintIndex(w io.Writer, h *trieHandle, id string) (n int, err error) {
p := func(f string, a ...interface{}) {
nn, e := fmt.Fprintf(w, f, a...)
n += nn
if err == nil {
err = e
}
}
p("\t{ // %s\n", id)
p("\t\tlookupOffset: 0x%x,\n", h.lookupStart)
p("\t\tvaluesOffset: 0x%x,\n", h.valueStart)
p("\t},\n")
return
}
func printColElems(w io.Writer, a []uint32, name string) (n, sz int, err error) {
p := func(f string, a ...interface{}) {
nn, e := fmt.Fprintf(w, f, a...)
n += nn
if err == nil {
err = e
}
}
sz = len(a) * int(reflect.TypeOf(uint32(0)).Size())
p("// %s: %d entries, %d bytes\n", name, len(a), sz)
p("var %s = [%d]uint32 {", name, len(a))
for i, c := range a {
switch {
case i%64 == 0:
p("\n\t// Block %d, offset 0x%x\n", i/64, i)
case (i%64)%6 == 0:
p("\n\t")
}
p("0x%.8X, ", c)
}
p("\n}\n\n")
return
}
此差异已折叠。
此差异已折叠。
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package collate
import "golang.org/x/text/internal/colltab"
const blockSize = 64
func getTable(t tableIndex) *colltab.Table {
return &colltab.Table{
Index: colltab.Trie{
Index0: mainLookup[:][blockSize*t.lookupOffset:],
Values0: mainValues[:][blockSize*t.valuesOffset:],
Index: mainLookup[:],
Values: mainValues[:],
},
ExpandElem: mainExpandElem[:],
ContractTries: colltab.ContractTrieSet(mainCTEntries[:]),
ContractElem: mainContractElem[:],
MaxContractLen: 18,
VariableTop: varTop,
}
}
// tableIndex holds information for constructing a table
// for a certain locale based on the main table.
type tableIndex struct {
lookupOffset uint32
valuesOffset uint32
}
此差异已折叠。
此差异已折叠。
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package collate
import (
"bytes"
"sort"
)
const (
maxSortBuffer = 40960
maxSortEntries = 4096
)
type swapper interface {
Swap(i, j int)
}
type sorter struct {
buf *Buffer
keys [][]byte
src swapper
}
func (s *sorter) init(n int) {
if s.buf == nil {
s.buf = &Buffer{}
s.buf.init()
}
if cap(s.keys) < n {
s.keys = make([][]byte, n)
}
s.keys = s.keys[0:n]
}
func (s *sorter) sort(src swapper) {
s.src = src
sort.Sort(s)
}
func (s sorter) Len() int {
return len(s.keys)
}
func (s sorter) Less(i, j int) bool {
return bytes.Compare(s.keys[i], s.keys[j]) == -1
}
func (s sorter) Swap(i, j int) {
s.keys[i], s.keys[j] = s.keys[j], s.keys[i]
s.src.Swap(i, j)
}
// A Lister can be sorted by Collator's Sort method.
type Lister interface {
Len() int
Swap(i, j int)
// Bytes returns the bytes of the text at index i.
Bytes(i int) []byte
}
// Sort uses sort.Sort to sort the strings represented by x using the rules of c.
func (c *Collator) Sort(x Lister) {
n := x.Len()
c.sorter.init(n)
for i := 0; i < n; i++ {
c.sorter.keys[i] = c.Key(c.sorter.buf, x.Bytes(i))
}
c.sorter.sort(x)
}
// SortStrings uses sort.Sort to sort the strings in x using the rules of c.
func (c *Collator) SortStrings(x []string) {
c.sorter.init(len(x))
for i, s := range x {
c.sorter.keys[i] = c.KeyFromString(c.sorter.buf, s)
}
c.sorter.sort(sort.StringSlice(x))
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package colltab // import "golang.org/x/text/internal/colltab"
// A Weighter can be used as a source for Collator and Searcher.
type Weighter interface {
// Start finds the start of the segment that includes position p.
Start(p int, b []byte) int
// StartString finds the start of the segment that includes position p.
StartString(p int, s string) int
// AppendNext appends Elems to buf corresponding to the longest match
// of a single character or contraction from the start of s.
// It returns the new buf and the number of bytes consumed.
AppendNext(buf []Elem, s []byte) (ce []Elem, n int)
// AppendNextString appends Elems to buf corresponding to the longest match
// of a single character or contraction from the start of s.
// It returns the new buf and the number of bytes consumed.
AppendNextString(buf []Elem, s string) (ce []Elem, n int)
// Domain returns a slice of all single characters and contractions for which
// collation elements are defined in this table.
Domain() []string
// Top returns the highest variable primary value.
Top() uint32
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册