diff --git a/.ci/clusters/compute_v1alpha1_function_builtin_hpa.yaml b/.ci/clusters/compute_v1alpha1_function_builtin_hpa.yaml new file mode 100644 index 000000000..dcd2685e3 --- /dev/null +++ b/.ci/clusters/compute_v1alpha1_function_builtin_hpa.yaml @@ -0,0 +1,76 @@ +apiVersion: compute.functionmesh.io/v1alpha1 +kind: Function +metadata: + name: function-builtin-hpa-sample + namespace: default +spec: + image: streamnative/pulsar-functions-java-sample:2.8.0.7 + className: org.apache.pulsar.functions.api.examples.ExclamationFunction + forwardSourceMessageProperty: true + MaxPendingAsyncRequests: 1000 + replicas: 1 + maxReplicas: 5 + logTopic: persistent://public/default/logging-function-logs + input: + topics: + - persistent://public/default/input-java-topic + typeClassName: java.lang.String + output: + topic: persistent://public/default/output-java-topic + typeClassName: java.lang.String + resources: + requests: + cpu: "0.1" + memory: 1G + limits: + cpu: "0.2" + memory: 1.1G + # each secret will be loaded ad an env variable from the `path` secret with the `key` in that secret in the name of `name` + secretsMap: + "name": + path: "test-secret" + key: "username" + "pwd": + path: "test-secret" + key: "password" + pulsar: + pulsarConfig: "test-pulsar" + #authConfig: "test-auth" + java: + jar: /pulsar/examples/api-examples.jar + # to be delete & use admission hook + clusterName: test + autoAck: true + pod: + builtinAutoscaler: + - AverageUtilizationCPUPercent20 + - AverageUtilizationMemoryPercent20 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-pulsar +data: + webServiceURL: http://sn-platform-pulsar-broker.default.svc.cluster.local:8080 + brokerServiceURL: pulsar://sn-platform-pulsar-broker.default.svc.cluster.local:6650 +#--- +#apiVersion: v1 +#kind: ConfigMap +#metadata: +# name: test-auth +#data: +# clientAuthenticationPlugin: "abc" +# clientAuthenticationParameters: "xyz" +# tlsTrustCertsFilePath: "uvw" +# useTls: "true" +# tlsAllowInsecureConnection: "false" +# tlsHostnameVerificationEnable: "true" +--- +apiVersion: v1 +data: + username: YWRtaW4= + password: MWYyZDFlMmU2N2Rm +kind: Secret +metadata: + name: test-secret +type: Opaque diff --git a/.ci/clusters/compute_v1alpha1_function_hpa.yaml b/.ci/clusters/compute_v1alpha1_function_hpa.yaml new file mode 100644 index 000000000..2ce4af9a4 --- /dev/null +++ b/.ci/clusters/compute_v1alpha1_function_hpa.yaml @@ -0,0 +1,80 @@ +apiVersion: compute.functionmesh.io/v1alpha1 +kind: Function +metadata: + name: function-hpa-sample + namespace: default +spec: + image: streamnative/pulsar-functions-java-sample:2.8.0.7 + className: org.apache.pulsar.functions.api.examples.ExclamationFunction + forwardSourceMessageProperty: true + MaxPendingAsyncRequests: 1000 + replicas: 1 + maxReplicas: 5 + logTopic: persistent://public/default/logging-function-logs + input: + topics: + - persistent://public/default/input-java-topic + typeClassName: java.lang.String + output: + topic: persistent://public/default/output-java-topic + typeClassName: java.lang.String + resources: + requests: + cpu: "0.1" + memory: 1G + limits: + cpu: "0.2" + memory: 1.1G + # each secret will be loaded ad an env variable from the `path` secret with the `key` in that secret in the name of `name` + secretsMap: + "name": + path: "test-secret" + key: "username" + "pwd": + path: "test-secret" + key: "password" + pulsar: + pulsarConfig: "test-pulsar" + #authConfig: "test-auth" + java: + jar: /pulsar/examples/api-examples.jar + # to be delete & use admission hook + clusterName: test + autoAck: true + pod: + autoScalingMetrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 50 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-pulsar +data: + webServiceURL: http://sn-platform-pulsar-broker.default.svc.cluster.local:8080 + brokerServiceURL: pulsar://sn-platform-pulsar-broker.default.svc.cluster.local:6650 +#--- +#apiVersion: v1 +#kind: ConfigMap +#metadata: +# name: test-auth +#data: +# clientAuthenticationPlugin: "abc" +# clientAuthenticationParameters: "xyz" +# tlsTrustCertsFilePath: "uvw" +# useTls: "true" +# tlsAllowInsecureConnection: "false" +# tlsHostnameVerificationEnable: "true" +--- +apiVersion: v1 +data: + username: YWRtaW4= + password: MWYyZDFlMmU2N2Rm +kind: Secret +metadata: + name: test-secret +type: Opaque diff --git a/.ci/deploy_pulsar_cluster.sh b/.ci/deploy_pulsar_cluster.sh index 0825dac47..6c4791602 100755 --- a/.ci/deploy_pulsar_cluster.sh +++ b/.ci/deploy_pulsar_cluster.sh @@ -40,6 +40,9 @@ fi # install storage provisioner ci::install_storage_provisioner +# install metrics server +ci::install_metrics_server + # install pulsar chart ci::install_pulsar_charts "$VALUES_FILE" diff --git a/.ci/helm.sh b/.ci/helm.sh index 7e69ac18f..797973b27 100644 --- a/.ci/helm.sh +++ b/.ci/helm.sh @@ -64,6 +64,20 @@ function ci::install_storage_provisioner() { echo "Successfully installed the local storage provisioner." } +function ci::install_metrics_server() { + echo "install metrics-server" + ${KUBECTL} apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.7/components.yaml + ${KUBECTL} patch deployment metrics-server -n kube-system -p '{"spec":{"template":{"spec":{"containers":[{"name":"metrics-server","args":["--cert-dir=/tmp", "--secure-port=4443", "--kubelet-insecure-tls","--kubelet-preferred-address-types=InternalIP"]}]}}}}' + echo "Successfully installed the metrics-server." + WC=$(${KUBECTL} get pods -n kube-system --field-selector=status.phase=Running | grep metrics-server | wc -l) + while [[ ${WC} -lt 1 ]]; do + echo ${WC}; + sleep 20 + ${KUBECTL} get pods -n kube-system + WC=$(${KUBECTL} get pods -n kube-system --field-selector=status.phase=Running | grep metrics-server | wc -l) + done +} + function ci::install_pulsar_charts() { echo "Installing the pulsar charts ..." values=${1:-".ci/clusters/values.yaml"} @@ -126,6 +140,24 @@ function ci::verify_function_mesh() { ${KUBECTL} describe pod -lname=${FUNCTION_NAME} } +function ci::verify_hpa() { + FUNCTION_NAME=$1 + ${KUBECTL} get function + ${KUBECTL} get function ${FUNCTION_NAME} -o yaml + ${KUBECTL} get hpa.v2beta2.autoscaling + ${KUBECTL} get hpa.v2beta2.autoscaling ${FUNCTION_NAME}-function -o yaml + ${KUBECTL} describe hpa.v2beta2.autoscaling ${FUNCTION_NAME}-function + WC=$(${KUBECTL} get hpa.v2beta2.autoscaling ${FUNCTION_NAME}-function -o jsonpath='{.status.conditions[?(@.type=="AbleToScale")].status}' | grep False | wc -l) + while [[ ${WC} -lt 0 ]]; do + echo ${WC}; + sleep 20 + ${KUBECTL} get hpa.v2beta2.autoscaling ${FUNCTION_NAME}-function -o yaml + ${KUBECTL} describe hpa.v2beta2.autoscaling ${FUNCTION_NAME}-function + ${KUBECTL} get hpa.v2beta2.autoscaling ${FUNCTION_NAME}-function -o jsonpath='{.status.conditions[?(@.type=="AbleToScale")].status}' + WC=$(${KUBECTL} get hpa.v2beta2.autoscaling ${FUNCTION_NAME}-function -o jsonpath='{.status.conditions[?(@.type=="AbleToScale")].status}' | grep False | wc -l) + done +} + function ci::test_function_runners() { ${KUBECTL} exec -n ${NAMESPACE} ${CLUSTER}-pulsar-broker-0 -- bin/pulsar-admin functions create --tenant public --namespace default --name test-java --className org.apache.pulsar.functions.api.examples.ExclamationFunction --inputs persistent://public/default/test-java-input --jar /pulsar/examples/api-examples.jar --cpu 0.1 sleep 15 diff --git a/.ci/verify_function_mesh.sh b/.ci/verify_function_mesh.sh index 8e235a633..d09ee5389 100755 --- a/.ci/verify_function_mesh.sh +++ b/.ci/verify_function_mesh.sh @@ -57,4 +57,20 @@ case ${1} in ci::print_function_log functionmesh-sample-python-function ci::verify_mesh_function ;; + compute_v1alpha1_function_hpa) + ci::verify_function_mesh function-hpa-sample + ci::verify_hpa function-hpa-sample + sleep 60 + ci::print_function_log function-hpa-sample + ci::verify_java_function function-hpa-sample + ci::verify_hpa function-hpa-sample + ;; + compute_v1alpha1_function_builtin_hpa) + ci::verify_function_mesh function-builtin-hpa-sample + ci::verify_hpa function-builtin-hpa-sample + sleep 60 + ci::print_function_log function-builtin-hpa-sample + ci::verify_java_function function-builtin-hpa-sample + ci::verify_hpa function-builtin-hpa-sample + ;; esac \ No newline at end of file diff --git a/.github/workflows/test-helm-charts.yml b/.github/workflows/test-helm-charts.yml index 0e44c79e7..8ba540710 100644 --- a/.github/workflows/test-helm-charts.yml +++ b/.github/workflows/test-helm-charts.yml @@ -62,7 +62,7 @@ jobs: if: steps.list-changed.outputs.changed == 'true' - name: Create kind cluster - uses: helm/kind-action@v1.2.0 + run: hack/kind-cluster-build.sh --name chart-testing -c 3 -v 10 if: steps.list-changed.outputs.changed == 'true' with: node_image: kindest/node:v1.15.12 diff --git a/.github/workflows/test-integration-kind-samples.yml b/.github/workflows/test-integration-kind-samples.yml index 2505746a2..b87945cfc 100644 --- a/.github/workflows/test-integration-kind-samples.yml +++ b/.github/workflows/test-integration-kind-samples.yml @@ -63,6 +63,7 @@ jobs: - name: Verify Function kind - Java Function run: | .ci/verify_function_mesh.sh compute_v1alpha1_function + kubectl delete -f .ci/clusters/compute_v1alpha1_function.yaml - name: Test Function kind - Python Function run: | @@ -72,6 +73,7 @@ jobs: - name: Verify Function kind - Python Function run: | .ci/verify_function_mesh.sh compute_v1alpha1_py_function + kubectl delete -f .ci/clusters/compute_v1alpha1_py_function.yaml - name: Test Function kind - Go Function run: | @@ -81,6 +83,7 @@ jobs: - name: Verify Function kind - Go Function run: | .ci/verify_function_mesh.sh compute_v1alpha1_go_function + kubectl delete -f .ci/clusters/compute_v1alpha1_go_function.yaml - name: Test Mesh kind run: | @@ -90,3 +93,24 @@ jobs: - name: Verify Mesh kind run: | .ci/verify_function_mesh.sh compute_v1alpha1_functionmesh + kubectl delete -f .ci/clusters/compute_v1alpha1_functionmesh.yaml + + - name: Test Function HPA + run: | + kubectl apply -f .ci/clusters/compute_v1alpha1_function_hpa.yaml + kubectl get all + + - name: Verify Function HPA + run: | + .ci/verify_function_mesh.sh compute_v1alpha1_function_hpa + kubectl delete -f .ci/clusters/compute_v1alpha1_function_hpa.yaml + + - name: Test Function Builtin HPA + run: | + kubectl apply -f .ci/clusters/compute_v1alpha1_function_builtin_hpa.yaml + kubectl get all + + - name: Verify Function Builtin HPA + run: | + .ci/verify_function_mesh.sh compute_v1alpha1_function_builtin_hpa + kubectl delete -f .ci/clusters/compute_v1alpha1_function_builtin_hpa.yaml diff --git a/api/v1alpha1/common.go b/api/v1alpha1/common.go index 9c7ac7660..f9e542bce 100644 --- a/api/v1alpha1/common.go +++ b/api/v1alpha1/common.go @@ -20,6 +20,7 @@ package v1alpha1 import ( "encoding/json" + 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" @@ -93,6 +94,26 @@ type PodPolicy struct { // ServiceAccountName is the name of the ServiceAccount to use to run this pod. // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` + + // BuiltinAutoscaler refers to the built-in autoscaling rules + // Available values: AverageUtilizationCPUPercent80, AverageUtilizationCPUPercent50, AverageUtilizationCPUPercent20 + // AverageUtilizationMemoryPercent80, AverageUtilizationMemoryPercent50, AverageUtilizationMemoryPercent20 + // +optional + // TODO: validate the rules, user may provide duplicate rules, should check with webhook + BuiltinAutoscaler []BuiltinHPARule `json:"builtinAutoscaler,omitempty"` + + // AutoScalingMetrics contains the specifications for which to use to calculate the + // desired replica count (the maximum replica count across all metrics will + // be used). + // More info: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#metricspec-v2beta2-autoscaling + // +optional + AutoScalingMetrics []autov2beta2.MetricSpec `json:"autoScalingMetrics,omitempty"` + + // AutoScalingBehavior configures the scaling behavior of the target + // in both Up and Down directions (scaleUp and scaleDown fields respectively). + // If not set, the default HPAScalingRules for scale up and scale down are used. + // +optional + AutoScalingBehavior *autov2beta2.HorizontalPodAutoscalerBehavior `json:"autoScalingBehavior,omitempty"` } type Runtime struct { diff --git a/api/v1alpha1/function_types.go b/api/v1alpha1/function_types.go index c5fb89375..a79b73dd7 100644 --- a/api/v1alpha1/function_types.go +++ b/api/v1alpha1/function_types.go @@ -29,11 +29,15 @@ import ( type FunctionSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Name string `json:"name,omitempty"` - ClassName string `json:"className,omitempty"` - Tenant string `json:"tenant,omitempty"` - ClusterName string `json:"clusterName,omitempty"` - Replicas *int32 `json:"replicas,omitempty"` + Name string `json:"name,omitempty"` + ClassName string `json:"className,omitempty"` + Tenant string `json:"tenant,omitempty"` + ClusterName string `json:"clusterName,omitempty"` + Replicas *int32 `json:"replicas,omitempty"` + + // MaxReplicas indicates the maximum number of replicas and enables the HorizontalPodAutoscaler + // If provided, a default HPA with CPU at average of 80% will be used. + // For complex HPA strategies, please refer to Pod.HPAutoscaler. MaxReplicas *int32 `json:"maxReplicas,omitempty"` // if provided, turn on autoscaling Input InputConf `json:"input,omitempty"` Output OutputConf `json:"output,omitempty"` diff --git a/api/v1alpha1/hpa.go b/api/v1alpha1/hpa.go new file mode 100644 index 000000000..07382f7e8 --- /dev/null +++ b/api/v1alpha1/hpa.go @@ -0,0 +1,33 @@ +// 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 contains API Schema definitions for the cloud v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=compute.functionmesh.io +package v1alpha1 + +type BuiltinHPARule string + +const ( + AverageUtilizationCPUPercent80 BuiltinHPARule = "AverageUtilizationCPUPercent80" + AverageUtilizationCPUPercent50 BuiltinHPARule = "AverageUtilizationCPUPercent50" + AverageUtilizationCPUPercent20 BuiltinHPARule = "AverageUtilizationCPUPercent20" + + AverageUtilizationMemoryPercent80 BuiltinHPARule = "AverageUtilizationMemoryPercent80" + AverageUtilizationMemoryPercent50 BuiltinHPARule = "AverageUtilizationMemoryPercent50" + AverageUtilizationMemoryPercent20 BuiltinHPARule = "AverageUtilizationMemoryPercent20" +) diff --git a/api/v1alpha1/sink_types.go b/api/v1alpha1/sink_types.go index a29a5f1b8..1a60a986e 100644 --- a/api/v1alpha1/sink_types.go +++ b/api/v1alpha1/sink_types.go @@ -29,12 +29,16 @@ import ( type SinkSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Name string `json:"name,omitempty"` - ClassName string `json:"className,omitempty"` - ClusterName string `json:"clusterName,omitempty"` - Tenant string `json:"tenant,omitempty"` - SinkType string `json:"sinkType,omitempty"` // refer to `--sink-type` as builtin connector - Replicas *int32 `json:"replicas,omitempty"` + Name string `json:"name,omitempty"` + ClassName string `json:"className,omitempty"` + ClusterName string `json:"clusterName,omitempty"` + Tenant string `json:"tenant,omitempty"` + SinkType string `json:"sinkType,omitempty"` // refer to `--sink-type` as builtin connector + Replicas *int32 `json:"replicas,omitempty"` + + // MaxReplicas indicates the maximum number of replicas and enables the HorizontalPodAutoscaler + // If provided, a default HPA with CPU at average of 80% will be used. + // For complex HPA strategies, please refer to Pod.HPAutoscaler. MaxReplicas *int32 `json:"maxReplicas,omitempty"` // if provided, turn on autoscaling Input InputConf `json:"input,omitempty"` diff --git a/api/v1alpha1/source_types.go b/api/v1alpha1/source_types.go index daba57645..861466a40 100644 --- a/api/v1alpha1/source_types.go +++ b/api/v1alpha1/source_types.go @@ -29,12 +29,16 @@ import ( type SourceSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Name string `json:"name,omitempty"` - ClassName string `json:"className,omitempty"` - Tenant string `json:"tenant,omitempty"` - ClusterName string `json:"clusterName,omitempty"` - SourceType string `json:"sourceType,omitempty"` // refer to `--source-type` as builtin connector - Replicas *int32 `json:"replicas,omitempty"` + Name string `json:"name,omitempty"` + ClassName string `json:"className,omitempty"` + Tenant string `json:"tenant,omitempty"` + ClusterName string `json:"clusterName,omitempty"` + SourceType string `json:"sourceType,omitempty"` // refer to `--source-type` as builtin connector + Replicas *int32 `json:"replicas,omitempty"` + + // MaxReplicas indicates the maximum number of replicas and enables the HorizontalPodAutoscaler + // If provided, a default HPA with CPU at average of 80% will be used. + // For complex HPA strategies, please refer to Pod.HPAutoscaler. MaxReplicas *int32 `json:"maxReplicas,omitempty"` // if provided, turn on autoscaling Output OutputConf `json:"output,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 20a45b24a..f8bcc964d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/api/autoscaling/v2beta2" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -582,6 +583,23 @@ func (in *PodPolicy) DeepCopyInto(out *PodPolicy) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.BuiltinAutoscaler != nil { + in, out := &in.BuiltinAutoscaler, &out.BuiltinAutoscaler + *out = make([]BuiltinHPARule, len(*in)) + copy(*out, *in) + } + if in.AutoScalingMetrics != nil { + in, out := &in.AutoScalingMetrics, &out.AutoScalingMetrics + *out = make([]v2beta2.MetricSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AutoScalingBehavior != nil { + in, out := &in.AutoScalingBehavior, &out.AutoScalingBehavior + *out = new(v2beta2.HorizontalPodAutoscalerBehavior) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodPolicy. diff --git a/charts/function-mesh-operator/crds/compute.functionmesh.io_functionmeshes.yaml b/charts/function-mesh-operator/crds/compute.functionmesh.io_functionmeshes.yaml index 5f561a5df..18cbd7ebd 100644 --- a/charts/function-mesh-operator/crds/compute.functionmesh.io_functionmeshes.yaml +++ b/charts/function-mesh-operator/crds/compute.functionmesh.io_functionmeshes.yaml @@ -478,6 +478,253 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array imagePullSecrets: items: properties: @@ -2372,8 +2619,6 @@ spec: jarLocation: type: string type: object - logTopic: - type: string maxMessageRetry: format: int32 type: integer @@ -2598,23 +2843,237 @@ spec: items: type: string type: array - topologyKey: - type: string - required: - - topologyKey + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object type: object - weight: + required: + - name + type: object + target: + properties: + averageUtilization: format: int32 type: integer + averageValue: + type: string + type: + type: string + value: + type: string required: - - podAffinityTerm - - weight + - type type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: properties: - labelSelector: + name: + type: string + selector: properties: matchExpressions: items: @@ -2637,22 +3096,55 @@ spec: type: string type: object type: object - namespaces: - items: - type: string - type: array - topologyKey: + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: type: string required: - - topologyKey + - type type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array imagePullSecrets: items: properties: @@ -4472,8 +4964,6 @@ spec: jarLocation: type: string type: object - logTopic: - type: string maxReplicas: format: int32 type: integer @@ -4806,6 +5296,253 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array imagePullSecrets: items: properties: diff --git a/charts/function-mesh-operator/crds/compute.functionmesh.io_functions.yaml b/charts/function-mesh-operator/crds/compute.functionmesh.io_functions.yaml index bf21dd5c9..947862775 100644 --- a/charts/function-mesh-operator/crds/compute.functionmesh.io_functions.yaml +++ b/charts/function-mesh-operator/crds/compute.functionmesh.io_functions.yaml @@ -479,6 +479,253 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array imagePullSecrets: items: properties: diff --git a/charts/function-mesh-operator/crds/compute.functionmesh.io_sinks.yaml b/charts/function-mesh-operator/crds/compute.functionmesh.io_sinks.yaml index bf7915149..83f3a3cd7 100644 --- a/charts/function-mesh-operator/crds/compute.functionmesh.io_sinks.yaml +++ b/charts/function-mesh-operator/crds/compute.functionmesh.io_sinks.yaml @@ -134,8 +134,6 @@ spec: jarLocation: type: string type: object - logTopic: - type: string maxMessageRetry: format: int32 type: integer @@ -415,6 +413,253 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array imagePullSecrets: items: properties: diff --git a/charts/function-mesh-operator/crds/compute.functionmesh.io_sources.yaml b/charts/function-mesh-operator/crds/compute.functionmesh.io_sources.yaml index 109615848..1dc29098f 100644 --- a/charts/function-mesh-operator/crds/compute.functionmesh.io_sources.yaml +++ b/charts/function-mesh-operator/crds/compute.functionmesh.io_sources.yaml @@ -56,8 +56,6 @@ spec: jarLocation: type: string type: object - logTopic: - type: string maxReplicas: format: int32 type: integer @@ -390,6 +388,253 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array imagePullSecrets: items: properties: diff --git a/config/crd/bases/compute.functionmesh.io_functionmeshes.yaml b/config/crd/bases/compute.functionmesh.io_functionmeshes.yaml index dfdec7497..b5e263b8d 100644 --- a/config/crd/bases/compute.functionmesh.io_functionmeshes.yaml +++ b/config/crd/bases/compute.functionmesh.io_functionmeshes.yaml @@ -478,6 +478,257 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: + type: string + type: array imagePullSecrets: items: properties: @@ -2596,23 +2847,237 @@ spec: items: type: string type: array - topologyKey: - type: string - required: - - topologyKey + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object type: object - weight: + required: + - name + type: object + target: + properties: + averageUtilization: format: int32 type: integer + averageValue: + type: string + type: + type: string + value: + type: string required: - - podAffinityTerm - - weight + - type type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: properties: - labelSelector: + name: + type: string + selector: properties: matchExpressions: items: @@ -2635,22 +3100,59 @@ spec: type: string type: object type: object - namespaces: - items: - type: string - type: array - topologyKey: + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: type: string required: - - topologyKey + - type type: object - type: array - type: object - type: object - annotations: - additionalProperties: + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: type: string - type: object + type: array imagePullSecrets: items: properties: @@ -4804,6 +5306,257 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: + type: string + type: array imagePullSecrets: items: properties: diff --git a/config/crd/bases/compute.functionmesh.io_functions.yaml b/config/crd/bases/compute.functionmesh.io_functions.yaml index bf21dd5c9..e7fc91d48 100644 --- a/config/crd/bases/compute.functionmesh.io_functions.yaml +++ b/config/crd/bases/compute.functionmesh.io_functions.yaml @@ -479,6 +479,257 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: + type: string + type: array imagePullSecrets: items: properties: diff --git a/config/crd/bases/compute.functionmesh.io_sinks.yaml b/config/crd/bases/compute.functionmesh.io_sinks.yaml index 652705200..c64d10a3f 100644 --- a/config/crd/bases/compute.functionmesh.io_sinks.yaml +++ b/config/crd/bases/compute.functionmesh.io_sinks.yaml @@ -413,6 +413,257 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: + type: string + type: array imagePullSecrets: items: properties: diff --git a/config/crd/bases/compute.functionmesh.io_sources.yaml b/config/crd/bases/compute.functionmesh.io_sources.yaml index 006d7cf83..b3b22344f 100644 --- a/config/crd/bases/compute.functionmesh.io_sources.yaml +++ b/config/crd/bases/compute.functionmesh.io_sources.yaml @@ -390,6 +390,257 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: + type: string + type: array imagePullSecrets: items: properties: diff --git a/controllers/function.go b/controllers/function.go index ea888b8c4..9d12f3331 100644 --- a/controllers/function.go +++ b/controllers/function.go @@ -19,11 +19,14 @@ package controllers import ( "context" + "reflect" + + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/streamnative/function-mesh/api/v1alpha1" "github.com/streamnative/function-mesh/controllers/spec" appsv1 "k8s.io/api/apps/v1" - autov1 "k8s.io/api/autoscaling/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" @@ -186,7 +189,7 @@ func (r *FunctionReconciler) ObserveFunctionHPA(ctx context.Context, req ctrl.Re return nil } - hpa := &autov1.HorizontalPodAutoscaler{} + hpa := &autov2beta2.HorizontalPodAutoscaler{} err := r.Get(ctx, types.NamespacedName{Namespace: function.Namespace, Name: spec.MakeFunctionObjectMeta(function).Name}, hpa) if err != nil { @@ -197,6 +200,17 @@ func (r *FunctionReconciler) ObserveFunctionHPA(ctx context.Context, req ctrl.Re return err } + if hpa.Spec.MaxReplicas != *function.Spec.MaxReplicas || + !reflect.DeepEqual(hpa.Spec.Metrics, function.Spec.Pod.AutoScalingMetrics) || + (function.Spec.Pod.AutoScalingBehavior != nil && hpa.Spec.Behavior == nil) || + (function.Spec.Pod.AutoScalingBehavior != nil && hpa.Spec.Behavior != nil && + !reflect.DeepEqual(*hpa.Spec.Behavior, *function.Spec.Pod.AutoScalingBehavior)) { + condition.Status = metav1.ConditionFalse + condition.Action = v1alpha1.Update + function.Status.Conditions[v1alpha1.HPA] = condition + return nil + } + condition.Action = v1alpha1.NoAction condition.Status = metav1.ConditionTrue function.Status.Conditions[v1alpha1.HPA] = condition @@ -223,7 +237,36 @@ func (r *FunctionReconciler) ApplyFunctionHPA(ctx context.Context, req ctrl.Requ r.Log.Error(err, "failed to create pod autoscaler for function", "name", function.Name) return err } - case v1alpha1.Wait: + key, _ := client.ObjectKeyFromObject(hpa) + r.Get(context.Background(), key, hpa) + case v1alpha1.Update: + hpa := &autov2beta2.HorizontalPodAutoscaler{} + err := r.Get(ctx, types.NamespacedName{Namespace: function.Namespace, + Name: spec.MakeFunctionObjectMeta(function).Name}, hpa) + if err != nil { + r.Log.Error(err, "failed to update pod autoscaler for function, cannot find hpa", "name", function.Name) + return err + } + if hpa.Spec.MaxReplicas != *function.Spec.MaxReplicas { + hpa.Spec.MaxReplicas = *function.Spec.MaxReplicas + } + if len(function.Spec.Pod.AutoScalingMetrics) > 0 && !reflect.DeepEqual(hpa.Spec.Metrics, function.Spec.Pod.AutoScalingMetrics) { + hpa.Spec.Metrics = function.Spec.Pod.AutoScalingMetrics + } + if function.Spec.Pod.AutoScalingBehavior != nil { + hpa.Spec.Behavior = function.Spec.Pod.AutoScalingBehavior + } + if len(function.Spec.Pod.BuiltinAutoscaler) > 0 { + metrics := spec.MakeMetricsFromBuiltinHPARules(function.Spec.Pod.BuiltinAutoscaler) + if !reflect.DeepEqual(hpa.Spec.Metrics, metrics) { + hpa.Spec.Metrics = metrics + } + } + if err := r.Update(ctx, hpa); err != nil { + r.Log.Error(err, "failed to update pod autoscaler for function", "name", function.Name) + return err + } + case v1alpha1.Wait, v1alpha1.NoAction: // do nothing } diff --git a/controllers/function_controller.go b/controllers/function_controller.go index 83669558a..2d4f51adb 100644 --- a/controllers/function_controller.go +++ b/controllers/function_controller.go @@ -23,7 +23,7 @@ import ( "github.com/go-logr/logr" "github.com/streamnative/function-mesh/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" - autov1 "k8s.io/api/autoscaling/v1" + autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -106,7 +106,7 @@ func (r *FunctionReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&v1alpha1.Function{}). Owns(&appsv1.StatefulSet{}). Owns(&corev1.Service{}). - Owns(&autov1.HorizontalPodAutoscaler{}). + Owns(&autov2beta2.HorizontalPodAutoscaler{}). Owns(&corev1.Secret{}). Complete(r) } diff --git a/controllers/function_controller_test.go b/controllers/function_controller_test.go index 3c25ea1b4..ac9e8d7c8 100644 --- a/controllers/function_controller_test.go +++ b/controllers/function_controller_test.go @@ -19,107 +19,203 @@ package controllers import ( "context" + "time" + + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/types" + + autov2beta2 "k8s.io/api/autoscaling/v2beta2" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/streamnative/function-mesh/api/v1alpha1" "github.com/streamnative/function-mesh/controllers/spec" v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var log = logf.Log.WithName("function-resource-test") + +const ( + timeout = time.Second * 10 + interval = time.Millisecond * 250 ) var _ = Describe("Function Controller", func() { Context("Simple Function Item", func() { - configs := makeSamplePulsarConfig() function := makeFunctionSample(TestFunctionName) - createFunctionConfigMap(configs) createFunction(function) - deleteFunction(function) - deleteFunctionConfigMap(configs) }) }) var _ = Describe("Function Controller (E2E)", func() { Context("Function With E2E Crypto Item", func() { - cryptosecrets := &v1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "java-function-crypto-sample-crypto-secret", - Namespace: "default", - UID: "dead-beef-secret", - }, - Data: map[string][]byte{ - "test_ecdsa_privkey.pem": []byte{0x00, 0x01, 0x02}, - "test_ecdsa_pubkey.pem": []byte{0x02, 0x01, 0x00}, - }, - Type: "Opaque", - } - configs := makeSamplePulsarConfig() function := makeFunctionSampleWithCryptoEnabled() - createFunctionConfigMap(configs) - createFunctionSecret(cryptosecrets) createFunction(function) - deleteFunction(function) - deleteFunctionSecret(cryptosecrets) - deleteFunctionConfigMap(configs) }) }) var _ = Describe("Function Controller (Batcher)", func() { Context("Function With Batcher Item", func() { - configs := makeSamplePulsarConfig() function := makeFunctionSampleWithKeyBasedBatcher() - createFunctionConfigMap(configs) createFunction(function) - deleteFunction(function) - deleteFunctionConfigMap(configs) }) }) -func createFunction(function *v1alpha1.Function) { - if function.Status.Conditions == nil { - function.Status.Conditions = make(map[v1alpha1.Component]v1alpha1.ResourceCondition) - } +var _ = Describe("Function Controller (HPA)", func() { + Context("Simple Function Item with HPA", func() { + function := makeFunctionSample(TestFunctionHPAName) + cpuPercentage := int32(20) + function.Spec.Pod.AutoScalingMetrics = []autov2beta2.MetricSpec{ + { + Type: autov2beta2.ResourceMetricSourceType, + Resource: &autov2beta2.ResourceMetricSource{ + Name: v1.ResourceCPU, + Target: autov2beta2.MetricTarget{ + Type: autov2beta2.UtilizationMetricType, + AverageUtilization: &cpuPercentage, + }, + }, + }, + { + Type: autov2beta2.ResourceMetricSourceType, + Resource: &autov2beta2.ResourceMetricSource{ + Name: v1.ResourceMemory, + Target: autov2beta2.MetricTarget{ + Type: autov2beta2.UtilizationMetricType, + AverageUtilization: &cpuPercentage, + }, + }, + }, + } - It("StatefulSet should be created", func() { - statefulSet := spec.MakeFunctionStatefulSet(function) - Expect(k8sClient.Create(context.Background(), statefulSet)).Should(Succeed()) + createFunction(function) }) -} +}) -func createFunctionConfigMap(configs *v1.ConfigMap) { - It("Should create pulsar configmap successfully", func() { - Expect(k8sClient.Create(context.Background(), configs)).Should(Succeed()) +var _ = Describe("Function Controller (builtin HPA)", func() { + Context("Simple Function Item with builtin HPA", func() { + r := int32(100) + function := makeFunctionSample(TestFunctionBuiltinHPAName) + function.Spec.MaxReplicas = &r + function.Spec.Pod.BuiltinAutoscaler = []v1alpha1.BuiltinHPARule{ + v1alpha1.AverageUtilizationCPUPercent20, + v1alpha1.AverageUtilizationMemoryPercent20, + } + + createFunction(function) }) -} +}) -func createFunctionSecret(secret *v1.Secret) { - It("Should create crypto secret successfully", func() { - Expect(k8sClient.Create(context.Background(), secret)).Should(Succeed()) +func createFunction(function *v1alpha1.Function) { + + It("Function should be created", func() { + Eventually(func() bool { + err := k8sClient.Create(context.Background(), function) + return err == nil + }, timeout, interval).Should(BeTrue()) }) -} -func deleteFunction(function *v1alpha1.Function) { - It("StatefulSet should be deleted", func() { - statefulSet := spec.MakeFunctionStatefulSet(function) - Expect(k8sClient.Delete(context.Background(), statefulSet)).Should(Succeed()) + It("StatefulSet should be created", func() { + statefulSet := &appsv1.StatefulSet{} + + Eventually(func() bool { + err := k8sClient.Get(context.Background(), types.NamespacedName{ + Namespace: function.Namespace, + Name: spec.MakeFunctionObjectMeta(function).Name, + }, statefulSet) + return err == nil + }, timeout, interval).Should(BeTrue()) + + Expect(statefulSet.Name).Should(Equal(spec.MakeFunctionObjectMeta(function).Name)) + Expect(*statefulSet.Spec.Replicas).Should(Equal(int32(1))) }) -} -func deleteFunctionConfigMap(configs *v1.ConfigMap) { - It("Should create pulsar configmap successfully", func() { - Expect(k8sClient.Delete(context.Background(), configs)).Should(Succeed()) + It("Service should be created", func() { + srv := &v1.Service{} + svcName := spec.MakeHeadlessServiceName(spec.MakeFunctionObjectMeta(function).Name) + Eventually(func() bool { + err := k8sClient.Get(context.Background(), types.NamespacedName{Namespace: function.Namespace, + Name: svcName}, srv) + return err == nil + }, timeout, interval).Should(BeTrue()) }) -} -func deleteFunctionSecret(secret *v1.Secret) { - It("Should delete crypto secret successfully", func() { - Expect(k8sClient.Delete(context.Background(), secret)).Should(Succeed()) + It("HPA should be created", func() { + if function.Spec.MaxReplicas != nil { + hpa := &autov2beta2.HorizontalPodAutoscaler{} + Eventually(func() bool { + err := k8sClient.Get(context.Background(), types.NamespacedName{Namespace: function.Namespace, + Name: spec.MakeFunctionObjectMeta(function).Name}, hpa) + return err == nil + }, timeout, interval).Should(BeTrue()) + + log.Info("HPA should be created", "hpa", hpa, "name", spec.MakeFunctionObjectMeta(function).Name) + + if len(function.Spec.Pod.AutoScalingMetrics) > 0 { + Expect(len(hpa.Spec.Metrics)).Should(Equal(len(function.Spec.Pod.AutoScalingMetrics))) + for _, metric := range function.Spec.Pod.AutoScalingMetrics { + Expect(hpa.Spec.Metrics).Should(ContainElement(metric)) + } + } + + if len(function.Spec.Pod.BuiltinAutoscaler) > 0 { + Expect(len(hpa.Spec.Metrics)).Should(Equal(len(function.Spec.Pod.BuiltinAutoscaler))) + for _, rule := range function.Spec.Pod.BuiltinAutoscaler { + autoscaler := spec.GetBuiltinAutoScaler(rule) + Expect(autoscaler).Should(Not(BeNil())) + Expect(hpa.Spec.Metrics).Should(ContainElement(autoscaler.Metrics()[0])) + } + } + } + }) + + It("Function should be deleted", func() { + key, err := client.ObjectKeyFromObject(function) + Expect(err).To(Succeed()) + + log.Info("deleting resource", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + Expect(k8sClient.Delete(context.Background(), function)).To(Succeed()) + + log.Info("waiting for resource to disappear", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + Eventually(func() error { + return k8sClient.Get(context.Background(), key, function) + }, timeout, interval).Should(HaveOccurred()) + log.Info("deleted resource", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + + statefulsets := new(appsv1.StatefulSetList) + err = k8sClient.List(context.Background(), statefulsets, client.InNamespace(function.Namespace)) + Expect(err).Should(BeNil()) + for _, item := range statefulsets.Items { + log.Info("deleting statefulset resource", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + Expect(k8sClient.Delete(context.Background(), &item)).To(Succeed()) + } + log.Info("waiting for StatefulSet resource to disappear", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + Eventually(func() bool { + err := k8sClient.List(context.Background(), statefulsets, client.InNamespace(function.Namespace)) + return err == nil && len(statefulsets.Items) == 0 + }, timeout, interval).Should(BeTrue()) + log.Info("StatefulSet resource deleted", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + + hpas := new(autov2beta2.HorizontalPodAutoscalerList) + err = k8sClient.List(context.Background(), hpas, client.InNamespace(function.Namespace)) + Expect(err).Should(BeNil()) + for _, item := range hpas.Items { + log.Info("deleting HPA resource", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + log.Info("deleting HPA", "item", item) + Expect(k8sClient.Delete(context.Background(), &item)).To(Succeed()) + } + log.Info("waiting for HPA resource to disappear", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + Eventually(func() bool { + err := k8sClient.List(context.Background(), hpas, client.InNamespace(function.Namespace)) + return err == nil && len(hpas.Items) == 0 + }, timeout, interval).Should(BeTrue()) + log.Info("HPA resource deleted", "namespace", key.Namespace, "name", key.Name, "test", CurrentGinkgoTestDescription().FullTestText) + }) } diff --git a/controllers/sink.go b/controllers/sink.go index 965a36c54..ea5499b38 100644 --- a/controllers/sink.go +++ b/controllers/sink.go @@ -19,11 +19,12 @@ package controllers import ( "context" + "reflect" "github.com/streamnative/function-mesh/api/v1alpha1" "github.com/streamnative/function-mesh/controllers/spec" appsv1 "k8s.io/api/apps/v1" - autov1 "k8s.io/api/autoscaling/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" @@ -166,7 +167,7 @@ func (r *SinkReconciler) ObserveSinkHPA(ctx context.Context, req ctrl.Request, s Action: v1alpha1.NoAction, } - hpa := &autov1.HorizontalPodAutoscaler{} + hpa := &autov2beta2.HorizontalPodAutoscaler{} err := r.Get(ctx, types.NamespacedName{Namespace: sink.Namespace, Name: spec.MakeSinkObjectMeta(sink).Name}, hpa) if err != nil { @@ -182,6 +183,17 @@ func (r *SinkReconciler) ObserveSinkHPA(ctx context.Context, req ctrl.Request, s condition.Status = metav1.ConditionTrue } + if hpa.Spec.MaxReplicas != *sink.Spec.MaxReplicas || + !reflect.DeepEqual(hpa.Spec.Metrics, sink.Spec.Pod.AutoScalingMetrics) || + (sink.Spec.Pod.AutoScalingBehavior != nil && hpa.Spec.Behavior == nil) || + (sink.Spec.Pod.AutoScalingBehavior != nil && hpa.Spec.Behavior != nil && + !reflect.DeepEqual(*hpa.Spec.Behavior, *sink.Spec.Pod.AutoScalingBehavior)) { + condition.Status = metav1.ConditionFalse + condition.Action = v1alpha1.Update + sink.Status.Conditions[v1alpha1.HPA] = condition + return nil + } + sink.Status.Conditions[v1alpha1.HPA] = condition return nil } @@ -205,6 +217,33 @@ func (r *SinkReconciler) ApplySinkHPA(ctx context.Context, req ctrl.Request, sin r.Log.Error(err, "failed to create pod autoscaler for sink", "name", sink.Name) return err } + case v1alpha1.Update: + hpa := &autov2beta2.HorizontalPodAutoscaler{} + err := r.Get(ctx, types.NamespacedName{Namespace: sink.Namespace, + Name: spec.MakeSinkObjectMeta(sink).Name}, hpa) + if err != nil { + r.Log.Error(err, "failed to update pod autoscaler for sink, cannot find hpa", "name", sink.Name) + return err + } + if hpa.Spec.MaxReplicas != *sink.Spec.MaxReplicas { + hpa.Spec.MaxReplicas = *sink.Spec.MaxReplicas + } + if len(sink.Spec.Pod.AutoScalingMetrics) > 0 && !reflect.DeepEqual(hpa.Spec.Metrics, sink.Spec.Pod.AutoScalingMetrics) { + hpa.Spec.Metrics = sink.Spec.Pod.AutoScalingMetrics + } + if sink.Spec.Pod.AutoScalingBehavior != nil { + hpa.Spec.Behavior = sink.Spec.Pod.AutoScalingBehavior + } + if len(sink.Spec.Pod.BuiltinAutoscaler) > 0 { + metrics := spec.MakeMetricsFromBuiltinHPARules(sink.Spec.Pod.BuiltinAutoscaler) + if !reflect.DeepEqual(hpa.Spec.Metrics, metrics) { + hpa.Spec.Metrics = metrics + } + } + if err := r.Update(ctx, hpa); err != nil { + r.Log.Error(err, "failed to update pod autoscaler for sink", "name", sink.Name) + return err + } case v1alpha1.Wait, v1alpha1.NoAction: // do nothing } diff --git a/controllers/sink_controller.go b/controllers/sink_controller.go index e78ec8727..708663467 100644 --- a/controllers/sink_controller.go +++ b/controllers/sink_controller.go @@ -23,7 +23,7 @@ import ( "github.com/go-logr/logr" computev1alpha1 "github.com/streamnative/function-mesh/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" - autov1 "k8s.io/api/autoscaling/v1" + autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -104,6 +104,6 @@ func (r *SinkReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&computev1alpha1.Sink{}). Owns(&appsv1.StatefulSet{}). Owns(&corev1.Service{}). - Owns(&autov1.HorizontalPodAutoscaler{}). + Owns(&autov2beta2.HorizontalPodAutoscaler{}). Complete(r) } diff --git a/controllers/source.go b/controllers/source.go index 79f904a93..2068004f2 100644 --- a/controllers/source.go +++ b/controllers/source.go @@ -19,11 +19,12 @@ package controllers import ( "context" + "reflect" "github.com/streamnative/function-mesh/api/v1alpha1" "github.com/streamnative/function-mesh/controllers/spec" appsv1 "k8s.io/api/apps/v1" - autov1 "k8s.io/api/autoscaling/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" @@ -168,7 +169,7 @@ func (r *SourceReconciler) ObserveSourceHPA(ctx context.Context, req ctrl.Reques Action: v1alpha1.NoAction, } - hpa := &autov1.HorizontalPodAutoscaler{} + hpa := &autov2beta2.HorizontalPodAutoscaler{} err := r.Get(ctx, types.NamespacedName{Namespace: source.Namespace, Name: spec.MakeSourceObjectMeta(source).Name}, hpa) if err != nil { @@ -184,6 +185,17 @@ func (r *SourceReconciler) ObserveSourceHPA(ctx context.Context, req ctrl.Reques condition.Status = metav1.ConditionTrue } + if hpa.Spec.MaxReplicas != *source.Spec.MaxReplicas || + !reflect.DeepEqual(hpa.Spec.Metrics, source.Spec.Pod.AutoScalingMetrics) || + (source.Spec.Pod.AutoScalingBehavior != nil && hpa.Spec.Behavior == nil) || + (source.Spec.Pod.AutoScalingBehavior != nil && hpa.Spec.Behavior != nil && + !reflect.DeepEqual(*hpa.Spec.Behavior, *source.Spec.Pod.AutoScalingBehavior)) { + condition.Status = metav1.ConditionFalse + condition.Action = v1alpha1.Update + source.Status.Conditions[v1alpha1.HPA] = condition + return nil + } + source.Status.Conditions[v1alpha1.HPA] = condition return nil } @@ -207,6 +219,33 @@ func (r *SourceReconciler) ApplySourceHPA(ctx context.Context, req ctrl.Request, r.Log.Error(err, "failed to create pod autoscaler for source", "name", source.Name) return err } + case v1alpha1.Update: + hpa := &autov2beta2.HorizontalPodAutoscaler{} + err := r.Get(ctx, types.NamespacedName{Namespace: source.Namespace, + Name: spec.MakeSourceObjectMeta(source).Name}, hpa) + if err != nil { + r.Log.Error(err, "failed to update pod autoscaler for source, cannot find hpa", "name", source.Name) + return err + } + if hpa.Spec.MaxReplicas != *source.Spec.MaxReplicas { + hpa.Spec.MaxReplicas = *source.Spec.MaxReplicas + } + if len(source.Spec.Pod.AutoScalingMetrics) > 0 && !reflect.DeepEqual(hpa.Spec.Metrics, source.Spec.Pod.AutoScalingMetrics) { + hpa.Spec.Metrics = source.Spec.Pod.AutoScalingMetrics + } + if source.Spec.Pod.AutoScalingBehavior != nil { + hpa.Spec.Behavior = source.Spec.Pod.AutoScalingBehavior + } + if len(source.Spec.Pod.BuiltinAutoscaler) > 0 { + metrics := spec.MakeMetricsFromBuiltinHPARules(source.Spec.Pod.BuiltinAutoscaler) + if !reflect.DeepEqual(hpa.Spec.Metrics, metrics) { + hpa.Spec.Metrics = metrics + } + } + if err := r.Update(ctx, hpa); err != nil { + r.Log.Error(err, "failed to update pod autoscaler for source", "name", source.Name) + return err + } case v1alpha1.Wait, v1alpha1.NoAction: // do nothing } diff --git a/controllers/source_controller.go b/controllers/source_controller.go index 21eba31b4..8963a4a66 100644 --- a/controllers/source_controller.go +++ b/controllers/source_controller.go @@ -23,7 +23,7 @@ import ( "github.com/go-logr/logr" computev1alpha1 "github.com/streamnative/function-mesh/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" - autov1 "k8s.io/api/autoscaling/v1" + autov2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -104,6 +104,6 @@ func (r *SourceReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&computev1alpha1.Source{}). Owns(&appsv1.StatefulSet{}). Owns(&corev1.Service{}). - Owns(&autov1.HorizontalPodAutoscaler{}). + Owns(&autov2beta2.HorizontalPodAutoscaler{}). Complete(r) } diff --git a/controllers/spec/common.go b/controllers/spec/common.go index cf209c303..d539d6b23 100644 --- a/controllers/spec/common.go +++ b/controllers/spec/common.go @@ -28,7 +28,6 @@ import ( "github.com/streamnative/function-mesh/controllers/proto" appsv1 "k8s.io/api/apps/v1" - autov1 "k8s.io/api/autoscaling/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -95,29 +94,6 @@ func MakeHeadlessServiceName(serviceName string) string { return fmt.Sprintf("%s-headless", serviceName) } -func MakeHPA(objectMeta *metav1.ObjectMeta, minReplicas, maxReplicas int32, - kind string) *autov1.HorizontalPodAutoscaler { - // TODO: configurable cpu percentage - cpuPercentage := int32(80) - return &autov1.HorizontalPodAutoscaler{ - TypeMeta: metav1.TypeMeta{ - Kind: "autoscaling/v1", - APIVersion: "HorizontalPodAutoscaler", - }, - ObjectMeta: *objectMeta, - Spec: autov1.HorizontalPodAutoscalerSpec{ - ScaleTargetRef: autov1.CrossVersionObjectReference{ - Kind: kind, - Name: objectMeta.Name, - APIVersion: "compute.functionmesh.io/v1alpha1", - }, - MinReplicas: &minReplicas, - MaxReplicas: maxReplicas, - TargetCPUUtilizationPercentage: &cpuPercentage, - }, - } -} - func MakeStatefulSet(objectMeta *metav1.ObjectMeta, replicas *int32, container *corev1.Container, volumes []corev1.Volume, labels map[string]string, policy v1alpha1.PodPolicy) *appsv1.StatefulSet { return &appsv1.StatefulSet{ diff --git a/controllers/spec/function.go b/controllers/spec/function.go index 66b391686..6a4737feb 100644 --- a/controllers/spec/function.go +++ b/controllers/spec/function.go @@ -21,18 +21,28 @@ import ( "github.com/gogo/protobuf/jsonpb" "github.com/streamnative/function-mesh/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" - autov1 "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" logf "sigs.k8s.io/controller-runtime/pkg/log" ) // log is for logging in this package. -var log = logf.Log.WithName("sink-resource") +var log = logf.Log.WithName("function-resource") -func MakeFunctionHPA(function *v1alpha1.Function) *autov1.HorizontalPodAutoscaler { +func MakeFunctionHPA(function *v1alpha1.Function) *autov2beta2.HorizontalPodAutoscaler { objectMeta := MakeFunctionObjectMeta(function) - return MakeHPA(objectMeta, *function.Spec.Replicas, *function.Spec.MaxReplicas, function.Kind) + targetRef := autov2beta2.CrossVersionObjectReference{ + Kind: function.Kind, + Name: function.Name, + APIVersion: function.APIVersion, + } + if isBuiltinHPAEnabled(function.Spec.Replicas, function.Spec.MaxReplicas, function.Spec.Pod) { + return makeBuiltinHPA(objectMeta, *function.Spec.Replicas, *function.Spec.MaxReplicas, targetRef, function.Spec.Pod.BuiltinAutoscaler) + } else if !isDefaultHPAEnabled(function.Spec.Replicas, function.Spec.MaxReplicas, function.Spec.Pod) { + return makeHPA(objectMeta, *function.Spec.Replicas, *function.Spec.MaxReplicas, function.Spec.Pod, targetRef) + } + return makeDefaultHPA(objectMeta, *function.Spec.Replicas, *function.Spec.MaxReplicas, targetRef) } func MakeFunctionService(function *v1alpha1.Function) *corev1.Service { diff --git a/controllers/spec/hpa.go b/controllers/spec/hpa.go new file mode 100644 index 000000000..3c401da66 --- /dev/null +++ b/controllers/spec/hpa.go @@ -0,0 +1,173 @@ +// 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 spec + +import ( + "github.com/streamnative/function-mesh/api/v1alpha1" + autov2beta2 "k8s.io/api/autoscaling/v2beta2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type BuiltinAutoScaler interface { + Metrics() []autov2beta2.MetricSpec +} + +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 +} + +func isBuiltinHPAEnabled(minReplicas, maxReplicas *int32, podPolicy v1alpha1.PodPolicy) bool { + return minReplicas != nil && maxReplicas != nil && len(podPolicy.BuiltinAutoscaler) > 0 && *maxReplicas > *minReplicas +} + +type HPARuleAverageUtilizationCPUPercent struct { + cpuPercentage int32 +} + +type HPARuleAverageUtilizationResourceMemoryPercent struct { + memoryPercentage int32 +} + +func (H *HPARuleAverageUtilizationResourceMemoryPercent) Metrics() []autov2beta2.MetricSpec { + return []autov2beta2.MetricSpec{ + { + Type: autov2beta2.ResourceMetricSourceType, + Resource: &autov2beta2.ResourceMetricSource{ + Name: corev1.ResourceMemory, + Target: autov2beta2.MetricTarget{ + Type: autov2beta2.UtilizationMetricType, + AverageUtilization: &H.memoryPercentage, + }, + }, + }, + } +} + +func (H *HPARuleAverageUtilizationCPUPercent) Metrics() []autov2beta2.MetricSpec { + return []autov2beta2.MetricSpec{ + { + Type: autov2beta2.ResourceMetricSourceType, + Resource: &autov2beta2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autov2beta2.MetricTarget{ + Type: autov2beta2.UtilizationMetricType, + AverageUtilization: &H.cpuPercentage, + }, + }, + }, + } +} + +func NewHPARuleAverageUtilizationCPUPercent(cpuPercentage int32) BuiltinAutoScaler { + return &HPARuleAverageUtilizationCPUPercent{ + cpuPercentage: cpuPercentage, + } +} + +func NewHPARuleAverageUtilizationMemoryPercent(memoryPercentage int32) BuiltinAutoScaler { + return &HPARuleAverageUtilizationResourceMemoryPercent{ + memoryPercentage: memoryPercentage, + } +} + +func GetBuiltinAutoScaler(builtinRule v1alpha1.BuiltinHPARule) BuiltinAutoScaler { + switch builtinRule { + case v1alpha1.AverageUtilizationCPUPercent80: + return NewHPARuleAverageUtilizationCPUPercent(80) + case v1alpha1.AverageUtilizationCPUPercent50: + return NewHPARuleAverageUtilizationCPUPercent(50) + case v1alpha1.AverageUtilizationCPUPercent20: + return NewHPARuleAverageUtilizationCPUPercent(20) + case v1alpha1.AverageUtilizationMemoryPercent80: + return NewHPARuleAverageUtilizationMemoryPercent(80) + case v1alpha1.AverageUtilizationMemoryPercent50: + return NewHPARuleAverageUtilizationMemoryPercent(50) + case v1alpha1.AverageUtilizationMemoryPercent20: + return NewHPARuleAverageUtilizationMemoryPercent(20) + default: + return nil + } +} + +// defaultHPAMetrics generates a default HPA Metrics settings based on CPU usage and utilized on 80%. +func defaultHPAMetrics() []autov2beta2.MetricSpec { + return NewHPARuleAverageUtilizationCPUPercent(80).Metrics() +} + +func makeDefaultHPA(objectMeta *metav1.ObjectMeta, minReplicas, maxReplicas int32, targetRef autov2beta2.CrossVersionObjectReference) *autov2beta2.HorizontalPodAutoscaler { + return &autov2beta2.HorizontalPodAutoscaler{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "autoscaling/v2beta2", + Kind: "HorizontalPodAutoscaler", + }, + ObjectMeta: *objectMeta, + Spec: autov2beta2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: targetRef, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: defaultHPAMetrics(), + }, + } +} + +func MakeMetricsFromBuiltinHPARules(builtinRules []v1alpha1.BuiltinHPARule) []autov2beta2.MetricSpec { + metrics := []autov2beta2.MetricSpec{} + for _, r := range builtinRules { + s := GetBuiltinAutoScaler(r) + if s != nil { + metrics = append(metrics, s.Metrics()...) + } + } + return metrics +} + +func makeBuiltinHPA(objectMeta *metav1.ObjectMeta, minReplicas, maxReplicas int32, targetRef autov2beta2.CrossVersionObjectReference, builtinRules []v1alpha1.BuiltinHPARule) *autov2beta2.HorizontalPodAutoscaler { + metrics := MakeMetricsFromBuiltinHPARules(builtinRules) + return &autov2beta2.HorizontalPodAutoscaler{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "autoscaling/v2beta2", + Kind: "HorizontalPodAutoscaler", + }, + ObjectMeta: *objectMeta, + Spec: autov2beta2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: targetRef, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: metrics, + }, + } +} + +func makeHPA(objectMeta *metav1.ObjectMeta, minReplicas, maxReplicas int32, podPolicy v1alpha1.PodPolicy, targetRef autov2beta2.CrossVersionObjectReference) *autov2beta2.HorizontalPodAutoscaler { + spec := autov2beta2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: targetRef, + MinReplicas: &minReplicas, + MaxReplicas: maxReplicas, + Metrics: podPolicy.AutoScalingMetrics, + Behavior: podPolicy.AutoScalingBehavior, + } + return &autov2beta2.HorizontalPodAutoscaler{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "autoscaling/v2beta2", + Kind: "HorizontalPodAutoscaler", + }, + ObjectMeta: *objectMeta, + Spec: spec, + } +} diff --git a/controllers/spec/sink.go b/controllers/spec/sink.go index 2cead0730..aaa1f7adb 100644 --- a/controllers/spec/sink.go +++ b/controllers/spec/sink.go @@ -21,14 +21,24 @@ import ( "github.com/gogo/protobuf/jsonpb" "github.com/streamnative/function-mesh/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" - autov1 "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" ) -func MakeSinkHPA(sink *v1alpha1.Sink) *autov1.HorizontalPodAutoscaler { +func MakeSinkHPA(sink *v1alpha1.Sink) *autov2beta2.HorizontalPodAutoscaler { objectMeta := MakeSinkObjectMeta(sink) - return MakeHPA(objectMeta, *sink.Spec.Replicas, *sink.Spec.MaxReplicas, sink.Kind) + targetRef := autov2beta2.CrossVersionObjectReference{ + Kind: sink.Kind, + Name: sink.Name, + APIVersion: sink.APIVersion, + } + if isBuiltinHPAEnabled(sink.Spec.Replicas, sink.Spec.MaxReplicas, sink.Spec.Pod) { + return makeBuiltinHPA(objectMeta, *sink.Spec.Replicas, *sink.Spec.MaxReplicas, targetRef, sink.Spec.Pod.BuiltinAutoscaler) + } else if !isDefaultHPAEnabled(sink.Spec.Replicas, sink.Spec.MaxReplicas, sink.Spec.Pod) { + return makeHPA(objectMeta, *sink.Spec.Replicas, *sink.Spec.MaxReplicas, sink.Spec.Pod, targetRef) + } + return makeDefaultHPA(objectMeta, *sink.Spec.Replicas, *sink.Spec.MaxReplicas, targetRef) } func MakeSinkService(sink *v1alpha1.Sink) *corev1.Service { diff --git a/controllers/spec/source.go b/controllers/spec/source.go index 5c33d1204..bc363bade 100644 --- a/controllers/spec/source.go +++ b/controllers/spec/source.go @@ -21,14 +21,24 @@ import ( "github.com/gogo/protobuf/jsonpb" "github.com/streamnative/function-mesh/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" - autov1 "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" ) -func MakeSourceHPA(source *v1alpha1.Source) *autov1.HorizontalPodAutoscaler { +func MakeSourceHPA(source *v1alpha1.Source) *autov2beta2.HorizontalPodAutoscaler { objectMeta := MakeSourceObjectMeta(source) - return MakeHPA(objectMeta, *source.Spec.Replicas, *source.Spec.MaxReplicas, source.Kind) + targetRef := autov2beta2.CrossVersionObjectReference{ + Kind: source.Kind, + Name: source.Name, + APIVersion: source.APIVersion, + } + if isBuiltinHPAEnabled(source.Spec.Replicas, source.Spec.MaxReplicas, source.Spec.Pod) { + return makeBuiltinHPA(objectMeta, *source.Spec.Replicas, *source.Spec.MaxReplicas, targetRef, source.Spec.Pod.BuiltinAutoscaler) + } else if !isDefaultHPAEnabled(source.Spec.Replicas, source.Spec.MaxReplicas, source.Spec.Pod) { + return makeHPA(objectMeta, *source.Spec.Replicas, *source.Spec.MaxReplicas, source.Spec.Pod, targetRef) + } + return makeDefaultHPA(objectMeta, *source.Spec.Replicas, *source.Spec.MaxReplicas, targetRef) } func MakeSourceService(source *v1alpha1.Source) *corev1.Service { diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 7362fe590..b105916d3 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -67,6 +67,7 @@ var _ = BeforeSuite(func(done Done) { Paths: []string{filepath.Join("..", "config", "crd", "bases")}, }, AttachControlPlaneOutput: true, + ErrorIfCRDPathMissing: true, } } var err error diff --git a/controllers/test_utils_test.go b/controllers/test_utils_test.go index e19266b80..a1f2e7eb6 100644 --- a/controllers/test_utils_test.go +++ b/controllers/test_utils_test.go @@ -18,13 +18,20 @@ package controllers import ( + "fmt" + "github.com/streamnative/function-mesh/api/v1alpha1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" ) const TestClusterName string = "test-pulsar" const TestFunctionName string = "test-function" +const TestFunctionE2EName string = "test-function-e2e" +const TestFunctionKeyBatcherName string = "test-function-key-batcher" +const TestFunctionHPAName string = "test-function-hpa" +const TestFunctionBuiltinHPAName string = "test-function-builtin-hpa" const TestFunctionMeshName = "test-function-mesh" const TestSinkName string = "test-sink" const TestSourceName string = "test-source" @@ -34,7 +41,7 @@ func makeSampleObjectMeta(name string) *metav1.ObjectMeta { return &metav1.ObjectMeta{ Name: name, Namespace: TestNameSpace, - UID: "dead-beef", // uid not generate automatically with fake k8s + UID: types.UID(fmt.Sprintf("dead-beef-%s", name)), // uid not generate automatically with fake k8s } } @@ -102,7 +109,7 @@ func makeFunctionSample(functionName string) *v1alpha1.Function { } func makeFunctionSampleWithCryptoEnabled() *v1alpha1.Function { - function := makeFunctionSample(TestFunctionName) + function := makeFunctionSample(TestFunctionE2EName) function.Spec.Input = v1alpha1.InputConf{ Topics: []string{ "persistent://public/default/java-function-input-topic", @@ -137,7 +144,7 @@ func makeFunctionSampleWithCryptoEnabled() *v1alpha1.Function { } func makeFunctionSampleWithKeyBasedBatcher() *v1alpha1.Function { - function := makeFunctionSample(TestFunctionName) + function := makeFunctionSample(TestFunctionKeyBatcherName) function.Spec.Output = v1alpha1.OutputConf{ Topic: "persistent://public/default/java-function-output-topic", ProducerConf: &v1alpha1.ProducerConfig{ diff --git a/hack/kind-cluster-build.sh b/hack/kind-cluster-build.sh index 15a9bbcd4..c67c1912a 100755 --- a/hack/kind-cluster-build.sh +++ b/hack/kind-cluster-build.sh @@ -118,6 +118,8 @@ configFile=${workDir}/kind-config.yaml cat < ${configFile} kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 +runtimeConfig: + "autoscaling/v2beta2": "true" nodes: - role: control-plane extraPortMappings: diff --git a/manifests/crd.yaml b/manifests/crd.yaml index ab34957c6..4ac2a19c0 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -476,6 +476,257 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: + type: string + type: array imagePullSecrets: items: properties: @@ -2649,37 +2900,288 @@ spec: additionalProperties: type: string type: object - imagePullSecrets: - items: - properties: - name: - type: string - type: object - type: array - initContainers: - items: - properties: - args: - items: + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: type: string - type: array - command: - items: + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: type: string - type: array - env: - items: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: + type: string + type: array + imagePullSecrets: + items: + properties: + name: + type: string + type: object + type: array + initContainers: + items: + properties: + args: + items: + type: string + type: array + command: + items: + type: string + type: array + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string name: type: string optional: @@ -4763,7 +5265,221 @@ spec: requiredDuringSchedulingIgnoredDuringExecution: items: properties: - labelSelector: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: properties: matchExpressions: items: @@ -4786,22 +5502,59 @@ spec: type: string type: object type: object - namespaces: - items: - type: string - type: array - topologyKey: + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: type: string required: - - topologyKey + - type type: object - type: array - type: object - type: object - annotations: - additionalProperties: + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: type: string - type: object + type: array imagePullSecrets: items: properties: @@ -6991,7 +7744,254 @@ spec: requiredDuringSchedulingIgnoredDuringExecution: items: properties: - labelSelector: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: properties: matchExpressions: items: @@ -7014,68 +8014,35 @@ spec: type: string type: object type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string required: - - topologyKey + - name type: object - type: array - type: object - podAntiAffinity: - properties: - preferredDuringSchedulingIgnoredDuringExecution: - items: + target: properties: - podAffinityTerm: - properties: - labelSelector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - type: object - type: object - namespaces: - items: - type: string - type: array - topologyKey: - type: string - required: - - topologyKey - type: object - weight: + averageUtilization: format: int32 type: integer + averageValue: + type: string + type: + type: string + value: + type: string required: - - podAffinityTerm - - weight + - type type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: properties: - labelSelector: + name: + type: string + selector: properties: matchExpressions: items: @@ -7098,22 +8065,59 @@ spec: type: string type: object type: object - namespaces: - items: - type: string - type: array - topologyKey: + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: type: string required: - - topologyKey + - type type: object - type: array - type: object - type: object - annotations: - additionalProperties: + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: type: string - type: object + type: array imagePullSecrets: items: properties: @@ -9296,23 +10300,237 @@ spec: items: type: string type: array - topologyKey: - type: string - required: - - topologyKey + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object type: object - weight: + required: + - name + type: object + target: + properties: + averageUtilization: format: int32 type: integer + averageValue: + type: string + type: + type: string + value: + type: string required: - - podAffinityTerm - - weight + - type type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - items: + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: properties: - labelSelector: + name: + type: string + selector: properties: matchExpressions: items: @@ -9335,22 +10553,59 @@ spec: type: string type: object type: object - namespaces: - items: - type: string - type: array - topologyKey: + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: type: string required: - - topologyKey + - type type: object - type: array - type: object - type: object - annotations: - additionalProperties: + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: type: string - type: object + type: array imagePullSecrets: items: properties: @@ -11568,6 +12823,257 @@ spec: additionalProperties: type: string type: object + autoScalingBehavior: + properties: + scaleDown: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + scaleUp: + properties: + policies: + items: + properties: + periodSeconds: + format: int32 + type: integer + type: + type: string + value: + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + type: string + stabilizationWindowSeconds: + format: int32 + type: integer + type: object + type: object + autoScalingMetrics: + items: + properties: + external: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + object: + properties: + describedObject: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - describedObject + - metric + - target + type: object + pods: + properties: + metric: + properties: + name: + type: string + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + required: + - name + type: object + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - metric + - target + type: object + resource: + properties: + name: + type: string + target: + properties: + averageUtilization: + format: int32 + type: integer + averageValue: + type: string + type: + type: string + value: + type: string + required: + - type + type: object + required: + - name + - target + type: object + type: + type: string + required: + - type + type: object + type: array + builtinAutoscaler: + items: + type: string + type: array imagePullSecrets: items: properties: