Feat: support interactive mode to manually skip encountered errors (#5266)

This commit is contained in:
Somefive
2023-01-06 15:03:47 +08:00
committed by GitHub
parent 693eb3cb1d
commit 5a845104fb
13 changed files with 424 additions and 316 deletions
@@ -75,9 +75,6 @@ const (
const (
// baseWorkflowBackoffWaitTime is the time to wait gc check
baseGCBackoffWaitTime = 3000 * time.Millisecond
// resourceTrackerFinalizer is to delete the resource tracker of the latest app revision.
resourceTrackerFinalizer = "app.oam.dev/resource-tracker-finalizer"
)
var (
@@ -366,18 +363,18 @@ func (r *Reconciler) result(err error) *reconcileResult {
// We must delete all resource trackers related to an application through finalizer logic.
func (r *Reconciler) handleFinalizers(ctx monitorContext.Context, app *v1beta1.Application, handler *AppHandler) (bool, ctrl.Result, error) {
if app.ObjectMeta.DeletionTimestamp.IsZero() {
if !meta.FinalizerExists(app, resourceTrackerFinalizer) {
if !meta.FinalizerExists(app, oam.FinalizerResourceTracker) {
subCtx := ctx.Fork("handle-finalizers", monitorContext.DurationMetric(func(v float64) {
metrics.HandleFinalizersDurationHistogram.WithLabelValues("application", "add").Observe(v)
}))
defer subCtx.Commit("finish add finalizers")
meta.AddFinalizer(app, resourceTrackerFinalizer)
subCtx.Info("Register new finalizer for application", "finalizer", resourceTrackerFinalizer)
meta.AddFinalizer(app, oam.FinalizerResourceTracker)
subCtx.Info("Register new finalizer for application", "finalizer", oam.FinalizerResourceTracker)
endReconcile := !EnableReconcileLoopReduction
return r.result(errors.Wrap(r.Client.Update(ctx, app), errUpdateApplicationFinalizer)).end(endReconcile)
}
} else {
if slices.Contains(app.GetFinalizers(), resourceTrackerFinalizer) {
if slices.Contains(app.GetFinalizers(), oam.FinalizerResourceTracker) {
subCtx := ctx.Fork("handle-finalizers", monitorContext.DurationMetric(func(v float64) {
metrics.HandleFinalizersDurationHistogram.WithLabelValues("application", "remove").Observe(v)
}))
@@ -391,7 +388,7 @@ func (r *Reconciler) handleFinalizers(ctx monitorContext.Context, app *v1beta1.A
return true, result, err
}
if rootRT == nil && currentRT == nil && len(historyRTs) == 0 && cvRT == nil {
meta.RemoveFinalizer(app, resourceTrackerFinalizer)
meta.RemoveFinalizer(app, oam.FinalizerResourceTracker)
meta.RemoveFinalizer(app, oam.FinalizerOrphanResource)
return r.result(errors.Wrap(r.Client.Update(ctx, app), errUpdateApplicationFinalizer)).end(true)
}
@@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/testutil"
. "github.com/onsi/ginkgo"
@@ -117,7 +118,7 @@ var _ = Describe("Test application controller finalizer logic", func() {
checkApp = new(v1beta1.Application)
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
Expect(len(checkApp.Finalizers)).Should(BeEquivalentTo(1))
Expect(checkApp.Finalizers[0]).Should(BeEquivalentTo(resourceTrackerFinalizer))
Expect(checkApp.Finalizers[0]).Should(BeEquivalentTo(oam.FinalizerResourceTracker))
By("delete this cross workload app")
Expect(k8sClient.Delete(ctx, checkApp)).Should(BeNil())
By("delete app will delete resourceTracker")
@@ -148,7 +149,7 @@ var _ = Describe("Test application controller finalizer logic", func() {
checkApp = new(v1beta1.Application)
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
Expect(len(checkApp.Finalizers)).Should(BeEquivalentTo(1))
Expect(checkApp.Finalizers[0]).Should(BeEquivalentTo(resourceTrackerFinalizer))
Expect(checkApp.Finalizers[0]).Should(BeEquivalentTo(oam.FinalizerResourceTracker))
Expect(len(rt.Spec.ManagedResources)).Should(BeEquivalentTo(1))
By("Update the app, set type to normal-worker")
checkApp.Spec.Components[0].Type = "normal-worker"
+7 -6
View File
@@ -179,9 +179,6 @@ const (
// AnnotationDefinitionRevisionName is used to specify the name of DefinitionRevision in component/trait definition
AnnotationDefinitionRevisionName = "definitionrevision.oam.dev/name"
// AnnotationResourceTrackerLifeLong is used to identify life-long resourcetracker which should only be recycled when application is deleted
AnnotationResourceTrackerLifeLong = "resourcetracker.oam.dev/life-long"
// AnnotationAddonsName records the name of initializer stored in configMap
AnnotationAddonsName = "addons.oam.dev/name"
@@ -247,6 +244,10 @@ const (
ResourceTopologyFormatJSON = "json"
)
// FinalizerOrphanResource indicates that the gc process should orphan managed
// resources instead of deleting them
const FinalizerOrphanResource = "app.oam.dev/orphan-resource"
const (
// FinalizerResourceTracker is the application finalizer for gc
FinalizerResourceTracker = "app.oam.dev/resource-tracker-finalizer"
// FinalizerOrphanResource indicates that the gc process should orphan managed
// resources instead of deleting them
FinalizerOrphanResource = "app.oam.dev/orphan-resource"
)
+12 -3
View File
@@ -112,13 +112,22 @@ func (cache *resourceCache) exists(manifest *unstructured.Unstructured) bool {
if cache.app == nil {
return true
}
appKey, controlledBy := apply.GetAppKey(cache.app), apply.GetControlledBy(manifest)
if appKey == controlledBy || (manifest.GetResourceVersion() == "" && !hasOrphanFinalizer(cache.app)) {
return IsResourceManagedByApplication(manifest, cache.app)
}
// IsResourceManagedByApplication check if resource is managed by application
// If the resource has no ResourceVersion, always return true.
// If the owner label of the resource equals the given app, return true.
// If the sharer label of the resource contains the given app, return true.
// Otherwise, return false.
func IsResourceManagedByApplication(manifest *unstructured.Unstructured, app *v1beta1.Application) bool {
appKey, controlledBy := apply.GetAppKey(app), apply.GetControlledBy(manifest)
if appKey == controlledBy || (manifest.GetResourceVersion() == "" && !hasOrphanFinalizer(app)) {
return true
}
annotations := manifest.GetAnnotations()
if annotations == nil || annotations[oam.AnnotationAppSharedBy] == "" {
return false
}
return apply.ContainsSharer(annotations[oam.AnnotationAppSharedBy], cache.app)
return apply.ContainsSharer(annotations[oam.AnnotationAppSharedBy], app)
}
+27 -20
View File
@@ -329,7 +329,8 @@ func (h *gcHandler) deleteIndependentComponent(ctx context.Context, mr v1beta1.M
return nil
}
func (h *gcHandler) deleteSharedManagedResource(ctx context.Context, manifest *unstructured.Unstructured, sharedBy string) error {
// UpdateSharedManagedResourceOwner update owner & sharer labels for managed resource
func UpdateSharedManagedResourceOwner(ctx context.Context, cli client.Client, manifest *unstructured.Unstructured, sharedBy string) error {
parts := strings.Split(apply.FirstSharer(sharedBy), "/")
appName, appNs := "", metav1.NamespaceDefault
if len(parts) == 1 {
@@ -342,7 +343,7 @@ func (h *gcHandler) deleteSharedManagedResource(ctx context.Context, manifest *u
oam.LabelAppName: appName,
oam.LabelAppNamespace: appNs,
})
return h.Client.Update(ctx, manifest)
return cli.Update(ctx, manifest)
}
func (h *gcHandler) deleteManagedResource(ctx context.Context, mr v1beta1.ManagedResource, rt *v1beta1.ResourceTracker) error {
@@ -354,27 +355,33 @@ func (h *gcHandler) deleteManagedResource(ctx context.Context, mr v1beta1.Manage
return entry.err
}
if entry.exists {
_ctx := multicluster.ContextWithClusterName(ctx, mr.Cluster)
if annotations := entry.obj.GetAnnotations(); annotations != nil && annotations[oam.AnnotationAppSharedBy] != "" {
sharedBy := apply.RemoveSharer(annotations[oam.AnnotationAppSharedBy], h.app)
if sharedBy != "" {
if err := h.deleteSharedManagedResource(_ctx, entry.obj, sharedBy); err != nil {
return errors.Wrapf(err, "failed to remove sharer from resource %s", mr.ResourceKey())
}
return nil
return DeleteManagedResourceInApplication(ctx, h.Client, mr, entry.obj, h.app)
}
return nil
}
// DeleteManagedResourceInApplication delete managed resource in application
func DeleteManagedResourceInApplication(ctx context.Context, cli client.Client, mr v1beta1.ManagedResource, obj *unstructured.Unstructured, app *v1beta1.Application) error {
_ctx := multicluster.ContextWithClusterName(ctx, mr.Cluster)
if annotations := obj.GetAnnotations(); annotations != nil && annotations[oam.AnnotationAppSharedBy] != "" {
sharedBy := apply.RemoveSharer(annotations[oam.AnnotationAppSharedBy], app)
if sharedBy != "" {
if err := UpdateSharedManagedResourceOwner(_ctx, cli, obj, sharedBy); err != nil {
return errors.Wrapf(err, "failed to remove sharer from resource %s", mr.ResourceKey())
}
return nil
}
if mr.SkipGC || hasOrphanFinalizer(h.app) {
if labels := entry.obj.GetLabels(); labels != nil {
delete(labels, oam.LabelAppName)
delete(labels, oam.LabelAppNamespace)
entry.obj.SetLabels(labels)
}
return errors.Wrapf(h.Client.Update(_ctx, entry.obj), "failed to remove owner labels for resource while skipping gc")
}
if err := h.Client.Delete(_ctx, entry.obj); err != nil && !kerrors.IsNotFound(err) {
return errors.Wrapf(err, "failed to delete resource %s", mr.ResourceKey())
}
if mr.SkipGC || hasOrphanFinalizer(app) {
if labels := obj.GetLabels(); labels != nil {
delete(labels, oam.LabelAppName)
delete(labels, oam.LabelAppNamespace)
obj.SetLabels(labels)
}
return errors.Wrapf(cli.Update(_ctx, obj), "failed to remove owner labels for resource while skipping gc")
}
if err := cli.Delete(_ctx, obj); err != nil && !kerrors.IsNotFound(err) {
return errors.Wrapf(err, "failed to delete resource %s", mr.ResourceKey())
}
return nil
}