提交 bd721de0 编写于 作者: S Sharif Elgamal

Merge branch 'master' of github.com:kubernetes/minikube into master

......@@ -151,14 +151,14 @@ var mountCmd = &cobra.Command{
bindIP = "127.0.0.1"
}
out.T(out.Mounting, "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...", out.V{"sourcePath": hostPath, "destinationPath": vmPath})
out.T(out.Option, "Mount type: {{.name}}", out.V{"type": cfg.Type})
out.T(out.Option, "User ID: {{.userID}}", out.V{"userID": cfg.UID})
out.T(out.Option, "Group ID: {{.groupID}}", out.V{"groupID": cfg.GID})
out.T(out.Option, "Version: {{.version}}", out.V{"version": cfg.Version})
out.T(out.Option, "Message Size: {{.size}}", out.V{"size": cfg.MSize})
out.T(out.Option, "Permissions: {{.octalMode}} ({{.writtenMode}})", out.V{"octalMode": fmt.Sprintf("%o", cfg.Mode), "writtenMode": cfg.Mode})
out.T(out.Option, "Options: {{.options}}", out.V{"options": cfg.Options})
out.T(out.Option, "Bind Address: {{.Address}}", out.V{"Address": net.JoinHostPort(bindIP, fmt.Sprint(port))})
out.Infof("Mount type: {{.name}}", out.V{"type": cfg.Type})
out.Infof("User ID: {{.userID}}", out.V{"userID": cfg.UID})
out.Infof("Group ID: {{.groupID}}", out.V{"groupID": cfg.GID})
out.Infof("Version: {{.version}}", out.V{"version": cfg.Version})
out.Infof("Message Size: {{.size}}", out.V{"size": cfg.MSize})
out.Infof("Permissions: {{.octalMode}} ({{.writtenMode}})", out.V{"octalMode": fmt.Sprintf("%o", cfg.Mode), "writtenMode": cfg.Mode})
out.Infof("Options: {{.options}}", out.V{"options": cfg.Options})
out.Infof("Bind Address: {{.Address}}", out.V{"Address": net.JoinHostPort(bindIP, fmt.Sprint(port))})
var wg sync.WaitGroup
if cfg.Type == nineP {
......
......@@ -57,6 +57,8 @@ import (
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/notify"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/registry"
"k8s.io/minikube/pkg/minikube/translate"
"k8s.io/minikube/pkg/util"
......@@ -122,6 +124,7 @@ func platform() string {
// runStart handles the executes the flow of "minikube start"
func runStart(cmd *cobra.Command, args []string) {
out.SetJSON(viper.GetString(startOutput) == "json")
displayVersion(version.GetVersion())
// No need to do the update check if no one is going to see it
......@@ -348,6 +351,7 @@ func displayVersion(version string) {
prefix = fmt.Sprintf("[%s] ", ClusterFlagValue())
}
register.Reg.SetStep(register.InitialSetup)
out.T(out.Happy, "{{.prefix}}minikube {{.version}} on {{.platform}}", out.V{"prefix": prefix, "version": version, "platform": platform()})
}
......@@ -358,12 +362,13 @@ func displayEnviron(env []string) {
k := bits[0]
v := bits[1]
if strings.HasPrefix(k, "MINIKUBE_") || k == constants.KubeconfigEnvVar {
out.T(out.Option, "{{.key}}={{.value}}", out.V{"key": k, "value": v})
out.Infof("{{.key}}={{.value}}", out.V{"key": k, "value": v})
}
}
}
func showKubectlInfo(kcs *kubeconfig.Settings, k8sVersion string, machineName string) error {
register.Reg.SetStep(register.Done)
if kcs.KeepContext {
out.T(out.Kubectl, "To connect to this cluster, use: kubectl --context={{.name}}", out.V{"name": kcs.ClusterName})
} else {
......@@ -476,7 +481,7 @@ func kubectlVersion(path string) (string, error) {
func selectDriver(existing *config.ClusterConfig) (registry.DriverState, []registry.DriverState, bool) {
// Technically unrelated, but important to perform before detection
driver.SetLibvirtURI(viper.GetString(kvmQemuURI))
register.Reg.SetStep(register.SelectingDriver)
// By default, the driver is whatever we used last time
if existing != nil {
old := hostDriver(existing)
......@@ -520,7 +525,7 @@ func selectDriver(existing *config.ClusterConfig) (registry.DriverState, []regis
if pick.Name == "" {
out.T(out.ThumbsDown, "Unable to pick a default driver. Here is what was considered, in preference order:")
for _, r := range rejects {
out.T(out.Option, "{{ .name }}: {{ .rejection }}", out.V{"name": r.Name, "rejection": r.Rejection})
out.Infof("{{ .name }}: {{ .rejection }}", out.V{"name": r.Name, "rejection": r.Rejection})
}
out.T(out.Workaround, "Try specifying a --driver, or see https://minikube.sigs.k8s.io/docs/start/")
os.Exit(exit.Unavailable)
......@@ -921,6 +926,10 @@ func validateFlags(cmd *cobra.Command, drvName string) {
}
}
if s := viper.GetString(startOutput); s != "text" && s != "json" {
exit.UsageT("Sorry, please set the --output flag to one of the following valid options: [text,json]")
}
validateRegistryMirror()
}
......@@ -980,6 +989,10 @@ func autoSetDriverOptions(cmd *cobra.Command, drvName string) (err error) {
hints := driver.FlagDefaults(drvName)
if len(hints.ExtraOptions) > 0 {
for _, eo := range hints.ExtraOptions {
if config.ExtraOptions.Exists(eo) {
glog.Infof("skipping extra-config %q.", eo)
continue
}
glog.Infof("auto setting extra-config to %q.", eo)
err = config.ExtraOptions.Set(eo)
if err != nil {
......
......@@ -104,6 +104,7 @@ const (
deleteOnFailure = "delete-on-failure"
forceSystemd = "force-systemd"
kicBaseImage = "base-image"
startOutput = "output"
)
// initMinikubeFlags includes commandline flags for minikube.
......@@ -144,6 +145,7 @@ func initMinikubeFlags() {
startCmd.Flags().Bool(preload, true, "If set, download tarball of preloaded images if available to improve start time. Defaults to true.")
startCmd.Flags().Bool(deleteOnFailure, false, "If set, delete the current cluster if start fails and try again. Defaults to false.")
startCmd.Flags().Bool(forceSystemd, false, "If set, force the container runtime to use sytemd as cgroup manager. Currently available for docker and crio. Defaults to false.")
startCmd.Flags().String(startOutput, "text", "Format to print stdout in. Options include: [text,json]")
}
// initKubernetesFlags inits the commandline flags for Kubernetes related options
......
......@@ -11,6 +11,7 @@ require (
github.com/c4milo/gotoolkit v0.0.0-20170318115440-bcc06269efa9 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/cheggaaa/pb/v3 v3.0.1
github.com/cloudevents/sdk-go/v2 v2.1.0
github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 // indirect
github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57 // indirect
......@@ -28,6 +29,7 @@ require (
github.com/google/go-containerregistry v0.0.0-20200601195303-96cf69f03a3c
github.com/google/go-github v17.0.0+incompatible
github.com/google/slowjam v0.0.0-20200530021616-df27e642fe7b
github.com/google/uuid v1.1.1
github.com/googleapis/gnostic v0.3.0 // indirect
github.com/hashicorp/go-getter v1.4.0
github.com/hashicorp/go-retryablehttp v0.6.6
......
......@@ -212,6 +212,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudevents/sdk-go/v2 v2.1.0 h1:bmgrU8k+K2ppZ+G/q5xEQx/Xk9HRtJmkrEO3qtDO2k0=
github.com/cloudevents/sdk-go/v2 v2.1.0/go.mod h1:3CTrpB4+u7Iaj6fd7E2Xvm5IxMdRoaAhqaRVnOr2rCU=
github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:Yg2hDs4b13Evkpj42FU2idX2cVXVFqQSheXYKM86Qsk=
github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:MgJyK38wkzZbiZSKeIeFankxxSA8gayko/nr5x5bgBA=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
......@@ -719,6 +721,8 @@ github.com/libvirt/libvirt-go v3.4.0+incompatible h1:Cpyalgj1x8JIeTlL6SDYZBo7j8n
github.com/libvirt/libvirt-go v3.4.0+incompatible/go.mod h1:34zsnB4iGeOv7Byj6qotuW8Ya4v4Tr43ttjz/F0wjLE=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac h1:+2b6iGRJe3hvV/yVXrd41yVEjxuFHxasJqDhkIjS4gk=
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac/go.mod h1:Frd2bnT3w5FB5q49ENTfVlztJES+1k/7lyWX2+9gq/M=
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
......@@ -855,6 +859,7 @@ github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
......@@ -1093,6 +1098,7 @@ github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
......@@ -1136,8 +1142,11 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI=
......
......@@ -40,6 +40,7 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/storageclass"
"k8s.io/minikube/pkg/util/retry"
)
......@@ -382,6 +383,7 @@ func Start(wg *sync.WaitGroup, cc *config.ClusterConfig, toEnable map[string]boo
var awg sync.WaitGroup
defer func() { // making it show after verifications( not perfect till #7613 is closed)
register.Reg.SetStep(register.EnablingAddons)
out.T(out.AddonEnable, "Enabled addons: {{.addons}}", out.V{"addons": strings.Join(toEnableList, ", ")})
}()
for _, a := range toEnableList {
......
......@@ -20,7 +20,6 @@ package hyperkit
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
......
......@@ -16,7 +16,17 @@ limitations under the License.
package kubeadm
import "errors"
import (
"errors"
"fmt"
)
// max minutes wait for kubeadm init. usually finishes in less than 1 minute.
// giving it a generous timeout for possible super slow machines.
const initTimeoutMinutes = 10
// max seconds to wait for running kubectl apply manifests to the cluster to exit
const applyTimeoutSeconds = 10
// FailFastError type is an error that could not be solved by trying again
type FailFastError struct {
......@@ -30,3 +40,6 @@ func (f *FailFastError) Error() string {
// ErrNoExecLinux is thrown on linux when the kubeadm binaries are mounted in a noexec volume on Linux as seen in https://github.com/kubernetes/minikube/issues/8327#issuecomment-651288459
// this error could be seen on docker/podman or none driver.
var ErrNoExecLinux = &FailFastError{errors.New("mounted kubeadm binary is not executable")}
// ErrInitTimedout is thrown if kubeadm init takes longer than max time allowed
var ErrInitTimedout = fmt.Errorf("kubeadm init timed out in %d minutes", initTimeoutMinutes)
......@@ -55,6 +55,7 @@ import (
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/sysinit"
"k8s.io/minikube/pkg/minikube/vmpath"
"k8s.io/minikube/pkg/util"
......@@ -224,9 +225,15 @@ func (k *Bootstrapper) init(cfg config.ClusterConfig) error {
}
conf := bsutil.KubeadmYamlPath
c := exec.Command("/bin/bash", "-c", fmt.Sprintf("%s init --config %s %s --ignore-preflight-errors=%s",
ctx, cancel := context.WithTimeout(context.Background(), initTimeoutMinutes*time.Minute)
defer cancel()
c := exec.CommandContext(ctx, "/bin/bash", "-c", fmt.Sprintf("%s init --config %s %s --ignore-preflight-errors=%s",
bsutil.InvokeKubeadm(cfg.KubernetesConfig.KubernetesVersion), conf, extraFlags, strings.Join(ignore, ",")))
if _, err := k.c.RunCmd(c); err != nil {
if ctx.Err() == context.DeadlineExceeded {
return ErrInitTimedout
}
if strings.Contains(err.Error(), "'kubeadm': Permission denied") {
return ErrNoExecLinux
}
......@@ -394,6 +401,7 @@ func (k *Bootstrapper) WaitForNode(cfg config.ClusterConfig, n config.Node, time
return nil
}
register.Reg.SetStep(register.VerifyingKubernetes)
out.T(out.HealthCheck, "Verifying Kubernetes components...")
// TODO: #7706: for better performance we could use k.client inside minikube to avoid asking for external IP:PORT
......@@ -830,8 +838,7 @@ func (k *Bootstrapper) applyNodeLabels(cfg config.ClusterConfig) error {
commitLbl := "minikube.k8s.io/commit=" + version.GetGitCommitID()
nameLbl := "minikube.k8s.io/name=" + cfg.Name
// Allow no more than 5 seconds for applying labels
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), applyTimeoutSeconds*time.Second)
defer cancel()
// example:
// sudo /var/lib/minikube/binaries/<version>/kubectl label nodes minikube.k8s.io/version=<version> minikube.k8s.io/commit=aa91f39ffbcf27dcbb93c4ff3f457c54e585cf4a-dirty minikube.k8s.io/name=p1 minikube.k8s.io/updated_at=2020_02_20T12_05_35_0700 --all --overwrite --kubeconfig=/var/lib/minikube/kubeconfig
......@@ -840,6 +847,9 @@ func (k *Bootstrapper) applyNodeLabels(cfg config.ClusterConfig) error {
fmt.Sprintf("--kubeconfig=%s", path.Join(vmpath.GuestPersistentDir, "kubeconfig")))
if _, err := k.c.RunCmd(cmd); err != nil {
if ctx.Err() == context.DeadlineExceeded {
return errors.Wrapf(err, "timeout apply labels")
}
return errors.Wrapf(err, "applying node labels")
}
return nil
......
......@@ -17,6 +17,9 @@ limitations under the License.
package cni
import (
"os/exec"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/config"
)
......@@ -637,6 +640,12 @@ func (c Flannel) String() string {
// Apply enables the CNI
func (c Flannel) Apply(r Runner) error {
// Mostly applicable to the 'none' driver
_, err := r.RunCmd(exec.Command("stat", "/opt/cni/bin/portmap"))
if err != nil {
return errors.Wrap(err, "required 'portmap' CNI plug-in not found")
}
return applyManifest(c.cc, r, manifestAsset([]byte(flannelTmpl)))
}
......
......@@ -18,6 +18,7 @@ package cni
import (
"bytes"
"os/exec"
"text/template"
"github.com/pkg/errors"
......@@ -168,6 +169,12 @@ func (c KindNet) manifest() (assets.CopyableFile, error) {
// Apply enables the CNI
func (c KindNet) Apply(r Runner) error {
// This is mostly applicable to the 'none' driver
_, err := r.RunCmd(exec.Command("stat", "/opt/cni/bin/portmap"))
if err != nil {
return errors.Wrap(err, "required 'portmap' CNI plug-in not found")
}
m, err := c.manifest()
if err != nil {
return errors.Wrap(err, "manifest")
......
......@@ -19,6 +19,8 @@ package config
import (
"fmt"
"strings"
"github.com/golang/glog"
)
// ExtraOption is an extra option
......@@ -38,6 +40,29 @@ type ExtraOptionSlice []ExtraOption
// ComponentExtraOptionMap maps components to their extra opts, which is a map of keys to values
type ComponentExtraOptionMap map[string]map[string]string
// Exists returns true if component.key (parsed from value) is already in ExtraOptionSlice
func (es *ExtraOptionSlice) Exists(value string) bool {
// The component is the value before the first dot.
componentSplit := strings.SplitN(value, ".", 2)
if len(componentSplit) != 2 {
glog.Errorf("invalid value: must contain at least one period: %q", value)
return false
}
keySplit := strings.SplitN(componentSplit[1], "=", 2)
if len(keySplit) != 2 {
glog.Errorf("invalid value: must contain one equal sign: %q", value)
return false
}
for _, opt := range *es {
if opt.Component == componentSplit[0] && opt.Key == keySplit[0] {
return true
}
}
return false
}
// Set parses the string value into a slice
func (es *ExtraOptionSlice) Set(value string) error {
// The component is the value before the first dot.
......
......@@ -79,6 +79,29 @@ func TestValidFlags(t *testing.T) {
}
}
func TestExists(t *testing.T) {
extraOptions := ExtraOptionSlice{
ExtraOption{Component: "c1", Key: "bar", Value: "c1-bar"},
ExtraOption{Component: "c1", Key: "baz", Value: "c1-baz"},
ExtraOption{Component: "c2", Key: "bar", Value: "c2-bar"},
}
for _, tc := range []struct {
searchString string
expRes bool
}{
{"c1.bar=bar", true},
{"c1.foo=foo", false},
{"c2.bar=bar", true},
{"c2.baz=baz", false},
{"c3.baz=baz", false},
} {
if res := extraOptions.Exists(tc.searchString); res != tc.expRes {
t.Errorf("Unexpected value. Expected %t, got %t", tc.expRes, res)
}
}
}
func TestGet(t *testing.T) {
extraOptions := ExtraOptionSlice{
ExtraOption{Component: "c1", Key: "bar", Value: "c1-bar"},
......
......@@ -39,6 +39,7 @@ import (
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/proxy"
"k8s.io/minikube/pkg/minikube/registry"
"k8s.io/minikube/pkg/minikube/vmpath"
......@@ -257,9 +258,11 @@ func showHostInfo(cfg config.ClusterConfig) {
return
}
if driver.IsKIC(cfg.Driver) { // TODO:medyagh add free disk space on docker machine
register.Reg.SetStep(register.CreatingContainer)
out.T(out.StartingVM, "Creating {{.driver_name}} {{.machine_type}} (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB) ...", out.V{"driver_name": cfg.Driver, "number_of_cpus": cfg.CPUs, "memory_size": cfg.Memory, "machine_type": machineType})
return
}
register.Reg.SetStep(register.CreatingVM)
out.T(out.StartingVM, "Creating {{.driver_name}} {{.machine_type}} (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...", out.V{"driver_name": cfg.Driver, "number_of_cpus": cfg.CPUs, "memory_size": cfg.Memory, "disk_size": cfg.DiskSize, "machine_type": machineType})
}
......
......@@ -32,17 +32,19 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/util/lock"
)
func showVersionInfo(k8sVersion string, cr cruntime.Manager) {
version, _ := cr.Version()
register.Reg.SetStep(register.PreparingKubernetes)
out.T(cr.Style(), "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...", out.V{"k8sVersion": k8sVersion, "runtime": cr.Name(), "runtimeVersion": version})
for _, v := range config.DockerOpt {
out.T(out.Option, "opt {{.docker_option}}", out.V{"docker_option": v})
out.Infof("opt {{.docker_option}}", out.V{"docker_option": v})
}
for _, v := range config.DockerEnv {
out.T(out.Option, "env {{.docker_env}}", out.V{"docker_env": v})
out.Infof("env {{.docker_env}}", out.V{"docker_env": v})
}
}
......
......@@ -53,6 +53,7 @@ import (
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/proxy"
"k8s.io/minikube/pkg/util"
"k8s.io/minikube/pkg/util/retry"
......@@ -205,7 +206,7 @@ func Start(starter Starter, apiServer bool) (*kubeconfig.Settings, error) {
// Provision provisions the machine/container for the node
func Provision(cc *config.ClusterConfig, n *config.Node, apiServer bool, delOnFail bool) (command.Runner, bool, libmachine.API, *host.Host, error) {
register.Reg.SetStep(register.StartingNode)
name := driver.MachineName(*cc, *n)
if apiServer {
out.T(out.ThumbsUp, "Starting control plane node {{.name}} in cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
......@@ -288,7 +289,7 @@ func setupKubeAdm(mAPI libmachine.API, cfg config.ClusterConfig, n config.Node,
exit.WithError("Failed to get bootstrapper", err)
}
for _, eo := range config.ExtraOptions {
out.T(out.Option, "{{.extra_option_component_name}}.{{.key}}={{.value}}", out.V{"extra_option_component_name": eo.Component, "key": eo.Key, "value": eo.Value})
out.Infof("{{.extra_option_component_name}}.{{.key}}={{.value}}", out.V{"extra_option_component_name": eo.Component, "key": eo.Key, "value": eo.Value})
}
// Loads cached images, generates config files, download binaries
// update cluster and set up certs
......@@ -423,7 +424,7 @@ func validateNetwork(h *host.Host, r command.Runner, imageRepository string) (st
out.T(out.Internet, "Found network options:")
optSeen = true
}
out.T(out.Option, "{{.key}}={{.value}}", out.V{"key": k, "value": v})
out.Infof("{{.key}}={{.value}}", out.V{"key": k, "value": v})
ipExcluded := proxy.IsIPExcluded(ip) // Skip warning if minikube ip is already in NO_PROXY
k = strings.ToUpper(k) // for http_proxy & https_proxy
if (k == "HTTP_PROXY" || k == "HTTPS_PROXY") && !ipExcluded && !warnedOnce {
......
......@@ -26,6 +26,7 @@ import (
"github.com/golang/glog"
isatty "github.com/mattn/go-isatty"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/translate"
)
......@@ -50,6 +51,8 @@ var (
useColor = false
// OverrideEnv is the environment variable used to override color/emoji usage
OverrideEnv = "MINIKUBE_IN_STYLE"
// JSON is whether or not we should output stdout in JSON format. Set using SetJSON()
JSON = false
)
// MaxLogEntries controls the number of log entries to show for each source
......@@ -66,7 +69,25 @@ type V map[string]interface{}
// T writes a stylized and templated message to stdout
func T(style StyleEnum, format string, a ...V) {
if style == Option {
Infof(format, a...)
return
}
outStyled := ApplyTemplateFormatting(style, useColor, format, a...)
if JSON {
register.PrintStep(outStyled)
return
}
String(outStyled)
}
// Infof is used for informational logs (options, env variables, etc)
func Infof(format string, a ...V) {
outStyled := ApplyTemplateFormatting(Option, useColor, format, a...)
if JSON {
register.PrintInfo(outStyled)
return
}
String(outStyled)
}
......@@ -87,6 +108,10 @@ func String(format string, a ...interface{}) {
// Ln writes a basic formatted string with a newline to stdout
func Ln(format string, a ...interface{}) {
if JSON {
glog.Warningf("please use out.T to log steps in JSON")
return
}
String(format+"\n", a...)
}
......@@ -140,6 +165,12 @@ func SetOutFile(w fdWriter) {
useColor = wantsColor(w.Fd())
}
// SetJSON configures printing to STDOUT in JSON
func SetJSON(j bool) {
glog.Infof("Setting JSON to %v", j)
JSON = j
}
// SetErrFile configures which writer error output goes to.
func SetErrFile(w fdWriter) {
glog.Infof("Setting ErrFile to fd %d...", w.Fd())
......
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package register
import (
"fmt"
"io"
"os"
cloudevents "github.com/cloudevents/sdk-go/v2"
"github.com/golang/glog"
guuid "github.com/google/uuid"
)
const (
specVersion = "1.0"
)
var (
outputFile io.Writer = os.Stdout
getUUID = randomID
)
func printAsCloudEvent(log Log, data map[string]string) {
event := cloudevents.NewEvent()
event.SetSource("https://minikube.sigs.k8s.io/")
event.SetType(log.Type())
event.SetSpecVersion(specVersion)
if err := event.SetData(cloudevents.ApplicationJSON, data); err != nil {
glog.Warningf("error setting data: %v", err)
}
event.SetID(getUUID())
json, err := event.MarshalJSON()
if err != nil {
glog.Warningf("error marashalling event: %v", err)
}
fmt.Fprintln(outputFile, string(json))
}
func randomID() string {
return guuid.New().String()
}
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package register
// PrintStep prints a Step type in JSON format
func PrintStep(message string) {
s := NewStep(message)
printAsCloudEvent(s, s.data)
}
// PrintInfo prints an Info type in JSON format
func PrintInfo(message string) {
s := NewInfo(message)
printAsCloudEvent(s, s.data)
}
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package register
import (
"bytes"
"fmt"
"os"
"testing"
)
func TestPrintStep(t *testing.T) {
expected := `{"data":{"currentstep":"0","message":"message","name":"Initial Minikube Setup","totalsteps":"%v"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.step"}`
expected = fmt.Sprintf(expected, Reg.totalSteps())
expected += "\n"
buf := bytes.NewBuffer([]byte{})
outputFile = buf
defer func() { outputFile = os.Stdout }()
getUUID = func() string {
return "random-id"
}
PrintStep("message")
actual := buf.String()
if actual != expected {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", expected, actual)
}
}
func TestPrintInfo(t *testing.T) {
expected := `{"data":{"message":"info"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.info"}`
expected += "\n"
buf := bytes.NewBuffer([]byte{})
outputFile = buf
defer func() { outputFile = os.Stdout }()
getUUID = func() string {
return "random-id"
}
PrintInfo("info")
actual := buf.String()
if actual != expected {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", expected, actual)
}
}
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package register
// Log represents the different types of logs that can be output as JSON
// This includes: Step, Download, DownloadProgress, Warning, Info, Error
type Log interface {
Type() string
}
// Step represents a normal step in minikube execution
type Step struct {
data map[string]string
}
func (s *Step) Type() string {
return "io.k8s.sigs.minikube.step"
}
func NewStep(message string) *Step {
return &Step{data: map[string]string{
"totalsteps": Reg.totalSteps(),
"currentstep": Reg.currentStep(),
"message": message,
"name": string(Reg.current),
}}
}
// TODO (priyawadhwa@): implement all types below this comment
// Download will be used to notify the user that a download has begun
type Download struct {
}
func (s *Download) Type() string {
return "io.k8s.sigs.minikube.download"
}
// DownloadProgress will be used to notify the user around the progress of a download
type DownloadProgress struct {
}
func (s *DownloadProgress) Type() string {
return "io.k8s.sigs.minikube.download.progress"
}
// Warning will be used to notify the user of warnings
type Warning struct {
}
func (s *Warning) Type() string {
return "io.k8s.sigs.minikube.warning"
}
// Info will be used to notify users of any extra info (env variables, options)
type Info struct {
data map[string]string
}
func (s *Info) Type() string {
return "io.k8s.sigs.minikube.info"
}
// NewInfo returns a new Info type
func NewInfo(message string) *Info {
return &Info{
map[string]string{
"message": message,
},
}
}
// Error will be used to notify the user of errors
type Error struct {
}
func (s *Error) Type() string {
return "io.k8s.sigs.minikube.error"
}
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package register contains all the logic to print out `minikube start` in JSON
package register
import (
"fmt"
)
const (
InitialSetup RegStep = "Initial Minikube Setup"
SelectingDriver RegStep = "Selecting Driver"
DownloadingArtifacts RegStep = "Downloading Artifacts"
StartingNode RegStep = "Starting Node"
CreatingContainer RegStep = "Creating Container"
CreatingVM RegStep = "Creating VM"
PreparingKubernetes RegStep = "Preparing Kubernetes"
VerifyingKubernetes RegStep = "Verifying Kubernetes"
EnablingAddons RegStep = "Enabling Addons"
Done RegStep = "Done"
)
// RegStep is a type representing a distinct step of `minikube start`
type RegStep string
// Register holds all of the steps we could see in `minikube start`
// and keeps track of the current step
type Register struct {
steps []RegStep
current RegStep
}
// Reg keeps track of all possible steps and the current step we are on
var Reg Register
func init() {
Reg = Register{
steps: []RegStep{
InitialSetup,
SelectingDriver,
DownloadingArtifacts,
StartingNode,
CreatingContainer,
CreatingVM,
PreparingKubernetes,
VerifyingKubernetes,
EnablingAddons,
Done,
},
current: InitialSetup,
}
}
// totalSteps returns the total number of steps in the register
func (r *Register) totalSteps() string {
return fmt.Sprintf("%d", len(r.steps)-1)
}
// currentStep returns the current step we are on
func (r *Register) currentStep() string {
for i, s := range r.steps {
if r.current == s {
return fmt.Sprintf("%d", i)
}
}
// all steps should be registered so this shouldn't happen
// can't call exit.WithError as it creates an import dependency loop
panic(fmt.Sprintf("%v is not a registered step", r.current))
}
// SetStep sets the current step
func (r *Register) SetStep(s RegStep) {
r.current = s
}
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package register
import (
"bytes"
"fmt"
"os"
"testing"
)
func TestSetCurrentStep(t *testing.T) {
secondStep := Reg.steps[1]
Reg.SetStep(secondStep)
expected := `{"data":{"currentstep":"1","message":"message","name":"%s","totalsteps":"%v"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.step"}`
expected = fmt.Sprintf(expected, secondStep, Reg.totalSteps())
expected += "\n"
buf := bytes.NewBuffer([]byte{})
outputFile = buf
defer func() { outputFile = os.Stdout }()
getUUID = func() string {
return "random-id"
}
PrintStep("message")
actual := buf.String()
if actual != expected {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", expected, actual)
}
}
......@@ -155,7 +155,7 @@ func applyStyle(style StyleEnum, useColor bool, format string) string {
}
// Similar to CSS styles, if no style matches, output an unformatted string.
if !ok {
if !ok || JSON {
return format
}
......
......@@ -83,6 +83,7 @@ minikube start [flags]
--nfs-shares-root string Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only) (default "/nfsshares")
--no-vtx-check Disable checking for the availability of hardware virtualization before the vm is started (virtualbox driver only)
-n, --nodes int The number of nodes to spin up. Defaults to 1. (default 1)
--output string Format to print stdout in. Options include: [text,json] (default "text")
--preload If set, download tarball of preloaded images if available to improve start time. Defaults to true. (default true)
--registry-mirror strings Registry mirrors to pass to the Docker daemon
--service-cluster-ip-range string The CIDR to be used for service cluster IPs. (default "10.96.0.0/12")
......
......@@ -49,7 +49,7 @@ multinode-demo-m02 Ready <none> 33s v1.18.2
- You can also check the status of your nodes:
```
$ minikube status
$ minikube status -p multinode-demo
multinode-demo
type: Control Plane
host: Running
......
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package integration
import (
"context"
"encoding/json"
"os/exec"
"strings"
"testing"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
func TestJSONOutput(t *testing.T) {
if NoneDriver() || DockerDriver() {
t.Skipf("skipping: test drivers once all JSON output is enabled")
}
profile := UniqueProfileName("json-output")
ctx, cancel := context.WithTimeout(context.Background(), Minutes(40))
defer Cleanup(t, profile, cancel)
startArgs := []string{"start", "-p", profile, "--memory=2200", "--output=json", "--wait=true"}
startArgs = append(startArgs, StartArgs()...)
rr, err := Run(t, exec.CommandContext(ctx, Target(), startArgs...))
if err != nil {
t.Errorf("failed to clean up: args %q: %v", rr.Command(), err)
}
type validateJSONOutputFunc func(context.Context, *testing.T, *RunResult)
t.Run("serial", func(t *testing.T) {
serialTests := []struct {
name string
validator validateJSONOutputFunc
}{
{"CloudEvents", validateCloudEvents},
}
for _, stc := range serialTests {
t.Run(stc.name, func(t *testing.T) {
stc.validator(ctx, t, rr)
})
}
})
}
// make sure all output can be marshaled as a cloud event
func validateCloudEvents(ctx context.Context, t *testing.T, rr *RunResult) {
stdout := strings.Split(rr.Stdout.String(), "\n")
for _, s := range stdout {
if s == "" {
continue
}
event := cloudevents.NewEvent()
if err := json.Unmarshal([]byte(s), &event); err != nil {
t.Fatalf("unable to unmarshal output: %v\n%s", err, s)
}
}
}
......@@ -90,7 +90,16 @@ func TestNetworkPlugins(t *testing.T) {
}
if !t.Failed() {
t.Run("KubeletFlags", func(t *testing.T) {
rr, err := Run(t, exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "pgrep -a kubelet"))
var rr *RunResult
var err error
// none does not support 'minikube ssh'
if NoneDriver() {
rr, err = Run(t, exec.CommandContext(ctx, "pgrep", "-a", "kubelet"))
} else {
rr, err = Run(t, exec.CommandContext(ctx, Target(), "ssh", "-p", profile, "pgrep -a kubelet"))
}
if err != nil {
t.Fatalf("ssh failed: %v", err)
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册