@@ -29,6 +29,7 @@ import (
29
29
"k8s.io/apimachinery/pkg/runtime/schema"
30
30
"k8s.io/apimachinery/pkg/util/rand"
31
31
"k8s.io/apimachinery/pkg/util/sets"
32
+ "k8s.io/apimachinery/pkg/util/wait"
32
33
"sigs.k8s.io/controller-runtime/pkg/client"
33
34
34
35
"sigs.k8s.io/cluster-api/cmd/clusterctl/internal/scheme"
@@ -83,12 +84,8 @@ func (m *crdMigrator) run(ctx context.Context, newCRD *apiextensionsv1.CustomRes
83
84
84
85
// Gets the list of version supported by the new CRD
85
86
newVersions := sets.Set [string ]{}
86
- servedVersions := sets.Set [string ]{}
87
87
for _ , version := range newCRD .Spec .Versions {
88
88
newVersions .Insert (version .Name )
89
- if version .Served {
90
- servedVersions .Insert (version .Name )
91
- }
92
89
}
93
90
94
91
// Get the current CRD.
@@ -115,23 +112,22 @@ func (m *crdMigrator) run(ctx context.Context, newCRD *apiextensionsv1.CustomRes
115
112
}
116
113
117
114
currentStatusStoredVersions := sets.Set [string ]{}.Insert (currentCRD .Status .StoredVersions ... )
118
- // If the new CRD still contains all current stored versions, nothing to do
119
- // as no previous storage version will be dropped.
120
- if servedVersions .HasAll (currentStatusStoredVersions .UnsortedList ()... ) {
115
+ // If the old CRD only contains its current storageVersion as storedVersion,
116
+ // nothing to do as all objects are already on the current storageVersion.
117
+ // Note: We want to migrate objects to new storage versions as soon as possible
118
+ // to prevent unnecessary conversion webhook calls.
119
+ if currentStatusStoredVersions .Len () == 1 && currentCRD .Status .StoredVersions [0 ] == currentStorageVersion {
121
120
log .V (2 ).Info ("CRD migration check passed" , "name" , newCRD .Name )
122
121
return false , nil
123
122
}
124
123
125
- // Otherwise a version that has been used as storage version will be dropped, so it is necessary to migrate all the
126
- // objects and drop the storage version from the current CRD status before installing the new CRD.
127
- // Ref https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#writing-reading-and-updating-versioned-customresourcedefinition-objects
128
124
// Note: We are simply migrating all CR objects independent of the version in which they are actually stored in etcd.
129
125
// This way we can make sure that all CR objects are now stored in the current storage version.
130
126
// Alternatively, we would have to figure out which objects are stored in which version but this information is not
131
127
// exposed by the apiserver.
132
- storedVersionsToDelete := currentStatusStoredVersions . Difference ( servedVersions )
133
- storedVersionsToPreserve := currentStatusStoredVersions .Intersection ( servedVersions )
134
- log .Info ("CR migration required" , "kind" , newCRD .Spec .Names .Kind , "storedVersionsToDelete" , strings .Join (sets .List (storedVersionsToDelete ), "," ), "storedVersionsToPreserve " , strings . Join ( sets . List ( storedVersionsToPreserve ), "," ) )
128
+ // Ref https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#writing-reading-and-updating-versioned-customresourcedefinition-objects
129
+ storedVersionsToDelete := currentStatusStoredVersions .Delete ( currentStorageVersion )
130
+ log .Info ("CR migration required" , "kind" , newCRD .Spec .Names .Kind , "storedVersionsToDelete" , strings .Join (sets .List (storedVersionsToDelete ), "," ), "storedVersionToPreserve " , currentStorageVersion )
135
131
136
132
if err := m .migrateResourcesForCRD (ctx , currentCRD , currentStorageVersion ); err != nil {
137
133
return false , err
@@ -157,7 +153,7 @@ func (m *crdMigrator) migrateResourcesForCRD(ctx context.Context, crd *apiextens
157
153
158
154
var i int
159
155
for {
160
- if err := retryWithExponentialBackoff (ctx , newReadBackoff (), func (ctx context.Context ) error {
156
+ if err := retryWithExponentialBackoff (ctx , newCRDMigrationBackoff (), func (ctx context.Context ) error {
161
157
return m .Client .List (ctx , list , client .Continue (list .GetContinue ()))
162
158
}); err != nil {
163
159
return errors .Wrapf (err , "failed to list %q" , list .GetKind ())
@@ -167,7 +163,7 @@ func (m *crdMigrator) migrateResourcesForCRD(ctx context.Context, crd *apiextens
167
163
obj := list .Items [i ]
168
164
169
165
log .V (5 ).Info ("Migrating" , logf .UnstructuredToValues (obj )... )
170
- if err := retryWithExponentialBackoff (ctx , newWriteBackoff (), func (ctx context.Context ) error {
166
+ if err := retryWithExponentialBackoff (ctx , newCRDMigrationBackoff (), func (ctx context.Context ) error {
171
167
return handleMigrateErr (m .Client .Update (ctx , & obj ))
172
168
}); err != nil {
173
169
return errors .Wrapf (err , "failed to migrate %s/%s" , obj .GetNamespace (), obj .GetName ())
@@ -230,3 +226,20 @@ func storageVersionForCRD(crd *apiextensionsv1.CustomResourceDefinition) (string
230
226
}
231
227
return "" , errors .Errorf ("could not find storage version for CRD %q" , crd .Name )
232
228
}
229
+
230
+ // newCRDMigrationBackoff creates a new API Machinery backoff parameter set suitable for use with crd migration operations.
231
+ // Clusterctl upgrades cert-manager right before doing CRD migration. This may lead to rollout of new certificates.
232
+ // The time between new certificate creation + injection into objects (CRD, Webhooks) and the new secrets getting propagated
233
+ // to the controller can be 60-90s, because the kubelet only periodically syncs secret contents to pods.
234
+ // During this timespan conversion, validating- or mutating-webhooks may be unavailable and cause a failure.
235
+ func newCRDMigrationBackoff () wait.Backoff {
236
+ // Return a exponential backoff configuration which returns durations for a total time of ~1m30s + some buffer.
237
+ // Example: 0, .25s, .6s, 1.1s, 1.8s, 2.7s, 4s, 6s, 9s, 12s, 17s, 25s, 35s, 49s, 69s, 97s, 135s
238
+ // Jitter is added as a random fraction of the duration multiplied by the jitter factor.
239
+ return wait.Backoff {
240
+ Duration : 250 * time .Millisecond ,
241
+ Factor : 1.4 ,
242
+ Steps : 17 ,
243
+ Jitter : 0.1 ,
244
+ }
245
+ }
0 commit comments