Skip to content

Commit 2ca5279

Browse files
committed
fix: retry manifest updates in upgrade-k8s
This showed up recently frequently in integration-provision tests (might be related to Kubernetes upgrade), but anyways errors should be retried. Refactored the function to extract the retryable part. Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
1 parent eeb7561 commit 2ca5279

File tree

1 file changed

+77
-44
lines changed

1 file changed

+77
-44
lines changed

pkg/cluster/kubernetes/talos_managed.go

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ type UpgradeProvider interface {
5050
}
5151

5252
var deprecations = map[string][]string{
53-
// https://kubernetes.io/blog/2021/07/14/upcoming-changes-in-kubernetes-1-22/#api-changes
53+
// https://kubernetes.io/docs/reference/using-api/deprecation-guide/
5454
"1.21->1.22": {
5555
"validatingwebhookconfigurations.v1beta1.admissionregistration.k8s.io",
5656
"mutatingwebhookconfigurations.v1beta1.admissionregistration.k8s.io",
@@ -394,7 +394,61 @@ func getManifests(ctx context.Context, cluster UpgradeProvider) ([]*unstructured
394394
}
395395
}
396396

397-
//nolint:gocyclo,cyclop
397+
func updateManifest(
398+
ctx context.Context,
399+
mapper *restmapper.DeferredDiscoveryRESTMapper,
400+
k8sClient dynamic.Interface,
401+
obj *unstructured.Unstructured,
402+
dryRun bool,
403+
) (
404+
resp *unstructured.Unstructured,
405+
diff string,
406+
skipped bool,
407+
err error,
408+
) {
409+
mapping, err := mapper.RESTMapping(obj.GroupVersionKind().GroupKind(), obj.GroupVersionKind().Version)
410+
if err != nil {
411+
err = fmt.Errorf("error creating mapping for object %s: %w", obj.GetName(), err)
412+
413+
return nil, "", false, err
414+
}
415+
416+
var dr dynamic.ResourceInterface
417+
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
418+
// namespaced resources should specify the namespace
419+
dr = k8sClient.Resource(mapping.Resource).Namespace(obj.GetNamespace())
420+
} else {
421+
// for cluster-wide resources
422+
dr = k8sClient.Resource(mapping.Resource)
423+
}
424+
425+
exists := true
426+
427+
diff, err = getResourceDiff(ctx, dr, obj)
428+
if err != nil {
429+
if !apierrors.IsNotFound(err) {
430+
return nil, "", false, err
431+
}
432+
433+
exists = false
434+
diff = "resource is going to be created"
435+
}
436+
437+
switch {
438+
case dryRun:
439+
return nil, diff, exists, nil
440+
case !exists:
441+
resp, err = dr.Create(ctx, obj, metav1.CreateOptions{})
442+
case diff != "":
443+
resp, err = dr.Update(ctx, obj, metav1.UpdateOptions{})
444+
default:
445+
skipped = true
446+
}
447+
448+
return resp, diff, skipped, err
449+
}
450+
451+
//nolint:gocyclo
398452
func syncManifests(ctx context.Context, objects []*unstructured.Unstructured, cluster UpgradeProvider, options UpgradeOptions) error {
399453
config, err := cluster.K8sRestConfig(ctx)
400454
if err != nil {
@@ -418,70 +472,49 @@ func syncManifests(ctx context.Context, objects []*unstructured.Unstructured, cl
418472

419473
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(dc))
420474

421-
var (
422-
resp *unstructured.Unstructured
423-
mapping *meta.RESTMapping
424-
// list of deployments to wait for to become ready after update
425-
deployments []*unstructured.Unstructured
426-
)
475+
// list of deployments to wait for to become ready after update
476+
var deployments []*unstructured.Unstructured
427477

428478
options.Log("updating manifests")
429479

430480
for _, obj := range objects {
431-
mapping, err = mapper.RESTMapping(obj.GroupVersionKind().GroupKind(), obj.GroupVersionKind().Version)
432-
if err != nil {
433-
return fmt.Errorf("error creating mapping for object %s: %w", obj.GetName(), err)
434-
}
435-
436-
var dr dynamic.ResourceInterface
437-
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
438-
// namespaced resources should specify the namespace
439-
dr = k8sClient.Resource(mapping.Resource).Namespace(obj.GetNamespace())
440-
} else {
441-
// for cluster-wide resources
442-
dr = k8sClient.Resource(mapping.Resource)
443-
}
444-
445-
var diff string
481+
options.Log(" > processing manifest %s %s", obj.GetKind(), obj.GetName())
482+
483+
var (
484+
resp *unstructured.Unstructured
485+
diff string
486+
skipped bool
487+
)
488+
489+
err = retry.Constant(3*time.Minute, retry.WithUnits(10*time.Second), retry.WithErrorLogging(true)).RetryWithContext(ctx, func(ctx context.Context) error {
490+
resp, diff, skipped, err = updateManifest(ctx, mapper, k8sClient, obj, options.DryRun)
491+
if kubernetes.IsRetryableError(err) {
492+
return retry.ExpectedError(err)
493+
}
446494

447-
exists := true
495+
return err
496+
})
448497

449-
diff, err = getResourceDiff(ctx, dr, obj)
450498
if err != nil {
451-
if !apierrors.IsNotFound(err) {
452-
return err
453-
}
454-
455-
exists = false
456-
diff = "resource is going to be created"
499+
return err
457500
}
458501

459-
options.Log(" > apply manifest %s %s", obj.GetKind(), obj.GetName())
460-
461502
switch {
462503
case options.DryRun:
463504
var diffInfo string
464505
if diff != "" {
465506
diffInfo = fmt.Sprintf(", diff:\n%s", diff)
466507
}
467508

468-
options.Log(" > apply skipped in dry run%s", diffInfo)
509+
options.Log(" < apply skipped in dry run%s", diffInfo)
469510

470511
continue
471-
case !exists:
472-
resp, err = dr.Create(ctx, obj, metav1.CreateOptions{})
473-
case diff != "":
474-
resp, err = dr.Update(ctx, obj, metav1.UpdateOptions{})
475-
default:
476-
options.Log(" > apply skipped: nothing to update")
512+
case skipped:
513+
options.Log(" < apply skipped: nothing to update")
477514

478515
continue
479516
}
480517

481-
if err != nil {
482-
return err
483-
}
484-
485518
if resp.GetKind() == "Deployment" {
486519
deployments = append(deployments, resp)
487520
}

0 commit comments

Comments
 (0)