diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index ddc12c4b05..3f1034070b 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -24,8 +24,10 @@ import ( "github.com/docker/machine/libmachine" "github.com/spf13/cobra" + cfg "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/kubeconfig" ) // startCmd represents the start command @@ -68,11 +70,20 @@ func runStart(cmd *cobra.Command, args []string) { kubeHost = strings.Replace(kubeHost, "tcp://", "https://", -1) kubeHost = strings.Replace(kubeHost, ":2376", ":443", -1) fmt.Printf("Kubernetes is available at %s.\n", kubeHost) - fmt.Println("Run this command to use the cluster: ") - fmt.Printf("kubectl config set-cluster minikube --server=%s --certificate-authority=$HOME/.minikube/apiserver.crt\n", kubeHost) - fmt.Println("kubectl config set-credentials minikube --client-certificate=$HOME/.minikube/apiserver.crt --client-key=$HOME/.minikube/apiserver.key") - fmt.Println("kubectl config set-context minikube --cluster=minikube --user=minikube") - fmt.Println("kubectl config use-context minikube") + + // setup kubeconfig + name := constants.MinikubeContext + certAuth := constants.MakeMiniPath("apiserver.crt") + clientCert := constants.MakeMiniPath("apiserver.crt") + clientKey := constants.MakeMiniPath("apiserver.key") + active, err := setupKubeconfig(name, kubeHost, certAuth, clientCert, clientKey) + if err != nil { + log.Println("Error setting up kubeconfig: ", err) + os.Exit(1) + } else if !active { + fmt.Println("Run this command to use the cluster: ") + fmt.Printf("kubectl config use-context %s\n", name) + } if err := cluster.GetCreds(host); err != nil { log.Println("Error configuring authentication: ", err) @@ -80,6 +91,52 @@ func runStart(cmd *cobra.Command, args []string) { } } +// setupKubeconfig reads config from disk, adds the minikube settings and writes it back. +// activeContext is true when minikube is the CurrentContext +// If no CurrentContext is set, the given name will be used. +func setupKubeconfig(name, server, certAuth, cliCert, cliKey string) (activeContext bool, err error) { + configFile := constants.KubeconfigPath + + // read existing config or create new if does not exist + config, err := kubeconfig.ReadConfigOrNew(configFile) + if err != nil { + return false, err + } + + clusterName := name + cluster := cfg.NewCluster() + cluster.Server = server + cluster.CertificateAuthority = certAuth + config.Clusters[clusterName] = cluster + + // user + userName := name + user := cfg.NewAuthInfo() + user.ClientCertificate = cliCert + user.ClientKey = cliKey + config.AuthInfos[userName] = user + + // context + contextName := name + context := cfg.NewContext() + context.Cluster = clusterName + context.AuthInfo = userName + config.Contexts[contextName] = context + + // set current context to minikube if unset + if len(config.CurrentContext) == 0 { + config.CurrentContext = contextName + } + + // write back to disk + if err := kubeconfig.WriteConfig(config, configFile); err != nil { + return false, err + } + + // activeContext if current matches name + return name == config.CurrentContext, nil +} + func init() { startCmd.Flags().StringVarP(&localkubeURL, "localkube-url", "", "https://storage.googleapis.com/tinykube/localkube", "Location of the localkube binary") startCmd.Flags().MarkHidden("localkube-url") diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index 05bf07a8ef..795878fb96 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -27,6 +27,13 @@ const MachineName = "minikubeVM" // Fix for windows var Minipath = filepath.Join(os.Getenv("HOME"), ".minikube") +// TODO: Fix for windows +// KubeconfigPath is the path to the Kubernetes client config +var KubeconfigPath = filepath.Join(os.Getenv("HOME"), ".kube", "config") + +// MinikubeContext is the kubeconfig context name used for minikube +const MinikubeContext = "minikube" + // MakeMiniPath is a utility to calculate a relative path to our directory. func MakeMiniPath(fileName string) string { return filepath.Join(Minipath, fileName) diff --git a/pkg/minikube/kubeconfig/config.go b/pkg/minikube/kubeconfig/config.go index bea9acbce2..bf92ddd6a0 100644 --- a/pkg/minikube/kubeconfig/config.go +++ b/pkg/minikube/kubeconfig/config.go @@ -11,11 +11,14 @@ import ( "k8s.io/kubernetes/pkg/runtime" ) -// ReadConfig retrieves Kubernetes client configuration from a file. -func ReadConfig(filename string) (*api.Config, error) { +// ReadConfigOrNew retrieves Kubernetes client configuration from a file. +// If no files exists, an empty configuration is returned. +func ReadConfigOrNew(filename string) (*api.Config, error) { data, err := ioutil.ReadFile(filename) - if err != nil { - return nil, fmt.Errorf("could not read config: %v", err) + if os.IsNotExist(err) { + return api.NewConfig(), nil + } else if err != nil { + return nil, err } // decode config, empty if no bytes diff --git a/pkg/minikube/kubeconfig/config_test.go b/pkg/minikube/kubeconfig/config_test.go index a49e873f3e..60fa21ef59 100644 --- a/pkg/minikube/kubeconfig/config_test.go +++ b/pkg/minikube/kubeconfig/config_test.go @@ -13,7 +13,7 @@ func TestEmptyConfig(t *testing.T) { tmp := tempFile(t, []byte{}) defer os.Remove(tmp) - cfg, err := ReadConfig(tmp) + cfg, err := ReadConfigOrNew(tmp) if err != nil { t.Fatalf("could not read config: %v", err) } @@ -48,7 +48,7 @@ func TestNewConfig(t *testing.T) { t.Fatal(err) } - actual, err := ReadConfig(filename) + actual, err := ReadConfigOrNew(filename) if err != nil { t.Fatal(err) }