Skip to content

Commit 6de38dd

Browse files
authored
Merge pull request #12927 from sivchari/implement-generate-upgrade-plan-handler
✨ Implement GenerateUpgradePlan handler
2 parents ba6b0c0 + 01ec43b commit 6de38dd

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Copyright 2025 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 upgradeplan contains the handlers for the upgrade plan hook.
18+
//
19+
// The implementation of the handlers is specifically designed for Cluster API E2E tests use cases.
20+
// When implementing custom RuntimeExtension, it is only required to expose HandlerFunc with the
21+
// signature defined in sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.
22+
package upgradeplan
23+
24+
import (
25+
"context"
26+
"slices"
27+
28+
"k8s.io/klog/v2"
29+
ctrl "sigs.k8s.io/controller-runtime"
30+
"sigs.k8s.io/controller-runtime/pkg/client"
31+
32+
runtimehooksv1 "sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1"
33+
"sigs.k8s.io/cluster-api/exp/topology/desiredstate"
34+
"sigs.k8s.io/cluster-api/test/infrastructure/kind"
35+
)
36+
37+
// ExtensionHandlers provides a common struct for the upgrade plan hook handler.
38+
// NOTE: it is not mandatory to use a ExtensionHandlers in custom RuntimeExtension, what is important
39+
// is to expose HandlerFunc with the signature defined in sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.
40+
type ExtensionHandlers struct {
41+
client client.Client
42+
}
43+
44+
// NewExtensionHandlers returns a ExtensionHandlers for the upgrade plan hook handler.
45+
func NewExtensionHandlers(client client.Client) *ExtensionHandlers {
46+
return &ExtensionHandlers{
47+
client: client,
48+
}
49+
}
50+
51+
// DoGenerateUpgradePlan implements the HandlerFunc for the GenerateUpgradePlan hook.
52+
// This hook generates an upgrade plan based on the Kubernetes versions available in the kind mapper, thus allowing E2E tests to control the behavior during tests.
53+
// NOTE: custom RuntimeExtension, must implement the body of this func according to the specific use case.
54+
func (m *ExtensionHandlers) DoGenerateUpgradePlan(ctx context.Context, request *runtimehooksv1.GenerateUpgradePlanRequest, response *runtimehooksv1.GenerateUpgradePlanResponse) {
55+
log := ctrl.LoggerFrom(ctx).WithValues("Cluster", klog.KObj(&request.Cluster))
56+
ctx = ctrl.LoggerInto(ctx, log)
57+
log.Info("GenerateUpgradePlan is called")
58+
59+
// Get the list of Kubernetes versions from the kind mapper.
60+
versions := kind.GetKubernetesVersions()
61+
if !slices.Contains(versions, request.ToKubernetesVersion) {
62+
versions = append(versions, request.ToKubernetesVersion)
63+
}
64+
65+
// Use GetUpgradePlanFromClusterClassVersions to generate the upgrade plan.
66+
getUpgradePlan := desiredstate.GetUpgradePlanFromClusterClassVersions(versions)
67+
68+
// Call the upgrade plan function.
69+
controlPlaneUpgrades, workersUpgrades, err := getUpgradePlan(ctx, request.ToKubernetesVersion, request.FromControlPlaneKubernetesVersion, request.FromWorkersKubernetesVersion)
70+
if err != nil {
71+
response.Status = runtimehooksv1.ResponseStatusFailure
72+
response.Message = err.Error()
73+
return
74+
}
75+
76+
// Convert the upgrade plans to UpgradeStep format.
77+
response.ControlPlaneUpgrades = make([]runtimehooksv1.UpgradeStep, len(controlPlaneUpgrades))
78+
for i, version := range controlPlaneUpgrades {
79+
response.ControlPlaneUpgrades[i] = runtimehooksv1.UpgradeStep{
80+
Version: version,
81+
}
82+
}
83+
84+
response.WorkersUpgrades = make([]runtimehooksv1.UpgradeStep, len(workersUpgrades))
85+
for i, version := range workersUpgrades {
86+
response.WorkersUpgrades[i] = runtimehooksv1.UpgradeStep{
87+
Version: version,
88+
}
89+
}
90+
91+
response.Status = runtimehooksv1.ResponseStatusSuccess
92+
}

test/extension/main.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import (
5353
"sigs.k8s.io/cluster-api/test/extension/handlers/inplaceupdate"
5454
"sigs.k8s.io/cluster-api/test/extension/handlers/lifecycle"
5555
"sigs.k8s.io/cluster-api/test/extension/handlers/topologymutation"
56+
"sigs.k8s.io/cluster-api/test/extension/handlers/upgradeplan"
5657
"sigs.k8s.io/cluster-api/util/flags"
5758
"sigs.k8s.io/cluster-api/version"
5859
)
@@ -263,6 +264,7 @@ func main() {
263264
setupTopologyMutationHookHandlers(runtimeExtensionWebhookServer)
264265
setupLifecycleHookHandlers(mgr, runtimeExtensionWebhookServer)
265266
setupInPlaceUpdateHookHandlers(mgr, runtimeExtensionWebhookServer)
267+
setupUpgradePlanHookHandlers(mgr, runtimeExtensionWebhookServer)
266268

267269
// Setup checks, indexes, reconcilers and webhooks.
268270
setupChecks(mgr)
@@ -436,6 +438,23 @@ func setupInPlaceUpdateHookHandlers(mgr ctrl.Manager, runtimeExtensionWebhookSer
436438
}
437439
}
438440

441+
// setupUpgradePlanHookHandlers sets up Upgrade Plan Hooks.
442+
func setupUpgradePlanHookHandlers(mgr ctrl.Manager, runtimeExtensionWebhookServer *server.Server) {
443+
// Create the ExtensionHandlers for the upgrade plan hooks
444+
// NOTE: it is not mandatory to group all the ExtensionHandlers using a struct, what is important
445+
// is to have HandlerFunc with the signature defined in sigs.k8s.io/cluster-api/api/runtime/hooks/v1alpha1.
446+
upgradePlanExtensionHandlers := upgradeplan.NewExtensionHandlers(mgr.GetClient())
447+
448+
if err := runtimeExtensionWebhookServer.AddExtensionHandler(server.ExtensionHandler{
449+
Hook: runtimehooksv1.GenerateUpgradePlan,
450+
Name: "generate-upgrade-plan",
451+
HandlerFunc: upgradePlanExtensionHandlers.DoGenerateUpgradePlan,
452+
}); err != nil {
453+
setupLog.Error(err, "Error adding handler")
454+
os.Exit(1)
455+
}
456+
}
457+
439458
func setupChecks(mgr ctrl.Manager) {
440459
if err := mgr.AddReadyzCheck("webhook", mgr.GetWebhookServer().StartedChecker()); err != nil {
441460
setupLog.Error(err, "Unable to create ready check")

0 commit comments

Comments
 (0)