Skip to content

Commit 01a95b2

Browse files
Ensure provider upgrades when a version changes
Signed-off-by: Danil Grigorev <[email protected]>
1 parent 6f7c927 commit 01a95b2

File tree

2 files changed

+102
-17
lines changed

2 files changed

+102
-17
lines changed

internal/controller/client_proxy.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,82 @@ import (
2424

2525
"k8s.io/apimachinery/pkg/api/meta"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
2728
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2829
"k8s.io/client-go/rest"
2930
"k8s.io/klog/v2"
31+
32+
operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha2"
33+
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
3034
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
3135
"sigs.k8s.io/controller-runtime/pkg/client"
3236
)
3337

38+
// clientProxy implements the Proxy interface from the clusterctl. It is used to
39+
// interact with the management cluster.
40+
type clientProxy struct {
41+
client.Client
42+
}
43+
44+
func (c clientProxy) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
45+
switch l := list.(type) {
46+
case *clusterctlv1.ProviderList:
47+
return listProviders(ctx, c.Client, l)
48+
default:
49+
return c.Client.List(ctx, l, opts...)
50+
}
51+
}
52+
53+
func (c clientProxy) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
54+
switch o := obj.(type) {
55+
case *clusterctlv1.Provider:
56+
return nil
57+
default:
58+
return c.Client.Get(ctx, key, o, opts...)
59+
}
60+
}
61+
62+
func (c clientProxy) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
63+
switch o := obj.(type) {
64+
case *clusterctlv1.Provider:
65+
return nil
66+
default:
67+
return c.Client.Patch(ctx, o, patch, opts...)
68+
}
69+
}
70+
71+
func listProviders(ctx context.Context, cl client.Client, list *clusterctlv1.ProviderList) error {
72+
providers := []operatorv1.GenericProviderList{
73+
&operatorv1.CoreProviderList{},
74+
&operatorv1.InfrastructureProviderList{},
75+
&operatorv1.BootstrapProviderList{},
76+
&operatorv1.ControlPlaneProviderList{},
77+
&operatorv1.AddonProviderList{},
78+
&operatorv1.IPAMProviderList{},
79+
}
80+
81+
for _, group := range providers {
82+
g, ok := group.(client.ObjectList)
83+
if !ok {
84+
continue
85+
}
86+
87+
if err := cl.List(ctx, g); err != nil {
88+
return err
89+
}
90+
91+
for _, p := range group.GetItems() {
92+
list.Items = append(list.Items, getProvider(p, ""))
93+
}
94+
}
95+
96+
return nil
97+
}
98+
3499
// controllerProxy implements the Proxy interface from the clusterctl. It is used to
35100
// interact with the management cluster.
36101
type controllerProxy struct {
37-
ctrlClient client.Client
102+
ctrlClient clientProxy
38103
ctrlConfig *rest.Config
39104
}
40105

internal/controller/phases.go

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"net/url"
2626
"os"
2727
"strings"
28+
"time"
2829

2930
corev1 "k8s.io/api/core/v1"
3031
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -458,7 +459,7 @@ func (p *phaseReconciler) fetch(ctx context.Context) (reconcile.Result, error) {
458459
}
459460

460461
// preInstall ensure all the clusterctl CRDs are available before installing the provider,
461-
// and delete existing components if required for upgrade.
462+
// and update existing components if required.
462463
func (p *phaseReconciler) preInstall(ctx context.Context) (reconcile.Result, error) {
463464
log := ctrl.LoggerFrom(ctx)
464465

@@ -467,9 +468,23 @@ func (p *phaseReconciler) preInstall(ctx context.Context) (reconcile.Result, err
467468
return reconcile.Result{}, nil
468469
}
469470

470-
log.Info("Changes detected, deleting existing components")
471+
if *p.provider.GetStatus().InstalledVersion == p.provider.GetSpec().Version {
472+
return reconcile.Result{}, nil
473+
}
474+
475+
log.Info("Version changes detected, updating existing components")
471476

472-
return p.delete(ctx)
477+
if err := p.newClusterClient().ProviderUpgrader().ApplyCustomPlan(ctx, cluster.UpgradeOptions{
478+
WaitProviders: true,
479+
WaitProviderTimeout: 5 * time.Minute,
480+
}, cluster.UpgradeItem{
481+
NextVersion: p.provider.GetSpec().Version,
482+
Provider: getProvider(p.provider, p.options.Version),
483+
}); err != nil {
484+
return reconcile.Result{}, err
485+
}
486+
487+
return reconcile.Result{}, nil
473488
}
474489

475490
// install installs the provider components using clusterctl library.
@@ -501,26 +516,31 @@ func (p *phaseReconciler) install(ctx context.Context) (reconcile.Result, error)
501516
return reconcile.Result{}, nil
502517
}
503518

519+
func getProvider(provider operatorv1.GenericProvider, defaultVersion string) clusterctlv1.Provider {
520+
clusterctlProvider := &clusterctlv1.Provider{}
521+
clusterctlProvider.Name = clusterctlProviderName(provider).Name
522+
clusterctlProvider.Namespace = provider.GetNamespace()
523+
clusterctlProvider.Type = string(util.ClusterctlProviderType(provider))
524+
clusterctlProvider.ProviderName = provider.GetName()
525+
526+
if provider.GetStatus().InstalledVersion != nil {
527+
clusterctlProvider.Version = *provider.GetStatus().InstalledVersion
528+
} else {
529+
clusterctlProvider.Version = defaultVersion
530+
}
531+
532+
return *clusterctlProvider
533+
}
534+
504535
// delete deletes the provider components using clusterctl library.
505536
func (p *phaseReconciler) delete(ctx context.Context) (reconcile.Result, error) {
506537
log := ctrl.LoggerFrom(ctx)
507538
log.Info("Deleting provider")
508539

509540
clusterClient := p.newClusterClient()
510541

511-
p.clusterctlProvider.Name = clusterctlProviderName(p.provider).Name
512-
p.clusterctlProvider.Namespace = p.provider.GetNamespace()
513-
p.clusterctlProvider.Type = string(util.ClusterctlProviderType(p.provider))
514-
p.clusterctlProvider.ProviderName = p.provider.GetName()
515-
516-
if p.provider.GetStatus().InstalledVersion != nil {
517-
p.clusterctlProvider.Version = *p.provider.GetStatus().InstalledVersion
518-
} else {
519-
p.clusterctlProvider.Version = p.options.Version
520-
}
521-
522542
err := clusterClient.ProviderComponents().Delete(ctx, cluster.DeleteOptions{
523-
Provider: *p.clusterctlProvider,
543+
Provider: getProvider(p.provider, p.options.Version),
524544
IncludeNamespace: false,
525545
IncludeCRDs: false,
526546
})
@@ -549,7 +569,7 @@ func clusterctlProviderName(provider operatorv1.GenericProvider) client.ObjectKe
549569
// newClusterClient returns a clusterctl client for interacting with management cluster.
550570
func (p *phaseReconciler) newClusterClient() cluster.Client {
551571
return cluster.New(cluster.Kubeconfig{}, p.configClient, cluster.InjectProxy(&controllerProxy{
552-
ctrlClient: p.ctrlClient,
572+
ctrlClient: clientProxy{p.ctrlClient},
553573
ctrlConfig: p.ctrlConfig,
554574
}))
555575
}

0 commit comments

Comments
 (0)