From 48fd6ecb65acddad2c16f12eabd20a99fa992441 Mon Sep 17 00:00:00 2001 From: Matt Rickard Date: Thu, 24 Aug 2017 15:17:57 -0700 Subject: [PATCH] Add localkube as a bootstrapper Introduce the --bootstraper or -b flag that toggles the cluster bootstrapper. The default bootstrapper (and only bootstrapper at this time) is localkube. The bootstrapper interface contains all the functions needed to bootstrap a kubernetes cluster. It ingests a KubernetesConfig, which contains all the configuration for the cluster, as well as a CommandRunner, which is the interface upon which it will run commands and transfer files to possibly remotely set up the cluster. This moves the localkube functions out of cluster.go into their own package at k8s.io/minikube/pkg/minikube/bootstrapper/localkube. --- cmd/minikube/cmd/config/config.go | 6 + cmd/minikube/cmd/logs.go | 15 +- cmd/minikube/cmd/root.go | 24 +- cmd/minikube/cmd/start.go | 45 ++- cmd/minikube/cmd/status.go | 14 +- pkg/minikube/bootstrapper/bootstrapper.go | 46 +++ pkg/minikube/bootstrapper/certs.go | 110 ++++++++ pkg/minikube/bootstrapper/certs_test.go | 52 ++++ .../{runner.go => command_runner.go} | 0 .../localkube}/commands.go | 47 +--- .../localkube}/commands_test.go | 7 +- .../bootstrapper/localkube/localkube.go | 148 ++++++++++ .../localkube}/localkube_caching.go | 12 +- .../bootstrapper/localkube/localkube_test.go | 265 ++++++++++++++++++ pkg/minikube/cluster/cluster.go | 190 +++---------- pkg/minikube/cluster/cluster_test.go | 263 ----------------- pkg/minikube/cluster/credentials.go | 43 --- pkg/minikube/cluster/types.go | 19 +- pkg/minikube/constants/constants.go | 14 +- 19 files changed, 761 insertions(+), 559 deletions(-) create mode 100644 pkg/minikube/bootstrapper/bootstrapper.go create mode 100644 pkg/minikube/bootstrapper/certs.go create mode 100644 pkg/minikube/bootstrapper/certs_test.go rename pkg/minikube/bootstrapper/{runner.go => command_runner.go} (100%) rename pkg/minikube/{cluster => bootstrapper/localkube}/commands.go (82%) rename pkg/minikube/{cluster => bootstrapper/localkube}/commands_test.go (91%) create mode 100644 pkg/minikube/bootstrapper/localkube/localkube.go rename pkg/minikube/{cluster => bootstrapper/localkube}/localkube_caching.go (89%) create mode 100644 pkg/minikube/bootstrapper/localkube/localkube_test.go delete mode 100644 pkg/minikube/cluster/credentials.go diff --git a/cmd/minikube/cmd/config/config.go b/cmd/minikube/cmd/config/config.go index 1c2d9dc08d..9f4e2d78c9 100644 --- a/cmd/minikube/cmd/config/config.go +++ b/cmd/minikube/cmd/config/config.go @@ -29,6 +29,8 @@ import ( "k8s.io/minikube/pkg/minikube/constants" ) +const Bootstrapper = "bootstrapper" + type setFn func(string, string) error type Setting struct { @@ -113,6 +115,10 @@ var settings = []Setting{ name: config.MachineProfile, set: SetString, }, + { + name: Bootstrapper, + set: SetString, //TODO(r2d4): more validation here? + }, { name: "dashboard", set: SetBool, diff --git a/cmd/minikube/cmd/logs.go b/cmd/minikube/cmd/logs.go index c28dc7f5e9..494b9329bc 100644 --- a/cmd/minikube/cmd/logs.go +++ b/cmd/minikube/cmd/logs.go @@ -23,9 +23,9 @@ import ( "github.com/golang/glog" "github.com/spf13/cobra" + "github.com/spf13/viper" + cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" cmdUtil "k8s.io/minikube/cmd/util" - "k8s.io/minikube/pkg/minikube/cluster" - "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/machine" ) @@ -45,15 +45,12 @@ var logsCmd = &cobra.Command{ os.Exit(1) } defer api.Close() - h, err := api.Load(config.GetMachineName()) + clusterBootstrapper, err := GetClusterBootstrapper(api, viper.GetString(cmdcfg.Bootstrapper)) if err != nil { - glog.Errorln("Error getting host") + glog.Exitf("Error getting cluster bootstrapper: %s", err) } - cmdRunner, err := machine.GetCommandRunner(h) - if err != nil { - glog.Errorln("Error getting command runner interface") - } - s, err := cluster.GetHostLogs(cmdRunner, follow) + + s, err := clusterBootstrapper.GetClusterLogs(follow) if err != nil { log.Println("Error getting machine logs:", err) cmdUtil.MaybeReportErrorAndExit(err) diff --git a/cmd/minikube/cmd/root.go b/cmd/minikube/cmd/root.go index 0d5e72a9be..e63842c997 100755 --- a/cmd/minikube/cmd/root.go +++ b/cmd/minikube/cmd/root.go @@ -18,18 +18,23 @@ package cmd import ( goflag "flag" + "fmt" "io/ioutil" "os" "runtime" "strings" + "github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/log" "github.com/golang/glog" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" configCmd "k8s.io/minikube/cmd/minikube/cmd/config" "k8s.io/minikube/cmd/util" + "k8s.io/minikube/pkg/minikube/bootstrapper" + "k8s.io/minikube/pkg/minikube/bootstrapper/localkube" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/notify" @@ -98,7 +103,6 @@ func Execute() { if err := RootCmd.Execute(); err != nil { os.Exit(1) } - } // Handle config values for flags used in external packages (e.g. glog) @@ -121,6 +125,7 @@ func setFlagsUsingViper() { func init() { RootCmd.PersistentFlags().StringP(config.MachineProfile, "p", constants.DefaultMachineName, `The name of the minikube VM being used. This can be modified to allow for multiple minikube instances to be run independently`) + RootCmd.PersistentFlags().StringP(configCmd.Bootstrapper, "b", constants.DefaultClusterBootstrapper, "The name of the cluster bootstrapper that will set up the kubernetes cluster.") RootCmd.AddCommand(configCmd.ConfigCmd) RootCmd.AddCommand(configCmd.AddonsCmd) RootCmd.AddCommand(configCmd.ProfileCmd) @@ -157,3 +162,20 @@ func setupViper() { viper.SetDefault(config.WantKubectlDownloadMsg, true) setFlagsUsingViper() } + +// GetClusterBootstrapper returns a new bootstrapper for the cluster +func GetClusterBootstrapper(api libmachine.API, bootstrapperName string) (bootstrapper.Bootstrapper, error) { + var b bootstrapper.Bootstrapper + var err error + switch bootstrapperName { + case bootstrapper.BootstrapperTypeLocalkube: + b, err = localkube.NewLocalkubeBootstrapper(api) + if err != nil { + return nil, errors.Wrap(err, "getting localkube bootstrapper") + } + default: + return nil, fmt.Errorf("Unknown bootstrapper: %s", bootstrapperName) + } + + return b, nil +} diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 05bc86eaa6..245f249261 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -33,7 +33,9 @@ import ( "github.com/golang/glog" "github.com/spf13/cobra" "github.com/spf13/viper" + cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" cmdUtil "k8s.io/minikube/cmd/util" + "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/cluster" cfg "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" @@ -92,6 +94,11 @@ func runStart(cmd *cobra.Command, args []string) { } defer api.Close() + exists, err := api.Exists(cfg.GetMachineName()) + if err != nil { + glog.Exitf("checking if machine exists: %s", err) + } + diskSize := viper.GetString(humanReadableDiskSize) diskSizeMB := calculateDiskSizeInMB(diskSize) @@ -171,9 +178,10 @@ func runStart(cmd *cobra.Command, args []string) { } } - kubernetesConfig := cluster.KubernetesConfig{ + kubernetesConfig := bootstrapper.KubernetesConfig{ KubernetesVersion: selectedKubernetesVersion, NodeIP: ip, + NodeName: cfg.GetMachineName(), APIServerName: viper.GetString(apiServerName), DNSDomain: viper.GetString(dnsDomain), FeatureGates: viper.GetString(featureGates), @@ -182,6 +190,11 @@ func runStart(cmd *cobra.Command, args []string) { ExtraOptions: extraOptions, } + clusterBootstrapper, err := GetClusterBootstrapper(api, viper.GetString(cmdcfg.Bootstrapper)) + if err != nil { + glog.Exitf("Error getting cluster bootstrapper: %s", err) + } + // Write profile cluster configuration to file clusterConfig := cluster.Config{ MachineConfig: config, @@ -192,30 +205,18 @@ func runStart(cmd *cobra.Command, args []string) { glog.Errorln("Error saving profile cluster configuration: ", err) } - cmdRunner, err := machine.GetCommandRunner(host) - if err != nil { - glog.Errorln("Error getting command runner interface") - } - fmt.Println("Moving files into cluster...") - if err := cluster.UpdateCluster(cmdRunner, kubernetesConfig); err != nil { + if err := clusterBootstrapper.UpdateCluster(kubernetesConfig); err != nil { glog.Errorln("Error updating cluster: ", err) cmdUtil.MaybeReportErrorAndExit(err) } fmt.Println("Setting up certs...") - if err := cluster.SetupCerts(cmdRunner, kubernetesConfig); err != nil { + if err := clusterBootstrapper.SetupCerts(kubernetesConfig); err != nil { glog.Errorln("Error configuring authentication: ", err) cmdUtil.MaybeReportErrorAndExit(err) } - fmt.Println("Starting cluster components...") - - if err := cluster.StartCluster(cmdRunner, kubernetesConfig); err != nil { - glog.Errorln("Error starting cluster: ", err) - cmdUtil.MaybeReportErrorAndExit(err) - } - fmt.Println("Connecting to cluster...") kubeHost, err := host.Driver.GetURL() if err != nil { @@ -244,6 +245,20 @@ func runStart(cmd *cobra.Command, args []string) { cmdUtil.MaybeReportErrorAndExit(err) } + fmt.Println("Starting cluster components...") + + if !exists { + if err := clusterBootstrapper.StartCluster(kubernetesConfig); err != nil { + glog.Errorln("Error starting cluster: ", err) + cmdUtil.MaybeReportErrorAndExit(err) + } + } else { + if err := clusterBootstrapper.RestartCluster(kubernetesConfig); err != nil { + glog.Errorln("Error restarting cluster: ", err) + cmdUtil.MaybeReportErrorAndExit(err) + } + } + // start 9p server mount if viper.GetBool(createMount) { fmt.Printf("Setting up hostmount on %s...\n", viper.GetString(mountString)) diff --git a/cmd/minikube/cmd/status.go b/cmd/minikube/cmd/status.go index 534071f501..5e86cce3b2 100755 --- a/cmd/minikube/cmd/status.go +++ b/cmd/minikube/cmd/status.go @@ -24,6 +24,8 @@ import ( "github.com/docker/machine/libmachine/state" "github.com/golang/glog" "github.com/spf13/cobra" + "github.com/spf13/viper" + cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" cmdUtil "k8s.io/minikube/cmd/util" "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/config" @@ -62,18 +64,14 @@ var statusCmd = &cobra.Command{ cs := state.None.String() ks := state.None.String() if ms == state.Running.String() { - h, err := api.Load(config.GetMachineName()) + clusterBootstrapper, err := GetClusterBootstrapper(api, viper.GetString(cmdcfg.Bootstrapper)) if err != nil { - glog.Exitln("Error getting host") - } - cmdRunner, err := machine.GetCommandRunner(h) - if err != nil { - glog.Errorln("Error getting command runner interface") + glog.Errorf("Error getting cluster bootstrapper: %s", err) cmdUtil.MaybeReportErrorAndExit(err) } - cs, err = cluster.GetLocalkubeStatus(cmdRunner) + cs, err = clusterBootstrapper.GetClusterStatus() if err != nil { - glog.Errorln("Error localkube status:", err) + glog.Errorln("Error cluster status:", err) cmdUtil.MaybeReportErrorAndExit(err) } ip, err := cluster.GetHostDriverIP(api) diff --git a/pkg/minikube/bootstrapper/bootstrapper.go b/pkg/minikube/bootstrapper/bootstrapper.go new file mode 100644 index 0000000000..57fbfe8c9c --- /dev/null +++ b/pkg/minikube/bootstrapper/bootstrapper.go @@ -0,0 +1,46 @@ +/* +Copyright 2016 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 bootstrapper + +import "k8s.io/minikube/pkg/util" + +// Bootstrapper contains all the methods needed to bootstrap a kubernetes cluster +type Bootstrapper interface { + StartCluster(KubernetesConfig) error + UpdateCluster(KubernetesConfig) error + RestartCluster(KubernetesConfig) error + GetClusterLogs(follow bool) (string, error) + SetupCerts(cfg KubernetesConfig) error + GetClusterStatus() (string, error) +} + +// KubernetesConfig contains the parameters used to configure the VM Kubernetes. +type KubernetesConfig struct { + KubernetesVersion string + NodeIP string + NodeName string + APIServerName string + DNSDomain string + ContainerRuntime string + NetworkPlugin string + FeatureGates string + ExtraOptions util.ExtraOptionSlice +} + +const ( + BootstrapperTypeLocalkube = "localkube" +) diff --git a/pkg/minikube/bootstrapper/certs.go b/pkg/minikube/bootstrapper/certs.go new file mode 100644 index 0000000000..ac09a75388 --- /dev/null +++ b/pkg/minikube/bootstrapper/certs.go @@ -0,0 +1,110 @@ +/* +Copyright 2016 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 bootstrapper + +import ( + "net" + "path/filepath" + "strings" + + "github.com/golang/glog" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/clientcmd/api" + "k8s.io/client-go/tools/clientcmd/api/latest" + "k8s.io/minikube/pkg/minikube/assets" + "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/util" + "k8s.io/minikube/pkg/util/kubeconfig" +) + +var ( + certs = []string{"ca.crt", "ca.key", "apiserver.crt", "apiserver.key"} + // This is the internalIP , the API server and other components communicate on. + internalIP = net.ParseIP(util.DefaultServiceClusterIP) +) + +// SetupCerts gets the generated credentials required to talk to the APIServer. +func SetupCerts(cmd CommandRunner, k8s KubernetesConfig) error { + localPath := constants.GetMinipath() + glog.Infoln("Setting up certificates for IP: %s", k8s.NodeIP) + + ip := net.ParseIP(k8s.NodeIP) + caCert := filepath.Join(localPath, "ca.crt") + caKey := filepath.Join(localPath, "ca.key") + publicPath := filepath.Join(localPath, "apiserver.crt") + privatePath := filepath.Join(localPath, "apiserver.key") + if err := generateCerts(caCert, caKey, publicPath, privatePath, ip, k8s.APIServerName, k8s.DNSDomain); err != nil { + return errors.Wrap(err, "Error generating certs") + } + + copyableFiles := []assets.CopyableFile{} + + for _, cert := range certs { + p := filepath.Join(localPath, cert) + perms := "0644" + if strings.HasSuffix(cert, ".key") { + perms = "0600" + } + certFile, err := assets.NewFileAsset(p, util.DefaultCertPath, cert, perms) + if err != nil { + return err + } + copyableFiles = append(copyableFiles, certFile) + } + + kubeCfgSetup := &kubeconfig.KubeConfigSetup{ + ClusterName: k8s.NodeName, + ClusterServerAddress: "https://localhost:8443", + ClientCertificate: filepath.Join(util.DefaultCertPath, "apiserver.crt"), + ClientKey: filepath.Join(util.DefaultCertPath, "apiserver.key"), + CertificateAuthority: filepath.Join(util.DefaultCertPath, "ca.crt"), + KeepContext: false, + } + + kubeCfg := api.NewConfig() + kubeconfig.PopulateKubeConfig(kubeCfgSetup, kubeCfg) + data, err := runtime.Encode(latest.Codec, kubeCfg) + if err != nil { + return errors.Wrap(err, "encoding kubeconfig") + } + + kubeCfgFile := assets.NewMemoryAsset(data, + util.DefaultLocalkubeDirectory, "kubeconfig", "0644") + copyableFiles = append(copyableFiles, kubeCfgFile) + + for _, f := range copyableFiles { + if err := cmd.Copy(f); err != nil { + return err + } + } + return nil +} + +func generateCerts(caCert, caKey, pub, priv string, ip net.IP, name string, dnsDomain string) error { + if !(util.CanReadFile(caCert) && util.CanReadFile(caKey)) { + if err := util.GenerateCACert(caCert, caKey, name); err != nil { + return errors.Wrap(err, "Error generating certificate") + } + } + + ips := []net.IP{ip, internalIP} + if err := util.GenerateSignedCert(pub, priv, ips, util.GetAlternateDNS(dnsDomain), caCert, caKey); err != nil { + return errors.Wrap(err, "Error generating signed cert") + } + return nil +} diff --git a/pkg/minikube/bootstrapper/certs_test.go b/pkg/minikube/bootstrapper/certs_test.go new file mode 100644 index 0000000000..7c5f2b417f --- /dev/null +++ b/pkg/minikube/bootstrapper/certs_test.go @@ -0,0 +1,52 @@ +/* +Copyright 2016 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 bootstrapper + +import ( + "os" + "path/filepath" + "testing" + + "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/tests" +) + +func TestSetupCerts(t *testing.T) { + tempDir := tests.MakeTempDir() + defer os.RemoveAll(tempDir) + + f := NewFakeCommandRunner() + k8s := KubernetesConfig{ + APIServerName: constants.APIServerName, + DNSDomain: constants.ClusterDNSDomain, + } + + var filesToBeTransferred []string + for _, cert := range certs { + filesToBeTransferred = append(filesToBeTransferred, filepath.Join(constants.GetMinipath(), cert)) + } + + if err := SetupCerts(f, k8s); err != nil { + t.Fatalf("Error starting cluster: %s", err) + } + for _, cert := range filesToBeTransferred { + _, err := f.GetFileToContents(cert) + if err != nil { + t.Errorf("Cert not generated: %s", cert) + } + } +} diff --git a/pkg/minikube/bootstrapper/runner.go b/pkg/minikube/bootstrapper/command_runner.go similarity index 100% rename from pkg/minikube/bootstrapper/runner.go rename to pkg/minikube/bootstrapper/command_runner.go diff --git a/pkg/minikube/cluster/commands.go b/pkg/minikube/bootstrapper/localkube/commands.go similarity index 82% rename from pkg/minikube/cluster/commands.go rename to pkg/minikube/bootstrapper/localkube/commands.go index 3eb6b4cf53..69f81a87f7 100644 --- a/pkg/minikube/cluster/commands.go +++ b/pkg/minikube/bootstrapper/localkube/commands.go @@ -14,17 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cluster +package localkube import ( "bytes" gflag "flag" "fmt" - "net" "strings" "text/template" + "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/constants" ) @@ -65,7 +65,7 @@ else fi ` -func GetStartCommand(kubernetesConfig KubernetesConfig) (string, error) { +func GetStartCommand(kubernetesConfig bootstrapper.KubernetesConfig) (string, error) { localkubeStartCommand, err := GenLocalkubeStartCmd(kubernetesConfig) if err != nil { return "", err @@ -93,7 +93,7 @@ func GetStartCommand(kubernetesConfig KubernetesConfig) (string, error) { return buf.String(), nil } -func GetStartCommandNoSystemd(kubernetesConfig KubernetesConfig, localkubeStartCmd string) (string, error) { +func GetStartCommandNoSystemd(kubernetesConfig bootstrapper.KubernetesConfig, localkubeStartCmd string) (string, error) { t := template.Must(template.New("startCommand").Parse(startCommandNoSystemdTemplate)) buf := bytes.Buffer{} data := struct { @@ -113,7 +113,7 @@ func GetStartCommandNoSystemd(kubernetesConfig KubernetesConfig, localkubeStartC return buf.String(), nil } -func GetStartCommandSystemd(kubernetesConfig KubernetesConfig, localkubeStartCmd string) (string, error) { +func GetStartCommandSystemd(kubernetesConfig bootstrapper.KubernetesConfig, localkubeStartCmd string) (string, error) { t, err := template.New("localkubeConfig").Parse(localkubeSystemdTmpl) if err != nil { return "", err @@ -131,7 +131,7 @@ func GetStartCommandSystemd(kubernetesConfig KubernetesConfig, localkubeStartCmd constants.LocalkubeServicePath), nil } -func GenLocalkubeStartCmd(kubernetesConfig KubernetesConfig) (string, error) { +func GenLocalkubeStartCmd(kubernetesConfig bootstrapper.KubernetesConfig) (string, error) { flagVals := make([]string, len(constants.LogFlags)) for _, logFlag := range constants.LogFlags { if logVal := gflag.Lookup(logFlag); logVal != nil && logVal.Value.String() != logVal.DefValue { @@ -226,38 +226,3 @@ else fi fi `, constants.LocalkubePIDPath) - -func GetMountCleanupCommand(path string) string { - return fmt.Sprintf("sudo umount %s;", path) -} - -var mountTemplate = ` -sudo mkdir -p {{.Path}} || true; -sudo mount -t 9p -o trans=tcp,port={{.Port}},dfltuid={{.UID}},dfltgid={{.GID}},version={{.Version}},msize={{.Msize}} {{.IP}} {{.Path}}; -sudo chmod 775 {{.Path}};` - -func GetMountCommand(ip net.IP, path, port, mountVersion string, uid, gid, msize int) (string, error) { - t := template.Must(template.New("mountCommand").Parse(mountTemplate)) - buf := bytes.Buffer{} - data := struct { - IP string - Path string - Port string - Version string - UID int - GID int - Msize int - }{ - IP: ip.String(), - Path: path, - Port: port, - Version: mountVersion, - UID: uid, - GID: gid, - Msize: msize, - } - if err := t.Execute(&buf, data); err != nil { - return "", err - } - return buf.String(), nil -} diff --git a/pkg/minikube/cluster/commands_test.go b/pkg/minikube/bootstrapper/localkube/commands_test.go similarity index 91% rename from pkg/minikube/cluster/commands_test.go rename to pkg/minikube/bootstrapper/localkube/commands_test.go index 5ae8ea286e..5b19c10368 100644 --- a/pkg/minikube/cluster/commands_test.go +++ b/pkg/minikube/bootstrapper/localkube/commands_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cluster +package localkube import ( gflag "flag" @@ -22,6 +22,7 @@ import ( "strings" "testing" + "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/util" ) @@ -31,7 +32,7 @@ func TestGetStartCommandCustomValues(t *testing.T) { "vmodule": "cluster*=5", } flagMapToSetFlags(flagMap) - startCommand, err := GetStartCommand(KubernetesConfig{}) + startCommand, err := GetStartCommand(bootstrapper.KubernetesConfig{}) if err != nil { t.Fatalf("Error generating start command: %s", err) } @@ -46,7 +47,7 @@ func TestGetStartCommandCustomValues(t *testing.T) { } func TestGetStartCommandExtraOptions(t *testing.T) { - k := KubernetesConfig{ + k := bootstrapper.KubernetesConfig{ ExtraOptions: util.ExtraOptionSlice{ util.ExtraOption{Component: "a", Key: "b", Value: "c"}, util.ExtraOption{Component: "d", Key: "e.f", Value: "g"}, diff --git a/pkg/minikube/bootstrapper/localkube/localkube.go b/pkg/minikube/bootstrapper/localkube/localkube.go new file mode 100644 index 0000000000..c7032a2842 --- /dev/null +++ b/pkg/minikube/bootstrapper/localkube/localkube.go @@ -0,0 +1,148 @@ +/* +Copyright 2016 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 localkube + +import ( + "fmt" + "strings" + + "k8s.io/minikube/pkg/minikube/assets" + "k8s.io/minikube/pkg/minikube/bootstrapper" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/sshutil" + + "github.com/docker/machine/libmachine" + "github.com/docker/machine/libmachine/state" + "github.com/pkg/errors" +) + +type LocalkubeBootstrapper struct { + cmd bootstrapper.CommandRunner +} + +func NewLocalkubeBootstrapper(api libmachine.API) (*LocalkubeBootstrapper, error) { + h, err := api.Load(config.GetMachineName()) + if err != nil { + return nil, errors.Wrap(err, "getting api client") + } + var cmd bootstrapper.CommandRunner + // The none driver executes commands directly on the host + if h.Driver.DriverName() == constants.DriverNone { + cmd = &bootstrapper.ExecRunner{} + } else { + client, err := sshutil.NewSSHClient(h.Driver) + if err != nil { + return nil, errors.Wrap(err, "getting ssh client") + } + cmd = bootstrapper.NewSSHRunner(client) + } + return &LocalkubeBootstrapper{ + cmd: cmd, + }, nil +} + +// GetClusterLogs If follow is specified, it will tail the logs +func (lk *LocalkubeBootstrapper) GetClusterLogs(follow bool) (string, error) { + logsCommand, err := GetLogsCommand(follow) + if err != nil { + return "", errors.Wrap(err, "Error getting logs command") + } + + logs, err := lk.cmd.CombinedOutput(logsCommand) + if err != nil { + return "", errors.Wrap(err, "getting cluster logs") + } + + return logs, nil +} + +// GetClusterStatus gets the status of localkube from the host VM. +func (lk *LocalkubeBootstrapper) GetClusterStatus() (string, error) { + s, err := lk.cmd.CombinedOutput(localkubeStatusCommand) + if err != nil { + return "", err + } + s = strings.TrimSpace(s) + if state.Running.String() == s { + return state.Running.String(), nil + } else if state.Stopped.String() == s { + return state.Stopped.String(), nil + } else { + return "", fmt.Errorf("Error: Unrecognize output from GetLocalkubeStatus: %s", s) + } +} + +// StartCluster starts a k8s cluster on the specified Host. +func (lk *LocalkubeBootstrapper) StartCluster(kubernetesConfig bootstrapper.KubernetesConfig) error { + startCommand, err := GetStartCommand(kubernetesConfig) + if err != nil { + return errors.Wrapf(err, "Error generating start command: %s", err) + } + err = lk.cmd.Run(startCommand) //needs to be sudo for none driver + if err != nil { + return errors.Wrapf(err, "Error running ssh command: %s", startCommand) + } + return nil +} + +func (lk *LocalkubeBootstrapper) RestartCluster(kubernetesConfig bootstrapper.KubernetesConfig) error { + return lk.StartCluster(kubernetesConfig) +} + +func (lk *LocalkubeBootstrapper) UpdateCluster(config bootstrapper.KubernetesConfig) error { + copyableFiles := []assets.CopyableFile{} + var localkubeFile assets.CopyableFile + var err error + + //add url/file/bundled localkube to file list + if localkubeURIWasSpecified(config) && config.KubernetesVersion != constants.DefaultKubernetesVersion { + lCacher := localkubeCacher{config} + localkubeFile, err = lCacher.fetchLocalkubeFromURI() + if err != nil { + return errors.Wrap(err, "Error updating localkube from uri") + } + } else { + localkubeFile = assets.NewBinDataAsset("out/localkube", "/usr/local/bin", "localkube", "0777") + } + copyableFiles = append(copyableFiles, localkubeFile) + + // add addons to file list + // custom addons + assets.AddMinikubeAddonsDirToAssets(©ableFiles) + // bundled addons + for _, addonBundle := range assets.Addons { + if isEnabled, err := addonBundle.IsEnabled(); err == nil && isEnabled { + for _, addon := range addonBundle.Assets { + copyableFiles = append(copyableFiles, addon) + } + } else if err != nil { + return err + } + } + + for _, f := range copyableFiles { + if err := lk.cmd.Copy(f); err != nil { + return err + } + } + return nil +} + +func (lk *LocalkubeBootstrapper) SetupCerts(k8s bootstrapper.KubernetesConfig) error { + return bootstrapper.SetupCerts(lk.cmd, k8s) +} diff --git a/pkg/minikube/cluster/localkube_caching.go b/pkg/minikube/bootstrapper/localkube/localkube_caching.go similarity index 89% rename from pkg/minikube/cluster/localkube_caching.go rename to pkg/minikube/bootstrapper/localkube/localkube_caching.go index fa52c17fb2..f6997515fd 100644 --- a/pkg/minikube/cluster/localkube_caching.go +++ b/pkg/minikube/bootstrapper/localkube/localkube_caching.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cluster +package localkube import ( "fmt" @@ -27,13 +27,14 @@ import ( "github.com/pkg/errors" "k8s.io/minikube/pkg/minikube/assets" + "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/util" ) // localkubeCacher is a struct with methods designed for caching localkube type localkubeCacher struct { - k8sConf KubernetesConfig + k8sConf bootstrapper.KubernetesConfig } func (l *localkubeCacher) getLocalkubeCacheFilepath() string { @@ -41,6 +42,11 @@ func (l *localkubeCacher) getLocalkubeCacheFilepath() string { filepath.Base(url.QueryEscape("localkube-"+l.k8sConf.KubernetesVersion))) } +func localkubeURIWasSpecified(config bootstrapper.KubernetesConfig) bool { + // see if flag is different than default -> it was passed by user + return config.KubernetesVersion != constants.DefaultKubernetesVersion +} + func (l *localkubeCacher) isLocalkubeCached() bool { if _, err := os.Stat(l.getLocalkubeCacheFilepath()); os.IsNotExist(err) { return false @@ -71,7 +77,7 @@ func (l *localkubeCacher) fetchLocalkubeFromURI() (assets.CopyableFile, error) { if err != nil { return nil, errors.Wrap(err, "Error parsing --kubernetes-version url") } - if urlObj.Scheme == fileScheme { + if urlObj.Scheme == constants.FileScheme { return l.genLocalkubeFileFromFile() } return l.genLocalkubeFileFromURL() diff --git a/pkg/minikube/bootstrapper/localkube/localkube_test.go b/pkg/minikube/bootstrapper/localkube/localkube_test.go new file mode 100644 index 0000000000..852ff8966d --- /dev/null +++ b/pkg/minikube/bootstrapper/localkube/localkube_test.go @@ -0,0 +1,265 @@ +/* +Copyright 2016 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 localkube + +import ( + "os" + "testing" + + "k8s.io/minikube/pkg/minikube/bootstrapper" + "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/minikube/tests" +) + +func TestStartCluster(t *testing.T) { + expectedStartCmd, err := GetStartCommand(bootstrapper.KubernetesConfig{}) + if err != nil { + t.Fatalf("generating start command: %s", err) + } + + cases := []struct { + description string + startCmd string + }{ + { + description: "start cluster success", + startCmd: expectedStartCmd, + }, + { + description: "start cluster failure", + startCmd: "something else", + }, + } + + for _, test := range cases { + t.Run(test.description, func(t *testing.T) { + t.Parallel() + f := bootstrapper.NewFakeCommandRunner() + f.SetCommandToOutput(map[string]string{test.startCmd: "ok"}) + l := LocalkubeBootstrapper{f} + err := l.StartCluster(bootstrapper.KubernetesConfig{}) + if err != nil && test.startCmd == expectedStartCmd { + t.Errorf("Error starting cluster: %s", err) + } + }) + } +} + +func TestUpdateCluster(t *testing.T) { + defaultCfg := bootstrapper.KubernetesConfig{ + KubernetesVersion: constants.DefaultKubernetesVersion, + } + defaultAddons := []string{ + "deploy/addons/kube-dns/kube-dns-cm.yaml", + "deploy/addons/kube-dns/kube-dns-svc.yaml", + "deploy/addons/addon-manager.yaml", + "deploy/addons/dashboard/dashboard-rc.yaml", + "deploy/addons/dashboard/dashboard-svc.yaml", + "deploy/addons/storageclass/storageclass.yaml", + "deploy/addons/kube-dns/kube-dns-controller.yaml", + } + cases := []struct { + description string + k8s bootstrapper.KubernetesConfig + expectedFiles []string + shouldErr bool + }{ + { + description: "transfer localkube correct", + k8s: defaultCfg, + expectedFiles: []string{"out/localkube"}, + }, + { + description: "addons are transferred", + k8s: defaultCfg, + expectedFiles: defaultAddons, + }, + { + description: "no localkube version", + k8s: bootstrapper.KubernetesConfig{}, + shouldErr: true, + }, + } + + for _, test := range cases { + t.Run(test.description, func(t *testing.T) { + t.Parallel() + f := bootstrapper.NewFakeCommandRunner() + l := LocalkubeBootstrapper{f} + err := l.UpdateCluster(test.k8s) + if err != nil && !test.shouldErr { + t.Errorf("Error updating cluster: %s", err) + return + } + if err == nil && test.shouldErr { + t.Error("Didn't get error, but expected to") + return + } + for _, expectedFile := range test.expectedFiles { + _, err := f.GetFileToContents(expectedFile) + if err != nil { + t.Errorf("Expected file %s, but was not present", expectedFile) + } + } + }) + } +} + +func TestGetLocalkubeStatus(t *testing.T) { + cases := []struct { + description string + statusCmdMap map[string]string + expectedStatus string + shouldErr bool + }{ + { + description: "get status running", + statusCmdMap: map[string]string{localkubeStatusCommand: "Running"}, + expectedStatus: "Running", + }, + { + description: "get status stopped", + statusCmdMap: map[string]string{localkubeStatusCommand: "Stopped"}, + expectedStatus: "Stopped", + }, + { + description: "get status unknown status", + statusCmdMap: map[string]string{localkubeStatusCommand: "Recalculating..."}, + shouldErr: true, + }, + { + description: "get status error", + statusCmdMap: map[string]string{"a": "b"}, + shouldErr: true, + }, + } + + for _, test := range cases { + t.Run(test.description, func(t *testing.T) { + t.Parallel() + f := bootstrapper.NewFakeCommandRunner() + f.SetCommandToOutput(test.statusCmdMap) + l := LocalkubeBootstrapper{f} + actualStatus, err := l.GetClusterStatus() + if err != nil && !test.shouldErr { + t.Errorf("Error getting localkube status: %s", err) + return + } + if err == nil && test.shouldErr { + t.Error("Didn't get error, but expected to") + return + } + if test.expectedStatus != actualStatus { + t.Errorf("Expected status: %s, Actual status: %s", test.expectedStatus, actualStatus) + } + }) + } +} + +func TestGetHostLogs(t *testing.T) { + logs, err := GetLogsCommand(false) + if err != nil { + t.Fatalf("Error getting logs command: %s", err) + } + logsf, err := GetLogsCommand(true) + if err != nil { + t.Fatalf("Error gettings logs -f command: %s", err) + } + + cases := []struct { + description string + logsCmdMap map[string]string + follow bool + shouldErr bool + }{ + { + description: "get logs correct", + logsCmdMap: map[string]string{logs: "fee"}, + }, + { + description: "follow logs correct", + logsCmdMap: map[string]string{logsf: "fi"}, + follow: true, + }, + { + description: "get logs incorrect", + logsCmdMap: map[string]string{"fo": "fum"}, + shouldErr: true, + }, + } + + for _, test := range cases { + t.Run(test.description, func(t *testing.T) { + t.Parallel() + f := bootstrapper.NewFakeCommandRunner() + f.SetCommandToOutput(test.logsCmdMap) + l := LocalkubeBootstrapper{f} + _, err := l.GetClusterLogs(test.follow) + if err != nil && !test.shouldErr { + t.Errorf("Error getting localkube logs: %s", err) + return + } + if err == nil && test.shouldErr { + t.Error("Didn't get error, but expected to") + return + } + }) + } +} + +func TestIsLocalkubeCached(t *testing.T) { + tempDir := tests.MakeTempDir() + defer os.RemoveAll(tempDir) + + inputArr := [...]string{ + "v1.3.3", + "1.3.0", + "http://test-url.localkube.com/localkube-binary", + "file:///test/dir/to/localkube-binary", + } + + localkubeCacher := localkubeCacher{ + k8sConf: bootstrapper.KubernetesConfig{}, + } + + inner := func(input string) { + localkubeCacher.k8sConf = bootstrapper.KubernetesConfig{ + KubernetesVersion: input, + } + if localkubeCacher.isLocalkubeCached() { + t.Errorf("IsLocalKubeCached returned true even though %s was not cached", + localkubeCacher.getLocalkubeCacheFilepath()) + return + } + + f, err := os.Create(localkubeCacher.getLocalkubeCacheFilepath()) + if err != nil { + t.Errorf("failed to create dummy cache file: %v", err) + return + } + f.Close() + defer os.Remove(f.Name()) + if !localkubeCacher.isLocalkubeCached() { + t.Errorf("IsLocalKubeCached returned false even though %s was cached", + localkubeCacher.getLocalkubeCacheFilepath()) + } + + } + for _, input := range inputArr { + inner(input) + } +} diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index cf1ddf43b3..d51f754c1b 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -17,15 +17,15 @@ limitations under the License. package cluster import ( + "bytes" "encoding/json" "flag" "fmt" + "html/template" "net" "os" "os/exec" - "path/filepath" "regexp" - "strings" "time" "github.com/docker/machine/drivers/virtualbox" @@ -38,16 +38,10 @@ import ( "github.com/docker/machine/libmachine/state" "github.com/golang/glog" "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/clientcmd/api" - "k8s.io/client-go/tools/clientcmd/api/latest" - "k8s.io/minikube/pkg/minikube/assets" - "k8s.io/minikube/pkg/minikube/bootstrapper" cfg "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/util" - "k8s.io/minikube/pkg/util/kubeconfig" ) var ( @@ -154,22 +148,6 @@ func GetHostStatus(api libmachine.API) (string, error) { return s.String(), nil } -// GetLocalkubeStatus gets the status of localkube from the host VM. -func GetLocalkubeStatus(cmd bootstrapper.CommandRunner) (string, error) { - s, err := cmd.CombinedOutput(localkubeStatusCommand) - if err != nil { - return "", err - } - s = strings.TrimSpace(s) - if state.Running.String() == s { - return state.Running.String(), nil - } else if state.Stopped.String() == s { - return state.Stopped.String(), nil - } else { - return "", fmt.Errorf("Error: Unrecognize output from GetLocalkubeStatus: %s", s) - } -} - // GetHostDriverIP gets the ip address of the current minikube cluster func GetHostDriverIP(api libmachine.API) (net.IP, error) { host, err := CheckIfApiExistsAndLoad(api) @@ -188,121 +166,6 @@ func GetHostDriverIP(api libmachine.API) (net.IP, error) { return ip, nil } -// StartCluster starts a k8s cluster on the specified Host. -func StartCluster(cmd bootstrapper.CommandRunner, kubernetesConfig KubernetesConfig) error { - startCommand, err := GetStartCommand(kubernetesConfig) - if err != nil { - return errors.Wrapf(err, "Error generating start command: %s", err) - } - if err := cmd.Run(startCommand); err != nil { - return errors.Wrapf(err, "Error running start command: %s", startCommand) - } - return nil -} - -func UpdateCluster(cmd bootstrapper.CommandRunner, config KubernetesConfig) error { - copyableFiles := []assets.CopyableFile{} - var localkubeFile assets.CopyableFile - var err error - - //add url/file/bundled localkube to file list - if localkubeURIWasSpecified(config) && config.KubernetesVersion != constants.DefaultKubernetesVersion { - lCacher := localkubeCacher{config} - localkubeFile, err = lCacher.fetchLocalkubeFromURI() - if err != nil { - return errors.Wrap(err, "Error updating localkube from uri") - } - } else { - localkubeFile = assets.NewBinDataAsset("out/localkube", "/usr/local/bin", "localkube", "0777") - } - copyableFiles = append(copyableFiles, localkubeFile) - - // add addons to file list - // custom addons - assets.AddMinikubeAddonsDirToAssets(©ableFiles) - // bundled addons - for _, addonBundle := range assets.Addons { - if isEnabled, err := addonBundle.IsEnabled(); err == nil && isEnabled { - for _, addon := range addonBundle.Assets { - copyableFiles = append(copyableFiles, addon) - } - } else if err != nil { - return err - } - } - - for _, f := range copyableFiles { - // fmt.Println(f.GetAssetName()) - if err := cmd.Copy(f); err != nil { - return err - } - } - return nil -} - -func localkubeURIWasSpecified(config KubernetesConfig) bool { - // see if flag is different than default -> it was passed by user - return config.KubernetesVersion != constants.DefaultKubernetesVersion -} - -// SetupCerts gets the generated credentials required to talk to the APIServer. -func SetupCerts(cmd bootstrapper.CommandRunner, k8s KubernetesConfig) error { - localPath := constants.GetMinipath() - ip := net.ParseIP(k8s.NodeIP) - glog.Infoln("Setting up certificates for IP: %s", ip) - - caCert := filepath.Join(localPath, "ca.crt") - caKey := filepath.Join(localPath, "ca.key") - publicPath := filepath.Join(localPath, "apiserver.crt") - privatePath := filepath.Join(localPath, "apiserver.key") - if err := GenerateCerts(caCert, caKey, publicPath, privatePath, ip, k8s.APIServerName, k8s.DNSDomain); err != nil { - return errors.Wrap(err, "Error generating certs") - } - - copyableFiles := []assets.CopyableFile{} - - for _, cert := range certs { - p := filepath.Join(localPath, cert) - perms := "0644" - if strings.HasSuffix(cert, ".key") { - perms = "0600" - } - certFile, err := assets.NewFileAsset(p, util.DefaultCertPath, cert, perms) - if err != nil { - return err - } - copyableFiles = append(copyableFiles, certFile) - } - - kubeCfgSetup := &kubeconfig.KubeConfigSetup{ - ClusterName: cfg.GetMachineName(), - ClusterServerAddress: "https://localhost:8443", - ClientCertificate: filepath.Join(util.DefaultCertPath, "apiserver.crt"), - ClientKey: filepath.Join(util.DefaultCertPath, "apiserver.key"), - CertificateAuthority: filepath.Join(util.DefaultCertPath, "ca.crt"), - KeepContext: false, - } - - kubeCfg := api.NewConfig() - kubeconfig.PopulateKubeConfig(kubeCfgSetup, kubeCfg) - data, err := runtime.Encode(latest.Codec, kubeCfg) - if err != nil { - return errors.Wrap(err, "setup certs: encoding kubeconfig") - } - - kubeCfgFile := assets.NewMemoryAsset(data, - util.DefaultLocalkubeDirectory, "kubeconfig", "0644") - copyableFiles = append(copyableFiles, kubeCfgFile) - - for _, f := range copyableFiles { - if err := cmd.Copy(f); err != nil { - return err - } - } - - return nil -} - func engineOptions(config MachineConfig) *engine.Options { o := engine.Options{ Env: config.DockerEnv, @@ -400,20 +263,6 @@ func GetHostDockerEnv(api libmachine.API) (map[string]string, error) { return envMap, nil } -// GetHostLogs gets the localkube logs of the host VM. -// If follow is specified, it will tail the logs -func GetHostLogs(cmd bootstrapper.CommandRunner, follow bool) (string, error) { - logsCommand, err := GetLogsCommand(follow) - if err != nil { - return "", errors.Wrap(err, "Error getting logs command") - } - logs, err := cmd.CombinedOutput(logsCommand) - if err != nil { - return "", errors.Wrap(err, "running logs command") - } - return logs, nil -} - // MountHost runs the mount command from the 9p client on the VM to the 9p server on the host func MountHost(api libmachine.API, ip net.IP, path, port, mountVersion string, uid, gid, msize int) error { host, err := CheckIfApiExistsAndLoad(api) @@ -536,3 +385,38 @@ func EnsureMinikubeRunningOrExit(api libmachine.API, exitStatus int) { os.Exit(exitStatus) } } + +func GetMountCleanupCommand(path string) string { + return fmt.Sprintf("sudo umount %s;", path) +} + +var mountTemplate = ` +sudo mkdir -p {{.Path}} || true; +sudo mount -t 9p -o trans=tcp,port={{.Port}},dfltuid={{.UID}},dfltgid={{.GID}},version={{.Version}},msize={{.Msize}} {{.IP}} {{.Path}}; +sudo chmod 775 {{.Path}};` + +func GetMountCommand(ip net.IP, path, port, mountVersion string, uid, gid, msize int) (string, error) { + t := template.Must(template.New("mountCommand").Parse(mountTemplate)) + buf := bytes.Buffer{} + data := struct { + IP string + Path string + Port string + Version string + UID int + GID int + Msize int + }{ + IP: ip.String(), + Path: path, + Port: port, + Version: mountVersion, + UID: uid, + GID: gid, + Msize: msize, + } + if err := t.Execute(&buf, data); err != nil { + return "", err + } + return buf.String(), nil +} diff --git a/pkg/minikube/cluster/cluster_test.go b/pkg/minikube/cluster/cluster_test.go index 0299f282a2..f8729100d7 100644 --- a/pkg/minikube/cluster/cluster_test.go +++ b/pkg/minikube/cluster/cluster_test.go @@ -18,7 +18,6 @@ package cluster import ( "os" - "path/filepath" "strings" "testing" @@ -26,7 +25,6 @@ import ( "github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/provision" "github.com/docker/machine/libmachine/state" - "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/tests" @@ -81,224 +79,6 @@ func TestCreateHost(t *testing.T) { } } -func TestStartCluster(t *testing.T) { - expectedStartCmd, err := GetStartCommand(KubernetesConfig{}) - if err != nil { - t.Fatalf("generating start command: %s", err) - } - - cases := []struct { - description string - startCmd string - }{ - { - description: "start cluster success", - startCmd: expectedStartCmd, - }, - { - description: "start cluster failure", - startCmd: "something else", - }, - } - - for _, test := range cases { - t.Run(test.description, func(t *testing.T) { - t.Parallel() - f := bootstrapper.NewFakeCommandRunner() - f.SetCommandToOutput(map[string]string{test.startCmd: "ok"}) - err := StartCluster(f, KubernetesConfig{}) - if err != nil && test.startCmd == expectedStartCmd { - t.Errorf("Error starting cluster: %s", err) - } - }) - } -} - -func TestUpdateCluster(t *testing.T) { - defaultCfg := KubernetesConfig{ - KubernetesVersion: constants.DefaultKubernetesVersion, - } - defaultAddons := []string{ - "deploy/addons/kube-dns/kube-dns-cm.yaml", - "deploy/addons/kube-dns/kube-dns-svc.yaml", - "deploy/addons/addon-manager.yaml", - "deploy/addons/dashboard/dashboard-rc.yaml", - "deploy/addons/dashboard/dashboard-svc.yaml", - "deploy/addons/storageclass/storageclass.yaml", - "deploy/addons/kube-dns/kube-dns-controller.yaml", - } - cases := []struct { - description string - k8s KubernetesConfig - expectedFiles []string - shouldErr bool - }{ - { - description: "transfer localkube correct", - k8s: defaultCfg, - expectedFiles: []string{"out/localkube"}, - }, - { - description: "addons are transferred", - k8s: defaultCfg, - expectedFiles: defaultAddons, - }, - { - description: "no localkube version", - k8s: KubernetesConfig{}, - shouldErr: true, - }, - } - - for _, test := range cases { - t.Run(test.description, func(t *testing.T) { - t.Parallel() - f := bootstrapper.NewFakeCommandRunner() - err := UpdateCluster(f, test.k8s) - if err != nil && !test.shouldErr { - t.Errorf("Error updating cluster: %s", err) - return - } - if err == nil && test.shouldErr { - t.Error("Didn't get error, but expected to") - return - } - for _, expectedFile := range test.expectedFiles { - _, err := f.GetFileToContents(expectedFile) - if err != nil { - t.Errorf("Expected file %s, but was not present", expectedFile) - } - } - }) - } -} - -func TestGetLocalkubeStatus(t *testing.T) { - cases := []struct { - description string - statusCmdMap map[string]string - expectedStatus string - shouldErr bool - }{ - { - description: "get status running", - statusCmdMap: map[string]string{localkubeStatusCommand: "Running"}, - expectedStatus: "Running", - }, - { - description: "get status stopped", - statusCmdMap: map[string]string{localkubeStatusCommand: "Stopped"}, - expectedStatus: "Stopped", - }, - { - description: "get status unknown status", - statusCmdMap: map[string]string{localkubeStatusCommand: "Recalculating..."}, - shouldErr: true, - }, - { - description: "get status error", - statusCmdMap: map[string]string{"a": "b"}, - shouldErr: true, - }, - } - - for _, test := range cases { - t.Run(test.description, func(t *testing.T) { - t.Parallel() - f := bootstrapper.NewFakeCommandRunner() - f.SetCommandToOutput(test.statusCmdMap) - actualStatus, err := GetLocalkubeStatus(f) - if err != nil && !test.shouldErr { - t.Errorf("Error getting localkube status: %s", err) - return - } - if err == nil && test.shouldErr { - t.Error("Didn't get error, but expected to") - return - } - if test.expectedStatus != actualStatus { - t.Errorf("Expected status: %s, Actual status: %s", test.expectedStatus, actualStatus) - } - }) - } -} - -func TestGetHostLogs(t *testing.T) { - logs, err := GetLogsCommand(false) - if err != nil { - t.Fatalf("Error getting logs command: %s", err) - } - logsf, err := GetLogsCommand(true) - if err != nil { - t.Fatalf("Error gettings logs -f command: %s", err) - } - - cases := []struct { - description string - logsCmdMap map[string]string - follow bool - shouldErr bool - }{ - { - description: "get logs correct", - logsCmdMap: map[string]string{logs: "fee"}, - }, - { - description: "follow logs correct", - logsCmdMap: map[string]string{logsf: "fi"}, - follow: true, - }, - { - description: "get logs incorrect", - logsCmdMap: map[string]string{"fo": "fum"}, - shouldErr: true, - }, - } - - for _, test := range cases { - t.Run(test.description, func(t *testing.T) { - t.Parallel() - f := bootstrapper.NewFakeCommandRunner() - f.SetCommandToOutput(test.logsCmdMap) - _, err := GetHostLogs(f, test.follow) - if err != nil && !test.shouldErr { - t.Errorf("Error getting localkube logs: %s", err) - return - } - if err == nil && test.shouldErr { - t.Error("Didn't get error, but expected to") - return - } - }) - } -} - -func TestSetupCerts(t *testing.T) { - tempDir := tests.MakeTempDir() - defer os.RemoveAll(tempDir) - - f := bootstrapper.NewFakeCommandRunner() - k8s := KubernetesConfig{ - APIServerName: constants.APIServerName, - DNSDomain: constants.ClusterDNSDomain, - } - - var filesToBeTransferred []string - for _, cert := range certs { - filesToBeTransferred = append(filesToBeTransferred, filepath.Join(constants.GetMinipath(), cert)) - } - - if err := SetupCerts(f, k8s); err != nil { - t.Fatalf("Error starting cluster: %s", err) - } - for _, cert := range filesToBeTransferred { - _, err := f.GetFileToContents(cert) - if err != nil { - t.Errorf("Cert not generated: %s", cert) - } - } -} - func TestStartHostExists(t *testing.T) { api := tests.NewMockAPI() // Create an initial host. @@ -609,46 +389,3 @@ func TestCreateSSHShell(t *testing.T) { t.Fatalf("Expected ssh session to be run") } } - -func TestIsLocalkubeCached(t *testing.T) { - tempDir := tests.MakeTempDir() - defer os.RemoveAll(tempDir) - - inputArr := [...]string{ - "v1.3.3", - "1.3.0", - "http://test-url.localkube.com/localkube-binary", - "file:///test/dir/to/localkube-binary", - } - - localkubeCacher := localkubeCacher{ - k8sConf: KubernetesConfig{}, - } - - inner := func(input string) { - localkubeCacher.k8sConf = KubernetesConfig{ - KubernetesVersion: input, - } - if localkubeCacher.isLocalkubeCached() { - t.Errorf("IsLocalKubeCached returned true even though %s was not cached", - localkubeCacher.getLocalkubeCacheFilepath()) - return - } - - f, err := os.Create(localkubeCacher.getLocalkubeCacheFilepath()) - if err != nil { - t.Errorf("failed to create dummy cache file: %v", err) - return - } - f.Close() - defer os.Remove(f.Name()) - if !localkubeCacher.isLocalkubeCached() { - t.Errorf("IsLocalKubeCached returned false even though %s was cached", - localkubeCacher.getLocalkubeCacheFilepath()) - } - - } - for _, input := range inputArr { - inner(input) - } -} diff --git a/pkg/minikube/cluster/credentials.go b/pkg/minikube/cluster/credentials.go deleted file mode 100644 index 070b7cc3b7..0000000000 --- a/pkg/minikube/cluster/credentials.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2016 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 cluster - -import ( - "net" - - "github.com/pkg/errors" - "k8s.io/minikube/pkg/util" -) - -var ( - // This is the internalIP , the API server and other components communicate on. - internalIP = net.ParseIP(util.DefaultServiceClusterIP) -) - -func GenerateCerts(caCert, caKey, pub, priv string, ip net.IP, name string, dnsDomain string) error { - if !(util.CanReadFile(caCert) && util.CanReadFile(caKey)) { - if err := util.GenerateCACert(caCert, caKey, name); err != nil { - return errors.Wrap(err, "Error generating certificate") - } - } - - ips := []net.IP{ip, internalIP} - if err := util.GenerateSignedCert(pub, priv, ips, util.GetAlternateDNS(dnsDomain), caCert, caKey); err != nil { - return errors.Wrap(err, "Error generating signed cert") - } - return nil -} diff --git a/pkg/minikube/cluster/types.go b/pkg/minikube/cluster/types.go index 11ae0dd2d9..c524be30d3 100644 --- a/pkg/minikube/cluster/types.go +++ b/pkg/minikube/cluster/types.go @@ -16,7 +16,10 @@ limitations under the License. package cluster -import "k8s.io/minikube/pkg/util" +import ( + "k8s.io/minikube/pkg/minikube/bootstrapper" + "k8s.io/minikube/pkg/util" +) // MachineConfig contains the parameters used to start a cluster. type MachineConfig struct { @@ -37,20 +40,8 @@ type MachineConfig struct { DisableDriverMounts bool // Only used by virtualbox and xhyve } -// KubernetesConfig contains the parameters used to configure the VM Kubernetes. -type KubernetesConfig struct { - KubernetesVersion string - NodeIP string - APIServerName string - DNSDomain string - ContainerRuntime string - NetworkPlugin string - FeatureGates string - ExtraOptions util.ExtraOptionSlice -} - // Config contains machine and k8s config type Config struct { MachineConfig MachineConfig - KubernetesConfig KubernetesConfig + KubernetesConfig bootstrapper.KubernetesConfig } diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index d11fcb56cf..0238fbd383 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -91,12 +91,13 @@ const ( DefaultVMDriver = "virtualbox" DefaultStatusFormat = "minikube: {{.MinikubeStatus}}\n" + "cluster: {{.ClusterStatus}}\n" + "kubectl: {{.KubeconfigStatus}}\n" - DefaultAddonListFormat = "- {{.AddonName}}: {{.AddonStatus}}\n" - DefaultConfigViewFormat = "- {{.ConfigKey}}: {{.ConfigValue}}\n" - GithubMinikubeReleasesURL = "https://storage.googleapis.com/minikube/releases.json" - KubernetesVersionGCSURL = "https://storage.googleapis.com/minikube/k8s_releases.json" - DefaultWait = 20 - DefaultInterval = 6 + DefaultAddonListFormat = "- {{.AddonName}}: {{.AddonStatus}}\n" + DefaultConfigViewFormat = "- {{.ConfigKey}}: {{.ConfigValue}}\n" + GithubMinikubeReleasesURL = "https://storage.googleapis.com/minikube/releases.json" + KubernetesVersionGCSURL = "https://storage.googleapis.com/minikube/k8s_releases.json" + DefaultWait = 20 + DefaultInterval = 6 + DefaultClusterBootstrapper = "localkube" ) var DefaultIsoUrl = fmt.Sprintf("https://storage.googleapis.com/%s/minikube-%s.iso", minikubeVersion.GetIsoPath(), minikubeVersion.GetIsoVersion()) @@ -144,3 +145,4 @@ const ( const IsMinikubeChildProcess = "IS_MINIKUBE_CHILD_PROCESS" const DriverNone = "none" +const FileScheme = "file"