Workflow Support Resource GC (#1970)

* gc

* add test cases

* test case
This commit is contained in:
Jian.Li
2021-07-27 19:22:05 +08:00
committed by GitHub
parent 804024599b
commit a736b1f7b0
12 changed files with 237 additions and 77 deletions

View File

@@ -150,7 +150,7 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
r.Recorder.Event(app, event.Normal(velatypes.ReasonRevisoned, velatypes.MessageRevisioned))
klog.Info("Successfully apply application revision", "application", klog.KObj(app))
policies, wfSteps, err := appFile.GenerateWorkflowAndPolicy(ctx, r.dm, r.Client, r.pd)
policies, wfSteps, err := appFile.GenerateWorkflowAndPolicy(ctx, r.dm, r.Client, r.pd, handler.Dispatch)
if err != nil {
klog.Error(err, "[Handle GenerateWorkflowAndPolicy]")
r.Recorder.Event(app, event.Warning(velatypes.ReasonFailedRender, err))
@@ -174,16 +174,37 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
r.Recorder.Event(app, event.Normal(velatypes.ReasonApplied, velatypes.MessageApplied))
klog.Info("Successfully apply application manifests", "application", klog.KObj(app))
done, err := workflow.NewWorkflow(app, r.Client).ExecuteSteps(ctx, handler.currentAppRev.Name, wfSteps)
done, pause, err := workflow.NewWorkflow(app, r.Client).ExecuteSteps(ctx, handler.currentAppRev.Name, wfSteps)
if err != nil {
klog.Error(err, "[handle workflow]")
r.Recorder.Event(app, event.Warning(velatypes.ReasonFailedWorkflow, err))
return r.endWithNegativeCondition(ctx, app, utils.ErrorCondition("Workflow", err))
}
if pause {
if err := r.patchStatus(ctx, app); err != nil {
return r.endWithNegativeCondition(ctx, app, v1alpha1.ReconcileError(err))
}
return ctrl.Result{}, nil
}
if !done {
return reconcile.Result{RequeueAfter: WorkflowReconcileWaitTime}, r.patchStatus(ctx, app)
}
if wfStatus := app.Status.Workflow; wfStatus != nil && !wfStatus.Terminated {
ref, err := handler.DispatchAndGC(ctx)
if err != nil {
klog.ErrorS(err, "Failed to gc after workflow",
"application", klog.KObj(app))
r.Recorder.Event(app, event.Warning(velatypes.ReasonFailedGC, err))
return r.endWithNegativeCondition(ctx, app, utils.ErrorCondition("GCAfterWorkflow", err))
}
wfStatus.Terminated = true
app.Status.ResourceTracker = ref
return r.endWithNegativeCondition(ctx, app, utils.ReadyCondition("GCAfterWorkflow"))
}
// if inplace is false and rolloutPlan is nil, it means the user will use an outer AppRollout object to rollout the application
if handler.app.Spec.RolloutPlan != nil {
res, err := handler.handleRollout(ctx)

View File

@@ -20,7 +20,6 @@ import (
"context"
runtimev1alpha1 "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -49,10 +48,51 @@ type AppHandler struct {
app *v1beta1.Application
currentAppRev *v1beta1.ApplicationRevision
latestAppRev *v1beta1.ApplicationRevision
latestTracker *v1beta1.ResourceTracker
dispatcher *dispatch.AppManifestsDispatcher
isNewRevision bool
currentRevHash string
}
// Dispatch apply manifests into k8s.
func (h *AppHandler) Dispatch(ctx context.Context, manifests ...*unstructured.Unstructured) error {
h.initDispatcher()
_, err := h.dispatcher.Dispatch(ctx, manifests)
return err
}
// DispatchAndGC apply manifests and do GC.
func (h *AppHandler) DispatchAndGC(ctx context.Context, manifests ...*unstructured.Unstructured) (*runtimev1alpha1.TypedReference, error) {
h.initDispatcher()
tracker, err := h.dispatcher.EndAndGC(h.latestTracker).Dispatch(ctx, manifests)
if err != nil {
return nil, errors.WithMessage(err, "cannot dispatch application manifests")
}
return &runtimev1alpha1.TypedReference{
APIVersion: tracker.APIVersion,
Kind: tracker.Kind,
Name: tracker.Name,
UID: tracker.UID,
}, nil
}
func (h *AppHandler) initDispatcher() {
if h.latestTracker == nil {
if h.app.Status.ResourceTracker != nil {
h.latestTracker = &v1beta1.ResourceTracker{}
h.latestTracker.Name = h.app.Status.ResourceTracker.Name
} else if h.app.Status.LatestRevision != nil {
h.latestTracker = &v1beta1.ResourceTracker{}
h.latestTracker.SetName(dispatch.ConstructResourceTrackerName(h.app.Status.LatestRevision.Name, h.app.Namespace))
}
}
if h.dispatcher == nil {
// only do GC when ALL resources are dispatched successfully
// so skip GC while dispatching addon resources
h.dispatcher = dispatch.NewAppManifestsDispatcher(h.r.Client, h.currentAppRev).StartAndSkipGC(h.latestTracker)
}
}
// ApplyAppManifests will dispatch Application manifests
func (h *AppHandler) ApplyAppManifests(ctx context.Context, comps []*types.ComponentManifest, policies []*unstructured.Unstructured) error {
appRev := h.currentAppRev
@@ -68,13 +108,10 @@ func (h *AppHandler) ApplyAppManifests(ctx context.Context, comps []*types.Compo
latestTracker = &v1beta1.ResourceTracker{}
latestTracker.SetName(dispatch.ConstructResourceTrackerName(h.app.Status.LatestRevision.Name, h.app.Namespace))
}
// only do GC when ALL resources are dispatched successfully
// so skip GC while dispatching addon resources
d := dispatch.NewAppManifestsDispatcher(h.r.Client, appRev).StartAndSkipGC(latestTracker)
// dispatch packaged workload resources before dispatching assembled manifests
for _, comp := range comps {
if len(comp.PackagedWorkloadResources) != 0 {
if _, err := d.Dispatch(ctx, comp.PackagedWorkloadResources); err != nil {
if err := h.Dispatch(ctx, comp.PackagedWorkloadResources...); err != nil {
return errors.WithMessage(err, "cannot dispatch packaged workload resources")
}
}
@@ -82,15 +119,13 @@ func (h *AppHandler) ApplyAppManifests(ctx context.Context, comps []*types.Compo
continue
}
}
a := assemble.NewAppManifests(appRev).WithWorkloadOption(assemble.DiscoveryHelmBasedWorkload(ctx, h.r.Client))
a := assemble.NewAppManifests(h.currentAppRev).WithWorkloadOption(assemble.DiscoveryHelmBasedWorkload(ctx, h.r.Client))
manifests, err := a.AssembledManifests()
if err != nil {
return errors.WithMessage(err, "cannot assemble application manifests")
}
if _, err := d.EndAndGC(latestTracker).Dispatch(ctx, manifests); err != nil {
return errors.WithMessage(err, "cannot dispatch application manifests")
}
return nil
_, err = h.DispatchAndGC(ctx, manifests...)
return err
}
func (h *AppHandler) aggregateHealthStatus(appFile *appfile.Appfile) ([]common.ApplicationComponentStatus, bool, error) {

View File

@@ -111,7 +111,7 @@ var _ = Describe("Test Workflow", func() {
})
It("should execute workflow step to apply and wait", func() {
Expect(k8sClient.Create(ctx, appWithWorkflow)).Should(BeNil())
Expect(k8sClient.Create(ctx, appWithWorkflow.DeepCopy())).Should(BeNil())
// first try to add finalizer
tryReconcile(reconciler, appWithWorkflow.Name, appWithWorkflow.Namespace)
@@ -146,6 +146,7 @@ var _ = Describe("Test Workflow", func() {
triggerWorkflowStepToSucceed(stepObj)
Expect(k8sClient.Update(ctx, stepObj)).Should(BeNil())
tryReconcile(reconciler, appWithWorkflow.Name, appWithWorkflow.Namespace)
tryReconcile(reconciler, appWithWorkflow.Name, appWithWorkflow.Namespace)
// check workflow status is succeeded
@@ -155,7 +156,50 @@ var _ = Describe("Test Workflow", func() {
}, appObj)).Should(BeNil())
Expect(appObj.Status.Workflow.Steps[0].Phase).Should(Equal(common.WorkflowStepPhaseSucceeded))
Expect(appObj.Status.Workflow.Terminated).Should(BeTrue())
})
It("test workflow suspend", func() {
suspendApp := appWithWorkflow.DeepCopy()
suspendApp.Name = "test-app-suspend"
suspendApp.Spec.Workflow.Steps = []oamcore.WorkflowStep{{
Name: "suspend",
Type: "suspend",
Properties: runtime.RawExtension{Raw: []byte(`{}`)},
}}
Expect(k8sClient.Create(ctx, suspendApp)).Should(BeNil())
// first try to add finalizer
tryReconcile(reconciler, suspendApp.Name, suspendApp.Namespace)
tryReconcile(reconciler, suspendApp.Name, suspendApp.Namespace)
appObj := &oamcore.Application{}
Expect(k8sClient.Get(ctx, client.ObjectKey{
Name: suspendApp.Name,
Namespace: suspendApp.Namespace,
}, appObj)).Should(BeNil())
Expect(appObj.Status.Workflow.Suspend).Should(BeTrue())
Expect(appObj.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow))
// resume
appObj.Status.Workflow.Suspend = false
Expect(k8sClient.Status().Patch(ctx, appObj, client.Merge)).Should(BeNil())
tryReconcile(reconciler, suspendApp.Name, suspendApp.Namespace)
tryReconcile(reconciler, suspendApp.Name, suspendApp.Namespace)
appObj = &oamcore.Application{}
Expect(k8sClient.Get(ctx, client.ObjectKey{
Name: suspendApp.Name,
Namespace: suspendApp.Namespace,
}, appObj)).Should(BeNil())
Expect(appObj.Status.Workflow.Suspend).Should(BeFalse())
Expect(appObj.Status.Workflow.Terminated).Should(BeTrue())
Expect(appObj.Status.Workflow.StepIndex).Should(BeEquivalentTo(1))
})
})
func triggerWorkflowStepToSucceed(obj *unstructured.Unstructured) {