From 90a9cd8ca1cb8dc4dce3cf375b898196b595979e Mon Sep 17 00:00:00 2001 From: Sandeep Rajan Date: Fri, 17 Nov 2017 11:41:14 -0500 Subject: [PATCH] support upgrade plan for coredns --- cmd/kubeadm/app/cmd/phases/addons.go | 19 ++++++++++-- cmd/kubeadm/app/cmd/upgrade/BUILD | 1 + cmd/kubeadm/app/cmd/upgrade/apply.go | 2 +- cmd/kubeadm/app/cmd/upgrade/common.go | 11 ++++++- cmd/kubeadm/app/cmd/upgrade/plan.go | 17 +++++++---- cmd/kubeadm/app/cmd/upgrade/plan_test.go | 3 +- cmd/kubeadm/app/cmd/upgrade/upgrade.go | 6 ++++ cmd/kubeadm/app/constants/constants.go | 2 +- cmd/kubeadm/app/phases/upgrade/BUILD | 1 + cmd/kubeadm/app/phases/upgrade/compute.go | 24 ++++++++++----- .../app/phases/upgrade/compute_test.go | 3 +- cmd/kubeadm/app/phases/upgrade/postupgrade.go | 29 +++++++++++++++++++ 12 files changed, 96 insertions(+), 22 deletions(-) diff --git a/cmd/kubeadm/app/cmd/phases/addons.go b/cmd/kubeadm/app/cmd/phases/addons.go index 19b1433f2d3..c837113f9b5 100644 --- a/cmd/kubeadm/app/cmd/phases/addons.go +++ b/cmd/kubeadm/app/cmd/phases/addons.go @@ -17,6 +17,8 @@ limitations under the License. package phases import ( + "strings" + "github.com/spf13/cobra" clientset "k8s.io/client-go/kubernetes" @@ -24,6 +26,7 @@ import ( kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" + "k8s.io/kubernetes/cmd/kubeadm/app/features" dnsaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" proxyaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" @@ -93,7 +96,7 @@ func getAddonsSubCommands() []*cobra.Command { // Default values for the cobra help text legacyscheme.Scheme.Default(cfg) - var cfgPath, kubeConfigFile string + var cfgPath, kubeConfigFile, featureGatesString string var subCmds []*cobra.Command subCmdProperties := []struct { @@ -131,7 +134,7 @@ func getAddonsSubCommands() []*cobra.Command { Short: properties.short, Long: properties.long, Example: properties.examples, - Run: runAddonsCmdFunc(properties.cmdFunc, cfg, &kubeConfigFile, &cfgPath), + Run: runAddonsCmdFunc(properties.cmdFunc, cfg, &kubeConfigFile, &cfgPath, &featureGatesString), } // Add flags to the command @@ -149,6 +152,8 @@ func getAddonsSubCommands() []*cobra.Command { if properties.use == "all" || properties.use == "kube-dns" { cmd.Flags().StringVar(&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain, `Alternative domain for services`) cmd.Flags().StringVar(&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet, `The range of IP address used for service VIPs`) + cmd.Flags().StringVar(&featureGatesString, "feature-gates", featureGatesString, "A set of key=value pairs that describe feature gates for various features."+ + "Options are:\n"+strings.Join(features.KnownFeatures(&features.InitFeatureGates), "\n")) } subCmds = append(subCmds, cmd) } @@ -157,7 +162,7 @@ func getAddonsSubCommands() []*cobra.Command { } // runAddonsCmdFunc creates a cobra.Command Run function, by composing the call to the given cmdFunc with necessary additional steps (e.g preparation of input parameters) -func runAddonsCmdFunc(cmdFunc func(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error, cfg *kubeadmapiext.MasterConfiguration, kubeConfigFile *string, cfgPath *string) func(cmd *cobra.Command, args []string) { +func runAddonsCmdFunc(cmdFunc func(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error, cfg *kubeadmapiext.MasterConfiguration, kubeConfigFile *string, cfgPath *string, featureGatesString *string) func(cmd *cobra.Command, args []string) { // the following statement build a clousure that wraps a call to a cmdFunc, binding // the function itself with the specific parameters of each sub command. @@ -165,16 +170,24 @@ func runAddonsCmdFunc(cmdFunc func(cfg *kubeadmapi.MasterConfiguration, client c // are shared between sub commands and gets access to current value e.g. flags value. return func(cmd *cobra.Command, args []string) { + var err error if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil { kubeadmutil.CheckErr(err) } + if cfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, *featureGatesString); err != nil { + kubeadmutil.CheckErr(err) + } + internalcfg := &kubeadmapi.MasterConfiguration{} legacyscheme.Scheme.Convert(cfg, internalcfg, nil) client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile) kubeadmutil.CheckErr(err) internalcfg, err = configutil.ConfigFileAndDefaultsToInternalConfig(*cfgPath, cfg) kubeadmutil.CheckErr(err) + if err := features.ValidateVersion(features.InitFeatureGates, internalcfg.FeatureGates, internalcfg.KubernetesVersion); err != nil { + kubeadmutil.CheckErr(err) + } // Execute the cmdFunc err = cmdFunc(internalcfg, client) diff --git a/cmd/kubeadm/app/cmd/upgrade/BUILD b/cmd/kubeadm/app/cmd/upgrade/BUILD index 28658102d5c..0787faf9465 100644 --- a/cmd/kubeadm/app/cmd/upgrade/BUILD +++ b/cmd/kubeadm/app/cmd/upgrade/BUILD @@ -16,6 +16,7 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library", "//cmd/kubeadm/app/cmd/util:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", + "//cmd/kubeadm/app/features:go_default_library", "//cmd/kubeadm/app/phases/controlplane:go_default_library", "//cmd/kubeadm/app/phases/upgrade:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library", diff --git a/cmd/kubeadm/app/cmd/upgrade/apply.go b/cmd/kubeadm/app/cmd/upgrade/apply.go index 7b09327d2bb..19f5dd5d18b 100644 --- a/cmd/kubeadm/app/cmd/upgrade/apply.go +++ b/cmd/kubeadm/app/cmd/upgrade/apply.go @@ -119,7 +119,7 @@ func NewCmdApply(parentFlags *cmdUpgradeFlags) *cobra.Command { func RunApply(flags *applyFlags) error { // Start with the basics, verify that the cluster is healthy and get the configuration from the cluster (using the ConfigMap) - upgradeVars, err := enforceRequirements(flags.parent.kubeConfigPath, flags.parent.cfgPath, flags.parent.printConfig, flags.dryRun) + upgradeVars, err := enforceRequirements(flags.parent.featureGatesString, flags.parent.kubeConfigPath, flags.parent.cfgPath, flags.parent.printConfig, flags.dryRun) if err != nil { return err } diff --git a/cmd/kubeadm/app/cmd/upgrade/common.go b/cmd/kubeadm/app/cmd/upgrade/common.go index 00e1fa031d3..e38f8a9afe0 100644 --- a/cmd/kubeadm/app/cmd/upgrade/common.go +++ b/cmd/kubeadm/app/cmd/upgrade/common.go @@ -30,6 +30,7 @@ import ( fakediscovery "k8s.io/client-go/discovery/fake" clientset "k8s.io/client-go/kubernetes" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + "k8s.io/kubernetes/cmd/kubeadm/app/features" "k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" "k8s.io/kubernetes/cmd/kubeadm/app/preflight" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" @@ -47,7 +48,7 @@ type upgradeVariables struct { } // enforceRequirements verifies that it's okay to upgrade and then returns the variables needed for the rest of the procedure -func enforceRequirements(kubeConfigPath, cfgPath string, printConfig, dryRun bool) (*upgradeVariables, error) { +func enforceRequirements(featureGatesString, kubeConfigPath, cfgPath string, printConfig, dryRun bool) (*upgradeVariables, error) { client, err := getClient(kubeConfigPath, dryRun) if err != nil { return nil, fmt.Errorf("couldn't create a Kubernetes client from file %q: %v", kubeConfigPath, err) @@ -69,6 +70,14 @@ func enforceRequirements(kubeConfigPath, cfgPath string, printConfig, dryRun boo printConfiguration(cfg, os.Stdout) } + cfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString) + if err != nil { + return nil, fmt.Errorf("[upgrade/config] FATAL: %v", err) + } + if err := features.ValidateVersion(features.InitFeatureGates, cfg.FeatureGates, cfg.KubernetesVersion); err != nil { + return nil, err + } + return &upgradeVariables{ client: client, cfg: cfg, diff --git a/cmd/kubeadm/app/cmd/upgrade/plan.go b/cmd/kubeadm/app/cmd/upgrade/plan.go index e1f2b9058fe..2d3e8fd6841 100644 --- a/cmd/kubeadm/app/cmd/upgrade/plan.go +++ b/cmd/kubeadm/app/cmd/upgrade/plan.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/cobra" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" + "k8s.io/kubernetes/cmd/kubeadm/app/features" "k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" ) @@ -53,29 +54,29 @@ func NewCmdPlan(parentFlags *cmdUpgradeFlags) *cobra.Command { // RunPlan takes care of outputting available versions to upgrade to for the user func RunPlan(parentFlags *cmdUpgradeFlags) error { - // Start with the basics, verify that the cluster is healthy, build a client and a versionGetter. Never set dry-run for plan. - upgradeVars, err := enforceRequirements(parentFlags.kubeConfigPath, parentFlags.cfgPath, parentFlags.printConfig, false) + upgradeVars, err := enforceRequirements(parentFlags.featureGatesString, parentFlags.kubeConfigPath, parentFlags.cfgPath, parentFlags.printConfig, false) if err != nil { return err } // Define Local Etcd cluster to be able to retrieve information etcdCluster := kubeadmutil.LocalEtcdCluster{} + // Compute which upgrade possibilities there are - availUpgrades, err := upgrade.GetAvailableUpgrades(upgradeVars.versionGetter, parentFlags.allowExperimentalUpgrades, parentFlags.allowRCUpgrades, etcdCluster) + availUpgrades, err := upgrade.GetAvailableUpgrades(upgradeVars.versionGetter, parentFlags.allowExperimentalUpgrades, parentFlags.allowRCUpgrades, etcdCluster, upgradeVars.cfg.FeatureGates) if err != nil { return fmt.Errorf("[upgrade/versions] FATAL: %v", err) } // Tell the user which upgrades are available - printAvailableUpgrades(availUpgrades, os.Stdout) + printAvailableUpgrades(availUpgrades, os.Stdout, upgradeVars.cfg.FeatureGates) return nil } // printAvailableUpgrades prints a UX-friendly overview of what versions are available to upgrade to // TODO look into columnize or some other formatter when time permits instead of using the tabwriter -func printAvailableUpgrades(upgrades []upgrade.Upgrade, w io.Writer) { +func printAvailableUpgrades(upgrades []upgrade.Upgrade, w io.Writer, featureGates map[string]bool) { // Return quickly if no upgrades can be made if len(upgrades) == 0 { @@ -117,7 +118,11 @@ func printAvailableUpgrades(upgrades []upgrade.Upgrade, w io.Writer) { fmt.Fprintf(tabw, "Controller Manager\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion) fmt.Fprintf(tabw, "Scheduler\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion) fmt.Fprintf(tabw, "Kube Proxy\t%s\t%s\n", upgrade.Before.KubeVersion, upgrade.After.KubeVersion) - fmt.Fprintf(tabw, "Kube DNS\t%s\t%s\n", upgrade.Before.DNSVersion, upgrade.After.DNSVersion) + if features.Enabled(featureGates, features.CoreDNS) { + fmt.Fprintf(tabw, "CoreDNS\t%s\t%s\n", upgrade.Before.DNSVersion, upgrade.After.DNSVersion) + } else { + fmt.Fprintf(tabw, "Kube DNS\t%s\t%s\n", upgrade.Before.DNSVersion, upgrade.After.DNSVersion) + } fmt.Fprintf(tabw, "Etcd\t%s\t%s\n", upgrade.Before.EtcdVersion, upgrade.After.EtcdVersion) // The tabwriter should be flushed at this stage as we have now put in all the required content for this time. This is required for the tabs' size to be correct. diff --git a/cmd/kubeadm/app/cmd/upgrade/plan_test.go b/cmd/kubeadm/app/cmd/upgrade/plan_test.go index 95fbcc2505e..75eb8f212aa 100644 --- a/cmd/kubeadm/app/cmd/upgrade/plan_test.go +++ b/cmd/kubeadm/app/cmd/upgrade/plan_test.go @@ -60,6 +60,7 @@ func TestSortedSliceFromStringIntMap(t *testing.T) { // TODO Think about modifying this test to be less verbose checking b/c it can be brittle. func TestPrintAvailableUpgrades(t *testing.T) { + featureGates := make(map[string]bool) var tests = []struct { upgrades []upgrade.Upgrade buf *bytes.Buffer @@ -334,7 +335,7 @@ _____________________________________________________________________ } for _, rt := range tests { rt.buf = bytes.NewBufferString("") - printAvailableUpgrades(rt.upgrades, rt.buf) + printAvailableUpgrades(rt.upgrades, rt.buf, featureGates) actualBytes := rt.buf.Bytes() if !bytes.Equal(actualBytes, rt.expectedBytes) { t.Errorf( diff --git a/cmd/kubeadm/app/cmd/upgrade/upgrade.go b/cmd/kubeadm/app/cmd/upgrade/upgrade.go index 0be0ef1254d..08a2d634399 100644 --- a/cmd/kubeadm/app/cmd/upgrade/upgrade.go +++ b/cmd/kubeadm/app/cmd/upgrade/upgrade.go @@ -18,16 +18,19 @@ package upgrade import ( "io" + "strings" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/util/sets" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" + "k8s.io/kubernetes/cmd/kubeadm/app/features" ) // cmdUpgradeFlags holds the values for the common flags in `kubeadm upgrade` type cmdUpgradeFlags struct { kubeConfigPath string cfgPath string + featureGatesString string allowExperimentalUpgrades bool allowRCUpgrades bool printConfig bool @@ -41,6 +44,7 @@ func NewCmdUpgrade(out io.Writer) *cobra.Command { flags := &cmdUpgradeFlags{ kubeConfigPath: "/etc/kubernetes/admin.conf", cfgPath: "", + featureGatesString: "", allowExperimentalUpgrades: false, allowRCUpgrades: false, printConfig: false, @@ -62,6 +66,8 @@ func NewCmdUpgrade(out io.Writer) *cobra.Command { cmd.PersistentFlags().StringSliceVar(&flags.ignoreChecksErrors, "ignore-checks-errors", flags.ignoreChecksErrors, "A list of checks whose errors will be shown as warnings. Example: 'IsPrivilegedUser,Swap'. Value 'all' ignores errors from all checks.") cmd.PersistentFlags().BoolVar(&flags.skipPreFlight, "skip-preflight-checks", flags.skipPreFlight, "Skip preflight checks that normally run before modifying the system.") cmd.PersistentFlags().MarkDeprecated("skip-preflight-checks", "it is now equivalent to --ignore-checks-errors=all") + cmd.PersistentFlags().StringVar(&flags.featureGatesString, "feature-gates", flags.featureGatesString, "A set of key=value pairs that describe feature gates for various features."+ + "Options are:\n"+strings.Join(features.KnownFeatures(&features.InitFeatureGates), "\n")) cmd.AddCommand(NewCmdApply(flags)) cmd.AddCommand(NewCmdPlan(flags)) diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index bc84458cfab..f1063785f12 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -188,7 +188,7 @@ const ( DefaultCIImageRepository = "gcr.io/kubernetes-ci-images" // CoreDNS defines a variable used internally when referring to the CoreDNS addon for a cluster - CoreDNS = "CoreDNS" + CoreDNS = "coredns" // KubeDNS defines a variable used internally when referring to the kube-dns addon for a cluster KubeDNS = "kube-dns" ) diff --git a/cmd/kubeadm/app/phases/upgrade/BUILD b/cmd/kubeadm/app/phases/upgrade/BUILD index 41e2c1e2434..02c1243eb37 100644 --- a/cmd/kubeadm/app/phases/upgrade/BUILD +++ b/cmd/kubeadm/app/phases/upgrade/BUILD @@ -21,6 +21,7 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", + "//cmd/kubeadm/app/features:go_default_library", "//cmd/kubeadm/app/images:go_default_library", "//cmd/kubeadm/app/phases/addons/dns:go_default_library", "//cmd/kubeadm/app/phases/addons/proxy:go_default_library", diff --git a/cmd/kubeadm/app/phases/upgrade/compute.go b/cmd/kubeadm/app/phases/upgrade/compute.go index 814eefc3ef2..220c6ffa613 100644 --- a/cmd/kubeadm/app/phases/upgrade/compute.go +++ b/cmd/kubeadm/app/phases/upgrade/compute.go @@ -21,6 +21,7 @@ import ( "strings" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/features" "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/pkg/util/version" @@ -49,6 +50,14 @@ func (u *Upgrade) CanUpgradeKubelets() bool { return !sameVersionFound } +// ActiveDNSAddon returns the version of CoreDNS or kube-dns +func ActiveDNSAddon(featureGates map[string]bool) string { + if features.Enabled(featureGates, features.CoreDNS) { + return kubeadmconstants.CoreDNS + } + return kubeadmconstants.KubeDNS +} + // ClusterState describes the state of certain versions for a cluster type ClusterState struct { // KubeVersion describes the version of the Kubernetes API Server, Controller Manager, Scheduler and Proxy. @@ -65,7 +74,7 @@ type ClusterState struct { // GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which // kinds of upgrades can be performed -func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed bool, cluster util.EtcdCluster) ([]Upgrade, error) { +func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed bool, cluster util.EtcdCluster, featureGates map[string]bool) ([]Upgrade, error) { fmt.Println("[upgrade] Fetching available versions to upgrade to") // Collect the upgrades kubeadm can do in this list @@ -104,10 +113,9 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA } // Construct a descriptor for the current state of the world - // TODO: Make CoreDNS available here. beforeState := ClusterState{ KubeVersion: clusterVersionStr, - DNSVersion: dns.GetDNSVersion(clusterVersion, kubeadmconstants.KubeDNS), + DNSVersion: dns.GetDNSVersion(clusterVersion, ActiveDNSAddon(featureGates)), KubeadmVersion: kubeadmVersionStr, KubeletVersions: kubeletVersions, EtcdVersion: etcdStatus.Version, @@ -150,7 +158,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA Before: beforeState, After: ClusterState{ KubeVersion: patchVersionStr, - DNSVersion: dns.GetDNSVersion(patchVersion, kubeadmconstants.KubeDNS), + DNSVersion: dns.GetDNSVersion(patchVersion, ActiveDNSAddon(featureGates)), KubeadmVersion: newKubeadmVer, EtcdVersion: getSuggestedEtcdVersion(patchVersionStr), // KubeletVersions is unset here as it is not used anywhere in .After @@ -166,7 +174,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA Before: beforeState, After: ClusterState{ KubeVersion: stableVersionStr, - DNSVersion: dns.GetDNSVersion(stableVersion, kubeadmconstants.KubeDNS), + DNSVersion: dns.GetDNSVersion(stableVersion, ActiveDNSAddon(featureGates)), KubeadmVersion: stableVersionStr, EtcdVersion: getSuggestedEtcdVersion(stableVersionStr), // KubeletVersions is unset here as it is not used anywhere in .After @@ -211,7 +219,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA Before: beforeState, After: ClusterState{ KubeVersion: previousBranchLatestVersionStr, - DNSVersion: dns.GetDNSVersion(previousBranchLatestVersion, kubeadmconstants.KubeDNS), + DNSVersion: dns.GetDNSVersion(previousBranchLatestVersion, ActiveDNSAddon(featureGates)), KubeadmVersion: previousBranchLatestVersionStr, EtcdVersion: getSuggestedEtcdVersion(previousBranchLatestVersionStr), // KubeletVersions is unset here as it is not used anywhere in .After @@ -224,12 +232,12 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA // Default to assume that the experimental version to show is the unstable one unstableKubeVersion := latestVersionStr - unstableKubeDNSVersion := dns.GetDNSVersion(latestVersion, kubeadmconstants.KubeDNS) + unstableKubeDNSVersion := dns.GetDNSVersion(latestVersion, ActiveDNSAddon(featureGates)) // Ẃe should not display alpha.0. The previous branch's beta/rc versions are more relevant due how the kube branching process works. if latestVersion.PreRelease() == "alpha.0" { unstableKubeVersion = previousBranchLatestVersionStr - unstableKubeDNSVersion = dns.GetDNSVersion(previousBranchLatestVersion, kubeadmconstants.KubeDNS) + unstableKubeDNSVersion = dns.GetDNSVersion(previousBranchLatestVersion, ActiveDNSAddon(featureGates)) } upgrades = append(upgrades, Upgrade{ diff --git a/cmd/kubeadm/app/phases/upgrade/compute_test.go b/cmd/kubeadm/app/phases/upgrade/compute_test.go index 6c356b76840..3ad6bb162e7 100644 --- a/cmd/kubeadm/app/phases/upgrade/compute_test.go +++ b/cmd/kubeadm/app/phases/upgrade/compute_test.go @@ -69,6 +69,7 @@ func (f fakeEtcdCluster) GetEtcdClusterStatus() (*clientv3.StatusResponse, error } func TestGetAvailableUpgrades(t *testing.T) { + featureGates := make(map[string]bool) tests := []struct { vg *fakeVersionGetter expectedUpgrades []Upgrade @@ -444,7 +445,7 @@ func TestGetAvailableUpgrades(t *testing.T) { testCluster := fakeEtcdCluster{} for _, rt := range tests { - actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, testCluster) + actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, testCluster, featureGates) if !reflect.DeepEqual(actualUpgrades, rt.expectedUpgrades) { t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades: %v\n\tgot: %v", rt.expectedUpgrades, actualUpgrades) } diff --git a/cmd/kubeadm/app/phases/upgrade/postupgrade.go b/cmd/kubeadm/app/phases/upgrade/postupgrade.go index 37598e3b949..2eae65ab1a2 100644 --- a/cmd/kubeadm/app/phases/upgrade/postupgrade.go +++ b/cmd/kubeadm/app/phases/upgrade/postupgrade.go @@ -19,16 +19,20 @@ package upgrade import ( "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/errors" clientset "k8s.io/client-go/kubernetes" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/features" "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy" "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo" nodebootstraptoken "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node" certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" "k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig" + "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "k8s.io/kubernetes/pkg/util/version" ) @@ -88,8 +92,33 @@ func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.MasterC if err := dns.EnsureDNSAddon(cfg, client); err != nil { errs = append(errs, err) } + + if err := coreDNSDeployment(cfg, client); err != nil { + errs = append(errs, err) + } + if err := proxy.EnsureProxyAddon(cfg, client); err != nil { errs = append(errs, err) } return errors.NewAggregate(errs) } + +func coreDNSDeployment(cfg *kubeadmapi.MasterConfiguration, client clientset.Interface) error { + if features.Enabled(cfg.FeatureGates, features.CoreDNS) { + return apiclient.TryRunCommand(func() error { + getCoreDNS, err := client.AppsV1beta2().Deployments(metav1.NamespaceSystem).Get(kubeadmconstants.CoreDNS, metav1.GetOptions{}) + if err != nil { + return err + } + if getCoreDNS.Status.ReadyReplicas == 0 { + return fmt.Errorf("the CodeDNS deployment isn't ready yet") + } + err = client.AppsV1beta2().Deployments(metav1.NamespaceSystem).Delete(kubeadmconstants.KubeDNS, nil) + if err != nil { + return err + } + return nil + }, 5) + } + return nil +}