diff --git a/artifacts/flagger/account.yaml b/artifacts/flagger/account.yaml index ae07a7f89..79f0aae14 100644 --- a/artifacts/flagger/account.yaml +++ b/artifacts/flagger/account.yaml @@ -239,6 +239,19 @@ rules: - update - patch - delete + - apiGroups: + - http.keda.sh + resources: + - httpscaledobjects + - httpscaledobjects/finalizers + verbs: + - get + - list + - watch + - create + - update + - patch + - delete - apiGroups: - apisix.apache.org resources: diff --git a/artifacts/flagger/crd.yaml b/artifacts/flagger/crd.yaml index 99940df06..20277fa4a 100644 --- a/artifacts/flagger/crd.yaml +++ b/artifacts/flagger/crd.yaml @@ -119,6 +119,7 @@ spec: enum: - HorizontalPodAutoscaler - ScaledObject + - HTTPScaledObject name: type: string primaryScalerQueries: @@ -132,6 +133,38 @@ spec: type: number maxReplicas: type: number + canaryInterceptorProxyService: + type: object + description: Specify this service if you want to change the Canary interceptor proxy service from its default value. + properties: + name: + default: keda-http-add-on-interceptor-proxy + maxLength: 253 + minLength: 1 + type: string + namespace: + default: keda + maxLength: 63 + minLength: 1 + type: string + primaryScalingSet: + type: object + description: |- + PrimaryScalingSet to be used for primary HTTPScaledObject, if empty, default interceptor and scaler will be used. + properties: + kind: + description: Kind of the resource being referred to. Defaults to HTTPScalingSet. + enum: + - HTTPScalingSet + - ClusterHTTPScalingSet + type: string + name: + description: Name of the scaling set + type: string + namespace: + maxLength: 63 + minLength: 1 + type: string ingressRef: description: Ingress selector type: object diff --git a/charts/flagger/Chart.yaml b/charts/flagger/Chart.yaml index 51b82c21a..87f396b38 100644 --- a/charts/flagger/Chart.yaml +++ b/charts/flagger/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 name: flagger -version: 1.38.0 -appVersion: 1.38.0 +version: 1.39.0 +appVersion: 1.39.0 kubeVersion: ">=1.19.0-0" engine: gotpl description: Flagger is a progressive delivery operator for Kubernetes diff --git a/charts/flagger/crds/crd.yaml b/charts/flagger/crds/crd.yaml index 99940df06..20277fa4a 100644 --- a/charts/flagger/crds/crd.yaml +++ b/charts/flagger/crds/crd.yaml @@ -119,6 +119,7 @@ spec: enum: - HorizontalPodAutoscaler - ScaledObject + - HTTPScaledObject name: type: string primaryScalerQueries: @@ -132,6 +133,38 @@ spec: type: number maxReplicas: type: number + canaryInterceptorProxyService: + type: object + description: Specify this service if you want to change the Canary interceptor proxy service from its default value. + properties: + name: + default: keda-http-add-on-interceptor-proxy + maxLength: 253 + minLength: 1 + type: string + namespace: + default: keda + maxLength: 63 + minLength: 1 + type: string + primaryScalingSet: + type: object + description: |- + PrimaryScalingSet to be used for primary HTTPScaledObject, if empty, default interceptor and scaler will be used. + properties: + kind: + description: Kind of the resource being referred to. Defaults to HTTPScalingSet. + enum: + - HTTPScalingSet + - ClusterHTTPScalingSet + type: string + name: + description: Name of the scaling set + type: string + namespace: + maxLength: 63 + minLength: 1 + type: string ingressRef: description: Ingress selector type: object diff --git a/charts/flagger/templates/rbac.yaml b/charts/flagger/templates/rbac.yaml index ae9c70155..6085fd8b3 100644 --- a/charts/flagger/templates/rbac.yaml +++ b/charts/flagger/templates/rbac.yaml @@ -247,6 +247,19 @@ rules: - update - patch - delete + - apiGroups: + - http.keda.sh + resources: + - httpscaledobjects + - httpscaledobjects/finalizers + verbs: + - get + - list + - watch + - create + - update + - patch + - delete - apiGroups: - apisix.apache.org resources: diff --git a/docs/diagrams/flagger-keda-http-add-on.png b/docs/diagrams/flagger-keda-http-add-on.png new file mode 100644 index 000000000..2c4adeb9b Binary files /dev/null and b/docs/diagrams/flagger-keda-http-add-on.png differ diff --git a/docs/gitbook/SUMMARY.md b/docs/gitbook/SUMMARY.md index 868549d81..31f39c064 100644 --- a/docs/gitbook/SUMMARY.md +++ b/docs/gitbook/SUMMARY.md @@ -38,6 +38,7 @@ * [Blue/Green Deployments](tutorials/kubernetes-blue-green.md) * [Canary analysis with Prometheus Operator](tutorials/prometheus-operator.md) * [Canary analysis with KEDA ScaledObjects](tutorials/keda-scaledobject.md) +* [Canary analysis with KEDA HTTPScaledObjects](tutorials/keda-httpscaledobject.md) * [Zero downtime deployments](tutorials/zero-downtime-deployments.md) ## Dev diff --git a/docs/gitbook/tutorials/keda-httpscaledobject.md b/docs/gitbook/tutorials/keda-httpscaledobject.md new file mode 100644 index 000000000..095fb40ca --- /dev/null +++ b/docs/gitbook/tutorials/keda-httpscaledobject.md @@ -0,0 +1,760 @@ +# Canary analysis with KEDA HTTP Add-on HTTPScaledObjects + +This guide shows you how to use Flagger with KEDA HTTPScaledObjects which will automatically scale (including to/from zero) based on incoming HTTP traffic a Canary analysis run. + +![Flagger Canary Stages](https://raw.githubusercontent.com/fluxcd/flagger/main/docs/diagrams/flagger-keda-http-add-on.png) + +## Prerequisites + +Flagger requires a Kubernetes cluster **v1.19** or newer and any mesh/ingress that implements the `v1` version of Gateway API. For this tutorial, we'll need KEDA **2.16.0** or newer and KEDA HTTP Add-on **0.9.0** or newer. + +Install the Gateway API CRDs: + +```bash +kubectl apply -k "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.2.0" +``` + +Install Istio: + +```bash +istioctl install --set profile=minimal -y + +# Suggestion: Please change release-1.20 in below command, to your real istio version. +kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/prometheus.yaml +``` + +Install KEDA: + +```bash +helm repo add kedacore https://kedacore.github.io/charts +helm repo update +helm install keda kedacore/keda --namespace keda --create-namespace +``` + +Install KEDA HTTP Add-on: + +```bash +helm install http-add-on kedacore/keda-add-ons-http --namespace keda +``` + +Install Flagger: + +```bash +helm repo add flagger https://flagger.app +helm repo update + +helm upgrade -i flagger flagger/flagger \ + --namespace flagger-system \ + --create-namespace \ + --set prometheus.install=false \ + --set meshProvider=gatewayapi:v1 \ + --set metricsServer=http://prometheus.istio-system:9090 +``` + +> Note: The above installation sets the mesh provider to be `gatewayapi:v1`. If your Gateway API implementation uses the `v1beta1` CRDs, then +set the `--meshProvider` value to `gatewayapi:v1beta1`. + +Create a namespace for the `Gateway`: + +```bash +kubectl create ns istio-ingress +``` + +Create a `Gateway` that configures load balancing, traffic ACL, etc: + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway + namespace: istio-ingress +spec: + gatewayClassName: istio + listeners: + - name: default + hostname: "*.example.com" + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: All +``` + +Create a interceptor for the primary workloads: + +```yaml +kind: ClusterHTTPScalingSet +apiVersion: http.keda.sh/v1alpha1 +metadata: + name: primary +spec: + interceptor: + config: + adminPort: 9090 + connectTimeout: 500ms + expectContinueTimeout: 1s + forceHTTP2: false + handshakeTimeout: 10s + headerTimeout: 500ms + idleConnTimeout: 90s + keepAlive: 1s + maxIdleConnections: 100 + pollingInterval: 1000 + proxyPort: 8080 + waitTimeout: 20s + replicas: 1 + resources: {} + serviceAccountName: keda-add-ons-http-interceptor + scaler: + serviceAccountName: keda-add-ons-http-external-scaler +``` + +## Bootstrap + +Flagger takes a Kubernetes deployment and a KEDA HTTPScaledObject targeting the deployment. It then creates a series of objects +(Kubernetes deployments, ClusterIP services targeting the Deployments, another KEDA HTTPScaledObject targeting the created Deployment and ClusterIP service, HTTPRoute targeting the KEDA interceptor). +These objects expose the application inside the mesh and drive the Canary analysis and Blue/Green promotion. + +Create a test namespace: + +```bash +kubectl create ns test +``` + +Create a Deployment named `podinfo`: + +```bash +kubectl apply -n test -f https://raw.githubusercontent.com/fluxcd/flagger/main/kustomize/podinfo/deployment.yaml +``` + +Create a ClusterIP Service and HTTPScaledObject which targets the `podinfo` deployment and uses HTTP Request as a trigger: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: podinfo + namespace: test + labels: + app: podinfo +spec: + type: ClusterIP + ports: + - port: 9898 + targetPort: http + protocol: TCP + name: http + selector: + app: podinfo +--- +apiVersion: http.keda.sh/v1alpha1 +kind: HTTPScaledObject +metadata: + name: podinfo + namespace: test + labels: + app: podinfo +spec: + hosts: + - www.example.com + pathPrefixes: + - / + replicas: + max: 10 + min: 0 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: podinfo + port: 9898 + service: podinfo + scalingMetric: + concurrency: + targetValue: 200 +``` + +Save the above resource as podinfo-canary-httpso.yaml and then apply it: + +```bash +kubectl apply -f ./podinfo-canary-httpso.yaml +``` + +Deploy the load testing service to generate traffic during the analysis: + +```bash +kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=main +``` + +Create a canary custom resource \(replace "www.example.com" with your own domain\): + +```yaml +apiVersion: flagger.app/v1beta1 +kind: Canary +metadata: + name: podinfo + namespace: test +spec: + # deployment reference + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: podinfo + # HTTPScaledObject reference + autoscalerRef: + apiVersion: keda.sh/v1alpha1 + kind: HTTPScaledObject + name: podinfo + primaryScalingSet: + name: primary + kind: ClusterHTTPScalingSet + # the maximum time in seconds for the canary deployment + # to make progress before rollback (default 600s) + progressDeadlineSeconds: 60 + service: + # service port number + port: 8080 + # Reference to the Gateway that the generated HTTPRoute would attach to. + gatewayRefs: + - name: gateway + namespace: istio-ingress + analysis: + # schedule interval (default 60s) + interval: 1m + # max number of failed metric checks before rollback + threshold: 5 + # max traffic percentage routed to canary + # percentage (0-100) + maxWeight: 50 + # canary increment step + # percentage (0-100) + stepWeight: 10 + metrics: + - name: error-rate + # max error rate (5xx responses) + # percentage (0-100) + templateRef: + name: error-rate + namespace: flagger-system + thresholdRange: + max: 1 + interval: 1m + - name: latency + templateRef: + name: latency + namespace: flagger-system + # seconds + thresholdRange: + max: 0.5 + interval: 30s + # testing (optional) + webhooks: + - name: smoke-test + type: pre-rollout + url: http://flagger-loadtester.test/ + timeout: 15s + metadata: + type: bash + cmd: "curl -sd 'anon' -H 'Host: www.example.com' http://keda-http-add-on-interceptor-proxy.keda:8080/token | grep token" + - name: load-test + url: http://flagger-loadtester.test/ + timeout: 5s + metadata: + cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/" +``` + +Save the above resource as `podinfo-canary.yaml` and then apply it: + +```bash +kubectl apply -f ./podinfo-canary.yaml +``` + +After a couple of seconds Flagger will create the canary objects: + +```bash +# applied +deployment.apps/podinfo +service/podinfo +httpscaledobject.http.keda.sh/podinfo +canary.flagger.app/podinfo + +# generated +deployment.apps/podinfo-primary +service/podinfo-primary +httpscaledobject.http.keda.sh/podinfo-primary +httproutes.gateway.networking.k8s.io/podinfo +``` + +We refer to our HTTPScaledObject for the canary deployment using `.spec.autoscalerRef`. Flagger will use this to generate a primary HTTPScaledObject which will automatically scale their primary deployment up and down (including to/from zero) based on incoming HTTP traffic. + +In the situation when it is desired to have different scaling replica configuration between the canary and primary deployment HTTPScaledObject you can use +the `.spec.autoscalerRef.primaryScalerReplicas` to override these values for the generated primary HTTPScaledObject. + +After the boostrap, the podinfo deployment will be scaled to zero and the traffic to `podinfo.test` will be routed to the primary pods. +When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary. The Blue/Green deployment will run for five iterations while validating the HTTP metrics and rollout hooks every 15 seconds. + +## Expose the app outside the cluster + +Find the external address of Istio's load balancer: + +```bash +export ADDRESS="$(kubectl -n istio-ingress get svc/gateway-istio -ojson \ +| jq -r ".status.loadBalancer.ingress[].hostname")" +echo $ADDRESS +``` + +Configure your DNS server with a CNAME record \(AWS\) or A record \(GKE/AKS/DOKS\) and point a domain e.g. `www.example.com` to the LB address. + +Now you can access the podinfo UI using your domain address. + +Note that you should be using HTTPS when exposing production workloads on internet. You can obtain free TLS certs from Let's Encrypt, read this +[guide](https://github.com/stefanprodan/istio-gke) on how to configure cert-manager to secure Istio with TLS certificates. + +If you're using a local cluster via kind/k3s you can port forward the Envoy LoadBalancer service: + +```bash +kubectl port-forward -n istio-ingress svc/gateway-istio 8080:80 +``` + +Now you can access podinfo via `curl -H "Host: www.example.com" localhost:8080` + +## Automated canary promotion + +Trigger a canary deployment by updating the container image: + +```bash +kubectl -n test set image deployment/podinfo \ +podinfod=stefanprodan/podinfo:6.0.1 +``` + +Flagger detects that the deployment revision changed and starts a new rollout: + +```text +kubectl -n test describe canary/podinfo + +Status: + Canary Weight: 0 + Failed Checks: 0 + Phase: Succeeded +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Synced 3m flagger New revision detected podinfo.test + Normal Synced 3m flagger Scaling up podinfo.test + Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available + Normal Synced 3m flagger Advance podinfo.test canary weight 5 + Normal Synced 3m flagger Advance podinfo.test canary weight 10 + Normal Synced 3m flagger Advance podinfo.test canary weight 15 + Normal Synced 2m flagger Advance podinfo.test canary weight 20 + Normal Synced 2m flagger Advance podinfo.test canary weight 25 + Normal Synced 1m flagger Advance podinfo.test canary weight 30 + Normal Synced 1m flagger Advance podinfo.test canary weight 35 + Normal Synced 55s flagger Advance podinfo.test canary weight 40 + Normal Synced 45s flagger Advance podinfo.test canary weight 45 + Normal Synced 35s flagger Advance podinfo.test canary weight 50 + Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test + Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available + Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test +``` + +**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. + +A canary deployment is triggered by changes in any of the following objects: + +* Deployment PodSpec \(container image, command, ports, env, resources, etc\) +* ConfigMaps mounted as volumes or mapped to environment variables +* Secrets mounted as volumes or mapped to environment variables + +You can monitor how Flagger progressively changes the weights of the HTTPRoute object that is attahed to the Gateway with: + +```bash +watch kubectl get httproute -n test podinfo -o=jsonpath='{.spec.rules}' +``` + +You can monitor all canaries with: + +```bash +watch kubectl get canaries --all-namespaces + +NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME +test podinfo Progressing 15 2022-01-16T14:05:07Z +prod frontend Succeeded 0 2022-01-15T16:15:07Z +prod backend Failed 0 2022-01-14T17:05:07Z +``` + +## Automated rollback + +During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses the rollout. + +Trigger another canary deployment: + +```bash +kubectl -n test set image deployment/podinfo \ +podinfod=stefanprodan/podinfo:6.0.2 +``` + +Exec into the load tester pod with: + +```bash +kubectl -n test exec -it flagger-loadtester-xx-xx sh +``` + +Generate HTTP 500 errors: + +```bash +watch curl -H 'Host: www.example.com' http://keda-http-add-on-interceptor-proxy.keda:8080/status/500 +``` + +Generate latency: + +```bash +watch curl -H 'Host: www.example.com' http://keda-http-add-on-interceptor-proxy.keda:8080/delay/1 +``` + +When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, the canary is scaled to zero and the rollout is marked as failed. + +```text +kubectl -n test describe canary/podinfo + +Status: + Canary Weight: 0 + Failed Checks: 10 + Phase: Failed +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Synced 3m flagger Starting canary deployment for podinfo.test + Normal Synced 3m flagger Advance podinfo.test canary weight 5 + Normal Synced 3m flagger Advance podinfo.test canary weight 10 + Normal Synced 3m flagger Advance podinfo.test canary weight 15 + Normal Synced 3m flagger Halt podinfo.test advancement error rate 69.17% > 1% + Normal Synced 2m flagger Halt podinfo.test advancement error rate 61.39% > 1% + Normal Synced 2m flagger Halt podinfo.test advancement error rate 55.06% > 1% + Normal Synced 2m flagger Halt podinfo.test advancement error rate 47.00% > 1% + Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement error rate 38.08% > 1% + Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10 + Warning Synced 1m flagger Canary failed! Scaling down podinfo.test +``` + +## Session Affinity + +While Flagger can perform weighted routing and A/B testing individually, with Gateway API it can combine the two leading to a Canary +release with session affinity. +For more information you can read the [deployment strategies docs](../usage/deployment-strategies.md#canary-release-with-session-affinity). + +> **Note:** The implementation must have support for the [`ResponseHeaderModifier`](https://github.com/kubernetes-sigs/gateway-api/blob/3d22aa5a08413222cb79e6b2e245870360434614/apis/v1beta1/httproute_types.go#L651) API. + +Create a canary custom resource \(replace with your own domain\): + +```yaml +apiVersion: flagger.app/v1beta1 +kind: Canary +metadata: + name: podinfo + namespace: test +spec: + # deployment reference + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: podinfo + # the maximum time in seconds for the canary deployment + # to make progress before it is rollback (default 600s) + progressDeadlineSeconds: 60 + # HTTPScaledObject reference + autoscalerRef: + apiVersion: keda.sh/v1alpha1 + kind: HTTPScaledObject + name: podinfo + primaryScalingSet: + name: primary + kind: ClusterHTTPScalingSet + service: + # service port number + port: 9898 + # Reference to the Gateway that the generated HTTPRoute would attach to. + gatewayRefs: + - name: gateway + namespace: istio-ingress + analysis: + # schedule interval (default 60s) + interval: 1m + # max number of failed metric checks before rollback + threshold: 5 + # max traffic percentage routed to canary + # percentage (0-100) + maxWeight: 50 + # canary increment step + # percentage (0-100) + stepWeight: 10 + # session affinity config + sessionAffinity: + # name of the cookie used + cookieName: flagger-cookie + # max age of the cookie (in seconds) + # optional; defaults to 86400 + maxAge: 21600 + metrics: + - name: error-rate + # max error rate (5xx responses) + # percentage (0-100) + templateRef: + name: error-rate + namespace: flagger-system + thresholdRange: + max: 1 + interval: 1m + - name: latency + templateRef: + name: latency + namespace: flagger-system + # seconds + thresholdRange: + max: 0.5 + interval: 30s + # testing (optional) + webhooks: + - name: smoke-test + type: pre-rollout + url: http://flagger-loadtester.test/ + timeout: 15s + metadata: + type: bash + cmd: "curl -sd 'anon' -H 'Host: www.example.com' http://keda-http-add-on-interceptor-proxy.keda:8080/token | grep token" + - name: load-test + url: http://flagger-loadtester.test/ + timeout: 5s + metadata: + cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/" +``` + +Save the above resource as podinfo-canary-session-affinity.yaml and then apply it: + +```bash +kubectl apply -f ./podinfo-canary-session-affinity.yaml +``` + +Trigger a canary deployment by updating the container image: + +```bash +kubectl -n test set image deployment/podinfo \ +podinfod=ghcr.io/stefanprodan/podinfo:6.0.1 +``` + +You can load `www.example.com` in your browser and refresh it until you see the requests being served by `podinfo:6.0.1`. +All subsequent requests after that will be served by `podinfo:6.0.1` and not `podinfo:6.0.0` because of the session affinity +configured by Flagger in the HTTPRoute object. + +# A/B Testing + +Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. This is particularly useful for frontend applications that require session affinity. + +![Flagger A/B Testing Stages](https://raw.githubusercontent.com/fluxcd/flagger/main/docs/diagrams/flagger-abtest-steps.png) + +Create a canary custom resource \(replace "www.example.com" with your own domain\): + +```yaml +apiVersion: flagger.app/v1beta1 +kind: Canary +metadata: + name: podinfo + namespace: test +spec: + # deployment reference + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: podinfo + # the maximum time in seconds for the canary deployment + # to make progress before it is rollback (default 600s) + progressDeadlineSeconds: 60 + # HTTPScaledObject reference + autoscalerRef: + apiVersion: keda.sh/v1alpha1 + kind: HTTPScaledObject + name: podinfo + primaryScalingSet: + name: primary + kind: ClusterHTTPScalingSet + service: + # service port number + port: 9898 + # Reference to the Gateway that the generated HTTPRoute would attach to. + gatewayRefs: + - name: gateway + namespace: istio-ingress + analysis: + # schedule interval (default 60s) + interval: 1m + # max number of failed metric checks before rollback + threshold: 5 + # max traffic percentage routed to canary + # percentage (0-100) + maxWeight: 50 + # canary increment step + # percentage (0-100) + stepWeight: 10 + metrics: + - name: error-rate + # max error rate (5xx responses) + # percentage (0-100) + templateRef: + name: error-rate + namespace: flagger-system + thresholdRange: + max: 1 + interval: 1m + - name: latency + templateRef: + name: latency + namespace: flagger-system + # seconds + thresholdRange: + max: 0.5 + interval: 30s + # testing (optional) + webhooks: + - name: smoke-test + type: pre-rollout + url: http://flagger-loadtester.test/ + timeout: 15s + metadata: + type: bash + cmd: "curl -sd 'anon' -H 'Host: www.example.com' http://keda-http-add-on-interceptor-proxy.keda:8080/token | grep token" + - name: load-test + url: http://flagger-loadtester.test/ + timeout: 5s + metadata: + cmd: "hey -z 2m -q 10 -c 2 -host www.example.com -H 'X-Canary: insider' http://gateway-istio.istio-ingress/" +``` + +The above configuration will run an analysis for ten minutes targeting those users that have an insider cookie. + +Save the above resource as podinfo-ab-canary.yaml and then apply it: + +```bash +kubectl apply -f ./podinfo-ab-canary.yaml +``` + +Trigger a canary deployment by updating the container image: + +```bash +kubectl -n test set image deployment/podinfo \ +podinfod=stefanprodan/podinfo:6.0.3 +``` + +Flagger detects that the deployment revision changed and starts a new rollout: + +```text +kubectl -n test describe canary/podinfo + +Status: + Failed Checks: 0 + Phase: Succeeded +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Synced 3m flagger New revision detected podinfo.test + Normal Synced 3m flagger Scaling up podinfo.test + Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available + Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10 + Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10 + Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10 + Normal Synced 2m flagger Advance podinfo.test canary iteration 4/10 + Normal Synced 2m flagger Advance podinfo.test canary iteration 5/10 + Normal Synced 1m flagger Advance podinfo.test canary iteration 6/10 + Normal Synced 1m flagger Advance podinfo.test canary iteration 7/10 + Normal Synced 55s flagger Advance podinfo.test canary iteration 8/10 + Normal Synced 45s flagger Advance podinfo.test canary iteration 9/10 + Normal Synced 35s flagger Advance podinfo.test canary iteration 10/10 + Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test + Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available + Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test +``` + +## Traffic mirroring + +![Flagger Canary Traffic Shadowing](https://raw.githubusercontent.com/fluxcd/flagger/main/docs/diagrams/flagger-canary-traffic-mirroring.png) + +For applications that perform read operations, Flagger can be configured to do B/G tests with traffic mirroring. +Gateway API traffic mirroring will copy each incoming request, sending one request to the primary and one to the canary service. +The response from the primary is sent back to the user and the response from the canary is discarded. +Metrics are collected on both requests so that the deployment will only proceed if the canary metrics are within the threshold values. + +Note that mirroring should be used for requests that are **idempotent** or capable of being processed twice \(once by the primary and once by the canary\). + +You can enable mirroring by replacing `stepWeight` with `iterations` and by setting `analysis.mirror` to `true`: + +```yaml +apiVersion: flagger.app/v1beta1 +kind: Canary +metadata: + name: podinfo + namespace: test +spec: + # deployment reference + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: podinfo + service: + # service port number + port: 9898 + # container port number or name (optional) + targetPort: 9898 + # Gateway API HTTPRoute host names + hosts: + - www.example.com + # Reference to the Gateway that the generated HTTPRoute would attach to. + gatewayRefs: + - name: gateway + namespace: istio-ingress + analysis: + # schedule interval + interval: 1m + # max number of failed metric checks before rollback + threshold: 5 + # total number of iterations + iterations: 10 + # enable traffic shadowing + mirror: true + # Gateway API HTTPRoute host names + metrics: + - name: request-success-rate + thresholdRange: + min: 99 + interval: 1m + - name: request-duration + thresholdRange: + max: 500 + interval: 1m + webhooks: + - name: load-test + url: http://flagger-loadtester.test/ + timeout: 5s + metadata: + cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/" +``` + +With the above configuration, Flagger will run a canary release with the following steps: + +* detect new revision \(deployment spec, secrets or configmaps changes\) +* scale from zero the canary deployment +* wait for the HPA to set the canary minimum replicas +* check canary pods health +* run the acceptance tests +* abort the canary release if tests fail +* start the load tests +* mirror 100% of the traffic from primary to canary +* check request success rate and request duration every minute +* abort the canary release if the metrics check failure threshold is reached +* stop traffic mirroring after the number of iterations is reached +* route live traffic to the canary pods +* promote the canary \(update the primary secrets, configmaps and deployment spec\) +* wait for the primary deployment rollout to finish +* wait for the HPA to set the primary minimum replicas +* check primary pods health +* switch live traffic back to primary +* scale to zero the canary +* send notification with the canary analysis result + +The above procedures can be extended with [custom metrics](../usage/metrics.md) checks, [webhooks](../usage/webhooks.md), [manual promotion](../usage/webhooks.md#manual-gating) approval and [Slack or MS Teams](../usage/alerting.md) notifications. diff --git a/kustomize/base/flagger/crd.yaml b/kustomize/base/flagger/crd.yaml index 99940df06..20277fa4a 100644 --- a/kustomize/base/flagger/crd.yaml +++ b/kustomize/base/flagger/crd.yaml @@ -119,6 +119,7 @@ spec: enum: - HorizontalPodAutoscaler - ScaledObject + - HTTPScaledObject name: type: string primaryScalerQueries: @@ -132,6 +133,38 @@ spec: type: number maxReplicas: type: number + canaryInterceptorProxyService: + type: object + description: Specify this service if you want to change the Canary interceptor proxy service from its default value. + properties: + name: + default: keda-http-add-on-interceptor-proxy + maxLength: 253 + minLength: 1 + type: string + namespace: + default: keda + maxLength: 63 + minLength: 1 + type: string + primaryScalingSet: + type: object + description: |- + PrimaryScalingSet to be used for primary HTTPScaledObject, if empty, default interceptor and scaler will be used. + properties: + kind: + description: Kind of the resource being referred to. Defaults to HTTPScalingSet. + enum: + - HTTPScalingSet + - ClusterHTTPScalingSet + type: string + name: + description: Name of the scaling set + type: string + namespace: + maxLength: 63 + minLength: 1 + type: string ingressRef: description: Ingress selector type: object diff --git a/kustomize/base/flagger/rbac.yaml b/kustomize/base/flagger/rbac.yaml index 7e46cd99c..9b7c1e210 100644 --- a/kustomize/base/flagger/rbac.yaml +++ b/kustomize/base/flagger/rbac.yaml @@ -229,6 +229,19 @@ rules: - update - patch - delete + - apiGroups: + - http.keda.sh + resources: + - httpscaledobjects + - httpscaledobjects/finalizers + verbs: + - get + - list + - watch + - create + - update + - patch + - delete - apiGroups: - apisix.apache.org resources: diff --git a/pkg/apis/flagger/v1beta1/canary.go b/pkg/apis/flagger/v1beta1/canary.go index 93fe004ed..6043776a8 100644 --- a/pkg/apis/flagger/v1beta1/canary.go +++ b/pkg/apis/flagger/v1beta1/canary.go @@ -21,6 +21,7 @@ import ( "time" "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" + http "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -454,6 +455,35 @@ type LocalObjectReference struct { Name string `json:"name"` } +// CanaryInterceptorProxyService specifies the service if you want to change +// the Canary interceptor proxy service from its default value. +type CanaryInterceptorProxyService struct { + // Name of the canary interceptor proxy service. + // Defaults to "keda-http-add-on-interceptor-proxy". + // +optional + Name string `json:"name,omitempty"` + + // Namespace of the canary interceptor proxy service. + // Defaults to "keda". + // +optional + Namespace string `json:"namespace,omitempty"` +} + +// PrimaryScalingSet defines the desired scaling set to be used +type PrimaryScalingSet struct { + // Kind of the resource being referred to. Defaults to HTTPScalingSet. + // +optional + Kind http.ScalingSetKind `json:"kind,omitempty"` + + // Name of the scaling set + Name string `json:"name,omitempty"` + + // Namespace of the scaling set + // Defaults to "keda". + // +optional + Namespace string `json:"namespace,omitempty"` +} + type AutoscalerRefernce struct { // API version of the scaler // +required @@ -476,6 +506,16 @@ type AutoscalerRefernce struct { // autoscaler replicas. // +optional PrimaryScalerReplicas *ScalerReplicas `json:"primaryScalerReplicas,omitempty"` + + // CanaryInterceptorProxyService specifies the service if you want to change + // the Canary interceptor proxy service from its default value. + // +optional + CanaryInterceptorProxyService *CanaryInterceptorProxyService `json:"canaryInterceptorProxyService,omitempty"` + + // PrimaryScalingSet is the scaling set to be used for the primary + // scaler, if a scaler supports scaling using queries. + // +optional + PrimaryScalingSet *PrimaryScalingSet `json:"primaryScalingSet,omitempty"` } // ScalerReplicas holds overrides for autoscaler replicas @@ -620,3 +660,8 @@ func (c *Canary) SkipAnalysis() bool { } return c.Spec.SkipAnalysis } + +// IsHTTPScaledObject returns true if the autoscalerRef is a HTTPScaledObject +func (c *Canary) IsHTTPScaledObject() bool { + return c.Spec.AutoscalerRef != nil && c.Spec.AutoscalerRef.Kind == "HTTPScaledObject" +} diff --git a/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go b/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go index 340fc0439..25651167e 100644 --- a/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go @@ -166,6 +166,16 @@ func (in *AutoscalerRefernce) DeepCopyInto(out *AutoscalerRefernce) { *out = new(ScalerReplicas) (*in).DeepCopyInto(*out) } + if in.CanaryInterceptorProxyService != nil { + in, out := &in.CanaryInterceptorProxyService, &out.CanaryInterceptorProxyService + *out = new(CanaryInterceptorProxyService) + **out = **in + } + if in.PrimaryScalingSet != nil { + in, out := &in.PrimaryScalingSet, &out.PrimaryScalingSet + *out = new(PrimaryScalingSet) + **out = **in + } return } @@ -304,6 +314,22 @@ func (in *CanaryCondition) DeepCopy() *CanaryCondition { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CanaryInterceptorProxyService) DeepCopyInto(out *CanaryInterceptorProxyService) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanaryInterceptorProxyService. +func (in *CanaryInterceptorProxyService) DeepCopy() *CanaryInterceptorProxyService { + if in == nil { + return nil + } + out := new(CanaryInterceptorProxyService) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CanaryList) DeepCopyInto(out *CanaryList) { *out = *in @@ -868,6 +894,22 @@ func (in *MetricTemplateStatus) DeepCopy() *MetricTemplateStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrimaryScalingSet) DeepCopyInto(out *PrimaryScalingSet) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrimaryScalingSet. +func (in *PrimaryScalingSet) DeepCopy() *PrimaryScalingSet { + if in == nil { + return nil + } + out := new(PrimaryScalingSet) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ScalerReplicas) DeepCopyInto(out *ScalerReplicas) { *out = *in diff --git a/pkg/apis/http/register.go b/pkg/apis/http/register.go new file mode 100644 index 000000000..5ee2620f0 --- /dev/null +++ b/pkg/apis/http/register.go @@ -0,0 +1,5 @@ +package http + +const ( + GroupName = "http.keda.sh" +) diff --git a/pkg/apis/http/v1alpha1/condition_types.go b/pkg/apis/http/v1alpha1/condition_types.go new file mode 100644 index 000000000..114265d37 --- /dev/null +++ b/pkg/apis/http/v1alpha1/condition_types.go @@ -0,0 +1,88 @@ +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// +kubebuilder:validation:Enum=Ready + +// HTTPScaledObjectCreationStatus describes the creation status +// of the scaler's additional resources such as Services, Ingresses and Deployments +type HTTPScaledObjectCreationStatus string + +const ( + // Ready indicates the object is fully created + Ready HTTPScaledObjectCreationStatus = "Ready" +) + +// +kubebuilder:validation:Enum=ErrorCreatingAppScaledObject;AppScaledObjectCreated;TerminatingResources;AppScaledObjectTerminated;AppScaledObjectTerminationError;PendingCreation;HTTPScaledObjectIsReady; + +// HTTPScaledObjectConditionReason describes the reason why the condition transitioned +type HTTPScaledObjectConditionReason string + +const ( + ErrorCreatingAppScaledObject HTTPScaledObjectConditionReason = "ErrorCreatingAppScaledObject" + AppScaledObjectCreated HTTPScaledObjectConditionReason = "AppScaledObjectCreated" + TerminatingResources HTTPScaledObjectConditionReason = "TerminatingResources" + AppScaledObjectTerminated HTTPScaledObjectConditionReason = "AppScaledObjectTerminated" + AppScaledObjectTerminationError HTTPScaledObjectConditionReason = "AppScaledObjectTerminationError" + PendingCreation HTTPScaledObjectConditionReason = "PendingCreation" + HTTPScaledObjectIsReady HTTPScaledObjectConditionReason = "HTTPScaledObjectIsReady" +) + +// HTTPScaledObjectCondition stores the condition state +type HTTPScaledObjectCondition struct { + // Timestamp of the condition + // +optional + Timestamp string `json:"timestamp" description:"Timestamp of this condition"` + // Type of condition + // +required + Type HTTPScaledObjectCreationStatus `json:"type" description:"type of status condition"` + // Status of the condition, one of True, False, Unknown. + // +required + Status metav1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` + // Reason for the condition's last transition. + // +optional + Reason HTTPScaledObjectConditionReason `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` + // Message indicating details about the transition. + // +optional + Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` +} + +type Conditions []HTTPScaledObjectCondition + +// GetReadyCondition returns Condition of type Ready +func (c *Conditions) GetReadyCondition() HTTPScaledObjectCondition { + if *c == nil { + c = GetInitializedConditions() + } + return c.getCondition(Ready) +} + +// GetInitializedConditions returns Conditions initialized to the default -> Status: Unknown +func GetInitializedConditions() *Conditions { + return &Conditions{{Type: Ready, Status: metav1.ConditionUnknown}} +} + +// IsTrue is true if the condition is True +func (c *HTTPScaledObjectCondition) IsTrue() bool { + if c == nil { + return false + } + return c.Status == metav1.ConditionTrue +} + +// IsFalse is true if the condition is False +func (c *HTTPScaledObjectCondition) IsFalse() bool { + if c == nil { + return false + } + return c.Status == metav1.ConditionFalse +} + +func (c Conditions) getCondition(conditionType HTTPScaledObjectCreationStatus) HTTPScaledObjectCondition { + for i := range c { + if c[i].Type == conditionType { + return c[i] + } + } + return HTTPScaledObjectCondition{} +} diff --git a/pkg/apis/http/v1alpha1/doc.go b/pkg/apis/http/v1alpha1/doc.go new file mode 100644 index 000000000..47b747cf1 --- /dev/null +++ b/pkg/apis/http/v1alpha1/doc.go @@ -0,0 +1,5 @@ +// +k8s:deepcopy-gen=package + +// Package v1 is the v1 version of the API. +// +groupName=http.keda.sh +package v1alpha1 diff --git a/pkg/apis/http/v1alpha1/httpscaledobject_types.go b/pkg/apis/http/v1alpha1/httpscaledobject_types.go new file mode 100644 index 000000000..f1f61b04f --- /dev/null +++ b/pkg/apis/http/v1alpha1/httpscaledobject_types.go @@ -0,0 +1,183 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ScalingSetKind string + +const ( + HTTPScalingSetKind ScalingSetKind = "HTTPScalingSet" + ClusterHTTPScalingSetKind ScalingSetKind = "ClusterHTTPScalingSet" +) + +// ScaleTargetRef contains all the details about an HTTP application to scale and route to +type ScaleTargetRef struct { + // +optional + Name string `json:"name"` + // +optional + APIVersion string `json:"apiVersion,omitempty"` + // +optional + Kind string `json:"kind,omitempty"` + // The name of the service to route to + Service string `json:"service"` + // The port to route to + Port int32 `json:"port"` +} + +// ReplicaStruct contains the minimum and maximum amount of replicas to have in the deployment +type ReplicaStruct struct { + // Minimum amount of replicas to have in the deployment (Default 0) + Min *int32 `json:"min,omitempty" description:"Minimum amount of replicas to have in the deployment (Default 0)"` + // Maximum amount of replicas to have in the deployment (Default 100) + Max *int32 `json:"max,omitempty" description:"Maximum amount of replicas to have in the deployment (Default 100)"` +} + +// ScalingMetricSpec contains the scaling calculation type +type ScalingMetricSpec struct { + // Scaling based on concurrent requests for a given target + Concurrency *ConcurrencyMetricSpec `json:"concurrency,omitempty" description:"Scaling based on concurrent requests for a given target. 'concurrency' and 'rate' are mutually exclusive."` + // Scaling based the average rate during an specific time window for a given target + Rate *RateMetricSpec `json:"requestRate,omitempty" description:"Scaling based the average rate during an specific time window for a given target. 'concurrency' and 'rate' are mutually exclusive."` +} + +// ConcurrencyMetricSpec defines the concurrency scaling +type ConcurrencyMetricSpec struct { + // Target value for rate scaling + // +kubebuilder:default=100 + // +optional + TargetValue int `json:"targetValue" description:"Target value for concurrency scaling"` +} + +// RateMetricSpec defines the concurrency scaling +type RateMetricSpec struct { + // Target value for rate scaling + // +kubebuilder:default=100 + // +optional + TargetValue int `json:"targetValue" description:"Target value for rate scaling"` + // Time window for rate calculation + // +kubebuilder:default="1m" + // +optional + Window metav1.Duration `json:"window" description:"Time window for rate calculation"` + // Time granularity for rate calculation + // +kubebuilder:default="1s" + // +optional + Granularity metav1.Duration `json:"granularity" description:"Time granularity for rate calculation"` +} + +// HTTPSalingSetTargetRef defines the desired scaling set to be used +type HTTPSalingSetTargetRef struct { + // Name of the scaling set + Name string `json:"name,omitempty"` + // Kind of the resource being referred to. Defaults to HTTPScalingSet. + // +kubebuilder:validation:Enum=HTTPScalingSet;ClusterHTTPScalingSet + // +optional + Kind ScalingSetKind `json:"kind,omitempty"` +} + +func (so *HTTPScaledObjectSpec) GetHTTPSalingSetTargetRef() HTTPSalingSetTargetRef { + r := HTTPSalingSetTargetRef{} + if so.ScalingSet == nil { + return r + } + + r.Name = so.ScalingSet.Name + r.Kind = ClusterHTTPScalingSetKind + if so.ScalingSet.Kind != "" { + r.Kind = so.ScalingSet.Kind + } + return r +} + +// HTTPScaledObjectSpec defines the desired state of HTTPScaledObject +type HTTPScaledObjectSpec struct { + // ScalingSet to be used for this HTTPScaledObject, if empty, default + // interceptor and scaler will be used + // +optional + ScalingSet *HTTPSalingSetTargetRef `json:"scalingSet,omitempty"` + // The hosts to route. All requests which the "Host" header + // matches any .spec.hosts (and the Request Target matches any + // .spec.pathPrefixes) will be routed to the Service and Port specified in + // the scaleTargetRef. + Hosts []string `json:"hosts,omitempty"` + // The paths to route. All requests which the Request Target matches any + // .spec.pathPrefixes (and the "Host" header matches any .spec.hosts) + // will be routed to the Service and Port specified in + // the scaleTargetRef. + // +optional + PathPrefixes []string `json:"pathPrefixes,omitempty"` + // The name of the deployment to route HTTP requests to (and to autoscale). + ScaleTargetRef ScaleTargetRef `json:"scaleTargetRef"` + // (optional) Replica information + // +optional + Replicas *ReplicaStruct `json:"replicas,omitempty"` + // (optional) DEPRECATED (use ScalingMetric instead) Target metric value + // +optional + TargetPendingRequests *int32 `json:"targetPendingRequests,omitempty" description:"The target metric value for the HPA (Default 100)"` + // (optional) Cooldown period value + // +optional + CooldownPeriod *int32 `json:"scaledownPeriod,omitempty" description:"Cooldown period (seconds) for resources to scale down (Default 300)"` + // (optional) Configuration for the metric used for scaling + // +optional + ScalingMetric *ScalingMetricSpec `json:"scalingMetric,omitempty" description:"Configuration for the metric used for scaling. If empty 'concurrency' will be used"` +} + +// HTTPScaledObjectStatus defines the observed state of HTTPScaledObject +type HTTPScaledObjectStatus struct { + // TargetWorkload reflects details about the scaled workload. + // +optional + TargetWorkload string `json:"targetWorkload,omitempty" description:"It reflects details about the scaled workload"` + // TargetService reflects details about the scaled service. + // +optional + TargetService string `json:"targetService,omitempty" description:"It reflects details about the scaled service"` + // Conditions of the operator + Conditions Conditions `json:"conditions,omitempty" description:"List of auditable conditions of the operator"` +} + +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:printcolumn:name="TargetWorkload",type="string",JSONPath=".status.targetWorkload" +// +kubebuilder:printcolumn:name="TargetService",type="string",JSONPath=".status.targetService" +// +kubebuilder:printcolumn:name="MinReplicas",type="integer",JSONPath=".spec.replicas.min" +// +kubebuilder:printcolumn:name="MaxReplicas",type="integer",JSONPath=".spec.replicas.max" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Active",type="string",JSONPath=".status.conditions[?(@.type==\"HTTPScaledObjectIsReady\")].status" +// +kubebuilder:resource:shortName=httpso +// +kubebuilder:subresource:status + +// HTTPScaledObject is the Schema for the httpscaledobjects API +type HTTPScaledObject struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HTTPScaledObjectSpec `json:"spec"` + Status HTTPScaledObjectStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// HTTPScaledObjectList contains a list of HTTPScaledObject +type HTTPScaledObjectList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []HTTPScaledObject `json:"items"` +} diff --git a/pkg/apis/http/v1alpha1/httpscalingset_defaults.go b/pkg/apis/http/v1alpha1/httpscalingset_defaults.go new file mode 100644 index 000000000..0f8a4f6e1 --- /dev/null +++ b/pkg/apis/http/v1alpha1/httpscalingset_defaults.go @@ -0,0 +1,149 @@ +/* +Copyright 2024 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" +) + +var ( + interceptorImage = fmt.Sprintf("ghcr.io/kedacore/http-add-on-interceptor:%s", Version()) + scalerImage = fmt.Sprintf("ghcr.io/kedacore/http-add-on-scaler:%s", Version()) +) + +func (c *HTTPInterceptorSpec) GetProxyPort() int32 { + if c.Config == nil || c.Config.ProxyPort == nil { + return 8080 + } + return *c.Config.ProxyPort +} + +func (c *HTTPInterceptorSpec) GetAdminPort() int32 { + if c.Config == nil || c.Config.AdminPort == nil { + return 9090 + } + return *c.Config.AdminPort +} +func (c *HTTPInterceptorSpec) GetConnectTimeout() string { + if c.Config == nil || c.Config.ConnectTimeout == nil { + return "500ms" + } + return *c.Config.ConnectTimeout +} +func (c *HTTPInterceptorSpec) GetHeaderTimeout() string { + if c.Config == nil || c.Config.HeaderTimeout == nil { + return "500ms" + } + return *c.Config.HeaderTimeout +} +func (c *HTTPInterceptorSpec) GetWaitTimeout() string { + if c.Config == nil || c.Config.WaitTimeout == nil { + return "1500ms" + } + return *c.Config.WaitTimeout +} +func (c *HTTPInterceptorSpec) GetIdleConnTimeout() string { + if c.Config == nil || c.Config.IdleConnTimeout == nil { + return "90s" + } + return *c.Config.IdleConnTimeout +} +func (c *HTTPInterceptorSpec) GetTLSHandshakeTimeout() string { + if c.Config == nil || c.Config.TLSHandshakeTimeout == nil { + return "10s" + } + return *c.Config.TLSHandshakeTimeout +} +func (c *HTTPInterceptorSpec) GetExpectContinueTimeout() string { + if c.Config == nil || c.Config.ExpectContinueTimeout == nil { + return "1s" + } + return *c.Config.ExpectContinueTimeout +} +func (c *HTTPInterceptorSpec) GetForceHTTP2() bool { + if c.Config == nil || c.Config.ForceHTTP2 == nil { + return false + } + return *c.Config.ForceHTTP2 +} +func (c *HTTPInterceptorSpec) GetKeepAlive() string { + if c.Config == nil || c.Config.KeepAlive == nil { + return "1s" + } + return *c.Config.KeepAlive +} +func (c *HTTPInterceptorSpec) GetMaxIdleConns() int { + if c.Config == nil || c.Config.MaxIdleConns == nil { + return 100 + } + return *c.Config.MaxIdleConns +} +func (c *HTTPInterceptorSpec) GetPollingInterval() int { + if c.Config == nil || c.Config.PollingInterval == nil { + return 1000 + } + return *c.Config.PollingInterval +} + +func (c *HTTPInterceptorSpec) GetImage() string { + if c.Image == nil { + return interceptorImage + } + return *c.Image +} + +func (c *HTTPInterceptorSpec) GetLabels() map[string]string { + if c.Labels == nil { + return map[string]string{} + } + return c.Labels +} + +func (c *HTTPInterceptorSpec) GetAnnotations() map[string]string { + if c.Annotations == nil { + return map[string]string{} + } + return c.Annotations +} + +func (c *HTTPScalerSpec) GetPort() int32 { + if c.Config.Port == nil { + return 9090 + } + return *c.Config.Port +} + +func (c *HTTPScalerSpec) GetImage() string { + if c.Image == nil { + return scalerImage + } + return *c.Image +} + +func (c *HTTPScalerSpec) GetLabels() map[string]string { + if c.Labels == nil { + return map[string]string{} + } + return c.Labels +} + +func (c *HTTPScalerSpec) GetAnnotations() map[string]string { + if c.Annotations == nil { + return map[string]string{} + } + return c.Annotations +} diff --git a/pkg/apis/http/v1alpha1/httpscalingset_types.go b/pkg/apis/http/v1alpha1/httpscalingset_types.go new file mode 100644 index 000000000..bedf621cd --- /dev/null +++ b/pkg/apis/http/v1alpha1/httpscalingset_types.go @@ -0,0 +1,233 @@ +/* +Copyright 2024 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// HTTPInterceptorScalingSpec defines the desired state of Interceptor autoscaling +type HTTPInterceptorScalingSpec struct { + // +kubebuilder:default=3 + // +optional + // Minimum replicas for the interceptor + MinReplicas int `json:"minReplicas"` + // +kubebuilder:default=100 + // +optional + // Maximum replicas for the interceptor + MaxReplicas int `json:"maxReplicas"` + // +kubebuilder:default=100 + // +optional + // Target concurrent requests + Target int `json:"target"` +} + +// HTTPInterceptorConfigurationSpec defines the desired state of Interceptor configuration +type HTTPInterceptorConfigurationSpec struct { + // +optional + // Port to be used for proxy operations + ProxyPort *int32 `json:"proxyPort,omitempty"` + // +optional + // Port to be used for admin operations + AdminPort *int32 `json:"adminPort,omitempty"` + // +optional + // Timeout for establishing the connection + ConnectTimeout *string `json:"connectTimeout,omitempty"` + // +optional + // How long to wait between when the HTTP request + // is sent to the backing app and when response headers need to arrive + HeaderTimeout *string `json:"headerTimeout,omitempty"` + // +optional + // How long to wait for the backing workload + // to have 1 or more replicas before connecting and sending the HTTP request. + WaitTimeout *string `json:"waitTimeout,omitempty"` + // +optional + // Timeout after which a connection in the interceptor's + // internal connection pool will be closed + IdleConnTimeout *string `json:"idleConnTimeout,omitempty"` + // +optional + // Max amount of time the interceptor will + // wait to establish a TLS connection + TLSHandshakeTimeout *string `json:"handshakeTimeout,omitempty"` + // +optional + // Max amount of time the interceptor will wait + // after sending request headers if the server returned an Expect: 100-continue + // header + ExpectContinueTimeout *string `json:"expectContinueTimeout,omitempty"` + // +optional + // Try to force HTTP2 for all requests + ForceHTTP2 *bool `json:"forceHTTP2,omitempty"` + // +optional + // Interval between keepalive probes + KeepAlive *string `json:"keepAlive,omitempty"` + // +optional + // Max number of connections that can be idle in the + // interceptor's internal connection pool + MaxIdleConns *int `json:"maxIdleConnections,omitempty"` + // +optional + // The interceptor has an internal process that periodically fetches the state + // of endpoints that is running the servers it forwards to. + // This is the interval (in milliseconds) representing how often to do a fetch + PollingInterval *int `json:"pollingInterval,omitempty"` +} + +// HTTPInterceptorSpec defines the desired state of Interceptor component +type HTTPInterceptorSpec struct { + // +optional + // Traffic configuration + Config *HTTPInterceptorConfigurationSpec `json:"config,omitempty"` + // Number of replicas for the interceptor + Replicas *int32 `json:"replicas,omitempty"` + // Container image name. + // +optional + Image *string `json:"image,omitempty"` + // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + // If specified, these secrets will be passed to individual puller implementations for them to use. + // More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + // +optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + // Autoscaling options for the interceptor + Autoscaling *HTTPInterceptorScalingSpec `json:"autoscaling,omitempty"` + // Compute Resources required by this interceptor. + // Cannot be updated. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // +optional + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + // Map of string keys and values that can be used to organize and categorize + // (scope and select) objects. May match selectors of replication controllers + // and services. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + // +optional + Labels map[string]string `json:"labels,omitempty"` + // Annotations is an unstructured key value map stored with a resource that may be + // set by external tools to store and retrieve arbitrary metadata. They are not + // queryable and should be preserved when modifying objects. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + // +kubebuilder:default=default + // +optional + // Name of the service account to be used + ServiceAccountName string `json:"serviceAccountName"` +} + +// HTTPScalerConfigurationSpec defines the desired state of scaler configuration +type HTTPScalerConfigurationSpec struct { + // +kubebuilder:default=9090 + // +optional + // Port to be used for proxy operations + Port *int32 `json:"port,omitempty"` +} + +// HTTPScalerSpec defines the desired state of Scaler component +type HTTPScalerSpec struct { + // +kubebuilder:default={} + // +optional + // Traffic configuration + Config HTTPScalerConfigurationSpec `json:"config,omitempty"` + // Number of replicas for the interceptor + Replicas *int32 `json:"replicas,omitempty"` + // Container image name. + // +optional + Image *string `json:"image,omitempty"` + // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + // If specified, these secrets will be passed to individual puller implementations for them to use. + // More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + // +optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + // Compute Resources required by this scaler. + // Cannot be updated. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // +optional + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + // Map of string keys and values that can be used to organize and categorize + // (scope and select) objects. May match selectors of replication controllers + // and services. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + // +optional + Labels map[string]string `json:"labels,omitempty"` + // Annotations is an unstructured key value map stored with a resource that may be + // set by external tools to store and retrieve arbitrary metadata. They are not + // queryable and should be preserved when modifying objects. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + // +kubebuilder:default=default + // +optional + // Name of the service account to be used + ServiceAccountName string `json:"serviceAccountName"` +} + +// HTTPScalingSetSpec defines the desired state of HTTPScalingSet +type HTTPScalingSetSpec struct { + Interceptor HTTPInterceptorSpec `json:"interceptor"` + Scaler HTTPScalerSpec `json:"scaler"` +} + +// HTTPScalingSetStatus defines the observed state of HTTPScalingSet +type HTTPScalingSetStatus struct{} + +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:shortName=ss +// +kubebuilder:subresource:status + +// HTTPScalingSet is the Schema for the httpscalingset API +type HTTPScalingSet struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HTTPScalingSetSpec `json:"spec,omitempty"` + Status HTTPScalingSetStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// HTTPScalingSetList contains a list of HTTPScalingSetList +type HTTPScalingSetList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []HTTPScalingSet `json:"items"` +} + +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:shortName=css,scope=Cluster +// +kubebuilder:subresource:status + +// ClusterHTTPScalingSet is the Schema for the cluster httpscalingset API +type ClusterHTTPScalingSet struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HTTPScalingSetSpec `json:"spec,omitempty"` + Status HTTPScalingSetStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// ClusterHTTPScalingSetList contains a list of ClusterHTTPScalingSet +type ClusterHTTPScalingSetList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterHTTPScalingSet `json:"items"` +} diff --git a/pkg/apis/http/v1alpha1/register.go b/pkg/apis/http/v1alpha1/register.go new file mode 100644 index 000000000..6af69e227 --- /dev/null +++ b/pkg/apis/http/v1alpha1/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/fluxcd/flagger/pkg/apis/http" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: http.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &HTTPScaledObject{}, + &HTTPScaledObjectList{}, + &HTTPScalingSet{}, + &HTTPScalingSetList{}, + &ClusterHTTPScalingSet{}, + &ClusterHTTPScalingSetList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/http/v1alpha1/version.go b/pkg/apis/http/v1alpha1/version.go new file mode 100644 index 000000000..af463fcc2 --- /dev/null +++ b/pkg/apis/http/v1alpha1/version.go @@ -0,0 +1,30 @@ +package v1alpha1 + +import ( + "fmt" + "runtime" + + "github.com/go-logr/logr" +) + +var ( + version = "main" + gitCommit string +) + +// Version returns the current git SHA of commit the binary was built from +func Version() string { + return version +} + +// GitCommit stores the current commit hash +func GitCommit() string { + return gitCommit +} + +func PrintComponentInfo(logger logr.Logger, component string) { + logger.Info(fmt.Sprintf("%s Version: %s", component, Version())) + logger.Info(fmt.Sprintf("%s Commit: %s", component, GitCommit())) + logger.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) + logger.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) +} diff --git a/pkg/apis/http/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/http/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..35358fa54 --- /dev/null +++ b/pkg/apis/http/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,687 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterHTTPScalingSet) DeepCopyInto(out *ClusterHTTPScalingSet) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterHTTPScalingSet. +func (in *ClusterHTTPScalingSet) DeepCopy() *ClusterHTTPScalingSet { + if in == nil { + return nil + } + out := new(ClusterHTTPScalingSet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterHTTPScalingSet) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterHTTPScalingSetList) DeepCopyInto(out *ClusterHTTPScalingSetList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterHTTPScalingSet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterHTTPScalingSetList. +func (in *ClusterHTTPScalingSetList) DeepCopy() *ClusterHTTPScalingSetList { + if in == nil { + return nil + } + out := new(ClusterHTTPScalingSetList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterHTTPScalingSetList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConcurrencyMetricSpec) DeepCopyInto(out *ConcurrencyMetricSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConcurrencyMetricSpec. +func (in *ConcurrencyMetricSpec) DeepCopy() *ConcurrencyMetricSpec { + if in == nil { + return nil + } + out := new(ConcurrencyMetricSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Conditions) DeepCopyInto(out *Conditions) { + { + in := &in + *out = make(Conditions, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Conditions. +func (in Conditions) DeepCopy() Conditions { + if in == nil { + return nil + } + out := new(Conditions) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPInterceptorConfigurationSpec) DeepCopyInto(out *HTTPInterceptorConfigurationSpec) { + *out = *in + if in.ProxyPort != nil { + in, out := &in.ProxyPort, &out.ProxyPort + *out = new(int32) + **out = **in + } + if in.AdminPort != nil { + in, out := &in.AdminPort, &out.AdminPort + *out = new(int32) + **out = **in + } + if in.ConnectTimeout != nil { + in, out := &in.ConnectTimeout, &out.ConnectTimeout + *out = new(string) + **out = **in + } + if in.HeaderTimeout != nil { + in, out := &in.HeaderTimeout, &out.HeaderTimeout + *out = new(string) + **out = **in + } + if in.WaitTimeout != nil { + in, out := &in.WaitTimeout, &out.WaitTimeout + *out = new(string) + **out = **in + } + if in.IdleConnTimeout != nil { + in, out := &in.IdleConnTimeout, &out.IdleConnTimeout + *out = new(string) + **out = **in + } + if in.TLSHandshakeTimeout != nil { + in, out := &in.TLSHandshakeTimeout, &out.TLSHandshakeTimeout + *out = new(string) + **out = **in + } + if in.ExpectContinueTimeout != nil { + in, out := &in.ExpectContinueTimeout, &out.ExpectContinueTimeout + *out = new(string) + **out = **in + } + if in.ForceHTTP2 != nil { + in, out := &in.ForceHTTP2, &out.ForceHTTP2 + *out = new(bool) + **out = **in + } + if in.KeepAlive != nil { + in, out := &in.KeepAlive, &out.KeepAlive + *out = new(string) + **out = **in + } + if in.MaxIdleConns != nil { + in, out := &in.MaxIdleConns, &out.MaxIdleConns + *out = new(int) + **out = **in + } + if in.PollingInterval != nil { + in, out := &in.PollingInterval, &out.PollingInterval + *out = new(int) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPInterceptorConfigurationSpec. +func (in *HTTPInterceptorConfigurationSpec) DeepCopy() *HTTPInterceptorConfigurationSpec { + if in == nil { + return nil + } + out := new(HTTPInterceptorConfigurationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPInterceptorScalingSpec) DeepCopyInto(out *HTTPInterceptorScalingSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPInterceptorScalingSpec. +func (in *HTTPInterceptorScalingSpec) DeepCopy() *HTTPInterceptorScalingSpec { + if in == nil { + return nil + } + out := new(HTTPInterceptorScalingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPInterceptorSpec) DeepCopyInto(out *HTTPInterceptorSpec) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(HTTPInterceptorConfigurationSpec) + (*in).DeepCopyInto(*out) + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } + if in.Autoscaling != nil { + in, out := &in.Autoscaling, &out.Autoscaling + *out = new(HTTPInterceptorScalingSpec) + **out = **in + } + in.Resources.DeepCopyInto(&out.Resources) + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPInterceptorSpec. +func (in *HTTPInterceptorSpec) DeepCopy() *HTTPInterceptorSpec { + if in == nil { + return nil + } + out := new(HTTPInterceptorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPSalingSetTargetRef) DeepCopyInto(out *HTTPSalingSetTargetRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPSalingSetTargetRef. +func (in *HTTPSalingSetTargetRef) DeepCopy() *HTTPSalingSetTargetRef { + if in == nil { + return nil + } + out := new(HTTPSalingSetTargetRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScaledObject) DeepCopyInto(out *HTTPScaledObject) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScaledObject. +func (in *HTTPScaledObject) DeepCopy() *HTTPScaledObject { + if in == nil { + return nil + } + out := new(HTTPScaledObject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPScaledObject) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScaledObjectCondition) DeepCopyInto(out *HTTPScaledObjectCondition) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScaledObjectCondition. +func (in *HTTPScaledObjectCondition) DeepCopy() *HTTPScaledObjectCondition { + if in == nil { + return nil + } + out := new(HTTPScaledObjectCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScaledObjectList) DeepCopyInto(out *HTTPScaledObjectList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HTTPScaledObject, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScaledObjectList. +func (in *HTTPScaledObjectList) DeepCopy() *HTTPScaledObjectList { + if in == nil { + return nil + } + out := new(HTTPScaledObjectList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPScaledObjectList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScaledObjectSpec) DeepCopyInto(out *HTTPScaledObjectSpec) { + *out = *in + if in.ScalingSet != nil { + in, out := &in.ScalingSet, &out.ScalingSet + *out = new(HTTPSalingSetTargetRef) + **out = **in + } + if in.Hosts != nil { + in, out := &in.Hosts, &out.Hosts + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PathPrefixes != nil { + in, out := &in.PathPrefixes, &out.PathPrefixes + *out = make([]string, len(*in)) + copy(*out, *in) + } + out.ScaleTargetRef = in.ScaleTargetRef + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(ReplicaStruct) + (*in).DeepCopyInto(*out) + } + if in.TargetPendingRequests != nil { + in, out := &in.TargetPendingRequests, &out.TargetPendingRequests + *out = new(int32) + **out = **in + } + if in.CooldownPeriod != nil { + in, out := &in.CooldownPeriod, &out.CooldownPeriod + *out = new(int32) + **out = **in + } + if in.ScalingMetric != nil { + in, out := &in.ScalingMetric, &out.ScalingMetric + *out = new(ScalingMetricSpec) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScaledObjectSpec. +func (in *HTTPScaledObjectSpec) DeepCopy() *HTTPScaledObjectSpec { + if in == nil { + return nil + } + out := new(HTTPScaledObjectSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScaledObjectStatus) DeepCopyInto(out *HTTPScaledObjectStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(Conditions, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScaledObjectStatus. +func (in *HTTPScaledObjectStatus) DeepCopy() *HTTPScaledObjectStatus { + if in == nil { + return nil + } + out := new(HTTPScaledObjectStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalerConfigurationSpec) DeepCopyInto(out *HTTPScalerConfigurationSpec) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalerConfigurationSpec. +func (in *HTTPScalerConfigurationSpec) DeepCopy() *HTTPScalerConfigurationSpec { + if in == nil { + return nil + } + out := new(HTTPScalerConfigurationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalerSpec) DeepCopyInto(out *HTTPScalerSpec) { + *out = *in + in.Config.DeepCopyInto(&out.Config) + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } + in.Resources.DeepCopyInto(&out.Resources) + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalerSpec. +func (in *HTTPScalerSpec) DeepCopy() *HTTPScalerSpec { + if in == nil { + return nil + } + out := new(HTTPScalerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalingSet) DeepCopyInto(out *HTTPScalingSet) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalingSet. +func (in *HTTPScalingSet) DeepCopy() *HTTPScalingSet { + if in == nil { + return nil + } + out := new(HTTPScalingSet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPScalingSet) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalingSetList) DeepCopyInto(out *HTTPScalingSetList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HTTPScalingSet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalingSetList. +func (in *HTTPScalingSetList) DeepCopy() *HTTPScalingSetList { + if in == nil { + return nil + } + out := new(HTTPScalingSetList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPScalingSetList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalingSetSpec) DeepCopyInto(out *HTTPScalingSetSpec) { + *out = *in + in.Interceptor.DeepCopyInto(&out.Interceptor) + in.Scaler.DeepCopyInto(&out.Scaler) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalingSetSpec. +func (in *HTTPScalingSetSpec) DeepCopy() *HTTPScalingSetSpec { + if in == nil { + return nil + } + out := new(HTTPScalingSetSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalingSetStatus) DeepCopyInto(out *HTTPScalingSetStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalingSetStatus. +func (in *HTTPScalingSetStatus) DeepCopy() *HTTPScalingSetStatus { + if in == nil { + return nil + } + out := new(HTTPScalingSetStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RateMetricSpec) DeepCopyInto(out *RateMetricSpec) { + *out = *in + out.Window = in.Window + out.Granularity = in.Granularity + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateMetricSpec. +func (in *RateMetricSpec) DeepCopy() *RateMetricSpec { + if in == nil { + return nil + } + out := new(RateMetricSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReplicaStruct) DeepCopyInto(out *ReplicaStruct) { + *out = *in + if in.Min != nil { + in, out := &in.Min, &out.Min + *out = new(int32) + **out = **in + } + if in.Max != nil { + in, out := &in.Max, &out.Max + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplicaStruct. +func (in *ReplicaStruct) DeepCopy() *ReplicaStruct { + if in == nil { + return nil + } + out := new(ReplicaStruct) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScaleTargetRef) DeepCopyInto(out *ScaleTargetRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScaleTargetRef. +func (in *ScaleTargetRef) DeepCopy() *ScaleTargetRef { + if in == nil { + return nil + } + out := new(ScaleTargetRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalingMetricSpec) DeepCopyInto(out *ScalingMetricSpec) { + *out = *in + if in.Concurrency != nil { + in, out := &in.Concurrency, &out.Concurrency + *out = new(ConcurrencyMetricSpec) + **out = **in + } + if in.Rate != nil { + in, out := &in.Rate, &out.Rate + *out = new(RateMetricSpec) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalingMetricSpec. +func (in *ScalingMetricSpec) DeepCopy() *ScalingMetricSpec { + if in == nil { + return nil + } + out := new(ScalingMetricSpec) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/canary/deployment_controller.go b/pkg/canary/deployment_controller.go index 708f4babe..0781d62ca 100644 --- a/pkg/canary/deployment_controller.go +++ b/pkg/canary/deployment_controller.go @@ -170,6 +170,10 @@ func (c *DeploymentController) ScaleFromZero(cd *flaggerv1.Canary) error { return fmt.Errorf("deployment %s.%s get query error: %w", targetName, cd.Namespace, err) } + if cd.IsHTTPScaledObject() { + return nil + } + replicas := int32p(1) if dep.Spec.Replicas != nil && *dep.Spec.Replicas > 0 { replicas = dep.Spec.Replicas @@ -272,9 +276,12 @@ func (c *DeploymentController) createPrimaryDeployment(cd *flaggerv1.Canary, inc return fmt.Errorf("makeAnnotations failed: %w", err) } - replicas := int32(1) + replicas := int32p(int32(1)) if canaryDep.Spec.Replicas != nil && *canaryDep.Spec.Replicas > 0 { - replicas = *canaryDep.Spec.Replicas + replicas = int32p(*canaryDep.Spec.Replicas) + } + if cd.IsHTTPScaledObject() { + replicas = nil } // create primary deployment @@ -296,7 +303,7 @@ func (c *DeploymentController) createPrimaryDeployment(cd *flaggerv1.Canary, inc ProgressDeadlineSeconds: canaryDep.Spec.ProgressDeadlineSeconds, MinReadySeconds: canaryDep.Spec.MinReadySeconds, RevisionHistoryLimit: canaryDep.Spec.RevisionHistoryLimit, - Replicas: int32p(replicas), + Replicas: replicas, Strategy: canaryDep.Spec.Strategy, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ @@ -380,6 +387,9 @@ func (c *DeploymentController) Finalize(cd *flaggerv1.Canary) error { // Scale sets the canary deployment replicas func (c *DeploymentController) scale(cd *flaggerv1.Canary, replicas int32) error { + if cd.IsHTTPScaledObject() { + return nil + } targetName := cd.Spec.TargetRef.Name dep, err := c.kubeClient.AppsV1().Deployments(cd.Namespace).Get(context.TODO(), targetName, metav1.GetOptions{}) if err != nil { diff --git a/pkg/canary/factory.go b/pkg/canary/factory.go index ec3924f4a..498719f98 100644 --- a/pkg/canary/factory.go +++ b/pkg/canary/factory.go @@ -99,11 +99,20 @@ func (factory *Factory) ScalerReconciler(kind string) ScalerReconciler { includeLabelPrefix: factory.includeLabelPrefix, } + httpsoReconciler := &HTTPScaledObjectReconciler{ + logger: factory.logger, + kubeClient: factory.kubeClient, + flaggerClient: factory.flaggerClient, + includeLabelPrefix: factory.includeLabelPrefix, + } + switch kind { case "HorizontalPodAutoscaler": return hpaReconciler case "ScaledObject": return soReconciler + case "HTTPScaledObject": + return httpsoReconciler default: return nil } diff --git a/pkg/canary/http_scaled_object_reconciler.go b/pkg/canary/http_scaled_object_reconciler.go new file mode 100644 index 000000000..533261fc0 --- /dev/null +++ b/pkg/canary/http_scaled_object_reconciler.go @@ -0,0 +1,130 @@ +package canary + +import ( + "context" + "fmt" + + "go.uber.org/zap" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" + + flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1" + http "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + clientset "github.com/fluxcd/flagger/pkg/client/clientset/versioned" + "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// HTTPScaledObjectReconciler is a ScalerReconciler that reconciles KEDA HTTPScaledObjects. +type HTTPScaledObjectReconciler struct { + kubeClient kubernetes.Interface + flaggerClient clientset.Interface + logger *zap.SugaredLogger + includeLabelPrefix []string +} + +func (httpsor *HTTPScaledObjectReconciler) ReconcilePrimaryScaler(cd *flaggerv1.Canary, init bool) error { + if cd.Spec.AutoscalerRef != nil { + if err := httpsor.reconcilePrimaryScaler(cd, init); err != nil { + return err + } + } + return nil +} + +func (httpsor *HTTPScaledObjectReconciler) reconcilePrimaryScaler(cd *flaggerv1.Canary, init bool) error { + primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name) + targetSo, err := httpsor.flaggerClient.HttpV1alpha1().HTTPScaledObjects(cd.Namespace).Get(context.TODO(), cd.Spec.AutoscalerRef.Name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("Keda HTTPScaledObject %s.%s get query error: %w", + cd.Spec.AutoscalerRef.Name, cd.Namespace, err) + } + targetSoClone := targetSo.DeepCopy() + primaryServiceName := fmt.Sprintf("%s-primary", targetSoClone.Spec.ScaleTargetRef.Service) + + httpsoSpec := http.HTTPScaledObjectSpec{ + Hosts: targetSoClone.Spec.Hosts, + PathPrefixes: targetSoClone.Spec.PathPrefixes, + ScaleTargetRef: http.ScaleTargetRef{ + Name: primaryName, + APIVersion: targetSoClone.Spec.ScaleTargetRef.APIVersion, + Kind: targetSoClone.Spec.ScaleTargetRef.Kind, + Service: primaryServiceName, + Port: targetSoClone.Spec.ScaleTargetRef.Port, + }, + Replicas: targetSoClone.Spec.Replicas, + TargetPendingRequests: targetSoClone.Spec.TargetPendingRequests, + CooldownPeriod: targetSoClone.Spec.CooldownPeriod, + ScalingMetric: targetSoClone.Spec.ScalingMetric, + } + + if scalingSet := cd.Spec.AutoscalerRef.PrimaryScalingSet; scalingSet != nil { + httpsoSpec.ScalingSet = &http.HTTPSalingSetTargetRef{ + Name: scalingSet.Name, + Kind: scalingSet.Kind, + } + } + + if replicas := cd.Spec.AutoscalerRef.PrimaryScalerReplicas; replicas != nil { + if minReplicas := replicas.MinReplicas; minReplicas != nil { + httpsoSpec.Replicas.Min = minReplicas + } + if maxReplicas := replicas.MaxReplicas; maxReplicas != nil { + httpsoSpec.Replicas.Max = maxReplicas + } + } + + primarySoName := fmt.Sprintf("%s-primary", cd.Spec.AutoscalerRef.Name) + primarySo, err := httpsor.flaggerClient.HttpV1alpha1().HTTPScaledObjects(cd.Namespace).Get(context.TODO(), primarySoName, metav1.GetOptions{}) + if errors.IsNotFound(err) { + primarySo = &http.HTTPScaledObject{ + ObjectMeta: makeObjectMeta(primarySoName, targetSoClone.Labels, cd), + Spec: httpsoSpec, + } + _, err = httpsor.flaggerClient.HttpV1alpha1().HTTPScaledObjects(cd.Namespace).Create(context.TODO(), primarySo, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("creating Keda HTTPScaledObject %s.%s failed: %w", + primarySo.Name, primarySo.Namespace, err) + } + httpsor.logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Infof( + "Keda HTTPScaledObject %s.%s created", primarySo.GetName(), cd.Namespace) + return nil + } else if err != nil { + return fmt.Errorf("Keda HTTPScaledObject %s.%s get query failed: %w", + primarySo.Name, primarySo.Namespace, err) + } + + if primarySo != nil && !init { + if diff := cmp.Diff(httpsoSpec, primarySo.Spec); diff != "" { + err = retry.RetryOnConflict(retry.DefaultBackoff, func() error { + primarySo, err := httpsor.flaggerClient.HttpV1alpha1().HTTPScaledObjects(cd.Namespace).Get(context.TODO(), primarySoName, metav1.GetOptions{}) + if err != nil { + return err + } + primarySoClone := primarySo.DeepCopy() + primarySoClone.Spec = httpsoSpec + + filteredAnnotations := includeLabelsByPrefix(primarySo.Annotations, httpsor.includeLabelPrefix) + primarySoClone.Annotations = filteredAnnotations + filteredLabels := includeLabelsByPrefix(primarySo.ObjectMeta.Labels, httpsor.includeLabelPrefix) + primarySoClone.Labels = filteredLabels + + _, err = httpsor.flaggerClient.HttpV1alpha1().HTTPScaledObjects(cd.Namespace).Update(context.TODO(), primarySoClone, metav1.UpdateOptions{}) + return err + }) + if err != nil { + return fmt.Errorf("updating HTTPScaledObject %s.%s failed: %w", primarySoName, cd.Namespace, err) + } + } + } + return nil +} + +func (httpsor *HTTPScaledObjectReconciler) PauseTargetScaler(cd *flaggerv1.Canary) error { + return nil +} + +func (httpsor *HTTPScaledObjectReconciler) ResumeTargetScaler(cd *flaggerv1.Canary) error { + return nil +} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index bde8b926e..555c290fc 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -30,6 +30,7 @@ import ( gatewayapiv1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1" gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1" gloov1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gloo/v1" + httpv1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/http/v1alpha1" networkingv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/istio/v1beta1" kedav1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/keda/v1alpha1" kumav1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/kuma/v1alpha1" @@ -53,6 +54,7 @@ type Interface interface { GatewayapiV1beta1() gatewayapiv1beta1.GatewayapiV1beta1Interface GatewayapiV1() gatewayapiv1.GatewayapiV1Interface GlooV1() gloov1.GlooV1Interface + HttpV1alpha1() httpv1alpha1.HttpV1alpha1Interface NetworkingV1beta1() networkingv1beta1.NetworkingV1beta1Interface KedaV1alpha1() kedav1alpha1.KedaV1alpha1Interface KumaV1alpha1() kumav1alpha1.KumaV1alpha1Interface @@ -74,6 +76,7 @@ type Clientset struct { gatewayapiV1beta1 *gatewayapiv1beta1.GatewayapiV1beta1Client gatewayapiV1 *gatewayapiv1.GatewayapiV1Client glooV1 *gloov1.GlooV1Client + httpV1alpha1 *httpv1alpha1.HttpV1alpha1Client networkingV1beta1 *networkingv1beta1.NetworkingV1beta1Client kedaV1alpha1 *kedav1alpha1.KedaV1alpha1Client kumaV1alpha1 *kumav1alpha1.KumaV1alpha1Client @@ -124,6 +127,11 @@ func (c *Clientset) GlooV1() gloov1.GlooV1Interface { return c.glooV1 } +// HttpV1alpha1 retrieves the HttpV1alpha1Client +func (c *Clientset) HttpV1alpha1() httpv1alpha1.HttpV1alpha1Interface { + return c.httpV1alpha1 +} + // NetworkingV1beta1 retrieves the NetworkingV1beta1Client func (c *Clientset) NetworkingV1beta1() networkingv1beta1.NetworkingV1beta1Interface { return c.networkingV1beta1 @@ -240,6 +248,10 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, if err != nil { return nil, err } + cs.httpV1alpha1, err = httpv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } cs.networkingV1beta1, err = networkingv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient) if err != nil { return nil, err @@ -301,6 +313,7 @@ func New(c rest.Interface) *Clientset { cs.gatewayapiV1beta1 = gatewayapiv1beta1.New(c) cs.gatewayapiV1 = gatewayapiv1.New(c) cs.glooV1 = gloov1.New(c) + cs.httpV1alpha1 = httpv1alpha1.New(c) cs.networkingV1beta1 = networkingv1beta1.New(c) cs.kedaV1alpha1 = kedav1alpha1.New(c) cs.kumaV1alpha1 = kumav1alpha1.New(c) diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index c0fe650b6..2f2256259 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -36,6 +36,8 @@ import ( fakegatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake" gloov1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gloo/v1" fakegloov1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/gloo/v1/fake" + httpv1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/http/v1alpha1" + fakehttpv1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/http/v1alpha1/fake" networkingv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/istio/v1beta1" fakenetworkingv1beta1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/istio/v1beta1/fake" kedav1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/keda/v1alpha1" @@ -149,6 +151,11 @@ func (c *Clientset) GlooV1() gloov1.GlooV1Interface { return &fakegloov1.FakeGlooV1{Fake: &c.Fake} } +// HttpV1alpha1 retrieves the HttpV1alpha1Client +func (c *Clientset) HttpV1alpha1() httpv1alpha1.HttpV1alpha1Interface { + return &fakehttpv1alpha1.FakeHttpV1alpha1{Fake: &c.Fake} +} + // NetworkingV1beta1 retrieves the NetworkingV1beta1Client func (c *Clientset) NetworkingV1beta1() networkingv1beta1.NetworkingV1beta1Interface { return &fakenetworkingv1beta1.FakeNetworkingV1beta1{Fake: &c.Fake} diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 020e785bf..c8f54596f 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -27,6 +27,7 @@ import ( gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" gatewayv1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1" gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1" + httpv1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" networkingv1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1" kedav1alpha1 "github.com/fluxcd/flagger/pkg/apis/keda/v1alpha1" kumav1alpha1 "github.com/fluxcd/flagger/pkg/apis/kuma/v1alpha1" @@ -54,6 +55,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ gatewayapiv1beta1.AddToScheme, gatewayapiv1.AddToScheme, gloov1.AddToScheme, + httpv1alpha1.AddToScheme, networkingv1beta1.AddToScheme, kedav1alpha1.AddToScheme, kumav1alpha1.AddToScheme, diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index 7f446ef79..26164ba7f 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -27,6 +27,7 @@ import ( gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" gatewayv1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1" gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1" + httpv1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" networkingv1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1" kedav1alpha1 "github.com/fluxcd/flagger/pkg/apis/keda/v1alpha1" kumav1alpha1 "github.com/fluxcd/flagger/pkg/apis/kuma/v1alpha1" @@ -54,6 +55,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ gatewayapiv1beta1.AddToScheme, gatewayapiv1.AddToScheme, gloov1.AddToScheme, + httpv1alpha1.AddToScheme, networkingv1beta1.AddToScheme, kedav1alpha1.AddToScheme, kumav1alpha1.AddToScheme, diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/clusterhttpscalingset.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/clusterhttpscalingset.go new file mode 100644 index 000000000..de350b98f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/clusterhttpscalingset.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + scheme "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterHTTPScalingSetsGetter has a method to return a ClusterHTTPScalingSetInterface. +// A group's client should implement this interface. +type ClusterHTTPScalingSetsGetter interface { + ClusterHTTPScalingSets(namespace string) ClusterHTTPScalingSetInterface +} + +// ClusterHTTPScalingSetInterface has methods to work with ClusterHTTPScalingSet resources. +type ClusterHTTPScalingSetInterface interface { + Create(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.CreateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) + Update(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) + UpdateStatus(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterHTTPScalingSet, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterHTTPScalingSetList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterHTTPScalingSet, err error) + ClusterHTTPScalingSetExpansion +} + +// clusterHTTPScalingSets implements ClusterHTTPScalingSetInterface +type clusterHTTPScalingSets struct { + client rest.Interface + ns string +} + +// newClusterHTTPScalingSets returns a ClusterHTTPScalingSets +func newClusterHTTPScalingSets(c *HttpV1alpha1Client, namespace string) *clusterHTTPScalingSets { + return &clusterHTTPScalingSets{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the clusterHTTPScalingSet, and returns the corresponding clusterHTTPScalingSet object, and an error if there is any. +func (c *clusterHTTPScalingSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Get(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterHTTPScalingSets that match those selectors. +func (c *clusterHTTPScalingSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterHTTPScalingSetList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ClusterHTTPScalingSetList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterHTTPScalingSets. +func (c *clusterHTTPScalingSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterHTTPScalingSet and creates it. Returns the server's representation of the clusterHTTPScalingSet, and an error, if there is any. +func (c *clusterHTTPScalingSets) Create(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.CreateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Post(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterHTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterHTTPScalingSet and updates it. Returns the server's representation of the clusterHTTPScalingSet, and an error, if there is any. +func (c *clusterHTTPScalingSets) Update(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Put(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(clusterHTTPScalingSet.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterHTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *clusterHTTPScalingSets) UpdateStatus(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Put(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(clusterHTTPScalingSet.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterHTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterHTTPScalingSet and deletes it. Returns an error if one occurs. +func (c *clusterHTTPScalingSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterHTTPScalingSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterHTTPScalingSet. +func (c *clusterHTTPScalingSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/doc.go new file mode 100644 index 000000000..9c7b8cc3b --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/doc.go new file mode 100644 index 000000000..1ccd91197 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_clusterhttpscalingset.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_clusterhttpscalingset.go new file mode 100644 index 000000000..7c8dffdb2 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_clusterhttpscalingset.go @@ -0,0 +1,141 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeClusterHTTPScalingSets implements ClusterHTTPScalingSetInterface +type FakeClusterHTTPScalingSets struct { + Fake *FakeHttpV1alpha1 + ns string +} + +var clusterhttpscalingsetsResource = v1alpha1.SchemeGroupVersion.WithResource("clusterhttpscalingsets") + +var clusterhttpscalingsetsKind = v1alpha1.SchemeGroupVersion.WithKind("ClusterHTTPScalingSet") + +// Get takes name of the clusterHTTPScalingSet, and returns the corresponding clusterHTTPScalingSet object, and an error if there is any. +func (c *FakeClusterHTTPScalingSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(clusterhttpscalingsetsResource, c.ns, name), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} + +// List takes label and field selectors, and returns the list of ClusterHTTPScalingSets that match those selectors. +func (c *FakeClusterHTTPScalingSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterHTTPScalingSetList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(clusterhttpscalingsetsResource, clusterhttpscalingsetsKind, c.ns, opts), &v1alpha1.ClusterHTTPScalingSetList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ClusterHTTPScalingSetList{ListMeta: obj.(*v1alpha1.ClusterHTTPScalingSetList).ListMeta} + for _, item := range obj.(*v1alpha1.ClusterHTTPScalingSetList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested clusterHTTPScalingSets. +func (c *FakeClusterHTTPScalingSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(clusterhttpscalingsetsResource, c.ns, opts)) + +} + +// Create takes the representation of a clusterHTTPScalingSet and creates it. Returns the server's representation of the clusterHTTPScalingSet, and an error, if there is any. +func (c *FakeClusterHTTPScalingSets) Create(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.CreateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(clusterhttpscalingsetsResource, c.ns, clusterHTTPScalingSet), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} + +// Update takes the representation of a clusterHTTPScalingSet and updates it. Returns the server's representation of the clusterHTTPScalingSet, and an error, if there is any. +func (c *FakeClusterHTTPScalingSets) Update(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(clusterhttpscalingsetsResource, c.ns, clusterHTTPScalingSet), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeClusterHTTPScalingSets) UpdateStatus(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(clusterhttpscalingsetsResource, "status", c.ns, clusterHTTPScalingSet), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} + +// Delete takes name of the clusterHTTPScalingSet and deletes it. Returns an error if one occurs. +func (c *FakeClusterHTTPScalingSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(clusterhttpscalingsetsResource, c.ns, name, opts), &v1alpha1.ClusterHTTPScalingSet{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeClusterHTTPScalingSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(clusterhttpscalingsetsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.ClusterHTTPScalingSetList{}) + return err +} + +// Patch applies the patch and returns the patched clusterHTTPScalingSet. +func (c *FakeClusterHTTPScalingSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(clusterhttpscalingsetsResource, c.ns, name, pt, data, subresources...), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_http_client.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_http_client.go new file mode 100644 index 000000000..b495666c6 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_http_client.go @@ -0,0 +1,48 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/fluxcd/flagger/pkg/client/clientset/versioned/typed/http/v1alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeHttpV1alpha1 struct { + *testing.Fake +} + +func (c *FakeHttpV1alpha1) ClusterHTTPScalingSets(namespace string) v1alpha1.ClusterHTTPScalingSetInterface { + return &FakeClusterHTTPScalingSets{c, namespace} +} + +func (c *FakeHttpV1alpha1) HTTPScaledObjects(namespace string) v1alpha1.HTTPScaledObjectInterface { + return &FakeHTTPScaledObjects{c, namespace} +} + +func (c *FakeHttpV1alpha1) HTTPScalingSets(namespace string) v1alpha1.HTTPScalingSetInterface { + return &FakeHTTPScalingSets{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeHttpV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscaledobject.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscaledobject.go new file mode 100644 index 000000000..a36f62af8 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscaledobject.go @@ -0,0 +1,141 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeHTTPScaledObjects implements HTTPScaledObjectInterface +type FakeHTTPScaledObjects struct { + Fake *FakeHttpV1alpha1 + ns string +} + +var httpscaledobjectsResource = v1alpha1.SchemeGroupVersion.WithResource("httpscaledobjects") + +var httpscaledobjectsKind = v1alpha1.SchemeGroupVersion.WithKind("HTTPScaledObject") + +// Get takes name of the hTTPScaledObject, and returns the corresponding hTTPScaledObject object, and an error if there is any. +func (c *FakeHTTPScaledObjects) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPScaledObject, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(httpscaledobjectsResource, c.ns, name), &v1alpha1.HTTPScaledObject{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScaledObject), err +} + +// List takes label and field selectors, and returns the list of HTTPScaledObjects that match those selectors. +func (c *FakeHTTPScaledObjects) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPScaledObjectList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(httpscaledobjectsResource, httpscaledobjectsKind, c.ns, opts), &v1alpha1.HTTPScaledObjectList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.HTTPScaledObjectList{ListMeta: obj.(*v1alpha1.HTTPScaledObjectList).ListMeta} + for _, item := range obj.(*v1alpha1.HTTPScaledObjectList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested hTTPScaledObjects. +func (c *FakeHTTPScaledObjects) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(httpscaledobjectsResource, c.ns, opts)) + +} + +// Create takes the representation of a hTTPScaledObject and creates it. Returns the server's representation of the hTTPScaledObject, and an error, if there is any. +func (c *FakeHTTPScaledObjects) Create(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.CreateOptions) (result *v1alpha1.HTTPScaledObject, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(httpscaledobjectsResource, c.ns, hTTPScaledObject), &v1alpha1.HTTPScaledObject{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScaledObject), err +} + +// Update takes the representation of a hTTPScaledObject and updates it. Returns the server's representation of the hTTPScaledObject, and an error, if there is any. +func (c *FakeHTTPScaledObjects) Update(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (result *v1alpha1.HTTPScaledObject, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(httpscaledobjectsResource, c.ns, hTTPScaledObject), &v1alpha1.HTTPScaledObject{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScaledObject), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeHTTPScaledObjects) UpdateStatus(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (*v1alpha1.HTTPScaledObject, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(httpscaledobjectsResource, "status", c.ns, hTTPScaledObject), &v1alpha1.HTTPScaledObject{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScaledObject), err +} + +// Delete takes name of the hTTPScaledObject and deletes it. Returns an error if one occurs. +func (c *FakeHTTPScaledObjects) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(httpscaledobjectsResource, c.ns, name, opts), &v1alpha1.HTTPScaledObject{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeHTTPScaledObjects) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(httpscaledobjectsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.HTTPScaledObjectList{}) + return err +} + +// Patch applies the patch and returns the patched hTTPScaledObject. +func (c *FakeHTTPScaledObjects) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScaledObject, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(httpscaledobjectsResource, c.ns, name, pt, data, subresources...), &v1alpha1.HTTPScaledObject{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScaledObject), err +} diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscalingset.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscalingset.go new file mode 100644 index 000000000..5d134df15 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscalingset.go @@ -0,0 +1,141 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeHTTPScalingSets implements HTTPScalingSetInterface +type FakeHTTPScalingSets struct { + Fake *FakeHttpV1alpha1 + ns string +} + +var httpscalingsetsResource = v1alpha1.SchemeGroupVersion.WithResource("httpscalingsets") + +var httpscalingsetsKind = v1alpha1.SchemeGroupVersion.WithKind("HTTPScalingSet") + +// Get takes name of the hTTPScalingSet, and returns the corresponding hTTPScalingSet object, and an error if there is any. +func (c *FakeHTTPScalingSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(httpscalingsetsResource, c.ns, name), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} + +// List takes label and field selectors, and returns the list of HTTPScalingSets that match those selectors. +func (c *FakeHTTPScalingSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPScalingSetList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(httpscalingsetsResource, httpscalingsetsKind, c.ns, opts), &v1alpha1.HTTPScalingSetList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.HTTPScalingSetList{ListMeta: obj.(*v1alpha1.HTTPScalingSetList).ListMeta} + for _, item := range obj.(*v1alpha1.HTTPScalingSetList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested hTTPScalingSets. +func (c *FakeHTTPScalingSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(httpscalingsetsResource, c.ns, opts)) + +} + +// Create takes the representation of a hTTPScalingSet and creates it. Returns the server's representation of the hTTPScalingSet, and an error, if there is any. +func (c *FakeHTTPScalingSets) Create(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.CreateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(httpscalingsetsResource, c.ns, hTTPScalingSet), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} + +// Update takes the representation of a hTTPScalingSet and updates it. Returns the server's representation of the hTTPScalingSet, and an error, if there is any. +func (c *FakeHTTPScalingSets) Update(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(httpscalingsetsResource, c.ns, hTTPScalingSet), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeHTTPScalingSets) UpdateStatus(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.HTTPScalingSet, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(httpscalingsetsResource, "status", c.ns, hTTPScalingSet), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} + +// Delete takes name of the hTTPScalingSet and deletes it. Returns an error if one occurs. +func (c *FakeHTTPScalingSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(httpscalingsetsResource, c.ns, name, opts), &v1alpha1.HTTPScalingSet{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeHTTPScalingSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(httpscalingsetsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.HTTPScalingSetList{}) + return err +} + +// Patch applies the patch and returns the patched hTTPScalingSet. +func (c *FakeHTTPScalingSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(httpscalingsetsResource, c.ns, name, pt, data, subresources...), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/generated_expansion.go new file mode 100644 index 000000000..2244fdafb --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/generated_expansion.go @@ -0,0 +1,25 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type ClusterHTTPScalingSetExpansion interface{} + +type HTTPScaledObjectExpansion interface{} + +type HTTPScalingSetExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/http_client.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/http_client.go new file mode 100644 index 000000000..39d27e87a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/http_client.go @@ -0,0 +1,117 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "net/http" + + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type HttpV1alpha1Interface interface { + RESTClient() rest.Interface + ClusterHTTPScalingSetsGetter + HTTPScaledObjectsGetter + HTTPScalingSetsGetter +} + +// HttpV1alpha1Client is used to interact with features provided by the http.keda.sh group. +type HttpV1alpha1Client struct { + restClient rest.Interface +} + +func (c *HttpV1alpha1Client) ClusterHTTPScalingSets(namespace string) ClusterHTTPScalingSetInterface { + return newClusterHTTPScalingSets(c, namespace) +} + +func (c *HttpV1alpha1Client) HTTPScaledObjects(namespace string) HTTPScaledObjectInterface { + return newHTTPScaledObjects(c, namespace) +} + +func (c *HttpV1alpha1Client) HTTPScalingSets(namespace string) HTTPScalingSetInterface { + return newHTTPScalingSets(c, namespace) +} + +// NewForConfig creates a new HttpV1alpha1Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). +func NewForConfig(c *rest.Config) (*HttpV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new HttpV1alpha1Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*HttpV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientForConfigAndClient(&config, h) + if err != nil { + return nil, err + } + return &HttpV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new HttpV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *HttpV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new HttpV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *HttpV1alpha1Client { + return &HttpV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *HttpV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/httpscaledobject.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/httpscaledobject.go new file mode 100644 index 000000000..de0e6ab24 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/httpscaledobject.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + scheme "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// HTTPScaledObjectsGetter has a method to return a HTTPScaledObjectInterface. +// A group's client should implement this interface. +type HTTPScaledObjectsGetter interface { + HTTPScaledObjects(namespace string) HTTPScaledObjectInterface +} + +// HTTPScaledObjectInterface has methods to work with HTTPScaledObject resources. +type HTTPScaledObjectInterface interface { + Create(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.CreateOptions) (*v1alpha1.HTTPScaledObject, error) + Update(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (*v1alpha1.HTTPScaledObject, error) + UpdateStatus(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (*v1alpha1.HTTPScaledObject, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.HTTPScaledObject, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.HTTPScaledObjectList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScaledObject, err error) + HTTPScaledObjectExpansion +} + +// hTTPScaledObjects implements HTTPScaledObjectInterface +type hTTPScaledObjects struct { + client rest.Interface + ns string +} + +// newHTTPScaledObjects returns a HTTPScaledObjects +func newHTTPScaledObjects(c *HttpV1alpha1Client, namespace string) *hTTPScaledObjects { + return &hTTPScaledObjects{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the hTTPScaledObject, and returns the corresponding hTTPScaledObject object, and an error if there is any. +func (c *hTTPScaledObjects) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPScaledObject, err error) { + result = &v1alpha1.HTTPScaledObject{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httpscaledobjects"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of HTTPScaledObjects that match those selectors. +func (c *hTTPScaledObjects) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPScaledObjectList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.HTTPScaledObjectList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httpscaledobjects"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested hTTPScaledObjects. +func (c *hTTPScaledObjects) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("httpscaledobjects"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a hTTPScaledObject and creates it. Returns the server's representation of the hTTPScaledObject, and an error, if there is any. +func (c *hTTPScaledObjects) Create(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.CreateOptions) (result *v1alpha1.HTTPScaledObject, err error) { + result = &v1alpha1.HTTPScaledObject{} + err = c.client.Post(). + Namespace(c.ns). + Resource("httpscaledobjects"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScaledObject). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a hTTPScaledObject and updates it. Returns the server's representation of the hTTPScaledObject, and an error, if there is any. +func (c *hTTPScaledObjects) Update(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (result *v1alpha1.HTTPScaledObject, err error) { + result = &v1alpha1.HTTPScaledObject{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httpscaledobjects"). + Name(hTTPScaledObject.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScaledObject). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *hTTPScaledObjects) UpdateStatus(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (result *v1alpha1.HTTPScaledObject, err error) { + result = &v1alpha1.HTTPScaledObject{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httpscaledobjects"). + Name(hTTPScaledObject.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScaledObject). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the hTTPScaledObject and deletes it. Returns an error if one occurs. +func (c *hTTPScaledObjects) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("httpscaledobjects"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *hTTPScaledObjects) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("httpscaledobjects"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched hTTPScaledObject. +func (c *hTTPScaledObjects) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScaledObject, err error) { + result = &v1alpha1.HTTPScaledObject{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("httpscaledobjects"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/http/v1alpha1/httpscalingset.go b/pkg/client/clientset/versioned/typed/http/v1alpha1/httpscalingset.go new file mode 100644 index 000000000..3c594c949 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/http/v1alpha1/httpscalingset.go @@ -0,0 +1,195 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + scheme "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// HTTPScalingSetsGetter has a method to return a HTTPScalingSetInterface. +// A group's client should implement this interface. +type HTTPScalingSetsGetter interface { + HTTPScalingSets(namespace string) HTTPScalingSetInterface +} + +// HTTPScalingSetInterface has methods to work with HTTPScalingSet resources. +type HTTPScalingSetInterface interface { + Create(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.CreateOptions) (*v1alpha1.HTTPScalingSet, error) + Update(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.HTTPScalingSet, error) + UpdateStatus(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.HTTPScalingSet, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.HTTPScalingSet, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.HTTPScalingSetList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScalingSet, err error) + HTTPScalingSetExpansion +} + +// hTTPScalingSets implements HTTPScalingSetInterface +type hTTPScalingSets struct { + client rest.Interface + ns string +} + +// newHTTPScalingSets returns a HTTPScalingSets +func newHTTPScalingSets(c *HttpV1alpha1Client, namespace string) *hTTPScalingSets { + return &hTTPScalingSets{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the hTTPScalingSet, and returns the corresponding hTTPScalingSet object, and an error if there is any. +func (c *hTTPScalingSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of HTTPScalingSets that match those selectors. +func (c *hTTPScalingSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPScalingSetList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.HTTPScalingSetList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested hTTPScalingSets. +func (c *hTTPScalingSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("httpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a hTTPScalingSet and creates it. Returns the server's representation of the hTTPScalingSet, and an error, if there is any. +func (c *hTTPScalingSets) Create(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.CreateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Post(). + Namespace(c.ns). + Resource("httpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a hTTPScalingSet and updates it. Returns the server's representation of the hTTPScalingSet, and an error, if there is any. +func (c *hTTPScalingSets) Update(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(hTTPScalingSet.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *hTTPScalingSets) UpdateStatus(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(hTTPScalingSet.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the hTTPScalingSet and deletes it. Returns an error if one occurs. +func (c *hTTPScalingSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *hTTPScalingSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("httpscalingsets"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched hTTPScalingSet. +func (c *hTTPScalingSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 66f1e2369..c962779b9 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -30,6 +30,7 @@ import ( gateway "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gateway" gatewayapi "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gatewayapi" gloo "github.com/fluxcd/flagger/pkg/client/informers/externalversions/gloo" + http "github.com/fluxcd/flagger/pkg/client/informers/externalversions/http" internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" istio "github.com/fluxcd/flagger/pkg/client/informers/externalversions/istio" keda "github.com/fluxcd/flagger/pkg/client/informers/externalversions/keda" @@ -260,6 +261,7 @@ type SharedInformerFactory interface { Gateway() gateway.Interface Gatewayapi() gatewayapi.Interface Gloo() gloo.Interface + Http() http.Interface Networking() istio.Interface Keda() keda.Interface Kuma() kuma.Interface @@ -292,6 +294,10 @@ func (f *sharedInformerFactory) Gloo() gloo.Interface { return gloo.New(f, f.namespace, f.tweakListOptions) } +func (f *sharedInformerFactory) Http() http.Interface { + return http.New(f, f.namespace, f.tweakListOptions) +} + func (f *sharedInformerFactory) Networking() istio.Interface { return istio.New(f, f.namespace, f.tweakListOptions) } diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 0158715f3..9fc021209 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -29,8 +29,9 @@ import ( gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" v1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1" gloov1 "github.com/fluxcd/flagger/pkg/apis/gloo/gloo/v1" + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1" - v1alpha1 "github.com/fluxcd/flagger/pkg/apis/keda/v1alpha1" + kedav1alpha1 "github.com/fluxcd/flagger/pkg/apis/keda/v1alpha1" kumav1alpha1 "github.com/fluxcd/flagger/pkg/apis/kuma/v1alpha1" projectcontourv1 "github.com/fluxcd/flagger/pkg/apis/projectcontour/v1" smiv1alpha1 "github.com/fluxcd/flagger/pkg/apis/smi/v1alpha1" @@ -111,8 +112,16 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource case gloov1.SchemeGroupVersion.WithResource("upstreams"): return &genericInformer{resource: resource.GroupResource(), informer: f.Gloo().V1().Upstreams().Informer()}, nil + // Group=http.keda.sh, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("clusterhttpscalingsets"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Http().V1alpha1().ClusterHTTPScalingSets().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("httpscaledobjects"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Http().V1alpha1().HTTPScaledObjects().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("httpscalingsets"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Http().V1alpha1().HTTPScalingSets().Informer()}, nil + // Group=keda.sh, Version=v1alpha1 - case v1alpha1.SchemeGroupVersion.WithResource("scaledobjects"): + case kedav1alpha1.SchemeGroupVersion.WithResource("scaledobjects"): return &genericInformer{resource: resource.GroupResource(), informer: f.Keda().V1alpha1().ScaledObjects().Informer()}, nil // Group=kuma.io, Version=v1alpha1 diff --git a/pkg/client/informers/externalversions/http/interface.go b/pkg/client/informers/externalversions/http/interface.go new file mode 100644 index 000000000..ea0cf4502 --- /dev/null +++ b/pkg/client/informers/externalversions/http/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package http + +import ( + v1alpha1 "github.com/fluxcd/flagger/pkg/client/informers/externalversions/http/v1alpha1" + internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/http/v1alpha1/clusterhttpscalingset.go b/pkg/client/informers/externalversions/http/v1alpha1/clusterhttpscalingset.go new file mode 100644 index 000000000..10545cf3e --- /dev/null +++ b/pkg/client/informers/externalversions/http/v1alpha1/clusterhttpscalingset.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + httpv1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + versioned "github.com/fluxcd/flagger/pkg/client/clientset/versioned" + internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/fluxcd/flagger/pkg/client/listers/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ClusterHTTPScalingSetInformer provides access to a shared informer and lister for +// ClusterHTTPScalingSets. +type ClusterHTTPScalingSetInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ClusterHTTPScalingSetLister +} + +type clusterHTTPScalingSetInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewClusterHTTPScalingSetInformer constructs a new informer for ClusterHTTPScalingSet type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewClusterHTTPScalingSetInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterHTTPScalingSetInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredClusterHTTPScalingSetInformer constructs a new informer for ClusterHTTPScalingSet type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredClusterHTTPScalingSetInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().ClusterHTTPScalingSets(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().ClusterHTTPScalingSets(namespace).Watch(context.TODO(), options) + }, + }, + &httpv1alpha1.ClusterHTTPScalingSet{}, + resyncPeriod, + indexers, + ) +} + +func (f *clusterHTTPScalingSetInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterHTTPScalingSetInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *clusterHTTPScalingSetInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&httpv1alpha1.ClusterHTTPScalingSet{}, f.defaultInformer) +} + +func (f *clusterHTTPScalingSetInformer) Lister() v1alpha1.ClusterHTTPScalingSetLister { + return v1alpha1.NewClusterHTTPScalingSetLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/http/v1alpha1/httpscaledobject.go b/pkg/client/informers/externalversions/http/v1alpha1/httpscaledobject.go new file mode 100644 index 000000000..ecc194c9f --- /dev/null +++ b/pkg/client/informers/externalversions/http/v1alpha1/httpscaledobject.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + httpv1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + versioned "github.com/fluxcd/flagger/pkg/client/clientset/versioned" + internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/fluxcd/flagger/pkg/client/listers/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// HTTPScaledObjectInformer provides access to a shared informer and lister for +// HTTPScaledObjects. +type HTTPScaledObjectInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.HTTPScaledObjectLister +} + +type hTTPScaledObjectInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewHTTPScaledObjectInformer constructs a new informer for HTTPScaledObject type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewHTTPScaledObjectInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredHTTPScaledObjectInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredHTTPScaledObjectInformer constructs a new informer for HTTPScaledObject type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredHTTPScaledObjectInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().HTTPScaledObjects(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().HTTPScaledObjects(namespace).Watch(context.TODO(), options) + }, + }, + &httpv1alpha1.HTTPScaledObject{}, + resyncPeriod, + indexers, + ) +} + +func (f *hTTPScaledObjectInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredHTTPScaledObjectInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *hTTPScaledObjectInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&httpv1alpha1.HTTPScaledObject{}, f.defaultInformer) +} + +func (f *hTTPScaledObjectInformer) Lister() v1alpha1.HTTPScaledObjectLister { + return v1alpha1.NewHTTPScaledObjectLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/http/v1alpha1/httpscalingset.go b/pkg/client/informers/externalversions/http/v1alpha1/httpscalingset.go new file mode 100644 index 000000000..8a7f62226 --- /dev/null +++ b/pkg/client/informers/externalversions/http/v1alpha1/httpscalingset.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + httpv1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + versioned "github.com/fluxcd/flagger/pkg/client/clientset/versioned" + internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/fluxcd/flagger/pkg/client/listers/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// HTTPScalingSetInformer provides access to a shared informer and lister for +// HTTPScalingSets. +type HTTPScalingSetInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.HTTPScalingSetLister +} + +type hTTPScalingSetInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewHTTPScalingSetInformer constructs a new informer for HTTPScalingSet type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewHTTPScalingSetInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredHTTPScalingSetInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredHTTPScalingSetInformer constructs a new informer for HTTPScalingSet type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredHTTPScalingSetInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().HTTPScalingSets(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().HTTPScalingSets(namespace).Watch(context.TODO(), options) + }, + }, + &httpv1alpha1.HTTPScalingSet{}, + resyncPeriod, + indexers, + ) +} + +func (f *hTTPScalingSetInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredHTTPScalingSetInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *hTTPScalingSetInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&httpv1alpha1.HTTPScalingSet{}, f.defaultInformer) +} + +func (f *hTTPScalingSetInformer) Lister() v1alpha1.HTTPScalingSetLister { + return v1alpha1.NewHTTPScalingSetLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/http/v1alpha1/interface.go b/pkg/client/informers/externalversions/http/v1alpha1/interface.go new file mode 100644 index 000000000..8423c6fdb --- /dev/null +++ b/pkg/client/informers/externalversions/http/v1alpha1/interface.go @@ -0,0 +1,59 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // ClusterHTTPScalingSets returns a ClusterHTTPScalingSetInformer. + ClusterHTTPScalingSets() ClusterHTTPScalingSetInformer + // HTTPScaledObjects returns a HTTPScaledObjectInformer. + HTTPScaledObjects() HTTPScaledObjectInformer + // HTTPScalingSets returns a HTTPScalingSetInformer. + HTTPScalingSets() HTTPScalingSetInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// ClusterHTTPScalingSets returns a ClusterHTTPScalingSetInformer. +func (v *version) ClusterHTTPScalingSets() ClusterHTTPScalingSetInformer { + return &clusterHTTPScalingSetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// HTTPScaledObjects returns a HTTPScaledObjectInformer. +func (v *version) HTTPScaledObjects() HTTPScaledObjectInformer { + return &hTTPScaledObjectInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// HTTPScalingSets returns a HTTPScalingSetInformer. +func (v *version) HTTPScalingSets() HTTPScalingSetInformer { + return &hTTPScalingSetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/listers/http/v1alpha1/clusterhttpscalingset.go b/pkg/client/listers/http/v1alpha1/clusterhttpscalingset.go new file mode 100644 index 000000000..5eab21c98 --- /dev/null +++ b/pkg/client/listers/http/v1alpha1/clusterhttpscalingset.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ClusterHTTPScalingSetLister helps list ClusterHTTPScalingSets. +// All objects returned here must be treated as read-only. +type ClusterHTTPScalingSetLister interface { + // List lists all ClusterHTTPScalingSets in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ClusterHTTPScalingSet, err error) + // ClusterHTTPScalingSets returns an object that can list and get ClusterHTTPScalingSets. + ClusterHTTPScalingSets(namespace string) ClusterHTTPScalingSetNamespaceLister + ClusterHTTPScalingSetListerExpansion +} + +// clusterHTTPScalingSetLister implements the ClusterHTTPScalingSetLister interface. +type clusterHTTPScalingSetLister struct { + indexer cache.Indexer +} + +// NewClusterHTTPScalingSetLister returns a new ClusterHTTPScalingSetLister. +func NewClusterHTTPScalingSetLister(indexer cache.Indexer) ClusterHTTPScalingSetLister { + return &clusterHTTPScalingSetLister{indexer: indexer} +} + +// List lists all ClusterHTTPScalingSets in the indexer. +func (s *clusterHTTPScalingSetLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterHTTPScalingSet, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ClusterHTTPScalingSet)) + }) + return ret, err +} + +// ClusterHTTPScalingSets returns an object that can list and get ClusterHTTPScalingSets. +func (s *clusterHTTPScalingSetLister) ClusterHTTPScalingSets(namespace string) ClusterHTTPScalingSetNamespaceLister { + return clusterHTTPScalingSetNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ClusterHTTPScalingSetNamespaceLister helps list and get ClusterHTTPScalingSets. +// All objects returned here must be treated as read-only. +type ClusterHTTPScalingSetNamespaceLister interface { + // List lists all ClusterHTTPScalingSets in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ClusterHTTPScalingSet, err error) + // Get retrieves the ClusterHTTPScalingSet from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.ClusterHTTPScalingSet, error) + ClusterHTTPScalingSetNamespaceListerExpansion +} + +// clusterHTTPScalingSetNamespaceLister implements the ClusterHTTPScalingSetNamespaceLister +// interface. +type clusterHTTPScalingSetNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ClusterHTTPScalingSets in the indexer for a given namespace. +func (s clusterHTTPScalingSetNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterHTTPScalingSet, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ClusterHTTPScalingSet)) + }) + return ret, err +} + +// Get retrieves the ClusterHTTPScalingSet from the indexer for a given namespace and name. +func (s clusterHTTPScalingSetNamespaceLister) Get(name string) (*v1alpha1.ClusterHTTPScalingSet, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("clusterhttpscalingset"), name) + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), nil +} diff --git a/pkg/client/listers/http/v1alpha1/expansion_generated.go b/pkg/client/listers/http/v1alpha1/expansion_generated.go new file mode 100644 index 000000000..ba2ad2485 --- /dev/null +++ b/pkg/client/listers/http/v1alpha1/expansion_generated.go @@ -0,0 +1,43 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +// ClusterHTTPScalingSetListerExpansion allows custom methods to be added to +// ClusterHTTPScalingSetLister. +type ClusterHTTPScalingSetListerExpansion interface{} + +// ClusterHTTPScalingSetNamespaceListerExpansion allows custom methods to be added to +// ClusterHTTPScalingSetNamespaceLister. +type ClusterHTTPScalingSetNamespaceListerExpansion interface{} + +// HTTPScaledObjectListerExpansion allows custom methods to be added to +// HTTPScaledObjectLister. +type HTTPScaledObjectListerExpansion interface{} + +// HTTPScaledObjectNamespaceListerExpansion allows custom methods to be added to +// HTTPScaledObjectNamespaceLister. +type HTTPScaledObjectNamespaceListerExpansion interface{} + +// HTTPScalingSetListerExpansion allows custom methods to be added to +// HTTPScalingSetLister. +type HTTPScalingSetListerExpansion interface{} + +// HTTPScalingSetNamespaceListerExpansion allows custom methods to be added to +// HTTPScalingSetNamespaceLister. +type HTTPScalingSetNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/http/v1alpha1/httpscaledobject.go b/pkg/client/listers/http/v1alpha1/httpscaledobject.go new file mode 100644 index 000000000..6a9c8df65 --- /dev/null +++ b/pkg/client/listers/http/v1alpha1/httpscaledobject.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// HTTPScaledObjectLister helps list HTTPScaledObjects. +// All objects returned here must be treated as read-only. +type HTTPScaledObjectLister interface { + // List lists all HTTPScaledObjects in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.HTTPScaledObject, err error) + // HTTPScaledObjects returns an object that can list and get HTTPScaledObjects. + HTTPScaledObjects(namespace string) HTTPScaledObjectNamespaceLister + HTTPScaledObjectListerExpansion +} + +// hTTPScaledObjectLister implements the HTTPScaledObjectLister interface. +type hTTPScaledObjectLister struct { + indexer cache.Indexer +} + +// NewHTTPScaledObjectLister returns a new HTTPScaledObjectLister. +func NewHTTPScaledObjectLister(indexer cache.Indexer) HTTPScaledObjectLister { + return &hTTPScaledObjectLister{indexer: indexer} +} + +// List lists all HTTPScaledObjects in the indexer. +func (s *hTTPScaledObjectLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPScaledObject, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HTTPScaledObject)) + }) + return ret, err +} + +// HTTPScaledObjects returns an object that can list and get HTTPScaledObjects. +func (s *hTTPScaledObjectLister) HTTPScaledObjects(namespace string) HTTPScaledObjectNamespaceLister { + return hTTPScaledObjectNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// HTTPScaledObjectNamespaceLister helps list and get HTTPScaledObjects. +// All objects returned here must be treated as read-only. +type HTTPScaledObjectNamespaceLister interface { + // List lists all HTTPScaledObjects in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.HTTPScaledObject, err error) + // Get retrieves the HTTPScaledObject from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.HTTPScaledObject, error) + HTTPScaledObjectNamespaceListerExpansion +} + +// hTTPScaledObjectNamespaceLister implements the HTTPScaledObjectNamespaceLister +// interface. +type hTTPScaledObjectNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all HTTPScaledObjects in the indexer for a given namespace. +func (s hTTPScaledObjectNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPScaledObject, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HTTPScaledObject)) + }) + return ret, err +} + +// Get retrieves the HTTPScaledObject from the indexer for a given namespace and name. +func (s hTTPScaledObjectNamespaceLister) Get(name string) (*v1alpha1.HTTPScaledObject, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("httpscaledobject"), name) + } + return obj.(*v1alpha1.HTTPScaledObject), nil +} diff --git a/pkg/client/listers/http/v1alpha1/httpscalingset.go b/pkg/client/listers/http/v1alpha1/httpscalingset.go new file mode 100644 index 000000000..2fd70d4a2 --- /dev/null +++ b/pkg/client/listers/http/v1alpha1/httpscalingset.go @@ -0,0 +1,99 @@ +/* +Copyright 2020 The Flux authors + +Licensed 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/fluxcd/flagger/pkg/apis/http/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// HTTPScalingSetLister helps list HTTPScalingSets. +// All objects returned here must be treated as read-only. +type HTTPScalingSetLister interface { + // List lists all HTTPScalingSets in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.HTTPScalingSet, err error) + // HTTPScalingSets returns an object that can list and get HTTPScalingSets. + HTTPScalingSets(namespace string) HTTPScalingSetNamespaceLister + HTTPScalingSetListerExpansion +} + +// hTTPScalingSetLister implements the HTTPScalingSetLister interface. +type hTTPScalingSetLister struct { + indexer cache.Indexer +} + +// NewHTTPScalingSetLister returns a new HTTPScalingSetLister. +func NewHTTPScalingSetLister(indexer cache.Indexer) HTTPScalingSetLister { + return &hTTPScalingSetLister{indexer: indexer} +} + +// List lists all HTTPScalingSets in the indexer. +func (s *hTTPScalingSetLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPScalingSet, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HTTPScalingSet)) + }) + return ret, err +} + +// HTTPScalingSets returns an object that can list and get HTTPScalingSets. +func (s *hTTPScalingSetLister) HTTPScalingSets(namespace string) HTTPScalingSetNamespaceLister { + return hTTPScalingSetNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// HTTPScalingSetNamespaceLister helps list and get HTTPScalingSets. +// All objects returned here must be treated as read-only. +type HTTPScalingSetNamespaceLister interface { + // List lists all HTTPScalingSets in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.HTTPScalingSet, err error) + // Get retrieves the HTTPScalingSet from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.HTTPScalingSet, error) + HTTPScalingSetNamespaceListerExpansion +} + +// hTTPScalingSetNamespaceLister implements the HTTPScalingSetNamespaceLister +// interface. +type hTTPScalingSetNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all HTTPScalingSets in the indexer for a given namespace. +func (s hTTPScalingSetNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPScalingSet, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HTTPScalingSet)) + }) + return ret, err +} + +// Get retrieves the HTTPScalingSet from the indexer for a given namespace and name. +func (s hTTPScalingSetNamespaceLister) Get(name string) (*v1alpha1.HTTPScalingSet, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("httpscalingset"), name) + } + return obj.(*v1alpha1.HTTPScalingSet), nil +} diff --git a/pkg/router/factory.go b/pkg/router/factory.go index 0a62a5ef0..ab380bf86 100644 --- a/pkg/router/factory.go +++ b/pkg/router/factory.go @@ -59,18 +59,16 @@ func NewFactory(kubeConfig *restclient.Config, kubeClient kubernetes.Interface, // KubernetesRouter returns a KubernetesRouter interface implementation func (factory *Factory) KubernetesRouter(kind string, labelSelector string, labelValue string, ports map[string]int32) KubernetesRouter { - switch kind { - case "Service": + if kind == "Service" { return &KubernetesNoopRouter{} - default: // Daemonset or Deployment - return &KubernetesDefaultRouter{ - logger: factory.logger, - flaggerClient: factory.flaggerClient, - kubeClient: factory.kubeClient, - labelSelector: labelSelector, - labelValue: labelValue, - ports: ports, - } + } + return &KubernetesDefaultRouter{ + logger: factory.logger, + flaggerClient: factory.flaggerClient, + kubeClient: factory.kubeClient, + labelSelector: labelSelector, + labelValue: labelValue, + ports: ports, } } diff --git a/pkg/router/gateway_api.go b/pkg/router/gateway_api.go index dd4ecd49f..092e8f753 100644 --- a/pkg/router/gateway_api.go +++ b/pkg/router/gateway_api.go @@ -59,16 +59,64 @@ type GatewayAPIRouter struct { setOwnerRefs bool } +func (gwr *GatewayAPIRouter) getServiceRef(canary *flaggerv1.Canary) (hostNames []v1.Hostname, apexSvcName, primarySvcName, primarySvcNamespace, canarySvcName, canarySvcNamespace string, err error) { + apexSvcName, primarySvcName, canarySvcName = canary.GetServiceNames() + canarySvcNamespace = canary.Namespace + primarySvcNamespace = canary.Namespace + err = nil + + for _, host := range canary.Spec.Service.Hosts { + hostNames = append(hostNames, v1.Hostname(host)) + } + + if canary.IsHTTPScaledObject() { + canarySvcName = "keda-http-add-on-interceptor-proxy" + canarySvcNamespace = "keda" + primarySvcNamespace = "keda" + targetHTTPSo, getErr := gwr.gatewayAPIClient.HttpV1alpha1().HTTPScaledObjects(canary.Namespace).Get(context.TODO(), canary.Spec.AutoscalerRef.Name, metav1.GetOptions{}) + if getErr != nil { + err = fmt.Errorf("HTTPScaledObject %s.%s get error: %w", canary.Spec.AutoscalerRef.Name, canary.Namespace, getErr) + return + } + for _, host := range targetHTTPSo.Spec.Hosts { + hostNames = append(hostNames, v1.Hostname(host)) + } + if canary.Spec.AutoscalerRef.CanaryInterceptorProxyService != nil { + canarySvcName = canary.Spec.AutoscalerRef.CanaryInterceptorProxyService.Name + if canary.Spec.AutoscalerRef.CanaryInterceptorProxyService.Namespace != "" { + canarySvcNamespace = canary.Spec.AutoscalerRef.CanaryInterceptorProxyService.Namespace + } + } + + if canary.Spec.AutoscalerRef.PrimaryScalingSet == nil { + err = fmt.Errorf("PrimaryScalingSet must be specified when using HTTPScaledObject as a Autoscaler.") + return + } + primarySvcName = fmt.Sprintf("%s-interceptor-proxy", canary.Spec.AutoscalerRef.PrimaryScalingSet.Name) + if canary.Spec.AutoscalerRef.PrimaryScalingSet.Namespace == "" { + if canary.Spec.AutoscalerRef.CanaryInterceptorProxyService != nil { + primarySvcNamespace = canary.Spec.AutoscalerRef.CanaryInterceptorProxyService.Namespace + } + } + if canary.Spec.AutoscalerRef.PrimaryScalingSet.Namespace != "" { + primarySvcNamespace = canary.Spec.AutoscalerRef.PrimaryScalingSet.Namespace + } + } + return +} + func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error { if len(canary.Spec.Service.GatewayRefs) == 0 { return fmt.Errorf("GatewayRefs must be specified when using Gateway API as a provider.") } - apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames() + hostNames, apexSvcName, primarySvcName, primarySvcNamespace, canarySvcName, canarySvcNamespace, err := gwr.getServiceRef(canary) + if err != nil { + return fmt.Errorf("getServiceRef error: %w", err) + } hrNamespace := canary.Namespace - var hostNames []v1.Hostname for _, host := range canary.Spec.Service.Hosts { hostNames = append(hostNames, v1.Hostname(host)) } @@ -96,10 +144,10 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error { Filters: gwr.makeFilters(canary), BackendRefs: []v1.HTTPBackendRef{ { - BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port), + BackendRef: gwr.makeBackendRef(primarySvcName, primarySvcNamespace, initialPrimaryWeight, canary.Spec.Service.Port), }, { - BackendRef: gwr.makeBackendRef(canarySvcName, initialCanaryWeight, canary.Spec.Service.Port), + BackendRef: gwr.makeBackendRef(canarySvcName, canarySvcNamespace, initialCanaryWeight, canary.Spec.Service.Port), }, }, }, @@ -122,7 +170,7 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error { Filters: gwr.makeFilters(canary), BackendRefs: []v1.HTTPBackendRef{ { - BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port), + BackendRef: gwr.makeBackendRef(primarySvcName, primarySvcNamespace, initialPrimaryWeight, canary.Spec.Service.Port), }, }, }) @@ -246,7 +294,10 @@ func (gwr *GatewayAPIRouter) GetRoutes(canary *flaggerv1.Canary) ( mirrored bool, err error, ) { - apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames() + _, apexSvcName, primarySvcName, primarySvcNamespace, canarySvcName, canarySvcNamespace, err := gwr.getServiceRef(canary) + if err != nil { + return 0, 0, false, err + } hrNamespace := canary.Namespace httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{}) if err != nil { @@ -269,10 +320,14 @@ func (gwr *GatewayAPIRouter) GetRoutes(canary *flaggerv1.Canary) ( // A/B testing: Avoid reading the rule with only for backendRef. if len(rule.BackendRefs) == 2 { for _, backendRef := range rule.BackendRefs { - if backendRef.Name == v1.ObjectName(primarySvcName) { + ns := httpRoute.Namespace + if backendRef.Namespace != nil { + ns = string(*backendRef.Namespace) + } + if backendRef.Name == v1.ObjectName(primarySvcName) && ns == primarySvcNamespace { primaryWeight = int(*backendRef.Weight) } - if backendRef.Name == v1.ObjectName(canarySvcName) { + if backendRef.Name == v1.ObjectName(canarySvcName) && ns == canarySvcNamespace { canaryWeight = int(*backendRef.Weight) } } @@ -304,19 +359,16 @@ func (gwr *GatewayAPIRouter) SetRoutes( canaryWeight int, mirrored bool, ) error { + hostNames, apexSvcName, primarySvcName, primarySvcNamespace, canarySvcName, canarySvcNamespace, err := gwr.getServiceRef(canary) + pWeight := int32(primaryWeight) cWeight := int32(canaryWeight) - apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames() hrNamespace := canary.Namespace httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{}) if err != nil { return fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err) } hrClone := httpRoute.DeepCopy() - hostNames := []v1.Hostname{} - for _, host := range canary.Spec.Service.Hosts { - hostNames = append(hostNames, v1.Hostname(host)) - } matches, err := gwr.mapRouteMatches(canary.Spec.Service.Match) if err != nil { return fmt.Errorf("Invalid request matching selectors: %w", err) @@ -339,10 +391,10 @@ func (gwr *GatewayAPIRouter) SetRoutes( Filters: gwr.makeFilters(canary), BackendRefs: []v1.HTTPBackendRef{ { - BackendRef: gwr.makeBackendRef(primarySvcName, pWeight, canary.Spec.Service.Port), + BackendRef: gwr.makeBackendRef(primarySvcName, primarySvcNamespace, pWeight, canary.Spec.Service.Port), }, { - BackendRef: gwr.makeBackendRef(canarySvcName, cWeight, canary.Spec.Service.Port), + BackendRef: gwr.makeBackendRef(canarySvcName, canarySvcNamespace, cWeight, canary.Spec.Service.Port), }, }, } @@ -398,7 +450,7 @@ func (gwr *GatewayAPIRouter) SetRoutes( Filters: gwr.makeFilters(canary), BackendRefs: []v1.HTTPBackendRef{ { - BackendRef: gwr.makeBackendRef(primarySvcName, initialPrimaryWeight, canary.Spec.Service.Port), + BackendRef: gwr.makeBackendRef(primarySvcName, primarySvcNamespace, pWeight, canary.Spec.Service.Port), }, }, Timeouts: &v1.HTTPRouteTimeouts{ @@ -430,7 +482,10 @@ func (gwr *GatewayAPIRouter) Finalize(_ *flaggerv1.Canary) error { // session affinity based Canary releases. func (gwr *GatewayAPIRouter) getSessionAffinityRouteRules(canary *flaggerv1.Canary, canaryWeight int, weightedRouteRule *v1.HTTPRouteRule) ([]v1.HTTPRouteRule, error) { - _, primarySvcName, canarySvcName := canary.GetServiceNames() + _, _, primarySvcName, primarySvcNamespace, canarySvcName, canarySvcNamespace, err := gwr.getServiceRef(canary) + if err != nil { + return nil, err + } stickyRouteRule := *weightedRouteRule // If a canary run is active, we want all responses corresponding to requests hitting the canary deployment @@ -483,10 +538,10 @@ func (gwr *GatewayAPIRouter) getSessionAffinityRouteRules(canary *flaggerv1.Cana stickyRouteRule.Matches = mergedMatches stickyRouteRule.BackendRefs = []v1.HTTPBackendRef{ { - BackendRef: gwr.makeBackendRef(primarySvcName, 0, canary.Spec.Service.Port), + BackendRef: gwr.makeBackendRef(primarySvcName, primarySvcNamespace, 0, canary.Spec.Service.Port), }, { - BackendRef: gwr.makeBackendRef(canarySvcName, 100, canary.Spec.Service.Port), + BackendRef: gwr.makeBackendRef(canarySvcName, canarySvcNamespace, 100, canary.Spec.Service.Port), }, } } else { @@ -610,13 +665,14 @@ func (gwr *GatewayAPIRouter) mapRouteMatches(requestMatches []istiov1beta1.HTTPM return matches, nil } -func (gwr *GatewayAPIRouter) makeBackendRef(svcName string, weight, port int32) v1.BackendRef { +func (gwr *GatewayAPIRouter) makeBackendRef(svcName, namespace string, weight, port int32) v1.BackendRef { return v1.BackendRef{ BackendObjectReference: v1.BackendObjectReference{ - Group: (*v1.Group)(&backendRefGroup), - Kind: (*v1.Kind)(&backendRefKind), - Name: v1.ObjectName(svcName), - Port: (*v1.PortNumber)(&port), + Group: (*v1.Group)(&backendRefGroup), + Kind: (*v1.Kind)(&backendRefKind), + Name: v1.ObjectName(svcName), + Namespace: (*v1.Namespace)(&namespace), + Port: (*v1.PortNumber)(&port), }, Weight: &weight, } diff --git a/pkg/router/kubernetes_default.go b/pkg/router/kubernetes_default.go index 840005672..c2ee2718d 100644 --- a/pkg/router/kubernetes_default.go +++ b/pkg/router/kubernetes_default.go @@ -49,14 +49,18 @@ type KubernetesDefaultRouter struct { func (c *KubernetesDefaultRouter) Initialize(canary *flaggerv1.Canary) error { _, primaryName, canaryName := canary.GetServiceNames() - // canary svc - err := c.reconcileService(canary, canaryName, c.labelValue, canary.Spec.Service.Canary) - if err != nil { - return fmt.Errorf("reconcileService failed: %w", err) + isHTTPScaledObject := canary.Spec.AutoscalerRef != nil && canary.Spec.AutoscalerRef.Kind == "HTTPScaledObject" + // For HTTPScaledObject, we do not create a Service for canary as it is not used. + if !isHTTPScaledObject { + // canary svc + err := c.reconcileService(canary, canaryName, c.labelValue, canary.Spec.Service.Canary) + if err != nil { + return fmt.Errorf("reconcileService failed: %w", err) + } } // primary svc - err = c.reconcileService(canary, primaryName, fmt.Sprintf("%s-primary", c.labelValue), canary.Spec.Service.Primary) + err := c.reconcileService(canary, primaryName, fmt.Sprintf("%s-primary", c.labelValue), canary.Spec.Service.Primary) if err != nil { return fmt.Errorf("reconcileService failed: %w", err) } @@ -68,6 +72,10 @@ func (c *KubernetesDefaultRouter) Initialize(canary *flaggerv1.Canary) error { func (c *KubernetesDefaultRouter) Reconcile(canary *flaggerv1.Canary) error { apexName, _, _ := canary.GetServiceNames() + if canary.Spec.AutoscalerRef != nil && canary.Spec.AutoscalerRef.Kind == "HTTPScaledObject" { + return nil + } + // main svc err := c.reconcileService(canary, apexName, fmt.Sprintf("%s-primary", c.labelValue), canary.Spec.Service.Apex) if err != nil {