-
Notifications
You must be signed in to change notification settings - Fork 40k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement the kubeadm upgrade
command
#48899
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ package upgrade | |
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
|
@@ -26,12 +27,20 @@ import ( | |
clientset "k8s.io/client-go/kubernetes" | ||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" | ||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" | ||
"k8s.io/kubernetes/cmd/kubeadm/app/constants" | ||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane" | ||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" | ||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" | ||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" | ||
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun" | ||
"k8s.io/kubernetes/pkg/api" | ||
"k8s.io/kubernetes/pkg/util/version" | ||
) | ||
|
||
const ( | ||
upgradeManifestTimeout = 1 * time.Minute | ||
) | ||
|
||
// applyFlags holds the information about the flags that can be passed to apply | ||
type applyFlags struct { | ||
nonInteractiveMode bool | ||
|
@@ -102,7 +111,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) | ||
upgradeVars, err := enforceRequirements(flags.parent.kubeConfigPath, flags.parent.cfgPath, flags.parent.printConfig, flags.dryRun) | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -126,16 +135,24 @@ func RunApply(flags *applyFlags) error { | |
} | ||
} | ||
|
||
// TODO: Implement a prepulling mechanism here | ||
// Use a prepuller implementation based on creating DaemonSets | ||
// and block until all DaemonSets are ready; then we know for sure that all control plane images are cached locally | ||
prepuller := upgrade.NewDaemonSetPrepuller(upgradeVars.client, upgradeVars.waiter, internalcfg) | ||
upgrade.PrepullImagesInParallel(prepuller, flags.imagePullTimeout) | ||
|
||
// Now; perform the upgrade procedure | ||
if err := PerformControlPlaneUpgrade(flags, upgradeVars.client, internalcfg); err != nil { | ||
if err := PerformControlPlaneUpgrade(flags, upgradeVars.client, upgradeVars.waiter, internalcfg); err != nil { | ||
return fmt.Errorf("[upgrade/apply] FATAL: %v", err) | ||
} | ||
|
||
// Upgrade RBAC rules and addons. Optionally, if needed, perform some extra task for a specific version | ||
if err := upgrade.PerformPostUpgradeTasks(upgradeVars.client, internalcfg, flags.newK8sVersion); err != nil { | ||
return fmt.Errorf("[upgrade/postupgrade] FATAL: %v", err) | ||
return fmt.Errorf("[upgrade/postupgrade] FATAL post-upgrade error: %v", err) | ||
} | ||
|
||
if flags.dryRun { | ||
fmt.Println("[dryrun] Finished dryrunning successfully!") | ||
return nil | ||
} | ||
|
||
fmt.Println("") | ||
|
@@ -182,7 +199,7 @@ func EnforceVersionPolicies(flags *applyFlags, versionGetter upgrade.VersionGett | |
if len(versionSkewErrs.Skippable) > 0 { | ||
// Return the error if the user hasn't specified the --force flag | ||
if !flags.force { | ||
return fmt.Errorf("The --version argument is invalid due to these errors: %v. Can be bypassed if you pass the --force flag", versionSkewErrs.Mandatory) | ||
return fmt.Errorf("The --version argument is invalid due to these errors: %v. Can be bypassed if you pass the --force flag", versionSkewErrs.Skippable) | ||
} | ||
// Soft errors found, but --force was specified | ||
fmt.Printf("[upgrade/version] Found %d potential version compatibility errors but skipping since the --force flag is set: %v\n", len(versionSkewErrs.Skippable), versionSkewErrs.Skippable) | ||
|
@@ -192,21 +209,60 @@ func EnforceVersionPolicies(flags *applyFlags, versionGetter upgrade.VersionGett | |
} | ||
|
||
// PerformControlPlaneUpgrade actually performs the upgrade procedure for the cluster of your type (self-hosted or static-pod-hosted) | ||
func PerformControlPlaneUpgrade(flags *applyFlags, client clientset.Interface, internalcfg *kubeadmapi.MasterConfiguration) error { | ||
func PerformControlPlaneUpgrade(flags *applyFlags, client clientset.Interface, waiter apiclient.Waiter, internalcfg *kubeadmapi.MasterConfiguration) error { | ||
|
||
// Check if the cluster is self-hosted and act accordingly | ||
if upgrade.IsControlPlaneSelfHosted(client) { | ||
fmt.Printf("[upgrade/apply] Upgrading your Self-Hosted control plane to version %q...\n", flags.newK8sVersionStr) | ||
|
||
// Upgrade a self-hosted cluster | ||
// TODO(luxas): Implement this later when we have the new upgrade strategy | ||
return fmt.Errorf("not implemented") | ||
// Upgrade the self-hosted cluster | ||
return upgrade.SelfHostedControlPlane(client, waiter, internalcfg, flags.newK8sVersion) | ||
} | ||
|
||
// OK, the cluster is hosted using static pods. Upgrade a static-pod hosted cluster | ||
fmt.Printf("[upgrade/apply] Upgrading your Static Pod-hosted control plane to version %q...\n", flags.newK8sVersionStr) | ||
|
||
if err := upgrade.PerformStaticPodControlPlaneUpgrade(client, internalcfg, flags.newK8sVersion); err != nil { | ||
if flags.dryRun { | ||
return DryRunStaticPodUpgrade(internalcfg) | ||
} | ||
return PerformStaticPodUpgrade(client, waiter, internalcfg) | ||
} | ||
|
||
// PerformStaticPodUpgrade performs the upgrade of the control plane components for a static pod hosted cluster | ||
func PerformStaticPodUpgrade(client clientset.Interface, waiter apiclient.Waiter, internalcfg *kubeadmapi.MasterConfiguration) error { | ||
pathManager, err := upgrade.NewKubeStaticPodPathManagerUsingTempDirs(constants.GetStaticPodDirectory()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NewKubeStaticPodPathManagerUsingTempDirs <- Does it really need to be this long? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blocking right now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NewKubeStaticPods ... PathManagerUsingTempDirs seems unnecessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now just leave it. |
||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := upgrade.StaticPodControlPlane(waiter, pathManager, internalcfg); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// DryRunStaticPodUpgrade fakes an upgrade of the control plane | ||
func DryRunStaticPodUpgrade(internalcfg *kubeadmapi.MasterConfiguration) error { | ||
|
||
dryRunManifestDir, err := constants.CreateTempDirForKubeadm("kubeadm-upgrade-dryrun") | ||
if err != nil { | ||
return err | ||
} | ||
defer os.RemoveAll(dryRunManifestDir) | ||
|
||
if err := controlplane.CreateInitStaticPodManifestFiles(dryRunManifestDir, internalcfg); err != nil { | ||
return err | ||
} | ||
|
||
// Print the contents of the upgraded manifests and pretend like they were in /etc/kubernetes/manifests | ||
files := []dryrunutil.FileToPrint{} | ||
for _, component := range constants.MasterComponents { | ||
realPath := constants.GetStaticPodFilepath(component, dryRunManifestDir) | ||
outputPath := constants.GetStaticPodFilepath(component, constants.GetStaticPodDirectory()) | ||
files = append(files, dryrunutil.NewFileToPrint(realPath, outputPath)) | ||
} | ||
|
||
if err := dryrunutil.PrintDryRunFiles(files, os.Stdout); err != nil { | ||
return err | ||
} | ||
return nil | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,8 @@ package constants | |
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
|
||
|
@@ -31,6 +33,7 @@ var KubernetesDir = "/etc/kubernetes" | |
|
||
const ( | ||
ManifestsSubDirName = "manifests" | ||
TempDirForKubeadm = "/etc/kubernetes/tmp" | ||
|
||
CACertAndKeyBaseName = "ca" | ||
CACertName = "ca.crt" | ||
|
@@ -181,3 +184,17 @@ func GetAdminKubeConfigPath() string { | |
func AddSelfHostedPrefix(componentName string) string { | ||
return fmt.Sprintf("%s%s", SelfHostingPrefix, componentName) | ||
} | ||
|
||
// CreateTempDirForKubeadm is a function that creates a temporary directory under /etc/kubernetes/tmp (not using /tmp as that would potentially be dangerous) | ||
func CreateTempDirForKubeadm(dirName string) (string, error) { | ||
// creates target folder if not already exists | ||
if err := os.MkdirAll(TempDirForKubeadm, 0700); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. root only on TempDir? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes; we concluded that there are possible, pretty nasty |
||
return "", fmt.Errorf("failed to create directory %q: %v", TempDirForKubeadm, err) | ||
} | ||
|
||
tempDir, err := ioutil.TempDir(TempDirForKubeadm, dirName) | ||
if err != nil { | ||
return "", fmt.Errorf("couldn't create a temporary directory: %v", err) | ||
} | ||
return tempDir, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just to speed up the process? It doesn't seem strictly necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is pretty crucial for the predictability of this command; there are lots of network failures that might happen, and I want to minimize time that something can go wrong in the actual upgrade process.
For me, this is strictly necessary.
Rationale etc. was in the original proposal as well.