Merge pull request #5068 from medyagh/profile_create
Create empty profile if profile is set to a not-existing profilepull/5056/head
commit
29a1eee54c
|
@ -48,6 +48,15 @@ var ProfileCmd = &cobra.Command{
|
|||
if profile == "default" {
|
||||
profile = "minikube"
|
||||
}
|
||||
|
||||
if !pkgConfig.ProfileExists(profile) {
|
||||
err := pkgConfig.CreateEmptyProfile(profile)
|
||||
if err != nil {
|
||||
exit.WithError("Creating a new profile failed", err)
|
||||
}
|
||||
out.SuccessT("Created a new profile : {{.profile_name}}", out.V{"profile_name": profile})
|
||||
}
|
||||
|
||||
err := Set(pkgConfig.MachineProfile, profile)
|
||||
if err != nil {
|
||||
exit.WithError("Setting profile failed", err)
|
||||
|
|
|
@ -84,7 +84,7 @@ func runDelete(cmd *cobra.Command, args []string) {
|
|||
out.FatalT("Failed to kill mount process: {{.error}}", out.V{"error": err})
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(constants.GetProfilePath(viper.GetString(pkg_config.MachineProfile))); err != nil {
|
||||
if err := pkg_config.DeleteProfile(viper.GetString(pkg_config.MachineProfile)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
out.T(out.Meh, `"{{.profile_name}}" profile does not exist`, out.V{"profile_name": profile})
|
||||
os.Exit(0)
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
@ -32,9 +31,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/minikube/pkg/minikube/drivers/none"
|
||||
"k8s.io/minikube/pkg/minikube/kubeconfig"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
|
@ -54,7 +50,9 @@ import (
|
|||
cfg "k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/minikube/cruntime"
|
||||
"k8s.io/minikube/pkg/minikube/drivers/none"
|
||||
"k8s.io/minikube/pkg/minikube/exit"
|
||||
"k8s.io/minikube/pkg/minikube/kubeconfig"
|
||||
"k8s.io/minikube/pkg/minikube/logs"
|
||||
"k8s.io/minikube/pkg/minikube/machine"
|
||||
"k8s.io/minikube/pkg/minikube/out"
|
||||
|
@ -920,47 +918,8 @@ func configureMounts() {
|
|||
}
|
||||
|
||||
// saveConfig saves profile cluster configuration in $MINIKUBE_HOME/profiles/<profilename>/config.json
|
||||
func saveConfig(clusterConfig *cfg.Config) error {
|
||||
data, err := json.MarshalIndent(clusterConfig, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infof("Saving config:\n%s", data)
|
||||
path := constants.GetProfileFile(viper.GetString(cfg.MachineProfile))
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If no config file exists, don't worry about swapping paths
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
if err := ioutil.WriteFile(path, data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
tf, err := ioutil.TempFile(filepath.Dir(path), "config.json.tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
if err = ioutil.WriteFile(tf.Name(), data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tf.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.Remove(path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.Rename(tf.Name(), path); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func saveConfig(clusterCfg *cfg.Config) error {
|
||||
return cfg.CreateProfile(viper.GetString(cfg.MachineProfile), clusterCfg)
|
||||
}
|
||||
|
||||
func validateDriverVersion(vmDriver string) {
|
||||
|
|
|
@ -21,9 +21,8 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
|
@ -152,10 +151,10 @@ type simpleConfigLoader struct{}
|
|||
// DefaultLoader is the default config loader
|
||||
var DefaultLoader Loader = &simpleConfigLoader{}
|
||||
|
||||
func (c *simpleConfigLoader) LoadConfigFromFile(profile string, miniHome ...string) (*Config, error) {
|
||||
func (c *simpleConfigLoader) LoadConfigFromFile(profileName string, miniHome ...string) (*Config, error) {
|
||||
var cc Config
|
||||
|
||||
path := constants.GetProfileFile(profile, miniHome...)
|
||||
// Move to profile package
|
||||
path := profileFilePath(profileName, miniHome...)
|
||||
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return nil, err
|
||||
|
|
|
@ -17,9 +17,12 @@ limitations under the License.
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
)
|
||||
|
||||
|
@ -34,6 +37,76 @@ func (p *Profile) isValid() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// ProfileExists returns true if there is a profile config (regardless of being valid)
|
||||
func ProfileExists(name string, miniHome ...string) bool {
|
||||
miniPath := constants.GetMinipath()
|
||||
if len(miniHome) > 0 {
|
||||
miniPath = miniHome[0]
|
||||
}
|
||||
|
||||
p := profileFilePath(name, miniPath)
|
||||
_, err := os.Stat(p)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// CreateProfile creates an empty profile stores in $MINIKUBE_HOME/profiles/<profilename>/config.json
|
||||
func CreateEmptyProfile(name string, miniHome ...string) error {
|
||||
cfg := &Config{}
|
||||
return CreateProfile(name, cfg, miniHome...)
|
||||
}
|
||||
|
||||
// CreateProfile creates an profile out of the cfg and stores in $MINIKUBE_HOME/profiles/<profilename>/config.json
|
||||
func CreateProfile(name string, cfg *Config, miniHome ...string) error {
|
||||
data, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infof("Saving config:\n%s", data)
|
||||
path := profileFilePath(name, miniHome...)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If no config file exists, don't worry about swapping paths
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
if err := ioutil.WriteFile(path, data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
tf, err := ioutil.TempFile(filepath.Dir(path), "config.json.tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
if err = ioutil.WriteFile(tf.Name(), data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tf.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.Remove(path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.Rename(tf.Name(), path); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteProfile(profile string, miniHome ...string) error {
|
||||
miniPath := constants.GetMinipath()
|
||||
if len(miniHome) > 0 {
|
||||
miniPath = miniHome[0]
|
||||
}
|
||||
return os.RemoveAll(profileFolderPath(profile, miniPath))
|
||||
}
|
||||
|
||||
// ListProfiles returns all valid and invalid (if any) minikube profiles
|
||||
// invalidPs are the profiles that have a directory or config file but not usable
|
||||
// invalidPs would be suggeted to be deleted
|
||||
|
@ -82,3 +155,22 @@ func profileDirs(miniHome ...string) (dirs []string, err error) {
|
|||
}
|
||||
return dirs, err
|
||||
}
|
||||
|
||||
// profileFilePath returns the Minikube profile config file
|
||||
func profileFilePath(profile string, miniHome ...string) string {
|
||||
miniPath := constants.GetMinipath()
|
||||
if len(miniHome) > 0 {
|
||||
miniPath = miniHome[0]
|
||||
}
|
||||
|
||||
return filepath.Join(miniPath, "profiles", profile, "config.json")
|
||||
}
|
||||
|
||||
// profileFolderPath returns path of profile folder
|
||||
func profileFolderPath(profile string, miniHome ...string) string {
|
||||
miniPath := constants.GetMinipath()
|
||||
if len(miniHome) > 0 {
|
||||
miniPath = miniHome[0]
|
||||
}
|
||||
return filepath.Join(miniPath, "profiles", profile)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
// TestListProfiles uses a different uses different MINIKUBE_HOME with rest of tests since it relies on file list index
|
||||
func TestListProfiles(t *testing.T) {
|
||||
miniDir, err := filepath.Abs("./testdata/.minikube")
|
||||
if err != nil {
|
||||
|
@ -70,3 +71,124 @@ func TestListProfiles(t *testing.T) {
|
|||
t.Errorf("error listing profiles %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileExists(t *testing.T) {
|
||||
miniDir, err := filepath.Abs("./testdata/.minikube2")
|
||||
if err != nil {
|
||||
t.Errorf("error getting dir path for ./testdata/.minikube : %v", err)
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{"p1", true},
|
||||
{"p2", true},
|
||||
{"p3_empty", true},
|
||||
{"p4_invalid_file", true},
|
||||
{"p5_partial_config", true},
|
||||
{"p6_no_file", false},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
got := ProfileExists(tt.name, miniDir)
|
||||
if got != tt.expected {
|
||||
t.Errorf("expected ProfileExists(%q,%q)=%t but got %t ", tt.name, miniDir, tt.expected, got)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCreateEmptyProfile(t *testing.T) {
|
||||
miniDir, err := filepath.Abs("./testdata/.minikube2")
|
||||
if err != nil {
|
||||
t.Errorf("error getting dir path for ./testdata/.minikube : %v", err)
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
name string
|
||||
expectErr bool
|
||||
}{
|
||||
{"p13", false},
|
||||
{"p_13", false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
n := tc.name // capturing loop variable
|
||||
gotErr := CreateEmptyProfile(n, miniDir)
|
||||
if gotErr != nil && tc.expectErr == false {
|
||||
t.Errorf("expected CreateEmptyProfile not to error but got err=%v", gotErr)
|
||||
}
|
||||
|
||||
defer func() { // tear down
|
||||
err := DeleteProfile(n, miniDir)
|
||||
if err != nil {
|
||||
t.Errorf("error test tear down %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCreateProfile(t *testing.T) {
|
||||
miniDir, err := filepath.Abs("./testdata/.minikube2")
|
||||
if err != nil {
|
||||
t.Errorf("error getting dir path for ./testdata/.minikube : %v", err)
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
name string
|
||||
cfg *Config
|
||||
expectErr bool
|
||||
}{
|
||||
{"p_empty_config", &Config{}, false},
|
||||
{"p_partial_config", &Config{KubernetesConfig: KubernetesConfig{
|
||||
ShouldLoadCachedImages: false}}, false},
|
||||
{"p_partial_config2", &Config{MachineConfig: MachineConfig{
|
||||
KeepContext: false}, KubernetesConfig: KubernetesConfig{
|
||||
ShouldLoadCachedImages: false}}, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
n := tc.name // capturing loop variable
|
||||
gotErr := CreateProfile(n, tc.cfg, miniDir)
|
||||
if gotErr != nil && tc.expectErr == false {
|
||||
t.Errorf("expected CreateEmptyProfile not to error but got err=%v", gotErr)
|
||||
}
|
||||
|
||||
defer func() { // tear down
|
||||
|
||||
err := DeleteProfile(n, miniDir)
|
||||
if err != nil {
|
||||
t.Errorf("error test tear down %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDeleteProfile(t *testing.T) {
|
||||
miniDir, err := filepath.Abs("./testdata/.minikube2")
|
||||
if err != nil {
|
||||
t.Errorf("error getting dir path for ./testdata/.minikube : %v", err)
|
||||
}
|
||||
|
||||
err = CreateEmptyProfile("existing_prof", miniDir)
|
||||
if err != nil {
|
||||
t.Errorf("error setting up TestDeleteProfile %v", err)
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
name string
|
||||
expectErr bool
|
||||
}{
|
||||
{"existing_prof", false},
|
||||
{"non_existing_prof", false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
gotErr := DeleteProfile(tc.name, miniDir)
|
||||
if gotErr != nil && tc.expectErr == false {
|
||||
t.Errorf("expected CreateEmptyProfile not to error but got err=%v", gotErr)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"MachineConfig": {
|
||||
"KeepContext": false,
|
||||
"MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso",
|
||||
"Memory": 2000,
|
||||
"CPUs": 2,
|
||||
"DiskSize": 20000,
|
||||
"VMDriver": "hyperkit",
|
||||
"ContainerRuntime": "docker",
|
||||
"HyperkitVpnKitSock": "",
|
||||
"HyperkitVSockPorts": [],
|
||||
"XhyveDiskDriver": "ahci-hd",
|
||||
"DockerEnv": null,
|
||||
"InsecureRegistry": null,
|
||||
"RegistryMirror": null,
|
||||
"HostOnlyCIDR": "192.168.99.1/24",
|
||||
"HypervVirtualSwitch": "",
|
||||
"KVMNetwork": "default",
|
||||
"KVMQemuURI": "qemu:///system",
|
||||
"KVMGPU": false,
|
||||
"KVMHidden": false,
|
||||
"DockerOpt": null,
|
||||
"DisableDriverMounts": false,
|
||||
"NFSShare": [],
|
||||
"NFSSharesRoot": "/nfsshares",
|
||||
"UUID": "",
|
||||
"NoVTXCheck": false,
|
||||
"DNSProxy": false,
|
||||
"HostDNSResolver": true
|
||||
},
|
||||
"KubernetesConfig": {
|
||||
"KubernetesVersion": "v1.15.0",
|
||||
"NodeIP": "192.168.64.75",
|
||||
"NodePort": 8443,
|
||||
"NodeName": "minikube",
|
||||
"APIServerName": "minikubeCA",
|
||||
"APIServerNames": null,
|
||||
"APIServerIPs": null,
|
||||
"DNSDomain": "cluster.local",
|
||||
"ContainerRuntime": "docker",
|
||||
"CRISocket": "",
|
||||
"NetworkPlugin": "",
|
||||
"FeatureGates": "",
|
||||
"ServiceCIDR": "10.96.0.0/12",
|
||||
"ImageRepository": "",
|
||||
"ExtraOptions": null,
|
||||
"ShouldLoadCachedImages": true,
|
||||
"EnableDefaultCNI": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"MachineConfig": {
|
||||
"KeepContext": false,
|
||||
"MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso",
|
||||
"Memory": 2000,
|
||||
"CPUs": 2,
|
||||
"DiskSize": 20000,
|
||||
"VMDriver": "virtualbox",
|
||||
"ContainerRuntime": "docker",
|
||||
"HyperkitVpnKitSock": "",
|
||||
"HyperkitVSockPorts": [],
|
||||
"DockerEnv": null,
|
||||
"InsecureRegistry": null,
|
||||
"RegistryMirror": null,
|
||||
"HostOnlyCIDR": "192.168.99.1/24",
|
||||
"HypervVirtualSwitch": "",
|
||||
"KVMNetwork": "default",
|
||||
"KVMQemuURI": "qemu:///system",
|
||||
"KVMGPU": false,
|
||||
"KVMHidden": false,
|
||||
"DockerOpt": null,
|
||||
"DisableDriverMounts": false,
|
||||
"NFSShare": [],
|
||||
"NFSSharesRoot": "/nfsshares",
|
||||
"UUID": "",
|
||||
"NoVTXCheck": false,
|
||||
"DNSProxy": false,
|
||||
"HostDNSResolver": true
|
||||
},
|
||||
"KubernetesConfig": {
|
||||
"KubernetesVersion": "v1.15.0",
|
||||
"NodeIP": "192.168.99.136",
|
||||
"NodePort": 8443,
|
||||
"NodeName": "minikube",
|
||||
"APIServerName": "minikubeCA",
|
||||
"APIServerNames": null,
|
||||
"APIServerIPs": null,
|
||||
"DNSDomain": "cluster.local",
|
||||
"ContainerRuntime": "docker",
|
||||
"CRISocket": "",
|
||||
"NetworkPlugin": "",
|
||||
"FeatureGates": "",
|
||||
"ServiceCIDR": "10.96.0.0/12",
|
||||
"ImageRepository": "",
|
||||
"ExtraOptions": null,
|
||||
"ShouldLoadCachedImages": true,
|
||||
"EnableDefaultCNI": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
invalid json file :)
|
47
pkg/minikube/config/testdata/.minikube2/profiles/p5_partial_config/config.json
vendored
Normal file
47
pkg/minikube/config/testdata/.minikube2/profiles/p5_partial_config/config.json
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"MachineConfig": {
|
||||
"KeepContext": false,
|
||||
"MinikubeISO": "https://storage.googleapis.com/minikube/iso/minikube-v1.2.0.iso",
|
||||
"Memory": 2000,
|
||||
"CPUs": 2,
|
||||
"DiskSize": 20000,
|
||||
"ContainerRuntime": "docker",
|
||||
"HyperkitVpnKitSock": "",
|
||||
"HyperkitVSockPorts": [],
|
||||
"XhyveDiskDriver": "ahci-hd",
|
||||
"DockerEnv": null,
|
||||
"InsecureRegistry": null,
|
||||
"RegistryMirror": null,
|
||||
"HostOnlyCIDR": "192.168.99.1/24",
|
||||
"HypervVirtualSwitch": "",
|
||||
"KVMNetwork": "default",
|
||||
"KVMQemuURI": "qemu:///system",
|
||||
"KVMGPU": false,
|
||||
"KVMHidden": false,
|
||||
"DockerOpt": null,
|
||||
"DisableDriverMounts": false,
|
||||
"NFSShare": [],
|
||||
"NFSSharesRoot": "/nfsshares",
|
||||
"UUID": "",
|
||||
"NoVTXCheck": false,
|
||||
"DNSProxy": false,
|
||||
"HostDNSResolver": true
|
||||
},
|
||||
"KubernetesConfig": {
|
||||
"NodePort": 8443,
|
||||
"NodeName": "minikube",
|
||||
"APIServerName": "minikubeCA",
|
||||
"APIServerNames": null,
|
||||
"APIServerIPs": null,
|
||||
"DNSDomain": "cluster.local",
|
||||
"ContainerRuntime": "docker",
|
||||
"CRISocket": "",
|
||||
"NetworkPlugin": "",
|
||||
"FeatureGates": "",
|
||||
"ServiceCIDR": "10.96.0.0/12",
|
||||
"ImageRepository": "",
|
||||
"ExtraOptions": null,
|
||||
"ShouldLoadCachedImages": true,
|
||||
"EnableDefaultCNI": false
|
||||
}
|
||||
}
|
|
@ -192,24 +192,6 @@ var ConfigFilePath = MakeMiniPath("config")
|
|||
// ConfigFile is the path of the config file
|
||||
var ConfigFile = MakeMiniPath("config", "config.json")
|
||||
|
||||
// GetProfileFile returns the Minikube profile config file
|
||||
func GetProfileFile(profile string, miniHome ...string) string {
|
||||
miniPath := GetMinipath()
|
||||
if len(miniHome) > 0 {
|
||||
miniPath = miniHome[0]
|
||||
}
|
||||
return filepath.Join(miniPath, "profiles", profile, "config.json")
|
||||
}
|
||||
|
||||
// GetProfilePath returns the Minikube profile path of config file
|
||||
func GetProfilePath(profile string, miniHome ...string) string {
|
||||
miniPath := GetMinipath()
|
||||
if len(miniHome) > 0 {
|
||||
miniPath = miniHome[0]
|
||||
}
|
||||
return filepath.Join(miniPath, "profiles", profile)
|
||||
}
|
||||
|
||||
// AddonsPath is the default path of the addons configuration
|
||||
const AddonsPath = "/etc/kubernetes/addons"
|
||||
|
||||
|
|
Loading…
Reference in New Issue