From 3b3b8c53d4067ff3d2a02df9669a7c5b7c02e280 Mon Sep 17 00:00:00 2001 From: laminar Date: Thu, 5 Jan 2023 15:38:00 +0800 Subject: [PATCH 1/8] update resource condition to use metav1.Condition Signed-off-by: laminar --- api/compute/v1alpha1/common.go | 20 +- api/compute/v1alpha1/function_types.go | 8 +- api/compute/v1alpha1/function_webhook.go | 3 +- api/compute/v1alpha1/functionmesh_types.go | 10 +- api/compute/v1alpha1/sink_types.go | 8 +- api/compute/v1alpha1/sink_webhook.go | 3 +- api/compute/v1alpha1/source_types.go | 8 +- api/compute/v1alpha1/source_webhook.go | 3 +- api/compute/v1alpha1/status_handler.go | 184 +++++++++++ api/compute/v1alpha1/validate.go | 1 - api/compute/v1alpha1/zz_generated.deepcopy.go | 31 +- ...ompute.functionmesh.io-functionmeshes.yaml | 116 ++++++- ...crd-compute.functionmesh.io-functions.yaml | 29 +- .../crd-compute.functionmesh.io-sinks.yaml | 29 +- .../crd-compute.functionmesh.io-sources.yaml | 29 +- ...ompute.functionmesh.io_functionmeshes.yaml | 116 ++++++- .../compute.functionmesh.io_functions.yaml | 29 +- .../bases/compute.functionmesh.io_sinks.yaml | 29 +- .../compute.functionmesh.io_sources.yaml | 29 +- controllers/common.go | 135 -------- controllers/function.go | 259 ++++++++++----- controllers/function_controller.go | 32 +- controllers/function_controller_test.go | 15 +- controllers/function_mesh.go | 294 +++++++++--------- controllers/functionmesh_controller.go | 35 +-- controllers/functionmesh_controller_test.go | 9 +- controllers/sink.go | 259 ++++++++++----- controllers/sink_controller.go | 31 +- controllers/sink_controller_test.go | 10 +- controllers/source.go | 259 ++++++++++----- controllers/source_controller.go | 31 +- controllers/source_controller_test.go | 8 +- controllers/spec/common.go | 17 +- controllers/spec/common_test.go | 5 +- controllers/spec/function.go | 17 +- controllers/spec/function_mesh.go | 3 +- controllers/spec/function_test.go | 4 +- controllers/spec/hpa.go | 3 +- controllers/spec/sink.go | 17 +- controllers/spec/source.go | 17 +- controllers/spec/utils.go | 4 +- controllers/spec/utils_test.go | 4 +- controllers/spec/vpa.go | 5 +- controllers/spec/vpa_test.go | 5 +- controllers/suite_test.go | 5 +- controllers/test_utils_test.go | 3 +- 46 files changed, 1423 insertions(+), 748 deletions(-) create mode 100644 api/compute/v1alpha1/status_handler.go delete mode 100644 controllers/common.go diff --git a/api/compute/v1alpha1/common.go b/api/compute/v1alpha1/common.go index 0a17adc3a..be1bfb5e4 100644 --- a/api/compute/v1alpha1/common.go +++ b/api/compute/v1alpha1/common.go @@ -19,20 +19,17 @@ package v1alpha1 import ( "encoding/json" - "strconv" - - vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" - "fmt" + "strconv" "strings" - autov2beta2 "k8s.io/api/autoscaling/v2beta2" - pctlutil "github.com/streamnative/pulsarctl/pkg/pulsar/utils" + autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" + vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" ) type Messaging struct { @@ -349,8 +346,6 @@ type ResourceCondition struct { type ResourceConditionType string const ( - Orphaned ResourceConditionType = "Orphaned" - MeshReady ResourceConditionType = "MeshReady" FunctionReady ResourceConditionType = "FunctionReady" SourceReady ResourceConditionType = "SourceReady" @@ -552,15 +547,6 @@ func validateResourcePolicy(resourcePolicy *vpav1.PodResourcePolicy) field.Error return errs } -func CreateCondition(condType ResourceConditionType, status metav1.ConditionStatus, action ReconcileAction) ResourceCondition { - condition := ResourceCondition{ - Condition: condType, - Status: status, - Action: action, - } - return condition -} - const ( maxNameLength = 43 ) diff --git a/api/compute/v1alpha1/function_types.go b/api/compute/v1alpha1/function_types.go index 19fe70a8e..2c5e5d20c 100644 --- a/api/compute/v1alpha1/function_types.go +++ b/api/compute/v1alpha1/function_types.go @@ -96,10 +96,10 @@ type FunctionSpec struct { type FunctionStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - Conditions map[Component]ResourceCondition `json:"conditions"` - Replicas int32 `json:"replicas"` - Selector string `json:"selector"` - ObservedGeneration int64 `json:"observedGeneration,omitempty"` + Conditions map[Component]metav1.Condition `json:"conditions"` + Replicas int32 `json:"replicas"` + Selector string `json:"selector"` + ObservedGeneration int64 `json:"observedGeneration,omitempty"` } // +genclient diff --git a/api/compute/v1alpha1/function_webhook.go b/api/compute/v1alpha1/function_webhook.go index 691f6ae95..dbfa4be25 100644 --- a/api/compute/v1alpha1/function_webhook.go +++ b/api/compute/v1alpha1/function_webhook.go @@ -21,10 +21,9 @@ import ( "fmt" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" diff --git a/api/compute/v1alpha1/functionmesh_types.go b/api/compute/v1alpha1/functionmesh_types.go index 6e34b5922..03c4583c7 100644 --- a/api/compute/v1alpha1/functionmesh_types.go +++ b/api/compute/v1alpha1/functionmesh_types.go @@ -38,11 +38,11 @@ type FunctionMeshSpec struct { type FunctionMeshStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - SourceConditions map[string]ResourceCondition `json:"sourceConditions,omitempty"` - SinkConditions map[string]ResourceCondition `json:"sinkConditions,omitempty"` - FunctionConditions map[string]ResourceCondition `json:"functionConditions,omitempty"` - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - Condition *ResourceCondition `json:"condition,omitempty"` + SourceConditions map[string]metav1.Condition `json:"sourceConditions,omitempty"` + SinkConditions map[string]metav1.Condition `json:"sinkConditions,omitempty"` + FunctionConditions map[string]metav1.Condition `json:"functionConditions,omitempty"` + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + Condition metav1.Condition `json:"condition,omitempty"` } // +genclient diff --git a/api/compute/v1alpha1/sink_types.go b/api/compute/v1alpha1/sink_types.go index 9c73b54bd..5d03f2837 100644 --- a/api/compute/v1alpha1/sink_types.go +++ b/api/compute/v1alpha1/sink_types.go @@ -91,10 +91,10 @@ type SinkSpec struct { type SinkStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - Conditions map[Component]ResourceCondition `json:"conditions"` - Replicas int32 `json:"replicas"` - Selector string `json:"selector"` - ObservedGeneration int64 `json:"observedGeneration,omitempty"` + Conditions map[Component]metav1.Condition `json:"conditions"` + Replicas int32 `json:"replicas"` + Selector string `json:"selector"` + ObservedGeneration int64 `json:"observedGeneration,omitempty"` } // +genclient diff --git a/api/compute/v1alpha1/sink_webhook.go b/api/compute/v1alpha1/sink_webhook.go index aab3052be..90fc1e9e5 100644 --- a/api/compute/v1alpha1/sink_webhook.go +++ b/api/compute/v1alpha1/sink_webhook.go @@ -21,10 +21,9 @@ import ( "fmt" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" diff --git a/api/compute/v1alpha1/source_types.go b/api/compute/v1alpha1/source_types.go index ea5ce7aed..a9821790a 100644 --- a/api/compute/v1alpha1/source_types.go +++ b/api/compute/v1alpha1/source_types.go @@ -97,10 +97,10 @@ type BatchSourceConfig struct { type SourceStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - Conditions map[Component]ResourceCondition `json:"conditions"` - Replicas int32 `json:"replicas"` - Selector string `json:"selector"` - ObservedGeneration int64 `json:"observedGeneration,omitempty"` + Conditions map[Component]metav1.Condition `json:"conditions"` + Replicas int32 `json:"replicas"` + Selector string `json:"selector"` + ObservedGeneration int64 `json:"observedGeneration,omitempty"` } // +genclient diff --git a/api/compute/v1alpha1/source_webhook.go b/api/compute/v1alpha1/source_webhook.go index c129092de..5fb50a93b 100644 --- a/api/compute/v1alpha1/source_webhook.go +++ b/api/compute/v1alpha1/source_webhook.go @@ -21,10 +21,9 @@ import ( "fmt" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" diff --git a/api/compute/v1alpha1/status_handler.go b/api/compute/v1alpha1/status_handler.go new file mode 100644 index 000000000..4d9df18fc --- /dev/null +++ b/api/compute/v1alpha1/status_handler.go @@ -0,0 +1,184 @@ +package v1alpha1 + +import ( + "context" + "time" + + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type FunctionMeshConditionType string + +const ( + // Created indicates the resource has been created + Created FunctionMeshConditionType = "Created" + // Terminated indicates the resource has been terminated + Terminated FunctionMeshConditionType = "Terminated" + // Error indicates the resource had an error + Error FunctionMeshConditionType = "Error" + // Pending indicates the resource hasn't been created + Pending FunctionMeshConditionType = "Pending" + // Orphaned indicates that the resource is marked for deletion but hasn't + // been deleted yet + Orphaned FunctionMeshConditionType = "Orphaned" + // Unknown indicates the status is unavailable + Unknown FunctionMeshConditionType = "Unknown" + // Ready indicates the object is fully created + Ready FunctionMeshConditionType = "Ready" +) + +type FunctionMeshConditionReason string + +const ( + ErrorCreatingStatefulSet FunctionMeshConditionReason = "ErrorCreatingStatefulSet" + ErrorCreatingFunction FunctionMeshConditionReason = "ErrorCreatingFunction" + ErrorCreatingSource FunctionMeshConditionReason = "ErrorCreatingSource" + ErrorCreatingSink FunctionMeshConditionReason = "ErrorCreatingSink" + ErrorCreatingHPA FunctionMeshConditionReason = "ErrorCreatingHPA" + ErrorCreatingVPA FunctionMeshConditionReason = "ErrorCreatingVPA" + ErrorCreatingService FunctionMeshConditionReason = "ErrorCreatingService" + StatefulSetError FunctionMeshConditionReason = "StatefulSetError" + FunctionError FunctionMeshConditionReason = "FunctionError" + SourceError FunctionMeshConditionReason = "SourceError" + SinkError FunctionMeshConditionReason = "SinkError" + ServiceError FunctionMeshConditionReason = "ServiceError" + HPAError FunctionMeshConditionReason = "HPAError" + VPAError FunctionMeshConditionReason = "VPAError" + PendingCreation FunctionMeshConditionReason = "PendingCreation" + PendingTermination FunctionMeshConditionReason = "PendingTermination" + ServiceIsReady FunctionMeshConditionReason = "ServiceIsReady" + MeshIsReady FunctionMeshConditionReason = "MeshIsReady" + StatefulSetIsReady FunctionMeshConditionReason = "StatefulSetIsReady" + HPAIsReady FunctionMeshConditionReason = "HPAIsReady" + VPAIsReady FunctionMeshConditionReason = "VPAIsReady" + FunctionIsReady FunctionMeshConditionReason = "FunctionIsReady" + SourceIsReady FunctionMeshConditionReason = "SourceIsReady" + SinkIsReady FunctionMeshConditionReason = "SinkIsReady" +) + +// SaveStatus will trigger Function object update to save the current status +// conditions +func (r *Function) SaveStatus(ctx context.Context, logger logr.Logger, cl client.Client) { + logger.Info("Updating status on FunctionStatus", "resource version", r.ResourceVersion) + + err := cl.Status().Update(ctx, r) + if err != nil { + logger.Error(err, "failed to update status on FunctionStatus", "function", r) + } else { + logger.Info("Updated status on FunctionStatus", "resource version", r.ResourceVersion) + } +} + +// SetCondition adds a new condition to the Function +func (r *Function) SetCondition(component Component, condition *metav1.Condition) *Function { + if r.Status.Conditions == nil { + r.Status.Conditions = make(map[Component]metav1.Condition) + } + r.Status.Conditions[component] = *condition + return r +} + +// SaveStatus will trigger Sink object update to save the current status +// conditions +func (r *Sink) SaveStatus(ctx context.Context, logger logr.Logger, cl client.Client) { + logger.Info("Updating status on SinkStatus", "resource version", r.ResourceVersion) + + err := cl.Status().Update(ctx, r) + if err != nil { + logger.Error(err, "failed to update status on SinkStatus", "sink", r) + } else { + logger.Info("Updated status on SinkStatus", "resource version", r.ResourceVersion) + } +} + +// SetCondition adds a new condition to the Sink +func (r *Sink) SetCondition(component Component, condition *metav1.Condition) *Sink { + if r.Status.Conditions == nil { + r.Status.Conditions = make(map[Component]metav1.Condition) + } + r.Status.Conditions[component] = *condition + return r +} + +// SaveStatus will trigger Source object update to save the current status +// conditions +func (r *Source) SaveStatus(ctx context.Context, logger logr.Logger, cl client.Client) { + logger.Info("Updating status on SourceStatus", "resource version", r.ResourceVersion) + + err := cl.Status().Update(ctx, r) + if err != nil { + logger.Error(err, "failed to update status on SourceStatus", "source", r) + } else { + logger.Info("Updated status on SourceStatus", "resource version", r.ResourceVersion) + } +} + +// SetCondition adds a new condition to the Source +func (r *Source) SetCondition(component Component, condition *metav1.Condition) *Source { + if r.Status.Conditions == nil { + r.Status.Conditions = make(map[Component]metav1.Condition) + } + r.Status.Conditions[component] = *condition + return r +} + +// SaveStatus will trigger FunctionMesh object update to save the current status +// conditions +func (r *FunctionMesh) SaveStatus(ctx context.Context, logger logr.Logger, cl client.Client) { + logger.Info("Updating status on FunctionMeshStatus", "resource version", r.ResourceVersion) + + err := cl.Status().Update(ctx, r) + if err != nil { + logger.Error(err, "failed to update status on FunctionMeshStatus", "functionmesh", r) + } else { + logger.Info("Updated status on FunctionMeshStatus", "resource version", r.ResourceVersion) + } +} + +// SetCondition adds a new condition to the FunctionMesh +func (r *FunctionMesh) SetCondition(condition *metav1.Condition) *FunctionMesh { + r.Status.Condition = *condition + return r +} + +// SetFunctionCondition adds a new function condition to the FunctionMesh +func (r *FunctionMesh) SetFunctionCondition(name string, condition *metav1.Condition) *FunctionMesh { + if r.Status.FunctionConditions == nil { + r.Status.FunctionConditions = make(map[string]metav1.Condition) + } + r.Status.FunctionConditions[name] = *condition + return r +} + +// SetSinkCondition adds a new sink condition to the FunctionMesh +func (r *FunctionMesh) SetSinkCondition(name string, condition *metav1.Condition) *FunctionMesh { + if r.Status.SinkConditions == nil { + r.Status.SinkConditions = make(map[string]metav1.Condition) + } + r.Status.SinkConditions[name] = *condition + return r +} + +// SetSourceCondition adds a new source condition to the FunctionMesh +func (r *FunctionMesh) SetSourceCondition(name string, condition *metav1.Condition) *FunctionMesh { + if r.Status.SourceConditions == nil { + r.Status.SourceConditions = make(map[string]metav1.Condition) + } + r.Status.SourceConditions[name] = *condition + return r +} + +// CreateCondition initializes a new status condition +func CreateCondition(condType FunctionMeshConditionType, status metav1.ConditionStatus, + reason FunctionMeshConditionReason, message string) *metav1.Condition { + cond := &metav1.Condition{ + Type: string(condType), + Status: status, + Reason: string(reason), + Message: message, + LastTransitionTime: metav1.Time{Time: time.Now()}, + } + return cond +} diff --git a/api/compute/v1alpha1/validate.go b/api/compute/v1alpha1/validate.go index 13d0e1bbc..487dd7f5d 100644 --- a/api/compute/v1alpha1/validate.go +++ b/api/compute/v1alpha1/validate.go @@ -22,7 +22,6 @@ import ( "fmt" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/validation/field" ) diff --git a/api/compute/v1alpha1/zz_generated.deepcopy.go b/api/compute/v1alpha1/zz_generated.deepcopy.go index 861063034..49f6c2754 100644 --- a/api/compute/v1alpha1/zz_generated.deepcopy.go +++ b/api/compute/v1alpha1/zz_generated.deepcopy.go @@ -25,6 +25,7 @@ package v1alpha1 import ( "k8s.io/api/autoscaling/v2beta2" "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" autoscaling_k8s_iov1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" ) @@ -323,30 +324,26 @@ func (in *FunctionMeshStatus) DeepCopyInto(out *FunctionMeshStatus) { *out = *in if in.SourceConditions != nil { in, out := &in.SourceConditions, &out.SourceConditions - *out = make(map[string]ResourceCondition, len(*in)) + *out = make(map[string]metav1.Condition, len(*in)) for key, val := range *in { - (*out)[key] = val + (*out)[key] = *val.DeepCopy() } } if in.SinkConditions != nil { in, out := &in.SinkConditions, &out.SinkConditions - *out = make(map[string]ResourceCondition, len(*in)) + *out = make(map[string]metav1.Condition, len(*in)) for key, val := range *in { - (*out)[key] = val + (*out)[key] = *val.DeepCopy() } } if in.FunctionConditions != nil { in, out := &in.FunctionConditions, &out.FunctionConditions - *out = make(map[string]ResourceCondition, len(*in)) + *out = make(map[string]metav1.Condition, len(*in)) for key, val := range *in { - (*out)[key] = val + (*out)[key] = *val.DeepCopy() } } - if in.Condition != nil { - in, out := &in.Condition, &out.Condition - *out = new(ResourceCondition) - **out = **in - } + in.Condition.DeepCopyInto(&out.Condition) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionMeshStatus. @@ -443,9 +440,9 @@ func (in *FunctionStatus) DeepCopyInto(out *FunctionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(map[Component]ResourceCondition, len(*in)) + *out = make(map[Component]metav1.Condition, len(*in)) for key, val := range *in { - (*out)[key] = val + (*out)[key] = *val.DeepCopy() } } } @@ -1083,9 +1080,9 @@ func (in *SinkStatus) DeepCopyInto(out *SinkStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(map[Component]ResourceCondition, len(*in)) + *out = make(map[Component]metav1.Condition, len(*in)) for key, val := range *in { - (*out)[key] = val + (*out)[key] = *val.DeepCopy() } } } @@ -1232,9 +1229,9 @@ func (in *SourceStatus) DeepCopyInto(out *SourceStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(map[Component]ResourceCondition, len(*in)) + *out = make(map[Component]metav1.Condition, len(*in)) for key, val := range *in { - (*out)[key] = val + (*out)[key] = *val.DeepCopy() } } } diff --git a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-functionmeshes.yaml b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-functionmeshes.yaml index b14fe376c..b3146662c 100644 --- a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-functionmeshes.yaml +++ b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-functionmeshes.yaml @@ -9671,22 +9671,72 @@ spec: properties: condition: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object functionConditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object observedGeneration: @@ -9695,23 +9745,73 @@ spec: sinkConditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 type: string - condition: + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object sourceConditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object type: object diff --git a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-functions.yaml b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-functions.yaml index 2ab2006a9..7ed6112d8 100644 --- a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-functions.yaml +++ b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-functions.yaml @@ -3332,12 +3332,37 @@ spec: conditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object observedGeneration: diff --git a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-sinks.yaml b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-sinks.yaml index a74583f8f..00b9d9528 100644 --- a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-sinks.yaml +++ b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-sinks.yaml @@ -3240,12 +3240,37 @@ spec: conditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object observedGeneration: diff --git a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-sources.yaml b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-sources.yaml index 00a82ec51..d21fc7a19 100644 --- a/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-sources.yaml +++ b/charts/function-mesh-operator/charts/admission-webhook/templates/crd-compute.functionmesh.io-sources.yaml @@ -3215,12 +3215,37 @@ spec: conditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object observedGeneration: diff --git a/config/crd/bases/compute.functionmesh.io_functionmeshes.yaml b/config/crd/bases/compute.functionmesh.io_functionmeshes.yaml index c9ea92549..dc7d9815e 100644 --- a/config/crd/bases/compute.functionmesh.io_functionmeshes.yaml +++ b/config/crd/bases/compute.functionmesh.io_functionmeshes.yaml @@ -9672,22 +9672,72 @@ spec: properties: condition: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object functionConditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object observedGeneration: @@ -9696,23 +9746,73 @@ spec: sinkConditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 type: string - condition: + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object sourceConditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object type: object diff --git a/config/crd/bases/compute.functionmesh.io_functions.yaml b/config/crd/bases/compute.functionmesh.io_functions.yaml index a99332e64..63d023675 100644 --- a/config/crd/bases/compute.functionmesh.io_functions.yaml +++ b/config/crd/bases/compute.functionmesh.io_functions.yaml @@ -3311,12 +3311,37 @@ spec: conditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object observedGeneration: diff --git a/config/crd/bases/compute.functionmesh.io_sinks.yaml b/config/crd/bases/compute.functionmesh.io_sinks.yaml index c64d2aad1..946612472 100644 --- a/config/crd/bases/compute.functionmesh.io_sinks.yaml +++ b/config/crd/bases/compute.functionmesh.io_sinks.yaml @@ -3219,12 +3219,37 @@ spec: conditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object observedGeneration: diff --git a/config/crd/bases/compute.functionmesh.io_sources.yaml b/config/crd/bases/compute.functionmesh.io_sources.yaml index bbd844253..a56b088d7 100644 --- a/config/crd/bases/compute.functionmesh.io_sources.yaml +++ b/config/crd/bases/compute.functionmesh.io_sources.yaml @@ -3194,12 +3194,37 @@ spec: conditions: additionalProperties: properties: - action: + lastTransitionTime: + format: date-time type: string - condition: + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string + required: + - lastTransitionTime + - message + - reason + - status + - type type: object type: object observedGeneration: diff --git a/controllers/common.go b/controllers/common.go deleted file mode 100644 index 3c9380fb2..000000000 --- a/controllers/common.go +++ /dev/null @@ -1,135 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -// Package controllers define k8s operator controllers -package controllers - -import ( - "context" - "reflect" - - "github.com/go-logr/logr" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" - autoscaling "k8s.io/api/autoscaling/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func observeVPA(ctx context.Context, r client.Reader, name types.NamespacedName, vpaSpec *v1alpha1.VPASpec, conditions map[v1alpha1.Component]v1alpha1.ResourceCondition) error { - _, ok := conditions[v1alpha1.VPA] - condition := v1alpha1.ResourceCondition{Condition: v1alpha1.VPAReady} - if !ok { - if vpaSpec != nil { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - conditions[v1alpha1.VPA] = condition - return nil - } - // VPA is not enabled, skip further action - return nil - } - - vpa := &vpav1.VerticalPodAutoscaler{} - err := r.Get(ctx, name, vpa) - if err != nil { - if errors.IsNotFound(err) { - if vpaSpec == nil { // VPA is deleted, delete the status - delete(conditions, v1alpha1.VPA) - return nil - } - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - conditions[v1alpha1.VPA] = condition - return nil - } - return err - } - - // old VPA exists while new Spec removes it, delete the old one - if vpaSpec == nil { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Delete - conditions[v1alpha1.VPA] = condition - return nil - } - - // compare exists VPA with new Spec - if !reflect.DeepEqual(vpa.Spec.UpdatePolicy, vpaSpec.UpdatePolicy) || - !reflect.DeepEqual(vpa.Spec.ResourcePolicy, vpaSpec.ResourcePolicy) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Update - conditions[v1alpha1.VPA] = condition - return nil - } - - condition.Action = v1alpha1.NoAction - condition.Status = metav1.ConditionTrue - conditions[v1alpha1.VPA] = condition - return nil -} - -func applyVPA(ctx context.Context, r client.Client, logger logr.Logger, condition v1alpha1.ResourceCondition, meta *metav1.ObjectMeta, - targetRef *autoscaling.CrossVersionObjectReference, vpaSpec *v1alpha1.VPASpec, component string, namespace string, name string) error { - switch condition.Action { - case v1alpha1.Create: - vpa := spec.MakeVPA(meta, targetRef, vpaSpec) - if err := r.Create(ctx, vpa); err != nil { - logger.Error(err, "failed to create vertical pod autoscaler", "name", name, "component", component) - return err - } - - case v1alpha1.Update: - vpa := &vpav1.VerticalPodAutoscaler{} - err := r.Get(ctx, types.NamespacedName{Namespace: namespace, - Name: meta.Name}, vpa) - if err != nil { - logger.Error(err, "failed to update vertical pod autoscaler, cannot find vpa", "name", name, "component", component) - return err - } - newVpa := spec.MakeVPA(meta, targetRef, vpaSpec) - vpa.Spec = newVpa.Spec - if err := r.Update(ctx, vpa); err != nil { - logger.Error(err, "failed to update vertical pod autoscaler", "name", name, "component", component) - return err - } - - case v1alpha1.Delete: - vpa := &vpav1.VerticalPodAutoscaler{} - err := r.Get(ctx, types.NamespacedName{Namespace: namespace, - Name: meta.Name}, vpa) - if err != nil { - if errors.IsNotFound(err) { - return nil - } - logger.Error(err, "failed to delete vertical pod autoscaler, cannot find vpa", "name", name, "component", component) - return err - } - err = r.Delete(ctx, vpa) - if err != nil { - logger.Error(err, "failed to delete vertical pod autoscaler", "name", name, "component", component) - return err - } - - case v1alpha1.Wait, v1alpha1.NoAction: - // do nothing - } - return nil -} diff --git a/controllers/function.go b/controllers/function.go index 858d460a8..99776cf14 100644 --- a/controllers/function.go +++ b/controllers/function.go @@ -19,30 +19,23 @@ package controllers import ( "context" + "fmt" - autoscaling "k8s.io/api/autoscaling/v1" - - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" appsv1 "k8s.io/api/apps/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" ctrl "sigs.k8s.io/controller-runtime" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" ) func (r *FunctionReconciler) ObserveFunctionStatefulSet(ctx context.Context, function *v1alpha1.Function) error { - condition, ok := function.Status.Conditions[v1alpha1.StatefulSet] - if !ok { - function.Status.Conditions[v1alpha1.StatefulSet] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.StatefulSetReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - return nil - } + defer function.SaveStatus(ctx, r.Log, r.Client) statefulSet := &appsv1.StatefulSet{} err := r.Get(ctx, types.NamespacedName{ @@ -51,47 +44,58 @@ func (r *FunctionReconciler) ObserveFunctionStatefulSet(ctx context.Context, fun }, statefulSet) if err != nil { if errors.IsNotFound(err) { - r.Log.Info("function statefulSet is not ready yet...", + r.Log.Info("function statefulset is not ready yet...", "namespace", function.Namespace, "name", function.Name, - "statefulSet name", statefulSet.Name) - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - function.Status.Conditions[v1alpha1.StatefulSet] = condition + "statefulset name", statefulSet.Name) + function.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "function statefulset is not ready yet...")) return nil } + function.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.StatefulSetError, + fmt.Sprintf("failed to fetch function statefulset: %v", err))) return err } selector, err := metav1.LabelSelectorAsSelector(statefulSet.Spec.Selector) if err != nil { - r.Log.Error(err, "error retrieving statefulSet selector") + r.Log.Error(err, "error retrieving statefulset selector") + function.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.StatefulSetError, + fmt.Sprintf("error retrieving statefulset selector: %v", err))) return err } function.Status.Selector = selector.String() + function.Status.Replicas = *function.Spec.Replicas if r.checkIfStatefulSetNeedUpdate(statefulSet, function) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Update - function.Status.Conditions[v1alpha1.StatefulSet] = condition + function.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update function statefulset...")) return nil } if statefulSet.Status.ReadyReplicas == *function.Spec.Replicas { - condition.Action = v1alpha1.NoAction - } else { - condition.Action = v1alpha1.Wait + function.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.StatefulSetIsReady, + "")) + return nil } - condition.Status = metav1.ConditionTrue - function.Status.Replicas = *statefulSet.Spec.Replicas - function.Status.Conditions[v1alpha1.StatefulSet] = condition + function.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "function statefulset is pending creation...")) return nil } func (r *FunctionReconciler) ApplyFunctionStatefulSet(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { + defer function.SaveStatus(ctx, r.Log, r.Client) + condition := function.Status.Conditions[v1alpha1.StatefulSet] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredStatefulSet := spec.MakeFunctionStatefulSet(function) desiredStatefulSetSpec := desiredStatefulSet.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredStatefulSet, func() error { @@ -99,24 +103,22 @@ func (r *FunctionReconciler) ApplyFunctionStatefulSet(ctx context.Context, funct desiredStatefulSet.Spec = desiredStatefulSetSpec return nil }); err != nil { - r.Log.Error(err, "error create or update statefulSet workload for function", + r.Log.Error(err, "error create or update statefulset workload for function", "namespace", function.Namespace, "name", function.Name, - "statefulSet name", desiredStatefulSet.Name) + "statefulset name", desiredStatefulSet.Name) + function.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingStatefulSet, + fmt.Sprintf("error create or update statefulset workload for function: %v", err))) return err } + function.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating statefulset workload for function...")) return nil } func (r *FunctionReconciler) ObserveFunctionService(ctx context.Context, function *v1alpha1.Function) error { - condition, ok := function.Status.Conditions[v1alpha1.Service] - if !ok { - function.Status.Conditions[v1alpha1.Service] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.ServiceReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - return nil - } + defer function.SaveStatus(ctx, r.Log, r.Client) svc := &corev1.Service{} svcName := spec.MakeHeadlessServiceName(spec.MakeFunctionObjectMeta(function).Name) @@ -124,28 +126,34 @@ func (r *FunctionReconciler) ObserveFunctionService(ctx context.Context, functio Name: svcName}, svc) if err != nil { if errors.IsNotFound(err) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - function.Status.Conditions[v1alpha1.Service] = condition r.Log.Info("function service is not created...", "namespace", function.Namespace, "name", function.Name, "service name", svcName) + function.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "function service is not created...")) return nil } + function.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ServiceError, + fmt.Sprintf("failed to fetch function service: %v", err))) return err } - condition.Action = v1alpha1.NoAction - condition.Status = metav1.ConditionTrue - function.Status.Conditions[v1alpha1.Service] = condition + function.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.ServiceIsReady, + "")) return nil } func (r *FunctionReconciler) ApplyFunctionService(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { + defer function.SaveStatus(ctx, r.Log, r.Client) + condition := function.Status.Conditions[v1alpha1.Service] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredService := spec.MakeFunctionService(function) desiredServiceSpec := desiredService.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredService, func() error { @@ -156,24 +164,23 @@ func (r *FunctionReconciler) ApplyFunctionService(ctx context.Context, function r.Log.Error(err, "error create or update service for function", "namespace", function.Namespace, "name", function.Name, "service name", desiredService.Name) + function.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingService, + fmt.Sprintf("error create or update service for function: %v", err))) return err } + function.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating service for function...")) return nil } func (r *FunctionReconciler) ObserveFunctionHPA(ctx context.Context, function *v1alpha1.Function) error { + defer function.SaveStatus(ctx, r.Log, r.Client) + if function.Spec.MaxReplicas == nil { // HPA not enabled, skip further action - return nil - } - - condition, ok := function.Status.Conditions[v1alpha1.HPA] - if !ok { - function.Status.Conditions[v1alpha1.HPA] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.HPAReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } + delete(function.Status.Conditions, v1alpha1.HPA) return nil } @@ -182,39 +189,58 @@ func (r *FunctionReconciler) ObserveFunctionHPA(ctx context.Context, function *v Name: spec.MakeFunctionObjectMeta(function).Name}, hpa) if err != nil { if errors.IsNotFound(err) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - function.Status.Conditions[v1alpha1.HPA] = condition r.Log.Info("hpa is not created for function...", "namespace", function.Namespace, "name", function.Name, "hpa name", hpa.Name) + function.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "hpa is not created for function...")) return nil } + function.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.HPAError, + fmt.Sprintf("failed to fetch function hpa: %v", err))) return err } if r.checkIfHPANeedUpdate(hpa, function) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Update - function.Status.Conditions[v1alpha1.HPA] = condition + function.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update function hpa...")) return nil } - condition.Action = v1alpha1.NoAction - condition.Status = metav1.ConditionTrue - function.Status.Conditions[v1alpha1.HPA] = condition + function.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.HPAIsReady, + "")) return nil } func (r *FunctionReconciler) ApplyFunctionHPA(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { + defer function.SaveStatus(ctx, r.Log, r.Client) + if function.Spec.MaxReplicas == nil { - // HPA not enabled, skip further action + // HPA not enabled, clear the exists HPA + hpa := &autov2beta2.HorizontalPodAutoscaler{} + hpa.Namespace = function.Namespace + hpa.Name = spec.MakeFunctionObjectMeta(function).Name + if err := r.Delete(ctx, hpa); err != nil { + if errors.IsNotFound(err) { + return nil + } + function.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.HPAError, + fmt.Sprintf("failed to delete function hpa: %v", err))) + return err + } return nil } + condition := function.Status.Conditions[v1alpha1.HPA] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredHPA := spec.MakeFunctionHPA(function) desiredHPASpec := desiredHPA.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredHPA, func() error { @@ -225,39 +251,110 @@ func (r *FunctionReconciler) ApplyFunctionHPA(ctx context.Context, function *v1a r.Log.Error(err, "error create or update hpa for function", "namespace", function.Namespace, "name", function.Name, "hpa name", desiredHPA.Name) + function.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingHPA, + fmt.Sprintf("error create or update hpa for function: %v", err))) return err } + function.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating hpa for function...")) return nil } func (r *FunctionReconciler) ObserveFunctionVPA(ctx context.Context, function *v1alpha1.Function) error { - return observeVPA(ctx, r, types.NamespacedName{Namespace: function.Namespace, - Name: spec.MakeFunctionObjectMeta(function).Name}, function.Spec.Pod.VPA, function.Status.Conditions) -} + defer function.SaveStatus(ctx, r.Log, r.Client) -func (r *FunctionReconciler) ApplyFunctionVPA(ctx context.Context, function *v1alpha1.Function) error { + if function.Spec.Pod.VPA == nil { + delete(function.Status.Conditions, v1alpha1.VPA) + return nil + } - condition, ok := function.Status.Conditions[v1alpha1.VPA] + vpa := &vpav1.VerticalPodAutoscaler{} + err := r.Get(ctx, types.NamespacedName{ + Namespace: function.Namespace, + Name: spec.MakeFunctionObjectMeta(function).Name, + }, vpa) + if err != nil { + if errors.IsNotFound(err) { + r.Log.Info("vpa is not created for function...", + "namespace", function.Namespace, "name", function.Name, + "vpa name", vpa.Name) + function.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "vpa is not created for function...")) + return nil + } + function.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.VPAError, + fmt.Sprintf("failed to fetch function vpa: %v", err))) + return err + } - if !ok || condition.Status == metav1.ConditionTrue { + if r.checkIfVPANeedUpdate(vpa, function) { + function.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update function vpa...")) return nil } - objectMeta := spec.MakeFunctionObjectMeta(function) - targetRef := &autoscaling.CrossVersionObjectReference{ - Kind: function.Kind, - Name: function.Name, - APIVersion: function.APIVersion, + function.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.VPAIsReady, + "")) + return nil +} + +func (r *FunctionReconciler) ApplyFunctionVPA(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { + defer function.SaveStatus(ctx, r.Log, r.Client) + + if function.Spec.Pod.VPA == nil { + // VPA not enabled, clear the exists VPA + vpa := &vpav1.VerticalPodAutoscaler{} + vpa.Namespace = function.Namespace + vpa.Name = spec.MakeFunctionObjectMeta(function).Name + if err := r.Delete(ctx, vpa); err != nil { + if errors.IsNotFound(err) { + return nil + } + function.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.VPAError, + fmt.Sprintf("failed to delete function vpa: %v", err))) + return err + } + return nil } - err := applyVPA(ctx, r.Client, r.Log, condition, objectMeta, targetRef, function.Spec.Pod.VPA, "function", function.Namespace, function.Name) - if err != nil { - return err + condition := function.Status.Conditions[v1alpha1.VPA] + if condition.Status == metav1.ConditionTrue && !newGeneration { + return nil } + desiredVPA := spec.MakeFunctionVPA(function) + desiredVPASpec := desiredVPA.Spec + if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredVPA, func() error { + // function vpa mutate logic + desiredVPA.Spec = desiredVPASpec + return nil + }); err != nil { + r.Log.Error(err, "error create or update vpa for function", + "namespace", function.Namespace, "name", function.Name, + "vpa name", desiredVPA.Name) + function.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingVPA, + fmt.Sprintf("error create or update vpa for function: %v", err))) + return err + } + function.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating vpa for function...")) return nil } +func (r *FunctionReconciler) UpdateObservedGeneration(ctx context.Context, function *v1alpha1.Function) { + defer function.SaveStatus(ctx, r.Log, r.Client) + function.Status.ObservedGeneration = function.Generation +} + func (r *FunctionReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.StatefulSet, function *v1alpha1.Function) bool { return !spec.CheckIfStatefulSetSpecIsEqual(&statefulSet.Spec, &spec.MakeFunctionStatefulSet(function).Spec) } @@ -265,3 +362,7 @@ func (r *FunctionReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.St func (r *FunctionReconciler) checkIfHPANeedUpdate(hpa *autov2beta2.HorizontalPodAutoscaler, function *v1alpha1.Function) bool { return !spec.CheckIfHPASpecIsEqual(&hpa.Spec, &spec.MakeFunctionHPA(function).Spec) } + +func (r *FunctionReconciler) checkIfVPANeedUpdate(vpa *vpav1.VerticalPodAutoscaler, function *v1alpha1.Function) bool { + return !spec.CheckIfVPASpecIsEqual(&vpa.Spec, &spec.MakeFunctionVPA(function).Spec) +} diff --git a/controllers/function_controller.go b/controllers/function_controller.go index 305b61b8d..528a8b81a 100644 --- a/controllers/function_controller.go +++ b/controllers/function_controller.go @@ -21,9 +21,6 @@ import ( "context" "github.com/go-logr/logr" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" - "github.com/streamnative/function-mesh/utils" appsv1 "k8s.io/api/apps/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" @@ -35,6 +32,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" + "github.com/streamnative/function-mesh/utils" ) // FunctionReconciler reconciles a Function object @@ -73,11 +74,6 @@ func (r *FunctionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return reconcile.Result{}, nil } - // initialize component status map - if function.Status.Conditions == nil { - function.Status.Conditions = make(map[v1alpha1.Component]v1alpha1.ResourceCondition) - } - err = r.ObserveFunctionStatefulSet(ctx, function) if err != nil { return reconcile.Result{}, err @@ -96,11 +92,6 @@ func (r *FunctionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return reconcile.Result{}, err } } - err = r.Status().Update(ctx, function) - if err != nil { - r.Log.Error(err, "failed to update function status") - return ctrl.Result{}, err - } isNewGeneration := r.checkIfFunctionGenerationsIsIncreased(function) @@ -116,17 +107,14 @@ func (r *FunctionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c if err != nil { return reconcile.Result{}, err } - err = r.ApplyFunctionVPA(ctx, function) - if err != nil { - return reconcile.Result{}, err + if r.WatchFlags != nil && r.WatchFlags.WatchVPACRDs { + err = r.ApplyFunctionVPA(ctx, function, isNewGeneration) + if err != nil { + return reconcile.Result{}, err + } } - function.Status.ObservedGeneration = function.Generation - err = r.Status().Update(ctx, function) - if err != nil { - r.Log.Error(err, "failed to update function status") - return ctrl.Result{}, err - } + r.UpdateObservedGeneration(ctx, function) return ctrl.Result{}, nil } diff --git a/controllers/function_controller_test.go b/controllers/function_controller_test.go index 92730ed2a..59520399a 100644 --- a/controllers/function_controller_test.go +++ b/controllers/function_controller_test.go @@ -24,23 +24,20 @@ import ( "strings" "time" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + autov2beta2 "k8s.io/api/autoscaling/v2beta2" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/types" vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" - ctrl "sigs.k8s.io/controller-runtime" - - appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - autov2beta2 "k8s.io/api/autoscaling/v2beta2" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "github.com/streamnative/function-mesh/api/compute/v1alpha1" "github.com/streamnative/function-mesh/controllers/spec" - v1 "k8s.io/api/core/v1" ) var log = logf.Log.WithName("function-resource-test") diff --git a/controllers/function_mesh.go b/controllers/function_mesh.go index 3a08fdd08..451e79a04 100644 --- a/controllers/function_mesh.go +++ b/controllers/function_mesh.go @@ -19,18 +19,19 @@ package controllers import ( "context" + "fmt" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" ) func (r *FunctionMeshReconciler) ObserveFunctionMesh(ctx context.Context, req ctrl.Request, mesh *v1alpha1.FunctionMesh) error { - // TODO update deleted function status if err := r.observeFunctions(ctx, mesh); err != nil { return err } @@ -44,12 +45,14 @@ func (r *FunctionMeshReconciler) ObserveFunctionMesh(ctx context.Context, req ct } // Observation only - r.observeMeshes(mesh) + r.observeMeshes(ctx, mesh) return nil } func (r *FunctionMeshReconciler) observeFunctions(ctx context.Context, mesh *v1alpha1.FunctionMesh) error { + defer mesh.SaveStatus(ctx, r.Log, r.Client) + orphanedFunctions := map[string]bool{} if len(mesh.Status.FunctionConditions) > 0 { @@ -61,17 +64,6 @@ func (r *FunctionMeshReconciler) observeFunctions(ctx context.Context, mesh *v1a for _, functionSpec := range mesh.Spec.Functions { delete(orphanedFunctions, functionSpec.Name) - // present the original name to use in Status, but underlying use the complete-name - condition, ok := mesh.Status.FunctionConditions[functionSpec.Name] - if !ok { - mesh.Status.FunctionConditions[functionSpec.Name] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.FunctionReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - continue - } - function := &v1alpha1.Function{} err := r.Get(ctx, types.NamespacedName{ Namespace: mesh.Namespace, @@ -79,31 +71,34 @@ func (r *FunctionMeshReconciler) observeFunctions(ctx context.Context, mesh *v1a }, function) if err != nil { if errors.IsNotFound(err) { - r.Log.Info("function is not ready", "name", functionSpec.Name) - condition.SetCondition(v1alpha1.FunctionReady, v1alpha1.Create, metav1.ConditionFalse) - mesh.Status.FunctionConditions[functionSpec.Name] = condition + r.Log.Info("function is not ready", "namespace", functionSpec.Namespace, "name", functionSpec.Name) + mesh.SetFunctionCondition(functionSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "function is not ready yet...")) continue } + mesh.SetFunctionCondition(functionSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.FunctionError, + fmt.Sprintf("failed to fetch function: %v", err))) return err } - if function.Status.Conditions[v1alpha1.StatefulSet].Status == metav1.ConditionTrue && - function.Status.Conditions[v1alpha1.Service].Status == metav1.ConditionTrue { - condition.SetCondition(v1alpha1.FunctionReady, v1alpha1.NoAction, metav1.ConditionTrue) - mesh.Status.FunctionConditions[functionSpec.Name] = condition + if checkComponentsReady(function.Status.Conditions, functionSpec.MaxReplicas != nil, functionSpec.Pod.VPA != nil) { + mesh.SetFunctionCondition(functionSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.FunctionIsReady, "")) } else { // function created but subcomponents not ready, we need to wait - condition.SetCondition(v1alpha1.FunctionReady, v1alpha1.Wait, metav1.ConditionFalse) - mesh.Status.FunctionConditions[functionSpec.Name] = condition + mesh.SetFunctionCondition(functionSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Created, metav1.ConditionFalse, v1alpha1.PendingCreation, + "function is created, but needs to wait to be ready...")) } } for functionName, isOrphaned := range orphanedFunctions { if isOrphaned { - mesh.Status.FunctionConditions[functionName] = v1alpha1.CreateCondition( - v1alpha1.Orphaned, - metav1.ConditionTrue, - v1alpha1.Delete) + mesh.SetFunctionCondition(functionName, v1alpha1.CreateCondition( + v1alpha1.Orphaned, metav1.ConditionFalse, v1alpha1.PendingTermination, + fmt.Sprintf("function needs to be terminated: %s", functionName))) } } @@ -111,6 +106,8 @@ func (r *FunctionMeshReconciler) observeFunctions(ctx context.Context, mesh *v1a } func (r *FunctionMeshReconciler) observeSources(ctx context.Context, mesh *v1alpha1.FunctionMesh) error { + defer mesh.SaveStatus(ctx, r.Log, r.Client) + orphanedSources := map[string]bool{} if len(mesh.Status.SourceConditions) > 0 { @@ -122,17 +119,6 @@ func (r *FunctionMeshReconciler) observeSources(ctx context.Context, mesh *v1alp for _, sourceSpec := range mesh.Spec.Sources { delete(orphanedSources, sourceSpec.Name) - // present the original name to use in Status, but underlying use the complete-name - condition, ok := mesh.Status.SourceConditions[sourceSpec.Name] - if !ok { - mesh.Status.SourceConditions[sourceSpec.Name] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.SourceReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - continue - } - source := &v1alpha1.Source{} err := r.Get(ctx, types.NamespacedName{ Namespace: mesh.Namespace, @@ -140,37 +126,42 @@ func (r *FunctionMeshReconciler) observeSources(ctx context.Context, mesh *v1alp }, source) if err != nil { if errors.IsNotFound(err) { - r.Log.Info("source is not ready", "name", sourceSpec.Name) - condition.SetCondition(v1alpha1.SourceReady, v1alpha1.Create, metav1.ConditionFalse) - mesh.Status.SourceConditions[sourceSpec.Name] = condition + r.Log.Info("source is not ready", "namespace", sourceSpec.Namespace, "name", sourceSpec.Name) + mesh.SetSourceCondition(sourceSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "source is not ready yet...")) continue } + mesh.SetFunctionCondition(sourceSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.SourceError, + fmt.Sprintf("failed to fetch source: %v", err))) return err } - if source.Status.Conditions[v1alpha1.StatefulSet].Status == metav1.ConditionTrue && - source.Status.Conditions[v1alpha1.Service].Status == metav1.ConditionTrue { - condition.SetCondition(v1alpha1.SourceReady, v1alpha1.NoAction, metav1.ConditionTrue) - mesh.Status.SourceConditions[sourceSpec.Name] = condition + if checkComponentsReady(source.Status.Conditions, sourceSpec.MaxReplicas != nil, sourceSpec.Pod.VPA != nil) { + mesh.SetSourceCondition(sourceSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.SourceIsReady, "")) } else { - // function created but subcomponents not ready, we need to wait - condition.SetCondition(v1alpha1.SourceReady, v1alpha1.Wait, metav1.ConditionFalse) - mesh.Status.SourceConditions[sourceSpec.Name] = condition + // source created but subcomponents not ready, we need to wait + mesh.SetSourceCondition(sourceSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Created, metav1.ConditionFalse, v1alpha1.PendingCreation, + "source is created, but needs to wait to be ready...")) } } for sourceName, isOrphaned := range orphanedSources { if isOrphaned { - mesh.Status.SourceConditions[sourceName] = v1alpha1.CreateCondition( - v1alpha1.Orphaned, - metav1.ConditionTrue, - v1alpha1.Delete) + mesh.SetSourceCondition(sourceName, v1alpha1.CreateCondition( + v1alpha1.Orphaned, metav1.ConditionFalse, v1alpha1.PendingTermination, + fmt.Sprintf("source needs to be terminated: %s", sourceName))) } } return nil } func (r *FunctionMeshReconciler) observeSinks(ctx context.Context, mesh *v1alpha1.FunctionMesh) error { + defer mesh.SaveStatus(ctx, r.Log, r.Client) + orphanedSinks := map[string]bool{} if len(mesh.Status.SinkConditions) > 0 { @@ -182,17 +173,6 @@ func (r *FunctionMeshReconciler) observeSinks(ctx context.Context, mesh *v1alpha for _, sinkSpec := range mesh.Spec.Sinks { delete(orphanedSinks, sinkSpec.Name) - // present the original name to use in Status, but underlying use the complete-name - condition, ok := mesh.Status.SinkConditions[sinkSpec.Name] - if !ok { - mesh.Status.SinkConditions[sinkSpec.Name] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.SinkReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - continue - } - sink := &v1alpha1.Sink{} err := r.Get(ctx, types.NamespacedName{ Namespace: mesh.Namespace, @@ -200,138 +180,151 @@ func (r *FunctionMeshReconciler) observeSinks(ctx context.Context, mesh *v1alpha }, sink) if err != nil { if errors.IsNotFound(err) { - r.Log.Info("sink is not ready", "name", sinkSpec.Name) - condition.SetCondition(v1alpha1.SinkReady, v1alpha1.Create, metav1.ConditionFalse) - mesh.Status.SinkConditions[sinkSpec.Name] = condition + r.Log.Info("sink is not ready", "namespace", sinkSpec.Namespace, "name", sinkSpec.Name) + mesh.SetSinkCondition(sinkSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "sink is not ready yet...")) continue } + mesh.SetSinkCondition(sinkSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.SinkError, + fmt.Sprintf("failed to fetch sink: %v", err))) return err } - if sink.Status.Conditions[v1alpha1.StatefulSet].Status == metav1.ConditionTrue && - sink.Status.Conditions[v1alpha1.Service].Status == metav1.ConditionTrue { - condition.SetCondition(v1alpha1.SinkReady, v1alpha1.NoAction, metav1.ConditionTrue) - mesh.Status.SinkConditions[sinkSpec.Name] = condition + if checkComponentsReady(sink.Status.Conditions, sinkSpec.MaxReplicas != nil, sinkSpec.Pod.VPA != nil) { + mesh.SetSinkCondition(sinkSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.SinkIsReady, "")) } else { - // function created but subcomponents not ready, we need to wait - condition.SetCondition(v1alpha1.SinkReady, v1alpha1.Wait, metav1.ConditionFalse) - mesh.Status.SinkConditions[sinkSpec.Name] = condition + // sink created but subcomponents not ready, we need to wait + mesh.SetSinkCondition(sinkSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Created, metav1.ConditionFalse, v1alpha1.PendingCreation, + "sink is created, but needs to wait to be ready...")) } } for sinkName, isOrphaned := range orphanedSinks { if isOrphaned { - mesh.Status.SinkConditions[sinkName] = v1alpha1.CreateCondition( - v1alpha1.Orphaned, - metav1.ConditionTrue, - v1alpha1.Delete) + mesh.SetSinkCondition(sinkName, v1alpha1.CreateCondition( + v1alpha1.Orphaned, metav1.ConditionFalse, v1alpha1.PendingTermination, + fmt.Sprintf("sink needs to be terminated: %s", sinkName))) } } - return nil } -func (r *FunctionMeshReconciler) observeMeshes(mesh *v1alpha1.FunctionMesh) { +func (r *FunctionMeshReconciler) observeMeshes(ctx context.Context, mesh *v1alpha1.FunctionMesh) { + defer mesh.SaveStatus(ctx, r.Log, r.Client) + for _, cond := range mesh.Status.FunctionConditions { - if cond.Condition == v1alpha1.FunctionReady && cond.Status == metav1.ConditionTrue { + if cond.Type == string(v1alpha1.Ready) && cond.Status == metav1.ConditionTrue { continue } - mesh.Status.Condition.SetCondition(v1alpha1.MeshReady, v1alpha1.Wait, metav1.ConditionFalse) + mesh.SetCondition(v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionFalse, v1alpha1.PendingCreation, + "function is not ready yet...")) return } for _, cond := range mesh.Status.SinkConditions { - if cond.Condition == v1alpha1.SinkReady && cond.Status == metav1.ConditionTrue { + if cond.Type == string(v1alpha1.Ready) && cond.Status == metav1.ConditionTrue { continue } - mesh.Status.Condition.SetCondition(v1alpha1.MeshReady, v1alpha1.Wait, metav1.ConditionFalse) + mesh.SetCondition(v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionFalse, v1alpha1.PendingCreation, + "function is not ready yet...")) return } for _, cond := range mesh.Status.SourceConditions { - if cond.Condition == v1alpha1.SourceReady && cond.Status == metav1.ConditionTrue { + if cond.Type == string(v1alpha1.Ready) && cond.Status == metav1.ConditionTrue { continue } - mesh.Status.Condition.SetCondition(v1alpha1.MeshReady, v1alpha1.Wait, metav1.ConditionFalse) + mesh.SetCondition(v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionFalse, v1alpha1.PendingCreation, + "function is not ready yet...")) return } - mesh.Status.Condition.SetCondition(v1alpha1.MeshReady, v1alpha1.NoAction, metav1.ConditionTrue) + mesh.SetCondition(v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.MeshIsReady, "")) } func (r *FunctionMeshReconciler) UpdateFunctionMesh(ctx context.Context, req ctrl.Request, mesh *v1alpha1.FunctionMesh, newGeneration bool) error { - defer func() { - err := r.Status().Update(ctx, mesh) - if err != nil { - r.Log.Error(err, "failed to update mesh status") - } - }() + defer mesh.SaveStatus(ctx, r.Log, r.Client) for _, functionSpec := range mesh.Spec.Functions { condition := mesh.Status.FunctionConditions[functionSpec.Name] if !newGeneration && - functionSpec.MaxReplicas != nil && condition.Status == metav1.ConditionTrue && - condition.Action == v1alpha1.NoAction { + condition.Type == string(v1alpha1.Ready) { continue } function := spec.MakeFunctionComponent(makeComponentName(mesh.Name, functionSpec.Name), mesh, &functionSpec) if err := r.CreateOrUpdateFunction(ctx, function, function.Spec); err != nil { - r.Log.Error(err, "failed to handle function", "name", functionSpec.Name, "action", condition.Action) + r.Log.Error(err, "failed to handle function", "namespace", functionSpec.Namespace, "name", functionSpec.Name) + mesh.SetFunctionCondition(functionSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingFunction, + fmt.Sprintf("error create or update function for mesh: %v", err))) return err } + mesh.SetFunctionCondition(functionSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating function for mesh...")) } for _, sourceSpec := range mesh.Spec.Sources { condition := mesh.Status.SourceConditions[sourceSpec.Name] if !newGeneration && - sourceSpec.MaxReplicas != nil && condition.Status == metav1.ConditionTrue && - condition.Action == v1alpha1.NoAction { + condition.Type == string(v1alpha1.Ready) { continue } source := spec.MakeSourceComponent(makeComponentName(mesh.Name, sourceSpec.Name), mesh, &sourceSpec) if err := r.CreateOrUpdateSource(ctx, source, source.Spec); err != nil { - r.Log.Error(err, "failed to handle soure", "name", sourceSpec.Name, "action", condition.Action) + r.Log.Error(err, "failed to handle soure", "namespace", sourceSpec.Namespace, "name", sourceSpec.Name) + mesh.SetSourceCondition(sourceSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingSource, + fmt.Sprintf("error create or update source for mesh: %v", err))) return err } + mesh.SetSourceCondition(sourceSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating source for mesh...")) } for _, sinkSpec := range mesh.Spec.Sinks { condition := mesh.Status.SinkConditions[sinkSpec.Name] if !newGeneration && - sinkSpec.MaxReplicas != nil && condition.Status == metav1.ConditionTrue && - condition.Action == v1alpha1.NoAction { + condition.Type == string(v1alpha1.Ready) { continue } sink := spec.MakeSinkComponent(makeComponentName(mesh.Name, sinkSpec.Name), mesh, &sinkSpec) if err := r.CreateOrUpdateSink(ctx, sink, sink.Spec); err != nil { - r.Log.Error(err, "failed to handle sink", "name", sinkSpec.Name, "action", condition.Action) + r.Log.Error(err, "failed to handle sink", "namespace", sinkSpec.Namespace, "name", sinkSpec.Name) + mesh.SetSinkCondition(sinkSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingSink, + fmt.Sprintf("error create or update sink for mesh: %v", err))) return err } + mesh.SetSinkCondition(sinkSpec.Name, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating sink for mesh...")) } // handle logic for cleaning up orphaned subcomponents if len(mesh.Spec.Functions) != len(mesh.Status.FunctionConditions) { - for functionName, functionCondition := range mesh.Status.FunctionConditions { - if functionCondition.Condition == v1alpha1.Orphaned { - // clean up the orphaned functions + for functionName, condition := range mesh.Status.FunctionConditions { + if condition.Type == string(v1alpha1.Orphaned) { function := &v1alpha1.Function{} - if err := r.Get(ctx, types.NamespacedName{ - Namespace: mesh.Namespace, - Name: makeComponentName(mesh.Name, functionName), - }, function); err != nil { - if errors.IsNotFound(err) { - delete(mesh.Status.FunctionConditions, functionName) - continue - } - r.Log.Error(err, "failed to get orphaned function", "name", functionName) - return err - } + function.Namespace = mesh.Namespace + function.Name = makeComponentName(mesh.Name, functionName) if err := r.Delete(ctx, function); err != nil && !errors.IsNotFound(err) { - r.Log.Error(err, "failed to delete orphaned function", "name", functionName) + mesh.SetFunctionCondition(functionName, v1alpha1.CreateCondition( + v1alpha1.Orphaned, metav1.ConditionFalse, v1alpha1.FunctionError, + fmt.Sprintf("failed to delete function: %v", err))) return err } delete(mesh.Status.FunctionConditions, functionName) @@ -340,23 +333,15 @@ func (r *FunctionMeshReconciler) UpdateFunctionMesh(ctx context.Context, req ctr } if len(mesh.Spec.Sources) != len(mesh.Status.SourceConditions) { - for sourceName, sourceCondition := range mesh.Status.SourceConditions { - if sourceCondition.Condition == v1alpha1.Orphaned { - // clean up the orphaned sources + for sourceName, condition := range mesh.Status.SourceConditions { + if condition.Type == string(v1alpha1.Orphaned) { source := &v1alpha1.Source{} - if err := r.Get(ctx, types.NamespacedName{ - Namespace: mesh.Namespace, - Name: makeComponentName(mesh.Name, sourceName), - }, source); err != nil { - if errors.IsNotFound(err) { - delete(mesh.Status.SourceConditions, sourceName) - continue - } - r.Log.Error(err, "failed to get orphaned source", "name", sourceName) - return err - } + source.Namespace = mesh.Namespace + source.Name = makeComponentName(mesh.Name, sourceName) if err := r.Delete(ctx, source); err != nil && !errors.IsNotFound(err) { - r.Log.Error(err, "failed to delete orphaned source", "name", sourceName) + mesh.SetSourceCondition(sourceName, v1alpha1.CreateCondition( + v1alpha1.Orphaned, metav1.ConditionFalse, v1alpha1.SourceError, + fmt.Sprintf("failed to delete source: %v", err))) return err } delete(mesh.Status.SourceConditions, sourceName) @@ -365,23 +350,15 @@ func (r *FunctionMeshReconciler) UpdateFunctionMesh(ctx context.Context, req ctr } if len(mesh.Spec.Sinks) != len(mesh.Status.SinkConditions) { - for sinkName, sinkCondition := range mesh.Status.SinkConditions { - if sinkCondition.Condition == v1alpha1.Orphaned { - // clean up the orphaned sinks + for sinkName, condition := range mesh.Status.SinkConditions { + if condition.Type == string(v1alpha1.Orphaned) { sink := &v1alpha1.Sink{} - if err := r.Get(ctx, types.NamespacedName{ - Namespace: mesh.Namespace, - Name: makeComponentName(mesh.Name, sinkName), - }, sink); err != nil { - if errors.IsNotFound(err) { - delete(mesh.Status.SinkConditions, sinkName) - continue - } - r.Log.Error(err, "failed to get orphaned sink", "name", sinkName) - return err - } + sink.Namespace = mesh.Namespace + sink.Name = makeComponentName(mesh.Name, sinkName) if err := r.Delete(ctx, sink); err != nil && !errors.IsNotFound(err) { - r.Log.Error(err, "failed to delete orphaned sink", "name", sinkName) + mesh.SetSinkCondition(sinkName, v1alpha1.CreateCondition( + v1alpha1.Orphaned, metav1.ConditionFalse, v1alpha1.SinkError, + fmt.Sprintf("failed to delete sink: %v", err))) return err } delete(mesh.Status.SinkConditions, sinkName) @@ -431,3 +408,26 @@ func (r *FunctionMeshReconciler) CreateOrUpdateSource(ctx context.Context, sourc func makeComponentName(prefix, name string) string { return prefix + "-" + name } + +func (r *FunctionMeshReconciler) UpdateObservedGeneration(ctx context.Context, mesh *v1alpha1.FunctionMesh) { + defer mesh.SaveStatus(ctx, r.Log, r.Client) + mesh.Status.ObservedGeneration = mesh.Generation +} + +func checkComponentsReady(conditions map[v1alpha1.Component]metav1.Condition, isHPAEnabled bool, isVPAEnabled bool) bool { + ready := conditions[v1alpha1.StatefulSet].Type == string(v1alpha1.Ready) && + conditions[v1alpha1.StatefulSet].Status == metav1.ConditionTrue && + conditions[v1alpha1.Service].Type == string(v1alpha1.Ready) && + conditions[v1alpha1.Service].Status == metav1.ConditionTrue + if isHPAEnabled { + hpaReady := conditions[v1alpha1.HPA].Type == string(v1alpha1.Ready) && + conditions[v1alpha1.HPA].Status == metav1.ConditionTrue + ready = ready && hpaReady + } + if isVPAEnabled { + vpaReady := conditions[v1alpha1.VPA].Type == string(v1alpha1.Ready) && + conditions[v1alpha1.VPA].Status == metav1.ConditionTrue + ready = ready && vpaReady + } + return ready +} diff --git a/controllers/functionmesh_controller.go b/controllers/functionmesh_controller.go index e4b72d69f..0aebfcfa5 100644 --- a/controllers/functionmesh_controller.go +++ b/controllers/functionmesh_controller.go @@ -20,15 +20,15 @@ package controllers import ( "context" - "github.com/streamnative/function-mesh/controllers/spec" - "github.com/go-logr/logr" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" ) // FunctionMeshReconciler reconciles a FunctionMesh object @@ -62,34 +62,12 @@ func (r *FunctionMeshReconciler) Reconcile(ctx context.Context, req ctrl.Request return reconcile.Result{}, nil } - // initialize component status map - if mesh.Status.FunctionConditions == nil { - mesh.Status.FunctionConditions = make(map[string]v1alpha1.ResourceCondition) - } - if mesh.Status.SourceConditions == nil { - mesh.Status.SourceConditions = make(map[string]v1alpha1.ResourceCondition) - } - if mesh.Status.SinkConditions == nil { - mesh.Status.SinkConditions = make(map[string]v1alpha1.ResourceCondition) - } - if mesh.Status.Condition == nil { - mesh.Status.Condition = &v1alpha1.ResourceCondition{} - } - - // TODO validate function spec correctness such as no duplicated func name etc - // make observations err = r.ObserveFunctionMesh(ctx, req, mesh) if err != nil { return reconcile.Result{}, err } - err = r.Status().Update(ctx, mesh) - if err != nil { - r.Log.Error(err, "failed to update mesh status") - return ctrl.Result{}, err - } - isNewGeneration := r.checkIfFunctionMeshGenerationsIsIncreased(mesh) // apply changes @@ -98,12 +76,7 @@ func (r *FunctionMeshReconciler) Reconcile(ctx context.Context, req ctrl.Request return reconcile.Result{}, err } - mesh.Status.ObservedGeneration = mesh.Generation - err = r.Status().Update(ctx, mesh) - if err != nil { - r.Log.Error(err, "failed to update functionmesh status") - return ctrl.Result{}, err - } + r.UpdateObservedGeneration(ctx, mesh) return ctrl.Result{}, nil } diff --git a/controllers/functionmesh_controller_test.go b/controllers/functionmesh_controller_test.go index 61fd336bd..a84256215 100644 --- a/controllers/functionmesh_controller_test.go +++ b/controllers/functionmesh_controller_test.go @@ -22,7 +22,8 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/streamnative/function-mesh/controllers/spec" ) @@ -31,13 +32,13 @@ var _ = Describe("FunctionMesh Controller", func() { pulsarConfig := makeSamplePulsarConfig() mesh := makeFunctionMeshSample() if mesh.Status.FunctionConditions == nil { - mesh.Status.FunctionConditions = make(map[string]v1alpha1.ResourceCondition) + mesh.Status.FunctionConditions = make(map[string]metav1.Condition) } if mesh.Status.SourceConditions == nil { - mesh.Status.SourceConditions = make(map[string]v1alpha1.ResourceCondition) + mesh.Status.SourceConditions = make(map[string]metav1.Condition) } if mesh.Status.SinkConditions == nil { - mesh.Status.SinkConditions = make(map[string]v1alpha1.ResourceCondition) + mesh.Status.SinkConditions = make(map[string]metav1.Condition) } It("Should create pulsar configmap successfully", func() { diff --git a/controllers/sink.go b/controllers/sink.go index 06adc4245..71c2df75e 100644 --- a/controllers/sink.go +++ b/controllers/sink.go @@ -19,30 +19,23 @@ package controllers import ( "context" + "fmt" - autoscaling "k8s.io/api/autoscaling/v1" - - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" appsv1 "k8s.io/api/apps/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" ctrl "sigs.k8s.io/controller-runtime" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" ) func (r *SinkReconciler) ObserveSinkStatefulSet(ctx context.Context, sink *v1alpha1.Sink) error { - condition, ok := sink.Status.Conditions[v1alpha1.StatefulSet] - if !ok { - sink.Status.Conditions[v1alpha1.StatefulSet] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.StatefulSetReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - return nil - } + defer sink.SaveStatus(ctx, r.Log, r.Client) statefulSet := &appsv1.StatefulSet{} err := r.Get(ctx, types.NamespacedName{ @@ -51,47 +44,58 @@ func (r *SinkReconciler) ObserveSinkStatefulSet(ctx context.Context, sink *v1alp }, statefulSet) if err != nil { if errors.IsNotFound(err) { - r.Log.Info("sink statefulSet is not found...", + r.Log.Info("sink statefulset is not ready yet...", "namespace", sink.Namespace, "name", sink.Name, - "statefulSet name", statefulSet.Name) - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - sink.Status.Conditions[v1alpha1.StatefulSet] = condition + "statefulset name", statefulSet.Name) + sink.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "sink statefulset is not ready yet...")) return nil } + sink.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.StatefulSetError, + fmt.Sprintf("failed to fetch sink statefulset: %v", err))) return err } selector, err := metav1.LabelSelectorAsSelector(statefulSet.Spec.Selector) if err != nil { - r.Log.Error(err, "error retrieving statefulSet selector") + r.Log.Error(err, "error retrieving statefulset selector") + sink.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.StatefulSetError, + fmt.Sprintf("error retrieving statefulset selector: %v", err))) return err } sink.Status.Selector = selector.String() + sink.Status.Replicas = *sink.Spec.Replicas if r.checkIfStatefulSetNeedUpdate(statefulSet, sink) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Update - sink.Status.Conditions[v1alpha1.StatefulSet] = condition + sink.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update function statefulset...")) return nil } if statefulSet.Status.ReadyReplicas == *sink.Spec.Replicas { - condition.Action = v1alpha1.NoAction - } else { - condition.Action = v1alpha1.Wait + sink.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.StatefulSetIsReady, + "")) + return nil } - condition.Status = metav1.ConditionTrue - sink.Status.Replicas = *statefulSet.Spec.Replicas - sink.Status.Conditions[v1alpha1.StatefulSet] = condition + sink.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "sink statefulset is pending creation...")) return nil } func (r *SinkReconciler) ApplySinkStatefulSet(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { + defer sink.SaveStatus(ctx, r.Log, r.Client) + condition := sink.Status.Conditions[v1alpha1.StatefulSet] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredStatefulSet := spec.MakeSinkStatefulSet(sink) desiredStatefulSetSpec := desiredStatefulSet.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredStatefulSet, func() error { @@ -99,24 +103,22 @@ func (r *SinkReconciler) ApplySinkStatefulSet(ctx context.Context, sink *v1alpha desiredStatefulSet.Spec = desiredStatefulSetSpec return nil }); err != nil { - r.Log.Error(err, "error create or update statefulSet workload for sink", + r.Log.Error(err, "error create or update statefulset workload for sink", "namespace", sink.Namespace, "name", sink.Name, - "statefulSet name", desiredStatefulSet.Name) + "statefulset name", desiredStatefulSet.Name) + sink.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingStatefulSet, + fmt.Sprintf("error create or update statefulset workload for sink: %v", err))) return err } + sink.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating statefulset workload for sink...")) return nil } func (r *SinkReconciler) ObserveSinkService(ctx context.Context, sink *v1alpha1.Sink) error { - condition, ok := sink.Status.Conditions[v1alpha1.Service] - if !ok { - sink.Status.Conditions[v1alpha1.Service] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.ServiceReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - return nil - } + defer sink.SaveStatus(ctx, r.Log, r.Client) svc := &corev1.Service{} svcName := spec.MakeHeadlessServiceName(spec.MakeSinkObjectMeta(sink).Name) @@ -124,28 +126,34 @@ func (r *SinkReconciler) ObserveSinkService(ctx context.Context, sink *v1alpha1. Name: svcName}, svc) if err != nil { if errors.IsNotFound(err) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - sink.Status.Conditions[v1alpha1.Service] = condition r.Log.Info("sink service is not created...", "namespace", sink.Namespace, "name", sink.Name, "service name", svcName) + sink.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "sink service is not created...")) return nil } + sink.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ServiceError, + fmt.Sprintf("failed to fetch sink service: %v", err))) return err } - condition.Action = v1alpha1.NoAction - condition.Status = metav1.ConditionTrue - sink.Status.Conditions[v1alpha1.Service] = condition + sink.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.ServiceIsReady, + "")) return nil } func (r *SinkReconciler) ApplySinkService(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { + defer sink.SaveStatus(ctx, r.Log, r.Client) + condition := sink.Status.Conditions[v1alpha1.Service] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredService := spec.MakeSinkService(sink) desiredServiceSpec := desiredService.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredService, func() error { @@ -156,24 +164,23 @@ func (r *SinkReconciler) ApplySinkService(ctx context.Context, sink *v1alpha1.Si r.Log.Error(err, "error create or update service for sink", "namespace", sink.Namespace, "name", sink.Name, "service name", desiredService.Name) + sink.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingService, + fmt.Sprintf("error create or update service for sink: %v", err))) return err } + sink.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating service for sink...")) return nil } func (r *SinkReconciler) ObserveSinkHPA(ctx context.Context, sink *v1alpha1.Sink) error { + defer sink.SaveStatus(ctx, r.Log, r.Client) + if sink.Spec.MaxReplicas == nil { // HPA not enabled, skip further action - return nil - } - - condition, ok := sink.Status.Conditions[v1alpha1.HPA] - if !ok { - sink.Status.Conditions[v1alpha1.HPA] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.HPAReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } + delete(sink.Status.Conditions, v1alpha1.HPA) return nil } @@ -182,39 +189,58 @@ func (r *SinkReconciler) ObserveSinkHPA(ctx context.Context, sink *v1alpha1.Sink Name: spec.MakeSinkObjectMeta(sink).Name}, hpa) if err != nil { if errors.IsNotFound(err) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - sink.Status.Conditions[v1alpha1.HPA] = condition r.Log.Info("sink hpa is not created for sink...", "namespace", sink.Namespace, "name", sink.Name, "hpa name", hpa.Name) + sink.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "hpa is not created for sink...")) return nil } + sink.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.HPAError, + fmt.Sprintf("failed to fetch sink hpa: %v", err))) return err } if r.checkIfHPANeedUpdate(hpa, sink) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Update - sink.Status.Conditions[v1alpha1.HPA] = condition + sink.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update sink hpa...")) return nil } - condition.Action = v1alpha1.NoAction - condition.Status = metav1.ConditionTrue - sink.Status.Conditions[v1alpha1.HPA] = condition + sink.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.HPAIsReady, + "")) return nil } func (r *SinkReconciler) ApplySinkHPA(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { + defer sink.SaveStatus(ctx, r.Log, r.Client) + if sink.Spec.MaxReplicas == nil { - // HPA not enabled, skip further action + // HPA not enabled, clear the exists HPA + hpa := &autov2beta2.HorizontalPodAutoscaler{} + hpa.Namespace = sink.Namespace + hpa.Name = spec.MakeSinkObjectMeta(sink).Name + if err := r.Delete(ctx, hpa); err != nil { + if errors.IsNotFound(err) { + return nil + } + sink.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.HPAError, + fmt.Sprintf("failed to delete sink hpa: %v", err))) + return err + } return nil } + condition := sink.Status.Conditions[v1alpha1.HPA] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredHPA := spec.MakeSinkHPA(sink) desiredHPASpec := desiredHPA.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredHPA, func() error { @@ -225,39 +251,110 @@ func (r *SinkReconciler) ApplySinkHPA(ctx context.Context, sink *v1alpha1.Sink, r.Log.Error(err, "error create or update hpa for sink", "namespace", sink.Namespace, "name", sink.Name, "hpa name", desiredHPA.Name) + sink.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingHPA, + fmt.Sprintf("error create or update hpa for sink: %v", err))) return err } + sink.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating hpa for sink...")) return nil } func (r *SinkReconciler) ObserveSinkVPA(ctx context.Context, sink *v1alpha1.Sink) error { - return observeVPA(ctx, r, types.NamespacedName{Namespace: sink.Namespace, - Name: spec.MakeSinkObjectMeta(sink).Name}, sink.Spec.Pod.VPA, sink.Status.Conditions) -} + defer sink.SaveStatus(ctx, r.Log, r.Client) -func (r *SinkReconciler) ApplySinkVPA(ctx context.Context, sink *v1alpha1.Sink) error { + if sink.Spec.Pod.VPA == nil { + delete(sink.Status.Conditions, v1alpha1.VPA) + return nil + } - condition, ok := sink.Status.Conditions[v1alpha1.VPA] + vpa := &vpav1.VerticalPodAutoscaler{} + err := r.Get(ctx, types.NamespacedName{ + Namespace: sink.Namespace, + Name: spec.MakeSinkObjectMeta(sink).Name, + }, vpa) + if err != nil { + if errors.IsNotFound(err) { + r.Log.Info("vpa is not created for sink...", + "namespace", sink.Namespace, "name", sink.Name, + "vpa name", vpa.Name) + sink.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "vpa is not created for sink...")) + return nil + } + sink.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.VPAError, + fmt.Sprintf("failed to fetch sink vpa: %v", err))) + return err + } - if !ok || condition.Status == metav1.ConditionTrue { + if r.checkIfVPANeedUpdate(vpa, sink) { + sink.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update sink vpa...")) return nil } - objectMeta := spec.MakeSinkObjectMeta(sink) - targetRef := &autoscaling.CrossVersionObjectReference{ - Kind: sink.Kind, - Name: sink.Name, - APIVersion: sink.APIVersion, + sink.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.VPAIsReady, + "")) + return nil +} + +func (r *SinkReconciler) ApplySinkVPA(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { + defer sink.SaveStatus(ctx, r.Log, r.Client) + + if sink.Spec.Pod.VPA == nil { + // VPA not enabled, clear the exists VPA + vpa := &vpav1.VerticalPodAutoscaler{} + vpa.Namespace = sink.Namespace + vpa.Name = spec.MakeSinkObjectMeta(sink).Name + if err := r.Delete(ctx, vpa); err != nil { + if errors.IsNotFound(err) { + return nil + } + sink.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.VPAError, + fmt.Sprintf("failed to delete sink vpa: %v", err))) + return err + } + return nil } - err := applyVPA(ctx, r.Client, r.Log, condition, objectMeta, targetRef, sink.Spec.Pod.VPA, "sink", sink.Namespace, sink.Name) - if err != nil { - return err + condition := sink.Status.Conditions[v1alpha1.VPA] + if condition.Status == metav1.ConditionTrue && !newGeneration { + return nil } + desiredVPA := spec.MakeSinkVPA(sink) + desiredVPASpec := desiredVPA.Spec + if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredVPA, func() error { + // sink vpa mutate logic + desiredVPA.Spec = desiredVPASpec + return nil + }); err != nil { + r.Log.Error(err, "error create or update vpa for sink", + "namespace", sink.Namespace, "name", sink.Name, + "vpa name", desiredVPA.Name) + sink.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingVPA, + fmt.Sprintf("error create or update vpa for sink: %v", err))) + return err + } + sink.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating vpa for sink...")) return nil } +func (r *SinkReconciler) UpdateObservedGeneration(ctx context.Context, sink *v1alpha1.Sink) { + defer sink.SaveStatus(ctx, r.Log, r.Client) + sink.Status.ObservedGeneration = sink.Generation +} + func (r *SinkReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.StatefulSet, sink *v1alpha1.Sink) bool { return !spec.CheckIfStatefulSetSpecIsEqual(&statefulSet.Spec, &spec.MakeSinkStatefulSet(sink).Spec) } @@ -265,3 +362,7 @@ func (r *SinkReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.Statef func (r *SinkReconciler) checkIfHPANeedUpdate(hpa *autov2beta2.HorizontalPodAutoscaler, sink *v1alpha1.Sink) bool { return !spec.CheckIfHPASpecIsEqual(&hpa.Spec, &spec.MakeSinkHPA(sink).Spec) } + +func (r *SinkReconciler) checkIfVPANeedUpdate(vpa *vpav1.VerticalPodAutoscaler, sink *v1alpha1.Sink) bool { + return !spec.CheckIfVPASpecIsEqual(&vpa.Spec, &spec.MakeSinkVPA(sink).Spec) +} diff --git a/controllers/sink_controller.go b/controllers/sink_controller.go index a79988eb1..2b9b2f5ad 100644 --- a/controllers/sink_controller.go +++ b/controllers/sink_controller.go @@ -21,9 +21,6 @@ import ( "context" "github.com/go-logr/logr" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" - "github.com/streamnative/function-mesh/utils" appsv1 "k8s.io/api/apps/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" @@ -35,6 +32,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" + "github.com/streamnative/function-mesh/utils" ) // SinkReconciler reconciles a Topic object @@ -72,10 +73,6 @@ func (r *SinkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. return reconcile.Result{}, nil } - if sink.Status.Conditions == nil { - sink.Status.Conditions = make(map[v1alpha1.Component]v1alpha1.ResourceCondition) - } - err = r.ObserveSinkStatefulSet(ctx, sink) if err != nil { return reconcile.Result{}, err @@ -94,11 +91,6 @@ func (r *SinkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. return reconcile.Result{}, err } } - err = r.Status().Update(ctx, sink) - if err != nil { - r.Log.Error(err, "failed to update sink status") - return ctrl.Result{}, err - } isNewGeneration := r.checkIfSinkGenerationsIsIncreased(sink) @@ -114,17 +106,14 @@ func (r *SinkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. if err != nil { return reconcile.Result{}, err } - err = r.ApplySinkVPA(ctx, sink) - if err != nil { - return reconcile.Result{}, err + if r.WatchFlags != nil && r.WatchFlags.WatchVPACRDs { + err = r.ApplySinkVPA(ctx, sink, isNewGeneration) + if err != nil { + return reconcile.Result{}, err + } } - sink.Status.ObservedGeneration = sink.Generation - err = r.Status().Update(ctx, sink) - if err != nil { - r.Log.Error(err, "failed to update sink status") - return ctrl.Result{}, err - } + r.UpdateObservedGeneration(ctx, sink) return ctrl.Result{}, nil } diff --git a/controllers/sink_controller_test.go b/controllers/sink_controller_test.go index 38373fd2b..5a9750f99 100644 --- a/controllers/sink_controller_test.go +++ b/controllers/sink_controller_test.go @@ -23,15 +23,15 @@ import ( "strings" "time" - "github.com/streamnative/function-mesh/controllers/spec" - + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" appv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" ) var _ = Describe("Sink Controller", func() { @@ -39,7 +39,7 @@ var _ = Describe("Sink Controller", func() { pulsarConfig := makeSamplePulsarConfig() sink := makeSinkSample() if sink.Status.Conditions == nil { - sink.Status.Conditions = make(map[v1alpha1.Component]v1alpha1.ResourceCondition) + sink.Status.Conditions = make(map[v1alpha1.Component]metav1.Condition) } statefulSet := spec.MakeSinkStatefulSet(sink) diff --git a/controllers/source.go b/controllers/source.go index 04854e1d3..1f7097aba 100644 --- a/controllers/source.go +++ b/controllers/source.go @@ -19,30 +19,23 @@ package controllers import ( "context" + "fmt" - autoscaling "k8s.io/api/autoscaling/v1" - - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" appsv1 "k8s.io/api/apps/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" ctrl "sigs.k8s.io/controller-runtime" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" ) func (r *SourceReconciler) ObserveSourceStatefulSet(ctx context.Context, source *v1alpha1.Source) error { - condition, ok := source.Status.Conditions[v1alpha1.StatefulSet] - if !ok { - source.Status.Conditions[v1alpha1.StatefulSet] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.StatefulSetReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - return nil - } + defer source.SaveStatus(ctx, r.Log, r.Client) statefulSet := &appsv1.StatefulSet{} err := r.Get(ctx, types.NamespacedName{ @@ -51,47 +44,58 @@ func (r *SourceReconciler) ObserveSourceStatefulSet(ctx context.Context, source }, statefulSet) if err != nil { if errors.IsNotFound(err) { - r.Log.Info("source statefulSet is not ready yet...", + r.Log.Info("source statefulset is not ready yet...", "namespace", source.Namespace, "name", source.Name, - "statefulSet name", statefulSet.Name) - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - source.Status.Conditions[v1alpha1.StatefulSet] = condition + "statefulset name", statefulSet.Name) + source.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "source statefulset is not ready yet...")) return nil } + source.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.StatefulSetError, + fmt.Sprintf("failed to fetch source statefulset: %v", err))) return err } selector, err := metav1.LabelSelectorAsSelector(statefulSet.Spec.Selector) if err != nil { - r.Log.Error(err, "error retrieving statefulSet selector") + r.Log.Error(err, "error retrieving statefulset selector") + source.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.StatefulSetError, + fmt.Sprintf("error retrieving statefulset selector: %v", err))) return err } source.Status.Selector = selector.String() + source.Status.Replicas = *source.Spec.Replicas if r.checkIfStatefulSetNeedUpdate(statefulSet, source) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Update - source.Status.Conditions[v1alpha1.StatefulSet] = condition + source.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update source statefulset...")) return nil } if statefulSet.Status.ReadyReplicas == *source.Spec.Replicas { - condition.Action = v1alpha1.NoAction - } else { - condition.Action = v1alpha1.Wait + source.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.StatefulSetIsReady, + "")) + return nil } - condition.Status = metav1.ConditionTrue - source.Status.Replicas = *statefulSet.Spec.Replicas - source.Status.Conditions[v1alpha1.StatefulSet] = condition + source.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "source statefulset is pending creation...")) return nil } func (r *SourceReconciler) ApplySourceStatefulSet(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { + defer source.SaveStatus(ctx, r.Log, r.Client) + condition := source.Status.Conditions[v1alpha1.StatefulSet] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredStatefulSet := spec.MakeSourceStatefulSet(source) desiredStatefulSetSpec := desiredStatefulSet.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredStatefulSet, func() error { @@ -99,24 +103,22 @@ func (r *SourceReconciler) ApplySourceStatefulSet(ctx context.Context, source *v desiredStatefulSet.Spec = desiredStatefulSetSpec return nil }); err != nil { - r.Log.Error(err, "error create or update statefulSet workload for source", + r.Log.Error(err, "error create or update statefulset workload for source", "namespace", source.Namespace, "name", source.Name, - "statefulSet name", desiredStatefulSet.Name) + "statefulset name", desiredStatefulSet.Name) + source.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingStatefulSet, + fmt.Sprintf("error create or update statefulset workload for source: %v", err))) return err } + source.SetCondition(v1alpha1.StatefulSet, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating statefulset workload for source...")) return nil } func (r *SourceReconciler) ObserveSourceService(ctx context.Context, source *v1alpha1.Source) error { - condition, ok := source.Status.Conditions[v1alpha1.Service] - if !ok { - source.Status.Conditions[v1alpha1.Service] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.ServiceReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } - return nil - } + defer source.SaveStatus(ctx, r.Log, r.Client) svc := &corev1.Service{} svcName := spec.MakeHeadlessServiceName(spec.MakeSourceObjectMeta(source).Name) @@ -124,28 +126,34 @@ func (r *SourceReconciler) ObserveSourceService(ctx context.Context, source *v1a Name: svcName}, svc) if err != nil { if errors.IsNotFound(err) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - source.Status.Conditions[v1alpha1.Service] = condition r.Log.Info("source service is not created...", "namespace", source.Namespace, "name", source.Name, "service name", svcName) + source.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "source service is not created...")) return nil } + source.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ServiceError, + fmt.Sprintf("failed to fetch source service: %v", err))) return err } - condition.Action = v1alpha1.NoAction - condition.Status = metav1.ConditionTrue - source.Status.Conditions[v1alpha1.Service] = condition + source.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.ServiceIsReady, + "")) return nil } func (r *SourceReconciler) ApplySourceService(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { + defer source.SaveStatus(ctx, r.Log, r.Client) + condition := source.Status.Conditions[v1alpha1.Service] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredService := spec.MakeSourceService(source) desiredServiceSpec := desiredService.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredService, func() error { @@ -156,24 +164,23 @@ func (r *SourceReconciler) ApplySourceService(ctx context.Context, source *v1alp r.Log.Error(err, "error create or update service for source", "namespace", source.Namespace, "name", source.Name, "service name", desiredService.Name) + source.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingService, + fmt.Sprintf("error create or update service for source: %v", err))) return err } + source.SetCondition(v1alpha1.Service, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating service for source...")) return nil } func (r *SourceReconciler) ObserveSourceHPA(ctx context.Context, source *v1alpha1.Source) error { + defer source.SaveStatus(ctx, r.Log, r.Client) + if source.Spec.MaxReplicas == nil { // HPA not enabled, skip further action - return nil - } - - condition, ok := source.Status.Conditions[v1alpha1.HPA] - if !ok { - source.Status.Conditions[v1alpha1.HPA] = v1alpha1.ResourceCondition{ - Condition: v1alpha1.HPAReady, - Status: metav1.ConditionFalse, - Action: v1alpha1.Create, - } + delete(source.Status.Conditions, v1alpha1.HPA) return nil } @@ -182,39 +189,58 @@ func (r *SourceReconciler) ObserveSourceHPA(ctx context.Context, source *v1alpha Name: spec.MakeSourceObjectMeta(source).Name}, hpa) if err != nil { if errors.IsNotFound(err) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Create - source.Status.Conditions[v1alpha1.HPA] = condition r.Log.Info("hpa is not created for source...", "namespace", source.Namespace, "name", source.Name, "hpa name", hpa.Name) + source.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "hpa is not created for source...")) return nil } + source.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.HPAError, + fmt.Sprintf("failed to fetch source hpa: %v", err))) return err } if r.checkIfHPANeedUpdate(hpa, source) { - condition.Status = metav1.ConditionFalse - condition.Action = v1alpha1.Update - source.Status.Conditions[v1alpha1.HPA] = condition + source.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update source hpa...")) return nil } - condition.Action = v1alpha1.NoAction - condition.Status = metav1.ConditionTrue - source.Status.Conditions[v1alpha1.HPA] = condition + source.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.HPAIsReady, + "")) return nil } func (r *SourceReconciler) ApplySourceHPA(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { + defer source.SaveStatus(ctx, r.Log, r.Client) + if source.Spec.MaxReplicas == nil { - // HPA not enabled, skip further action + // HPA not enabled, clear the exists HPA + hpa := &autov2beta2.HorizontalPodAutoscaler{} + hpa.Namespace = source.Namespace + hpa.Name = spec.MakeSourceObjectMeta(source).Name + if err := r.Delete(ctx, hpa); err != nil { + if errors.IsNotFound(err) { + return nil + } + source.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.HPAError, + fmt.Sprintf("failed to delete source hpa: %v", err))) + return err + } return nil } + condition := source.Status.Conditions[v1alpha1.HPA] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil } + desiredHPA := spec.MakeSourceHPA(source) desiredHPASpec := desiredHPA.Spec if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredHPA, func() error { @@ -225,39 +251,110 @@ func (r *SourceReconciler) ApplySourceHPA(ctx context.Context, source *v1alpha1. r.Log.Error(err, "error create or update hpa for source", "namespace", source.Namespace, "name", source.Name, "hpa name", desiredHPA.Name) + source.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingHPA, + fmt.Sprintf("error create or update hpa for source: %v", err))) return err } + source.SetCondition(v1alpha1.HPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating hpa for source...")) return nil } func (r *SourceReconciler) ObserveSourceVPA(ctx context.Context, source *v1alpha1.Source) error { - return observeVPA(ctx, r, types.NamespacedName{Namespace: source.Namespace, - Name: spec.MakeSourceObjectMeta(source).Name}, source.Spec.Pod.VPA, source.Status.Conditions) -} + defer source.SaveStatus(ctx, r.Log, r.Client) -func (r *SourceReconciler) ApplySourceVPA(ctx context.Context, source *v1alpha1.Source) error { + if source.Spec.Pod.VPA == nil { + delete(source.Status.Conditions, v1alpha1.VPA) + return nil + } - condition, ok := source.Status.Conditions[v1alpha1.VPA] + vpa := &vpav1.VerticalPodAutoscaler{} + err := r.Get(ctx, types.NamespacedName{ + Namespace: source.Namespace, + Name: spec.MakeSourceObjectMeta(source).Name, + }, vpa) + if err != nil { + if errors.IsNotFound(err) { + r.Log.Info("vpa is not created for source...", + "namespace", source.Namespace, "name", source.Name, + "vpa name", vpa.Name) + source.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "vpa is not created for source...")) + return nil + } + source.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.VPAError, + fmt.Sprintf("failed to fetch source vpa: %v", err))) + return err + } - if !ok || condition.Status == metav1.ConditionTrue { + if r.checkIfVPANeedUpdate(vpa, source) { + source.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "need to update source vpa...")) return nil } - objectMeta := spec.MakeSourceObjectMeta(source) - targetRef := &autoscaling.CrossVersionObjectReference{ - Kind: source.Kind, - Name: source.Name, - APIVersion: source.APIVersion, + source.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Ready, metav1.ConditionTrue, v1alpha1.VPAIsReady, + "")) + return nil +} + +func (r *SourceReconciler) ApplySourceVPA(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { + defer source.SaveStatus(ctx, r.Log, r.Client) + + if source.Spec.Pod.VPA == nil { + // VPA not enabled, clear the exists VPA + vpa := &vpav1.VerticalPodAutoscaler{} + vpa.Namespace = source.Namespace + vpa.Name = spec.MakeSourceObjectMeta(source).Name + if err := r.Delete(ctx, vpa); err != nil { + if errors.IsNotFound(err) { + return nil + } + source.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.VPAError, + fmt.Sprintf("failed to delete source vpa: %v", err))) + return err + } + return nil } - err := applyVPA(ctx, r.Client, r.Log, condition, objectMeta, targetRef, source.Spec.Pod.VPA, "source", source.Namespace, source.Name) - if err != nil { - return err + condition := source.Status.Conditions[v1alpha1.VPA] + if condition.Status == metav1.ConditionTrue && !newGeneration { + return nil } + desiredVPA := spec.MakeSourceVPA(source) + desiredVPASpec := desiredVPA.Spec + if _, err := ctrl.CreateOrUpdate(ctx, r.Client, desiredVPA, func() error { + // source vpa mutate logic + desiredVPA.Spec = desiredVPASpec + return nil + }); err != nil { + r.Log.Error(err, "error create or update vpa for source", + "namespace", source.Namespace, "name", source.Name, + "vpa name", desiredVPA.Name) + source.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Error, metav1.ConditionFalse, v1alpha1.ErrorCreatingVPA, + fmt.Sprintf("error create or update vpa for source: %v", err))) + return err + } + source.SetCondition(v1alpha1.VPA, v1alpha1.CreateCondition( + v1alpha1.Pending, metav1.ConditionFalse, v1alpha1.PendingCreation, + "creating or updating vpa for source...")) return nil } +func (r *SourceReconciler) UpdateObservedGeneration(ctx context.Context, source *v1alpha1.Source) { + defer source.SaveStatus(ctx, r.Log, r.Client) + source.Status.ObservedGeneration = source.Generation +} + func (r *SourceReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.StatefulSet, source *v1alpha1.Source) bool { return !spec.CheckIfStatefulSetSpecIsEqual(&statefulSet.Spec, &spec.MakeSourceStatefulSet(source).Spec) } @@ -265,3 +362,7 @@ func (r *SourceReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.Stat func (r *SourceReconciler) checkIfHPANeedUpdate(hpa *autov2beta2.HorizontalPodAutoscaler, source *v1alpha1.Source) bool { return !spec.CheckIfHPASpecIsEqual(&hpa.Spec, &spec.MakeSourceHPA(source).Spec) } + +func (r *SourceReconciler) checkIfVPANeedUpdate(vpa *vpav1.VerticalPodAutoscaler, source *v1alpha1.Source) bool { + return !spec.CheckIfVPASpecIsEqual(&vpa.Spec, &spec.MakeSourceVPA(source).Spec) +} diff --git a/controllers/source_controller.go b/controllers/source_controller.go index 0c269b154..0d141571b 100644 --- a/controllers/source_controller.go +++ b/controllers/source_controller.go @@ -21,9 +21,6 @@ import ( "context" "github.com/go-logr/logr" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" - "github.com/streamnative/function-mesh/utils" appsv1 "k8s.io/api/apps/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" @@ -35,6 +32,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" + "github.com/streamnative/function-mesh/utils" ) // SourceReconciler reconciles a Source object @@ -72,10 +73,6 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return reconcile.Result{}, nil } - if source.Status.Conditions == nil { - source.Status.Conditions = make(map[v1alpha1.Component]v1alpha1.ResourceCondition) - } - err = r.ObserveSourceStatefulSet(ctx, source) if err != nil { return reconcile.Result{}, err @@ -94,11 +91,6 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return reconcile.Result{}, err } } - err = r.Status().Update(ctx, source) - if err != nil { - r.Log.Error(err, "failed to update source status") - return ctrl.Result{}, err - } isNewGeneration := r.checkIfSourceGenerationsIsIncreased(source) @@ -114,17 +106,14 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr if err != nil { return reconcile.Result{}, err } - err = r.ApplySourceVPA(ctx, source) - if err != nil { - return reconcile.Result{}, err + if r.WatchFlags != nil && r.WatchFlags.WatchVPACRDs { + err = r.ApplySourceVPA(ctx, source, isNewGeneration) + if err != nil { + return reconcile.Result{}, err + } } - source.Status.ObservedGeneration = source.Generation - err = r.Status().Update(ctx, source) - if err != nil { - r.Log.Error(err, "failed to update source status") - return ctrl.Result{}, err - } + r.UpdateObservedGeneration(ctx, source) return ctrl.Result{}, nil } diff --git a/controllers/source_controller_test.go b/controllers/source_controller_test.go index e2b20f1e5..0c3079060 100644 --- a/controllers/source_controller_test.go +++ b/controllers/source_controller_test.go @@ -26,11 +26,13 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/spec" appv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/spec" ) var _ = Describe("Source Controller", func() { @@ -38,7 +40,7 @@ var _ = Describe("Source Controller", func() { pulsarConfig := makeSamplePulsarConfig() source := makeSourceSample() if source.Status.Conditions == nil { - source.Status.Conditions = make(map[v1alpha1.Component]v1alpha1.ResourceCondition) + source.Status.Conditions = make(map[v1alpha1.Component]metav1.Condition) } statefulSet := spec.MakeSourceStatefulSet(source) diff --git a/controllers/spec/common.go b/controllers/spec/common.go index 05e4e2b24..b4dd52658 100644 --- a/controllers/spec/common.go +++ b/controllers/spec/common.go @@ -27,14 +27,16 @@ import ( "strconv" "strings" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/controllers/proto" - "github.com/streamnative/function-mesh/utils" appsv1 "k8s.io/api/apps/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/controllers/proto" + "github.com/streamnative/function-mesh/utils" ) const ( @@ -1714,6 +1716,15 @@ func CheckIfHPASpecIsEqual(spec *autov2beta2.HorizontalPodAutoscalerSpec, return true } +func CheckIfVPASpecIsEqual(spec *vpav1.VerticalPodAutoscalerSpec, + desiredSpec *vpav1.VerticalPodAutoscalerSpec) bool { + if !reflect.DeepEqual(spec.UpdatePolicy, desiredSpec.UpdatePolicy) || + !reflect.DeepEqual(spec.ResourcePolicy, desiredSpec.ResourcePolicy) { + return false + } + return true +} + func objectXOROperator(first interface{}, second interface{}) bool { return (first != nil && second == nil) || (first == nil && second != nil) } diff --git a/controllers/spec/common_test.go b/controllers/spec/common_test.go index 9e6e7f4b6..fa7d410e4 100644 --- a/controllers/spec/common_test.go +++ b/controllers/spec/common_test.go @@ -21,11 +21,12 @@ import ( "strings" "testing" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/utils" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/utils" ) func TestGetDownloadCommand(t *testing.T) { diff --git a/controllers/spec/function.go b/controllers/spec/function.go index 731bba39e..71b48b636 100644 --- a/controllers/spec/function.go +++ b/controllers/spec/function.go @@ -18,14 +18,17 @@ package spec import ( - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/utils" "google.golang.org/protobuf/encoding/protojson" appsv1 "k8s.io/api/apps/v1" + autoscaling "k8s.io/api/autoscaling/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/utils" ) // log is for logging in this package. @@ -180,3 +183,13 @@ func generateFunctionDetailsInJSON(function *v1alpha1.Function) string { log.Info(string(json)) return string(json) } + +func MakeFunctionVPA(function *v1alpha1.Function) *vpav1.VerticalPodAutoscaler { + objectMeta := MakeFunctionObjectMeta(function) + targetRef := &autoscaling.CrossVersionObjectReference{ + Kind: function.Kind, + Name: function.Name, + APIVersion: function.APIVersion, + } + return makeVPA(objectMeta, targetRef, function.Spec.Pod.VPA) +} diff --git a/controllers/spec/function_mesh.go b/controllers/spec/function_mesh.go index e39c93e73..03fbc68f5 100644 --- a/controllers/spec/function_mesh.go +++ b/controllers/spec/function_mesh.go @@ -18,8 +18,9 @@ package spec import ( - "github.com/streamnative/function-mesh/api/compute/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" ) func MakeFunctionComponent(functionName string, mesh *v1alpha1.FunctionMesh, diff --git a/controllers/spec/function_test.go b/controllers/spec/function_test.go index 59f64cf09..6a0ef242e 100644 --- a/controllers/spec/function_test.go +++ b/controllers/spec/function_test.go @@ -21,10 +21,10 @@ import ( "strings" "testing" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "gotest.tools/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" ) func TestCreateFunctionDetailsForStatefulFunction(t *testing.T) { diff --git a/controllers/spec/hpa.go b/controllers/spec/hpa.go index dd4f3e92c..89f2dfbd8 100644 --- a/controllers/spec/hpa.go +++ b/controllers/spec/hpa.go @@ -18,10 +18,11 @@ package spec import ( - "github.com/streamnative/function-mesh/api/compute/v1alpha1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" ) type BuiltinAutoScaler interface { diff --git a/controllers/spec/sink.go b/controllers/spec/sink.go index d9d1e4dbf..382747047 100644 --- a/controllers/spec/sink.go +++ b/controllers/spec/sink.go @@ -18,13 +18,16 @@ package spec import ( - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/utils" "google.golang.org/protobuf/encoding/protojson" appsv1 "k8s.io/api/apps/v1" + autoscaling "k8s.io/api/autoscaling/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/utils" ) func MakeSinkHPA(sink *v1alpha1.Sink) *autov2beta2.HorizontalPodAutoscaler { @@ -160,3 +163,13 @@ func generateSinkDetailsInJSON(sink *v1alpha1.Sink) string { log.Info(string(json)) return string(json) } + +func MakeSinkVPA(sink *v1alpha1.Sink) *vpav1.VerticalPodAutoscaler { + objectMeta := MakeSinkObjectMeta(sink) + targetRef := &autoscaling.CrossVersionObjectReference{ + Kind: sink.Kind, + Name: sink.Name, + APIVersion: sink.APIVersion, + } + return makeVPA(objectMeta, targetRef, sink.Spec.Pod.VPA) +} diff --git a/controllers/spec/source.go b/controllers/spec/source.go index 1c7be0d7a..f9a33996d 100644 --- a/controllers/spec/source.go +++ b/controllers/spec/source.go @@ -18,13 +18,16 @@ package spec import ( - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/utils" "google.golang.org/protobuf/encoding/protojson" appsv1 "k8s.io/api/apps/v1" + autoscaling "k8s.io/api/autoscaling/v1" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/utils" ) func MakeSourceHPA(source *v1alpha1.Source) *autov2beta2.HorizontalPodAutoscaler { @@ -155,3 +158,13 @@ func generateSourceDetailsInJSON(source *v1alpha1.Source) string { log.Info(string(json)) return string(json) } + +func MakeSourceVPA(source *v1alpha1.Source) *vpav1.VerticalPodAutoscaler { + objectMeta := MakeSourceObjectMeta(source) + targetRef := &autoscaling.CrossVersionObjectReference{ + Kind: source.Kind, + Name: source.Name, + APIVersion: source.APIVersion, + } + return makeVPA(objectMeta, targetRef, source.Spec.Pod.VPA) +} diff --git a/controllers/spec/utils.go b/controllers/spec/utils.go index e5bbb76c8..853220d75 100644 --- a/controllers/spec/utils.go +++ b/controllers/spec/utils.go @@ -23,13 +23,11 @@ import ( "regexp" "strings" - v1alpha1 "github.com/streamnative/function-mesh/api/compute/v1alpha1" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/validation" + v1alpha1 "github.com/streamnative/function-mesh/api/compute/v1alpha1" "github.com/streamnative/function-mesh/controllers/proto" "github.com/streamnative/function-mesh/utils" ) diff --git a/controllers/spec/utils_test.go b/controllers/spec/utils_test.go index 0665467db..841492578 100644 --- a/controllers/spec/utils_test.go +++ b/controllers/spec/utils_test.go @@ -20,10 +20,10 @@ package spec import ( "testing" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" ) func TestGetValFromPtrOrDefault(t *testing.T) { diff --git a/controllers/spec/vpa.go b/controllers/spec/vpa.go index b5145ac01..048dd00a4 100644 --- a/controllers/spec/vpa.go +++ b/controllers/spec/vpa.go @@ -19,13 +19,14 @@ package spec import ( - "github.com/streamnative/function-mesh/api/compute/v1alpha1" autoscaling "k8s.io/api/autoscaling/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" ) -func MakeVPA(objectMeta *metav1.ObjectMeta, targetRef *autoscaling.CrossVersionObjectReference, vpa *v1alpha1.VPASpec) *vpav1.VerticalPodAutoscaler { +func makeVPA(objectMeta *metav1.ObjectMeta, targetRef *autoscaling.CrossVersionObjectReference, vpa *v1alpha1.VPASpec) *vpav1.VerticalPodAutoscaler { return &vpav1.VerticalPodAutoscaler{ TypeMeta: metav1.TypeMeta{ APIVersion: "autoscaling.k8s.io/v1", diff --git a/controllers/spec/vpa_test.go b/controllers/spec/vpa_test.go index f93979ada..f9ed0edae 100644 --- a/controllers/spec/vpa_test.go +++ b/controllers/spec/vpa_test.go @@ -20,13 +20,14 @@ package spec import ( "testing" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" "github.com/stretchr/testify/assert" autoscaling "k8s.io/api/autoscaling/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" ) func TestMakeVPA(t *testing.T) { @@ -257,7 +258,7 @@ func TestMakeVPA(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equalf(t, tt.want, MakeVPA(tt.args.objectMeta, tt.args.targetRef, tt.args.vpa), "MakeVPA(%v, %v, %v)", tt.args.objectMeta, tt.args.targetRef, tt.args.vpa) + assert.Equalf(t, tt.want, makeVPA(tt.args.objectMeta, tt.args.targetRef, tt.args.vpa), "makeVPA(%v, %v, %v)", tt.args.objectMeta, tt.args.targetRef, tt.args.vpa) }) } } diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 585b9d8a7..ddb99c97f 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -25,8 +25,6 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" - "github.com/streamnative/function-mesh/utils" vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" @@ -36,6 +34,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest/printer" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" + "github.com/streamnative/function-mesh/utils" // +kubebuilder:scaffold:imports ) diff --git a/controllers/test_utils_test.go b/controllers/test_utils_test.go index c7e1b0e28..a48c64963 100644 --- a/controllers/test_utils_test.go +++ b/controllers/test_utils_test.go @@ -20,10 +20,11 @@ package controllers import ( "fmt" - "github.com/streamnative/function-mesh/api/compute/v1alpha1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + + "github.com/streamnative/function-mesh/api/compute/v1alpha1" ) const TestClusterName string = "test-pulsar" From ebee3cc33b7e63ffaa1ec0c3024c9e426caec36e Mon Sep 17 00:00:00 2001 From: laminar Date: Thu, 5 Jan 2023 16:25:41 +0800 Subject: [PATCH 2/8] fix ut Signed-off-by: laminar --- api/compute/v1alpha1/status_handler.go | 33 ++++++++++++++----- .../clientset/versioned/fake/register.go | 14 ++++---- .../clientset/versioned/scheme/register.go | 14 ++++---- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/api/compute/v1alpha1/status_handler.go b/api/compute/v1alpha1/status_handler.go index 4d9df18fc..59456cddb 100644 --- a/api/compute/v1alpha1/status_handler.go +++ b/api/compute/v1alpha1/status_handler.go @@ -1,3 +1,20 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package v1alpha1 import ( @@ -60,10 +77,10 @@ const ( // SaveStatus will trigger Function object update to save the current status // conditions -func (r *Function) SaveStatus(ctx context.Context, logger logr.Logger, cl client.Client) { +func (r *Function) SaveStatus(ctx context.Context, logger logr.Logger, c client.Client) { logger.Info("Updating status on FunctionStatus", "resource version", r.ResourceVersion) - err := cl.Status().Update(ctx, r) + err := c.Status().Update(ctx, r) if err != nil { logger.Error(err, "failed to update status on FunctionStatus", "function", r) } else { @@ -82,10 +99,10 @@ func (r *Function) SetCondition(component Component, condition *metav1.Condition // SaveStatus will trigger Sink object update to save the current status // conditions -func (r *Sink) SaveStatus(ctx context.Context, logger logr.Logger, cl client.Client) { +func (r *Sink) SaveStatus(ctx context.Context, logger logr.Logger, c client.Client) { logger.Info("Updating status on SinkStatus", "resource version", r.ResourceVersion) - err := cl.Status().Update(ctx, r) + err := c.Status().Update(ctx, r) if err != nil { logger.Error(err, "failed to update status on SinkStatus", "sink", r) } else { @@ -104,10 +121,10 @@ func (r *Sink) SetCondition(component Component, condition *metav1.Condition) *S // SaveStatus will trigger Source object update to save the current status // conditions -func (r *Source) SaveStatus(ctx context.Context, logger logr.Logger, cl client.Client) { +func (r *Source) SaveStatus(ctx context.Context, logger logr.Logger, c client.Client) { logger.Info("Updating status on SourceStatus", "resource version", r.ResourceVersion) - err := cl.Status().Update(ctx, r) + err := c.Status().Update(ctx, r) if err != nil { logger.Error(err, "failed to update status on SourceStatus", "source", r) } else { @@ -126,10 +143,10 @@ func (r *Source) SetCondition(component Component, condition *metav1.Condition) // SaveStatus will trigger FunctionMesh object update to save the current status // conditions -func (r *FunctionMesh) SaveStatus(ctx context.Context, logger logr.Logger, cl client.Client) { +func (r *FunctionMesh) SaveStatus(ctx context.Context, logger logr.Logger, c client.Client) { logger.Info("Updating status on FunctionMeshStatus", "resource version", r.ResourceVersion) - err := cl.Status().Update(ctx, r) + err := c.Status().Update(ctx, r) if err != nil { logger.Error(err, "failed to update status on FunctionMeshStatus", "functionmesh", r) } else { diff --git a/api/generated/clientset/versioned/fake/register.go b/api/generated/clientset/versioned/fake/register.go index ed6f1b974..d779fd9fa 100644 --- a/api/generated/clientset/versioned/fake/register.go +++ b/api/generated/clientset/versioned/fake/register.go @@ -38,14 +38,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/api/generated/clientset/versioned/scheme/register.go b/api/generated/clientset/versioned/scheme/register.go index 78f1cdd24..4a3fda2e9 100644 --- a/api/generated/clientset/versioned/scheme/register.go +++ b/api/generated/clientset/versioned/scheme/register.go @@ -38,14 +38,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. From 4c8bbc08f1c2af061edc0fa79463220381d88cd9 Mon Sep 17 00:00:00 2001 From: laminar Date: Thu, 5 Jan 2023 17:12:21 +0800 Subject: [PATCH 3/8] Remove redundant code Signed-off-by: laminar --- api/compute/v1alpha1/common.go | 38 ---------- api/compute/v1alpha1/status_handler.go | 70 +++++++++---------- api/compute/v1alpha1/zz_generated.deepcopy.go | 15 ---- 3 files changed, 33 insertions(+), 90 deletions(-) diff --git a/api/compute/v1alpha1/common.go b/api/compute/v1alpha1/common.go index be1bfb5e4..6ab120d30 100644 --- a/api/compute/v1alpha1/common.go +++ b/api/compute/v1alpha1/common.go @@ -26,7 +26,6 @@ import ( pctlutil "github.com/streamnative/pulsarctl/pkg/pulsar/utils" autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" vpav1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" @@ -336,37 +335,6 @@ const ( VPA Component = "VerticalPodAutoscaler" ) -// The `Status` of a given `Condition` and the `Action` needed to reach the `Status` -type ResourceCondition struct { - Condition ResourceConditionType `json:"condition,omitempty"` - Status metav1.ConditionStatus `json:"status,omitempty"` - Action ReconcileAction `json:"action,omitempty"` -} - -type ResourceConditionType string - -const ( - MeshReady ResourceConditionType = "MeshReady" - FunctionReady ResourceConditionType = "FunctionReady" - SourceReady ResourceConditionType = "SourceReady" - SinkReady ResourceConditionType = "SinkReady" - - StatefulSetReady ResourceConditionType = "StatefulSetReady" - ServiceReady ResourceConditionType = "ServiceReady" - HPAReady ResourceConditionType = "HPAReady" - VPAReady ResourceConditionType = "VPAReady" -) - -type ReconcileAction string - -const ( - Create ReconcileAction = "Create" - Delete ReconcileAction = "Delete" - Update ReconcileAction = "Update" - Wait ReconcileAction = "Wait" - NoAction ReconcileAction = "NoAction" -) - // ProcessGuarantee enum type // +kubebuilder:validation:Enum=atleast_once;atmost_once;effectively_once type ProcessGuarantee string @@ -634,9 +602,3 @@ type Liveness struct { // +kubebuilder:validation:Optional InitialDelaySeconds int32 `json:"initialDelaySeconds,omitempty"` } - -func (rc *ResourceCondition) SetCondition(condition ResourceConditionType, action ReconcileAction, status metav1.ConditionStatus) { - rc.Condition = condition - rc.Action = action - rc.Status = status -} diff --git a/api/compute/v1alpha1/status_handler.go b/api/compute/v1alpha1/status_handler.go index 59456cddb..38031e6fe 100644 --- a/api/compute/v1alpha1/status_handler.go +++ b/api/compute/v1alpha1/status_handler.go @@ -26,53 +26,49 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -type FunctionMeshConditionType string +type ResourceConditionType string const ( // Created indicates the resource has been created - Created FunctionMeshConditionType = "Created" - // Terminated indicates the resource has been terminated - Terminated FunctionMeshConditionType = "Terminated" + Created ResourceConditionType = "Created" // Error indicates the resource had an error - Error FunctionMeshConditionType = "Error" + Error ResourceConditionType = "Error" // Pending indicates the resource hasn't been created - Pending FunctionMeshConditionType = "Pending" + Pending ResourceConditionType = "Pending" // Orphaned indicates that the resource is marked for deletion but hasn't // been deleted yet - Orphaned FunctionMeshConditionType = "Orphaned" - // Unknown indicates the status is unavailable - Unknown FunctionMeshConditionType = "Unknown" + Orphaned ResourceConditionType = "Orphaned" // Ready indicates the object is fully created - Ready FunctionMeshConditionType = "Ready" + Ready ResourceConditionType = "Ready" ) -type FunctionMeshConditionReason string +type ResourceConditionReason string const ( - ErrorCreatingStatefulSet FunctionMeshConditionReason = "ErrorCreatingStatefulSet" - ErrorCreatingFunction FunctionMeshConditionReason = "ErrorCreatingFunction" - ErrorCreatingSource FunctionMeshConditionReason = "ErrorCreatingSource" - ErrorCreatingSink FunctionMeshConditionReason = "ErrorCreatingSink" - ErrorCreatingHPA FunctionMeshConditionReason = "ErrorCreatingHPA" - ErrorCreatingVPA FunctionMeshConditionReason = "ErrorCreatingVPA" - ErrorCreatingService FunctionMeshConditionReason = "ErrorCreatingService" - StatefulSetError FunctionMeshConditionReason = "StatefulSetError" - FunctionError FunctionMeshConditionReason = "FunctionError" - SourceError FunctionMeshConditionReason = "SourceError" - SinkError FunctionMeshConditionReason = "SinkError" - ServiceError FunctionMeshConditionReason = "ServiceError" - HPAError FunctionMeshConditionReason = "HPAError" - VPAError FunctionMeshConditionReason = "VPAError" - PendingCreation FunctionMeshConditionReason = "PendingCreation" - PendingTermination FunctionMeshConditionReason = "PendingTermination" - ServiceIsReady FunctionMeshConditionReason = "ServiceIsReady" - MeshIsReady FunctionMeshConditionReason = "MeshIsReady" - StatefulSetIsReady FunctionMeshConditionReason = "StatefulSetIsReady" - HPAIsReady FunctionMeshConditionReason = "HPAIsReady" - VPAIsReady FunctionMeshConditionReason = "VPAIsReady" - FunctionIsReady FunctionMeshConditionReason = "FunctionIsReady" - SourceIsReady FunctionMeshConditionReason = "SourceIsReady" - SinkIsReady FunctionMeshConditionReason = "SinkIsReady" + ErrorCreatingStatefulSet ResourceConditionReason = "ErrorCreatingStatefulSet" + ErrorCreatingFunction ResourceConditionReason = "ErrorCreatingFunction" + ErrorCreatingSource ResourceConditionReason = "ErrorCreatingSource" + ErrorCreatingSink ResourceConditionReason = "ErrorCreatingSink" + ErrorCreatingHPA ResourceConditionReason = "ErrorCreatingHPA" + ErrorCreatingVPA ResourceConditionReason = "ErrorCreatingVPA" + ErrorCreatingService ResourceConditionReason = "ErrorCreatingService" + StatefulSetError ResourceConditionReason = "StatefulSetError" + FunctionError ResourceConditionReason = "FunctionError" + SourceError ResourceConditionReason = "SourceError" + SinkError ResourceConditionReason = "SinkError" + ServiceError ResourceConditionReason = "ServiceError" + HPAError ResourceConditionReason = "HPAError" + VPAError ResourceConditionReason = "VPAError" + PendingCreation ResourceConditionReason = "PendingCreation" + PendingTermination ResourceConditionReason = "PendingTermination" + ServiceIsReady ResourceConditionReason = "ServiceIsReady" + MeshIsReady ResourceConditionReason = "MeshIsReady" + StatefulSetIsReady ResourceConditionReason = "StatefulSetIsReady" + HPAIsReady ResourceConditionReason = "HPAIsReady" + VPAIsReady ResourceConditionReason = "VPAIsReady" + FunctionIsReady ResourceConditionReason = "FunctionIsReady" + SourceIsReady ResourceConditionReason = "SourceIsReady" + SinkIsReady ResourceConditionReason = "SinkIsReady" ) // SaveStatus will trigger Function object update to save the current status @@ -188,8 +184,8 @@ func (r *FunctionMesh) SetSourceCondition(name string, condition *metav1.Conditi } // CreateCondition initializes a new status condition -func CreateCondition(condType FunctionMeshConditionType, status metav1.ConditionStatus, - reason FunctionMeshConditionReason, message string) *metav1.Condition { +func CreateCondition(condType ResourceConditionType, status metav1.ConditionStatus, + reason ResourceConditionReason, message string) *metav1.Condition { cond := &metav1.Condition{ Type: string(condType), Status: status, diff --git a/api/compute/v1alpha1/zz_generated.deepcopy.go b/api/compute/v1alpha1/zz_generated.deepcopy.go index 49f6c2754..31fac6737 100644 --- a/api/compute/v1alpha1/zz_generated.deepcopy.go +++ b/api/compute/v1alpha1/zz_generated.deepcopy.go @@ -868,21 +868,6 @@ func (in *PythonRuntime) DeepCopy() *PythonRuntime { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResourceCondition) DeepCopyInto(out *ResourceCondition) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceCondition. -func (in *ResourceCondition) DeepCopy() *ResourceCondition { - if in == nil { - return nil - } - out := new(ResourceCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Runtime) DeepCopyInto(out *Runtime) { *out = *in From 30c0bb7d2b75bd467a4ecbbe1e7cb415bd1da2a1 Mon Sep 17 00:00:00 2001 From: laminar Date: Thu, 5 Jan 2023 17:18:37 +0800 Subject: [PATCH 4/8] fix ut Signed-off-by: laminar --- api/compute/v1alpha1/status_handler.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/compute/v1alpha1/status_handler.go b/api/compute/v1alpha1/status_handler.go index 38031e6fe..62edf674a 100644 --- a/api/compute/v1alpha1/status_handler.go +++ b/api/compute/v1alpha1/status_handler.go @@ -73,7 +73,7 @@ const ( // SaveStatus will trigger Function object update to save the current status // conditions -func (r *Function) SaveStatus(ctx context.Context, logger logr.Logger, c client.Client) { +func (r *Function) SaveStatus(ctx context.Context, logger logr.Logger, c client.StatusClient) { logger.Info("Updating status on FunctionStatus", "resource version", r.ResourceVersion) err := c.Status().Update(ctx, r) @@ -95,7 +95,7 @@ func (r *Function) SetCondition(component Component, condition *metav1.Condition // SaveStatus will trigger Sink object update to save the current status // conditions -func (r *Sink) SaveStatus(ctx context.Context, logger logr.Logger, c client.Client) { +func (r *Sink) SaveStatus(ctx context.Context, logger logr.Logger, c client.StatusClient) { logger.Info("Updating status on SinkStatus", "resource version", r.ResourceVersion) err := c.Status().Update(ctx, r) @@ -117,7 +117,7 @@ func (r *Sink) SetCondition(component Component, condition *metav1.Condition) *S // SaveStatus will trigger Source object update to save the current status // conditions -func (r *Source) SaveStatus(ctx context.Context, logger logr.Logger, c client.Client) { +func (r *Source) SaveStatus(ctx context.Context, logger logr.Logger, c client.StatusClient) { logger.Info("Updating status on SourceStatus", "resource version", r.ResourceVersion) err := c.Status().Update(ctx, r) @@ -139,7 +139,7 @@ func (r *Source) SetCondition(component Component, condition *metav1.Condition) // SaveStatus will trigger FunctionMesh object update to save the current status // conditions -func (r *FunctionMesh) SaveStatus(ctx context.Context, logger logr.Logger, c client.Client) { +func (r *FunctionMesh) SaveStatus(ctx context.Context, logger logr.Logger, c client.StatusClient) { logger.Info("Updating status on FunctionMeshStatus", "resource version", r.ResourceVersion) err := c.Status().Update(ctx, r) From 69c254ca0121fe2ed26b4b6ce7f0cd603becd63b Mon Sep 17 00:00:00 2001 From: laminar Date: Sat, 7 Jan 2023 14:39:59 +0800 Subject: [PATCH 5/8] reduce the frequency of status updates Signed-off-by: laminar --- controllers/function.go | 17 ----------------- controllers/function_controller.go | 2 ++ controllers/function_mesh.go | 15 ++------------- controllers/functionmesh_controller.go | 2 ++ controllers/sink.go | 17 ----------------- controllers/sink_controller.go | 2 ++ controllers/source.go | 17 ----------------- controllers/source_controller.go | 2 ++ 8 files changed, 10 insertions(+), 64 deletions(-) diff --git a/controllers/function.go b/controllers/function.go index 99776cf14..ed14dd703 100644 --- a/controllers/function.go +++ b/controllers/function.go @@ -35,8 +35,6 @@ import ( ) func (r *FunctionReconciler) ObserveFunctionStatefulSet(ctx context.Context, function *v1alpha1.Function) error { - defer function.SaveStatus(ctx, r.Log, r.Client) - statefulSet := &appsv1.StatefulSet{} err := r.Get(ctx, types.NamespacedName{ Namespace: function.Namespace, @@ -89,8 +87,6 @@ func (r *FunctionReconciler) ObserveFunctionStatefulSet(ctx context.Context, fun } func (r *FunctionReconciler) ApplyFunctionStatefulSet(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { - defer function.SaveStatus(ctx, r.Log, r.Client) - condition := function.Status.Conditions[v1alpha1.StatefulSet] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil @@ -118,8 +114,6 @@ func (r *FunctionReconciler) ApplyFunctionStatefulSet(ctx context.Context, funct } func (r *FunctionReconciler) ObserveFunctionService(ctx context.Context, function *v1alpha1.Function) error { - defer function.SaveStatus(ctx, r.Log, r.Client) - svc := &corev1.Service{} svcName := spec.MakeHeadlessServiceName(spec.MakeFunctionObjectMeta(function).Name) err := r.Get(ctx, types.NamespacedName{Namespace: function.Namespace, @@ -147,8 +141,6 @@ func (r *FunctionReconciler) ObserveFunctionService(ctx context.Context, functio } func (r *FunctionReconciler) ApplyFunctionService(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { - defer function.SaveStatus(ctx, r.Log, r.Client) - condition := function.Status.Conditions[v1alpha1.Service] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil @@ -176,8 +168,6 @@ func (r *FunctionReconciler) ApplyFunctionService(ctx context.Context, function } func (r *FunctionReconciler) ObserveFunctionHPA(ctx context.Context, function *v1alpha1.Function) error { - defer function.SaveStatus(ctx, r.Log, r.Client) - if function.Spec.MaxReplicas == nil { // HPA not enabled, skip further action delete(function.Status.Conditions, v1alpha1.HPA) @@ -217,8 +207,6 @@ func (r *FunctionReconciler) ObserveFunctionHPA(ctx context.Context, function *v } func (r *FunctionReconciler) ApplyFunctionHPA(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { - defer function.SaveStatus(ctx, r.Log, r.Client) - if function.Spec.MaxReplicas == nil { // HPA not enabled, clear the exists HPA hpa := &autov2beta2.HorizontalPodAutoscaler{} @@ -263,8 +251,6 @@ func (r *FunctionReconciler) ApplyFunctionHPA(ctx context.Context, function *v1a } func (r *FunctionReconciler) ObserveFunctionVPA(ctx context.Context, function *v1alpha1.Function) error { - defer function.SaveStatus(ctx, r.Log, r.Client) - if function.Spec.Pod.VPA == nil { delete(function.Status.Conditions, v1alpha1.VPA) return nil @@ -305,8 +291,6 @@ func (r *FunctionReconciler) ObserveFunctionVPA(ctx context.Context, function *v } func (r *FunctionReconciler) ApplyFunctionVPA(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { - defer function.SaveStatus(ctx, r.Log, r.Client) - if function.Spec.Pod.VPA == nil { // VPA not enabled, clear the exists VPA vpa := &vpav1.VerticalPodAutoscaler{} @@ -351,7 +335,6 @@ func (r *FunctionReconciler) ApplyFunctionVPA(ctx context.Context, function *v1a } func (r *FunctionReconciler) UpdateObservedGeneration(ctx context.Context, function *v1alpha1.Function) { - defer function.SaveStatus(ctx, r.Log, r.Client) function.Status.ObservedGeneration = function.Generation } diff --git a/controllers/function_controller.go b/controllers/function_controller.go index 528a8b81a..4b42d2bb1 100644 --- a/controllers/function_controller.go +++ b/controllers/function_controller.go @@ -74,6 +74,8 @@ func (r *FunctionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return reconcile.Result{}, nil } + defer function.SaveStatus(ctx, r.Log, r.Client) + err = r.ObserveFunctionStatefulSet(ctx, function) if err != nil { return reconcile.Result{}, err diff --git a/controllers/function_mesh.go b/controllers/function_mesh.go index 451e79a04..19a77c829 100644 --- a/controllers/function_mesh.go +++ b/controllers/function_mesh.go @@ -45,14 +45,12 @@ func (r *FunctionMeshReconciler) ObserveFunctionMesh(ctx context.Context, req ct } // Observation only - r.observeMeshes(ctx, mesh) + r.observeMeshes(mesh) return nil } func (r *FunctionMeshReconciler) observeFunctions(ctx context.Context, mesh *v1alpha1.FunctionMesh) error { - defer mesh.SaveStatus(ctx, r.Log, r.Client) - orphanedFunctions := map[string]bool{} if len(mesh.Status.FunctionConditions) > 0 { @@ -106,8 +104,6 @@ func (r *FunctionMeshReconciler) observeFunctions(ctx context.Context, mesh *v1a } func (r *FunctionMeshReconciler) observeSources(ctx context.Context, mesh *v1alpha1.FunctionMesh) error { - defer mesh.SaveStatus(ctx, r.Log, r.Client) - orphanedSources := map[string]bool{} if len(mesh.Status.SourceConditions) > 0 { @@ -160,8 +156,6 @@ func (r *FunctionMeshReconciler) observeSources(ctx context.Context, mesh *v1alp } func (r *FunctionMeshReconciler) observeSinks(ctx context.Context, mesh *v1alpha1.FunctionMesh) error { - defer mesh.SaveStatus(ctx, r.Log, r.Client) - orphanedSinks := map[string]bool{} if len(mesh.Status.SinkConditions) > 0 { @@ -213,9 +207,7 @@ func (r *FunctionMeshReconciler) observeSinks(ctx context.Context, mesh *v1alpha return nil } -func (r *FunctionMeshReconciler) observeMeshes(ctx context.Context, mesh *v1alpha1.FunctionMesh) { - defer mesh.SaveStatus(ctx, r.Log, r.Client) - +func (r *FunctionMeshReconciler) observeMeshes(mesh *v1alpha1.FunctionMesh) { for _, cond := range mesh.Status.FunctionConditions { if cond.Type == string(v1alpha1.Ready) && cond.Status == metav1.ConditionTrue { continue @@ -252,8 +244,6 @@ func (r *FunctionMeshReconciler) observeMeshes(ctx context.Context, mesh *v1alph func (r *FunctionMeshReconciler) UpdateFunctionMesh(ctx context.Context, req ctrl.Request, mesh *v1alpha1.FunctionMesh, newGeneration bool) error { - defer mesh.SaveStatus(ctx, r.Log, r.Client) - for _, functionSpec := range mesh.Spec.Functions { condition := mesh.Status.FunctionConditions[functionSpec.Name] if !newGeneration && @@ -410,7 +400,6 @@ func makeComponentName(prefix, name string) string { } func (r *FunctionMeshReconciler) UpdateObservedGeneration(ctx context.Context, mesh *v1alpha1.FunctionMesh) { - defer mesh.SaveStatus(ctx, r.Log, r.Client) mesh.Status.ObservedGeneration = mesh.Generation } diff --git a/controllers/functionmesh_controller.go b/controllers/functionmesh_controller.go index 0aebfcfa5..a955fc454 100644 --- a/controllers/functionmesh_controller.go +++ b/controllers/functionmesh_controller.go @@ -62,6 +62,8 @@ func (r *FunctionMeshReconciler) Reconcile(ctx context.Context, req ctrl.Request return reconcile.Result{}, nil } + defer mesh.SaveStatus(ctx, r.Log, r.Client) + // make observations err = r.ObserveFunctionMesh(ctx, req, mesh) if err != nil { diff --git a/controllers/sink.go b/controllers/sink.go index 71c2df75e..bd7cb226c 100644 --- a/controllers/sink.go +++ b/controllers/sink.go @@ -35,8 +35,6 @@ import ( ) func (r *SinkReconciler) ObserveSinkStatefulSet(ctx context.Context, sink *v1alpha1.Sink) error { - defer sink.SaveStatus(ctx, r.Log, r.Client) - statefulSet := &appsv1.StatefulSet{} err := r.Get(ctx, types.NamespacedName{ Namespace: sink.Namespace, @@ -89,8 +87,6 @@ func (r *SinkReconciler) ObserveSinkStatefulSet(ctx context.Context, sink *v1alp } func (r *SinkReconciler) ApplySinkStatefulSet(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { - defer sink.SaveStatus(ctx, r.Log, r.Client) - condition := sink.Status.Conditions[v1alpha1.StatefulSet] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil @@ -118,8 +114,6 @@ func (r *SinkReconciler) ApplySinkStatefulSet(ctx context.Context, sink *v1alpha } func (r *SinkReconciler) ObserveSinkService(ctx context.Context, sink *v1alpha1.Sink) error { - defer sink.SaveStatus(ctx, r.Log, r.Client) - svc := &corev1.Service{} svcName := spec.MakeHeadlessServiceName(spec.MakeSinkObjectMeta(sink).Name) err := r.Get(ctx, types.NamespacedName{Namespace: sink.Namespace, @@ -147,8 +141,6 @@ func (r *SinkReconciler) ObserveSinkService(ctx context.Context, sink *v1alpha1. } func (r *SinkReconciler) ApplySinkService(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { - defer sink.SaveStatus(ctx, r.Log, r.Client) - condition := sink.Status.Conditions[v1alpha1.Service] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil @@ -176,8 +168,6 @@ func (r *SinkReconciler) ApplySinkService(ctx context.Context, sink *v1alpha1.Si } func (r *SinkReconciler) ObserveSinkHPA(ctx context.Context, sink *v1alpha1.Sink) error { - defer sink.SaveStatus(ctx, r.Log, r.Client) - if sink.Spec.MaxReplicas == nil { // HPA not enabled, skip further action delete(sink.Status.Conditions, v1alpha1.HPA) @@ -217,8 +207,6 @@ func (r *SinkReconciler) ObserveSinkHPA(ctx context.Context, sink *v1alpha1.Sink } func (r *SinkReconciler) ApplySinkHPA(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { - defer sink.SaveStatus(ctx, r.Log, r.Client) - if sink.Spec.MaxReplicas == nil { // HPA not enabled, clear the exists HPA hpa := &autov2beta2.HorizontalPodAutoscaler{} @@ -263,8 +251,6 @@ func (r *SinkReconciler) ApplySinkHPA(ctx context.Context, sink *v1alpha1.Sink, } func (r *SinkReconciler) ObserveSinkVPA(ctx context.Context, sink *v1alpha1.Sink) error { - defer sink.SaveStatus(ctx, r.Log, r.Client) - if sink.Spec.Pod.VPA == nil { delete(sink.Status.Conditions, v1alpha1.VPA) return nil @@ -305,8 +291,6 @@ func (r *SinkReconciler) ObserveSinkVPA(ctx context.Context, sink *v1alpha1.Sink } func (r *SinkReconciler) ApplySinkVPA(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { - defer sink.SaveStatus(ctx, r.Log, r.Client) - if sink.Spec.Pod.VPA == nil { // VPA not enabled, clear the exists VPA vpa := &vpav1.VerticalPodAutoscaler{} @@ -351,7 +335,6 @@ func (r *SinkReconciler) ApplySinkVPA(ctx context.Context, sink *v1alpha1.Sink, } func (r *SinkReconciler) UpdateObservedGeneration(ctx context.Context, sink *v1alpha1.Sink) { - defer sink.SaveStatus(ctx, r.Log, r.Client) sink.Status.ObservedGeneration = sink.Generation } diff --git a/controllers/sink_controller.go b/controllers/sink_controller.go index 2b9b2f5ad..268a217a0 100644 --- a/controllers/sink_controller.go +++ b/controllers/sink_controller.go @@ -73,6 +73,8 @@ func (r *SinkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. return reconcile.Result{}, nil } + defer sink.SaveStatus(ctx, r.Log, r.Client) + err = r.ObserveSinkStatefulSet(ctx, sink) if err != nil { return reconcile.Result{}, err diff --git a/controllers/source.go b/controllers/source.go index 1f7097aba..7e872e706 100644 --- a/controllers/source.go +++ b/controllers/source.go @@ -35,8 +35,6 @@ import ( ) func (r *SourceReconciler) ObserveSourceStatefulSet(ctx context.Context, source *v1alpha1.Source) error { - defer source.SaveStatus(ctx, r.Log, r.Client) - statefulSet := &appsv1.StatefulSet{} err := r.Get(ctx, types.NamespacedName{ Namespace: source.Namespace, @@ -89,8 +87,6 @@ func (r *SourceReconciler) ObserveSourceStatefulSet(ctx context.Context, source } func (r *SourceReconciler) ApplySourceStatefulSet(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { - defer source.SaveStatus(ctx, r.Log, r.Client) - condition := source.Status.Conditions[v1alpha1.StatefulSet] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil @@ -118,8 +114,6 @@ func (r *SourceReconciler) ApplySourceStatefulSet(ctx context.Context, source *v } func (r *SourceReconciler) ObserveSourceService(ctx context.Context, source *v1alpha1.Source) error { - defer source.SaveStatus(ctx, r.Log, r.Client) - svc := &corev1.Service{} svcName := spec.MakeHeadlessServiceName(spec.MakeSourceObjectMeta(source).Name) err := r.Get(ctx, types.NamespacedName{Namespace: source.Namespace, @@ -147,8 +141,6 @@ func (r *SourceReconciler) ObserveSourceService(ctx context.Context, source *v1a } func (r *SourceReconciler) ApplySourceService(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { - defer source.SaveStatus(ctx, r.Log, r.Client) - condition := source.Status.Conditions[v1alpha1.Service] if condition.Status == metav1.ConditionTrue && !newGeneration { return nil @@ -176,8 +168,6 @@ func (r *SourceReconciler) ApplySourceService(ctx context.Context, source *v1alp } func (r *SourceReconciler) ObserveSourceHPA(ctx context.Context, source *v1alpha1.Source) error { - defer source.SaveStatus(ctx, r.Log, r.Client) - if source.Spec.MaxReplicas == nil { // HPA not enabled, skip further action delete(source.Status.Conditions, v1alpha1.HPA) @@ -217,8 +207,6 @@ func (r *SourceReconciler) ObserveSourceHPA(ctx context.Context, source *v1alpha } func (r *SourceReconciler) ApplySourceHPA(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { - defer source.SaveStatus(ctx, r.Log, r.Client) - if source.Spec.MaxReplicas == nil { // HPA not enabled, clear the exists HPA hpa := &autov2beta2.HorizontalPodAutoscaler{} @@ -263,8 +251,6 @@ func (r *SourceReconciler) ApplySourceHPA(ctx context.Context, source *v1alpha1. } func (r *SourceReconciler) ObserveSourceVPA(ctx context.Context, source *v1alpha1.Source) error { - defer source.SaveStatus(ctx, r.Log, r.Client) - if source.Spec.Pod.VPA == nil { delete(source.Status.Conditions, v1alpha1.VPA) return nil @@ -305,8 +291,6 @@ func (r *SourceReconciler) ObserveSourceVPA(ctx context.Context, source *v1alpha } func (r *SourceReconciler) ApplySourceVPA(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { - defer source.SaveStatus(ctx, r.Log, r.Client) - if source.Spec.Pod.VPA == nil { // VPA not enabled, clear the exists VPA vpa := &vpav1.VerticalPodAutoscaler{} @@ -351,7 +335,6 @@ func (r *SourceReconciler) ApplySourceVPA(ctx context.Context, source *v1alpha1. } func (r *SourceReconciler) UpdateObservedGeneration(ctx context.Context, source *v1alpha1.Source) { - defer source.SaveStatus(ctx, r.Log, r.Client) source.Status.ObservedGeneration = source.Generation } diff --git a/controllers/source_controller.go b/controllers/source_controller.go index 0d141571b..82a9019d1 100644 --- a/controllers/source_controller.go +++ b/controllers/source_controller.go @@ -73,6 +73,8 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return reconcile.Result{}, nil } + defer source.SaveStatus(ctx, r.Log, r.Client) + err = r.ObserveSourceStatefulSet(ctx, source) if err != nil { return reconcile.Result{}, err From 104b6d39104596382feded65e82133a3daf99bad Mon Sep 17 00:00:00 2001 From: laminar Date: Tue, 10 Jan 2023 19:55:24 +0800 Subject: [PATCH 6/8] update Signed-off-by: laminar --- controllers/function.go | 24 ++++++++++++------------ controllers/function_controller.go | 17 ++++++----------- controllers/function_mesh.go | 8 +++----- controllers/functionmesh_controller.go | 11 +++-------- controllers/sink.go | 24 ++++++++++++------------ controllers/sink_controller.go | 17 ++++++----------- controllers/source.go | 24 ++++++++++++------------ controllers/source_controller.go | 17 ++++++----------- 8 files changed, 60 insertions(+), 82 deletions(-) diff --git a/controllers/function.go b/controllers/function.go index ed14dd703..c77c5d502 100644 --- a/controllers/function.go +++ b/controllers/function.go @@ -86,9 +86,10 @@ func (r *FunctionReconciler) ObserveFunctionStatefulSet(ctx context.Context, fun return nil } -func (r *FunctionReconciler) ApplyFunctionStatefulSet(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { +func (r *FunctionReconciler) ApplyFunctionStatefulSet(ctx context.Context, function *v1alpha1.Function) error { condition := function.Status.Conditions[v1alpha1.StatefulSet] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + function.Generation == function.Status.ObservedGeneration { return nil } @@ -140,9 +141,10 @@ func (r *FunctionReconciler) ObserveFunctionService(ctx context.Context, functio return nil } -func (r *FunctionReconciler) ApplyFunctionService(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { +func (r *FunctionReconciler) ApplyFunctionService(ctx context.Context, function *v1alpha1.Function) error { condition := function.Status.Conditions[v1alpha1.Service] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + function.Generation == function.Status.ObservedGeneration { return nil } @@ -206,7 +208,7 @@ func (r *FunctionReconciler) ObserveFunctionHPA(ctx context.Context, function *v return nil } -func (r *FunctionReconciler) ApplyFunctionHPA(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { +func (r *FunctionReconciler) ApplyFunctionHPA(ctx context.Context, function *v1alpha1.Function) error { if function.Spec.MaxReplicas == nil { // HPA not enabled, clear the exists HPA hpa := &autov2beta2.HorizontalPodAutoscaler{} @@ -225,7 +227,8 @@ func (r *FunctionReconciler) ApplyFunctionHPA(ctx context.Context, function *v1a } condition := function.Status.Conditions[v1alpha1.HPA] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + function.Generation == function.Status.ObservedGeneration { return nil } @@ -290,7 +293,7 @@ func (r *FunctionReconciler) ObserveFunctionVPA(ctx context.Context, function *v return nil } -func (r *FunctionReconciler) ApplyFunctionVPA(ctx context.Context, function *v1alpha1.Function, newGeneration bool) error { +func (r *FunctionReconciler) ApplyFunctionVPA(ctx context.Context, function *v1alpha1.Function) error { if function.Spec.Pod.VPA == nil { // VPA not enabled, clear the exists VPA vpa := &vpav1.VerticalPodAutoscaler{} @@ -309,7 +312,8 @@ func (r *FunctionReconciler) ApplyFunctionVPA(ctx context.Context, function *v1a } condition := function.Status.Conditions[v1alpha1.VPA] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + function.Generation == function.Status.ObservedGeneration { return nil } @@ -334,10 +338,6 @@ func (r *FunctionReconciler) ApplyFunctionVPA(ctx context.Context, function *v1a return nil } -func (r *FunctionReconciler) UpdateObservedGeneration(ctx context.Context, function *v1alpha1.Function) { - function.Status.ObservedGeneration = function.Generation -} - func (r *FunctionReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.StatefulSet, function *v1alpha1.Function) bool { return !spec.CheckIfStatefulSetSpecIsEqual(&statefulSet.Spec, &spec.MakeFunctionStatefulSet(function).Spec) } diff --git a/controllers/function_controller.go b/controllers/function_controller.go index 4b42d2bb1..f824eaf0d 100644 --- a/controllers/function_controller.go +++ b/controllers/function_controller.go @@ -95,35 +95,30 @@ func (r *FunctionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } } - isNewGeneration := r.checkIfFunctionGenerationsIsIncreased(function) - - err = r.ApplyFunctionStatefulSet(ctx, function, isNewGeneration) + err = r.ApplyFunctionStatefulSet(ctx, function) if err != nil { return reconcile.Result{}, err } - err = r.ApplyFunctionService(ctx, function, isNewGeneration) + err = r.ApplyFunctionService(ctx, function) if err != nil { return reconcile.Result{}, err } - err = r.ApplyFunctionHPA(ctx, function, isNewGeneration) + err = r.ApplyFunctionHPA(ctx, function) if err != nil { return reconcile.Result{}, err } if r.WatchFlags != nil && r.WatchFlags.WatchVPACRDs { - err = r.ApplyFunctionVPA(ctx, function, isNewGeneration) + err = r.ApplyFunctionVPA(ctx, function) if err != nil { return reconcile.Result{}, err } } - r.UpdateObservedGeneration(ctx, function) + // update status.ObservedGeneration when reconciliation succeeds + function.Status.ObservedGeneration = function.Generation return ctrl.Result{}, nil } -func (r *FunctionReconciler) checkIfFunctionGenerationsIsIncreased(function *v1alpha1.Function) bool { - return function.Generation != function.Status.ObservedGeneration -} - func (r *FunctionReconciler) SetupWithManager(mgr ctrl.Manager) error { manager := ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.Function{}). diff --git a/controllers/function_mesh.go b/controllers/function_mesh.go index 19a77c829..4ad991c50 100644 --- a/controllers/function_mesh.go +++ b/controllers/function_mesh.go @@ -243,7 +243,9 @@ func (r *FunctionMeshReconciler) observeMeshes(mesh *v1alpha1.FunctionMesh) { } func (r *FunctionMeshReconciler) UpdateFunctionMesh(ctx context.Context, req ctrl.Request, - mesh *v1alpha1.FunctionMesh, newGeneration bool) error { + mesh *v1alpha1.FunctionMesh) error { + newGeneration := mesh.Generation == mesh.Status.ObservedGeneration + for _, functionSpec := range mesh.Spec.Functions { condition := mesh.Status.FunctionConditions[functionSpec.Name] if !newGeneration && @@ -399,10 +401,6 @@ func makeComponentName(prefix, name string) string { return prefix + "-" + name } -func (r *FunctionMeshReconciler) UpdateObservedGeneration(ctx context.Context, mesh *v1alpha1.FunctionMesh) { - mesh.Status.ObservedGeneration = mesh.Generation -} - func checkComponentsReady(conditions map[v1alpha1.Component]metav1.Condition, isHPAEnabled bool, isVPAEnabled bool) bool { ready := conditions[v1alpha1.StatefulSet].Type == string(v1alpha1.Ready) && conditions[v1alpha1.StatefulSet].Status == metav1.ConditionTrue && diff --git a/controllers/functionmesh_controller.go b/controllers/functionmesh_controller.go index a955fc454..e5f4c714e 100644 --- a/controllers/functionmesh_controller.go +++ b/controllers/functionmesh_controller.go @@ -70,22 +70,17 @@ func (r *FunctionMeshReconciler) Reconcile(ctx context.Context, req ctrl.Request return reconcile.Result{}, err } - isNewGeneration := r.checkIfFunctionMeshGenerationsIsIncreased(mesh) - // apply changes - err = r.UpdateFunctionMesh(ctx, req, mesh, isNewGeneration) + err = r.UpdateFunctionMesh(ctx, req, mesh) if err != nil { return reconcile.Result{}, err } - r.UpdateObservedGeneration(ctx, mesh) + // update status.ObservedGeneration when reconciliation succeeds + mesh.Status.ObservedGeneration = mesh.Generation return ctrl.Result{}, nil } -func (r *FunctionMeshReconciler) checkIfFunctionMeshGenerationsIsIncreased(mesh *v1alpha1.FunctionMesh) bool { - return mesh.Generation != mesh.Status.ObservedGeneration -} - func (r *FunctionMeshReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.FunctionMesh{}). diff --git a/controllers/sink.go b/controllers/sink.go index bd7cb226c..3c7c6dad5 100644 --- a/controllers/sink.go +++ b/controllers/sink.go @@ -86,9 +86,10 @@ func (r *SinkReconciler) ObserveSinkStatefulSet(ctx context.Context, sink *v1alp return nil } -func (r *SinkReconciler) ApplySinkStatefulSet(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { +func (r *SinkReconciler) ApplySinkStatefulSet(ctx context.Context, sink *v1alpha1.Sink) error { condition := sink.Status.Conditions[v1alpha1.StatefulSet] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + sink.Generation == sink.Status.ObservedGeneration { return nil } @@ -140,9 +141,10 @@ func (r *SinkReconciler) ObserveSinkService(ctx context.Context, sink *v1alpha1. return nil } -func (r *SinkReconciler) ApplySinkService(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { +func (r *SinkReconciler) ApplySinkService(ctx context.Context, sink *v1alpha1.Sink) error { condition := sink.Status.Conditions[v1alpha1.Service] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + sink.Generation == sink.Status.ObservedGeneration { return nil } @@ -206,7 +208,7 @@ func (r *SinkReconciler) ObserveSinkHPA(ctx context.Context, sink *v1alpha1.Sink return nil } -func (r *SinkReconciler) ApplySinkHPA(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { +func (r *SinkReconciler) ApplySinkHPA(ctx context.Context, sink *v1alpha1.Sink) error { if sink.Spec.MaxReplicas == nil { // HPA not enabled, clear the exists HPA hpa := &autov2beta2.HorizontalPodAutoscaler{} @@ -225,7 +227,8 @@ func (r *SinkReconciler) ApplySinkHPA(ctx context.Context, sink *v1alpha1.Sink, } condition := sink.Status.Conditions[v1alpha1.HPA] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + sink.Generation == sink.Status.ObservedGeneration { return nil } @@ -290,7 +293,7 @@ func (r *SinkReconciler) ObserveSinkVPA(ctx context.Context, sink *v1alpha1.Sink return nil } -func (r *SinkReconciler) ApplySinkVPA(ctx context.Context, sink *v1alpha1.Sink, newGeneration bool) error { +func (r *SinkReconciler) ApplySinkVPA(ctx context.Context, sink *v1alpha1.Sink) error { if sink.Spec.Pod.VPA == nil { // VPA not enabled, clear the exists VPA vpa := &vpav1.VerticalPodAutoscaler{} @@ -309,7 +312,8 @@ func (r *SinkReconciler) ApplySinkVPA(ctx context.Context, sink *v1alpha1.Sink, } condition := sink.Status.Conditions[v1alpha1.VPA] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + sink.Generation == sink.Status.ObservedGeneration { return nil } @@ -334,10 +338,6 @@ func (r *SinkReconciler) ApplySinkVPA(ctx context.Context, sink *v1alpha1.Sink, return nil } -func (r *SinkReconciler) UpdateObservedGeneration(ctx context.Context, sink *v1alpha1.Sink) { - sink.Status.ObservedGeneration = sink.Generation -} - func (r *SinkReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.StatefulSet, sink *v1alpha1.Sink) bool { return !spec.CheckIfStatefulSetSpecIsEqual(&statefulSet.Spec, &spec.MakeSinkStatefulSet(sink).Spec) } diff --git a/controllers/sink_controller.go b/controllers/sink_controller.go index 268a217a0..c4a8f82f6 100644 --- a/controllers/sink_controller.go +++ b/controllers/sink_controller.go @@ -94,35 +94,30 @@ func (r *SinkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. } } - isNewGeneration := r.checkIfSinkGenerationsIsIncreased(sink) - - err = r.ApplySinkStatefulSet(ctx, sink, isNewGeneration) + err = r.ApplySinkStatefulSet(ctx, sink) if err != nil { return reconcile.Result{}, err } - err = r.ApplySinkService(ctx, sink, isNewGeneration) + err = r.ApplySinkService(ctx, sink) if err != nil { return reconcile.Result{}, err } - err = r.ApplySinkHPA(ctx, sink, isNewGeneration) + err = r.ApplySinkHPA(ctx, sink) if err != nil { return reconcile.Result{}, err } if r.WatchFlags != nil && r.WatchFlags.WatchVPACRDs { - err = r.ApplySinkVPA(ctx, sink, isNewGeneration) + err = r.ApplySinkVPA(ctx, sink) if err != nil { return reconcile.Result{}, err } } - r.UpdateObservedGeneration(ctx, sink) + // update status.ObservedGeneration when reconciliation succeeds + sink.Status.ObservedGeneration = sink.Generation return ctrl.Result{}, nil } -func (r *SinkReconciler) checkIfSinkGenerationsIsIncreased(sink *v1alpha1.Sink) bool { - return sink.Generation != sink.Status.ObservedGeneration -} - func (r *SinkReconciler) SetupWithManager(mgr ctrl.Manager) error { manager := ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.Sink{}). diff --git a/controllers/source.go b/controllers/source.go index 7e872e706..728ff4473 100644 --- a/controllers/source.go +++ b/controllers/source.go @@ -86,9 +86,10 @@ func (r *SourceReconciler) ObserveSourceStatefulSet(ctx context.Context, source return nil } -func (r *SourceReconciler) ApplySourceStatefulSet(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { +func (r *SourceReconciler) ApplySourceStatefulSet(ctx context.Context, source *v1alpha1.Source) error { condition := source.Status.Conditions[v1alpha1.StatefulSet] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + source.Generation == source.Status.ObservedGeneration { return nil } @@ -140,9 +141,10 @@ func (r *SourceReconciler) ObserveSourceService(ctx context.Context, source *v1a return nil } -func (r *SourceReconciler) ApplySourceService(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { +func (r *SourceReconciler) ApplySourceService(ctx context.Context, source *v1alpha1.Source) error { condition := source.Status.Conditions[v1alpha1.Service] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + source.Generation == source.Status.ObservedGeneration { return nil } @@ -206,7 +208,7 @@ func (r *SourceReconciler) ObserveSourceHPA(ctx context.Context, source *v1alpha return nil } -func (r *SourceReconciler) ApplySourceHPA(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { +func (r *SourceReconciler) ApplySourceHPA(ctx context.Context, source *v1alpha1.Source) error { if source.Spec.MaxReplicas == nil { // HPA not enabled, clear the exists HPA hpa := &autov2beta2.HorizontalPodAutoscaler{} @@ -225,7 +227,8 @@ func (r *SourceReconciler) ApplySourceHPA(ctx context.Context, source *v1alpha1. } condition := source.Status.Conditions[v1alpha1.HPA] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + source.Generation == source.Status.ObservedGeneration { return nil } @@ -290,7 +293,7 @@ func (r *SourceReconciler) ObserveSourceVPA(ctx context.Context, source *v1alpha return nil } -func (r *SourceReconciler) ApplySourceVPA(ctx context.Context, source *v1alpha1.Source, newGeneration bool) error { +func (r *SourceReconciler) ApplySourceVPA(ctx context.Context, source *v1alpha1.Source) error { if source.Spec.Pod.VPA == nil { // VPA not enabled, clear the exists VPA vpa := &vpav1.VerticalPodAutoscaler{} @@ -309,7 +312,8 @@ func (r *SourceReconciler) ApplySourceVPA(ctx context.Context, source *v1alpha1. } condition := source.Status.Conditions[v1alpha1.VPA] - if condition.Status == metav1.ConditionTrue && !newGeneration { + if condition.Status == metav1.ConditionTrue && + source.Generation == source.Status.ObservedGeneration { return nil } @@ -334,10 +338,6 @@ func (r *SourceReconciler) ApplySourceVPA(ctx context.Context, source *v1alpha1. return nil } -func (r *SourceReconciler) UpdateObservedGeneration(ctx context.Context, source *v1alpha1.Source) { - source.Status.ObservedGeneration = source.Generation -} - func (r *SourceReconciler) checkIfStatefulSetNeedUpdate(statefulSet *appsv1.StatefulSet, source *v1alpha1.Source) bool { return !spec.CheckIfStatefulSetSpecIsEqual(&statefulSet.Spec, &spec.MakeSourceStatefulSet(source).Spec) } diff --git a/controllers/source_controller.go b/controllers/source_controller.go index 82a9019d1..c8286c61b 100644 --- a/controllers/source_controller.go +++ b/controllers/source_controller.go @@ -94,35 +94,30 @@ func (r *SourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } } - isNewGeneration := r.checkIfSourceGenerationsIsIncreased(source) - - err = r.ApplySourceStatefulSet(ctx, source, isNewGeneration) + err = r.ApplySourceStatefulSet(ctx, source) if err != nil { return reconcile.Result{}, err } - err = r.ApplySourceService(ctx, source, isNewGeneration) + err = r.ApplySourceService(ctx, source) if err != nil { return reconcile.Result{}, err } - err = r.ApplySourceHPA(ctx, source, isNewGeneration) + err = r.ApplySourceHPA(ctx, source) if err != nil { return reconcile.Result{}, err } if r.WatchFlags != nil && r.WatchFlags.WatchVPACRDs { - err = r.ApplySourceVPA(ctx, source, isNewGeneration) + err = r.ApplySourceVPA(ctx, source) if err != nil { return reconcile.Result{}, err } } - r.UpdateObservedGeneration(ctx, source) + // update status.ObservedGeneration when reconciliation succeeds + source.Status.ObservedGeneration = source.Generation return ctrl.Result{}, nil } -func (r *SourceReconciler) checkIfSourceGenerationsIsIncreased(source *v1alpha1.Source) bool { - return source.Generation != source.Status.ObservedGeneration -} - func (r *SourceReconciler) SetupWithManager(mgr ctrl.Manager) error { manager := ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.Source{}). From 29947be71223709fca02446b4dbe1b19e7c145d7 Mon Sep 17 00:00:00 2001 From: laminar Date: Wed, 11 Jan 2023 09:58:33 +0800 Subject: [PATCH 7/8] fix hpa condition Signed-off-by: laminar --- controllers/spec/hpa.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/spec/hpa.go b/controllers/spec/hpa.go index 89f2dfbd8..adb2c74d6 100644 --- a/controllers/spec/hpa.go +++ b/controllers/spec/hpa.go @@ -30,11 +30,11 @@ type BuiltinAutoScaler interface { } func isDefaultHPAEnabled(minReplicas, maxReplicas *int32, podPolicy v1alpha1.PodPolicy) bool { - return minReplicas != nil && maxReplicas != nil && podPolicy.AutoScalingBehavior == nil && len(podPolicy.AutoScalingMetrics) == 0 && len(podPolicy.BuiltinAutoscaler) == 0 && *maxReplicas > *minReplicas + return minReplicas != nil && maxReplicas != nil && podPolicy.AutoScalingBehavior == nil && len(podPolicy.AutoScalingMetrics) == 0 && len(podPolicy.BuiltinAutoscaler) == 0 && *maxReplicas >= *minReplicas } func isBuiltinHPAEnabled(minReplicas, maxReplicas *int32, podPolicy v1alpha1.PodPolicy) bool { - return minReplicas != nil && maxReplicas != nil && len(podPolicy.BuiltinAutoscaler) > 0 && *maxReplicas > *minReplicas + return minReplicas != nil && maxReplicas != nil && len(podPolicy.BuiltinAutoscaler) > 0 && *maxReplicas >= *minReplicas } type HPARuleAverageUtilizationCPUPercent struct { From d0a351df9ea4b462625630855ed026fd98a35414 Mon Sep 17 00:00:00 2001 From: laminar Date: Wed, 11 Jan 2023 10:24:40 +0800 Subject: [PATCH 8/8] fix newGeneration condition Signed-off-by: laminar --- controllers/function_mesh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/function_mesh.go b/controllers/function_mesh.go index 4ad991c50..77a8b9a4b 100644 --- a/controllers/function_mesh.go +++ b/controllers/function_mesh.go @@ -244,7 +244,7 @@ func (r *FunctionMeshReconciler) observeMeshes(mesh *v1alpha1.FunctionMesh) { func (r *FunctionMeshReconciler) UpdateFunctionMesh(ctx context.Context, req ctrl.Request, mesh *v1alpha1.FunctionMesh) error { - newGeneration := mesh.Generation == mesh.Status.ObservedGeneration + newGeneration := mesh.Generation != mesh.Status.ObservedGeneration for _, functionSpec := range mesh.Spec.Functions { condition := mesh.Status.FunctionConditions[functionSpec.Name]