kubeadm: refactor upgrade to reduce duplication code

This commit is contained in:
SataQiu 2024-09-10 14:43:36 +08:00
parent 8db2dd3c8b
commit 8420b096c9
13 changed files with 102 additions and 312 deletions

View File

@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package node implements phases of 'kubeadm upgrade node'.
package node
// Package upgrade holds the common phases for 'kubeadm upgrade'.
package upgrade
import (
"fmt"
@ -28,7 +28,6 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
dnsaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
proxyaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
@ -39,7 +38,6 @@ func NewAddonPhase() workflow.Phase {
return workflow.Phase{
Name: "addon",
Short: "Upgrade the default kubeadm addons",
Long: cmdutil.MacroCommandLongDescription,
Phases: []workflow.Phase{
{
Name: "all",
@ -98,7 +96,6 @@ func runCoreDNSAddon(c workflow.RunData) error {
return nil
}
// Upgrade CoreDNS
if err := dnsaddon.EnsureDNSAddon(&cfg.ClusterConfiguration, client, patchesDir, out, dryRun); err != nil {
return err
}
@ -121,7 +118,6 @@ func runKubeProxyAddon(c workflow.RunData) error {
return nil
}
// Upgrade kube-proxy
if err := proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, dryRun); err != nil {
return err
}

View File

@ -1,144 +0,0 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package apply implements phases of 'kubeadm upgrade apply'.
package apply
import (
"fmt"
"io"
"github.com/pkg/errors"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
dnsaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
proxyaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
)
// NewAddonPhase returns a new addon phase.
func NewAddonPhase() workflow.Phase {
return workflow.Phase{
Name: "addon",
Short: "Upgrade the default kubeadm addons",
Long: cmdutil.MacroCommandLongDescription,
Phases: []workflow.Phase{
{
Name: "all",
Short: "Upgrade all the addons",
InheritFlags: getAddonPhaseFlags("all"),
RunAllSiblings: true,
},
{
Name: "coredns",
Short: "Upgrade the CoreDNS addon",
InheritFlags: getAddonPhaseFlags("coredns"),
Run: runCoreDNSAddon,
},
{
Name: "kube-proxy",
Short: "Upgrade the kube-proxy addon",
InheritFlags: getAddonPhaseFlags("kube-proxy"),
Run: runKubeProxyAddon,
},
},
}
}
func shouldUpgradeAddons(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, out io.Writer) (bool, error) {
unupgradedControlPlanes, err := upgrade.UnupgradedControlPlaneInstances(client, cfg.NodeRegistration.Name)
if err != nil {
return false, errors.Wrapf(err, "failed to determine whether all the control plane instances have been upgraded")
}
if len(unupgradedControlPlanes) > 0 {
fmt.Fprintf(out, "[upgrade/addon] Skipping upgrade of addons because control plane instances %v have not been upgraded\n", unupgradedControlPlanes)
return false, nil
}
return true, nil
}
func getInitData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.Interface, string, io.Writer, bool, error) {
data, ok := c.(Data)
if !ok {
return nil, nil, "", nil, false, errors.New("addon phase invoked with an invalid data struct")
}
return data.InitCfg(), data.Client(), data.PatchesDir(), data.OutputWriter(), data.DryRun(), nil
}
// runCoreDNSAddon upgrades the CoreDNS addon.
func runCoreDNSAddon(c workflow.RunData) error {
cfg, client, patchesDir, out, dryRun, err := getInitData(c)
if err != nil {
return err
}
shouldUpgradeAddons, err := shouldUpgradeAddons(client, cfg, out)
if err != nil {
return err
}
if !shouldUpgradeAddons {
return nil
}
// Upgrade CoreDNS
if err := dnsaddon.EnsureDNSAddon(&cfg.ClusterConfiguration, client, patchesDir, out, dryRun); err != nil {
return err
}
return nil
}
// runKubeProxyAddon upgrades the kube-proxy addon.
func runKubeProxyAddon(c workflow.RunData) error {
cfg, client, _, out, dryRun, err := getInitData(c)
if err != nil {
return err
}
shouldUpgradeAddons, err := shouldUpgradeAddons(client, cfg, out)
if err != nil {
return err
}
if !shouldUpgradeAddons {
return nil
}
// Upgrade kube-proxy
if err := proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, dryRun); err != nil {
return err
}
return nil
}
func getAddonPhaseFlags(name string) []string {
flags := []string{
options.CfgPath,
options.KubeconfigPath,
options.DryRun,
}
if name == "all" || name == "coredns" {
flags = append(flags,
options.Patches,
)
}
return flags
}

View File

@ -18,26 +18,14 @@ limitations under the License.
package apply
import (
"io"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade"
)
// Data is the interface to use for kubeadm upgrade apply phases.
// The "applyData" type from "cmd/upgrade/apply.go" must satisfy this interface.
type Data interface {
EtcdUpgrade() bool
RenewCerts() bool
DryRun() bool
Cfg() *kubeadmapi.UpgradeConfiguration
InitCfg() *kubeadmapi.InitConfiguration
Client() clientset.Interface
IgnorePreflightErrors() sets.Set[string]
PatchesDir() string
OutputWriter() io.Writer
upgrade.Data
SessionIsInteractive() bool
AllowExperimentalUpgrades() bool
AllowRCUpgrades() bool

View File

@ -28,7 +28,6 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig"
@ -40,7 +39,6 @@ func NewUploadConfigPhase() workflow.Phase {
Name: "upload-config",
Aliases: []string{"uploadconfig"},
Short: "Upload the kubeadm and kubelet configurations to ConfigMaps",
Long: cmdutil.MacroCommandLongDescription,
Phases: []workflow.Phase{
{
Name: "all",

View File

@ -0,0 +1,40 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package upgrade holds the common phases for 'kubeadm upgrade'.
package upgrade
import (
"io"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
// Data is the common interface to use for kubeadm upgrade phases.
type Data interface {
EtcdUpgrade() bool
RenewCerts() bool
DryRun() bool
Cfg() *kubeadmapi.UpgradeConfiguration
InitCfg() *kubeadmapi.InitConfiguration
Client() clientset.Interface
IgnorePreflightErrors() sets.Set[string]
PatchesDir() string
OutputWriter() io.Writer
}

View File

@ -0,0 +1,42 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package upgrade
import (
"io"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
// a package local type for testing purposes.
type testData struct{}
// testData must satisfy Data.
var _ Data = &testData{}
func (t *testData) EtcdUpgrade() bool { return false }
func (t *testData) RenewCerts() bool { return false }
func (t *testData) DryRun() bool { return false }
func (t *testData) Cfg() *kubeadmapi.UpgradeConfiguration { return nil }
func (t *testData) InitCfg() *kubeadmapi.InitConfiguration { return nil }
func (t *testData) Client() clientset.Interface { return nil }
func (t *testData) IgnorePreflightErrors() sets.Set[string] { return nil }
func (t *testData) PatchesDir() string { return "" }
func (t *testData) OutputWriter() io.Writer { return nil }

View File

@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package apply implements phases of 'kubeadm upgrade apply'.
package apply
// Package upgrade holds the common phases for 'kubeadm upgrade'.
package upgrade
import (
"fmt"

View File

@ -18,26 +18,14 @@ limitations under the License.
package node
import (
"io"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade"
)
// Data is the interface to use for kubeadm upgrade node phases.
// The "nodeData" type from "cmd/upgrade/node.go" must satisfy this interface.
type Data interface {
EtcdUpgrade() bool
RenewCerts() bool
DryRun() bool
Cfg() *kubeadmapi.UpgradeConfiguration
InitCfg() *kubeadmapi.InitConfiguration
upgrade.Data
IsControlPlaneNode() bool
Client() clientset.Interface
IgnorePreflightErrors() sets.Set[string]
PatchesDir() string
KubeConfigPath() string
OutputWriter() io.Writer
}

View File

@ -1,71 +0,0 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package node
import (
"fmt"
"github.com/pkg/errors"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
)
var (
kubeletConfigLongDesc = cmdutil.LongDesc(`
Upgrade the kubelet configuration for this node by downloading it from the kubelet-config ConfigMap stored in the cluster
`)
)
// NewKubeletConfigPhase returns a new kubelet-config phase.
func NewKubeletConfigPhase() workflow.Phase {
phase := workflow.Phase{
Name: "kubelet-config",
Short: "Upgrade the kubelet configuration for this node",
Long: kubeletConfigLongDesc,
Run: runKubeletConfigPhase(),
InheritFlags: []string{
options.CfgPath,
options.DryRun,
options.KubeconfigPath,
options.Patches,
},
}
return phase
}
func runKubeletConfigPhase() func(c workflow.RunData) error {
return func(c workflow.RunData) error {
data, ok := c.(Data)
if !ok {
return errors.New("kubelet-config phase invoked with an invalid data struct")
}
// Write the configuration for the kubelet down to disk and print the generated manifests instead if dry-running.
// If not dry-running, the kubelet config file will be backed up to /etc/kubernetes/tmp/ dir, so that it could be
// recovered if there is anything goes wrong.
err := upgrade.WriteKubeletConfigFiles(data.InitCfg(), data.PatchesDir(), data.DryRun(), data.OutputWriter())
if err != nil {
return err
}
fmt.Println("[upgrade/kubelet-config] The configuration for this node was successfully upgraded!")
return nil
}
}

View File

@ -1,49 +0,0 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package node implements phases of 'kubeadm upgrade node'.
package node
import (
"github.com/pkg/errors"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
)
// NewPostUpgradePhase returns a new post-upgrade phase.
func NewPostUpgradePhase() workflow.Phase {
return workflow.Phase{
Name: "post-upgrade",
Short: "Run post upgrade tasks",
Run: runPostUpgrade,
InheritFlags: []string{
options.CfgPath,
options.KubeconfigPath,
options.DryRun,
},
}
}
func runPostUpgrade(c workflow.RunData) error {
_, ok := c.(Data)
if !ok {
return errors.New("post-upgrade phase invoked with an invalid data struct")
}
// PLACEHOLDER: this phase should contain any release specific post-upgrade tasks.
return nil
}

View File

@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package apply implements phases of 'kubeadm upgrade apply'.
package apply
// Package upgrade holds the common phases for 'kubeadm upgrade'.
package upgrade
import (
"github.com/pkg/errors"

View File

@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
commonphases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade"
phases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade/apply"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
@ -134,10 +135,10 @@ func newCmdApply(apf *applyPlanFlags) *cobra.Command {
applyRunner.AppendPhase(phases.NewControlPlanePhase())
applyRunner.AppendPhase(phases.NewUploadConfigPhase())
applyRunner.AppendPhase(phases.NewKubeconfigPhase())
applyRunner.AppendPhase(phases.NewKubeletConfigPhase())
applyRunner.AppendPhase(commonphases.NewKubeletConfigPhase())
applyRunner.AppendPhase(phases.NewBootstrapTokenPhase())
applyRunner.AppendPhase(phases.NewAddonPhase())
applyRunner.AppendPhase(phases.NewPostUpgradePhase())
applyRunner.AppendPhase(commonphases.NewAddonPhase())
applyRunner.AppendPhase(commonphases.NewPostUpgradePhase())
// Sets the data builder function, that will be used by the runner,
// both when running the entire workflow or single phases.

View File

@ -31,6 +31,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
commonphases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade"
phases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade/node"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
@ -98,9 +99,9 @@ func newCmdNode(out io.Writer) *cobra.Command {
nodeRunner.AppendPhase(phases.NewPreflightPhase())
nodeRunner.AppendPhase(phases.NewControlPlane())
nodeRunner.AppendPhase(phases.NewKubeconfigPhase())
nodeRunner.AppendPhase(phases.NewKubeletConfigPhase())
nodeRunner.AppendPhase(phases.NewAddonPhase())
nodeRunner.AppendPhase(phases.NewPostUpgradePhase())
nodeRunner.AppendPhase(commonphases.NewKubeletConfigPhase())
nodeRunner.AppendPhase(commonphases.NewAddonPhase())
nodeRunner.AppendPhase(commonphases.NewPostUpgradePhase())
// sets the data builder function, that will be used by the runner
// both when running the entire workflow or single phases