Merge pull request #6440 from tstromberg/addons-on-startup

Add addon enablement to start
pull/6454/head
Thomas Strömberg 2020-01-31 16:25:30 -08:00 committed by GitHub
commit e206b25da5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 268 additions and 200 deletions

View File

@ -102,9 +102,9 @@ var printAddonsList = func() {
for _, addonName := range addonNames {
addonBundle := assets.Addons[addonName]
addonStatus, err := addonBundle.IsEnabled()
addonStatus, err := addonBundle.IsEnabled(pName)
if err != nil {
exit.WithError("Error getting addons status", err)
out.WarningT("Unable to get addon status for {{.name}}: {{.error}}", out.V{"name": addonName, "error": err})
}
tData = append(tData, []string{addonName, pName, fmt.Sprintf("%s %s", stringFromStatus(addonStatus), iconFromStatus(addonStatus))})
}
@ -114,12 +114,11 @@ var printAddonsList = func() {
v, _, err := config.ListProfiles()
if err != nil {
glog.Infof("error getting list of porfiles: %v", err)
glog.Errorf("list profiles returned error: %v", err)
}
if len(v) > 1 {
out.T(out.Tip, "To see addons list for other profiles use: `minikube addons -p name list`")
}
}
var printAddonsJSON = func() {
@ -135,9 +134,10 @@ var printAddonsJSON = func() {
for _, addonName := range addonNames {
addonBundle := assets.Addons[addonName]
addonStatus, err := addonBundle.IsEnabled()
addonStatus, err := addonBundle.IsEnabled(pName)
if err != nil {
exit.WithError("Error getting addons status", err)
glog.Errorf("Unable to get addon status for %s: %v", addonName, err)
continue
}
addonsMap[addonName] = map[string]interface{}{

View File

@ -39,7 +39,7 @@ var addonsDisableCmd = &cobra.Command{
if err != nil {
exit.WithError("disable failed", err)
}
out.SuccessT(`"{{.minikube_addon}}" was successfully disabled`, out.V{"minikube_addon": addon})
out.T(out.AddonDisable, `"The '{{.minikube_addon}}' addon is disabled`, out.V{"minikube_addon": addon})
},
}

View File

@ -33,13 +33,12 @@ var addonsEnableCmd = &cobra.Command{
if len(args) != 1 {
exit.UsageT("usage: minikube addons enable ADDON_NAME")
}
addon := args[0]
err := addons.Set(addon, "true", viper.GetString(config.MachineProfile))
if err != nil {
exit.WithError("enable failed", err)
}
out.SuccessT("{{.addonName}} was successfully enabled", out.V{"addonName": addon})
out.T(out.AddonEnable, "The '{{.addonName}}' addon is enabled", out.V{"addonName": addon})
},
}

View File

@ -24,8 +24,10 @@ import (
"github.com/pkg/browser"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/cluster"
pkg_config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
@ -66,7 +68,8 @@ var addonsOpenCmd = &cobra.Command{
}
defer api.Close()
if !cluster.IsMinikubeRunning(api) {
profileName := viper.GetString(pkg_config.MachineProfile)
if !cluster.IsHostRunning(api, profileName) {
os.Exit(1)
}
addon, ok := assets.Addons[addonName] // validate addon input
@ -75,7 +78,7 @@ var addonsOpenCmd = &cobra.Command{
To see the list of available addons run:
minikube addons list`, out.V{"name": addonName})
}
ok, err = addon.IsEnabled()
ok, err = addon.IsEnabled(profileName)
if err != nil {
exit.WithError("IsEnabled failed", err)
}

View File

@ -22,6 +22,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
pkg_config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/kubeconfig"
@ -78,7 +79,7 @@ var ProfileCmd = &cobra.Command{
}
cc, err := pkgConfig.Load(profile)
// might err when loading older version of cfg file that doesn't have KeepContext field
if err != nil && !os.IsNotExist(err) {
if err != nil && !pkg_config.IsNotExist(err) {
out.ErrT(out.Sad, `Error loading profile config: {{.error}}`, out.V{"error": err})
}
if err == nil {

View File

@ -142,7 +142,7 @@ var printProfilesJSON = func() {
var body = map[string]interface{}{}
if err == nil || os.IsNotExist(err) {
if err == nil || config.IsNotExist(err) {
body["valid"] = valid
body["invalid"] = invalid
jsonString, _ := json.Marshal(body)

View File

@ -36,7 +36,6 @@ import (
pkgaddons "k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
pkg_config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
@ -61,7 +60,7 @@ var dashboardCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
profileName := viper.GetString(pkg_config.MachineProfile)
cc, err := pkg_config.Load(profileName)
if err != nil && !os.IsNotExist(err) {
if err != nil && !pkg_config.IsNotExist(err) {
exit.WithError("Error loading profile config", err)
}
@ -103,18 +102,18 @@ var dashboardCmd = &cobra.Command{
exit.WithCodeT(exit.NoInput, "kubectl not found in PATH, but is required for the dashboard. Installation guide: https://kubernetes.io/docs/tasks/tools/install-kubectl/")
}
if !cluster.IsMinikubeRunning(api) {
if !cluster.IsHostRunning(api, profileName) {
os.Exit(1)
}
// Check dashboard status before enabling it
dashboardAddon := assets.Addons["dashboard"]
dashboardStatus, _ := dashboardAddon.IsEnabled()
dashboardStatus, _ := dashboardAddon.IsEnabled(profileName)
if !dashboardStatus {
// Send status messages to stderr for folks re-using this output.
out.ErrT(out.Enabling, "Enabling dashboard ...")
// Enable the dashboard add-on
err = pkgaddons.Set("dashboard", "true", viper.GetString(config.MachineProfile))
err = pkgaddons.Set("dashboard", "true", profileName)
if err != nil {
exit.WithError("Unable to enable dashboard", err)
}

View File

@ -188,7 +188,7 @@ func deleteProfile(profile *pkg_config.Profile) error {
}
defer api.Close()
cc, err := pkg_config.Load(profile.Name)
if err != nil && !os.IsNotExist(err) {
if err != nil && !pkg_config.IsNotExist(err) {
delErr := profileDeletionErr(profile.Name, fmt.Sprintf("error loading profile config: %v", err))
return DeletionError{Err: delErr, Errtype: MissingProfile}
}
@ -223,7 +223,7 @@ func deleteProfile(profile *pkg_config.Profile) error {
deleteProfileDirectory(profile.Name)
if err := pkg_config.DeleteProfile(profile.Name); err != nil {
if os.IsNotExist(err) {
if pkg_config.IsNotExist(err) {
delErr := profileDeletionErr(profile.Name, fmt.Sprintf("\"%s\" profile does not exist", profile.Name))
return DeletionError{Err: delErr, Errtype: MissingProfile}
}

View File

@ -27,7 +27,6 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/config"
pkg_config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
@ -51,8 +50,8 @@ minikube kubectl -- get pods --namespace kube-system`,
}
defer api.Close()
cc, err := pkg_config.Load(viper.GetString(config.MachineProfile))
if err != nil && !os.IsNotExist(err) {
cc, err := config.Load(viper.GetString(config.MachineProfile))
if err != nil && !config.IsNotExist(err) {
out.ErrLn("Error loading profile config: %v", err)
}

View File

@ -53,7 +53,7 @@ func runPause(cmd *cobra.Command, args []string) {
defer api.Close()
cc, err := config.Load(cname)
if err != nil && !os.IsNotExist(err) {
if err != nil && !config.IsNotExist(err) {
exit.WithError("Error loading profile config", err)
}

View File

@ -25,8 +25,10 @@ import (
"github.com/golang/glog"
"github.com/pkg/browser"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/cluster"
pkg_config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
@ -71,7 +73,8 @@ var serviceCmd = &cobra.Command{
}
defer api.Close()
if !cluster.IsMinikubeRunning(api) {
profileName := viper.GetString(pkg_config.MachineProfile)
if !cluster.IsHostRunning(api, profileName) {
os.Exit(1)
}

View File

@ -46,7 +46,7 @@ import (
"github.com/spf13/viper"
"golang.org/x/sync/errgroup"
cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config"
pkgaddons "k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/minikube/bootstrapper"
"k8s.io/minikube/pkg/minikube/bootstrapper/bsutil"
"k8s.io/minikube/pkg/minikube/bootstrapper/images"
@ -104,7 +104,6 @@ const (
imageMirrorCountry = "image-mirror-country"
mountString = "mount-string"
disableDriverMounts = "disable-driver-mounts"
addons = "addons"
cacheImages = "cache-images"
uuid = "uuid"
vpnkitSock = "hyperkit-vpnkit-sock"
@ -172,7 +171,7 @@ func initMinikubeFlags() {
startCmd.Flags().String(containerRuntime, "docker", "The container runtime to be used (docker, crio, containerd).")
startCmd.Flags().Bool(createMount, false, "This will start the mount daemon and automatically mount files into minikube.")
startCmd.Flags().String(mountString, constants.DefaultMountDir+":/minikube-host", "The argument to pass the minikube mount command on start.")
startCmd.Flags().StringArrayVar(&addonList, addons, nil, "Enable addons. see `minikube addons list` for a list of valid addon names.")
startCmd.Flags().StringArrayVar(&addonList, "addons", nil, "Enable addons. see `minikube addons list` for a list of valid addon names.")
startCmd.Flags().String(criSocket, "", "The cri socket path to be used.")
startCmd.Flags().String(networkPlugin, "", "The name of the network plugin.")
startCmd.Flags().Bool(enableDefaultCNI, false, "Enable the default CNI plugin (/etc/cni/net.d/k8s.conf). Used in conjunction with \"--network-plugin=cni\".")
@ -296,7 +295,7 @@ func runStart(cmd *cobra.Command, args []string) {
}
existing, err := config.Load(viper.GetString(config.MachineProfile))
if err != nil && !os.IsNotExist(err) {
if err != nil && !config.IsNotExist(err) {
exit.WithCodeT(exit.Data, "Unable to load config: {{.error}}", out.V{"error": err})
}
@ -368,8 +367,8 @@ func runStart(cmd *cobra.Command, args []string) {
bootstrapCluster(bs, cr, mRunner, mc, preExists, isUpgrade)
configureMounts()
// enable addons with start command
enableAddons()
// enable addons
addons.Start(viper.GetString(config.MachineProfile), addonList)
if err = cacheAndLoadImagesInConfig(); err != nil {
out.T(out.FailureType, "Unable to load cached images from config file.")
@ -408,15 +407,6 @@ func cacheISO(cfg *config.MachineConfig, driverName string) {
}
}
func enableAddons() {
for _, a := range addonList {
err := pkgaddons.Set(a, "true", viper.GetString(config.MachineProfile))
if err != nil {
exit.WithError("addon enable failed", err)
}
}
}
func displayVersion(version string) {
prefix := ""
if viper.GetString(config.MachineProfile) != constants.DefaultMachineName {
@ -761,7 +751,7 @@ func validateUser(drvName string) {
os.Exit(exit.Permissions)
}
_, err = config.Load(viper.GetString(config.MachineProfile))
if err == nil || !os.IsNotExist(err) {
if err == nil || !config.IsNotExist(err) {
out.T(out.Tip, "Tip: To remove this root owned cluster, run: sudo {{.cmd}} delete", out.V{"cmd": minikubeCmd()})
}
if !useForce {

View File

@ -45,7 +45,7 @@ var unpauseCmd = &cobra.Command{
defer api.Close()
cc, err := config.Load(cname)
if err != nil && !os.IsNotExist(err) {
if err != nil && !config.IsNotExist(err) {
exit.WithError("Error loading profile config", err)
}

1
go.mod
View File

@ -72,7 +72,6 @@ require (
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456
golang.org/x/text v0.3.2
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
gotest.tools v2.2.0+incompatible
k8s.io/api v0.17.2
k8s.io/apimachinery v0.17.2
k8s.io/client-go v11.0.0+incompatible

View File

@ -18,9 +18,10 @@ package addons
import (
"fmt"
"os"
"path/filepath"
"path"
"strconv"
"strings"
"time"
"github.com/golang/glog"
"github.com/pkg/errors"
@ -41,6 +42,7 @@ const defaultStorageClassProvisioner = "standard"
// Set sets a value
func Set(name, value, profile string) error {
glog.Infof("Setting %s=%s in profile %q", name, value, profile)
a, valid := isAddonValid(name)
if !valid {
return errors.Errorf("%s is not a valid addon", name)
@ -66,6 +68,7 @@ func Set(name, value, profile string) error {
return errors.Wrap(err, "running callbacks")
}
glog.Infof("Writing new config for %q ...", profile)
// Write the value
return config.Write(profile, c)
}
@ -100,6 +103,7 @@ func SetBool(m *config.MachineConfig, name string, val string) error {
// enableOrDisableAddon updates addon status executing any commands necessary
func enableOrDisableAddon(name, val, profile string) error {
glog.Infof("Setting addon %s=%s in %q", name, val, profile)
enable, err := strconv.ParseBool(val)
if err != nil {
return errors.Wrapf(err, "parsing bool: %s", name)
@ -107,14 +111,14 @@ func enableOrDisableAddon(name, val, profile string) error {
addon := assets.Addons[name]
// check addon status before enabling/disabling it
alreadySet, err := isAddonAlreadySet(addon, enable)
alreadySet, err := isAddonAlreadySet(addon, enable, profile)
if err != nil {
out.ErrT(out.Conflict, "{{.error}}", out.V{"error": err})
return err
}
//if addon is already enabled or disabled, do nothing
if alreadySet {
return nil
glog.Warningf("addon %s should already be in state %v", name, val)
}
if name == "istio" && enable {
@ -134,20 +138,15 @@ func enableOrDisableAddon(name, val, profile string) error {
}
defer api.Close()
//if minikube is not running, we return and simply update the value in the addon
//config and rewrite the file
if !cluster.IsMinikubeRunning(api) {
return nil
}
cfg, err := config.Load(profile)
if err != nil && !os.IsNotExist(err) {
if err != nil && !config.IsNotExist(err) {
exit.WithCodeT(exit.Data, "Unable to load config: {{.error}}", out.V{"error": err})
}
host, err := cluster.CheckIfHostExistsAndLoad(api, cfg.Name)
if err != nil {
return errors.Wrap(err, "getting host")
host, err := cluster.CheckIfHostExistsAndLoad(api, profile)
if err != nil || !cluster.IsHostRunning(api, profile) {
glog.Warningf("%q is not running, writing %s=%v to disk and skipping enablement (err=%v)", profile, addon.Name(), enable, err)
return nil
}
cmd, err := machine.CommandRunner(host)
@ -159,11 +158,10 @@ func enableOrDisableAddon(name, val, profile string) error {
return enableOrDisableAddonInternal(addon, cmd, data, enable, profile)
}
func isAddonAlreadySet(addon *assets.Addon, enable bool) (bool, error) {
addonStatus, err := addon.IsEnabled()
func isAddonAlreadySet(addon *assets.Addon, enable bool, profile string) (bool, error) {
addonStatus, err := addon.IsEnabled(profile)
if err != nil {
return false, errors.Wrap(err, "get the addon status")
return false, errors.Wrap(err, "is enabled")
}
if addonStatus && enable {
@ -178,42 +176,50 @@ func isAddonAlreadySet(addon *assets.Addon, enable bool) (bool, error) {
func enableOrDisableAddonInternal(addon *assets.Addon, cmd command.Runner, data interface{}, enable bool, profile string) error {
files := []string{}
for _, addon := range addon.Assets {
var addonFile assets.CopyableFile
var f assets.CopyableFile
var err error
if addon.IsTemplate() {
addonFile, err = addon.Evaluate(data)
f, err = addon.Evaluate(data)
if err != nil {
return errors.Wrapf(err, "evaluate bundled addon %s asset", addon.GetAssetName())
}
} else {
addonFile = addon
f = addon
}
fPath := path.Join(f.GetTargetDir(), f.GetTargetName())
if enable {
if err := cmd.Copy(addonFile); err != nil {
glog.Infof("installing %s", fPath)
if err := cmd.Copy(f); err != nil {
return err
}
} else {
glog.Infof("Removing %+v", fPath)
defer func() {
if err := cmd.Remove(addonFile); err != nil {
glog.Warningf("error removing %s; addon should still be disabled as expected", addonFile)
if err := cmd.Remove(f); err != nil {
glog.Warningf("error removing %s; addon should still be disabled as expected", fPath)
}
}()
}
files = append(files, filepath.Join(addonFile.GetTargetDir(), addonFile.GetTargetName()))
files = append(files, fPath)
}
command, err := kubectlCommand(profile, files, enable)
if err != nil {
return err
}
if result, err := cmd.RunCmd(command); err != nil {
return errors.Wrapf(err, "error updating addon:\n%s", result.Output())
glog.Infof("Running: %s", command)
rr, err := cmd.RunCmd(command)
if err != nil {
return errors.Wrapf(err, "addon apply")
}
glog.Infof("output:\n%s", rr.Output())
return nil
}
// enableOrDisableStorageClasses enables or disables storage classes
func enableOrDisableStorageClasses(name, val, profile string) error {
glog.Infof("enableOrDisableStorageClasses %s=%v on %q", name, val, profile)
enable, err := strconv.ParseBool(val)
if err != nil {
return errors.Wrap(err, "Error parsing boolean")
@ -228,6 +234,17 @@ func enableOrDisableStorageClasses(name, val, profile string) error {
return errors.Wrapf(err, "Error getting storagev1 interface %v ", err)
}
api, err := machine.NewAPIClient()
if err != nil {
return errors.Wrap(err, "machine client")
}
defer api.Close()
if !cluster.IsHostRunning(api, profile) {
glog.Warningf("%q is not running, writing %s=%v to disk and skipping enablement", profile, name, val)
return enableOrDisableAddon(name, val, profile)
}
if enable {
// Only StorageClass for 'name' should be marked as default
err = storageclass.SetDefaultStorageClass(storagev1, class)
@ -244,3 +261,39 @@ func enableOrDisableStorageClasses(name, val, profile string) error {
return enableOrDisableAddon(name, val, profile)
}
// Start enables the default addons for a profile, plus any additional
func Start(profile string, additional []string) {
start := time.Now()
glog.Infof("enableAddons")
defer func() {
glog.Infof("enableAddons completed in %s", time.Since(start))
}()
toEnable := []string{}
// Apply addons that are enabled by default
for name, a := range assets.Addons {
enabled, err := a.IsEnabled(profile)
if err != nil {
glog.Errorf("is-enabled failed for %q: %v", a.Name(), err)
continue
}
if enabled {
toEnable = append(toEnable, name)
}
}
toEnable = append(toEnable, additional...)
if len(toEnable) == 0 {
return
}
out.T(out.AddonEnable, "Enabling addons: {{.addons}}", out.V{"addons": strings.Join(toEnable, ", ")})
for _, a := range toEnable {
err := Set(a, "true", profile)
if err != nil {
// Intentionally non-fatal
out.WarningT("Enabling '{{.name}}' returned an error: {{.error}}", out.V{"name": a, "error": err})
}
}
}

View File

@ -17,93 +17,115 @@ limitations under the License.
package addons
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"gotest.tools/assert"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/localpath"
)
func createTestProfile(t *testing.T) string {
t.Helper()
td, err := ioutil.TempDir("", "profile")
if err != nil {
t.Fatalf("tempdir: %v", err)
}
err = os.Setenv(localpath.MinikubeHome, td)
if err != nil {
t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome)
}
// Not necessary, but it is a handy random alphanumeric
name := filepath.Base(td)
if err := os.MkdirAll(config.ProfileFolderPath(name), 0777); err != nil {
t.Fatalf("error creating temporary directory")
}
if err := config.DefaultLoader.WriteConfigToFile(name, &config.MachineConfig{}); err != nil {
t.Fatalf("error creating temporary profile config: %v", err)
}
return name
}
func TestIsAddonAlreadySet(t *testing.T) {
testCases := []struct {
addonName string
}{
{
addonName: "ingress",
},
{
addonName: "registry",
},
profile := createTestProfile(t)
if err := Set("registry", "true", profile); err != nil {
t.Errorf("unable to set registry true: %v", err)
}
enabled, err := assets.Addons["registry"].IsEnabled(profile)
if err != nil {
t.Errorf("registry: %v", err)
}
if !enabled {
t.Errorf("expected registry to be enabled")
}
for _, test := range testCases {
addon := assets.Addons[test.addonName]
addonStatus, _ := addon.IsEnabled()
alreadySet, err := isAddonAlreadySet(addon, addonStatus)
if !alreadySet {
if addonStatus {
t.Errorf("Did not get expected status, \n\n expected %+v already enabled", test.addonName)
} else {
t.Errorf("Did not get expected status, \n\n expected %+v already disabled", test.addonName)
}
}
if err != nil {
t.Errorf("Got unexpected error: %+v", err)
}
enabled, err = assets.Addons["ingress"].IsEnabled(profile)
if err != nil {
t.Errorf("ingress: %v", err)
}
if enabled {
t.Errorf("expected ingress to not be enabled")
}
}
func TestDisableUnknownAddon(t *testing.T) {
tmpProfile := "temp-minikube-profile"
if err := Set("InvalidAddon", "false", tmpProfile); err == nil {
profile := createTestProfile(t)
if err := Set("InvalidAddon", "false", profile); err == nil {
t.Fatalf("Disable did not return error for unknown addon")
}
}
func TestEnableUnknownAddon(t *testing.T) {
tmpProfile := "temp-minikube-profile"
if err := Set("InvalidAddon", "true", tmpProfile); err == nil {
profile := createTestProfile(t)
if err := Set("InvalidAddon", "true", profile); err == nil {
t.Fatalf("Enable did not return error for unknown addon")
}
}
func TestEnableAndDisableAddon(t *testing.T) {
tests := []struct {
name string
enable bool
}{
{
name: "test enable",
enable: true,
}, {
name: "test disable",
enable: false,
},
profile := createTestProfile(t)
// enable
if err := Set("dashboard", "true", profile); err != nil {
t.Errorf("Disable returned unexpected error: " + err.Error())
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
tmpProfile := "temp-minikube-profile"
if err := os.MkdirAll(config.ProfileFolderPath(tmpProfile), 0777); err != nil {
t.Fatalf("error creating temporary directory")
}
defer os.RemoveAll(config.ProfileFolderPath(tmpProfile))
c, err := config.DefaultLoader.LoadConfigFromFile(profile)
if err != nil {
t.Errorf("unable to load profile: %v", err)
}
if c.Addons["dashboard"] != true {
t.Errorf("expected dashboard to be enabled")
}
if err := config.DefaultLoader.WriteConfigToFile(tmpProfile, &config.MachineConfig{}); err != nil {
t.Fatalf("error creating temporary profile config: %v", err)
}
if err := Set("dashboard", fmt.Sprintf("%t", test.enable), tmpProfile); err != nil {
t.Fatalf("Disable returned unexpected error: " + err.Error())
}
c, err := config.DefaultLoader.LoadConfigFromFile(tmpProfile)
if err != nil {
t.Fatalf("error loading config: %v", err)
}
assert.Equal(t, c.Addons["dashboard"], test.enable)
})
// disable
if err := Set("dashboard", "false", profile); err != nil {
t.Errorf("Disable returned unexpected error: " + err.Error())
}
c, err = config.DefaultLoader.LoadConfigFromFile(profile)
if err != nil {
t.Errorf("unable to load profile: %v", err)
}
if c.Addons["dashboard"] != false {
t.Errorf("expected dashboard to be enabled")
}
}
func TestStart(t *testing.T) {
profile := createTestProfile(t)
Start(profile, []string{"dashboard"})
enabled, err := assets.Addons["dashboard"].IsEnabled(profile)
if err != nil {
t.Errorf("dashboard: %v", err)
}
if !enabled {
t.Errorf("expected dashboard to be enabled")
}
}

View File

@ -17,7 +17,6 @@ limitations under the License.
package addons
import (
"os"
"os/exec"
"path"
@ -53,7 +52,7 @@ func kubectlCommand(profile string, files []string, enable bool) (*exec.Cmd, err
func kubernetesVersion(profile string) (string, error) {
cc, err := config.Load(profile)
if err != nil && !os.IsNotExist(err) {
if err != nil && !config.IsNotExist(err) {
return "", err
}
version := constants.DefaultKubernetesVersion

View File

@ -23,8 +23,8 @@ import (
"path/filepath"
"runtime"
"github.com/golang/glog"
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/localpath"
@ -54,14 +54,21 @@ func (a *Addon) Name() string {
return a.addonName
}
// IsEnabled checks if an Addon is enabled for the current profile
func (a *Addon) IsEnabled() (bool, error) {
c, err := config.Load(viper.GetString(config.MachineProfile))
if err == nil {
if status, ok := c.Addons[a.Name()]; ok {
return status, nil
}
// IsEnabled checks if an Addon is enabled for the given profile
func (a *Addon) IsEnabled(profile string) (bool, error) {
c, err := config.Load(profile)
if err != nil {
return false, errors.Wrap(err, "load")
}
// Is this addon explicitly listed in their configuration?
status, ok := c.Addons[a.Name()]
glog.V(1).Infof("IsEnabled %q = %v (listed in config=%v)", a.Name(), status, ok)
if ok {
return status, nil
}
// Return the default unconfigured state of the addon
return a.enabled, nil
}

View File

@ -20,7 +20,6 @@ package bsutil
import (
"path"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/vmpath"
@ -52,33 +51,3 @@ func ConfigFileAssets(cfg config.KubernetesConfig, kubeadm []byte, kubelet []byt
}
return fs
}
// AddAddons adds addons to list of files
func AddAddons(files *[]assets.CopyableFile, data interface{}) error {
// add addons to file list
// custom addons
if err := assets.AddMinikubeDirAssets(files); err != nil {
return errors.Wrap(err, "adding minikube dir assets")
}
// bundled addons
for _, addonBundle := range assets.Addons {
if isEnabled, err := addonBundle.IsEnabled(); err == nil && isEnabled {
for _, addon := range addonBundle.Assets {
if addon.IsTemplate() {
addonFile, err := addon.Evaluate(data)
if err != nil {
return errors.Wrapf(err, "evaluate bundled addon %s asset", addon.GetAssetName())
}
*files = append(*files, addonFile)
} else {
*files = append(*files, addon)
}
}
} else if err != nil {
return nil
}
}
return nil
}

View File

@ -120,7 +120,7 @@ func TestGenerateKubeadmYAMLDNS(t *testing.T) {
t.Run(tname, func(t *testing.T) {
cfg := tc.cfg
cfg.Nodes = []config.Node{
config.Node{
{
IP: "1.1.1.1",
Name: "mk",
ControlPlane: true,
@ -179,7 +179,7 @@ func TestGenerateKubeadmYAML(t *testing.T) {
{"options", "docker", false, config.MachineConfig{KubernetesConfig: config.KubernetesConfig{ExtraOptions: extraOpts}}},
{"crio-options-gates", "crio", false, config.MachineConfig{KubernetesConfig: config.KubernetesConfig{ExtraOptions: extraOpts, FeatureGates: "a=b"}}},
{"unknown-component", "docker", true, config.MachineConfig{KubernetesConfig: config.KubernetesConfig{ExtraOptions: config.ExtraOptionSlice{config.ExtraOption{Component: "not-a-real-component", Key: "killswitch", Value: "true"}}}}},
{"containerd-api-port", "containerd", false, config.MachineConfig{Nodes: []config.Node{config.Node{Port: 12345}}}},
{"containerd-api-port", "containerd", false, config.MachineConfig{Nodes: []config.Node{{Port: 12345}}}},
{"containerd-pod-network-cidr", "containerd", false, config.MachineConfig{KubernetesConfig: config.KubernetesConfig{ExtraOptions: extraOptsPodCidr}}},
{"image-repository", "docker", false, config.MachineConfig{KubernetesConfig: config.KubernetesConfig{ImageRepository: "test/repo"}}},
}
@ -199,7 +199,7 @@ func TestGenerateKubeadmYAML(t *testing.T) {
cfg.Nodes[0].ControlPlane = true
} else {
cfg.Nodes = []config.Node{
config.Node{
{
IP: "1.1.1.1",
Name: "mk",
ControlPlane: true,

View File

@ -42,7 +42,7 @@ func TestGenerateKubeletConfig(t *testing.T) {
ContainerRuntime: "docker",
},
Nodes: []config.Node{
config.Node{
{
IP: "192.168.1.100",
Name: "minikube",
ControlPlane: true,
@ -67,7 +67,7 @@ ExecStart=/var/lib/minikube/binaries/v1.11.10/kubelet --allow-privileged=true --
ContainerRuntime: "cri-o",
},
Nodes: []config.Node{
config.Node{
{
IP: "192.168.1.100",
Name: "minikube",
ControlPlane: true,
@ -92,7 +92,7 @@ ExecStart=/var/lib/minikube/binaries/v1.17.2/kubelet --authorization-mode=Webhoo
ContainerRuntime: "containerd",
},
Nodes: []config.Node{
config.Node{
{
IP: "192.168.1.100",
Name: "minikube",
ControlPlane: true,
@ -124,7 +124,7 @@ ExecStart=/var/lib/minikube/binaries/v1.17.2/kubelet --authorization-mode=Webhoo
},
},
Nodes: []config.Node{
config.Node{
{
IP: "192.168.1.100",
Name: "minikube",
ControlPlane: true,
@ -150,7 +150,7 @@ ExecStart=/var/lib/minikube/binaries/v1.17.2/kubelet --authorization-mode=Webhoo
ImageRepository: "docker-proxy-image.io/google_containers",
},
Nodes: []config.Node{
config.Node{
{
IP: "192.168.1.100",
Name: "minikube",
ControlPlane: true,

View File

@ -41,7 +41,6 @@ import (
"k8s.io/minikube/pkg/drivers/kic"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/kapi"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/bootstrapper"
"k8s.io/minikube/pkg/minikube/bootstrapper/bsutil"
"k8s.io/minikube/pkg/minikube/bootstrapper/bsutil/kverify"
@ -493,10 +492,6 @@ func (k *Bootstrapper) UpdateCluster(cfg config.MachineConfig) error {
}
files := bsutil.ConfigFileAssets(cfg.KubernetesConfig, kubeadmCfg, kubeletCfg, kubeletService, cniFile)
if err := bsutil.AddAddons(&files, assets.GenerateTemplateData(cfg.KubernetesConfig)); err != nil {
return errors.Wrap(err, "adding addons")
}
// Combine mkdir request into a single call to reduce load
dirs := []string{}
for _, f := range files {

View File

@ -640,17 +640,18 @@ func getIPForInterface(name string) (net.IP, error) {
// CheckIfHostExistsAndLoad checks if a host exists, and loads it if it does
func CheckIfHostExistsAndLoad(api libmachine.API, machineName string) (*host.Host, error) {
glog.Infof("Checking if %q exists ...", machineName)
exists, err := api.Exists(machineName)
if err != nil {
return nil, errors.Wrapf(err, "Error checking that machine exists: %s", machineName)
}
if !exists {
return nil, errors.Errorf("Machine does not exist for api.Exists(%s)", machineName)
return nil, errors.Errorf("machine %q does not exist", machineName)
}
host, err := api.Load(machineName)
if err != nil {
return nil, errors.Wrapf(err, "Error loading store for: %s", machineName)
return nil, errors.Wrapf(err, "loading machine %q", machineName)
}
return host, nil
}
@ -679,14 +680,15 @@ func CreateSSHShell(api libmachine.API, args []string) error {
return client.Shell(args...)
}
// IsMinikubeRunning checks that minikube has a status available and that
// the status is `Running`
func IsMinikubeRunning(api libmachine.API) bool {
s, err := GetHostStatus(api, viper.GetString(config.MachineProfile))
// IsHostRunning asserts that this profile's primary host is in state "Running"
func IsHostRunning(api libmachine.API, name string) bool {
s, err := GetHostStatus(api, name)
if err != nil {
glog.Warningf("host status for %q returned error: %v", name, err)
return false
}
if s != state.Running.String() {
glog.Warningf("%q host status: %s", name, s)
return false
}
return true

View File

@ -18,12 +18,13 @@ package config
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/localpath"
)
@ -53,6 +54,22 @@ var (
ErrKeyNotFound = errors.New("specified key could not be found in config")
)
type ErrNotExist struct {
s string
}
func (e *ErrNotExist) Error() string {
return e.s
}
// IsNotExist returns whether the error means a nonexistent configuration
func IsNotExist(err error) bool {
if _, ok := err.(*ErrNotExist); ok {
return true
}
return false
}
// MinikubeConfig represents minikube config
type MinikubeConfig map[string]interface{}
@ -148,17 +165,20 @@ func (c *simpleConfigLoader) LoadConfigFromFile(profileName string, miniHome ...
// Move to profile package
path := profileFilePath(profileName, miniHome...)
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return nil, &ErrNotExist{fmt.Sprintf("cluster %q does not exist", profileName)}
}
return nil, errors.Wrap(err, "stat")
}
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "read")
}
if err := json.Unmarshal(data, &cc); err != nil {
return nil, err
return nil, errors.Wrap(err, "unmarshal")
}
return &cc, nil
}

View File

@ -116,6 +116,8 @@ var styles = map[StyleEnum]style{
MountOptions: {Prefix: "💾 "},
Fileserver: {Prefix: "🚀 ", OmitNewline: true},
DryRun: {Prefix: "🏜️ "},
AddonEnable: {Prefix: "🌟 "},
AddonDisable: {Prefix: "🌑 "},
}
// Add a prefix to a string

View File

@ -87,4 +87,6 @@ const (
Pause
Unpause
DryRun
AddonEnable
AddonDisable
)

View File

@ -40,10 +40,14 @@ func TestAPIError(t *testing.T) {
machineAPI, configLoader, machineName,
}
s, r, err := inspector.getStateAndRoute()
_, _, err := inspector.getStateAndRoute()
if err == nil {
t.Errorf("expected error, got nil")
}
if err == nil || !strings.Contains(err.Error(), "Machine does not exist") {
t.Errorf("cluster inspector should propagate errors from API, getStateAndRoute() returned \"%v, %v\", %v", s, r, err)
// Make sure we properly propagate errors upward
if !strings.Contains(err.Error(), "exist") {
t.Errorf("getStateAndRoute error=%q, expected *exist*", err)
}
}