Skip to content

Commit d088d4c

Browse files
authored
Merge pull request kubernetes#126032 from SataQiu/imp-apply-phase-20240711
kubeadm: implement 'kubeadm upgrade apply phase'
2 parents f803c11 + da234c9 commit d088d4c

File tree

15 files changed

+1215
-258
lines changed

15 files changed

+1215
-258
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package apply implements phases of 'kubeadm upgrade apply'.
18+
package apply
19+
20+
import (
21+
"fmt"
22+
"io"
23+
24+
"github.com/pkg/errors"
25+
26+
clientset "k8s.io/client-go/kubernetes"
27+
28+
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
29+
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
30+
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
31+
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
32+
dnsaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns"
33+
proxyaddon "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy"
34+
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
35+
)
36+
37+
// NewAddonPhase returns a new addon phase.
38+
func NewAddonPhase() workflow.Phase {
39+
return workflow.Phase{
40+
Name: "addon",
41+
Short: "Upgrade the default kubeadm addons",
42+
Long: cmdutil.MacroCommandLongDescription,
43+
Phases: []workflow.Phase{
44+
{
45+
Name: "all",
46+
Short: "Upgrade all the addons",
47+
InheritFlags: getAddonPhaseFlags("all"),
48+
RunAllSiblings: true,
49+
},
50+
{
51+
Name: "coredns",
52+
Short: "Upgrade the CoreDNS addon",
53+
InheritFlags: getAddonPhaseFlags("coredns"),
54+
Run: runCoreDNSAddon,
55+
},
56+
{
57+
Name: "kube-proxy",
58+
Short: "Upgrade the kube-proxy addon",
59+
InheritFlags: getAddonPhaseFlags("kube-proxy"),
60+
Run: runKubeProxyAddon,
61+
},
62+
},
63+
}
64+
}
65+
66+
func shouldUpgradeAddons(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, out io.Writer) (bool, error) {
67+
unupgradedControlPlanes, err := upgrade.UnupgradedControlPlaneInstances(client, cfg.NodeRegistration.Name)
68+
if err != nil {
69+
return false, errors.Wrapf(err, "failed to determine whether all the control plane instances have been upgraded")
70+
}
71+
if len(unupgradedControlPlanes) > 0 {
72+
fmt.Fprintf(out, "[upgrade/addon] Skipping upgrade of addons because control plane instances %v have not been upgraded\n", unupgradedControlPlanes)
73+
return false, nil
74+
}
75+
return true, nil
76+
}
77+
78+
func getInitData(c workflow.RunData) (*kubeadmapi.InitConfiguration, clientset.Interface, string, io.Writer, bool, error) {
79+
data, ok := c.(Data)
80+
if !ok {
81+
return nil, nil, "", nil, false, errors.New("addon phase invoked with an invalid data struct")
82+
}
83+
return data.InitCfg(), data.Client(), data.PatchesDir(), data.OutputWriter(), data.DryRun(), nil
84+
}
85+
86+
// runCoreDNSAddon upgrades the CoreDNS addon.
87+
func runCoreDNSAddon(c workflow.RunData) error {
88+
cfg, client, patchesDir, out, dryRun, err := getInitData(c)
89+
if err != nil {
90+
return err
91+
}
92+
93+
shouldUpgradeAddons, err := shouldUpgradeAddons(client, cfg, out)
94+
if err != nil {
95+
return err
96+
}
97+
if !shouldUpgradeAddons {
98+
return nil
99+
}
100+
101+
// Upgrade CoreDNS
102+
if err := dnsaddon.EnsureDNSAddon(&cfg.ClusterConfiguration, client, patchesDir, out, dryRun); err != nil {
103+
return err
104+
}
105+
106+
return nil
107+
}
108+
109+
// runKubeProxyAddon upgrades the kube-proxy addon.
110+
func runKubeProxyAddon(c workflow.RunData) error {
111+
cfg, client, _, out, dryRun, err := getInitData(c)
112+
if err != nil {
113+
return err
114+
}
115+
116+
shouldUpgradeAddons, err := shouldUpgradeAddons(client, cfg, out)
117+
if err != nil {
118+
return err
119+
}
120+
if !shouldUpgradeAddons {
121+
return nil
122+
}
123+
124+
// Upgrade kube-proxy
125+
if err := proxyaddon.EnsureProxyAddon(&cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, client, out, dryRun); err != nil {
126+
return err
127+
}
128+
129+
return nil
130+
}
131+
132+
func getAddonPhaseFlags(name string) []string {
133+
flags := []string{
134+
options.CfgPath,
135+
options.KubeconfigPath,
136+
options.DryRun,
137+
}
138+
if name == "all" || name == "coredns" {
139+
flags = append(flags,
140+
options.Patches,
141+
)
142+
}
143+
return flags
144+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package apply implements phases of 'kubeadm upgrade apply'.
18+
package apply
19+
20+
import (
21+
"fmt"
22+
23+
"github.com/pkg/errors"
24+
25+
errorsutil "k8s.io/apimachinery/pkg/util/errors"
26+
27+
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
28+
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
29+
clusterinfophase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo"
30+
nodebootstraptoken "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
31+
)
32+
33+
// NewBootstrapTokenPhase returns a new bootstrap-token phase.
34+
func NewBootstrapTokenPhase() workflow.Phase {
35+
return workflow.Phase{
36+
Name: "bootstrap-token",
37+
Short: "Configures bootstrap token and cluster-info RBAC rules",
38+
InheritFlags: []string{
39+
options.CfgPath,
40+
options.KubeconfigPath,
41+
options.DryRun,
42+
},
43+
Run: runBootstrapToken,
44+
}
45+
}
46+
47+
func runBootstrapToken(c workflow.RunData) error {
48+
data, ok := c.(Data)
49+
if !ok {
50+
return errors.New("bootstrap-token phase invoked with an invalid data struct")
51+
}
52+
53+
if data.DryRun() {
54+
fmt.Println("[dryrun] Would configure bootstrap token and cluster-info RBAC rules")
55+
return nil
56+
}
57+
58+
fmt.Println("[upgrade/bootstrap-token] Configuring bootstrap token and cluster-info RBAC rules")
59+
60+
client := data.Client()
61+
62+
var errs []error
63+
// Create RBAC rules that makes the bootstrap tokens able to get nodes
64+
if err := nodebootstraptoken.AllowBootstrapTokensToGetNodes(client); err != nil {
65+
errs = append(errs, err)
66+
}
67+
68+
// Create/update RBAC rules that makes the bootstrap tokens able to post CSRs
69+
if err := nodebootstraptoken.AllowBootstrapTokensToPostCSRs(client); err != nil {
70+
errs = append(errs, err)
71+
}
72+
73+
// Create/update RBAC rules that makes the bootstrap tokens able to get their CSRs approved automatically
74+
if err := nodebootstraptoken.AutoApproveNodeBootstrapTokens(client); err != nil {
75+
errs = append(errs, err)
76+
}
77+
78+
// Create/update RBAC rules that makes the nodes to rotate certificates and get their CSRs approved automatically
79+
if err := nodebootstraptoken.AutoApproveNodeCertificateRotation(client); err != nil {
80+
errs = append(errs, err)
81+
}
82+
83+
// Create/update RBAC rules that makes the cluster-info ConfigMap reachable
84+
if err := clusterinfophase.CreateClusterInfoRBACRules(client); err != nil {
85+
errs = append(errs, err)
86+
}
87+
88+
return errorsutil.NewAggregate(errs)
89+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package apply implements phases of 'kubeadm upgrade apply'.
18+
package apply
19+
20+
import (
21+
"fmt"
22+
"os"
23+
24+
"github.com/pkg/errors"
25+
26+
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
27+
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
28+
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
29+
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
30+
)
31+
32+
// NewControlPlanePhase returns a new control-plane phase.
33+
func NewControlPlanePhase() workflow.Phase {
34+
phase := workflow.Phase{
35+
Name: "control-plane",
36+
Short: "Upgrade the control plane",
37+
Run: runControlPlane,
38+
InheritFlags: []string{
39+
options.CfgPath,
40+
options.KubeconfigPath,
41+
options.DryRun,
42+
options.CertificateRenewal,
43+
options.EtcdUpgrade,
44+
options.Patches,
45+
},
46+
}
47+
return phase
48+
}
49+
50+
func runControlPlane(c workflow.RunData) error {
51+
data, ok := c.(Data)
52+
if !ok {
53+
return errors.New("control-plane phase invoked with an invalid data struct")
54+
}
55+
56+
initCfg, upgradeCfg, client, patchesDir := data.InitCfg(), data.Cfg(), data.Client(), data.PatchesDir()
57+
58+
if data.DryRun() {
59+
fmt.Printf("[dryrun] Would upgrade your static Pod-hosted control plane to version %q", initCfg.KubernetesVersion)
60+
return upgrade.DryRunStaticPodUpgrade(patchesDir, initCfg)
61+
}
62+
63+
fmt.Printf("[upgrade/control-plane] Upgrading your static Pod-hosted control plane to version %q (timeout: %v)...\n",
64+
initCfg.KubernetesVersion, upgradeCfg.Timeouts.UpgradeManifests.Duration)
65+
66+
waiter := apiclient.NewKubeWaiter(client, upgradeCfg.Timeouts.UpgradeManifests.Duration, os.Stdout)
67+
if err := upgrade.PerformStaticPodUpgrade(client, waiter, initCfg, data.EtcdUpgrade(), data.RenewCerts(), patchesDir); err != nil {
68+
return errors.Wrap(err, "couldn't complete the static pod upgrade")
69+
}
70+
71+
fmt.Println("[upgrade/control-plane] The control plane instance for this node was successfully upgraded!")
72+
73+
return nil
74+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package apply implements phases of 'kubeadm upgrade apply'.
18+
package apply
19+
20+
import (
21+
"io"
22+
23+
"k8s.io/apimachinery/pkg/util/sets"
24+
clientset "k8s.io/client-go/kubernetes"
25+
26+
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
27+
)
28+
29+
// Data is the interface to use for kubeadm upgrade apply phases.
30+
// The "applyData" type from "cmd/upgrade/apply.go" must satisfy this interface.
31+
type Data interface {
32+
EtcdUpgrade() bool
33+
RenewCerts() bool
34+
DryRun() bool
35+
Cfg() *kubeadmapi.UpgradeConfiguration
36+
InitCfg() *kubeadmapi.InitConfiguration
37+
Client() clientset.Interface
38+
IgnorePreflightErrors() sets.Set[string]
39+
PatchesDir() string
40+
OutputWriter() io.Writer
41+
SessionIsInteractive() bool
42+
AllowExperimentalUpgrades() bool
43+
AllowRCUpgrades() bool
44+
ForceUpgrade() bool
45+
}

0 commit comments

Comments
 (0)