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.pull/1886/head
parent
3a0d03f93b
commit
48fd6ecb65
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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"},
|
|
@ -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)
|
||||
}
|
|
@ -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()
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue