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
parent
245c4a04de
commit
e07eb01366
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -26,6 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/blang/semver"
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
"github.com/docker/machine/libmachine/host"
|
"github.com/docker/machine/libmachine/host"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
@ -40,6 +42,7 @@ import (
|
||||||
"k8s.io/minikube/pkg/util"
|
"k8s.io/minikube/pkg/util"
|
||||||
pkgutil "k8s.io/minikube/pkg/util"
|
pkgutil "k8s.io/minikube/pkg/util"
|
||||||
"k8s.io/minikube/pkg/util/kubeconfig"
|
"k8s.io/minikube/pkg/util/kubeconfig"
|
||||||
|
"k8s.io/minikube/pkg/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -142,8 +145,37 @@ func runStart(cmd *cobra.Command, args []string) {
|
||||||
glog.Errorln("Error getting VM IP address: ", err)
|
glog.Errorln("Error getting VM IP address: ", err)
|
||||||
cmdUtil.MaybeReportErrorAndExit(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{
|
kubernetesConfig := cluster.KubernetesConfig{
|
||||||
KubernetesVersion: viper.GetString(kubernetesVersion),
|
KubernetesVersion: selectedKubernetesVersion,
|
||||||
NodeIP: ip,
|
NodeIP: ip,
|
||||||
APIServerName: viper.GetString(apiServerName),
|
APIServerName: viper.GetString(apiServerName),
|
||||||
DNSDomain: viper.GetString(dnsDomain),
|
DNSDomain: viper.GetString(dnsDomain),
|
||||||
|
@ -153,6 +185,16 @@ func runStart(cmd *cobra.Command, args []string) {
|
||||||
ExtraOptions: extraOptions,
|
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...")
|
fmt.Println("Moving files into cluster...")
|
||||||
if err := cluster.UpdateCluster(host.Driver, kubernetesConfig); err != nil {
|
if err := cluster.UpdateCluster(host.Driver, kubernetesConfig); err != nil {
|
||||||
glog.Errorln("Error updating cluster: ", err)
|
glog.Errorln("Error updating cluster: ", err)
|
||||||
|
@ -312,3 +354,73 @@ func init() {
|
||||||
viper.BindPFlags(startCmd.Flags())
|
viper.BindPFlags(startCmd.Flags())
|
||||||
RootCmd.AddCommand(startCmd)
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ type MachineConfig struct {
|
||||||
HostOnlyCIDR string // Only used by the virtualbox driver
|
HostOnlyCIDR string // Only used by the virtualbox driver
|
||||||
HypervVirtualSwitch string
|
HypervVirtualSwitch string
|
||||||
KvmNetwork string // Only used by the KVM driver
|
KvmNetwork string // Only used by the KVM driver
|
||||||
Downloader util.ISODownloader
|
Downloader util.ISODownloader `json:"-"`
|
||||||
DockerOpt []string // Each entry is formatted as KEY=VALUE.
|
DockerOpt []string // Each entry is formatted as KEY=VALUE.
|
||||||
DisableDriverMounts bool // Only used by virtualbox and xhyve
|
DisableDriverMounts bool // Only used by virtualbox and xhyve
|
||||||
}
|
}
|
||||||
|
@ -48,3 +48,9 @@ type KubernetesConfig struct {
|
||||||
FeatureGates string
|
FeatureGates string
|
||||||
ExtraOptions util.ExtraOptionSlice
|
ExtraOptions util.ExtraOptionSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Config contains machine and k8s config
|
||||||
|
type Config struct {
|
||||||
|
MachineConfig MachineConfig
|
||||||
|
KubernetesConfig KubernetesConfig
|
||||||
|
}
|
||||||
|
|
|
@ -107,6 +107,11 @@ var DefaultKubernetesVersion = version.Get().GitVersion
|
||||||
var ConfigFilePath = MakeMiniPath("config")
|
var ConfigFilePath = MakeMiniPath("config")
|
||||||
var ConfigFile = MakeMiniPath("config", "config.json")
|
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 LocalkubeDownloadURLPrefix = "https://storage.googleapis.com/minikube/k8sReleases/"
|
||||||
var LocalkubeLinuxFilename = "localkube-linux-amd64"
|
var LocalkubeLinuxFilename = "localkube-linux-amd64"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue