From 3ac3c4580f53d1654df8f72698cc875607777e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20St=C3=A4bler?= Date: Wed, 15 Apr 2026 15:01:36 +0200 Subject: [PATCH] Add middleware information in status --- api/v1alpha1/function_types.go | 14 +++++++ api/v1alpha1/zz_generated.deepcopy.go | 38 +++++++++++++++++++ config/crd/bases/functions.dev_functions.yaml | 24 ++++++++++++ internal/controller/function_controller.go | 37 +++++++++++++----- .../controller/function_controller_test.go | 27 +++++++++++++ 5 files changed, 130 insertions(+), 10 deletions(-) diff --git a/api/v1alpha1/function_types.go b/api/v1alpha1/function_types.go index 61df2f2..cb7af2b 100644 --- a/api/v1alpha1/function_types.go +++ b/api/v1alpha1/function_types.go @@ -78,6 +78,7 @@ type FunctionStatus struct { Git FunctionStatusGit `json:"git,omitempty"` Deployment FunctionStatusDeployment `json:"deployment,omitempty"` + Middleware FunctionStatusMiddleware `json:"middleware,omitempty"` } type FunctionStatusGit struct { @@ -93,6 +94,19 @@ type FunctionStatusDeployment struct { Runtime string `json:"runtime,omitempty"` } +type FunctionStatusMiddleware struct { + Current string `json:"current,omitempty"` + Available *string `json:"available,omitempty"` + AutoUpdate FunctionStatusMiddlewareAutoUpdate `json:"autoUpdate"` + PendingRebuild bool `json:"pendingRebuild"` // no omitempty to have it always shown + LastRebuild metav1.Time `json:"lastRebuild,omitempty"` +} + +type FunctionStatusMiddlewareAutoUpdate struct { + Enabled bool `json:"enabled"` // no omitempty to have it always shown + Source string `json:"source,omitempty"` +} + // +kubebuilder:object:root=true // FunctionList contains a list of Function. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index f782f4e..98aa827 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -159,6 +159,7 @@ func (in *FunctionStatus) DeepCopyInto(out *FunctionStatus) { } in.Git.DeepCopyInto(&out.Git) in.Deployment.DeepCopyInto(&out.Deployment) + in.Middleware.DeepCopyInto(&out.Middleware) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionStatus. @@ -202,3 +203,40 @@ func (in *FunctionStatusGit) DeepCopy() *FunctionStatusGit { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FunctionStatusMiddleware) DeepCopyInto(out *FunctionStatusMiddleware) { + *out = *in + if in.Available != nil { + in, out := &in.Available, &out.Available + *out = new(string) + **out = **in + } + out.AutoUpdate = in.AutoUpdate + in.LastRebuild.DeepCopyInto(&out.LastRebuild) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionStatusMiddleware. +func (in *FunctionStatusMiddleware) DeepCopy() *FunctionStatusMiddleware { + if in == nil { + return nil + } + out := new(FunctionStatusMiddleware) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FunctionStatusMiddlewareAutoUpdate) DeepCopyInto(out *FunctionStatusMiddlewareAutoUpdate) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionStatusMiddlewareAutoUpdate. +func (in *FunctionStatusMiddlewareAutoUpdate) DeepCopy() *FunctionStatusMiddlewareAutoUpdate { + if in == nil { + return nil + } + out := new(FunctionStatusMiddlewareAutoUpdate) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/functions.dev_functions.yaml b/config/crd/bases/functions.dev_functions.yaml index e868264..ec422b9 100644 --- a/config/crd/bases/functions.dev_functions.yaml +++ b/config/crd/bases/functions.dev_functions.yaml @@ -187,6 +187,30 @@ spec: resolvedBranch: type: string type: object + middleware: + properties: + autoUpdate: + properties: + enabled: + type: boolean + source: + type: string + required: + - enabled + type: object + available: + type: string + current: + type: string + lastRebuild: + format: date-time + type: string + pendingRebuild: + type: boolean + required: + - autoUpdate + - pendingRebuild + type: object name: type: string required: diff --git a/internal/controller/function_controller.go b/internal/controller/function_controller.go index cb0a779..dd68bdc 100644 --- a/internal/controller/function_controller.go +++ b/internal/controller/function_controller.go @@ -214,6 +214,22 @@ func (r *FunctionReconciler) ensureDeployment(ctx context.Context, function *v1a func (r *FunctionReconciler) handleMiddlewareUpdate(ctx context.Context, function *v1alpha1.Function, repo *git.Repository, metadata *funcfn.Function) error { logger := log.FromContext(ctx) + functionDescribe, err := r.FuncCliManager.Describe(ctx, metadata.Name, function.Namespace) + if err != nil { + return fmt.Errorf("failed to describe function to get image details: %w", err) + } + function.Status.Deployment.Image = functionDescribe.Image + + isMiddlewareUpdateEnabled, source, err := r.isMiddlewareUpdateEnabled(ctx, function) + if err != nil { + function.MarkMiddlewareNotUpToDate("MiddlewareCheckFailed", "Failed to check if middleware should be updated: %s", err) + return fmt.Errorf("failed to check if middleware should be updated: %w", err) + } + function.Status.Middleware.AutoUpdate.Enabled = isMiddlewareUpdateEnabled + function.Status.Middleware.AutoUpdate.Source = source + function.Status.Middleware.Current = functionDescribe.Middleware.Version + function.Status.Middleware.PendingRebuild = false + isOnLatestMiddleware, err := r.isMiddlewareLatest(ctx, metadata, function.Namespace) if err != nil { function.MarkMiddlewareNotUpToDate("MiddlewareCheckFailed", "Failed to check middleware version: %s", err.Error()) @@ -221,11 +237,11 @@ func (r *FunctionReconciler) handleMiddlewareUpdate(ctx context.Context, functio } if !isOnLatestMiddleware { - isMiddlewareUpdateEnabled, source, err := r.isMiddlewareUpdateEnabled(ctx, function) + latestMiddleware, err := r.FuncCliManager.GetLatestMiddlewareVersion(ctx, metadata.Runtime, metadata.Invoke) if err != nil { - function.MarkMiddlewareNotUpToDate("MiddlewareCheckFailed", "Failed to check if middleware should be updated: %s", err) - return fmt.Errorf("failed to check if middleware should be updated: %w", err) + return fmt.Errorf("failed to get latest available middleware version: %w", err) } + function.Status.Middleware.Available = ptr.To(latestMiddleware) if !isMiddlewareUpdateEnabled { logger.Info("Skipping middleware update, as middleware update is disabled") @@ -235,12 +251,7 @@ func (r *FunctionReconciler) handleMiddlewareUpdate(ctx context.Context, functio logger.Info("Function is not on latest middleware and middleware update is enabled. Will redeploy") function.MarkMiddlewareNotUpToDate("MiddlewareOutdated", "Middleware is outdated, redeploying") - // update function image in status before long redeploy operation - functionDescribe, err := r.FuncCliManager.Describe(ctx, metadata.Name, function.Namespace) - if err != nil { - return fmt.Errorf("failed to describe function to get image details: %w", err) - } - function.Status.Deployment.Image = functionDescribe.Image + function.Status.Middleware.PendingRebuild = true // Flush status before long deploy operation if err := FlushStatus(ctx, function); err != nil { @@ -252,20 +263,26 @@ func (r *FunctionReconciler) handleMiddlewareUpdate(ctx context.Context, functio return fmt.Errorf("failed to redeploy function: %w", err) } + function.Status.Middleware.PendingRebuild = false + function.Status.Middleware.LastRebuild = metav1.Now() + // After successful deployment, middleware is now up-to-date function.MarkMiddlewareUpToDate() + function.Status.Middleware.Available = nil // if function is on latest, we don't need to show this field } } else { logger.Info("Function is deployed with latest middleware. No need to redeploy") function.MarkMiddlewareUpToDate() + function.Status.Middleware.Available = nil // if function is on latest, we don't need to show this field } // Update deployment status - functionDescribe, err := r.FuncCliManager.Describe(ctx, metadata.Name, function.Namespace) + functionDescribe, err = r.FuncCliManager.Describe(ctx, metadata.Name, function.Namespace) if err != nil { return fmt.Errorf("failed to describe function to get image details: %w", err) } function.Status.Deployment.Image = functionDescribe.Image + function.Status.Middleware.Current = functionDescribe.Middleware.Version function.MarkDeployReady() return nil diff --git a/internal/controller/function_controller_test.go b/internal/controller/function_controller_test.go index 2c0115a..0e6c792 100644 --- a/internal/controller/function_controller_test.go +++ b/internal/controller/function_controller_test.go @@ -275,6 +275,33 @@ var _ = Describe("Function Controller", func() { "autoUpdateMiddleware": "true", }, }), + + Entry("Should populate the middleware information in the status", reconcileTestCase{ + spec: functionsdevv1alpha1.FunctionSpec{ + Repository: functionsdevv1alpha1.FunctionSpecRepository{ + URL: "https://github.com/foo/bar", + }, + AutoUpdateMiddleware: ptr.To(true), + }, + configureMocks: func(funcMock *funccli.MockManager, gitMock *git.MockManager) { + funcMock.EXPECT().Describe(mock.Anything, functionName, resourceNamespace).Return(functions.Instance{ + Middleware: functions.Middleware{ + Version: "v2.0.0", + }, + Image: "my-image:v1.2.3", + }, nil) + funcMock.EXPECT().GetLatestMiddlewareVersion(mock.Anything, mock.Anything, mock.Anything).Return("v2.0.0", nil) + funcMock.EXPECT().GetMiddlewareVersion(mock.Anything, functionName, resourceNamespace).Return("v2.0.0", nil) + + gitMock.EXPECT().CloneRepository(mock.Anything, "https://github.com/foo/bar", "", "main", mock.Anything).Return(createTmpGitRepo(functions.Function{Name: "func-go"}), nil) + }, + statusChecks: func(status *functionsdevv1alpha1.FunctionStatus) { + Expect(status.Middleware.Current).Should(Equal("v2.0.0")) + Expect(status.Middleware.AutoUpdate.Enabled).Should(BeTrue()) + Expect(status.Middleware.AutoUpdate.Source).Should(Equal("function")) + Expect(status.Middleware.Available).Should(BeNil()) + }, + }), ) }) })