Merge pull request #5068 from medyagh/profile_create

Create empty profile if profile is set to a not-existing profile
pull/5056/head
Medya Ghazizadeh 2019-08-13 17:59:31 -07:00 committed by GitHub
commit 29a1eee54c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 379 additions and 69 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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) {

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -0,0 +1 @@
invalid json file :)

View 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
}
}

View File

@ -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"