Skip to content

Commit be5bcf1

Browse files
Add tests for air-gapped upgrade procedure
- Upgrades should modify manifests regularly as well. Signed-off-by: Danil Grigorev <[email protected]>
1 parent 01a95b2 commit be5bcf1

File tree

6 files changed

+171
-48
lines changed

6 files changed

+171
-48
lines changed

api/v1alpha2/conditions_consts.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ const (
4747
// ComponentsFetchErrorReason documents that an error occurred fetching the components.
4848
ComponentsFetchErrorReason = "ComponentsFetchError"
4949

50+
// ComponentsUpgradeErrorReason documents that an error occurred while upgrading the components.
51+
ComponentsUpgradeErrorReason = "ComponentsUpgradeError"
52+
5053
// OldComponentsDeletionErrorReason documents that an error occurred deleting the old components prior to upgrading.
5154
OldComponentsDeletionErrorReason = "OldComponentsDeletionError"
5255

@@ -63,4 +66,7 @@ const (
6366
const (
6467
// ProviderInstalledCondition documents a Provider that has been installed.
6568
ProviderInstalledCondition clusterv1.ConditionType = "ProviderInstalled"
69+
70+
// ProviderUpgradedCondition documents a Provider that has been recently upgraded.
71+
ProviderUpgradedCondition clusterv1.ConditionType = "ProviderUpgraded"
6672
)

internal/controller/client_proxy.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha2"
3333
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
3434
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
35+
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
3536
"sigs.k8s.io/controller-runtime/pkg/client"
3637
)
3738

@@ -218,3 +219,25 @@ func listObjByGVK(ctx context.Context, c client.Client, groupVersion, kind strin
218219

219220
return objList, nil
220221
}
222+
223+
type repositoryProxy struct {
224+
repository.Client
225+
226+
components repository.Components
227+
}
228+
229+
type repositoryClient struct {
230+
components repository.Components
231+
}
232+
233+
func (r repositoryClient) Raw(ctx context.Context, options repository.ComponentsOptions) ([]byte, error) {
234+
return nil, nil
235+
}
236+
237+
func (r repositoryClient) Get(ctx context.Context, options repository.ComponentsOptions) (repository.Components, error) {
238+
return r.components, nil
239+
}
240+
241+
func (r repositoryProxy) Components() repository.ComponentsClient {
242+
return repositoryClient{r.components}
243+
}

internal/controller/genericprovider_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,9 @@ func (r *GenericProviderReconciler) reconcile(ctx context.Context, provider gene
157157
reconciler.downloadManifests,
158158
reconciler.load,
159159
reconciler.fetch,
160-
reconciler.preInstall,
160+
reconciler.upgrade,
161161
reconciler.install,
162+
reconciler.reportStatus,
162163
}
163164

164165
res := reconcile.Result{}

internal/controller/genericprovider_controller_test.go

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ package controller
1818

1919
import (
2020
"testing"
21+
"time"
2122

2223
. "github.com/onsi/gomega"
24+
appsv1 "k8s.io/api/apps/v1"
2325
corev1 "k8s.io/api/core/v1"
2426
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2527
"k8s.io/apimachinery/pkg/runtime"
@@ -39,7 +41,7 @@ apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
3941
releaseSeries:
4042
- major: 0
4143
minor: 4
42-
contract: v1alpha4
44+
contract: v1beta1
4345
`
4446
testComponents = `
4547
apiVersion: apps/v1
@@ -215,22 +217,47 @@ func TestReconcilerPreflightConditions(t *testing.T) {
215217
}
216218
}
217219

218-
func TestUpgradeDowngradeProvider(t *testing.T) {
220+
func TestAirGappedUpgradeDowngradeProvider(t *testing.T) {
221+
currentVersion := "v999.9.2"
222+
futureMetadata := `
223+
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
224+
releaseSeries:
225+
- major: 999
226+
minor: 9
227+
contract: v1beta1
228+
`
229+
230+
dummyFutureConfigMap := func(ns, name string) *corev1.ConfigMap {
231+
return &corev1.ConfigMap{
232+
ObjectMeta: metav1.ObjectMeta{
233+
Name: name,
234+
Namespace: ns,
235+
Labels: map[string]string{
236+
"test": "dummy-config",
237+
},
238+
},
239+
Data: map[string]string{
240+
"metadata": futureMetadata,
241+
"components": testComponents,
242+
},
243+
}
244+
}
245+
219246
testCases := []struct {
220247
name string
221248
newVersion string
222249
}{
223250
{
224251
name: "same provider version",
225-
newVersion: "v0.4.2",
252+
newVersion: "v999.9.2",
226253
},
227254
{
228255
name: "upgrade provider version",
229-
newVersion: "v0.4.3",
256+
newVersion: "v999.9.3",
230257
},
231258
{
232259
name: "downgrade provider version",
233-
newVersion: "v0.4.1",
260+
newVersion: "v999.9.1",
234261
},
235262
}
236263

@@ -244,7 +271,7 @@ func TestUpgradeDowngradeProvider(t *testing.T) {
244271
},
245272
Spec: operatorv1.CoreProviderSpec{
246273
ProviderSpec: operatorv1.ProviderSpec{
247-
Version: testCurrentVersion,
274+
Version: currentVersion,
248275
},
249276
},
250277
}
@@ -254,7 +281,7 @@ func TestUpgradeDowngradeProvider(t *testing.T) {
254281
t.Log("Ensure namespace exists", namespace)
255282
g.Expect(env.EnsureNamespaceExists(ctx, namespace)).To(Succeed())
256283

257-
g.Expect(env.CreateAndWait(ctx, dummyConfigMap(namespace, testCurrentVersion))).To(Succeed())
284+
g.Expect(env.CreateAndWait(ctx, dummyFutureConfigMap(namespace, currentVersion))).To(Succeed())
258285

259286
insertDummyConfig(provider)
260287
provider.SetNamespace(namespace)
@@ -266,7 +293,7 @@ func TestUpgradeDowngradeProvider(t *testing.T) {
266293
return false
267294
}
268295

269-
if provider.GetStatus().InstalledVersion == nil || *provider.GetStatus().InstalledVersion != testCurrentVersion {
296+
if provider.GetStatus().InstalledVersion == nil || *provider.GetStatus().InstalledVersion != currentVersion {
270297
return false
271298
}
272299

@@ -283,13 +310,16 @@ func TestUpgradeDowngradeProvider(t *testing.T) {
283310
}, timeout).Should(BeEquivalentTo(true))
284311

285312
// creating another configmap with another version
286-
if tc.newVersion != testCurrentVersion {
287-
g.Expect(env.CreateAndWait(ctx, dummyConfigMap(namespace, tc.newVersion))).To(Succeed())
313+
if tc.newVersion != currentVersion {
314+
g.Expect(env.CreateAndWait(ctx, dummyFutureConfigMap(namespace, tc.newVersion))).To(Succeed())
288315
}
289316

290317
// Change provider version
291318
providerSpec := provider.GetSpec()
292319
providerSpec.Version = tc.newVersion
320+
providerSpec.Deployment = &operatorv1.DeploymentSpec{
321+
Replicas: pointer.Int(2),
322+
}
293323
provider.SetSpec(providerSpec)
294324

295325
// Set label (needed to start a reconciliation of the provider)
@@ -315,23 +345,68 @@ func TestUpgradeDowngradeProvider(t *testing.T) {
315345
return false
316346
}
317347

348+
allFound := false
318349
for _, cond := range provider.GetStatus().Conditions {
319350
if cond.Type == operatorv1.PreflightCheckCondition {
320351
t.Log(t.Name(), provider.GetName(), cond)
321352
if cond.Status == corev1.ConditionTrue {
322-
return true
353+
allFound = true
354+
break
323355
}
324356
}
325357
}
326358

327-
return false
359+
if !allFound {
360+
return false
361+
}
362+
363+
allFound = tc.newVersion == currentVersion
364+
for _, cond := range provider.GetStatus().Conditions {
365+
if cond.Type == operatorv1.ProviderUpgradedCondition {
366+
t.Log(t.Name(), provider.GetName(), cond)
367+
if cond.Status == corev1.ConditionTrue {
368+
allFound = tc.newVersion != currentVersion
369+
break
370+
}
371+
}
372+
}
373+
374+
if !allFound {
375+
return false
376+
}
377+
378+
// Ensure customization occurred
379+
dep := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{
380+
Namespace: provider.Namespace,
381+
Name: "capd-controller-manager",
382+
}}
383+
if err := env.Get(ctx, client.ObjectKeyFromObject(dep), dep); err != nil {
384+
return false
385+
}
386+
387+
return dep.Spec.Replicas != nil && *dep.Spec.Replicas == 2
328388
}, timeout).Should(BeEquivalentTo(true))
329389

390+
g.Consistently(func() bool {
391+
allSet := tc.newVersion == currentVersion
392+
for _, cond := range provider.GetStatus().Conditions {
393+
if cond.Type == operatorv1.ProviderUpgradedCondition {
394+
t.Log(t.Name(), provider.GetName(), cond)
395+
if cond.Status == corev1.ConditionTrue {
396+
allSet = tc.newVersion != currentVersion
397+
break
398+
}
399+
}
400+
}
401+
402+
return allSet
403+
}, 2*time.Second).Should(BeTrue())
404+
330405
// Clean up
331406
objs := []client.Object{provider}
332407
objs = append(objs, &corev1.ConfigMap{
333408
ObjectMeta: metav1.ObjectMeta{
334-
Name: testCurrentVersion,
409+
Name: currentVersion,
335410
Namespace: namespace,
336411
},
337412
})

internal/controller/manifests_downloader.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
6565

6666
exists, err := p.checkConfigMapExists(ctx, labelSelector)
6767
if err != nil {
68-
return reconcile.Result{}, wrapPhaseError(err, "failed to check that config map with manifests exists")
68+
return reconcile.Result{}, wrapPhaseError(err, "failed to check that config map with manifests exists", operatorv1.ProviderInstalledCondition)
6969
}
7070

7171
if exists {
@@ -80,7 +80,7 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
8080
if err != nil {
8181
err = fmt.Errorf("failed to create repo from provider url for provider %q: %w", p.provider.GetName(), err)
8282

83-
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
83+
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
8484
}
8585

8686
spec := p.provider.GetSpec()
@@ -98,22 +98,22 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
9898
if err != nil {
9999
err = fmt.Errorf("failed to read %q from the repository for provider %q: %w", metadataFile, p.provider.GetName(), err)
100100

101-
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
101+
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
102102
}
103103

104104
componentsFile, err := repo.GetFile(ctx, spec.Version, repo.ComponentsPath())
105105
if err != nil {
106106
err = fmt.Errorf("failed to read %q from the repository for provider %q: %w", componentsFile, p.provider.GetName(), err)
107107

108-
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
108+
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
109109
}
110110

111111
withCompression := needToCompress(metadataFile, componentsFile)
112112

113113
if err := p.createManifestsConfigMap(ctx, metadataFile, componentsFile, withCompression); err != nil {
114114
err = fmt.Errorf("failed to create config map for provider %q: %w", p.provider.GetName(), err)
115115

116-
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
116+
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
117117
}
118118

119119
return reconcile.Result{}, nil

0 commit comments

Comments
 (0)