fix(cmd/start): prevent k8s version downgrades

- Create Config struct to store MachineConfig and KubernetesConfig as cluster
configuration.
- Write cluster configuration under $MINIKUBE_HOME/profiles/ directory
when a cluster is launched.
- Load the cluster configuration at `start` and compare the loaded k8s
version with the requested version. Prevent any version downgrade requests.
pull/1772/head
Sunny 2017-07-30 19:15:02 +05:30
parent 245c4a04de
commit e07eb01366
3 changed files with 128 additions and 5 deletions

View File

@ -17,6 +17,7 @@ limitations under the License.
package cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
@ -26,6 +27,7 @@ import (
"strings"
"time"
"github.com/blang/semver"
units "github.com/docker/go-units"
"github.com/docker/machine/libmachine/host"
"github.com/golang/glog"
@ -40,6 +42,7 @@ import (
"k8s.io/minikube/pkg/util"
pkgutil "k8s.io/minikube/pkg/util"
"k8s.io/minikube/pkg/util/kubeconfig"
"k8s.io/minikube/pkg/version"
)
const (
@ -142,8 +145,37 @@ func runStart(cmd *cobra.Command, args []string) {
glog.Errorln("Error getting VM IP address: ", err)
cmdUtil.MaybeReportErrorAndExit(err)
}
selectedKubernetesVersion := viper.GetString(kubernetesVersion)
// Load profile cluster config from file
cc, err := loadConfigFromFile(viper.GetString(cfg.MachineProfile))
if err != nil {
if os.IsNotExist(err) {
fmt.Println("No existing profile config found. A new profile config would be created.")
} else {
glog.Errorln("Error loading profile config: ", err)
}
} else {
oldKubernetesVersion, err := semver.Make(strings.TrimPrefix(cc.KubernetesConfig.KubernetesVersion, version.VersionPrefix))
if err != nil {
glog.Errorln("Error parsing version semver: ", err)
}
newKubernetesVersion, err := semver.Make(strings.TrimPrefix(viper.GetString(kubernetesVersion), version.VersionPrefix))
if err != nil {
glog.Errorln("Error parsing version semver: ", err)
}
// Check if it's an attempt to downgrade version. Avoid version downgrad.
if newKubernetesVersion.LT(oldKubernetesVersion) {
selectedKubernetesVersion = version.VersionPrefix + oldKubernetesVersion.String()
fmt.Println("Kubernetes version downgrade is not supported. Using version:", selectedKubernetesVersion)
}
}
kubernetesConfig := cluster.KubernetesConfig{
KubernetesVersion: viper.GetString(kubernetesVersion),
KubernetesVersion: selectedKubernetesVersion,
NodeIP: ip,
APIServerName: viper.GetString(apiServerName),
DNSDomain: viper.GetString(dnsDomain),
@ -153,6 +185,16 @@ func runStart(cmd *cobra.Command, args []string) {
ExtraOptions: extraOptions,
}
// Write profile cluster configuration to file
clusterConfig := cluster.Config{
MachineConfig: config,
KubernetesConfig: kubernetesConfig,
}
if err := saveConfig(clusterConfig); err != nil {
glog.Errorln("Error saving profile cluster configuration: ", err)
}
fmt.Println("Moving files into cluster...")
if err := cluster.UpdateCluster(host.Driver, kubernetesConfig); err != nil {
glog.Errorln("Error updating cluster: ", err)
@ -312,3 +354,73 @@ func init() {
viper.BindPFlags(startCmd.Flags())
RootCmd.AddCommand(startCmd)
}
// saveConfig saves profile cluster configuration in
// $MINIKUBE_HOME/profiles/<profilename>/config.json
func saveConfig(clusterConfig cluster.Config) error {
data, err := json.MarshalIndent(clusterConfig, "", " ")
if err != nil {
return err
}
profileConfigFile := constants.GetProfileFile(viper.GetString(cfg.MachineProfile))
if err := os.MkdirAll(filepath.Dir(profileConfigFile), 0700); err != nil {
return err
}
if err := saveConfigToFile(data, profileConfigFile); err != nil {
return err
}
return nil
}
func saveConfigToFile(data []byte, file string) error {
if _, err := os.Stat(file); os.IsNotExist(err) {
return ioutil.WriteFile(file, data, 0600)
}
tmpfi, err := ioutil.TempFile(filepath.Dir(file), "config.json.tmp")
if err != nil {
return err
}
defer os.Remove(tmpfi.Name())
if err = ioutil.WriteFile(tmpfi.Name(), data, 0600); err != nil {
return err
}
if err = tmpfi.Close(); err != nil {
return err
}
if err = os.Remove(file); err != nil {
return err
}
if err = os.Rename(tmpfi.Name(), file); err != nil {
return err
}
return nil
}
func loadConfigFromFile(profile string) (cluster.Config, error) {
var cc cluster.Config
profileConfigFile := constants.GetProfileFile(profile)
if _, err := os.Stat(profileConfigFile); os.IsNotExist(err) {
return cc, err
}
data, err := ioutil.ReadFile(profileConfigFile)
if err != nil {
return cc, err
}
if err := json.Unmarshal(data, &cc); err != nil {
return cc, err
}
return cc, nil
}

View File

@ -31,10 +31,10 @@ type MachineConfig struct {
RegistryMirror []string
HostOnlyCIDR string // Only used by the virtualbox driver
HypervVirtualSwitch string
KvmNetwork string // Only used by the KVM driver
Downloader util.ISODownloader
DockerOpt []string // Each entry is formatted as KEY=VALUE.
DisableDriverMounts bool // Only used by virtualbox and xhyve
KvmNetwork string // Only used by the KVM driver
Downloader util.ISODownloader `json:"-"`
DockerOpt []string // Each entry is formatted as KEY=VALUE.
DisableDriverMounts bool // Only used by virtualbox and xhyve
}
// KubernetesConfig contains the parameters used to configure the VM Kubernetes.
@ -48,3 +48,9 @@ type KubernetesConfig struct {
FeatureGates string
ExtraOptions util.ExtraOptionSlice
}
// Config contains machine and k8s config
type Config struct {
MachineConfig MachineConfig
KubernetesConfig KubernetesConfig
}

View File

@ -107,6 +107,11 @@ var DefaultKubernetesVersion = version.Get().GitVersion
var ConfigFilePath = MakeMiniPath("config")
var ConfigFile = MakeMiniPath("config", "config.json")
// GetProfileFile returns the Minikube profile config file
func GetProfileFile(profile string) string {
return filepath.Join(GetMinipath(), "profiles", profile, "config.json")
}
var LocalkubeDownloadURLPrefix = "https://storage.googleapis.com/minikube/k8sReleases/"
var LocalkubeLinuxFilename = "localkube-linux-amd64"