@@ -2,19 +2,21 @@ package controllers
22
33import (
44"context"
5+ "errors"
56"fmt"
67"io"
78"net/http"
89"os"
910"path/filepath"
10- "sync"
1111"time"
1212
1313metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414"k8s.io/apimachinery/pkg/types"
15+ "k8s.io/utils/ptr"
1516ctrl "sigs.k8s.io/controller-runtime"
1617"sigs.k8s.io/controller-runtime/pkg/builder"
1718"sigs.k8s.io/controller-runtime/pkg/client"
19+ "sigs.k8s.io/controller-runtime/pkg/controller"
1820clog "sigs.k8s.io/controller-runtime/pkg/log"
1921"sigs.k8s.io/controller-runtime/pkg/predicate"
2022"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -41,9 +43,6 @@ type EdgeFunctionRevisionReconciler struct {
4143goStoreDir string
4244jsStoreDir string
4345edgeRuntime edgefunc.Runtime
44-
45- mu sync.Mutex
46- edgeFuncs map [string ]* v1alpha1.EdgeFunctionRevision
4746}
4847
4948// NewEdgeFunctionRevisionReconciler returns a new reconcile.Reconciler.
@@ -53,15 +52,15 @@ func NewEdgeFunctionRevisionReconciler(
5352wasmStore manifest.Store ,
5453goStoreDir string ,
5554jsStoreDir string ,
55+ edgeRuntime edgefunc.Runtime ,
5656) * EdgeFunctionRevisionReconciler {
5757return & EdgeFunctionRevisionReconciler {
5858Client : c ,
5959apiserverHost : apiserverHost ,
6060wasmStore : wasmStore ,
6161goStoreDir : goStoreDir ,
6262jsStoreDir : jsStoreDir ,
63-
64- edgeFuncs : make (map [string ]* v1alpha1.EdgeFunctionRevision ),
63+ edgeRuntime : edgeRuntime ,
6564}
6665}
6766
@@ -74,17 +73,16 @@ func (r *EdgeFunctionRevisionReconciler) downloadFuncData(
7473
7574resp , err := http .Get (fmt .Sprintf ("http://%s/%s/%s" , r .apiserverHost , fnType , ref ))
7675if err != nil {
77- return nil , fmt .Errorf ("failed to download Wasm module : %w" , err )
76+ return nil , fmt .Errorf ("failed to download edge function : %w" , err )
7877}
79-
8078if resp .StatusCode != http .StatusOK {
81- return nil , fmt .Errorf ("failed to download Wasm module : %s" , resp .Status )
79+ return nil , fmt .Errorf ("failed to download edge function : %s" , resp .Status )
8280}
8381defer resp .Body .Close ()
8482
8583data , err := io .ReadAll (resp .Body )
8684if err != nil {
87- return nil , fmt .Errorf ("failed to read Wasm module : %w" , err )
85+ return nil , fmt .Errorf ("failed to read edge function data : %w" , err )
8886}
8987
9088log .Info ("Downloaded EdgeFunction module" , "size" , len (data ), "type" , fnType , "ref" , ref )
@@ -101,6 +99,55 @@ func hasReadyCondition(conditions []metav1.Condition) bool {
10199return false
102100}
103101
102+ func (r * EdgeFunctionRevisionReconciler ) reconileEdgeRuntime (ctx context.Context , ref string ) error {
103+ log := clog .FromContext (ctx )
104+
105+ esZipPath := filepath .Join (r .jsStoreDir , ref , "bin.eszip" )
106+ if _ , err := os .Stat (esZipPath ); err != nil && ! os .IsNotExist (err ) {
107+ return fmt .Errorf ("Js bundle already exists for ref" )
108+ } else if os .IsNotExist (err ) {
109+ jsBundle , err := r .downloadFuncData (clog .IntoContext (ctx , log ), "js" , ref )
110+ if err != nil {
111+ return fmt .Errorf ("failed to download Js data: %w" , err )
112+ }
113+
114+ if err := os .MkdirAll (filepath .Join (r .jsStoreDir , ref ), 0755 ); err != nil {
115+ return fmt .Errorf ("failed to create Js directory: %w" , err )
116+ }
117+
118+ if err := os .WriteFile (filepath .Join (r .jsStoreDir , ref , "data" ), jsBundle , 0644 ); err != nil {
119+ return fmt .Errorf ("failed to write Js data: %w" , err )
120+ }
121+ // Use symlink to prevent Envoy from loading the plugin while it's being written to.
122+ if err := os .Symlink ("data" , esZipPath ); err != nil {
123+ return fmt .Errorf ("failed to create Js symlink: %w" , err )
124+ }
125+ }
126+
127+ s , err := r .edgeRuntime .Status (ctx , ref )
128+ if err != nil && ! errors .Is (err , edgefunc .ErrNotFound ) {
129+ return fmt .Errorf ("failed to get Edge Runtime status: %w" , err )
130+ }
131+
132+ log .Info ("Edge Runtime status" , "state" , s .State )
133+
134+ switch s .State {
135+ case edgefunc .StateRunning , edgefunc .StateCreated :
136+ log .Info ("Edge Runtime is already running or created" , "state" , s .State )
137+ return nil
138+ case edgefunc .StateStopped :
139+ log .Info ("Edge Runtime is stopped, starting it" )
140+ }
141+
142+ log .Info ("Starting Edge Runtime" )
143+
144+ if err := r .edgeRuntime .Start (ctx , ref , esZipPath ); err != nil {
145+ return fmt .Errorf ("failed to start Edge Runtime: %w" , err )
146+ }
147+
148+ return nil
149+ }
150+
104151// Reconcile implements reconcile.Reconciler.
105152func (r * EdgeFunctionRevisionReconciler ) Reconcile (ctx context.Context , request reconcile.Request ) (ctrl.Result , error ) {
106153log := clog .FromContext (ctx )
@@ -208,27 +255,9 @@ func (r *EdgeFunctionRevisionReconciler) Reconcile(ctx context.Context, request
208255} else if rev .Spec .Code .JsSource != nil {
209256log .Info ("Js source detected" )
210257
211- jsBundle , err := r .downloadFuncData (clog .IntoContext (ctx , log ), "js" , ref )
212- if err != nil {
213- return ctrl.Result {}, fmt .Errorf ("failed to download Js data: %w" , err )
214- }
215-
216- if err := os .MkdirAll (filepath .Join (r .jsStoreDir , ref ), 0755 ); err != nil {
217- return ctrl.Result {}, fmt .Errorf ("failed to create Js directory: %w" , err )
218- }
219-
220- if err := os .WriteFile (filepath .Join (r .jsStoreDir , ref , "data" ), jsBundle , 0644 ); err != nil {
221- return ctrl.Result {}, fmt .Errorf ("failed to write Js data: %w" , err )
222- }
223- // Use symlink to prevent Envoy from loading the plugin while it's being written to.
224- esZipPath := filepath .Join (r .jsStoreDir , ref , "bin.eszip" )
225- if err := os .Symlink ("data" , esZipPath ); err != nil {
226- return ctrl.Result {}, fmt .Errorf ("failed to create Js symlink: %w" , err )
258+ if err := r .reconileEdgeRuntime (ctx , ref ); err != nil {
259+ return ctrl.Result {}, fmt .Errorf ("failed to reconile Edge Runtime: %w" , err )
227260}
228-
229- r .mu .Lock ()
230- r .edgeFuncs [ref ] = rev
231- r .mu .Unlock ()
232261} else {
233262log .Info ("No source detected" )
234263return ctrl.Result {}, nil
@@ -237,33 +266,6 @@ func (r *EdgeFunctionRevisionReconciler) Reconcile(ctx context.Context, request
237266return ctrl.Result {}, nil
238267}
239268
240- func (r * EdgeFunctionRevisionReconciler ) reconileEdgeRuntime (ctx context.Context , ref string ) error {
241- log := clog .FromContext (ctx )
242-
243- s , err := r .edgeRuntime .Status (ctx , ref )
244- if err != nil {
245- return fmt .Errorf ("failed to get Edge Runtime status: %w" , err )
246- }
247-
248- switch s .State {
249- case edgefunc .StateRunning :
250- log .Info ("Edge Runtime is already running" )
251- return nil
252- case edgefunc .StateCreated :
253- log .Info ("Edge Runtime is already created" )
254- return nil
255- case edgefunc .StateStopped :
256- log .Info ("Edge Runtime is stopped, starting it" )
257- default :
258- return fmt .Errorf ("Edge Runtime is in an unknown state: %s" , s .State )
259- }
260-
261- if err := r .edgeRuntime .Start (ctx , ref , filepath .Join (r .jsStoreDir , ref , "bin.eszip" )); err != nil {
262- return fmt .Errorf ("failed to start Edge Runtime: %w" , err )
263- }
264- return nil
265- }
266-
267269func targetRefPredicate (proxyName string ) predicate.Funcs {
268270return predicate .NewPredicateFuncs (func (obj client.Object ) bool {
269271if obj == nil {
@@ -309,5 +311,9 @@ func (r *EdgeFunctionRevisionReconciler) SetupWithManager(
309311targetRefPredicate (proxyName ),
310312),
311313).
314+ WithOptions (controller.Options {
315+ MaxConcurrentReconciles : 1 ,
316+ RecoverPanic : ptr .To (true ),
317+ }).
312318Complete (r )
313319}
0 commit comments