From fc4bb12992c0b5161f8216c95cf8bac8ba68782d Mon Sep 17 00:00:00 2001 From: Gallardot Date: Tue, 13 Dec 2022 23:08:50 +0800 Subject: [PATCH] feat: Apache APISIX support. Fixes #2395 (#2437) * feat: Apache APISIX support Signed-off-by: Gallardot * feat: Apache APISIX support Signed-off-by: Gallardot * feat: Improve the apisix route Signed-off-by: Gallardot * chore: add apisix doc and examples Signed-off-by: Gallardot * chore: add apisix doc and examples Signed-off-by: Gallardot * chore: add UT Signed-off-by: Gallardot * doc: update README Signed-off-by: Gallardot Signed-off-by: Gallardot --- .gitignore | 2 + README.md | 33 +- docs/features/traffic-management/apisix.md | 186 +++++ docs/features/traffic-management/index.md | 1 + examples/apisix/rollout.yaml | 48 ++ examples/apisix/route.yaml | 23 + examples/apisix/services.yaml | 29 + manifests/crds/rollout-crd.yaml | 14 + manifests/install.yaml | 22 + manifests/namespace-install.yaml | 22 + manifests/role/argo-rollouts-clusterrole.yaml | 8 + pkg/apiclient/rollout/rollout.swagger.json | 31 + pkg/apis/api-rules/violation_exceptions.list | 1 + pkg/apis/rollouts/v1alpha1/generated.pb.go | 638 +++++++++++++++--- pkg/apis/rollouts/v1alpha1/generated.proto | 18 + .../rollouts/v1alpha1/openapi_generated.go | 68 +- pkg/apis/rollouts/v1alpha1/types.go | 16 + .../v1alpha1/zz_generated.deepcopy.go | 47 ++ rollout/trafficrouting.go | 11 + rollout/trafficrouting/apisix/apisix.go | 198 ++++++ rollout/trafficrouting/apisix/apisix_test.go | 427 ++++++++++++ rollout/trafficrouting/apisix/mocks/apisix.go | 101 +++ rollout/trafficrouting_test.go | 28 + test/e2e/apisix/rollout-apisix-canary.yaml | 99 +++ test/e2e/apisix_test.go | 108 +++ test/e2e/crds/apisix.yaml | 615 +++++++++++++++++ test/fixtures/common.go | 12 + test/fixtures/e2e_suite.go | 7 + ui/src/models/rollout/generated/api.ts | 38 ++ utils/apisix/apisix.go | 43 ++ utils/apisix/apisix_test.go | 33 + utils/defaults/defaults.go | 2 + 32 files changed, 2812 insertions(+), 117 deletions(-) create mode 100644 docs/features/traffic-management/apisix.md create mode 100644 examples/apisix/rollout.yaml create mode 100644 examples/apisix/route.yaml create mode 100644 examples/apisix/services.yaml create mode 100644 rollout/trafficrouting/apisix/apisix.go create mode 100644 rollout/trafficrouting/apisix/apisix_test.go create mode 100644 rollout/trafficrouting/apisix/mocks/apisix.go create mode 100644 test/e2e/apisix/rollout-apisix-canary.yaml create mode 100644 test/e2e/apisix_test.go create mode 100644 test/e2e/crds/apisix.yaml create mode 100644 utils/apisix/apisix.go create mode 100644 utils/apisix/apisix_test.go diff --git a/.gitignore b/.gitignore index 8ed86c45e7..5f6219dc82 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ cmd/**/debug debug.test coverage.out coverage.html +junit.xml +rerunreport.txt site/ vendor/ # generated diff --git a/README.md b/README.md index 35f72dea28..7e8470dd9d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # Argo Rollouts - Progressive Delivery for Kubernetes + [![codecov](https://codecov.io/gh/argoproj/argo-rollouts/branch/master/graph/badge.svg)](https://codecov.io/gh/argoproj/argo-rollouts) [![slack](https://img.shields.io/badge/slack-argoproj-brightgreen.svg?logo=slack)](https://argoproj.github.io/community/join-slack) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3834/badge)](https://bestpractices.coreinfrastructure.org/projects/3834) [![Artifact HUB](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/argo-rollouts)](https://artifacthub.io/packages/helm/argo/argo-rollouts) ## What is Argo Rollouts? -Argo Rollouts is a Kubernetes controller and set of CRDs which provide advanced deployment capabilities such as blue-green, canary, canary analysis, experimentation, and progressive delivery features to Kubernetes. + +Argo Rollouts is a Kubernetes controller and set of CRDs which provide advanced deployment capabilities such as blue-green, canary, canary analysis, experimentation, and progressive delivery features to Kubernetes. Argo Rollouts (optionally) integrates with ingress controllers and service meshes, leveraging their traffic shaping abilities to gradually shift traffic to the new version during an update. Additionally, Rollouts can query and interpret metrics from various providers to verify key KPIs and drive automated promotion or rollback during an update. @@ -14,15 +16,17 @@ Argo Rollouts (optionally) integrates with ingress controllers and service meshe ## Quick Start -``` +```bash kubectl create namespace argo-rollouts kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml ``` -Follow the full [getting started guide](docs/getting-started.md) to walk through creating and then updating a rollout object. +Follow the full [getting started guide](docs/getting-started.md) to walk through creating and then updating a rollout object. ## Why Argo Rollouts? + Kubernetes Deployments provides the `RollingUpdate` strategy which provide a basic set of safety guarantees (readiness probes) during an update. However the rolling update strategy faces many limitations: + * Few controls over the speed of the rollout * Inability to control traffic flow to the new version * Readiness probes are unsuitable for deeper, stress, or one-time checks @@ -32,6 +36,7 @@ Kubernetes Deployments provides the `RollingUpdate` strategy which provide a bas For these reasons, in large scale high-volume production environments, a rolling update is often considered too risky of an update procedure since it provides no control over the blast radius, may rollout too aggressively, and provides no automated rollback upon failures. ## Features + * Blue-Green update strategy * Canary update strategy * Fine-grained, weighted traffic shifting @@ -43,23 +48,25 @@ For these reasons, in large scale high-volume production environments, a rolling * Metric provider integration: Prometheus, Wavefront, Kayenta, Web, Kubernetes Jobs, Datadog, New Relic, InfluxDB ## Supported Traffic Shaping Integrations -| Traffic Shaping Integration | SetWeight | SetWeightExperiments | SetMirror | SetHeader | -|------------------------------|------------------------------|-----------------------------|----------------------------|----------------------------| -| ALB Ingress Controller | :white_check_mark: (stable) | :white_check_mark: (stable) | :x: | :white_check_mark: (alpha) | -| Ambassador | :white_check_mark: (stable) | :x: | :x: | :x: | -| Istio | :white_check_mark: (stable) | :white_check_mark: (stable) | :white_check_mark: (alpha) | :white_check_mark: (alpha) | -| Nginx Ingress Controller | :white_check_mark: (stable) | :x: | :x: | :x: | -| SMI | :white_check_mark: (stable) | :white_check_mark: (stable) | :x: | :x: | -| Traefik | :white_check_mark: (beta) | :x: | :x: | :x: | +| Traffic Shaping Integration | SetWeight | SetWeightExperiments | SetMirror | SetHeader | +|-----------------------------------|------------------------------|-----------------------------|----------------------------|----------------------------| +| ALB Ingress Controller | :white_check_mark: (stable) | :white_check_mark: (stable) | :x: | :white_check_mark: (alpha) | +| Ambassador | :white_check_mark: (stable) | :x: | :x: | :x: | +| Apache APISIX Ingress Controller | :white_check_mark: (alpha) | :x: | :x: | :x: | +| Istio | :white_check_mark: (stable) | :white_check_mark: (stable) | :white_check_mark: (alpha) | :white_check_mark: (alpha) | +| Nginx Ingress Controller | :white_check_mark: (stable) | :x: | :x: | :x: | +| SMI | :white_check_mark: (stable) | :white_check_mark: (stable) | :x: | :x: | +| Traefik | :white_check_mark: (beta) | :x: | :x: | :x: | :white_check_mark: = Supported :x: = Not Supported ## Documentation + To learn more about Argo Rollouts go to the [complete documentation](https://argoproj.github.io/argo-rollouts/). -## Community +## Community You can reach the Argo Rollouts community and developers via the following channels: @@ -89,5 +96,3 @@ You can reach the Argo Rollouts community and developers via the following chann * [How Scalable is Argo-Rollouts: A Cloud Operator’s Perspective](https://www.youtube.com/watch?v=rCEhxJ2NSTI) * [Minimize Impact in Kubernetes Using Argo Rollouts](https://medium.com/@arielsimhon/minimize-impact-in-kubernetes-using-argo-rollouts-992fb9519969) * [Progressive Application Delivery with GitOps on Red Hat OpenShift](https://www.youtube.com/watch?v=DfeL7cdTx4c) - - diff --git a/docs/features/traffic-management/apisix.md b/docs/features/traffic-management/apisix.md new file mode 100644 index 0000000000..62c836b6e1 --- /dev/null +++ b/docs/features/traffic-management/apisix.md @@ -0,0 +1,186 @@ +# Apache APISIX + +You can use the [Apache APISIX](https://apisix.apache.org/) and [Apache APISIX Ingress Controller](https://apisix.apache.org/docs/ingress-controller/getting-started/) for traffic management with Argo Rollouts. + +The [ApisixRoute](https://apisix.apache.org/docs/ingress-controller/concepts/apisix_route/) is the object that supports the ability for [weighted round robin load balancing](https://apisix.apache.org/docs/ingress-controller/concepts/apisix_route/#weight-based-traffic-split) when using Apache APISIX Ingress Controller as ingress. + +This guide shows you how to integrate ApisixRoute with Argo Rollouts using it as weighted round robin load balancer + +## Prerequisites + +Argo Rollouts requires Apache APISIX v2.15 or newer and Apache APISIX Ingress Controller v1.5.0 or newer. + +Install Apache APISIX and Apache APISIX Ingress Controller with Helm v3: + +```bash +helm repo add apisix https://charts.apiseven.com +kubectl create ns apisix + +helm upgrade -i apisix apisix/apisix --version=0.11.3 \ +--namespace apisix \ +--set ingress-controller.enabled=true \ +--set ingress-controller.config.apisix.serviceNamespace=apisix +``` + +## Bootstrap + +First, we need to create the ApisixRoute object using its ability for weighted round robin load balancing. + +```yaml +apiVersion: apisix.apache.org/v2 +kind: ApisixRoute +metadata: + name: rollouts-apisix-route +spec: + http: + - name: rollouts-apisix + match: + paths: + - /* + hosts: + - rollouts-demo.apisix.local + backends: + - serviceName: rollout-apisix-canary-stable + servicePort: 80 + - serviceName: rollout-apisix-canary-canary + servicePort: 80 +``` + +```bash +kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/examples/apisix/route.yaml +``` + +Notice, we don't specify the `weight` field. It is necessary to be synced with ArgoCD. If we specify this field and Argo Rollouts controller changes it, then the ArgoCD controller will notice it and will show that this resource is out of sync (if you are using Argo CD to manage your Rollout). + +Secondly, we need to create the Argo Rollouts object. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-apisix-canary +spec: + replicas: 5 + strategy: + canary: + canaryService: rollout-apisix-canary-canary + stableService: rollout-apisix-canary-stable + trafficRouting: + apisix: + route: + name: rollouts-apisix-route + rules: + - rollouts-apisix + steps: + - setWeight: 20 + - pause: {} + - setWeight: 40 + - pause: + duration: 15 + - setWeight: 60 + - pause: + duration: 15 + - setWeight: 80 + - pause: + duration: 15 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: rollout-apisix-canary + template: + metadata: + labels: + app: rollout-apisix-canary + spec: + containers: + - name: rollout-apisix-canary + image: argoproj/rollouts-demo:blue + ports: + - name: http + containerPort: 8080 + protocol: TCP + resources: + requests: + memory: 32Mi + cpu: 5m +``` + +```bash +kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/examples/apisix/rollout.yaml +``` + +Finally, we need to create the services for the Argo Rollouts object. + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: rollout-apisix-canary-canary +spec: + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: rollout-apisix-canary + # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: + # rollouts-pod-template-hash: 7bf84f9696 +--- +apiVersion: v1 +kind: Service +metadata: + name: rollout-apisix-canary-stable +spec: + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: rollout-apisix-canary + # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: + # rollouts-pod-template-hash: 789746c88d +``` + +```bash +kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/examples/apisix/services.yaml +``` + +Initial creations of any Rollout will immediately scale up the replicas to 100% (skipping any canary upgrade steps, analysis, etc...) since there was no upgrade that occurred. + +The Argo Rollouts kubectl plugin allows you to visualize the Rollout, its related resources (ReplicaSets, Pods, AnalysisRuns), and presents live state changes as they occur. To watch the rollout as it deploys, run the get rollout --watch command from plugin: + +```bash +kubectl argo rollouts get rollout rollout-apisix-canary --watch +``` + +## Updating a Rollout + +Next it is time to perform an update. Just as with Deployments, any change to the Pod template field (`spec.template`) results in a new version (i.e. ReplicaSet) to be deployed. Updating a Rollout involves modifying the rollout spec, typically changing the container image field with a new version, and then running `kubectl apply` against the new manifest. As a convenience, the rollouts plugin provides a `set image` command, which performs these steps against the live rollout object in-place. Run the following command to update the `rollout-apisix-canary` Rollout with the "yellow" version of the container: + +```shell +kubectl argo rollouts set image rollout-apisix-canary \ + rollout-apisix-canary=argoproj/rollouts-demo:yellow +``` + +During a rollout update, the controller will progress through the steps defined in the Rollout's update strategy. The example rollout sets a 20% traffic weight to the canary, and pauses the rollout indefinitely until user action is taken to unpause/promote the rollout. + +You can check ApisixRoute's backend weights by the following command +```bash +kubectl describe apisixroute rollouts-apisix-route + +...... +Spec: + Http: + Backends: + Service Name: rollout-apisix-canary-stable + Service Port: 80 + Weight: 80 + Service Name: rollout-apisix-canary-canary + Service Port: 80 + Weight: 20 +...... +``` + +The `rollout-apisix-canary-canary` service gets 20% traffic through the Apache APISIX. \ No newline at end of file diff --git a/docs/features/traffic-management/index.md b/docs/features/traffic-management/index.md index 1578cab580..5048965414 100644 --- a/docs/features/traffic-management/index.md +++ b/docs/features/traffic-management/index.md @@ -20,6 +20,7 @@ Argo Rollouts enables traffic management by manipulating the Service Mesh resour - [AWS ALB Ingress Controller](alb.md) - [Ambassador Edge Stack](ambassador.md) +- [Apache APISIX](apisix.md) - [Istio](istio.md) - [Nginx Ingress Controller](nginx.md) - [Service Mesh Interface (SMI)](smi.md) diff --git a/examples/apisix/rollout.yaml b/examples/apisix/rollout.yaml new file mode 100644 index 0000000000..9a8e923078 --- /dev/null +++ b/examples/apisix/rollout.yaml @@ -0,0 +1,48 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-apisix-canary +spec: + replicas: 5 + strategy: + canary: + canaryService: rollout-apisix-canary-canary + stableService: rollout-apisix-canary-stable + trafficRouting: + apisix: + route: + name: rollouts-apisix-route + rules: + - rollouts-apisix + steps: + - setWeight: 20 + - pause: { } + - setWeight: 40 + - pause: + duration: 15 + - setWeight: 60 + - pause: + duration: 15 + - setWeight: 80 + - pause: + duration: 15 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: rollout-apisix-canary + template: + metadata: + labels: + app: rollout-apisix-canary + spec: + containers: + - name: rollouts-demo + image: argoproj/rollouts-demo:blue + ports: + - name: http + containerPort: 8080 + protocol: TCP + resources: + requests: + memory: 32Mi + cpu: 5m diff --git a/examples/apisix/route.yaml b/examples/apisix/route.yaml new file mode 100644 index 0000000000..b6c571ee87 --- /dev/null +++ b/examples/apisix/route.yaml @@ -0,0 +1,23 @@ +apiVersion: apisix.apache.org/v2 +kind: ApisixRoute +metadata: + name: rollouts-apisix-route +spec: + http: + - name: rollouts-apisix + match: + paths: + - /* + methods: + - GET + - POST + - PUT + - DELETE + - PATCH + hosts: + - rollouts-demo.apisix.local + backends: + - serviceName: rollout-apisix-canary-stable + servicePort: 80 + - serviceName: rollout-apisix-canary-canary + servicePort: 80 diff --git a/examples/apisix/services.yaml b/examples/apisix/services.yaml new file mode 100644 index 0000000000..31a0a6b563 --- /dev/null +++ b/examples/apisix/services.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: Service +metadata: + name: rollout-apisix-canary-canary +spec: + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: rollout-apisix-canary + # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: + # rollouts-pod-template-hash: 7bf84f9696 +--- +apiVersion: v1 +kind: Service +metadata: + name: rollout-apisix-canary-stable +spec: + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: rollout-apisix-canary + # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: + # rollouts-pod-template-hash: 789746c88d diff --git a/manifests/crds/rollout-crd.yaml b/manifests/crds/rollout-crd.yaml index ee012c3c8b..6218833f00 100644 --- a/manifests/crds/rollout-crd.yaml +++ b/manifests/crds/rollout-crd.yaml @@ -709,6 +709,20 @@ spec: required: - mappings type: object + apisix: + properties: + route: + properties: + name: + type: string + rules: + items: + type: string + type: array + required: + - name + type: object + type: object appMesh: properties: virtualNodeGroup: diff --git a/manifests/install.yaml b/manifests/install.yaml index 5731483da8..a842ebd65e 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -11719,6 +11719,20 @@ spec: required: - mappings type: object + apisix: + properties: + route: + properties: + name: + type: string + rules: + items: + type: string + type: array + required: + - name + type: object + type: object appMesh: properties: virtualNodeGroup: @@ -14697,6 +14711,14 @@ rules: - watch - get - update +- apiGroups: + - apisix.apache.org + resources: + - apisixroutes + verbs: + - watch + - get + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index b831b286fe..982155343c 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -11719,6 +11719,20 @@ spec: required: - mappings type: object + apisix: + properties: + route: + properties: + name: + type: string + rules: + items: + type: string + type: array + required: + - name + type: object + type: object appMesh: properties: virtualNodeGroup: @@ -14697,6 +14711,14 @@ rules: - watch - get - update +- apiGroups: + - apisix.apache.org + resources: + - apisixroutes + verbs: + - watch + - get + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/manifests/role/argo-rollouts-clusterrole.yaml b/manifests/role/argo-rollouts-clusterrole.yaml index db1a97f875..d58c5bda56 100644 --- a/manifests/role/argo-rollouts-clusterrole.yaml +++ b/manifests/role/argo-rollouts-clusterrole.yaml @@ -229,3 +229,11 @@ rules: - watch - get - update +- apiGroups: + - apisix.apache.org + resources: + - apisixroutes + verbs: + - watch + - get + - update diff --git a/pkg/apiclient/rollout/rollout.swagger.json b/pkg/apiclient/rollout/rollout.swagger.json index 6d5177d93e..89c27e37b6 100644 --- a/pkg/apiclient/rollout/rollout.swagger.json +++ b/pkg/apiclient/rollout/rollout.swagger.json @@ -598,6 +598,33 @@ }, "title": "AntiAffinity defines which inter-pod scheduling rule to use for anti-affinity injection" }, + "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ApisixRoute": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name refer to the name of the APISIX Route used to route traffic to the service" + }, + "rules": { + "type": "array", + "items": { + "type": "string" + }, + "title": "RuleRef a list of the APISIX Route HTTP Rules used to route traffic to the service" + } + }, + "title": "ApisixRoute holds information on the APISIX Route the rollout needs to modify" + }, + "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ApisixTrafficRouting": { + "type": "object", + "properties": { + "route": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ApisixRoute", + "title": "Route references an Apisix Route to modify to shape traffic" + } + }, + "title": "ApisixTrafficRouting defines the configuration required to use APISIX as traffic router" + }, "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AppMeshTrafficRouting": { "type": "object", "properties": { @@ -1591,6 +1618,10 @@ "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MangedRoutes" }, "description": "A list of HTTP routes that Argo Rollouts manages, the order of this array also becomes the precedence in the upstream\ntraffic router." + }, + "apisix": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ApisixTrafficRouting", + "title": "Apisix holds specific configuration to use Apisix to route traffic" } }, "title": "RolloutTrafficRouting hosts all the different configuration for supported service meshes to enable more fine-grained traffic routing" diff --git a/pkg/apis/api-rules/violation_exceptions.list b/pkg/apis/api-rules/violation_exceptions.list index f43d8ecd14..75cfabdb7c 100644 --- a/pkg/apis/api-rules/violation_exceptions.list +++ b/pkg/apis/api-rules/violation_exceptions.list @@ -8,6 +8,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,AnalysisTemplateSpec,DryRun API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,AnalysisTemplateSpec,MeasurementRetention API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,AnalysisTemplateSpec,Metrics +API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,ApisixRoute,Rules API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,AppMeshVirtualService,Routes API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,CanaryStrategy,Steps API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,CloudWatchMetric,MetricDataQueries diff --git a/pkg/apis/rollouts/v1alpha1/generated.pb.go b/pkg/apis/rollouts/v1alpha1/generated.pb.go index 87a9546e78..746b006ba2 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.pb.go +++ b/pkg/apis/rollouts/v1alpha1/generated.pb.go @@ -412,10 +412,66 @@ func (m *AntiAffinity) XXX_DiscardUnknown() { var xxx_messageInfo_AntiAffinity proto.InternalMessageInfo +func (m *ApisixRoute) Reset() { *m = ApisixRoute{} } +func (*ApisixRoute) ProtoMessage() {} +func (*ApisixRoute) Descriptor() ([]byte, []int) { + return fileDescriptor_e0e705f843545fab, []int{13} +} +func (m *ApisixRoute) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ApisixRoute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ApisixRoute) XXX_Merge(src proto.Message) { + xxx_messageInfo_ApisixRoute.Merge(m, src) +} +func (m *ApisixRoute) XXX_Size() int { + return m.Size() +} +func (m *ApisixRoute) XXX_DiscardUnknown() { + xxx_messageInfo_ApisixRoute.DiscardUnknown(m) +} + +var xxx_messageInfo_ApisixRoute proto.InternalMessageInfo + +func (m *ApisixTrafficRouting) Reset() { *m = ApisixTrafficRouting{} } +func (*ApisixTrafficRouting) ProtoMessage() {} +func (*ApisixTrafficRouting) Descriptor() ([]byte, []int) { + return fileDescriptor_e0e705f843545fab, []int{14} +} +func (m *ApisixTrafficRouting) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ApisixTrafficRouting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ApisixTrafficRouting) XXX_Merge(src proto.Message) { + xxx_messageInfo_ApisixTrafficRouting.Merge(m, src) +} +func (m *ApisixTrafficRouting) XXX_Size() int { + return m.Size() +} +func (m *ApisixTrafficRouting) XXX_DiscardUnknown() { + xxx_messageInfo_ApisixTrafficRouting.DiscardUnknown(m) +} + +var xxx_messageInfo_ApisixTrafficRouting proto.InternalMessageInfo + func (m *AppMeshTrafficRouting) Reset() { *m = AppMeshTrafficRouting{} } func (*AppMeshTrafficRouting) ProtoMessage() {} func (*AppMeshTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{13} + return fileDescriptor_e0e705f843545fab, []int{15} } func (m *AppMeshTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -443,7 +499,7 @@ var xxx_messageInfo_AppMeshTrafficRouting proto.InternalMessageInfo func (m *AppMeshVirtualNodeGroup) Reset() { *m = AppMeshVirtualNodeGroup{} } func (*AppMeshVirtualNodeGroup) ProtoMessage() {} func (*AppMeshVirtualNodeGroup) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{14} + return fileDescriptor_e0e705f843545fab, []int{16} } func (m *AppMeshVirtualNodeGroup) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -471,7 +527,7 @@ var xxx_messageInfo_AppMeshVirtualNodeGroup proto.InternalMessageInfo func (m *AppMeshVirtualNodeReference) Reset() { *m = AppMeshVirtualNodeReference{} } func (*AppMeshVirtualNodeReference) ProtoMessage() {} func (*AppMeshVirtualNodeReference) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{15} + return fileDescriptor_e0e705f843545fab, []int{17} } func (m *AppMeshVirtualNodeReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -499,7 +555,7 @@ var xxx_messageInfo_AppMeshVirtualNodeReference proto.InternalMessageInfo func (m *AppMeshVirtualService) Reset() { *m = AppMeshVirtualService{} } func (*AppMeshVirtualService) ProtoMessage() {} func (*AppMeshVirtualService) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{16} + return fileDescriptor_e0e705f843545fab, []int{18} } func (m *AppMeshVirtualService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -527,7 +583,7 @@ var xxx_messageInfo_AppMeshVirtualService proto.InternalMessageInfo func (m *Argument) Reset() { *m = Argument{} } func (*Argument) ProtoMessage() {} func (*Argument) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{17} + return fileDescriptor_e0e705f843545fab, []int{19} } func (m *Argument) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -555,7 +611,7 @@ var xxx_messageInfo_Argument proto.InternalMessageInfo func (m *ArgumentValueFrom) Reset() { *m = ArgumentValueFrom{} } func (*ArgumentValueFrom) ProtoMessage() {} func (*ArgumentValueFrom) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{18} + return fileDescriptor_e0e705f843545fab, []int{20} } func (m *ArgumentValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -583,7 +639,7 @@ var xxx_messageInfo_ArgumentValueFrom proto.InternalMessageInfo func (m *AwsResourceRef) Reset() { *m = AwsResourceRef{} } func (*AwsResourceRef) ProtoMessage() {} func (*AwsResourceRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{19} + return fileDescriptor_e0e705f843545fab, []int{21} } func (m *AwsResourceRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -611,7 +667,7 @@ var xxx_messageInfo_AwsResourceRef proto.InternalMessageInfo func (m *BlueGreenStatus) Reset() { *m = BlueGreenStatus{} } func (*BlueGreenStatus) ProtoMessage() {} func (*BlueGreenStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{20} + return fileDescriptor_e0e705f843545fab, []int{22} } func (m *BlueGreenStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -639,7 +695,7 @@ var xxx_messageInfo_BlueGreenStatus proto.InternalMessageInfo func (m *BlueGreenStrategy) Reset() { *m = BlueGreenStrategy{} } func (*BlueGreenStrategy) ProtoMessage() {} func (*BlueGreenStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{21} + return fileDescriptor_e0e705f843545fab, []int{23} } func (m *BlueGreenStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -667,7 +723,7 @@ var xxx_messageInfo_BlueGreenStrategy proto.InternalMessageInfo func (m *CanaryStatus) Reset() { *m = CanaryStatus{} } func (*CanaryStatus) ProtoMessage() {} func (*CanaryStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{22} + return fileDescriptor_e0e705f843545fab, []int{24} } func (m *CanaryStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -695,7 +751,7 @@ var xxx_messageInfo_CanaryStatus proto.InternalMessageInfo func (m *CanaryStep) Reset() { *m = CanaryStep{} } func (*CanaryStep) ProtoMessage() {} func (*CanaryStep) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{23} + return fileDescriptor_e0e705f843545fab, []int{25} } func (m *CanaryStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -723,7 +779,7 @@ var xxx_messageInfo_CanaryStep proto.InternalMessageInfo func (m *CanaryStrategy) Reset() { *m = CanaryStrategy{} } func (*CanaryStrategy) ProtoMessage() {} func (*CanaryStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{24} + return fileDescriptor_e0e705f843545fab, []int{26} } func (m *CanaryStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -751,7 +807,7 @@ var xxx_messageInfo_CanaryStrategy proto.InternalMessageInfo func (m *CloudWatchMetric) Reset() { *m = CloudWatchMetric{} } func (*CloudWatchMetric) ProtoMessage() {} func (*CloudWatchMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{25} + return fileDescriptor_e0e705f843545fab, []int{27} } func (m *CloudWatchMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -779,7 +835,7 @@ var xxx_messageInfo_CloudWatchMetric proto.InternalMessageInfo func (m *CloudWatchMetricDataQuery) Reset() { *m = CloudWatchMetricDataQuery{} } func (*CloudWatchMetricDataQuery) ProtoMessage() {} func (*CloudWatchMetricDataQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{26} + return fileDescriptor_e0e705f843545fab, []int{28} } func (m *CloudWatchMetricDataQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -807,7 +863,7 @@ var xxx_messageInfo_CloudWatchMetricDataQuery proto.InternalMessageInfo func (m *CloudWatchMetricStat) Reset() { *m = CloudWatchMetricStat{} } func (*CloudWatchMetricStat) ProtoMessage() {} func (*CloudWatchMetricStat) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{27} + return fileDescriptor_e0e705f843545fab, []int{29} } func (m *CloudWatchMetricStat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -835,7 +891,7 @@ var xxx_messageInfo_CloudWatchMetricStat proto.InternalMessageInfo func (m *CloudWatchMetricStatMetric) Reset() { *m = CloudWatchMetricStatMetric{} } func (*CloudWatchMetricStatMetric) ProtoMessage() {} func (*CloudWatchMetricStatMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{28} + return fileDescriptor_e0e705f843545fab, []int{30} } func (m *CloudWatchMetricStatMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -863,7 +919,7 @@ var xxx_messageInfo_CloudWatchMetricStatMetric proto.InternalMessageInfo func (m *CloudWatchMetricStatMetricDimension) Reset() { *m = CloudWatchMetricStatMetricDimension{} } func (*CloudWatchMetricStatMetricDimension) ProtoMessage() {} func (*CloudWatchMetricStatMetricDimension) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{29} + return fileDescriptor_e0e705f843545fab, []int{31} } func (m *CloudWatchMetricStatMetricDimension) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -891,7 +947,7 @@ var xxx_messageInfo_CloudWatchMetricStatMetricDimension proto.InternalMessageInf func (m *ClusterAnalysisTemplate) Reset() { *m = ClusterAnalysisTemplate{} } func (*ClusterAnalysisTemplate) ProtoMessage() {} func (*ClusterAnalysisTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{30} + return fileDescriptor_e0e705f843545fab, []int{32} } func (m *ClusterAnalysisTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -919,7 +975,7 @@ var xxx_messageInfo_ClusterAnalysisTemplate proto.InternalMessageInfo func (m *ClusterAnalysisTemplateList) Reset() { *m = ClusterAnalysisTemplateList{} } func (*ClusterAnalysisTemplateList) ProtoMessage() {} func (*ClusterAnalysisTemplateList) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{31} + return fileDescriptor_e0e705f843545fab, []int{33} } func (m *ClusterAnalysisTemplateList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -947,7 +1003,7 @@ var xxx_messageInfo_ClusterAnalysisTemplateList proto.InternalMessageInfo func (m *DatadogMetric) Reset() { *m = DatadogMetric{} } func (*DatadogMetric) ProtoMessage() {} func (*DatadogMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{32} + return fileDescriptor_e0e705f843545fab, []int{34} } func (m *DatadogMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -975,7 +1031,7 @@ var xxx_messageInfo_DatadogMetric proto.InternalMessageInfo func (m *DryRun) Reset() { *m = DryRun{} } func (*DryRun) ProtoMessage() {} func (*DryRun) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{33} + return fileDescriptor_e0e705f843545fab, []int{35} } func (m *DryRun) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1003,7 +1059,7 @@ var xxx_messageInfo_DryRun proto.InternalMessageInfo func (m *Experiment) Reset() { *m = Experiment{} } func (*Experiment) ProtoMessage() {} func (*Experiment) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{34} + return fileDescriptor_e0e705f843545fab, []int{36} } func (m *Experiment) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1031,7 +1087,7 @@ var xxx_messageInfo_Experiment proto.InternalMessageInfo func (m *ExperimentAnalysisRunStatus) Reset() { *m = ExperimentAnalysisRunStatus{} } func (*ExperimentAnalysisRunStatus) ProtoMessage() {} func (*ExperimentAnalysisRunStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{35} + return fileDescriptor_e0e705f843545fab, []int{37} } func (m *ExperimentAnalysisRunStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1059,7 +1115,7 @@ var xxx_messageInfo_ExperimentAnalysisRunStatus proto.InternalMessageInfo func (m *ExperimentAnalysisTemplateRef) Reset() { *m = ExperimentAnalysisTemplateRef{} } func (*ExperimentAnalysisTemplateRef) ProtoMessage() {} func (*ExperimentAnalysisTemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{36} + return fileDescriptor_e0e705f843545fab, []int{38} } func (m *ExperimentAnalysisTemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1087,7 +1143,7 @@ var xxx_messageInfo_ExperimentAnalysisTemplateRef proto.InternalMessageInfo func (m *ExperimentCondition) Reset() { *m = ExperimentCondition{} } func (*ExperimentCondition) ProtoMessage() {} func (*ExperimentCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{37} + return fileDescriptor_e0e705f843545fab, []int{39} } func (m *ExperimentCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1115,7 +1171,7 @@ var xxx_messageInfo_ExperimentCondition proto.InternalMessageInfo func (m *ExperimentList) Reset() { *m = ExperimentList{} } func (*ExperimentList) ProtoMessage() {} func (*ExperimentList) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{38} + return fileDescriptor_e0e705f843545fab, []int{40} } func (m *ExperimentList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1143,7 +1199,7 @@ var xxx_messageInfo_ExperimentList proto.InternalMessageInfo func (m *ExperimentSpec) Reset() { *m = ExperimentSpec{} } func (*ExperimentSpec) ProtoMessage() {} func (*ExperimentSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{39} + return fileDescriptor_e0e705f843545fab, []int{41} } func (m *ExperimentSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1171,7 +1227,7 @@ var xxx_messageInfo_ExperimentSpec proto.InternalMessageInfo func (m *ExperimentStatus) Reset() { *m = ExperimentStatus{} } func (*ExperimentStatus) ProtoMessage() {} func (*ExperimentStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{40} + return fileDescriptor_e0e705f843545fab, []int{42} } func (m *ExperimentStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1199,7 +1255,7 @@ var xxx_messageInfo_ExperimentStatus proto.InternalMessageInfo func (m *FieldRef) Reset() { *m = FieldRef{} } func (*FieldRef) ProtoMessage() {} func (*FieldRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{41} + return fileDescriptor_e0e705f843545fab, []int{43} } func (m *FieldRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1227,7 +1283,7 @@ var xxx_messageInfo_FieldRef proto.InternalMessageInfo func (m *GraphiteMetric) Reset() { *m = GraphiteMetric{} } func (*GraphiteMetric) ProtoMessage() {} func (*GraphiteMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{42} + return fileDescriptor_e0e705f843545fab, []int{44} } func (m *GraphiteMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1255,7 +1311,7 @@ var xxx_messageInfo_GraphiteMetric proto.InternalMessageInfo func (m *HeaderRoutingMatch) Reset() { *m = HeaderRoutingMatch{} } func (*HeaderRoutingMatch) ProtoMessage() {} func (*HeaderRoutingMatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{43} + return fileDescriptor_e0e705f843545fab, []int{45} } func (m *HeaderRoutingMatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1283,7 +1339,7 @@ var xxx_messageInfo_HeaderRoutingMatch proto.InternalMessageInfo func (m *InfluxdbMetric) Reset() { *m = InfluxdbMetric{} } func (*InfluxdbMetric) ProtoMessage() {} func (*InfluxdbMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{44} + return fileDescriptor_e0e705f843545fab, []int{46} } func (m *InfluxdbMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1311,7 +1367,7 @@ var xxx_messageInfo_InfluxdbMetric proto.InternalMessageInfo func (m *IstioDestinationRule) Reset() { *m = IstioDestinationRule{} } func (*IstioDestinationRule) ProtoMessage() {} func (*IstioDestinationRule) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{45} + return fileDescriptor_e0e705f843545fab, []int{47} } func (m *IstioDestinationRule) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1339,7 +1395,7 @@ var xxx_messageInfo_IstioDestinationRule proto.InternalMessageInfo func (m *IstioTrafficRouting) Reset() { *m = IstioTrafficRouting{} } func (*IstioTrafficRouting) ProtoMessage() {} func (*IstioTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{46} + return fileDescriptor_e0e705f843545fab, []int{48} } func (m *IstioTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1367,7 +1423,7 @@ var xxx_messageInfo_IstioTrafficRouting proto.InternalMessageInfo func (m *IstioVirtualService) Reset() { *m = IstioVirtualService{} } func (*IstioVirtualService) ProtoMessage() {} func (*IstioVirtualService) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{47} + return fileDescriptor_e0e705f843545fab, []int{49} } func (m *IstioVirtualService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1395,7 +1451,7 @@ var xxx_messageInfo_IstioVirtualService proto.InternalMessageInfo func (m *JobMetric) Reset() { *m = JobMetric{} } func (*JobMetric) ProtoMessage() {} func (*JobMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{48} + return fileDescriptor_e0e705f843545fab, []int{50} } func (m *JobMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1423,7 +1479,7 @@ var xxx_messageInfo_JobMetric proto.InternalMessageInfo func (m *KayentaMetric) Reset() { *m = KayentaMetric{} } func (*KayentaMetric) ProtoMessage() {} func (*KayentaMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{49} + return fileDescriptor_e0e705f843545fab, []int{51} } func (m *KayentaMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1451,7 +1507,7 @@ var xxx_messageInfo_KayentaMetric proto.InternalMessageInfo func (m *KayentaScope) Reset() { *m = KayentaScope{} } func (*KayentaScope) ProtoMessage() {} func (*KayentaScope) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{50} + return fileDescriptor_e0e705f843545fab, []int{52} } func (m *KayentaScope) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1479,7 +1535,7 @@ var xxx_messageInfo_KayentaScope proto.InternalMessageInfo func (m *KayentaThreshold) Reset() { *m = KayentaThreshold{} } func (*KayentaThreshold) ProtoMessage() {} func (*KayentaThreshold) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{51} + return fileDescriptor_e0e705f843545fab, []int{53} } func (m *KayentaThreshold) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1507,7 +1563,7 @@ var xxx_messageInfo_KayentaThreshold proto.InternalMessageInfo func (m *MangedRoutes) Reset() { *m = MangedRoutes{} } func (*MangedRoutes) ProtoMessage() {} func (*MangedRoutes) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{52} + return fileDescriptor_e0e705f843545fab, []int{54} } func (m *MangedRoutes) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1535,7 +1591,7 @@ var xxx_messageInfo_MangedRoutes proto.InternalMessageInfo func (m *Measurement) Reset() { *m = Measurement{} } func (*Measurement) ProtoMessage() {} func (*Measurement) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{53} + return fileDescriptor_e0e705f843545fab, []int{55} } func (m *Measurement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1563,7 +1619,7 @@ var xxx_messageInfo_Measurement proto.InternalMessageInfo func (m *MeasurementRetention) Reset() { *m = MeasurementRetention{} } func (*MeasurementRetention) ProtoMessage() {} func (*MeasurementRetention) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{54} + return fileDescriptor_e0e705f843545fab, []int{56} } func (m *MeasurementRetention) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1591,7 +1647,7 @@ var xxx_messageInfo_MeasurementRetention proto.InternalMessageInfo func (m *Metric) Reset() { *m = Metric{} } func (*Metric) ProtoMessage() {} func (*Metric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{55} + return fileDescriptor_e0e705f843545fab, []int{57} } func (m *Metric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1619,7 +1675,7 @@ var xxx_messageInfo_Metric proto.InternalMessageInfo func (m *MetricProvider) Reset() { *m = MetricProvider{} } func (*MetricProvider) ProtoMessage() {} func (*MetricProvider) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{56} + return fileDescriptor_e0e705f843545fab, []int{58} } func (m *MetricProvider) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1647,7 +1703,7 @@ var xxx_messageInfo_MetricProvider proto.InternalMessageInfo func (m *MetricResult) Reset() { *m = MetricResult{} } func (*MetricResult) ProtoMessage() {} func (*MetricResult) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{57} + return fileDescriptor_e0e705f843545fab, []int{59} } func (m *MetricResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1675,7 +1731,7 @@ var xxx_messageInfo_MetricResult proto.InternalMessageInfo func (m *NewRelicMetric) Reset() { *m = NewRelicMetric{} } func (*NewRelicMetric) ProtoMessage() {} func (*NewRelicMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{58} + return fileDescriptor_e0e705f843545fab, []int{60} } func (m *NewRelicMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1703,7 +1759,7 @@ var xxx_messageInfo_NewRelicMetric proto.InternalMessageInfo func (m *NginxTrafficRouting) Reset() { *m = NginxTrafficRouting{} } func (*NginxTrafficRouting) ProtoMessage() {} func (*NginxTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{59} + return fileDescriptor_e0e705f843545fab, []int{61} } func (m *NginxTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1731,7 +1787,7 @@ var xxx_messageInfo_NginxTrafficRouting proto.InternalMessageInfo func (m *ObjectRef) Reset() { *m = ObjectRef{} } func (*ObjectRef) ProtoMessage() {} func (*ObjectRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{60} + return fileDescriptor_e0e705f843545fab, []int{62} } func (m *ObjectRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1759,7 +1815,7 @@ var xxx_messageInfo_ObjectRef proto.InternalMessageInfo func (m *PauseCondition) Reset() { *m = PauseCondition{} } func (*PauseCondition) ProtoMessage() {} func (*PauseCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{61} + return fileDescriptor_e0e705f843545fab, []int{63} } func (m *PauseCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1787,7 +1843,7 @@ var xxx_messageInfo_PauseCondition proto.InternalMessageInfo func (m *PingPongSpec) Reset() { *m = PingPongSpec{} } func (*PingPongSpec) ProtoMessage() {} func (*PingPongSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{62} + return fileDescriptor_e0e705f843545fab, []int{64} } func (m *PingPongSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1815,7 +1871,7 @@ var xxx_messageInfo_PingPongSpec proto.InternalMessageInfo func (m *PodTemplateMetadata) Reset() { *m = PodTemplateMetadata{} } func (*PodTemplateMetadata) ProtoMessage() {} func (*PodTemplateMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{63} + return fileDescriptor_e0e705f843545fab, []int{65} } func (m *PodTemplateMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1845,7 +1901,7 @@ func (m *PreferredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*PreferredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*PreferredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{64} + return fileDescriptor_e0e705f843545fab, []int{66} } func (m *PreferredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1873,7 +1929,7 @@ var xxx_messageInfo_PreferredDuringSchedulingIgnoredDuringExecution proto.Intern func (m *PrometheusMetric) Reset() { *m = PrometheusMetric{} } func (*PrometheusMetric) ProtoMessage() {} func (*PrometheusMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{65} + return fileDescriptor_e0e705f843545fab, []int{67} } func (m *PrometheusMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1903,7 +1959,7 @@ func (m *RequiredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*RequiredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*RequiredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{66} + return fileDescriptor_e0e705f843545fab, []int{68} } func (m *RequiredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1931,7 +1987,7 @@ var xxx_messageInfo_RequiredDuringSchedulingIgnoredDuringExecution proto.Interna func (m *RollbackWindowSpec) Reset() { *m = RollbackWindowSpec{} } func (*RollbackWindowSpec) ProtoMessage() {} func (*RollbackWindowSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{67} + return fileDescriptor_e0e705f843545fab, []int{69} } func (m *RollbackWindowSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1959,7 +2015,7 @@ var xxx_messageInfo_RollbackWindowSpec proto.InternalMessageInfo func (m *Rollout) Reset() { *m = Rollout{} } func (*Rollout) ProtoMessage() {} func (*Rollout) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{68} + return fileDescriptor_e0e705f843545fab, []int{70} } func (m *Rollout) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1987,7 +2043,7 @@ var xxx_messageInfo_Rollout proto.InternalMessageInfo func (m *RolloutAnalysis) Reset() { *m = RolloutAnalysis{} } func (*RolloutAnalysis) ProtoMessage() {} func (*RolloutAnalysis) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{69} + return fileDescriptor_e0e705f843545fab, []int{71} } func (m *RolloutAnalysis) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2015,7 +2071,7 @@ var xxx_messageInfo_RolloutAnalysis proto.InternalMessageInfo func (m *RolloutAnalysisBackground) Reset() { *m = RolloutAnalysisBackground{} } func (*RolloutAnalysisBackground) ProtoMessage() {} func (*RolloutAnalysisBackground) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{70} + return fileDescriptor_e0e705f843545fab, []int{72} } func (m *RolloutAnalysisBackground) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2043,7 +2099,7 @@ var xxx_messageInfo_RolloutAnalysisBackground proto.InternalMessageInfo func (m *RolloutAnalysisRunStatus) Reset() { *m = RolloutAnalysisRunStatus{} } func (*RolloutAnalysisRunStatus) ProtoMessage() {} func (*RolloutAnalysisRunStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{71} + return fileDescriptor_e0e705f843545fab, []int{73} } func (m *RolloutAnalysisRunStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2071,7 +2127,7 @@ var xxx_messageInfo_RolloutAnalysisRunStatus proto.InternalMessageInfo func (m *RolloutAnalysisTemplate) Reset() { *m = RolloutAnalysisTemplate{} } func (*RolloutAnalysisTemplate) ProtoMessage() {} func (*RolloutAnalysisTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{72} + return fileDescriptor_e0e705f843545fab, []int{74} } func (m *RolloutAnalysisTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2099,7 +2155,7 @@ var xxx_messageInfo_RolloutAnalysisTemplate proto.InternalMessageInfo func (m *RolloutCondition) Reset() { *m = RolloutCondition{} } func (*RolloutCondition) ProtoMessage() {} func (*RolloutCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{73} + return fileDescriptor_e0e705f843545fab, []int{75} } func (m *RolloutCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2127,7 +2183,7 @@ var xxx_messageInfo_RolloutCondition proto.InternalMessageInfo func (m *RolloutExperimentStep) Reset() { *m = RolloutExperimentStep{} } func (*RolloutExperimentStep) ProtoMessage() {} func (*RolloutExperimentStep) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{74} + return fileDescriptor_e0e705f843545fab, []int{76} } func (m *RolloutExperimentStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2157,7 +2213,7 @@ func (m *RolloutExperimentStepAnalysisTemplateRef) Reset() { } func (*RolloutExperimentStepAnalysisTemplateRef) ProtoMessage() {} func (*RolloutExperimentStepAnalysisTemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{75} + return fileDescriptor_e0e705f843545fab, []int{77} } func (m *RolloutExperimentStepAnalysisTemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2185,7 +2241,7 @@ var xxx_messageInfo_RolloutExperimentStepAnalysisTemplateRef proto.InternalMessa func (m *RolloutExperimentTemplate) Reset() { *m = RolloutExperimentTemplate{} } func (*RolloutExperimentTemplate) ProtoMessage() {} func (*RolloutExperimentTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{76} + return fileDescriptor_e0e705f843545fab, []int{78} } func (m *RolloutExperimentTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2213,7 +2269,7 @@ var xxx_messageInfo_RolloutExperimentTemplate proto.InternalMessageInfo func (m *RolloutList) Reset() { *m = RolloutList{} } func (*RolloutList) ProtoMessage() {} func (*RolloutList) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{77} + return fileDescriptor_e0e705f843545fab, []int{79} } func (m *RolloutList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2241,7 +2297,7 @@ var xxx_messageInfo_RolloutList proto.InternalMessageInfo func (m *RolloutPause) Reset() { *m = RolloutPause{} } func (*RolloutPause) ProtoMessage() {} func (*RolloutPause) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{78} + return fileDescriptor_e0e705f843545fab, []int{80} } func (m *RolloutPause) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2269,7 +2325,7 @@ var xxx_messageInfo_RolloutPause proto.InternalMessageInfo func (m *RolloutSpec) Reset() { *m = RolloutSpec{} } func (*RolloutSpec) ProtoMessage() {} func (*RolloutSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{79} + return fileDescriptor_e0e705f843545fab, []int{81} } func (m *RolloutSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2297,7 +2353,7 @@ var xxx_messageInfo_RolloutSpec proto.InternalMessageInfo func (m *RolloutStatus) Reset() { *m = RolloutStatus{} } func (*RolloutStatus) ProtoMessage() {} func (*RolloutStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{80} + return fileDescriptor_e0e705f843545fab, []int{82} } func (m *RolloutStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2325,7 +2381,7 @@ var xxx_messageInfo_RolloutStatus proto.InternalMessageInfo func (m *RolloutStrategy) Reset() { *m = RolloutStrategy{} } func (*RolloutStrategy) ProtoMessage() {} func (*RolloutStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{81} + return fileDescriptor_e0e705f843545fab, []int{83} } func (m *RolloutStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2353,7 +2409,7 @@ var xxx_messageInfo_RolloutStrategy proto.InternalMessageInfo func (m *RolloutTrafficRouting) Reset() { *m = RolloutTrafficRouting{} } func (*RolloutTrafficRouting) ProtoMessage() {} func (*RolloutTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{82} + return fileDescriptor_e0e705f843545fab, []int{84} } func (m *RolloutTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2381,7 +2437,7 @@ var xxx_messageInfo_RolloutTrafficRouting proto.InternalMessageInfo func (m *RouteMatch) Reset() { *m = RouteMatch{} } func (*RouteMatch) ProtoMessage() {} func (*RouteMatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{83} + return fileDescriptor_e0e705f843545fab, []int{85} } func (m *RouteMatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2409,7 +2465,7 @@ var xxx_messageInfo_RouteMatch proto.InternalMessageInfo func (m *RunSummary) Reset() { *m = RunSummary{} } func (*RunSummary) ProtoMessage() {} func (*RunSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{84} + return fileDescriptor_e0e705f843545fab, []int{86} } func (m *RunSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2437,7 +2493,7 @@ var xxx_messageInfo_RunSummary proto.InternalMessageInfo func (m *SMITrafficRouting) Reset() { *m = SMITrafficRouting{} } func (*SMITrafficRouting) ProtoMessage() {} func (*SMITrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{85} + return fileDescriptor_e0e705f843545fab, []int{87} } func (m *SMITrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2465,7 +2521,7 @@ var xxx_messageInfo_SMITrafficRouting proto.InternalMessageInfo func (m *ScopeDetail) Reset() { *m = ScopeDetail{} } func (*ScopeDetail) ProtoMessage() {} func (*ScopeDetail) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{86} + return fileDescriptor_e0e705f843545fab, []int{88} } func (m *ScopeDetail) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2493,7 +2549,7 @@ var xxx_messageInfo_ScopeDetail proto.InternalMessageInfo func (m *SecretKeyRef) Reset() { *m = SecretKeyRef{} } func (*SecretKeyRef) ProtoMessage() {} func (*SecretKeyRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{87} + return fileDescriptor_e0e705f843545fab, []int{89} } func (m *SecretKeyRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2521,7 +2577,7 @@ var xxx_messageInfo_SecretKeyRef proto.InternalMessageInfo func (m *SetCanaryScale) Reset() { *m = SetCanaryScale{} } func (*SetCanaryScale) ProtoMessage() {} func (*SetCanaryScale) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{88} + return fileDescriptor_e0e705f843545fab, []int{90} } func (m *SetCanaryScale) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2549,7 +2605,7 @@ var xxx_messageInfo_SetCanaryScale proto.InternalMessageInfo func (m *SetHeaderRoute) Reset() { *m = SetHeaderRoute{} } func (*SetHeaderRoute) ProtoMessage() {} func (*SetHeaderRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{89} + return fileDescriptor_e0e705f843545fab, []int{91} } func (m *SetHeaderRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2577,7 +2633,7 @@ var xxx_messageInfo_SetHeaderRoute proto.InternalMessageInfo func (m *SetMirrorRoute) Reset() { *m = SetMirrorRoute{} } func (*SetMirrorRoute) ProtoMessage() {} func (*SetMirrorRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{90} + return fileDescriptor_e0e705f843545fab, []int{92} } func (m *SetMirrorRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2605,7 +2661,7 @@ var xxx_messageInfo_SetMirrorRoute proto.InternalMessageInfo func (m *StickinessConfig) Reset() { *m = StickinessConfig{} } func (*StickinessConfig) ProtoMessage() {} func (*StickinessConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{91} + return fileDescriptor_e0e705f843545fab, []int{93} } func (m *StickinessConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2633,7 +2689,7 @@ var xxx_messageInfo_StickinessConfig proto.InternalMessageInfo func (m *StringMatch) Reset() { *m = StringMatch{} } func (*StringMatch) ProtoMessage() {} func (*StringMatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{92} + return fileDescriptor_e0e705f843545fab, []int{94} } func (m *StringMatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2661,7 +2717,7 @@ var xxx_messageInfo_StringMatch proto.InternalMessageInfo func (m *TCPRoute) Reset() { *m = TCPRoute{} } func (*TCPRoute) ProtoMessage() {} func (*TCPRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{93} + return fileDescriptor_e0e705f843545fab, []int{95} } func (m *TCPRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2689,7 +2745,7 @@ var xxx_messageInfo_TCPRoute proto.InternalMessageInfo func (m *TLSRoute) Reset() { *m = TLSRoute{} } func (*TLSRoute) ProtoMessage() {} func (*TLSRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{94} + return fileDescriptor_e0e705f843545fab, []int{96} } func (m *TLSRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2717,7 +2773,7 @@ var xxx_messageInfo_TLSRoute proto.InternalMessageInfo func (m *TemplateService) Reset() { *m = TemplateService{} } func (*TemplateService) ProtoMessage() {} func (*TemplateService) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{95} + return fileDescriptor_e0e705f843545fab, []int{97} } func (m *TemplateService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2745,7 +2801,7 @@ var xxx_messageInfo_TemplateService proto.InternalMessageInfo func (m *TemplateSpec) Reset() { *m = TemplateSpec{} } func (*TemplateSpec) ProtoMessage() {} func (*TemplateSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{96} + return fileDescriptor_e0e705f843545fab, []int{98} } func (m *TemplateSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2773,7 +2829,7 @@ var xxx_messageInfo_TemplateSpec proto.InternalMessageInfo func (m *TemplateStatus) Reset() { *m = TemplateStatus{} } func (*TemplateStatus) ProtoMessage() {} func (*TemplateStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{97} + return fileDescriptor_e0e705f843545fab, []int{99} } func (m *TemplateStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2801,7 +2857,7 @@ var xxx_messageInfo_TemplateStatus proto.InternalMessageInfo func (m *TraefikTrafficRouting) Reset() { *m = TraefikTrafficRouting{} } func (*TraefikTrafficRouting) ProtoMessage() {} func (*TraefikTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{98} + return fileDescriptor_e0e705f843545fab, []int{100} } func (m *TraefikTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2829,7 +2885,7 @@ var xxx_messageInfo_TraefikTrafficRouting proto.InternalMessageInfo func (m *TrafficWeights) Reset() { *m = TrafficWeights{} } func (*TrafficWeights) ProtoMessage() {} func (*TrafficWeights) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{99} + return fileDescriptor_e0e705f843545fab, []int{101} } func (m *TrafficWeights) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2857,7 +2913,7 @@ var xxx_messageInfo_TrafficWeights proto.InternalMessageInfo func (m *ValueFrom) Reset() { *m = ValueFrom{} } func (*ValueFrom) ProtoMessage() {} func (*ValueFrom) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{100} + return fileDescriptor_e0e705f843545fab, []int{102} } func (m *ValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2885,7 +2941,7 @@ var xxx_messageInfo_ValueFrom proto.InternalMessageInfo func (m *WavefrontMetric) Reset() { *m = WavefrontMetric{} } func (*WavefrontMetric) ProtoMessage() {} func (*WavefrontMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{101} + return fileDescriptor_e0e705f843545fab, []int{103} } func (m *WavefrontMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2913,7 +2969,7 @@ var xxx_messageInfo_WavefrontMetric proto.InternalMessageInfo func (m *WebMetric) Reset() { *m = WebMetric{} } func (*WebMetric) ProtoMessage() {} func (*WebMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{102} + return fileDescriptor_e0e705f843545fab, []int{104} } func (m *WebMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2941,7 +2997,7 @@ var xxx_messageInfo_WebMetric proto.InternalMessageInfo func (m *WebMetricHeader) Reset() { *m = WebMetricHeader{} } func (*WebMetricHeader) ProtoMessage() {} func (*WebMetricHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{103} + return fileDescriptor_e0e705f843545fab, []int{105} } func (m *WebMetricHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2969,7 +3025,7 @@ var xxx_messageInfo_WebMetricHeader proto.InternalMessageInfo func (m *WeightDestination) Reset() { *m = WeightDestination{} } func (*WeightDestination) ProtoMessage() {} func (*WeightDestination) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{104} + return fileDescriptor_e0e705f843545fab, []int{106} } func (m *WeightDestination) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3008,6 +3064,8 @@ func init() { proto.RegisterType((*AnalysisTemplateList)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisTemplateList") proto.RegisterType((*AnalysisTemplateSpec)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisTemplateSpec") proto.RegisterType((*AntiAffinity)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AntiAffinity") + proto.RegisterType((*ApisixRoute)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ApisixRoute") + proto.RegisterType((*ApisixTrafficRouting)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ApisixTrafficRouting") proto.RegisterType((*AppMeshTrafficRouting)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AppMeshTrafficRouting") proto.RegisterType((*AppMeshVirtualNodeGroup)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AppMeshVirtualNodeGroup") proto.RegisterType((*AppMeshVirtualNodeReference)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AppMeshVirtualNodeReference") @@ -4296,6 +4354,78 @@ func (m *AntiAffinity) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ApisixRoute) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ApisixRoute) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ApisixRoute) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Rules) > 0 { + for iNdEx := len(m.Rules) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Rules[iNdEx]) + copy(dAtA[i:], m.Rules[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Rules[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ApisixTrafficRouting) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ApisixTrafficRouting) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ApisixTrafficRouting) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Route != nil { + { + size, err := m.Route.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *AppMeshTrafficRouting) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -8360,6 +8490,18 @@ func (m *RolloutTrafficRouting) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Apisix != nil { + { + size, err := m.Apisix.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } if len(m.ManagedRoutes) > 0 { for iNdEx := len(m.ManagedRoutes) - 1; iNdEx >= 0; iNdEx-- { { @@ -9681,27 +9823,57 @@ func (m *AnalysisTemplateSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } - if len(m.MeasurementRetention) > 0 { - for _, e := range m.MeasurementRetention { - l = e.Size() + if len(m.MeasurementRetention) > 0 { + for _, e := range m.MeasurementRetention { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *AntiAffinity) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PreferredDuringSchedulingIgnoredDuringExecution != nil { + l = m.PreferredDuringSchedulingIgnoredDuringExecution.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.RequiredDuringSchedulingIgnoredDuringExecution != nil { + l = m.RequiredDuringSchedulingIgnoredDuringExecution.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + +func (m *ApisixRoute) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Rules) > 0 { + for _, s := range m.Rules { + l = len(s) n += 1 + l + sovGenerated(uint64(l)) } } return n } -func (m *AntiAffinity) Size() (n int) { +func (m *ApisixTrafficRouting) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.PreferredDuringSchedulingIgnoredDuringExecution != nil { - l = m.PreferredDuringSchedulingIgnoredDuringExecution.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - if m.RequiredDuringSchedulingIgnoredDuringExecution != nil { - l = m.RequiredDuringSchedulingIgnoredDuringExecution.Size() + if m.Route != nil { + l = m.Route.Size() n += 1 + l + sovGenerated(uint64(l)) } return n @@ -11228,6 +11400,10 @@ func (m *RolloutTrafficRouting) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if m.Apisix != nil { + l = m.Apisix.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -11821,6 +11997,27 @@ func (this *AntiAffinity) String() string { }, "") return s } +func (this *ApisixRoute) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ApisixRoute{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Rules:` + fmt.Sprintf("%v", this.Rules) + `,`, + `}`, + }, "") + return s +} +func (this *ApisixTrafficRouting) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ApisixTrafficRouting{`, + `Route:` + strings.Replace(this.Route.String(), "ApisixRoute", "ApisixRoute", 1) + `,`, + `}`, + }, "") + return s +} func (this *AppMeshTrafficRouting) String() string { if this == nil { return "nil" @@ -12938,6 +13135,7 @@ func (this *RolloutTrafficRouting) String() string { `AppMesh:` + strings.Replace(this.AppMesh.String(), "AppMeshTrafficRouting", "AppMeshTrafficRouting", 1) + `,`, `Traefik:` + strings.Replace(this.Traefik.String(), "TraefikTrafficRouting", "TraefikTrafficRouting", 1) + `,`, `ManagedRoutes:` + repeatedStringForManagedRoutes + `,`, + `Apisix:` + strings.Replace(this.Apisix.String(), "ApisixTrafficRouting", "ApisixTrafficRouting", 1) + `,`, `}`, }, "") return s @@ -15188,6 +15386,206 @@ func (m *AntiAffinity) Unmarshal(dAtA []byte) error { } return nil } +func (m *ApisixRoute) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ApisixRoute: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ApisixRoute: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rules", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Rules = append(m.Rules, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ApisixTrafficRouting) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ApisixTrafficRouting: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ApisixTrafficRouting: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Route", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Route == nil { + m.Route = &ApisixRoute{} + } + if err := m.Route.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *AppMeshTrafficRouting) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -28359,6 +28757,42 @@ func (m *RolloutTrafficRouting) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Apisix", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Apisix == nil { + m.Apisix = &ApisixTrafficRouting{} + } + if err := m.Apisix.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/pkg/apis/rollouts/v1alpha1/generated.proto b/pkg/apis/rollouts/v1alpha1/generated.proto index 47f548c751..901ee55d2e 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.proto +++ b/pkg/apis/rollouts/v1alpha1/generated.proto @@ -215,6 +215,21 @@ message AntiAffinity { optional RequiredDuringSchedulingIgnoredDuringExecution requiredDuringSchedulingIgnoredDuringExecution = 2; } +// ApisixRoute holds information on the APISIX Route the rollout needs to modify +message ApisixRoute { + // Name refer to the name of the APISIX Route used to route traffic to the service + optional string name = 1; + + // RuleRef a list of the APISIX Route HTTP Rules used to route traffic to the service + repeated string rules = 2; +} + +// ApisixTrafficRouting defines the configuration required to use APISIX as traffic router +message ApisixTrafficRouting { + // Route references an Apisix Route to modify to shape traffic + optional ApisixRoute route = 1; +} + // AppMeshTrafficRouting configuration for AWS AppMesh service mesh to enable fine grain configuration message AppMeshTrafficRouting { // VirtualService references an AppMesh VirtualService and VirtualRouter to modify to shape traffic @@ -1452,6 +1467,9 @@ message RolloutTrafficRouting { // A list of HTTP routes that Argo Rollouts manages, the order of this array also becomes the precedence in the upstream // traffic router. repeated MangedRoutes managedRoutes = 8; + + // Apisix holds specific configuration to use Apisix to route traffic + optional ApisixTrafficRouting apisix = 9; } message RouteMatch { diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index 61aa275f21..be67b21a29 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -43,6 +43,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisTemplateList": schema_pkg_apis_rollouts_v1alpha1_AnalysisTemplateList(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisTemplateSpec": schema_pkg_apis_rollouts_v1alpha1_AnalysisTemplateSpec(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AntiAffinity": schema_pkg_apis_rollouts_v1alpha1_AntiAffinity(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ApisixRoute": schema_pkg_apis_rollouts_v1alpha1_ApisixRoute(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ApisixTrafficRouting": schema_pkg_apis_rollouts_v1alpha1_ApisixTrafficRouting(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AppMeshTrafficRouting": schema_pkg_apis_rollouts_v1alpha1_AppMeshTrafficRouting(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AppMeshVirtualNodeGroup": schema_pkg_apis_rollouts_v1alpha1_AppMeshVirtualNodeGroup(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AppMeshVirtualNodeReference": schema_pkg_apis_rollouts_v1alpha1_AppMeshVirtualNodeReference(ref), @@ -791,6 +793,64 @@ func schema_pkg_apis_rollouts_v1alpha1_AntiAffinity(ref common.ReferenceCallback } } +func schema_pkg_apis_rollouts_v1alpha1_ApisixRoute(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ApisixRoute holds information on the APISIX Route the rollout needs to modify", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name refer to the name of the APISIX Route used to route traffic to the service", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "rules": { + SchemaProps: spec.SchemaProps{ + Description: "RuleRef a list of the APISIX Route HTTP Rules used to route traffic to the service", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + +func schema_pkg_apis_rollouts_v1alpha1_ApisixTrafficRouting(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ApisixTrafficRouting defines the configuration required to use APISIX as traffic router", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "route": { + SchemaProps: spec.SchemaProps{ + Description: "Route references an Apisix Route to modify to shape traffic", + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ApisixRoute"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ApisixRoute"}, + } +} + func schema_pkg_apis_rollouts_v1alpha1_AppMeshTrafficRouting(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4290,11 +4350,17 @@ func schema_pkg_apis_rollouts_v1alpha1_RolloutTrafficRouting(ref common.Referenc }, }, }, + "apisix": { + SchemaProps: spec.SchemaProps{ + Description: "Apisix holds specific configuration to use Apisix to route traffic", + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ApisixTrafficRouting"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ALBTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AmbassadorTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AppMeshTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.IstioTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MangedRoutes", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.NginxTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SMITrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.TraefikTrafficRouting"}, + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ALBTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AmbassadorTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ApisixTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AppMeshTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.IstioTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MangedRoutes", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.NginxTrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SMITrafficRouting", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.TraefikTrafficRouting"}, } } diff --git a/pkg/apis/rollouts/v1alpha1/types.go b/pkg/apis/rollouts/v1alpha1/types.go index 0304e5429c..516971af30 100644 --- a/pkg/apis/rollouts/v1alpha1/types.go +++ b/pkg/apis/rollouts/v1alpha1/types.go @@ -369,6 +369,8 @@ type RolloutTrafficRouting struct { // A list of HTTP routes that Argo Rollouts manages, the order of this array also becomes the precedence in the upstream // traffic router. ManagedRoutes []MangedRoutes `json:"managedRoutes,omitempty" protobuf:"bytes,8,rep,name=managedRoutes"` + // Apisix holds specific configuration to use Apisix to route traffic + Apisix *ApisixTrafficRouting `json:"apisix,omitempty" protobuf:"bytes,9,opt,name=apisix"` } type MangedRoutes struct { @@ -383,6 +385,20 @@ type TraefikTrafficRouting struct { WeightedTraefikServiceName string `json:"weightedTraefikServiceName" protobuf:"bytes,1,name=weightedTraefikServiceName"` } +// ApisixTrafficRouting defines the configuration required to use APISIX as traffic router +type ApisixTrafficRouting struct { + // Route references an Apisix Route to modify to shape traffic + Route *ApisixRoute `json:"route,omitempty" protobuf:"bytes,1,opt,name=route"` +} + +// ApisixRoute holds information on the APISIX Route the rollout needs to modify +type ApisixRoute struct { + // Name refer to the name of the APISIX Route used to route traffic to the service + Name string `json:"name" protobuf:"bytes,1,name=name"` + // RuleRef a list of the APISIX Route HTTP Rules used to route traffic to the service + Rules []string `json:"rules,omitempty" protobuf:"bytes,2,rep,name=rules"` +} + // AmbassadorTrafficRouting defines the configuration required to use Ambassador as traffic // router type AmbassadorTrafficRouting struct { diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go index ae420961f7..c8888b17f3 100644 --- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go @@ -397,6 +397,48 @@ func (in *AntiAffinity) DeepCopy() *AntiAffinity { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApisixRoute) DeepCopyInto(out *ApisixRoute) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRoute. +func (in *ApisixRoute) DeepCopy() *ApisixRoute { + if in == nil { + return nil + } + out := new(ApisixRoute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApisixTrafficRouting) DeepCopyInto(out *ApisixTrafficRouting) { + *out = *in + if in.Route != nil { + in, out := &in.Route, &out.Route + *out = new(ApisixRoute) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixTrafficRouting. +func (in *ApisixTrafficRouting) DeepCopy() *ApisixTrafficRouting { + if in == nil { + return nil + } + out := new(ApisixTrafficRouting) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AppMeshTrafficRouting) DeepCopyInto(out *AppMeshTrafficRouting) { *out = *in @@ -2304,6 +2346,11 @@ func (in *RolloutTrafficRouting) DeepCopyInto(out *RolloutTrafficRouting) { *out = make([]MangedRoutes, len(*in)) copy(*out, *in) } + if in.Apisix != nil { + in, out := &in.Apisix, &out.Apisix + *out = new(ApisixTrafficRouting) + (*in).DeepCopyInto(*out) + } return } diff --git a/rollout/trafficrouting.go b/rollout/trafficrouting.go index ebc22b9704..41e2db48be 100644 --- a/rollout/trafficrouting.go +++ b/rollout/trafficrouting.go @@ -10,11 +10,13 @@ import ( "github.com/argoproj/argo-rollouts/rollout/trafficrouting" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/alb" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/ambassador" + a6 "github.com/argoproj/argo-rollouts/rollout/trafficrouting/apisix" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/appmesh" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/istio" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/nginx" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/smi" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/traefik" + a6util "github.com/argoproj/argo-rollouts/utils/apisix" "github.com/argoproj/argo-rollouts/utils/conditions" "github.com/argoproj/argo-rollouts/utils/defaults" "github.com/argoproj/argo-rollouts/utils/record" @@ -94,6 +96,15 @@ func (c *Controller) NewTrafficRoutingReconciler(roCtx *rolloutContext) ([]traff })) } + if rollout.Spec.Strategy.Canary.TrafficRouting.Apisix != nil { + dynamicClient := a6util.NewDynamicClient(c.dynamicclientset, rollout.GetNamespace()) + trafficReconcilers = append(trafficReconcilers, a6.NewReconciler(&a6.ReconcilerConfig{ + Rollout: rollout, + Client: dynamicClient, + Recorder: c.recorder, + })) + } + // ensure that the trafficReconcilers is a healthy list and its not empty if len(trafficReconcilers) > 0 { return trafficReconcilers, nil diff --git a/rollout/trafficrouting/apisix/apisix.go b/rollout/trafficrouting/apisix/apisix.go new file mode 100644 index 0000000000..823a8b6102 --- /dev/null +++ b/rollout/trafficrouting/apisix/apisix.go @@ -0,0 +1,198 @@ +package apisix + +import ( + "context" + "fmt" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/utils/record" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// Type holds this controller type +const Type = "Apisix" + +const apisixRouteUpdateError = "ApisixRouteUpdateError" + +type ReconcilerConfig struct { + Rollout *v1alpha1.Rollout + Client ClientInterface + Recorder record.EventRecorder +} + +type Reconciler struct { + Rollout *v1alpha1.Rollout + Client ClientInterface + Recorder record.EventRecorder +} + +func (r *Reconciler) sendWarningEvent(id, msg string) { + r.sendEvent(corev1.EventTypeWarning, id, msg) +} + +func (r *Reconciler) sendEvent(eventType, id, msg string) { + r.Recorder.Eventf(r.Rollout, record.EventOptions{EventType: eventType, EventReason: id}, msg) +} + +type ClientInterface interface { + Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) + Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) +} + +func NewReconciler(cfg *ReconcilerConfig) *Reconciler { + reconciler := &Reconciler{ + Rollout: cfg.Rollout, + Client: cfg.Client, + Recorder: cfg.Recorder, + } + return reconciler +} + +func (r *Reconciler) UpdateHash(canaryHash, stableHash string, additionalDestinations ...v1alpha1.WeightDestination) error { + return nil +} + +func (r *Reconciler) SetWeight(desiredWeight int32, additionalDestinations ...v1alpha1.WeightDestination) error { + ctx := context.TODO() + rollout := r.Rollout + apisixRouteName := rollout.Spec.Strategy.Canary.TrafficRouting.Apisix.Route.Name + apisixRoute, err := r.Client.Get(ctx, apisixRouteName, metav1.GetOptions{}) + if err != nil { + return err + } + + httpRoutes, isFound, err := unstructured.NestedSlice(apisixRoute.Object, "spec", "http") + if err != nil { + return err + } + if !isFound { + return errors.New("spec.http was not found in Apisix Route manifest") + } + rules := rollout.Spec.Strategy.Canary.TrafficRouting.Apisix.Route.Rules + if rules == nil { + rules = append(rules, apisixRouteName) + } + for _, ruleName := range rules { + httpRoute, err := GetHttpRoute(httpRoutes, ruleName) + if err != nil { + return err + } + + backends, err := GetBackends(httpRoute) + if err != nil { + return err + } + + canaryBackendName := rollout.Spec.Strategy.Canary.CanaryService + err = setBackendWeight(canaryBackendName, backends, int64(desiredWeight)) + if err != nil { + return err + } + + stableBackendName := rollout.Spec.Strategy.Canary.StableService + err = setBackendWeight(stableBackendName, backends, int64(100-desiredWeight)) + if err != nil { + return err + } + } + + err = unstructured.SetNestedSlice(apisixRoute.Object, httpRoutes, "spec", "http") + if err != nil { + return err + } + _, err = r.Client.Update(ctx, apisixRoute, metav1.UpdateOptions{}) + if err != nil { + msg := fmt.Sprintf("Error updating apisix route %q: %s", apisixRoute.GetName(), err) + r.sendWarningEvent(apisixRouteUpdateError, msg) + } + + return err +} + +func GetHttpRoute(routes []interface{}, ref string) (interface{}, error) { + for _, route := range routes { + typedRoute, ok := route.(map[string]interface{}) + if !ok { + return nil, errors.New("Failed type assertion for Apisix http route") + } + rawName, ok := typedRoute["name"] + if !ok { + return nil, errors.New("Apisix http route rule name field not found") + } + typedName, ok := rawName.(string) + if !ok { + return nil, errors.New("Failed type assertion for Apisix http route rule name") + } + if typedName == ref { + return route, nil + } + + } + return nil, errors.New(fmt.Sprintf("Apisix http route rule %s not found", ref)) +} + +func GetBackends(httpRoute interface{}) ([]interface{}, error) { + typedHttpRoute, ok := httpRoute.(map[string]interface{}) + if !ok { + return nil, errors.New("Failed type assertion for Apisix http route") + } + rawBackends, ok := typedHttpRoute["backends"] + if !ok { + return nil, errors.New("Apisix http route backends not found") + } + backends, ok := rawBackends.([]interface{}) + if !ok { + return nil, errors.New("Failed type assertion for Apisix http route backends") + } + return backends, nil +} + +func setBackendWeight(backendName string, backends []interface{}, weight int64) error { + found := false + for _, backend := range backends { + typedBackend, ok := backend.(map[string]interface{}) + if !ok { + return errors.New("Failed type assertion for Apisix http route backend") + } + nameOfCurrentBackend, isFound, err := unstructured.NestedString(typedBackend, "serviceName") + if err != nil { + return err + } + if !isFound { + return errors.New("serviceName field was not found in backend") + } + if nameOfCurrentBackend == backendName { + found = true + typedBackend["weight"] = weight + break + } + } + + if !found { + return errors.New(fmt.Sprintf("apisix route %s backend was not found", backendName)) + } + return nil +} + +func (r *Reconciler) SetHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute) error { + return nil +} + +func (r *Reconciler) VerifyWeight(desiredWeight int32, additionalDestinations ...v1alpha1.WeightDestination) (*bool, error) { + return nil, nil +} + +func (r *Reconciler) Type() string { + return Type +} + +func (r *Reconciler) SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error { + return nil +} + +func (r *Reconciler) RemoveManagedRoutes() error { + return nil +} diff --git a/rollout/trafficrouting/apisix/apisix_test.go b/rollout/trafficrouting/apisix/apisix_test.go new file mode 100644 index 0000000000..6007484c76 --- /dev/null +++ b/rollout/trafficrouting/apisix/apisix_test.go @@ -0,0 +1,427 @@ +package apisix + +import ( + "testing" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/rollout/trafficrouting/apisix/mocks" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" +) + +const apisixRoute = ` +apiVersion: apisix.apache.org/v2 +kind: ApisixRoute +metadata: + name: mocks-apisix-route +spec: + http: + - name: mocks-apisix-route + match: + paths: + - /* + methods: + - GET + backends: + - serviceName: stable-rollout + servicePort: 80 + weight: 100 + - serviceName: canary-rollout + servicePort: 80 + weight: 0 +` + +const errorApisixRoute = ` +apiVersion: apisix.apache.org/v2 +kind: ApisixRoute +metadata: + name: mocks-apisix-route +` + +var ( + client *mocks.FakeClient = &mocks.FakeClient{} +) + +const ( + stableServiceName string = "stable-rollout" + fakeStableServiceName string = "fake-stable-rollout" + canaryServiceName string = "canary-rollout" + fakeCanaryServiceName string = "fake-canary-rollout" + apisixRouteName string = "mocks-apisix-route" +) + +func TestUpdateHash(t *testing.T) { + t.Run("UpdateHash", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: client, + } + r := NewReconciler(&cfg) + + // When + err := r.UpdateHash("", "") + + // Then + assert.NoError(t, err) + }) +} + +func TestSetWeight(t *testing.T) { + mocks.ApisixRouteObj = toUnstructured(t, apisixRoute) + mocks.ErrorApisixRouteObj = toUnstructured(t, errorApisixRoute) + t.Run("SetWeight", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: client, + } + r := NewReconciler(&cfg) + + // When + err := r.SetWeight(30) + + // Then + assert.NoError(t, err) + apisixHttpRoutesObj, isFound, err := unstructured.NestedSlice(mocks.ApisixRouteObj.Object, "spec", "http") + assert.NoError(t, err) + assert.Equal(t, isFound, true) + apisixHttpRouteObj, err := GetHttpRoute(apisixHttpRoutesObj, apisixRouteName) + assert.NoError(t, err) + backends, err := GetBackends(apisixHttpRouteObj) + assert.NoError(t, err) + for _, backend := range backends { + typedBackend, ok := backend.(map[string]interface{}) + assert.Equal(t, ok, true) + nameOfCurrentBackend, isFound, err := unstructured.NestedString(typedBackend, "serviceName") + assert.NoError(t, err) + assert.Equal(t, isFound, true) + if nameOfCurrentBackend == stableServiceName { + rawWeight, ok := typedBackend["weight"] + assert.Equal(t, ok, true) + weight, ok := rawWeight.(int64) + assert.Equal(t, ok, true) + assert.Equal(t, weight, int64(70)) + } + if nameOfCurrentBackend == canaryServiceName { + rawWeight, ok := typedBackend["weight"] + assert.Equal(t, ok, true) + weight, ok := rawWeight.(int64) + assert.Equal(t, ok, true) + assert.Equal(t, weight, int64(30)) + } + } + }) + t.Run("SetWeightWithError", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: &mocks.FakeClient{ + IsGetError: true, + }, + } + r := NewReconciler(&cfg) + + // When + err := r.SetWeight(30) + + // Then + assert.Error(t, err) + }) + t.Run("SetWeightWithErrorManifest", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: &mocks.FakeClient{ + IsGetErrorManifest: true, + }, + } + r := NewReconciler(&cfg) + + // When + err := r.SetWeight(30) + + // Then + assert.Error(t, err) + }) + t.Run("SetWeightWithErrorStableName", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(fakeStableServiceName, canaryServiceName, apisixRouteName), + Client: client, + } + r := NewReconciler(&cfg) + + // When + err := r.SetWeight(30) + + // Then + assert.Error(t, err) + }) + t.Run("SetWeightWithErrorCanaryName", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, fakeCanaryServiceName, apisixRouteName), + Client: client, + } + r := NewReconciler(&cfg) + + // When + err := r.SetWeight(30) + + // Then + assert.Error(t, err) + }) + t.Run("ApisixUpdateError", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: &mocks.FakeClient{ + UpdateError: true, + }, + Recorder: &mocks.FakeRecorder{}, + } + r := NewReconciler(&cfg) + + // When + err := r.SetWeight(30) + + // Then + assert.Error(t, err) + }) +} + +func TestGetHttpRouteError(t *testing.T) { + type testcase struct { + routes []interface{} + ref string + } + testcases := []testcase{ + { + routes: nil, + ref: "nil", + }, + { + routes: []interface{}{""}, + ref: "Failed type", + }, + { + routes: []interface{}{ + map[string]interface{}{ + "x": nil, + }, + }, + ref: "noname", + }, + { + routes: []interface{}{ + map[string]interface{}{ + "name": 123, + }, + }, + ref: "name type error", + }, + { + routes: []interface{}{ + map[string]interface{}{ + "name": "123", + }, + }, + ref: "name not found", + }, + } + + for _, tc := range testcases { + _, err := GetHttpRoute(tc.routes, tc.ref) + assert.Error(t, err) + } +} + +func TestGetBackendsError(t *testing.T) { + testcases := []interface{}{ + nil, + 123, + map[string]interface{}{}, + map[string]interface{}{ + "backends": "123", + }, + } + + for _, tc := range testcases { + _, err := GetBackends(tc) + assert.Error(t, err) + } +} + +func TestSetBackendWeightError(t *testing.T) { + type testcase struct { + backendName string + backends []interface{} + weight int64 + } + testcases := []testcase{ + {}, + { + backends: []interface{}{ + "", + }, + }, + { + backends: []interface{}{ + map[string]interface{}{ + "abc": 123, + }, + }, + }, + { + backends: []interface{}{ + map[string]interface{}{ + "serviceName": 123, + }, + }, + }, + } + + for _, tc := range testcases { + err := setBackendWeight(tc.backendName, tc.backends, tc.weight) + assert.Error(t, err) + } +} + +func TestSetHeaderRoute(t *testing.T) { + t.Run("SetHeaderRoute", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: client, + } + r := NewReconciler(&cfg) + + // When + err := r.SetHeaderRoute(&v1alpha1.SetHeaderRoute{ + Name: "set-header", + Match: []v1alpha1.HeaderRoutingMatch{{ + HeaderName: "header-name", + HeaderValue: &v1alpha1.StringMatch{ + Exact: "value", + }, + }}, + }) + + // Then + assert.NoError(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) + }) +} + +func TestSetMirrorRoute(t *testing.T) { + t.Run("SetMirrorRoute", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: client, + } + r := NewReconciler(&cfg) + + // When + err := r.SetMirrorRoute(&v1alpha1.SetMirrorRoute{ + Name: "mirror-route", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{Exact: "GET"}, + }}, + }) + + // Then + assert.NoError(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) + }) +} + +func toUnstructured(t *testing.T, manifest string) *unstructured.Unstructured { + t.Helper() + obj := &unstructured.Unstructured{} + + dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + _, _, err := dec.Decode([]byte(manifest), nil, obj) + if err != nil { + t.Fatal(err) + } + return obj +} + +func TestVerifyWeight(t *testing.T) { + t.Run("VerifyWeight", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: client, + } + r := NewReconciler(&cfg) + + // When + isSynced, err := r.VerifyWeight(32) + + // Then + assert.Nil(t, isSynced) + assert.Nil(t, err) + }) +} + +func TestType(t *testing.T) { + mocks.ApisixRouteObj = toUnstructured(t, apisixRoute) + t.Run("Type", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, apisixRouteName), + Client: client, + } + r := NewReconciler(&cfg) + + // When + reconcilerType := r.Type() + + // Then + assert.Equal(t, Type, reconcilerType) + }) +} + +func newRollout(stableSvc, canarySvc, apisixRouteRef string) *v1alpha1.Rollout { + return &v1alpha1.Rollout{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rollout", + Namespace: "default", + }, + Spec: v1alpha1.RolloutSpec{ + Strategy: v1alpha1.RolloutStrategy{ + Canary: &v1alpha1.CanaryStrategy{ + StableService: stableSvc, + CanaryService: canarySvc, + TrafficRouting: &v1alpha1.RolloutTrafficRouting{ + Apisix: &v1alpha1.ApisixTrafficRouting{ + Route: &v1alpha1.ApisixRoute{ + Name: apisixRouteRef, + }, + }, + }, + }, + }, + }, + } +} diff --git a/rollout/trafficrouting/apisix/mocks/apisix.go b/rollout/trafficrouting/apisix/mocks/apisix.go new file mode 100644 index 0000000000..77204364fd --- /dev/null +++ b/rollout/trafficrouting/apisix/mocks/apisix.go @@ -0,0 +1,101 @@ +package mocks + +import ( + "context" + + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/runtime" + + argoRecord "github.com/argoproj/argo-rollouts/utils/record" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/tools/record" +) + +type FakeDynamicClient struct { + IsListError bool +} + +type FakeClient struct { + IsGetError bool + IsGetErrorManifest bool + UpdateError bool + IsListError bool +} + +type FakeRecorder struct{} + +var ( + ApisixRouteObj *unstructured.Unstructured + ErrorApisixRouteObj *unstructured.Unstructured +) + +func (f *FakeRecorder) Eventf(object runtime.Object, opts argoRecord.EventOptions, messageFmt string, args ...interface{}) { +} + +func (f *FakeRecorder) Warnf(object runtime.Object, opts argoRecord.EventOptions, messageFmt string, args ...interface{}) { +} + +func (f *FakeRecorder) K8sRecorder() record.EventRecorder { + return nil +} + +func (f *FakeClient) Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) { + return nil, nil +} + +func (f *FakeClient) Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) { + if f.IsGetError { + return ApisixRouteObj, errors.New("Apisix get error") + } + if f.IsGetErrorManifest { + return ErrorApisixRouteObj, nil + } + return ApisixRouteObj, nil +} + +func (f *FakeClient) Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) { + if f.UpdateError { + return obj, errors.New("Apisix update error") + } + return obj, nil +} + +func (f *FakeClient) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error) { + return nil, nil +} + +func (f *FakeClient) Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error { + return nil +} + +func (f *FakeClient) DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error { + return nil +} + +func (f *FakeClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) { + if f.IsListError { + return nil, errors.New("Apisix list error") + } + return nil, nil +} + +func (f *FakeClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { + return nil, nil +} + +func (f *FakeClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) { + return nil, nil +} + +func (f *FakeClient) Namespace(string) dynamic.ResourceInterface { + return f +} + +func (f *FakeDynamicClient) Resource(schema.GroupVersionResource) dynamic.NamespaceableResourceInterface { + return &FakeClient{IsListError: f.IsListError} +} diff --git a/rollout/trafficrouting_test.go b/rollout/trafficrouting_test.go index 6c1e49f476..8ac4a08e67 100644 --- a/rollout/trafficrouting_test.go +++ b/rollout/trafficrouting_test.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "github.com/argoproj/argo-rollouts/rollout/trafficrouting/apisix" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -17,6 +19,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/rollout/mocks" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/alb" + apisixMocks "github.com/argoproj/argo-rollouts/rollout/trafficrouting/apisix/mocks" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/appmesh" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/istio" "github.com/argoproj/argo-rollouts/rollout/trafficrouting/nginx" @@ -664,6 +667,31 @@ func TestNewTrafficRoutingReconciler(t *testing.T) { assert.Equal(t, traefik.Type, networkReconciler.Type()) } } + { + tsController := Controller{ + reconcilerBase: reconcilerBase{ + dynamicclientset: &apisixMocks.FakeDynamicClient{}, + }, + } + r := newCanaryRollout("foo", 10, nil, steps, pointer.Int32Ptr(1), intstr.FromInt(1), intstr.FromInt(0)) + r.Spec.Strategy.Canary.TrafficRouting = &v1alpha1.RolloutTrafficRouting{ + Apisix: &v1alpha1.ApisixTrafficRouting{ + Route: &v1alpha1.ApisixRoute{ + Name: "apisix-route", + }, + }, + } + roCtx := &rolloutContext{ + rollout: r, + log: logutil.WithRollout(r), + } + networkReconcilerList, err := tsController.NewTrafficRoutingReconciler(roCtx) + for _, networkReconciler := range networkReconcilerList { + assert.Nil(t, err) + assert.NotNil(t, networkReconciler) + assert.Equal(t, apisix.Type, networkReconciler.Type()) + } + } { // (2) Multiple Reconcilers (Nginx + SMI) tsController := Controller{} diff --git a/test/e2e/apisix/rollout-apisix-canary.yaml b/test/e2e/apisix/rollout-apisix-canary.yaml new file mode 100644 index 0000000000..289e4bec60 --- /dev/null +++ b/test/e2e/apisix/rollout-apisix-canary.yaml @@ -0,0 +1,99 @@ +apiVersion: v1 +kind: Service +metadata: + name: rollout-apisix-canary-canary +spec: + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: rollout-apisix-canary + # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: + # rollouts-pod-template-hash: 7bf84f9696 +--- +apiVersion: v1 +kind: Service +metadata: + name: rollout-apisix-canary-stable +spec: + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: rollout-apisix-canary + # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: + # rollouts-pod-template-hash: 789746c88d +--- +apiVersion: apisix.apache.org/v2 +kind: ApisixRoute +metadata: + name: rollouts-apisix-route +spec: + http: + - name: rollouts-apisix + match: + paths: + - /* + methods: + - GET + - POST + - PUT + - DELETE + - PATCH + hosts: + - rollouts-demo.apisix.local + backends: + - serviceName: rollout-apisix-canary-stable + servicePort: 80 + weight: 100 + - serviceName: rollout-apisix-canary-canary + servicePort: 80 + weight: 0 +--- +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-apisix-canary +spec: + replicas: 1 + strategy: + canary: + canaryService: rollout-apisix-canary-canary + stableService: rollout-apisix-canary-stable + trafficRouting: + apisix: + route: + name: rollouts-apisix-route + rules: + - rollouts-apisix + steps: + - setWeight: 5 + - pause: + duration: 15 + - setWeight: 50 + - pause: + duration: 15 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: rollout-apisix-canary + template: + metadata: + labels: + app: rollout-apisix-canary + spec: + containers: + - name: rollout-apisix-canary + image: nginx:1.19-alpine + ports: + - name: http + containerPort: 80 + protocol: TCP + resources: + requests: + memory: 16Mi + cpu: 5m diff --git a/test/e2e/apisix_test.go b/test/e2e/apisix_test.go new file mode 100644 index 0000000000..d9a344aee9 --- /dev/null +++ b/test/e2e/apisix_test.go @@ -0,0 +1,108 @@ +//go:build e2e +// +build e2e + +package e2e + +import ( + a6 "github.com/argoproj/argo-rollouts/rollout/trafficrouting/apisix" + "github.com/argoproj/argo-rollouts/test/fixtures" + "github.com/stretchr/testify/suite" + "github.com/tj/assert" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "testing" + "time" +) + +const ( + apisixRouteName = "rollouts-apisix" + apisixCanaryService = "rollout-apisix-canary-canary" + apisixStableService = "rollout-apisix-canary-stable" +) + +type APISIXSuite struct { + fixtures.E2ESuite +} + +func TestAPISIXSuite(t *testing.T) { + suite.Run(t, new(APISIXSuite)) +} + +func (a *APISIXSuite) SetupSuite() { + a.E2ESuite.SetupSuite() + if !a.ApisixEnabled { + a.T().SkipNow() + } +} + +func (s *APISIXSuite) TestAPISIXCanaryStep() { + + s.Given(). + RolloutObjects("@apisix/rollout-apisix-canary.yaml"). + When(). + ApplyManifests(). + WaitForRolloutStatus("Healthy"). + Then(). + Assert(func(t *fixtures.Then) { + s.check(t, 100, 0) + }). + ExpectExperimentCount(0). + When(). + UpdateSpec(). + WaitForRolloutCanaryStepIndex(1). + Sleep(5*time.Second). + Then(). + Assert(func(t *fixtures.Then) { + s.check(t, 95, 5) + }). + ExpectExperimentCount(0). + When(). + WaitForRolloutCanaryStepIndex(2). + Sleep(3*time.Second). + Then(). + Assert(func(t *fixtures.Then) { + s.check(t, 50, 50) + }). + When(). + PromoteRollout(). + WaitForRolloutStatus("Healthy"). + Sleep(1*time.Second). // stable is currently set first, and then changes made to VirtualServices/DestinationRules + Then(). + Assert(func(t *fixtures.Then) { + s.check(t, 100, 0) + }). + ExpectRevisionPodCount("1", 1) // don't scale down old replicaset since it will be within scaleDownDelay +} + +func (s *APISIXSuite) check(t *fixtures.Then, stableWeight int64, canaryWeight int64) { + ar := t.GetApisixRoute() + assert.NotEmpty(s.T(), ar) + apisixHttpRoutesObj, isFound, err := unstructured.NestedSlice(ar.Object, "spec", "http") + assert.NoError(s.T(), err) + assert.Equal(s.T(), isFound, true) + apisixHttpRouteObj, err := a6.GetHttpRoute(apisixHttpRoutesObj, apisixRouteName) + assert.NoError(s.T(), err) + backends, err := a6.GetBackends(apisixHttpRouteObj) + assert.NoError(s.T(), err) + + for _, backend := range backends { + typedBackend, ok := backend.(map[string]interface{}) + assert.Equal(s.T(), ok, true) + nameOfCurrentBackend, isFound, err := unstructured.NestedString(typedBackend, "serviceName") + assert.NoError(s.T(), err) + assert.Equal(s.T(), isFound, true) + if nameOfCurrentBackend == apisixStableService { + rawWeight, ok := typedBackend["weight"] + assert.Equal(s.T(), ok, true) + weight, ok := rawWeight.(int64) + assert.Equal(s.T(), ok, true) + assert.Equal(s.T(), weight, stableWeight) + } + if nameOfCurrentBackend == apisixCanaryService { + rawWeight, ok := typedBackend["weight"] + assert.Equal(s.T(), ok, true) + weight, ok := rawWeight.(int64) + assert.Equal(s.T(), ok, true) + assert.Equal(s.T(), weight, canaryWeight) + } + } +} diff --git a/test/e2e/crds/apisix.yaml b/test/e2e/crds/apisix.yaml new file mode 100644 index 0000000000..38c4b5eb64 --- /dev/null +++ b/test/e2e/crds/apisix.yaml @@ -0,0 +1,615 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: apisixroutes.apisix.apache.org +spec: + group: apisix.apache.org + scope: Namespaced + names: + plural: apisixroutes + singular: apisixroute + kind: ApisixRoute + shortNames: + - ar + versions: + - name: v2beta3 + served: true + storage: false + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .spec.http[].match.hosts + name: Hosts + type: string + priority: 0 + - jsonPath: .spec.http[].match.paths + name: URIs + type: string + priority: 0 + - jsonPath: .spec.http[].backends[].serviceName + name: Target Service(HTTP) + type: string + priority: 1 + - jsonPath: .spec.tcp[].match.ingressPort + name: Ingress Server Port(TCP) + type: integer + priority: 1 + - jsonPath: .spec.tcp[].match.backend.serviceName + name: Target Service(TCP) + type: string + priority: 1 + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + priority: 0 + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + anyOf: + - required: ["http"] + - required: ["stream"] + properties: + http: + type: array + minItems: 1 + items: + type: object + required: ["name", "match", "backends"] + properties: + name: + type: string + minLength: 1 + priority: + type: integer + timeout: + type: object + properties: + connect: + type: string + send: + type: string + read: + type: string + match: + type: object + required: + - paths + properties: + paths: + type: array + minItems: 1 + items: + type: string + pattern: "^/[a-zA-Z0-9\\-._~%!$&'()+,;=:@/]*\\*?$" + hosts: + type: array + minItems: 1 + items: + type: string + pattern: "^\\*?[0-9a-zA-Z-._]+$" + methods: + type: array + minItems: 1 + items: + type: string + enum: + - "CONNECT" + - "DELETE" + - "GET" + - "HEAD" + - "OPTIONS" + - "PATCH" + - "POST" + - "PUT" + - "TRACE" + remoteAddrs: + type: array + minItems: 1 + items: + type: string + exprs: + type: array + minItems: 1 + items: + type: object + properties: + subject: + type: object + properties: + scope: + type: string + enum: + - "Cookie" + - "Header" + - "Path" + - "Query" + name: + type: string + minLength: 1 + required: + - scope + op: + type: string + enum: + - Equal + - NotEqual + - GreaterThan + - LessThan + - In + - NotIn + - RegexMatch + - RegexNotMatch + - RegexMatchCaseInsensitive + - RegexNotMatchCaseInsensitive + value: + type: string + set: + type: array + items: + type: string + oneOf: + - required: ["subject", "op", "value"] + - required: ["subject", "op", "set"] + websocket: + type: boolean + plugin_config_name: + type: string + minLength: 1 + backends: + type: array + minItems: 1 + items: + type: object + properties: + serviceName: + type: string + minLength: 1 + servicePort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resolveGranularity: + type: string + enum: ["endpoint", "service"] + weight: + type: integer + minimum: 0 + subset: + type: string + required: + - serviceName + - servicePort + plugins: + type: array + items: + type: object + properties: + name: + type: string + minLength: 1 + enable: + type: boolean + config: + type: object + x-kubernetes-preserve-unknown-fields: true # we have to enable it since plugin config + required: + - name + - enable + authentication: + type: object + properties: + enable: + type: boolean + type: + type: string + enum: + - "basicAuth" + - "keyAuth" + - "jwtAuth" + - "wolfRBAC" + - "hmacAuth" + keyAuth: + type: object + properties: + header: + type: string + jwtAuth: + type: object + properties: + header: + type: string + query: + type: string + cookie: + type: string + required: + - enable + stream: + type: array + minItems: 1 + items: + type: object + required: ["name", "match", "backend", "protocol"] + properties: + "protocol": + type: string + enum: ["TCP", "UDP"] + name: + type: string + minLength: 1 + match: + type: object + properties: + ingressPort: + type: integer + minimum: 1 + maximum: 65535 + required: + - ingressPort + backend: + type: object + properties: + serviceName: + type: string + minLength: 1 + servicePort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resolveGranularity: + type: string + enum: ["endpoint", "service"] + subset: + type: string + required: + - serviceName + - servicePort + status: + type: object + properties: + conditions: + type: array + items: + type: object + properties: + "type": + type: string + reason: + type: string + status: + type: string + message: + type: string + observedGeneration: + type: integer + - name: v2 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - jsonPath: .spec.http[].match.hosts + name: Hosts + type: string + priority: 0 + - jsonPath: .spec.http[].match.paths + name: URIs + type: string + priority: 0 + - jsonPath: .spec.http[].backends[].serviceName + name: Target Service(HTTP) + type: string + priority: 1 + - jsonPath: .spec.tcp[].match.ingressPort + name: Ingress Server Port(TCP) + type: integer + priority: 1 + - jsonPath: .spec.tcp[].match.backend.serviceName + name: Target Service(TCP) + type: string + priority: 1 + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + priority: 0 + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + anyOf: + - required: ["http"] + - required: ["stream"] + properties: + http: + type: array + minItems: 1 + items: + type: object + anyOf: + - required: ["name", "match", "backends"] + - required: ["name", "match", "upstreams"] + properties: + name: + type: string + minLength: 1 + priority: + type: integer + timeout: + type: object + properties: + connect: + type: string + send: + type: string + read: + type: string + match: + type: object + required: + - paths + properties: + paths: + type: array + minItems: 1 + items: + type: string + pattern: "^/[a-zA-Z0-9\\-._~%!$&'()+,;=:@/]*\\*?$" + hosts: + type: array + minItems: 1 + items: + type: string + pattern: "^\\*?[0-9a-zA-Z-._]+$" + methods: + type: array + minItems: 1 + items: + type: string + enum: + - "CONNECT" + - "DELETE" + - "GET" + - "HEAD" + - "OPTIONS" + - "PATCH" + - "POST" + - "PUT" + - "TRACE" + remoteAddrs: + type: array + minItems: 1 + items: + type: string + exprs: + type: array + minItems: 1 + items: + type: object + properties: + subject: + type: object + properties: + scope: + type: string + enum: + - "Cookie" + - "Header" + - "Path" + - "Query" + - "Variable" + name: + type: string + minLength: 1 + required: + - scope + op: + type: string + enum: + - Equal + - NotEqual + - GreaterThan + - GreaterThanEqual + - LessThan + - LessThanEqual + - In + - NotIn + - RegexMatch + - RegexNotMatch + - RegexMatchCaseInsensitive + - RegexNotMatchCaseInsensitive + value: + type: string + set: + type: array + items: + type: string + oneOf: + - required: ["subject", "op", "value"] + - required: ["subject", "op", "set"] + websocket: + type: boolean + plugin_config_name: + type: string + minLength: 1 + upstreams: + description: Upstreams refer to ApisixUpstream CRD + type: array + items: + description: ApisixRouteUpstreamReference contains a ApisixUpstream CRD reference + type: object + properties: + name: + type: string + weight: + type: integer + backends: + type: array + minItems: 1 + items: + type: object + properties: + serviceName: + type: string + minLength: 1 + servicePort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resolveGranularity: + type: string + enum: ["endpoint", "service"] + weight: + type: integer + minimum: 0 + subset: + type: string + required: + - serviceName + - servicePort + plugins: + type: array + items: + type: object + properties: + name: + type: string + minLength: 1 + enable: + type: boolean + config: + type: object + x-kubernetes-preserve-unknown-fields: true # we have to enable it since plugin config + required: + - name + - enable + authentication: + type: object + properties: + enable: + type: boolean + type: + type: string + enum: + - "basicAuth" + - "keyAuth" + - "jwtAuth" + - "wolfRBAC" + - "hmacAuth" + keyAuth: + type: object + properties: + header: + type: string + jwtAuth: + type: object + properties: + header: + type: string + query: + type: string + cookie: + type: string + required: + - enable + stream: + type: array + minItems: 1 + items: + type: object + required: ["name", "match", "backend", "protocol"] + properties: + "protocol": + type: string + enum: ["TCP", "UDP"] + name: + type: string + minLength: 1 + match: + type: object + properties: + host: + type: string + ingressPort: + type: integer + minimum: 1 + maximum: 65535 + required: + - ingressPort + backend: + type: object + properties: + serviceName: + type: string + minLength: 1 + servicePort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resolveGranularity: + type: string + enum: ["endpoint", "service"] + subset: + type: string + required: + - serviceName + - servicePort + plugins: + type: array + items: + type: object + properties: + name: + type: string + minLength: 1 + enable: + type: boolean + config: + type: object + x-kubernetes-preserve-unknown-fields: true # we have to enable it since plugin config + required: + - name + - enable + status: + type: object + properties: + conditions: + type: array + items: + type: object + properties: + "type": + type: string + reason: + type: string + status: + type: string + message: + type: string + observedGeneration: + type: integer diff --git a/test/fixtures/common.go b/test/fixtures/common.go index 890a3794a2..ed03d7efcc 100644 --- a/test/fixtures/common.go +++ b/test/fixtures/common.go @@ -13,6 +13,8 @@ import ( "text/tabwriter" "time" + a6util "github.com/argoproj/argo-rollouts/utils/apisix" + "github.com/ghodss/yaml" smiv1alpha1 "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/split/v1alpha1" smiclientset "github.com/servicemeshinterface/smi-sdk-go/pkg/gen/client/split/clientset/versioned" @@ -571,6 +573,16 @@ func (c *Common) GetVirtualService() *istio.VirtualService { return &vsvc } +func (c *Common) GetApisixRoute() *unstructured.Unstructured { + ro := c.Rollout() + ctx := context.TODO() + dyClient := a6util.NewDynamicClient(c.dynamicClient, c.namespace) + name := ro.Spec.Strategy.Canary.TrafficRouting.Apisix.Route.Name + a6Route, err := dyClient.Get(ctx, name, metav1.GetOptions{}) + c.CheckError(err) + return a6Route +} + func (c *Common) GetAppMeshVirtualRouter() *unstructured.Unstructured { ro := c.Rollout() ctx := context.TODO() diff --git a/test/fixtures/e2e_suite.go b/test/fixtures/e2e_suite.go index 6eb059b446..a774afcf94 100644 --- a/test/fixtures/e2e_suite.go +++ b/test/fixtures/e2e_suite.go @@ -8,6 +8,8 @@ import ( "strconv" "time" + a6util "github.com/argoproj/argo-rollouts/utils/apisix" + smiclientset "github.com/servicemeshinterface/smi-sdk-go/pkg/gen/client/split/clientset/versioned" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/suite" @@ -123,6 +125,7 @@ type E2ESuite struct { IstioEnabled bool SMIEnabled bool AppMeshEnabled bool + ApisixEnabled bool } func (s *E2ESuite) SetupSuite() { @@ -165,6 +168,10 @@ func (s *E2ESuite) SetupSuite() { if appmeshutil.DoesAppMeshExist(s.dynamicClient, s.namespace) { s.AppMeshEnabled = true } + + if a6util.DoesApisixExist(s.dynamicClient, s.namespace) { + s.ApisixEnabled = true + } } func (s *E2ESuite) TearDownSuite() { diff --git a/ui/src/models/rollout/generated/api.ts b/ui/src/models/rollout/generated/api.ts index 7b25b8912e..188a617c28 100644 --- a/ui/src/models/rollout/generated/api.ts +++ b/ui/src/models/rollout/generated/api.ts @@ -215,6 +215,38 @@ export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1AntiAffinit */ requiredDuringSchedulingIgnoredDuringExecution?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RequiredDuringSchedulingIgnoredDuringExecution; } +/** + * + * @export + * @interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixRoute + */ +export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixRoute { + /** + * + * @type {string} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixRoute + */ + name?: string; + /** + * + * @type {Array} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixRoute + */ + rules?: Array; +} +/** + * + * @export + * @interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixTrafficRouting + */ +export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixTrafficRouting { + /** + * + * @type {GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixRoute} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixTrafficRouting + */ + route?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixRoute; +} /** * * @export @@ -1546,6 +1578,12 @@ export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RolloutTraf * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RolloutTrafficRouting */ managedRoutes?: Array; + /** + * + * @type {GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixTrafficRouting} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RolloutTrafficRouting + */ + apisix?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1ApisixTrafficRouting; } /** * diff --git a/utils/apisix/apisix.go b/utils/apisix/apisix.go new file mode 100644 index 0000000000..3f5984e022 --- /dev/null +++ b/utils/apisix/apisix.go @@ -0,0 +1,43 @@ +package apisix + +import ( + "context" + "strings" + + "github.com/argoproj/argo-rollouts/utils/defaults" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" +) + +const apisixRoutes = "apisixroutes" + +var ( + apiGroupToResource = map[string]string{ + defaults.DefaultApisixAPIGroup: apisixRoutes, + } +) + +func DoesApisixExist(dynamicClient dynamic.Interface, namespace string) bool { + _, err := dynamicClient.Resource(GetMappingGVR()).Namespace(namespace).List(context.TODO(), metav1.ListOptions{Limit: 1}) + if err != nil { + return false + } + return true +} + +func NewDynamicClient(di dynamic.Interface, namespace string) dynamic.ResourceInterface { + return di.Resource(GetMappingGVR()).Namespace(namespace) +} + +func GetMappingGVR() schema.GroupVersionResource { + group := defaults.DefaultApisixAPIGroup + parts := strings.Split(defaults.DefaultApisixVersion, "/") + version := parts[len(parts)-1] + resourceName := apiGroupToResource[group] + return schema.GroupVersionResource{ + Group: group, + Version: version, + Resource: resourceName, + } +} diff --git a/utils/apisix/apisix_test.go b/utils/apisix/apisix_test.go new file mode 100644 index 0000000000..f3a45277ef --- /dev/null +++ b/utils/apisix/apisix_test.go @@ -0,0 +1,33 @@ +package apisix + +import ( + "testing" + + "github.com/tj/assert" + + "github.com/argoproj/argo-rollouts/rollout/trafficrouting/apisix/mocks" +) + +func TestNewDynamicClient(t *testing.T) { + t.Run("NewDynamicClient", func(t *testing.T) { + // Given + t.Parallel() + fakeDynamicClient := &mocks.FakeDynamicClient{} + + // When + NewDynamicClient(fakeDynamicClient, "default") + }) +} + +func TestDoesApisixExist(t *testing.T) { + t.Run("exist", func(t *testing.T) { + fakeDynamicClient := &mocks.FakeDynamicClient{} + assert.True(t, DoesApisixExist(fakeDynamicClient, "")) + }) + t.Run("not exist", func(t *testing.T) { + fakeDynamicClient := &mocks.FakeDynamicClient{ + IsListError: true, + } + assert.False(t, DoesApisixExist(fakeDynamicClient, "")) + }) +} diff --git a/utils/defaults/defaults.go b/utils/defaults/defaults.go index 9b18b16db1..db605eb99c 100644 --- a/utils/defaults/defaults.go +++ b/utils/defaults/defaults.go @@ -56,6 +56,8 @@ const ( DefaultAppMeshCRDVersion = "v1beta2" DefaultTraefikAPIGroup = "traefik.containo.us" DefaultTraefikVersion = "traefik.containo.us/v1alpha1" + DefaultApisixAPIGroup = "apisix.apache.org" + DefaultApisixVersion = "apisix.apache.org/v2" ) var (