Skip to content

Commit 9dd7e4a

Browse files
committed
[edgefunc] Implement Js source deletion
1 parent 6f9508e commit 9dd7e4a

File tree

3 files changed

+108
-37
lines changed

3 files changed

+108
-37
lines changed

cmd/backplane/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ func main() {
302302
}
303303
if err := bpctrl.NewEdgeFunctionRevisionReconciler(
304304
mgr.GetClient(),
305+
*replicaName,
305306
net.JoinHostPort(apiServerHost, strconv.Itoa(*wasmStorePort)),
306307
ms,
307308
*goPluginDir,

pkg/backplane/controllers/edgefunction.go

Lines changed: 92 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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"
2021
clog "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

3031
const (
3132
edgeFuncRevsOwnerIndex = "edgeFuncRevsOwner"
33+
34+
backplaneFinalizerPrefix = "controllers.apoxy.dev/backplane-"
3235
)
3336

3437
var _ reconcile.Reconciler = &EdgeFunctionRevisionReconciler{}
@@ -38,6 +41,7 @@ var _ reconcile.Reconciler = &EdgeFunctionRevisionReconciler{}
3841
type EdgeFunctionRevisionReconciler struct {
3942
client.Client
4043

44+
replicaName string
4145
apiserverHost string
4246
wasmStore manifest.Store
4347
goStoreDir string
@@ -48,6 +52,7 @@ type EdgeFunctionRevisionReconciler struct {
4852
// NewEdgeFunctionRevisionReconciler returns a new reconcile.Reconciler.
4953
func NewEdgeFunctionRevisionReconciler(
5054
c client.Client,
55+
replicaName string,
5156
apiserverHost string,
5257
wasmStore manifest.Store,
5358
goStoreDir string,
@@ -56,6 +61,7 @@ func NewEdgeFunctionRevisionReconciler(
5661
) *EdgeFunctionRevisionReconciler {
5762
return &EdgeFunctionRevisionReconciler{
5863
Client: c,
64+
replicaName: replicaName,
5965
apiserverHost: apiserverHost,
6066
wasmStore: wasmStore,
6167
goStoreDir: goStoreDir,
@@ -152,6 +158,48 @@ func (r *EdgeFunctionRevisionReconciler) reconileEdgeRuntime(
152158
return 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.
156204
func (r *EdgeFunctionRevisionReconciler) Reconcile(ctx context.Context, request reconcile.Request) (ctrl.Result, error) {
157205
log := clog.FromContext(ctx)
@@ -179,32 +227,57 @@ func (r *EdgeFunctionRevisionReconciler) Reconcile(ctx context.Context, request
179227
}
180228

181229
if !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
}
205269
return 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.
210283
if !hasReadyCondition(rev.Status.Conditions) {

pkg/edgefunc/runc/network/net.go

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -442,29 +442,22 @@ func (n *Network) Up(ctx context.Context, cid string) error {
442442

443443
func tearDownVeth(info *SandboxInfo) error {
444444
log.Infof("Removing veth pair for %s", info.Veth)
445+
445446
veth, err := netlink.LinkByName(info.Veth)
446-
if err == nil {
447-
// Deleting one end of the veth pair automatically deletes the other end.
448-
if err := netlink.LinkDel(veth); err != nil {
449-
return fmt.Errorf("failed to delete veth pair: %w", err)
450-
}
447+
if err != nil && !errors.Is(err, netlink.LinkNotFoundError{}) {
448+
return fmt.Errorf("failed to get veth: %w", err)
449+
} else if errors.Is(err, netlink.LinkNotFoundError{}) {
450+
log.Debugf("veth not found, nothing to delete")
451+
return nil
451452
}
452453

453-
log.Infof("Removing routes for %s", info.Veth)
454-
for _, r := range info.Routes {
455-
if err := netlink.RouteDel(&netlink.Route{
456-
LinkIndex: veth.Attrs().Index,
457-
Scope: netlink.SCOPE_UNIVERSE,
458-
Dst: &net.IPNet{
459-
IP: net.ParseIP(r.Dst),
460-
Mask: net.CIDRMask(32, 32),
461-
},
462-
Table: r.Table,
463-
}); err != nil && !errors.As(err, &netlink.LinkNotFoundError{}) {
464-
return fmt.Errorf("failed to delete route: %w", err)
465-
}
454+
// Deleting one end of the veth pair automatically deletes the other end.
455+
if err := netlink.LinkDel(veth); err != nil {
456+
return fmt.Errorf("failed to delete veth pair: %w", err)
466457
}
467458

459+
// No need to remove routes - they are delete when the device is removed above.
460+
468461
return nil
469462
}
470463

@@ -500,6 +493,7 @@ func (n *Network) Down(ctx context.Context, cid string) error {
500493
}
501494

502495
// Release prefix from the ipam.
496+
log.Infof("Releasing prefix %s", info.Cidr)
503497
prefix, err := n.ipamer.PrefixFrom(ctx, info.Cidr.String())
504498
if err != nil {
505499
return fmt.Errorf("failed to get prefix: %v", err)
@@ -511,6 +505,9 @@ func (n *Network) Down(ctx context.Context, cid string) error {
511505
if err := ns.Close(); err != nil {
512506
return fmt.Errorf("failed to close netns: %w", err)
513507
}
508+
if err := netns.DeleteNamed(cid); err != nil {
509+
return fmt.Errorf("failed to delete netns: %w", err)
510+
}
514511

515512
// Delete cid network info from the db
516513
if err := n.db.Update(func(txn *badger.Txn) error {

0 commit comments

Comments
 (0)