@@ -17,6 +17,7 @@ import (
1717"sigs.k8s.io/controller-runtime/pkg/builder"
1818"sigs.k8s.io/controller-runtime/pkg/client"
1919"sigs.k8s.io/controller-runtime/pkg/controller"
20+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2021clog "sigs.k8s.io/controller-runtime/pkg/log"
2122"sigs.k8s.io/controller-runtime/pkg/predicate"
2223"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -29,6 +30,8 @@ import (
2930
3031const (
3132edgeFuncRevsOwnerIndex = "edgeFuncRevsOwner"
33+
34+ backplaneFinalizerPrefix = "controllers.apoxy.dev/backplane-"
3235)
3336
3437var _ reconcile.Reconciler = & EdgeFunctionRevisionReconciler {}
@@ -38,6 +41,7 @@ var _ reconcile.Reconciler = &EdgeFunctionRevisionReconciler{}
3841type EdgeFunctionRevisionReconciler struct {
3942client.Client
4043
44+ replicaName string
4145apiserverHost string
4246wasmStore manifest.Store
4347goStoreDir string
@@ -48,6 +52,7 @@ type EdgeFunctionRevisionReconciler struct {
4852// NewEdgeFunctionRevisionReconciler returns a new reconcile.Reconciler.
4953func NewEdgeFunctionRevisionReconciler (
5054c client.Client ,
55+ replicaName string ,
5156apiserverHost string ,
5257wasmStore manifest.Store ,
5358goStoreDir string ,
@@ -56,6 +61,7 @@ func NewEdgeFunctionRevisionReconciler(
5661) * EdgeFunctionRevisionReconciler {
5762return & EdgeFunctionRevisionReconciler {
5863Client : c ,
64+ replicaName : replicaName ,
5965apiserverHost : apiserverHost ,
6066wasmStore : wasmStore ,
6167goStoreDir : goStoreDir ,
@@ -152,6 +158,48 @@ func (r *EdgeFunctionRevisionReconciler) reconileEdgeRuntime(
152158return nil
153159}
154160
161+ func (r * EdgeFunctionRevisionReconciler ) maybeCleanupEdgeRuntime (ctx context.Context , ref string ) (bool , error ) {
162+ log := clog .FromContext (ctx , "Ref" , ref )
163+
164+ log .Info ("Cleaning up Edge Runtime" )
165+
166+ if err := r .edgeRuntime .StopExec (ctx , ref ); err != nil {
167+ log .Error (err , "Failed to stop Edge Runtime" )
168+ return false , err
169+ }
170+ s , err := r .edgeRuntime .ExecStatus (ctx , ref )
171+ if err != nil {
172+ log .Error (err , "Failed to get Edge Runtime status" )
173+ return false , err
174+ }
175+
176+ switch s .State {
177+ case edgefunc .StateRunning , edgefunc .StateCreated :
178+ log .Info ("Edge Runtime is still waiting for termination" )
179+ return false , nil
180+ case edgefunc .StateStopped :
181+ log .Info ("Edge Runtime is stopped, cleaning up" )
182+ }
183+
184+ if err := r .edgeRuntime .DeleteExec (ctx , ref ); err != nil && ! errors .Is (err , edgefunc .ErrNotFound ) {
185+ log .Error (err , "Failed to delete Edge Runtime" )
186+ } else if errors .Is (err , edgefunc .ErrNotFound ) {
187+ log .Info ("Edge Runtime not found, nothing to delete" )
188+ return true , nil
189+ }
190+
191+ log .Info ("Edge Runtime deleted, cleaning up data" )
192+
193+ jsBundlePath := filepath .Join (r .jsStoreDir , ref , "data" )
194+ if err := os .RemoveAll (jsBundlePath ); err != nil {
195+ log .Error (err , "Failed to delete Js bundle" )
196+ }
197+
198+ log .Info ("Edge Runtime cleaned up" )
199+
200+ return true , nil
201+ }
202+
155203// Reconcile implements reconcile.Reconciler.
156204func (r * EdgeFunctionRevisionReconciler ) Reconcile (ctx context.Context , request reconcile.Request ) (ctrl.Result , error ) {
157205log := clog .FromContext (ctx )
@@ -179,32 +227,57 @@ func (r *EdgeFunctionRevisionReconciler) Reconcile(ctx context.Context, request
179227}
180228
181229if ! rev .DeletionTimestamp .IsZero () {
182- if ef .Status .LiveRevision == rev .Name { // Do not delete the live revision.
183- log .Info ("EdgeFunctionRevision is the live revision, Skipping deletion." )
184- return ctrl.Result {RequeueAfter : time .Second * 10 }, nil
185- }
186-
187- log .Info ("Deleting EdgeFunctionRevision" )
188-
189- if rev .Spec .Code .WasmSource != nil {
190- log .Info ("Deleting Wasm data" , "ref" , rev .Status .Ref )
191- if err := r .wasmStore .Delete (ctx , rev .Status .Ref ); err != nil {
192- return ctrl.Result {}, fmt .Errorf ("failed to delete Wasm data: %w" , err )
230+ finalizer := backplaneFinalizerPrefix + r .replicaName
231+ if controllerutil .ContainsFinalizer (rev , finalizer ) {
232+ if ef .Status .LiveRevision == rev .Name { // Do not delete the live revision.
233+ log .Info ("EdgeFunctionRevision is the live revision, Skipping deletion." )
234+ return ctrl.Result {RequeueAfter : time .Second * 10 }, nil
193235}
194- } else if rev .Spec .Code .GoPluginSource != nil {
195- log .Info ("Deleting Go plugin data" , "ref" , rev .Status .Ref )
196- if err := os .RemoveAll (filepath .Join (r .goStoreDir , rev .Status .Ref )); err != nil {
197- return ctrl.Result {}, fmt .Errorf ("failed to delete Go plugin data: %w" , err )
236+
237+ log .Info ("Deleting EdgeFunctionRevision" )
238+
239+ if rev .Spec .Code .WasmSource != nil {
240+ log .Info ("Deleting Wasm data" , "ref" , rev .Status .Ref )
241+ if err := r .wasmStore .Delete (ctx , rev .Status .Ref ); err != nil {
242+ return ctrl.Result {}, fmt .Errorf ("failed to delete Wasm data: %w" , err )
243+ }
244+ } else if rev .Spec .Code .GoPluginSource != nil {
245+ log .Info ("Deleting Go plugin data" , "ref" , rev .Status .Ref )
246+ if err := os .RemoveAll (filepath .Join (r .goStoreDir , rev .Status .Ref )); err != nil {
247+ return ctrl.Result {}, fmt .Errorf ("failed to delete Go plugin data: %w" , err )
248+ }
249+ } else if rev .Spec .Code .JsSource != nil {
250+ log .Info ("Deleting Js data" , "ref" , rev .Status .Ref )
251+ if err := os .RemoveAll (filepath .Join (r .jsStoreDir , rev .Status .Ref )); err != nil {
252+ return ctrl.Result {}, fmt .Errorf ("failed to delete Js data: %w" , err )
253+ }
254+
255+ if done , err := r .maybeCleanupEdgeRuntime (ctx , rev .Status .Ref ); err != nil {
256+ log .Error (err , "Failed to cleanup Edge Runtime" )
257+ return ctrl.Result {}, err
258+ } else if ! done {
259+ log .Info ("Edge Runtime is still running, waiting for termination" )
260+ return ctrl.Result {RequeueAfter : time .Second * 10 }, nil
261+ }
198262}
199- } else if rev . Spec . Code . JsSource != nil {
200- log . Info ( "Deleting Js data" , "ref" , rev . Status . Ref )
201- if err := os . RemoveAll ( filepath . Join ( r . jsStoreDir , rev . Status . Ref ) ); err != nil {
202- return ctrl.Result {}, fmt . Errorf ( "failed to delete Js data: %w" , err )
263+
264+ controllerutil . RemoveFinalizer ( rev , finalizer )
265+ if err := r . Update ( ctx , rev ); err != nil {
266+ return ctrl.Result {}, err
203267}
204268}
205269return ctrl.Result {}, nil
206270}
207271
272+ finalizer := backplaneFinalizerPrefix + r .replicaName
273+ if ! controllerutil .ContainsFinalizer (rev , finalizer ) {
274+ log .Info ("Adding finalizer" , "Finalizer" , finalizer )
275+ controllerutil .AddFinalizer (rev , finalizer )
276+ if err := r .Update (ctx , rev ); err != nil {
277+ return ctrl.Result {}, err
278+ }
279+ }
280+
208281// Stage the EdgeFunctionRevision that was successfully ingested (has a Ready condition)
209282// even if it's not Live yet.
210283if ! hasReadyCondition (rev .Status .Conditions ) {
0 commit comments