diff --git a/Makefile b/Makefile index 3302b41c1d..61f91da345 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ DEV_IMAGE=false E2E_INSTANCE_ID ?= argo-rollouts-e2e E2E_TEST_OPTIONS ?= E2E_PARALLEL ?= 4 -E2E_WAIT_TIMEOUT ?= 90 +E2E_WAIT_TIMEOUT ?= 120 override LDFLAGS += \ -X ${PACKAGE}/utils/version.version=${VERSION} \ diff --git a/docs/features/specification.md b/docs/features/specification.md index dbea102893..100ddbb430 100644 --- a/docs/features/specification.md +++ b/docs/features/specification.md @@ -268,19 +268,52 @@ spec: # Setting header based route will send all 100 traffic to the canary for the requests # O with a specified header, in this case request header "version":"2" # (supported only with trafficRouting, for Istio only at the moment) - - setHeaderRouting: + - setHeaderRoute: + # Name of the route that will be created by argo rollouts this must also be configured + # in spec.strategy.canary.trafficRouting.managedRoutes + name: "header-route-1" + # The matching rules for the header route, if this is missing it acts as a removal of the route. match: + # headerName The name of the header to apply the match rules to. - headerName: "version" + # headerValue must contain exactly one field of exact, regex, or prefix. Not all traffic routers support + # all types headerValue: + # Exact will only match if the header value is exactly the same exact: "2" - - # Sets header based route with specified header values using regex as a value - # Could be used 'headerValue' or 'headerRegex' one of that values - - setHeaderRouting: - match: - - headerName: "version" - headerValue: + # Will match the rule if the regular expression matches regex: "2.0.(.*)" + # prefix will be a prefix match of the header value + prefix: "2.0" + + # Sets up a mirror/shadow based route with the specified match rules + # The traffic will be mirrored at the configured percentage to the canary service + # during the rollout + # (supported only with trafficRouting, for Istio only at the moment) + - setMirrorRoute: + # Name of the route that will be created by argo rollouts this must also be configured + # in spec.strategy.canary.trafficRouting.managedRoutes + name: "header-route-1" + # The percentage of the matched traffic to mirror to the canary + percentage: 100 + # The matching rules for the header route, if this is missing it acts as a removal of the route. + # All conditions inside a single match block have AND semantics, while the list of match blocks have OR semantics. + # Each type within a match (method, path, headers) must have one and only one match type (exact, regex, prefix) + # Not all match types (exact, regex, prefix) will be supported by all traffic routers. + match: + - method: # What HTTP method to match + exact: "GET" + regex: "P.*" + prefix: "POST" + path: # What HTTP url paths to match. + exact: "/test" + regex: ""/test/.*" + prefix: ""/" + headers: + agent-1b: # What HTTP header name to use in the match. + exact: "firefox" + regex: "firefox2(.*)" + prefix: "firefox" # an inline analysis step - analysis: @@ -311,7 +344,14 @@ spec: # will achieve traffic split via a weighted replica counts between # the canary and stable ReplicaSet. trafficRouting: - + # This is a list of routes that Argo Rollouts has the rights to manage it is currently only required for + # setMirrorRoute and setHeaderRoute. The order of managedRoutes array also sets the precedence of the route + # in the traffic router. Argo Rollouts will place these routes in the order specified above any routes already + # defined in the used traffic router if something exists. The names here must match the names from the + # setHeaderRoute and setMirrorRoute steps. + managedRoutes: + - name: set-header + - name: mirror-route # Istio traffic routing configuration istio: # Either virtualService or virtualServices can be configured. diff --git a/docs/features/traffic-management/index.md b/docs/features/traffic-management/index.md index 78561992ca..19385548ec 100644 --- a/docs/features/traffic-management/index.md +++ b/docs/features/traffic-management/index.md @@ -52,11 +52,11 @@ Since the traffic is controlled independently by the Service Mesh resources, the ## Traffic routing based on a header values for Canary Argo Rollouts has ability to send all traffic to the canary-service based on a http request header value. Right now it's implemented for the Istio only. -The step for the header based traffic routing `setHeaderRouting` has a list of matchers for the header. +The step for the header based traffic routing `setHeaderRoute` has a list of matchers for the header. Should be specified the `headerName` - name of the header and a value. The value could be one of 3 `exact` - specify the exact header value, `regex` - value in a regex format, `prefix` - the prefix of the value could be provided. -To disable header based traffic routing just need to specify empty `setHeaderRouting`. +To disable header based traffic routing just need to specify empty `setHeaderRoute` with the name of the route. Example: @@ -69,13 +69,16 @@ spec: canary: canaryService: canary-service stableService: stable-service + managedRoutes: + - name: set-header-1 trafficRouting: istio: virtualService: name: rollouts-demo-vsvc steps: - setWeight: 20 - - setHeaderRouting: # enable header based traffic routing where + - setHeaderRoute: # enable header based traffic routing where + name: "set-header-1" match: - headerName: Custom-Header1 # Custom-Header1=Mozilla headerValue: @@ -87,5 +90,6 @@ spec: headerValue: regex: Mozilla(.*) - pause: {} - - setHeaderRouting: {} # disable header based traffic routing + - setHeaderRouting: + name: "set-header-1" # disable header based traffic routing ``` \ No newline at end of file diff --git a/manifests/crds/rollout-crd.yaml b/manifests/crds/rollout-crd.yaml index bda1fdd4fc..22f6fecd2a 100644 --- a/manifests/crds/rollout-crd.yaml +++ b/manifests/crds/rollout-crd.yaml @@ -593,7 +593,7 @@ spec: format: int32 type: integer type: object - setHeaderRouting: + setHeaderRoute: properties: match: items: @@ -614,6 +614,52 @@ spec: - headerValue type: object type: array + name: + type: string + type: object + setMirrorRoute: + properties: + match: + items: + properties: + headers: + additionalProperties: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + type: object + method: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + path: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + type: object + type: array + name: + type: string + percentage: + format: int32 + type: integer + required: + - name type: object setWeight: format: int32 @@ -755,6 +801,17 @@ spec: type: object type: array type: object + managedRoutes: + items: + properties: + canaryRoute: + type: boolean + name: + type: string + required: + - name + type: object + type: array nginx: properties: additionalIngressAnnotations: diff --git a/manifests/install.yaml b/manifests/install.yaml index 00ff531b5c..cbf53ab6b6 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -11561,7 +11561,7 @@ spec: format: int32 type: integer type: object - setHeaderRouting: + setHeaderRoute: properties: match: items: @@ -11582,6 +11582,52 @@ spec: - headerValue type: object type: array + name: + type: string + type: object + setMirrorRoute: + properties: + match: + items: + properties: + headers: + additionalProperties: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + type: object + method: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + path: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + type: object + type: array + name: + type: string + percentage: + format: int32 + type: integer + required: + - name type: object setWeight: format: int32 @@ -11723,6 +11769,17 @@ spec: type: object type: array type: object + managedRoutes: + items: + properties: + canaryRoute: + type: boolean + name: + type: string + required: + - name + type: object + type: array nginx: properties: additionalIngressAnnotations: diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 3edf1348e4..520783a946 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -11561,7 +11561,7 @@ spec: format: int32 type: integer type: object - setHeaderRouting: + setHeaderRoute: properties: match: items: @@ -11582,6 +11582,52 @@ spec: - headerValue type: object type: array + name: + type: string + type: object + setMirrorRoute: + properties: + match: + items: + properties: + headers: + additionalProperties: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + type: object + method: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + path: + properties: + exact: + type: string + prefix: + type: string + regex: + type: string + type: object + type: object + type: array + name: + type: string + percentage: + format: int32 + type: integer + required: + - name type: object setWeight: format: int32 @@ -11723,6 +11769,17 @@ spec: type: object type: array type: object + managedRoutes: + items: + properties: + canaryRoute: + type: boolean + name: + type: string + required: + - name + type: object + type: array nginx: properties: additionalIngressAnnotations: diff --git a/pkg/apiclient/rollout/rollout.swagger.json b/pkg/apiclient/rollout/rollout.swagger.json index 384103913b..260dffe493 100644 --- a/pkg/apiclient/rollout/rollout.swagger.json +++ b/pkg/apiclient/rollout/rollout.swagger.json @@ -821,9 +821,13 @@ "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetCanaryScale", "title": "SetCanaryScale defines how to scale the newRS without changing traffic weight\n+optional" }, - "setHeaderRouting": { - "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetHeaderRouting", - "title": "SetHeaderRouting defines the route with specified header name to send 100% of traffic to the canary service" + "setHeaderRoute": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetHeaderRoute", + "title": "SetHeaderRoute defines the route with specified header name to send 100% of traffic to the canary service\n+optional" + }, + "setMirrorRoute": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetMirrorRoute", + "title": "SetMirrorRoutes Mirrors traffic that matches rules to a particular destination\n+optional" } }, "description": "CanaryStep defines a step of a canary deployment." @@ -995,6 +999,17 @@ }, "title": "IstioVirtualService holds information on the virtual service the rollout needs to modify" }, + "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MangedRoutes": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "canaryRoute": { + "type": "boolean" + } + } + }, "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MeasurementRetention": { "type": "object", "properties": { @@ -1545,10 +1560,37 @@ "traefik": { "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.TraefikTrafficRouting", "title": "Traefik holds specific configuration to use Traefik to route traffic" + }, + "managedRoutes": { + "type": "array", + "items": { + "$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." } }, "title": "RolloutTrafficRouting hosts all the different configuration for supported service meshes to enable more fine-grained traffic routing" }, + "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.RouteMatch": { + "type": "object", + "properties": { + "method": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.StringMatch", + "title": "Method What http methods should be mirrored\n+optional" + }, + "path": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.StringMatch", + "title": "Path What url paths should be mirrored\n+optional" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.StringMatch" + }, + "title": "Headers What request with matching headers should be mirrored\n+optional" + } + } + }, "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SMITrafficRouting": { "type": "object", "properties": { @@ -1583,9 +1625,13 @@ }, "title": "SetCanaryScale defines how to scale the newRS without changing traffic weight" }, - "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetHeaderRouting": { + "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetHeaderRoute": { "type": "object", "properties": { + "name": { + "type": "string", + "title": "Name this is the name of the route to use for the mirroring of traffic this also needs\nto be included in the `spec.strategy.canary.trafficRouting.managedRoutes` field" + }, "match": { "type": "array", "items": { @@ -1593,7 +1639,28 @@ } } }, - "title": "SetHeaderRouting defines the route with specified header name to send 100% of traffic to the canary service" + "title": "SetHeaderRoute defines the route with specified header name to send 100% of traffic to the canary service" + }, + "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetMirrorRoute": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name this is the name of the route to use for the mirroring of traffic this also needs\nto be included in the `spec.strategy.canary.trafficRouting.managedRoutes` field" + }, + "match": { + "type": "array", + "items": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.RouteMatch" + }, + "title": "Match Contains a list of rules that if mated will mirror the traffic to the services\n+optional" + }, + "percentage": { + "type": "integer", + "format": "int32", + "title": "Services The list of services to mirror the traffic to if the method, path, headers match\nService string `json:\"service\" protobuf:\"bytes,3,opt,name=service\"`\nPercentage What percent of the traffic that matched the rules should be mirrored" + } + } }, "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.StickinessConfig": { "type": "object", diff --git a/pkg/apis/api-rules/violation_exceptions.list b/pkg/apis/api-rules/violation_exceptions.list index d9cc556c27..479a0f52c1 100644 --- a/pkg/apis/api-rules/violation_exceptions.list +++ b/pkg/apis/api-rules/violation_exceptions.list @@ -34,7 +34,9 @@ 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,RolloutExperimentStepAnalysisTemplateRef,Args API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,RolloutStatus,Conditions API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,RolloutStatus,PauseConditions -API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,SetHeaderRouting,Match +API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,RolloutTrafficRouting,ManagedRoutes +API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,SetHeaderRoute,Match +API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,SetMirrorRoute,Match API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,TLSRoute,SNIHosts API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,TrafficWeights,Additional API rule violation: list_type_missing,github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1,WebMetric,Headers diff --git a/pkg/apis/rollouts/v1alpha1/generated.pb.go b/pkg/apis/rollouts/v1alpha1/generated.pb.go index ae221882a7..f9b23035a7 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.pb.go +++ b/pkg/apis/rollouts/v1alpha1/generated.pb.go @@ -1476,10 +1476,38 @@ func (m *KayentaThreshold) XXX_DiscardUnknown() { var xxx_messageInfo_KayentaThreshold proto.InternalMessageInfo +func (m *MangedRoutes) Reset() { *m = MangedRoutes{} } +func (*MangedRoutes) ProtoMessage() {} +func (*MangedRoutes) Descriptor() ([]byte, []int) { + return fileDescriptor_e0e705f843545fab, []int{51} +} +func (m *MangedRoutes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MangedRoutes) 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 *MangedRoutes) XXX_Merge(src proto.Message) { + xxx_messageInfo_MangedRoutes.Merge(m, src) +} +func (m *MangedRoutes) XXX_Size() int { + return m.Size() +} +func (m *MangedRoutes) XXX_DiscardUnknown() { + xxx_messageInfo_MangedRoutes.DiscardUnknown(m) +} + +var xxx_messageInfo_MangedRoutes proto.InternalMessageInfo + func (m *Measurement) Reset() { *m = Measurement{} } func (*Measurement) ProtoMessage() {} func (*Measurement) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{51} + return fileDescriptor_e0e705f843545fab, []int{52} } func (m *Measurement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1507,7 +1535,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{52} + return fileDescriptor_e0e705f843545fab, []int{53} } func (m *MeasurementRetention) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1535,7 +1563,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{53} + return fileDescriptor_e0e705f843545fab, []int{54} } func (m *Metric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1563,7 +1591,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{54} + return fileDescriptor_e0e705f843545fab, []int{55} } func (m *MetricProvider) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1591,7 +1619,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{55} + return fileDescriptor_e0e705f843545fab, []int{56} } func (m *MetricResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1619,7 +1647,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{56} + return fileDescriptor_e0e705f843545fab, []int{57} } func (m *NewRelicMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1647,7 +1675,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{57} + return fileDescriptor_e0e705f843545fab, []int{58} } func (m *NginxTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1675,7 +1703,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{58} + return fileDescriptor_e0e705f843545fab, []int{59} } func (m *ObjectRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1703,7 +1731,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{59} + return fileDescriptor_e0e705f843545fab, []int{60} } func (m *PauseCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1731,7 +1759,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{60} + return fileDescriptor_e0e705f843545fab, []int{61} } func (m *PingPongSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1759,7 +1787,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{61} + return fileDescriptor_e0e705f843545fab, []int{62} } func (m *PodTemplateMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1789,7 +1817,7 @@ func (m *PreferredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*PreferredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*PreferredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{62} + return fileDescriptor_e0e705f843545fab, []int{63} } func (m *PreferredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1817,7 +1845,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{63} + return fileDescriptor_e0e705f843545fab, []int{64} } func (m *PrometheusMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1847,7 +1875,7 @@ func (m *RequiredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*RequiredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*RequiredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{64} + return fileDescriptor_e0e705f843545fab, []int{65} } func (m *RequiredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1875,7 +1903,7 @@ var xxx_messageInfo_RequiredDuringSchedulingIgnoredDuringExecution proto.Interna func (m *Rollout) Reset() { *m = Rollout{} } func (*Rollout) ProtoMessage() {} func (*Rollout) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{65} + return fileDescriptor_e0e705f843545fab, []int{66} } func (m *Rollout) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1903,7 +1931,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{66} + return fileDescriptor_e0e705f843545fab, []int{67} } func (m *RolloutAnalysis) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1931,7 +1959,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{67} + return fileDescriptor_e0e705f843545fab, []int{68} } func (m *RolloutAnalysisBackground) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1959,7 +1987,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{68} + return fileDescriptor_e0e705f843545fab, []int{69} } func (m *RolloutAnalysisRunStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1987,7 +2015,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{69} + return fileDescriptor_e0e705f843545fab, []int{70} } func (m *RolloutAnalysisTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2015,7 +2043,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{70} + return fileDescriptor_e0e705f843545fab, []int{71} } func (m *RolloutCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2043,7 +2071,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{71} + return fileDescriptor_e0e705f843545fab, []int{72} } func (m *RolloutExperimentStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2073,7 +2101,7 @@ func (m *RolloutExperimentStepAnalysisTemplateRef) Reset() { } func (*RolloutExperimentStepAnalysisTemplateRef) ProtoMessage() {} func (*RolloutExperimentStepAnalysisTemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{72} + return fileDescriptor_e0e705f843545fab, []int{73} } func (m *RolloutExperimentStepAnalysisTemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2101,7 +2129,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{73} + return fileDescriptor_e0e705f843545fab, []int{74} } func (m *RolloutExperimentTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2129,7 +2157,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{74} + return fileDescriptor_e0e705f843545fab, []int{75} } func (m *RolloutList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2157,7 +2185,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{75} + return fileDescriptor_e0e705f843545fab, []int{76} } func (m *RolloutPause) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2185,7 +2213,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{76} + return fileDescriptor_e0e705f843545fab, []int{77} } func (m *RolloutSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2213,7 +2241,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{77} + return fileDescriptor_e0e705f843545fab, []int{78} } func (m *RolloutStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2241,7 +2269,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{78} + return fileDescriptor_e0e705f843545fab, []int{79} } func (m *RolloutStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2269,7 +2297,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{79} + return fileDescriptor_e0e705f843545fab, []int{80} } func (m *RolloutTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2294,10 +2322,38 @@ func (m *RolloutTrafficRouting) XXX_DiscardUnknown() { var xxx_messageInfo_RolloutTrafficRouting proto.InternalMessageInfo +func (m *RouteMatch) Reset() { *m = RouteMatch{} } +func (*RouteMatch) ProtoMessage() {} +func (*RouteMatch) Descriptor() ([]byte, []int) { + return fileDescriptor_e0e705f843545fab, []int{81} +} +func (m *RouteMatch) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RouteMatch) 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 *RouteMatch) XXX_Merge(src proto.Message) { + xxx_messageInfo_RouteMatch.Merge(m, src) +} +func (m *RouteMatch) XXX_Size() int { + return m.Size() +} +func (m *RouteMatch) XXX_DiscardUnknown() { + xxx_messageInfo_RouteMatch.DiscardUnknown(m) +} + +var xxx_messageInfo_RouteMatch proto.InternalMessageInfo + func (m *RunSummary) Reset() { *m = RunSummary{} } func (*RunSummary) ProtoMessage() {} func (*RunSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{80} + return fileDescriptor_e0e705f843545fab, []int{82} } func (m *RunSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2325,7 +2381,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{81} + return fileDescriptor_e0e705f843545fab, []int{83} } func (m *SMITrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2353,7 +2409,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{82} + return fileDescriptor_e0e705f843545fab, []int{84} } func (m *ScopeDetail) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2381,7 +2437,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{83} + return fileDescriptor_e0e705f843545fab, []int{85} } func (m *SecretKeyRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2409,7 +2465,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{84} + return fileDescriptor_e0e705f843545fab, []int{86} } func (m *SetCanaryScale) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2434,15 +2490,43 @@ func (m *SetCanaryScale) XXX_DiscardUnknown() { var xxx_messageInfo_SetCanaryScale proto.InternalMessageInfo -func (m *SetHeaderRouting) Reset() { *m = SetHeaderRouting{} } -func (*SetHeaderRouting) ProtoMessage() {} -func (*SetHeaderRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{85} +func (m *SetHeaderRoute) Reset() { *m = SetHeaderRoute{} } +func (*SetHeaderRoute) ProtoMessage() {} +func (*SetHeaderRoute) Descriptor() ([]byte, []int) { + return fileDescriptor_e0e705f843545fab, []int{87} +} +func (m *SetHeaderRoute) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SetHeaderRoute) 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 *SetHeaderRoute) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetHeaderRoute.Merge(m, src) +} +func (m *SetHeaderRoute) XXX_Size() int { + return m.Size() +} +func (m *SetHeaderRoute) XXX_DiscardUnknown() { + xxx_messageInfo_SetHeaderRoute.DiscardUnknown(m) +} + +var xxx_messageInfo_SetHeaderRoute proto.InternalMessageInfo + +func (m *SetMirrorRoute) Reset() { *m = SetMirrorRoute{} } +func (*SetMirrorRoute) ProtoMessage() {} +func (*SetMirrorRoute) Descriptor() ([]byte, []int) { + return fileDescriptor_e0e705f843545fab, []int{88} } -func (m *SetHeaderRouting) XXX_Unmarshal(b []byte) error { +func (m *SetMirrorRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *SetHeaderRouting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *SetMirrorRoute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { @@ -2450,22 +2534,22 @@ func (m *SetHeaderRouting) XXX_Marshal(b []byte, deterministic bool) ([]byte, er } return b[:n], nil } -func (m *SetHeaderRouting) XXX_Merge(src proto.Message) { - xxx_messageInfo_SetHeaderRouting.Merge(m, src) +func (m *SetMirrorRoute) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetMirrorRoute.Merge(m, src) } -func (m *SetHeaderRouting) XXX_Size() int { +func (m *SetMirrorRoute) XXX_Size() int { return m.Size() } -func (m *SetHeaderRouting) XXX_DiscardUnknown() { - xxx_messageInfo_SetHeaderRouting.DiscardUnknown(m) +func (m *SetMirrorRoute) XXX_DiscardUnknown() { + xxx_messageInfo_SetMirrorRoute.DiscardUnknown(m) } -var xxx_messageInfo_SetHeaderRouting proto.InternalMessageInfo +var xxx_messageInfo_SetMirrorRoute proto.InternalMessageInfo func (m *StickinessConfig) Reset() { *m = StickinessConfig{} } func (*StickinessConfig) ProtoMessage() {} func (*StickinessConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{86} + return fileDescriptor_e0e705f843545fab, []int{89} } func (m *StickinessConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2493,7 +2577,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{87} + return fileDescriptor_e0e705f843545fab, []int{90} } func (m *StringMatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2521,7 +2605,7 @@ var xxx_messageInfo_StringMatch proto.InternalMessageInfo func (m *TLSRoute) Reset() { *m = TLSRoute{} } func (*TLSRoute) ProtoMessage() {} func (*TLSRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{88} + return fileDescriptor_e0e705f843545fab, []int{91} } func (m *TLSRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2549,7 +2633,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{89} + return fileDescriptor_e0e705f843545fab, []int{92} } func (m *TemplateService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2577,7 +2661,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{90} + return fileDescriptor_e0e705f843545fab, []int{93} } func (m *TemplateSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2605,7 +2689,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{91} + return fileDescriptor_e0e705f843545fab, []int{94} } func (m *TemplateStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2633,7 +2717,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{92} + return fileDescriptor_e0e705f843545fab, []int{95} } func (m *TraefikTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2661,7 +2745,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{93} + return fileDescriptor_e0e705f843545fab, []int{96} } func (m *TrafficWeights) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2689,7 +2773,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{94} + return fileDescriptor_e0e705f843545fab, []int{97} } func (m *ValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2717,7 +2801,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{95} + return fileDescriptor_e0e705f843545fab, []int{98} } func (m *WavefrontMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2745,7 +2829,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{96} + return fileDescriptor_e0e705f843545fab, []int{99} } func (m *WebMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2773,7 +2857,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{97} + return fileDescriptor_e0e705f843545fab, []int{100} } func (m *WebMetricHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2801,7 +2885,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{98} + return fileDescriptor_e0e705f843545fab, []int{101} } func (m *WeightDestination) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2878,6 +2962,7 @@ func init() { proto.RegisterType((*KayentaMetric)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.KayentaMetric") proto.RegisterType((*KayentaScope)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.KayentaScope") proto.RegisterType((*KayentaThreshold)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.KayentaThreshold") + proto.RegisterType((*MangedRoutes)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MangedRoutes") proto.RegisterType((*Measurement)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.Measurement") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.Measurement.MetadataEntry") proto.RegisterType((*MeasurementRetention)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MeasurementRetention") @@ -2912,12 +2997,15 @@ func init() { proto.RegisterType((*RolloutStatus)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.RolloutStatus") proto.RegisterType((*RolloutStrategy)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.RolloutStrategy") proto.RegisterType((*RolloutTrafficRouting)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.RolloutTrafficRouting") + proto.RegisterType((*RouteMatch)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.RouteMatch") + proto.RegisterMapType((map[string]StringMatch)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.RouteMatch.HeadersEntry") proto.RegisterType((*RunSummary)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.RunSummary") proto.RegisterType((*SMITrafficRouting)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SMITrafficRouting") proto.RegisterType((*ScopeDetail)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ScopeDetail") proto.RegisterType((*SecretKeyRef)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SecretKeyRef") proto.RegisterType((*SetCanaryScale)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetCanaryScale") - proto.RegisterType((*SetHeaderRouting)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetHeaderRouting") + proto.RegisterType((*SetHeaderRoute)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetHeaderRoute") + proto.RegisterType((*SetMirrorRoute)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.SetMirrorRoute") proto.RegisterType((*StickinessConfig)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.StickinessConfig") proto.RegisterType((*StringMatch)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.StringMatch") proto.RegisterType((*TLSRoute)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.TLSRoute") @@ -2938,465 +3026,477 @@ func init() { } var fileDescriptor_e0e705f843545fab = []byte{ - // 7317 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0x59, - 0x75, 0xf0, 0x56, 0xb7, 0xdb, 0x6e, 0x1f, 0x7b, 0xfc, 0x73, 0xc7, 0xc3, 0x78, 0xbd, 0x3b, 0xd3, - 0x4b, 0x2d, 0xda, 0x6f, 0xf9, 0x3e, 0xf0, 0xc0, 0xfe, 0x7c, 0x59, 0x58, 0xb4, 0x49, 0xb7, 0x3d, - 0xb3, 0xe3, 0x59, 0xdb, 0xd3, 0x73, 0xdb, 0x33, 0x03, 0x0b, 0x4b, 0x28, 0x77, 0x5f, 0xb7, 0x6b, - 0xa6, 0xbb, 0xaa, 0xa9, 0xaa, 0xf6, 0x8c, 0x97, 0x15, 0xec, 0x06, 0xed, 0x06, 0x22, 0x10, 0x9b, - 0x00, 0x8a, 0xa2, 0x88, 0x08, 0x45, 0x48, 0x89, 0x02, 0x4f, 0x28, 0x51, 0x5e, 0x90, 0x12, 0x05, - 0x50, 0x88, 0xa2, 0x44, 0x24, 0x4a, 0x02, 0x44, 0xa2, 0x93, 0x35, 0x79, 0x49, 0x94, 0x28, 0x8a, - 0x44, 0x14, 0x31, 0x4f, 0xd1, 0xfd, 0xad, 0x5b, 0x3f, 0xed, 0xe9, 0x76, 0x97, 0x87, 0x55, 0xc2, - 0x5b, 0xf7, 0x39, 0xe7, 0x9e, 0x73, 0xff, 0xcf, 0xb9, 0xe7, 0x9e, 0x7b, 0x0a, 0xd6, 0x9b, 0x76, - 0xb0, 0xdb, 0xdd, 0x5e, 0xae, 0xbb, 0xed, 0x73, 0x96, 0xd7, 0x74, 0x3b, 0x9e, 0x7b, 0x83, 0xfd, - 0x78, 0xa7, 0xe7, 0xb6, 0x5a, 0x6e, 0x37, 0xf0, 0xcf, 0x75, 0x6e, 0x36, 0xcf, 0x59, 0x1d, 0xdb, - 0x3f, 0xa7, 0x20, 0x7b, 0xef, 0xb6, 0x5a, 0x9d, 0x5d, 0xeb, 0xdd, 0xe7, 0x9a, 0xc4, 0x21, 0x9e, - 0x15, 0x90, 0xc6, 0x72, 0xc7, 0x73, 0x03, 0x17, 0xbd, 0x2f, 0xe4, 0xb6, 0x2c, 0xb9, 0xb1, 0x1f, - 0xbf, 0x28, 0xcb, 0x2e, 0x77, 0x6e, 0x36, 0x97, 0x29, 0xb7, 0x65, 0x05, 0x91, 0xdc, 0x96, 0xde, - 0xa9, 0xd5, 0xa5, 0xe9, 0x36, 0xdd, 0x73, 0x8c, 0xe9, 0x76, 0x77, 0x87, 0xfd, 0x63, 0x7f, 0xd8, - 0x2f, 0x2e, 0x6c, 0xe9, 0xe1, 0x9b, 0x4f, 0xf9, 0xcb, 0xb6, 0x4b, 0xeb, 0x76, 0x6e, 0xdb, 0x0a, - 0xea, 0xbb, 0xe7, 0xf6, 0x12, 0x35, 0x5a, 0x32, 0x35, 0xa2, 0xba, 0xeb, 0x91, 0x34, 0x9a, 0x27, - 0x42, 0x9a, 0xb6, 0x55, 0xdf, 0xb5, 0x1d, 0xe2, 0xed, 0x87, 0xad, 0x6e, 0x93, 0xc0, 0x4a, 0x2b, - 0x75, 0xae, 0x5f, 0x29, 0xaf, 0xeb, 0x04, 0x76, 0x9b, 0x24, 0x0a, 0xfc, 0xff, 0xbb, 0x15, 0xf0, - 0xeb, 0xbb, 0xa4, 0x6d, 0x25, 0xca, 0x3d, 0xde, 0xaf, 0x5c, 0x37, 0xb0, 0x5b, 0xe7, 0x6c, 0x27, - 0xf0, 0x03, 0x2f, 0x5e, 0xc8, 0xfc, 0x56, 0x1e, 0x26, 0xcb, 0xeb, 0x95, 0x5a, 0x60, 0x05, 0x5d, - 0x1f, 0xbd, 0x66, 0xc0, 0x74, 0xcb, 0xb5, 0x1a, 0x15, 0xab, 0x65, 0x39, 0x75, 0xe2, 0x2d, 0x1a, - 0x0f, 0x19, 0x8f, 0x4e, 0x3d, 0xb6, 0xbe, 0x3c, 0xca, 0x78, 0x2d, 0x97, 0x6f, 0xf9, 0x98, 0xf8, - 0x6e, 0xd7, 0xab, 0x13, 0x4c, 0x76, 0x2a, 0x0b, 0xdf, 0xe9, 0x95, 0xee, 0x3b, 0xe8, 0x95, 0xa6, - 0xd7, 0x35, 0x49, 0x38, 0x22, 0x17, 0x7d, 0xd1, 0x80, 0xf9, 0xba, 0xe5, 0x58, 0xde, 0xfe, 0x96, - 0xe5, 0x35, 0x49, 0xf0, 0xac, 0xe7, 0x76, 0x3b, 0x8b, 0xb9, 0x63, 0xa8, 0xcd, 0xfd, 0xa2, 0x36, - 0xf3, 0x2b, 0x71, 0x71, 0x38, 0x59, 0x03, 0x56, 0x2f, 0x3f, 0xb0, 0xb6, 0x5b, 0x44, 0xaf, 0x57, - 0xfe, 0x38, 0xeb, 0x55, 0x8b, 0x8b, 0xc3, 0xc9, 0x1a, 0x98, 0xaf, 0xe6, 0x61, 0xbe, 0xbc, 0x5e, - 0xd9, 0xf2, 0xac, 0x9d, 0x1d, 0xbb, 0x8e, 0xdd, 0x6e, 0x60, 0x3b, 0x4d, 0xf4, 0x76, 0x98, 0xb0, - 0x9d, 0xa6, 0x47, 0x7c, 0x9f, 0x0d, 0xe4, 0x64, 0x65, 0x56, 0x30, 0x9d, 0x58, 0xe3, 0x60, 0x2c, - 0xf1, 0xe8, 0x49, 0x98, 0xf2, 0x89, 0xb7, 0x67, 0xd7, 0x49, 0xd5, 0xf5, 0x02, 0xd6, 0xd3, 0x85, - 0xca, 0x49, 0x41, 0x3e, 0x55, 0x0b, 0x51, 0x58, 0xa7, 0xa3, 0xc5, 0x3c, 0xd7, 0x0d, 0x04, 0x9e, - 0x75, 0xc4, 0x64, 0x58, 0x0c, 0x87, 0x28, 0xac, 0xd3, 0xa1, 0xd7, 0x0d, 0x98, 0xf3, 0x03, 0xbb, - 0x7e, 0xd3, 0x76, 0x88, 0xef, 0xaf, 0xb8, 0xce, 0x8e, 0xdd, 0x5c, 0x2c, 0xb0, 0x5e, 0xdc, 0x1c, - 0xad, 0x17, 0x6b, 0x31, 0xae, 0x95, 0x85, 0x83, 0x5e, 0x69, 0x2e, 0x0e, 0xc5, 0x09, 0xe9, 0x68, - 0x15, 0xe6, 0x2c, 0xc7, 0x71, 0x03, 0x2b, 0xb0, 0x5d, 0xa7, 0xea, 0x91, 0x1d, 0xfb, 0xf6, 0xe2, - 0x18, 0x6b, 0xce, 0xa2, 0x68, 0xce, 0x5c, 0x39, 0x86, 0xc7, 0x89, 0x12, 0xe6, 0x2a, 0x2c, 0x96, - 0xdb, 0xdb, 0x96, 0xef, 0x5b, 0x0d, 0xd7, 0x8b, 0x8d, 0xc6, 0xa3, 0x50, 0x6c, 0x5b, 0x9d, 0x8e, - 0xed, 0x34, 0xe9, 0x70, 0xe4, 0x1f, 0x9d, 0xac, 0x4c, 0x1f, 0xf4, 0x4a, 0xc5, 0x0d, 0x01, 0xc3, - 0x0a, 0x6b, 0xfe, 0x20, 0x07, 0x53, 0x65, 0xc7, 0x6a, 0xed, 0xfb, 0xb6, 0x8f, 0xbb, 0x0e, 0xfa, - 0x08, 0x14, 0xe9, 0xee, 0xd2, 0xb0, 0x02, 0x4b, 0xac, 0xc8, 0x77, 0x2d, 0xf3, 0xc5, 0xbe, 0xac, - 0x2f, 0xf6, 0xb0, 0x5f, 0x28, 0xf5, 0xf2, 0xde, 0xbb, 0x97, 0x2f, 0x6f, 0xdf, 0x20, 0xf5, 0x60, - 0x83, 0x04, 0x56, 0x05, 0x89, 0x56, 0x40, 0x08, 0xc3, 0x8a, 0x2b, 0x72, 0x61, 0xcc, 0xef, 0x90, - 0xba, 0x58, 0x61, 0x1b, 0x23, 0xce, 0xe4, 0xb0, 0xea, 0xb5, 0x0e, 0xa9, 0x57, 0xa6, 0x85, 0xe8, - 0x31, 0xfa, 0x0f, 0x33, 0x41, 0xe8, 0x16, 0x8c, 0xfb, 0x6c, 0xcf, 0x11, 0x8b, 0xe7, 0x72, 0x76, - 0x22, 0x19, 0xdb, 0xca, 0x8c, 0x10, 0x3a, 0xce, 0xff, 0x63, 0x21, 0xce, 0xfc, 0x7b, 0x03, 0x4e, - 0x6a, 0xd4, 0x65, 0xaf, 0xd9, 0x6d, 0x13, 0x27, 0x40, 0x0f, 0xc1, 0x98, 0x63, 0xb5, 0x89, 0x58, - 0x28, 0xaa, 0xca, 0x9b, 0x56, 0x9b, 0x60, 0x86, 0x41, 0x0f, 0x43, 0x61, 0xcf, 0x6a, 0x75, 0x09, - 0xeb, 0xa4, 0xc9, 0xca, 0x09, 0x41, 0x52, 0xb8, 0x46, 0x81, 0x98, 0xe3, 0xd0, 0x4b, 0x30, 0xc9, - 0x7e, 0x5c, 0xf0, 0xdc, 0x76, 0x46, 0x4d, 0x13, 0x35, 0xbc, 0x26, 0xd9, 0x56, 0x4e, 0x1c, 0xf4, - 0x4a, 0x93, 0xea, 0x2f, 0x0e, 0x05, 0x9a, 0xff, 0x60, 0xc0, 0xac, 0xd6, 0xb8, 0x75, 0xdb, 0x0f, - 0xd0, 0x87, 0x12, 0x93, 0x67, 0x79, 0xb0, 0xc9, 0x43, 0x4b, 0xb3, 0xa9, 0x33, 0x27, 0x5a, 0x5a, - 0x94, 0x10, 0x6d, 0xe2, 0x38, 0x50, 0xb0, 0x03, 0xd2, 0xf6, 0x17, 0x73, 0x0f, 0xe5, 0x1f, 0x9d, - 0x7a, 0x6c, 0x2d, 0xb3, 0x61, 0x0c, 0xfb, 0x77, 0x8d, 0xf2, 0xc7, 0x5c, 0x8c, 0xf9, 0xf5, 0xb1, - 0x48, 0x0b, 0xe9, 0x8c, 0x42, 0x2e, 0x4c, 0xb4, 0x49, 0xe0, 0xd9, 0x75, 0xbe, 0xae, 0xa6, 0x1e, - 0x5b, 0x1d, 0xad, 0x16, 0x1b, 0x8c, 0x59, 0xb8, 0x59, 0xf2, 0xff, 0x3e, 0x96, 0x52, 0xd0, 0x2e, - 0x8c, 0x59, 0x5e, 0x53, 0xb6, 0xf9, 0x42, 0x36, 0xe3, 0x1b, 0xce, 0xb9, 0xb2, 0xd7, 0xf4, 0x31, - 0x93, 0x80, 0xce, 0xc1, 0x64, 0x40, 0xbc, 0xb6, 0xed, 0x58, 0x01, 0xdf, 0x5d, 0x8b, 0x95, 0x79, - 0x41, 0x36, 0xb9, 0x25, 0x11, 0x38, 0xa4, 0x41, 0x2d, 0x18, 0x6f, 0x78, 0xfb, 0xb8, 0xeb, 0x2c, - 0x8e, 0x65, 0xd1, 0x15, 0xab, 0x8c, 0x57, 0xb8, 0x98, 0xf8, 0x7f, 0x2c, 0x64, 0xa0, 0xaf, 0x18, - 0xb0, 0xd0, 0x26, 0x96, 0xdf, 0xf5, 0x08, 0x6d, 0x02, 0x26, 0x01, 0x71, 0xe8, 0x6e, 0xb8, 0x58, - 0x60, 0xc2, 0xf1, 0xa8, 0xe3, 0x90, 0xe4, 0x5c, 0x79, 0x50, 0x54, 0x65, 0x21, 0x0d, 0x8b, 0x53, - 0x6b, 0x63, 0xfe, 0x60, 0x0c, 0xe6, 0x13, 0x3b, 0x04, 0x7a, 0x02, 0x0a, 0x9d, 0x5d, 0xcb, 0x97, - 0x4b, 0xfe, 0xac, 0x9c, 0x6f, 0x55, 0x0a, 0xbc, 0xd3, 0x2b, 0x9d, 0x90, 0x45, 0x18, 0x00, 0x73, - 0x62, 0xaa, 0x53, 0xdb, 0xc4, 0xf7, 0xad, 0xa6, 0xdc, 0x07, 0xb4, 0x69, 0xc2, 0xc0, 0x58, 0xe2, - 0xd1, 0x2f, 0x1b, 0x70, 0x82, 0x4f, 0x19, 0x4c, 0xfc, 0x6e, 0x2b, 0xa0, 0x7b, 0x1d, 0xed, 0x96, - 0x4b, 0x59, 0x4c, 0x4f, 0xce, 0xb2, 0x72, 0x4a, 0x48, 0x3f, 0xa1, 0x43, 0x7d, 0x1c, 0x95, 0x8b, - 0xae, 0xc3, 0xa4, 0x1f, 0x58, 0x5e, 0x40, 0x1a, 0xe5, 0x80, 0x69, 0xb5, 0xa9, 0xc7, 0xfe, 0xef, - 0x60, 0x9b, 0xc0, 0x96, 0xdd, 0x26, 0x7c, 0xc3, 0xa9, 0x49, 0x06, 0x38, 0xe4, 0x85, 0x5e, 0x02, - 0xf0, 0xba, 0x4e, 0xad, 0xdb, 0x6e, 0x5b, 0xde, 0xbe, 0xd0, 0xe0, 0x17, 0x47, 0x6b, 0x1e, 0x56, - 0xfc, 0x42, 0x9d, 0x15, 0xc2, 0xb0, 0x26, 0x0f, 0xbd, 0x62, 0xc0, 0x09, 0x3e, 0x13, 0x65, 0x0d, - 0xc6, 0x33, 0xae, 0xc1, 0x3c, 0xed, 0xda, 0x55, 0x5d, 0x04, 0x8e, 0x4a, 0x34, 0xff, 0x36, 0xaa, - 0x4f, 0x6a, 0x01, 0xb5, 0xae, 0x9b, 0xfb, 0xe8, 0x83, 0x70, 0xbf, 0xdf, 0xad, 0xd7, 0x89, 0xef, - 0xef, 0x74, 0x5b, 0xb8, 0xeb, 0x5c, 0xb4, 0xfd, 0xc0, 0xf5, 0xf6, 0xd7, 0xed, 0xb6, 0x1d, 0xb0, - 0x19, 0x57, 0xa8, 0x9c, 0x39, 0xe8, 0x95, 0xee, 0xaf, 0xf5, 0x23, 0xc2, 0xfd, 0xcb, 0x23, 0x0b, - 0x1e, 0xe8, 0x3a, 0xfd, 0xd9, 0x73, 0xeb, 0xad, 0x74, 0xd0, 0x2b, 0x3d, 0x70, 0xb5, 0x3f, 0x19, - 0x3e, 0x8c, 0x87, 0xf9, 0x2f, 0x06, 0xcc, 0xc9, 0x76, 0x6d, 0x91, 0x76, 0xa7, 0x45, 0x77, 0x97, - 0xe3, 0x37, 0x44, 0x82, 0x88, 0x21, 0x82, 0xb3, 0x51, 0x27, 0xb2, 0xfe, 0xfd, 0xac, 0x11, 0xf3, - 0x9f, 0x0d, 0x58, 0x88, 0x13, 0xdf, 0x03, 0xe5, 0xe9, 0x47, 0x95, 0xe7, 0x66, 0xb6, 0xad, 0xed, - 0xa3, 0x41, 0x5f, 0x1b, 0x4b, 0xb6, 0xf5, 0x7f, 0xba, 0x1a, 0x0d, 0xb5, 0x62, 0xfe, 0xa7, 0xa9, - 0x15, 0xc7, 0xde, 0x54, 0x5a, 0xf1, 0x77, 0xc7, 0x60, 0xba, 0xec, 0x04, 0x76, 0x79, 0x67, 0xc7, - 0x76, 0xec, 0x60, 0x1f, 0x7d, 0x26, 0x07, 0xe7, 0x3a, 0x1e, 0xd9, 0x21, 0x9e, 0x47, 0x1a, 0xab, - 0x5d, 0xcf, 0x76, 0x9a, 0xb5, 0xfa, 0x2e, 0x69, 0x74, 0x5b, 0xb6, 0xd3, 0x5c, 0x6b, 0x3a, 0xae, - 0x02, 0x9f, 0xbf, 0x4d, 0xea, 0x5d, 0xd6, 0x24, 0xbe, 0x28, 0xda, 0xa3, 0x35, 0xa9, 0x3a, 0x9c, - 0xd0, 0xca, 0xe3, 0x07, 0xbd, 0xd2, 0xb9, 0x21, 0x0b, 0xe1, 0x61, 0x9b, 0x86, 0x3e, 0x95, 0x83, - 0x65, 0x8f, 0x7c, 0xb4, 0x6b, 0x0f, 0xde, 0x1b, 0x7c, 0xd7, 0x6a, 0x8d, 0xa8, 0x7e, 0x86, 0x92, - 0x59, 0x79, 0xec, 0xa0, 0x57, 0x1a, 0xb2, 0x0c, 0x1e, 0xb2, 0x5d, 0xe6, 0x37, 0x73, 0x70, 0xaa, - 0xdc, 0xe9, 0x6c, 0x10, 0x7f, 0x37, 0x76, 0xa8, 0xfd, 0x9c, 0x01, 0x33, 0x7b, 0xb6, 0x17, 0x74, - 0xad, 0x96, 0x74, 0x02, 0xf0, 0x29, 0x51, 0x1b, 0x71, 0x39, 0x73, 0x69, 0xd7, 0x22, 0xac, 0x2b, - 0xe8, 0xa0, 0x57, 0x9a, 0x89, 0xc2, 0x70, 0x4c, 0x3c, 0xfa, 0x75, 0x03, 0xe6, 0x04, 0x68, 0xd3, - 0x6d, 0x10, 0xdd, 0x73, 0x74, 0x35, 0xcb, 0x3a, 0x29, 0xe6, 0xdc, 0xc5, 0x10, 0x87, 0xe2, 0x44, - 0x25, 0xcc, 0x7f, 0xcb, 0xc1, 0xe9, 0x3e, 0x3c, 0xd0, 0xef, 0x18, 0xb0, 0xc0, 0xdd, 0x4d, 0x1a, - 0x0a, 0x93, 0x1d, 0xd1, 0x9b, 0x1f, 0xc8, 0xba, 0xe6, 0x98, 0xae, 0x05, 0xe2, 0xd4, 0x49, 0x65, - 0x91, 0x6e, 0x1b, 0x2b, 0x29, 0xa2, 0x71, 0x6a, 0x85, 0x58, 0x4d, 0xb9, 0x03, 0x2a, 0x56, 0xd3, - 0xdc, 0x3d, 0xa9, 0x69, 0x2d, 0x45, 0x34, 0x4e, 0xad, 0x90, 0xf9, 0xf3, 0xf0, 0xc0, 0x21, 0xec, - 0xee, 0x7e, 0xe2, 0x37, 0x5f, 0x50, 0xb3, 0x3e, 0x3a, 0xe7, 0x06, 0x70, 0x16, 0x98, 0x30, 0xee, - 0xb9, 0xdd, 0x80, 0x70, 0xed, 0x36, 0x59, 0x01, 0xaa, 0x27, 0x30, 0x83, 0x60, 0x81, 0x31, 0xbf, - 0x69, 0x40, 0x71, 0x08, 0xff, 0x43, 0x29, 0xea, 0x7f, 0x98, 0x4c, 0xf8, 0x1e, 0x82, 0xa4, 0xef, - 0xe1, 0xd9, 0xd1, 0x46, 0x63, 0x10, 0x9f, 0xc3, 0xbf, 0x1b, 0x30, 0x9f, 0xf0, 0x51, 0xa0, 0x5d, - 0x58, 0xe8, 0xb8, 0x0d, 0x69, 0x5f, 0x5c, 0xb4, 0xfc, 0x5d, 0x86, 0x13, 0xcd, 0x7b, 0x82, 0x8e, - 0x64, 0x35, 0x05, 0x7f, 0xa7, 0x57, 0x5a, 0x54, 0x4c, 0x62, 0x04, 0x38, 0x95, 0x23, 0xea, 0x40, - 0x71, 0xc7, 0x26, 0xad, 0x46, 0x38, 0x05, 0x47, 0xb4, 0x24, 0x2e, 0x08, 0x6e, 0xdc, 0x3d, 0x27, - 0xff, 0x61, 0x25, 0xc5, 0xbc, 0x02, 0x33, 0x51, 0x67, 0xed, 0x00, 0x83, 0x77, 0x06, 0xf2, 0x96, - 0xe7, 0x88, 0xa1, 0x9b, 0x12, 0x04, 0xf9, 0x32, 0xde, 0xc4, 0x14, 0x6e, 0xfe, 0x64, 0x0c, 0x66, - 0x2b, 0xad, 0x2e, 0x79, 0xd6, 0x23, 0x44, 0x9e, 0x4f, 0xcb, 0x30, 0xdb, 0xf1, 0xc8, 0x9e, 0x4d, - 0x6e, 0xd5, 0x48, 0x8b, 0xd4, 0x03, 0xd7, 0x13, 0xfc, 0x4f, 0x8b, 0xe2, 0xb3, 0xd5, 0x28, 0x1a, - 0xc7, 0xe9, 0xd1, 0x33, 0x30, 0x63, 0xd5, 0x03, 0x7b, 0x8f, 0x28, 0x0e, 0xbc, 0x02, 0x6f, 0x11, - 0x1c, 0x66, 0xca, 0x11, 0x2c, 0x8e, 0x51, 0xa3, 0x0f, 0xc1, 0xa2, 0x5f, 0xb7, 0x5a, 0xe4, 0x6a, - 0x47, 0x88, 0x5a, 0xd9, 0x25, 0xf5, 0x9b, 0x55, 0xd7, 0x76, 0x02, 0xe1, 0x8d, 0x78, 0x48, 0x70, - 0x5a, 0xac, 0xf5, 0xa1, 0xc3, 0x7d, 0x39, 0xa0, 0x3f, 0x32, 0xe0, 0x4c, 0xc7, 0x23, 0x55, 0xcf, - 0x6d, 0xbb, 0x54, 0xcd, 0x24, 0x8e, 0xe8, 0xe2, 0xa8, 0x7a, 0x6d, 0x44, 0x7d, 0xca, 0x21, 0x49, - 0x17, 0xe1, 0x5b, 0x0f, 0x7a, 0xa5, 0x33, 0xd5, 0xc3, 0x2a, 0x80, 0x0f, 0xaf, 0x1f, 0xfa, 0x13, - 0x03, 0xce, 0x76, 0x5c, 0x3f, 0x38, 0xa4, 0x09, 0x85, 0x63, 0x6d, 0x82, 0x79, 0xd0, 0x2b, 0x9d, - 0xad, 0x1e, 0x5a, 0x03, 0x7c, 0x97, 0x1a, 0x9a, 0x07, 0x53, 0x30, 0xaf, 0xcd, 0x3d, 0x71, 0x7e, - 0x7d, 0x1a, 0x4e, 0xc8, 0xc9, 0x10, 0xaa, 0xf5, 0xc9, 0xd0, 0xdf, 0x50, 0xd6, 0x91, 0x38, 0x4a, - 0x4b, 0xe7, 0x9d, 0x9a, 0x8a, 0xbc, 0x74, 0x6c, 0xde, 0x55, 0x23, 0x58, 0x1c, 0xa3, 0x46, 0x6b, - 0x70, 0x52, 0x40, 0x30, 0xe9, 0xb4, 0xec, 0xba, 0xb5, 0xe2, 0x76, 0xc5, 0x94, 0x2b, 0x54, 0x4e, - 0x1f, 0xf4, 0x4a, 0x27, 0xab, 0x49, 0x34, 0x4e, 0x2b, 0x83, 0xd6, 0x61, 0xc1, 0xea, 0x06, 0xae, - 0x6a, 0xff, 0x79, 0x87, 0x6a, 0x8a, 0x06, 0x9b, 0x5a, 0x45, 0xae, 0x52, 0xca, 0x29, 0x78, 0x9c, - 0x5a, 0x0a, 0x55, 0x63, 0xdc, 0x6a, 0xa4, 0xee, 0x3a, 0x0d, 0x3e, 0xca, 0x85, 0xd0, 0x0a, 0x2f, - 0xa7, 0xd0, 0xe0, 0xd4, 0x92, 0xa8, 0x05, 0x33, 0x6d, 0xeb, 0xf6, 0x55, 0xc7, 0xda, 0xb3, 0xec, - 0x16, 0x15, 0x22, 0x7c, 0x18, 0xfd, 0x0f, 0xd6, 0xdd, 0xc0, 0x6e, 0x2d, 0xf3, 0xeb, 0xbc, 0xe5, - 0x35, 0x27, 0xb8, 0xec, 0xd5, 0x02, 0x6a, 0xad, 0x71, 0xe3, 0x68, 0x23, 0xc2, 0x0b, 0xc7, 0x78, - 0xa3, 0xcb, 0x70, 0x8a, 0x2d, 0xc7, 0x55, 0xf7, 0x96, 0xb3, 0x4a, 0x5a, 0xd6, 0xbe, 0x6c, 0xc0, - 0x04, 0x6b, 0xc0, 0xfd, 0x07, 0xbd, 0xd2, 0xa9, 0x5a, 0x1a, 0x01, 0x4e, 0x2f, 0x87, 0x2c, 0x78, - 0x20, 0x8a, 0xc0, 0x64, 0xcf, 0xf6, 0x6d, 0xd7, 0xe1, 0x9e, 0x88, 0x62, 0xe8, 0x89, 0xa8, 0xf5, - 0x27, 0xc3, 0x87, 0xf1, 0x40, 0xbf, 0x69, 0xc0, 0x42, 0xda, 0x32, 0x5c, 0x9c, 0xcc, 0xe2, 0xb2, - 0x22, 0xb6, 0xb4, 0xf8, 0x8c, 0x48, 0xdd, 0x14, 0x52, 0x2b, 0x81, 0x5e, 0x36, 0x60, 0xda, 0xd2, - 0x4e, 0x51, 0x8b, 0xc0, 0x6a, 0x75, 0x69, 0xd4, 0xb3, 0x7c, 0xc8, 0xb1, 0x32, 0x77, 0xd0, 0x2b, - 0x45, 0x4e, 0x6a, 0x38, 0x22, 0x11, 0xfd, 0x96, 0x01, 0xa7, 0x52, 0xd7, 0xf8, 0xe2, 0xd4, 0x71, - 0xf4, 0x10, 0x9b, 0x24, 0xe9, 0x7b, 0x4e, 0x7a, 0x35, 0xd0, 0xeb, 0x86, 0x52, 0x65, 0x1b, 0xd2, - 0x9b, 0x32, 0xcd, 0xaa, 0x76, 0x65, 0xc4, 0x83, 0x63, 0x68, 0x10, 0x48, 0xc6, 0x95, 0x93, 0x9a, - 0x66, 0x94, 0x40, 0x1c, 0x17, 0x8f, 0x3e, 0x6b, 0x48, 0xd5, 0xa8, 0x6a, 0x74, 0xe2, 0xb8, 0x6a, - 0x84, 0x42, 0x4d, 0xab, 0x2a, 0x14, 0x13, 0x8e, 0x3e, 0x0c, 0x4b, 0xd6, 0xb6, 0xeb, 0x05, 0xa9, - 0x8b, 0x6f, 0x71, 0x86, 0x2d, 0xa3, 0xb3, 0x07, 0xbd, 0xd2, 0x52, 0xb9, 0x2f, 0x15, 0x3e, 0x84, - 0x83, 0xf9, 0xb5, 0x02, 0x4c, 0x73, 0x23, 0x5f, 0xa8, 0xae, 0x6f, 0x18, 0xf0, 0x60, 0xbd, 0xeb, - 0x79, 0xc4, 0x09, 0x6a, 0x01, 0xe9, 0x24, 0x15, 0x97, 0x71, 0xac, 0x8a, 0xeb, 0xa1, 0x83, 0x5e, - 0xe9, 0xc1, 0x95, 0x43, 0xe4, 0xe3, 0x43, 0x6b, 0x87, 0xfe, 0xd2, 0x00, 0x53, 0x10, 0x54, 0xac, - 0xfa, 0xcd, 0xa6, 0xe7, 0x76, 0x9d, 0x46, 0xb2, 0x11, 0xb9, 0x63, 0x6d, 0xc4, 0x23, 0x07, 0xbd, - 0x92, 0xb9, 0x72, 0xd7, 0x5a, 0xe0, 0x01, 0x6a, 0x8a, 0x9e, 0x85, 0x79, 0x41, 0x75, 0xfe, 0x76, - 0x87, 0x78, 0x36, 0x35, 0xa7, 0xc5, 0x7d, 0x7a, 0x18, 0xa2, 0x10, 0x27, 0xc0, 0xc9, 0x32, 0xc8, - 0x87, 0x89, 0x5b, 0xc4, 0x6e, 0xee, 0x06, 0xd2, 0x7c, 0x1a, 0x31, 0x2e, 0x41, 0x1c, 0xf8, 0xaf, - 0x73, 0x9e, 0x95, 0xa9, 0x83, 0x5e, 0x69, 0x42, 0xfc, 0xc1, 0x52, 0x12, 0xda, 0x84, 0x19, 0x7e, - 0x04, 0xab, 0xda, 0x4e, 0xb3, 0xea, 0x3a, 0xfc, 0x36, 0x7f, 0xb2, 0xf2, 0x88, 0x54, 0xf8, 0xb5, - 0x08, 0xf6, 0x4e, 0xaf, 0x34, 0x2d, 0x7f, 0x6f, 0xed, 0x77, 0x08, 0x8e, 0x95, 0x36, 0xbf, 0x59, - 0x00, 0x90, 0xd3, 0x95, 0x74, 0xd0, 0xff, 0x83, 0x49, 0x9f, 0x04, 0x5c, 0xaa, 0x70, 0x9e, 0xf3, - 0x3b, 0x09, 0x09, 0xc4, 0x21, 0x1e, 0xdd, 0x84, 0x42, 0xc7, 0xea, 0xfa, 0x44, 0x0c, 0xfe, 0xa5, - 0x4c, 0x06, 0xbf, 0x4a, 0x39, 0xf2, 0x33, 0x17, 0xfb, 0x89, 0xb9, 0x0c, 0xf4, 0x49, 0x03, 0x80, - 0x44, 0x07, 0x6c, 0x64, 0xdf, 0x87, 0x10, 0x19, 0x8e, 0x29, 0xed, 0x83, 0xca, 0xcc, 0x41, 0xaf, - 0x04, 0xda, 0xd0, 0x6b, 0x62, 0xd1, 0x2d, 0x28, 0x5a, 0x72, 0xcf, 0x1f, 0x3b, 0x8e, 0x3d, 0x9f, - 0x1d, 0x85, 0xd4, 0xa4, 0x55, 0xc2, 0xd0, 0xa7, 0x0c, 0x98, 0xf1, 0x49, 0x20, 0x86, 0x8a, 0xee, - 0x3c, 0xc2, 0xe0, 0x1d, 0x71, 0xd2, 0xd5, 0x22, 0x3c, 0xf9, 0x0e, 0x1a, 0x85, 0xe1, 0x98, 0x5c, - 0x1e, 0x53, 0x42, 0x82, 0x8b, 0xc4, 0x6a, 0x10, 0x4f, 0xb8, 0xa7, 0x84, 0x2d, 0xb5, 0x39, 0x72, - 0x65, 0x22, 0x5c, 0x45, 0x4c, 0x49, 0x0c, 0x8a, 0x13, 0xd2, 0xcd, 0xbf, 0x9e, 0x86, 0x19, 0x39, - 0x8b, 0x43, 0xb3, 0x9a, 0x7b, 0x55, 0xfa, 0x98, 0xd5, 0x2b, 0x3a, 0x12, 0x47, 0x69, 0x69, 0x61, - 0xbe, 0x4e, 0xa2, 0x56, 0xb5, 0x2a, 0x5c, 0xd3, 0x91, 0x38, 0x4a, 0x8b, 0xda, 0x50, 0xf0, 0x03, - 0xd2, 0x91, 0x97, 0x90, 0x23, 0xde, 0x91, 0x85, 0x8b, 0x33, 0xbc, 0x66, 0xa0, 0xff, 0x7c, 0xcc, - 0xa5, 0x30, 0xc7, 0x60, 0x10, 0xf1, 0x15, 0x8a, 0x99, 0x99, 0xcd, 0xe2, 0x88, 0xba, 0x21, 0xf9, - 0x04, 0x89, 0xc2, 0x70, 0x4c, 0x7c, 0x8a, 0xa5, 0x5d, 0x38, 0x46, 0x4b, 0xfb, 0x79, 0x28, 0xb6, - 0xad, 0xdb, 0xb5, 0xae, 0xd7, 0x3c, 0xba, 0x45, 0x2f, 0xe2, 0x83, 0x38, 0x17, 0xac, 0xf8, 0xa1, - 0x57, 0x0c, 0x6d, 0xbd, 0x4f, 0x30, 0xe6, 0xd7, 0xb3, 0x5d, 0xef, 0x4a, 0x51, 0xf5, 0x5d, 0xf9, - 0x09, 0xbb, 0xb7, 0x78, 0xcf, 0xed, 0x5e, 0x6a, 0xc3, 0xf1, 0x05, 0xa2, 0x6c, 0xb8, 0xc9, 0x63, - 0xb5, 0xe1, 0x56, 0x22, 0xc2, 0x70, 0x4c, 0x38, 0xab, 0x0f, 0x5f, 0x73, 0xaa, 0x3e, 0x70, 0xac, - 0xf5, 0xa9, 0x45, 0x84, 0xe1, 0x98, 0xf0, 0xfe, 0x87, 0xbd, 0xa9, 0xe3, 0x39, 0xec, 0x4d, 0x67, - 0x70, 0xd8, 0x3b, 0xdc, 0x0e, 0x3e, 0x31, 0xaa, 0x1d, 0x8c, 0x2e, 0x01, 0x6a, 0xec, 0x3b, 0x56, - 0xdb, 0xae, 0x8b, 0xcd, 0x92, 0xe9, 0xac, 0x19, 0xe6, 0x0c, 0x58, 0x12, 0x1b, 0x19, 0x5a, 0x4d, - 0x50, 0xe0, 0x94, 0x52, 0x28, 0x80, 0x62, 0x47, 0x9a, 0x3b, 0xb3, 0x59, 0xcc, 0x7e, 0x69, 0xfe, - 0xf0, 0x7b, 0x6a, 0xba, 0xf0, 0x24, 0x04, 0x2b, 0x49, 0xe6, 0x7f, 0x1a, 0x30, 0xb7, 0xd2, 0x72, - 0xbb, 0x8d, 0xeb, 0x56, 0x50, 0xdf, 0xe5, 0x97, 0xaa, 0xe8, 0x19, 0x28, 0xda, 0x4e, 0x40, 0xbc, - 0x3d, 0xab, 0x25, 0x34, 0x8a, 0x29, 0xef, 0x9d, 0xd7, 0x04, 0xfc, 0x4e, 0xaf, 0x34, 0xb3, 0xda, - 0xf5, 0x58, 0xb4, 0x22, 0xdf, 0x5f, 0xb0, 0x2a, 0x83, 0xbe, 0x6c, 0xc0, 0x3c, 0xbf, 0x96, 0x5d, - 0xb5, 0x02, 0xeb, 0x4a, 0x97, 0x78, 0x36, 0x91, 0x17, 0xb3, 0x23, 0x6e, 0x2d, 0xf1, 0xba, 0x4a, - 0x01, 0xfb, 0xa1, 0x5d, 0xbb, 0x11, 0x97, 0x8c, 0x93, 0x95, 0x31, 0x3f, 0x9f, 0x87, 0xfb, 0xfb, - 0xf2, 0x42, 0x4b, 0x90, 0xb3, 0x1b, 0xa2, 0xe9, 0x20, 0xf8, 0xe6, 0xd6, 0x1a, 0x38, 0x67, 0x37, - 0xd0, 0x32, 0x33, 0xd1, 0x3c, 0xe2, 0xfb, 0xf2, 0x8e, 0x6e, 0x52, 0x59, 0x53, 0x02, 0x8a, 0x35, - 0x0a, 0x54, 0x82, 0x42, 0xcb, 0xda, 0x26, 0x2d, 0x61, 0x7e, 0x33, 0xa3, 0x6f, 0x9d, 0x02, 0x30, - 0x87, 0xa3, 0x5f, 0x32, 0x00, 0x78, 0x05, 0xa9, 0xf1, 0x2e, 0xf4, 0x1a, 0xce, 0xb6, 0x9b, 0x28, - 0x67, 0x5e, 0xcb, 0xf0, 0x3f, 0xd6, 0xa4, 0xa2, 0x2d, 0x18, 0xa7, 0xf6, 0x9f, 0xdb, 0x38, 0xb2, - 0x1a, 0x63, 0x77, 0x12, 0x55, 0xc6, 0x03, 0x0b, 0x5e, 0xb4, 0xaf, 0x3c, 0x12, 0x74, 0x3d, 0x87, - 0x76, 0x2d, 0x53, 0x5c, 0x45, 0x5e, 0x0b, 0xac, 0xa0, 0x58, 0xa3, 0x30, 0xff, 0x30, 0x07, 0x0b, - 0x69, 0x55, 0xa7, 0xfa, 0x61, 0x9c, 0xd7, 0x56, 0x9c, 0x24, 0xdf, 0x9f, 0x7d, 0xff, 0x88, 0x08, - 0x03, 0x75, 0x0f, 0x2f, 0x62, 0xa0, 0x84, 0x5c, 0xf4, 0x7e, 0xd5, 0x43, 0xb9, 0x23, 0xf6, 0x90, - 0xe2, 0x1c, 0xeb, 0xa5, 0x87, 0x60, 0xcc, 0xa7, 0x23, 0x9f, 0x8f, 0xfa, 0xfb, 0xd9, 0x18, 0x31, - 0x0c, 0xa5, 0xe8, 0x3a, 0x76, 0x20, 0x42, 0x88, 0x15, 0xc5, 0x55, 0xc7, 0x0e, 0x30, 0xc3, 0x98, - 0x5f, 0xcc, 0xc1, 0x52, 0xff, 0x46, 0xa1, 0x2f, 0x1a, 0x00, 0x0d, 0x6a, 0xdd, 0xd3, 0x29, 0x29, - 0x23, 0x32, 0xac, 0xe3, 0xea, 0xc3, 0x55, 0x29, 0x29, 0x0c, 0xcf, 0x51, 0x20, 0x1f, 0x6b, 0x15, - 0x41, 0x8f, 0xc9, 0xa9, 0xbf, 0x69, 0xb5, 0xa5, 0x01, 0xaa, 0xca, 0x6c, 0x28, 0x0c, 0xd6, 0xa8, - 0xe8, 0xf1, 0xcd, 0xb1, 0xda, 0xc4, 0xef, 0x58, 0x2a, 0x46, 0x9c, 0x1d, 0xdf, 0x36, 0x25, 0x10, - 0x87, 0x78, 0xb3, 0x05, 0x0f, 0x0f, 0x50, 0xcf, 0x8c, 0xe2, 0x75, 0xcd, 0xff, 0x30, 0xe0, 0xf4, - 0x4a, 0xab, 0xeb, 0x07, 0xc4, 0xfb, 0x5f, 0x13, 0xed, 0xf4, 0x5f, 0x06, 0x3c, 0xd0, 0xa7, 0xcd, - 0xf7, 0x20, 0xe8, 0xe9, 0xc5, 0x68, 0xd0, 0xd3, 0xd5, 0x51, 0xa7, 0x74, 0x6a, 0x3b, 0xfa, 0xc4, - 0x3e, 0x05, 0x70, 0x82, 0xee, 0x5a, 0x0d, 0xb7, 0x99, 0x91, 0xde, 0x7c, 0x18, 0x0a, 0x1f, 0xa5, - 0xfa, 0x27, 0x3e, 0xc7, 0x98, 0x52, 0xc2, 0x1c, 0x67, 0xbe, 0x0f, 0x44, 0x84, 0x50, 0x6c, 0xf1, - 0x18, 0x83, 0x2c, 0x1e, 0xf3, 0xef, 0x72, 0xa0, 0x1d, 0xfb, 0xef, 0xc1, 0xa4, 0x74, 0x22, 0x93, - 0x72, 0xc4, 0x83, 0xbc, 0xe6, 0xc4, 0xe8, 0xf7, 0x14, 0x60, 0x2f, 0xf6, 0x14, 0x60, 0x33, 0x33, - 0x89, 0x87, 0xbf, 0x04, 0xf8, 0x9e, 0x01, 0x0f, 0x84, 0xc4, 0x49, 0x8f, 0xdc, 0xdd, 0x77, 0x98, - 0x27, 0x61, 0xca, 0x0a, 0x8b, 0x89, 0x39, 0xa0, 0x5e, 0xbf, 0x68, 0x1c, 0xb1, 0x4e, 0x17, 0x06, - 0x1e, 0xe7, 0x8f, 0x18, 0x78, 0x3c, 0x76, 0x78, 0xe0, 0xb1, 0xf9, 0xe3, 0x1c, 0x9c, 0x49, 0xb6, - 0x4c, 0xae, 0x8d, 0xc1, 0x2e, 0xac, 0x9f, 0x82, 0xe9, 0x40, 0x14, 0xd0, 0x76, 0x7a, 0xf5, 0x76, - 0x6b, 0x4b, 0xc3, 0xe1, 0x08, 0x25, 0x2d, 0x59, 0xe7, 0xab, 0xb2, 0x56, 0x77, 0x3b, 0x32, 0x6c, - 0x5d, 0x95, 0x5c, 0xd1, 0x70, 0x38, 0x42, 0xa9, 0x02, 0x02, 0xc7, 0x8e, 0x3d, 0x20, 0xb0, 0x06, - 0xa7, 0x64, 0x08, 0xd4, 0x05, 0xd7, 0x5b, 0x71, 0xdb, 0x9d, 0x16, 0x11, 0x81, 0xeb, 0xb4, 0xb2, - 0x67, 0x44, 0x91, 0x53, 0x38, 0x8d, 0x08, 0xa7, 0x97, 0x35, 0xbf, 0x97, 0x87, 0x93, 0x61, 0xb7, - 0xaf, 0xb8, 0x4e, 0xc3, 0x66, 0x81, 0x64, 0x4f, 0xc3, 0x58, 0xb0, 0xdf, 0x91, 0x9d, 0xfd, 0x7f, - 0x64, 0x75, 0xb6, 0xf6, 0x3b, 0x74, 0xb4, 0x4f, 0xa7, 0x14, 0x61, 0x3e, 0x51, 0x56, 0x08, 0xad, - 0xab, 0xd5, 0xc1, 0x47, 0xe0, 0x89, 0xe8, 0x6c, 0xbe, 0xd3, 0x2b, 0xa5, 0x3c, 0x5d, 0x5c, 0x56, - 0x9c, 0xa2, 0x73, 0x1e, 0xdd, 0x80, 0x99, 0x96, 0xe5, 0x07, 0x57, 0x3b, 0x0d, 0x2b, 0x20, 0x5b, - 0x76, 0x9b, 0x88, 0x35, 0x37, 0x4c, 0x34, 0xb8, 0xba, 0xc4, 0x5d, 0x8f, 0x70, 0xc2, 0x31, 0xce, - 0x68, 0x0f, 0x10, 0x85, 0x6c, 0x79, 0x96, 0xe3, 0xf3, 0x56, 0x51, 0x79, 0xc3, 0x47, 0x9f, 0xab, - 0x63, 0xd9, 0x7a, 0x82, 0x1b, 0x4e, 0x91, 0x80, 0x1e, 0x81, 0x71, 0x8f, 0x58, 0xbe, 0x18, 0xcc, - 0xc9, 0x70, 0xfd, 0x63, 0x06, 0xc5, 0x02, 0xab, 0x2f, 0xa8, 0xf1, 0xbb, 0x2c, 0xa8, 0x1f, 0x1a, - 0x30, 0x13, 0x0e, 0xd3, 0x3d, 0x50, 0x92, 0xed, 0xa8, 0x92, 0xbc, 0x98, 0xd5, 0x96, 0xd8, 0x47, - 0x2f, 0xfe, 0xe9, 0xb8, 0xde, 0x3e, 0x16, 0x0d, 0xfc, 0x31, 0x98, 0x94, 0xab, 0x5a, 0x5a, 0x9f, - 0x23, 0x9e, 0x6e, 0x23, 0x76, 0x89, 0xf6, 0x8a, 0x45, 0x08, 0xc1, 0xa1, 0x3c, 0xaa, 0x96, 0x1b, - 0x42, 0xe5, 0x8a, 0x69, 0xaf, 0xd4, 0xb2, 0x54, 0xc5, 0x69, 0x6a, 0x59, 0x96, 0x41, 0x57, 0xe1, - 0x74, 0xc7, 0x73, 0xd9, 0xcb, 0xc6, 0x55, 0x62, 0x35, 0x5a, 0xb6, 0x43, 0xa4, 0x0b, 0x81, 0xc7, - 0x10, 0x3c, 0x70, 0xd0, 0x2b, 0x9d, 0xae, 0xa6, 0x93, 0xe0, 0x7e, 0x65, 0xa3, 0xaf, 0x71, 0xc6, - 0x06, 0x78, 0x8d, 0xf3, 0x69, 0xe5, 0xa8, 0x23, 0xbe, 0x78, 0x13, 0xf3, 0xc1, 0xac, 0x86, 0x32, - 0x65, 0x5b, 0x0f, 0xa7, 0x54, 0x59, 0x08, 0xc5, 0x4a, 0x7c, 0x7f, 0x6f, 0xd0, 0xf8, 0x11, 0xbd, - 0x41, 0x61, 0x50, 0xf5, 0xc4, 0x4f, 0x33, 0xa8, 0xba, 0xf8, 0xa6, 0x0a, 0xaa, 0x7e, 0xb5, 0x00, - 0x73, 0x71, 0x0b, 0xe4, 0xf8, 0x5f, 0x1a, 0xfd, 0x9a, 0x01, 0x73, 0x72, 0xf5, 0x70, 0x99, 0x44, - 0xfa, 0xf9, 0xd7, 0x33, 0x5a, 0xb4, 0xdc, 0x96, 0x52, 0x6f, 0x61, 0xb7, 0x62, 0xd2, 0x70, 0x42, - 0x3e, 0x7a, 0x01, 0xa6, 0x94, 0x3b, 0xfc, 0x48, 0xcf, 0x8e, 0x66, 0x99, 0x15, 0x15, 0xb2, 0xc0, - 0x3a, 0x3f, 0xf4, 0xaa, 0x01, 0x50, 0x97, 0x6a, 0x4e, 0xae, 0xae, 0x2b, 0x59, 0xad, 0x2e, 0xa5, - 0x40, 0x43, 0x63, 0x59, 0x81, 0x7c, 0xac, 0x09, 0x46, 0x9f, 0x67, 0x8e, 0x70, 0x65, 0xdd, 0xd1, - 0xf5, 0x94, 0x1f, 0x3d, 0x0e, 0xf6, 0x10, 0xc3, 0x34, 0x34, 0xa5, 0x34, 0x94, 0x8f, 0x23, 0x95, - 0x30, 0x9f, 0x06, 0x15, 0xb9, 0x48, 0xb7, 0x2d, 0x16, 0xbb, 0x58, 0xb5, 0x82, 0x5d, 0x31, 0x05, - 0xd5, 0xb6, 0x75, 0x41, 0x22, 0x70, 0x48, 0x63, 0x7e, 0x04, 0x66, 0x9e, 0xf5, 0xac, 0xce, 0xae, - 0xcd, 0x1c, 0xce, 0xf4, 0x9c, 0xf4, 0x76, 0x98, 0xb0, 0x1a, 0x8d, 0xb4, 0x97, 0xe4, 0x65, 0x0e, - 0xc6, 0x12, 0x3f, 0xd8, 0x91, 0xe8, 0xcf, 0x0d, 0x40, 0x91, 0xbb, 0xb2, 0x0d, 0x7a, 0xda, 0xa7, - 0xe7, 0xa3, 0x5d, 0x06, 0x4d, 0x3b, 0x1f, 0x5d, 0x54, 0x18, 0xac, 0x51, 0xa1, 0x97, 0x0d, 0x98, - 0xe2, 0x7f, 0xaf, 0xa9, 0xd3, 0xfe, 0xc8, 0x0f, 0x51, 0xb9, 0x46, 0x61, 0x95, 0x0a, 0x0d, 0xfa, - 0x8b, 0xa1, 0x14, 0xac, 0x8b, 0x34, 0xbf, 0x65, 0xc0, 0xc2, 0x9a, 0x1f, 0xd8, 0xee, 0x2a, 0xf1, - 0x03, 0xba, 0xf3, 0xd3, 0xfd, 0xa1, 0xdb, 0x1a, 0x24, 0x4e, 0x78, 0x15, 0xe6, 0xc4, 0x1d, 0x5f, - 0x77, 0xdb, 0x27, 0x81, 0x66, 0x6a, 0xab, 0xa5, 0xb6, 0x12, 0xc3, 0xe3, 0x44, 0x09, 0xca, 0x45, - 0x5c, 0xf6, 0x85, 0x5c, 0xf2, 0x51, 0x2e, 0xb5, 0x18, 0x1e, 0x27, 0x4a, 0x98, 0xdf, 0xcd, 0xc3, - 0x49, 0xd6, 0x8c, 0x58, 0x8c, 0xff, 0x67, 0xfb, 0xc5, 0xf8, 0x8f, 0xb8, 0xda, 0x98, 0xac, 0x23, - 0x44, 0xf8, 0xff, 0xaa, 0x01, 0xb3, 0x8d, 0x68, 0x4f, 0x67, 0xe3, 0x41, 0x49, 0x1b, 0x43, 0x1e, - 0x4f, 0x14, 0x03, 0xe2, 0xb8, 0x7c, 0xf4, 0x05, 0x03, 0x66, 0xa3, 0xd5, 0x94, 0x1b, 0xf0, 0x31, - 0x74, 0x92, 0x0a, 0x00, 0x8e, 0xc2, 0x7d, 0x1c, 0xaf, 0x82, 0xf9, 0x37, 0x86, 0x18, 0xd2, 0xe3, - 0x08, 0x60, 0x47, 0xb7, 0x60, 0x32, 0x68, 0xf9, 0x1c, 0x28, 0x5a, 0x3b, 0xe2, 0xa1, 0x6d, 0x6b, - 0xbd, 0xc6, 0xd8, 0x69, 0x76, 0x95, 0x80, 0x50, 0xfb, 0x50, 0xca, 0x32, 0xbf, 0x6a, 0xc0, 0xe4, - 0x25, 0x77, 0x5b, 0x6c, 0x4e, 0x1f, 0xce, 0xc0, 0x25, 0xa2, 0x2c, 0x27, 0x75, 0x9b, 0x16, 0x1a, - 0xe3, 0xcf, 0x44, 0x1c, 0x22, 0x0f, 0x6a, 0xbc, 0x97, 0x59, 0x3e, 0x19, 0xca, 0xea, 0x92, 0xbb, - 0xdd, 0xd7, 0xdf, 0xf6, 0xdb, 0x05, 0x38, 0xf1, 0x9c, 0xb5, 0x4f, 0x9c, 0xc0, 0x1a, 0x7e, 0x3b, - 0x7d, 0x12, 0xa6, 0xac, 0x0e, 0x8b, 0x67, 0xd5, 0xac, 0xe1, 0xd0, 0xc7, 0x10, 0xa2, 0xb0, 0x4e, - 0x17, 0xee, 0x2b, 0x3c, 0xbd, 0x45, 0xda, 0x8e, 0xb0, 0x12, 0xc3, 0xe3, 0x44, 0x09, 0x74, 0x09, - 0x90, 0x78, 0xac, 0x57, 0xae, 0xd7, 0xdd, 0xae, 0xc3, 0x77, 0x16, 0xee, 0x7e, 0x50, 0xc7, 0xb2, - 0x8d, 0x04, 0x05, 0x4e, 0x29, 0x85, 0x3e, 0x04, 0x8b, 0x75, 0xc6, 0x59, 0x18, 0xe9, 0x3a, 0x47, - 0x7e, 0x50, 0x53, 0xb1, 0xe4, 0x2b, 0x7d, 0xe8, 0x70, 0x5f, 0x0e, 0xb4, 0xa6, 0x7e, 0xe0, 0x7a, - 0x56, 0x93, 0xe8, 0x7c, 0xc7, 0xa3, 0x35, 0xad, 0x25, 0x28, 0x70, 0x4a, 0x29, 0xf4, 0x09, 0x98, - 0x0c, 0x76, 0x3d, 0xe2, 0xef, 0xba, 0xad, 0x86, 0xb8, 0x5e, 0x1f, 0xd1, 0x27, 0x25, 0x46, 0x7f, - 0x4b, 0x72, 0xd5, 0xa6, 0xb7, 0x04, 0xe1, 0x50, 0x26, 0xf2, 0x60, 0xdc, 0xaf, 0xbb, 0x1d, 0xe2, - 0x0b, 0xe3, 0xf6, 0x52, 0x26, 0xd2, 0x99, 0x8f, 0x45, 0xf3, 0x86, 0x31, 0x09, 0x58, 0x48, 0x32, - 0xbf, 0x9d, 0x83, 0x69, 0x9d, 0x70, 0x80, 0x2d, 0xe2, 0x93, 0x06, 0x4c, 0xd7, 0x5d, 0x27, 0xf0, - 0xdc, 0x16, 0xf7, 0xf4, 0x64, 0xa3, 0x7a, 0x29, 0xab, 0x55, 0x12, 0x58, 0x76, 0x4b, 0x73, 0x1a, - 0x69, 0x62, 0x70, 0x44, 0x28, 0xfa, 0x8c, 0x01, 0xb3, 0x61, 0x28, 0x54, 0xe8, 0x72, 0xca, 0xb4, - 0x22, 0x6a, 0xc7, 0x3d, 0x1f, 0x95, 0x84, 0xe3, 0xa2, 0xcd, 0x6d, 0x98, 0x8b, 0x8f, 0x36, 0xed, - 0xca, 0x8e, 0x25, 0xd6, 0x7a, 0x3e, 0xec, 0xca, 0xaa, 0xe5, 0xfb, 0x98, 0x61, 0xd0, 0x3b, 0xa0, - 0xd8, 0xb6, 0xbc, 0xa6, 0xed, 0x58, 0x2d, 0xd6, 0x8b, 0x79, 0x6d, 0x43, 0x12, 0x70, 0xac, 0x28, - 0xcc, 0x1f, 0x8d, 0xc1, 0x94, 0x76, 0x26, 0x39, 0xfe, 0xf3, 0x45, 0x24, 0x7f, 0x40, 0x3e, 0xc3, - 0xfc, 0x01, 0xcf, 0x03, 0xec, 0xd8, 0x8e, 0xed, 0xef, 0x1e, 0x31, 0x33, 0x01, 0xbb, 0x9a, 0xbc, - 0xa0, 0x38, 0x60, 0x8d, 0x5b, 0x78, 0xff, 0x53, 0x38, 0x24, 0x5f, 0xcb, 0xab, 0x86, 0xa6, 0x3c, - 0xc6, 0xb3, 0xb8, 0xef, 0xd6, 0x06, 0x66, 0x59, 0x2a, 0x93, 0xf3, 0x4e, 0xe0, 0xed, 0x1f, 0xaa, - 0x63, 0xb6, 0xa0, 0xe8, 0x11, 0xbf, 0xdb, 0xa6, 0x27, 0xa5, 0x89, 0xa1, 0xbb, 0x81, 0xc5, 0x0a, - 0x60, 0x51, 0x1e, 0x2b, 0x4e, 0x4b, 0x4f, 0xc3, 0x89, 0x48, 0x15, 0xd0, 0x1c, 0xe4, 0x6f, 0x92, - 0x7d, 0x3e, 0x4f, 0x30, 0xfd, 0x89, 0x16, 0x22, 0xb7, 0x64, 0xa2, 0x5b, 0xde, 0x9b, 0x7b, 0xca, - 0x30, 0x5d, 0x48, 0x3d, 0xf8, 0x1e, 0xe5, 0x12, 0x83, 0x8e, 0x45, 0x4b, 0x4b, 0x4d, 0xa0, 0xc6, - 0x82, 0x47, 0x84, 0x70, 0x9c, 0xf9, 0xe3, 0x71, 0x10, 0x57, 0xb8, 0x03, 0x6c, 0x3e, 0xfa, 0xcd, - 0x4d, 0xee, 0x08, 0x37, 0x37, 0x97, 0x60, 0xda, 0x76, 0xec, 0xc0, 0xb6, 0x5a, 0xcc, 0xa9, 0x21, - 0x94, 0xa3, 0x8c, 0x57, 0x9d, 0x5e, 0xd3, 0x70, 0x29, 0x7c, 0x22, 0x65, 0xd1, 0x15, 0x28, 0x30, - 0xed, 0x21, 0x26, 0xf0, 0xf0, 0xf7, 0xcc, 0x2c, 0xc4, 0x80, 0x3f, 0x62, 0xe1, 0x9c, 0x98, 0x45, - 0xcf, 0x73, 0x33, 0xa8, 0x63, 0xa7, 0x98, 0xc7, 0xa1, 0x45, 0x1f, 0xc3, 0xe3, 0x44, 0x09, 0xca, - 0x65, 0xc7, 0xb2, 0x5b, 0x5d, 0x8f, 0x84, 0x5c, 0xc6, 0xa3, 0x5c, 0x2e, 0xc4, 0xf0, 0x38, 0x51, - 0x02, 0xed, 0xc0, 0xb4, 0x80, 0xf1, 0x38, 0x9f, 0x89, 0x23, 0xb6, 0x92, 0xc5, 0x73, 0x5d, 0xd0, - 0x38, 0xe1, 0x08, 0x5f, 0xd4, 0x85, 0x79, 0xdb, 0xa9, 0xbb, 0x4e, 0xbd, 0xd5, 0xf5, 0xed, 0x3d, - 0x12, 0xbe, 0x20, 0x39, 0x8a, 0xb0, 0x53, 0x07, 0xbd, 0xd2, 0xfc, 0x5a, 0x9c, 0x1d, 0x4e, 0x4a, - 0x40, 0xaf, 0x18, 0x70, 0xaa, 0xee, 0x3a, 0x3e, 0x7b, 0xec, 0xbc, 0x47, 0xce, 0x7b, 0x9e, 0xeb, - 0x71, 0xd9, 0x93, 0x47, 0x94, 0xcd, 0x7c, 0x69, 0x2b, 0x69, 0x2c, 0x71, 0xba, 0x24, 0xf4, 0x22, - 0x14, 0x3b, 0x9e, 0xbb, 0x67, 0x37, 0x88, 0x27, 0x62, 0xc6, 0xd6, 0xb3, 0x48, 0xbe, 0x50, 0x15, - 0x3c, 0xc3, 0xad, 0x47, 0x42, 0xb0, 0x92, 0x67, 0x7e, 0xad, 0x08, 0x33, 0x51, 0x72, 0xf4, 0x71, - 0x80, 0x8e, 0xe7, 0xb6, 0x49, 0xb0, 0x4b, 0xd4, 0x4b, 0x80, 0xcd, 0x51, 0xdf, 0xf8, 0x4b, 0x7e, - 0x32, 0x6a, 0x83, 0x6e, 0x17, 0x21, 0x14, 0x6b, 0x12, 0x91, 0x07, 0x13, 0x37, 0xb9, 0x12, 0x15, - 0x36, 0xc5, 0x73, 0x99, 0x58, 0x40, 0x42, 0x32, 0x0b, 0x61, 0x17, 0x20, 0x2c, 0x05, 0xa1, 0x6d, - 0xc8, 0xdf, 0x22, 0xdb, 0xd9, 0xbc, 0x9b, 0xbd, 0x4e, 0xc4, 0xd9, 0xa4, 0x32, 0x71, 0xd0, 0x2b, - 0xe5, 0xaf, 0x93, 0x6d, 0x4c, 0x99, 0xd3, 0x76, 0x35, 0xf8, 0xfd, 0xb3, 0xd8, 0x2a, 0x46, 0x6c, - 0x57, 0xe4, 0x32, 0x9b, 0xb7, 0x4b, 0x80, 0xb0, 0x14, 0x84, 0x5e, 0x84, 0xc9, 0x5b, 0xd6, 0x1e, - 0xd9, 0xf1, 0x5c, 0x27, 0x10, 0xa1, 0x42, 0x23, 0x06, 0x87, 0x5f, 0x97, 0xec, 0x84, 0x5c, 0xa6, - 0xde, 0x15, 0x10, 0x87, 0xe2, 0xd0, 0x1e, 0x14, 0x1d, 0x72, 0x0b, 0x93, 0x96, 0x5d, 0x17, 0x41, - 0xb0, 0x23, 0x4e, 0xeb, 0x4d, 0xc1, 0x4d, 0x48, 0x66, 0x7a, 0x4f, 0xc2, 0xb0, 0x92, 0x45, 0xc7, - 0xf2, 0x86, 0xbb, 0x2d, 0x36, 0xaa, 0x11, 0xc7, 0x52, 0x9d, 0x33, 0xf9, 0x58, 0x5e, 0x72, 0xb7, - 0x31, 0x65, 0x4e, 0xd7, 0x48, 0x5d, 0xc5, 0xa9, 0x88, 0x6d, 0x6a, 0x33, 0xdb, 0xf8, 0x1c, 0xbe, - 0x46, 0x42, 0x28, 0xd6, 0x24, 0xd2, 0xbe, 0x6d, 0x0a, 0x27, 0x9d, 0xd8, 0xa8, 0x46, 0xec, 0xdb, - 0xa8, 0xcb, 0x8f, 0xf7, 0xad, 0x84, 0x61, 0x25, 0xcb, 0xfc, 0xea, 0x38, 0x4c, 0xeb, 0xc9, 0xa6, - 0x06, 0xd0, 0xd5, 0xca, 0x3e, 0xcd, 0x0d, 0x63, 0x9f, 0xd2, 0xe3, 0x85, 0xe6, 0x63, 0x97, 0x1e, - 0x86, 0xb5, 0xcc, 0xcc, 0xb3, 0xf0, 0x78, 0xa1, 0x01, 0x7d, 0x1c, 0x11, 0x3a, 0xc4, 0xb5, 0x3b, - 0x35, 0x72, 0xb8, 0x19, 0x50, 0x88, 0x1a, 0x39, 0x11, 0xc5, 0xfe, 0x18, 0x40, 0x98, 0x74, 0x49, - 0xdc, 0xbd, 0x28, 0xeb, 0x49, 0x4b, 0x06, 0xa5, 0x51, 0xa1, 0x47, 0x60, 0x9c, 0x2a, 0x4a, 0xd2, - 0x10, 0xcf, 0x34, 0xd5, 0x19, 0xee, 0x02, 0x83, 0x62, 0x81, 0x45, 0x4f, 0x51, 0x9b, 0x26, 0x54, - 0x6f, 0xe2, 0xf5, 0xe5, 0x42, 0x68, 0xd3, 0x84, 0x38, 0x1c, 0xa1, 0xa4, 0x55, 0x27, 0x54, 0x1b, - 0xb1, 0x99, 0xa4, 0x55, 0x9d, 0xa9, 0x28, 0xcc, 0x71, 0xcc, 0xa7, 0x10, 0xd3, 0x5e, 0x4c, 0x59, - 0x15, 0x34, 0x9f, 0x42, 0x0c, 0x8f, 0x13, 0x25, 0x68, 0x63, 0xc4, 0xb5, 0xd1, 0x14, 0x8f, 0x2e, - 0xec, 0x73, 0xe1, 0xf3, 0x9a, 0x6e, 0x99, 0x4f, 0xb3, 0xa1, 0x7f, 0x7f, 0x76, 0x89, 0xd3, 0x06, - 0x37, 0xcd, 0x47, 0x33, 0xa2, 0x3f, 0x02, 0x33, 0xd1, 0x3d, 0x8b, 0x4e, 0xa8, 0x8e, 0xe7, 0xee, - 0xd8, 0x2d, 0x12, 0xf7, 0xfd, 0x54, 0x39, 0x18, 0x4b, 0xfc, 0x60, 0xae, 0xf4, 0x3f, 0xcb, 0xc3, - 0xc9, 0xcd, 0xa6, 0xed, 0xdc, 0x8e, 0x79, 0x6d, 0xd3, 0x12, 0x9a, 0x1a, 0xc3, 0x26, 0x34, 0x0d, - 0x9f, 0x9c, 0x88, 0x8c, 0xb1, 0xe9, 0x4f, 0x4e, 0x64, 0x3a, 0xd9, 0x28, 0x2d, 0xfa, 0xa1, 0x01, - 0x0f, 0x5a, 0x0d, 0x6e, 0x45, 0x5a, 0x2d, 0x01, 0x0d, 0x85, 0xca, 0x15, 0xed, 0x8f, 0xa8, 0x13, - 0x92, 0x8d, 0x5f, 0x2e, 0x1f, 0x22, 0x95, 0x8f, 0xf8, 0xdb, 0x44, 0x0b, 0x1e, 0x3c, 0x8c, 0x14, - 0x1f, 0x5a, 0xfd, 0xa5, 0xcb, 0xf0, 0xd6, 0xbb, 0x0a, 0x1a, 0x6a, 0xb6, 0x7c, 0xd2, 0x80, 0x49, - 0xee, 0x94, 0xc4, 0x64, 0x87, 0x6e, 0x15, 0x56, 0xc7, 0xbe, 0x46, 0x3c, 0x5f, 0x66, 0x5a, 0xd2, - 0x0e, 0x5a, 0xe5, 0xea, 0x9a, 0xc0, 0x60, 0x8d, 0x8a, 0x6e, 0xc6, 0x37, 0x6d, 0xa7, 0x21, 0x86, - 0x49, 0x6d, 0xc6, 0xcf, 0xd9, 0x4e, 0x03, 0x33, 0x8c, 0xda, 0xae, 0xf3, 0x7d, 0xd3, 0x9e, 0x7c, - 0xc5, 0x80, 0x19, 0xf6, 0xc8, 0x2d, 0x3c, 0x02, 0x3c, 0xa9, 0x62, 0x2a, 0x78, 0x35, 0xce, 0x44, - 0x63, 0x2a, 0xee, 0xf4, 0x4a, 0x53, 0xfc, 0x59, 0x5c, 0x34, 0xc4, 0xe2, 0x83, 0xc2, 0x6f, 0xc0, - 0x22, 0x3f, 0x72, 0x43, 0x1f, 0x6b, 0x95, 0x97, 0xac, 0x26, 0x99, 0xe0, 0x90, 0x9f, 0xf9, 0x12, - 0x4c, 0xeb, 0x01, 0xf3, 0xe8, 0x49, 0x98, 0xea, 0xd8, 0x4e, 0x33, 0xfa, 0xb0, 0x4a, 0x79, 0x4a, - 0xab, 0x21, 0x0a, 0xeb, 0x74, 0xac, 0x98, 0x1b, 0x16, 0x8b, 0x39, 0x58, 0xab, 0xae, 0x5e, 0x2c, - 0xfc, 0x63, 0xfe, 0x7e, 0x1e, 0x4e, 0xa6, 0x3c, 0xcc, 0x40, 0xaf, 0x1a, 0x30, 0xce, 0xa2, 0xc4, - 0x65, 0xd4, 0xc4, 0x0b, 0x99, 0x3f, 0xfe, 0x58, 0x66, 0xc1, 0xe8, 0x62, 0x1e, 0xab, 0xed, 0x93, - 0x03, 0xb1, 0x10, 0x8e, 0x7e, 0xc3, 0x80, 0x29, 0x4b, 0x5b, 0x6a, 0x3c, 0x90, 0x64, 0x3b, 0xfb, - 0xca, 0x24, 0x56, 0x96, 0x16, 0x00, 0x17, 0x2e, 0x24, 0xbd, 0x2e, 0x4b, 0xef, 0x81, 0x29, 0xad, - 0x09, 0xc3, 0xac, 0x90, 0xa5, 0x67, 0x60, 0x6e, 0xa4, 0x15, 0xf6, 0x01, 0x18, 0x36, 0x71, 0x18, - 0x55, 0x58, 0xb7, 0xf4, 0x97, 0xa7, 0xaa, 0xc7, 0xc5, 0xd3, 0x53, 0x81, 0x35, 0xb7, 0x61, 0x2e, - 0x7e, 0xc8, 0xc9, 0xfc, 0xde, 0xf4, 0x5d, 0x30, 0x64, 0xaa, 0x2f, 0xf3, 0x2f, 0x72, 0x30, 0x21, - 0x5e, 0x77, 0xdd, 0x83, 0xd8, 0xd1, 0x9b, 0x91, 0xab, 0x92, 0xb5, 0x4c, 0x1e, 0xa5, 0xf5, 0x0d, - 0x1c, 0xf5, 0x63, 0x81, 0xa3, 0xcf, 0x65, 0x23, 0xee, 0xf0, 0xa8, 0xd1, 0xaf, 0x8c, 0xc1, 0x6c, - 0xec, 0xb5, 0x1c, 0x35, 0x55, 0x12, 0xc1, 0x52, 0x57, 0x33, 0x7d, 0x90, 0xa7, 0xe2, 0x9a, 0x0f, - 0x8f, 0x9b, 0xf2, 0x23, 0x19, 0x15, 0xaf, 0x64, 0x96, 0x8c, 0xf9, 0x67, 0xc9, 0x15, 0x87, 0x8d, - 0x03, 0xfa, 0x27, 0x03, 0xee, 0xef, 0xfb, 0xa8, 0x92, 0x25, 0xc4, 0xf0, 0xa2, 0x58, 0xb1, 0x20, - 0x33, 0x7e, 0xb7, 0xad, 0xee, 0x2d, 0xe2, 0x39, 0x0c, 0xe2, 0xe2, 0xd1, 0x13, 0x30, 0xcd, 0x54, - 0x2b, 0xdd, 0x53, 0x02, 0xd2, 0x11, 0x8e, 0x5a, 0xe6, 0xb2, 0xab, 0x69, 0x70, 0x1c, 0xa1, 0x32, - 0xbf, 0x6c, 0xc0, 0x62, 0xbf, 0xf4, 0x08, 0x03, 0x1c, 0x0c, 0x7f, 0x2e, 0x16, 0xdc, 0x5a, 0x4a, - 0x04, 0xb7, 0xc6, 0x8e, 0x86, 0x32, 0x8e, 0x55, 0x3b, 0x95, 0xe5, 0xef, 0x12, 0xbb, 0xf9, 0x59, - 0x03, 0x4e, 0xf7, 0x59, 0x4d, 0x89, 0x20, 0x67, 0xe3, 0xc8, 0x41, 0xce, 0xb9, 0x41, 0x83, 0x9c, - 0xcd, 0xbf, 0xca, 0xc3, 0x9c, 0xa8, 0x4f, 0x68, 0x5f, 0x3d, 0x15, 0x09, 0x11, 0x7e, 0x5b, 0x2c, - 0x44, 0x78, 0x21, 0x4e, 0xff, 0xb3, 0xf8, 0xe0, 0x37, 0x57, 0x7c, 0xf0, 0x4f, 0x72, 0x70, 0x2a, - 0x35, 0x6b, 0x03, 0xfa, 0x54, 0x8a, 0x6a, 0xb8, 0x9e, 0x71, 0x7a, 0x88, 0x01, 0x95, 0xc3, 0xa8, - 0x41, 0xb5, 0x5f, 0xd0, 0x83, 0x59, 0xf9, 0x56, 0xbf, 0x73, 0x0c, 0x89, 0x2e, 0x86, 0x8c, 0x6b, - 0x35, 0x7f, 0x25, 0x0f, 0x8f, 0x0e, 0xca, 0xe8, 0x4d, 0xfa, 0xee, 0xc1, 0x8f, 0xbc, 0x7b, 0xb8, - 0x47, 0x6a, 0xfb, 0x58, 0x9e, 0x40, 0x7c, 0x35, 0xaf, 0xd4, 0x5e, 0x72, 0x7e, 0x0e, 0x74, 0xab, - 0x37, 0x41, 0x4d, 0x3b, 0x99, 0xcb, 0x31, 0xdc, 0x0a, 0x27, 0x6a, 0x1c, 0x7c, 0xa7, 0x57, 0x9a, - 0x17, 0xf9, 0xdd, 0x6a, 0x24, 0x10, 0x40, 0x2c, 0x0b, 0xa1, 0x47, 0xa1, 0xe8, 0x71, 0xac, 0x8c, - 0xf4, 0x16, 0x57, 0xa3, 0x1c, 0x86, 0x15, 0x16, 0x7d, 0x42, 0xb3, 0x85, 0xc7, 0x8e, 0xeb, 0x95, - 0xfe, 0x61, 0x37, 0xbe, 0x2f, 0x40, 0xd1, 0x97, 0x59, 0x19, 0xb9, 0x5b, 0xfe, 0xf1, 0x01, 0x1f, - 0x10, 0xd0, 0xa3, 0x93, 0x4c, 0xd1, 0xc8, 0xdb, 0xa7, 0x12, 0x38, 0x2a, 0x96, 0xc8, 0x54, 0xa7, - 0x16, 0xee, 0x63, 0x84, 0x94, 0x13, 0xcb, 0xf7, 0x0c, 0x98, 0x12, 0xa3, 0x75, 0x0f, 0xde, 0x34, - 0xdc, 0x88, 0xbe, 0x69, 0x38, 0x9f, 0xc9, 0xde, 0xd1, 0xe7, 0x41, 0xc3, 0x0d, 0x98, 0xd6, 0x13, - 0xf7, 0xa0, 0xe7, 0xb5, 0xbd, 0xcf, 0x18, 0x25, 0x1b, 0x87, 0xdc, 0x1d, 0xc3, 0x7d, 0xd1, 0xfc, - 0x52, 0x51, 0xf5, 0x22, 0xf3, 0x43, 0xe8, 0x73, 0xd0, 0x38, 0x74, 0x0e, 0xea, 0x53, 0x20, 0x97, - 0xfd, 0x14, 0xb8, 0x02, 0x45, 0xb9, 0x41, 0x09, 0x35, 0xfe, 0xb0, 0x1e, 0xbb, 0x46, 0x6d, 0x01, - 0xca, 0x4c, 0x9b, 0xb8, 0xec, 0xa8, 0xa5, 0xc6, 0x50, 0x6d, 0x9c, 0x8a, 0x0d, 0x7a, 0x11, 0xa6, - 0x6e, 0xb9, 0xde, 0xcd, 0x96, 0x6b, 0xb1, 0x7c, 0xab, 0x90, 0xc5, 0x05, 0x8b, 0x72, 0x78, 0xf1, - 0x80, 0xef, 0xeb, 0x21, 0x7f, 0xac, 0x0b, 0x43, 0x65, 0x98, 0x6d, 0xdb, 0x0e, 0x26, 0x56, 0x43, - 0x3d, 0x5d, 0x18, 0xe3, 0x09, 0x21, 0xa5, 0x91, 0xbb, 0x11, 0x45, 0xe3, 0x38, 0x3d, 0xfa, 0x18, - 0x14, 0x7d, 0x91, 0x89, 0x27, 0x9b, 0xab, 0x30, 0x75, 0x66, 0xe4, 0x4c, 0xc3, 0xbe, 0x93, 0x10, - 0xac, 0x04, 0xa2, 0x75, 0x58, 0xf0, 0x44, 0xae, 0x8b, 0xc8, 0xd7, 0x1a, 0xf8, 0xfa, 0x64, 0x79, - 0x07, 0x71, 0x0a, 0x1e, 0xa7, 0x96, 0xa2, 0x56, 0x0c, 0xcb, 0x40, 0xc5, 0xef, 0x04, 0x34, 0x37, - 0x3a, 0x9b, 0xf0, 0x0d, 0x2c, 0xb0, 0x87, 0x3d, 0x85, 0x29, 0x8e, 0xf0, 0x14, 0xa6, 0x06, 0xa7, - 0xe2, 0x28, 0x96, 0x91, 0x83, 0x25, 0x01, 0xd1, 0xb4, 0x47, 0x35, 0x8d, 0x08, 0xa7, 0x97, 0x45, - 0xd7, 0x61, 0xd2, 0x23, 0xec, 0x7c, 0x51, 0x96, 0x97, 0xef, 0x43, 0x87, 0x19, 0x61, 0xc9, 0x00, - 0x87, 0xbc, 0xe8, 0xb8, 0x5b, 0xd1, 0x9c, 0x88, 0x57, 0x32, 0xfc, 0xde, 0x94, 0x18, 0xfb, 0x3e, - 0x99, 0x72, 0xcc, 0x37, 0x66, 0xe0, 0x44, 0xc4, 0xb7, 0x80, 0x1e, 0x86, 0x02, 0x4b, 0x51, 0xc2, - 0xb6, 0x87, 0x62, 0xb8, 0x85, 0xf1, 0xce, 0xe1, 0x38, 0xf4, 0x39, 0x03, 0x66, 0x3b, 0x11, 0x2f, - 0xac, 0xdc, 0x39, 0x47, 0xbc, 0xe7, 0x8b, 0xba, 0x76, 0xb5, 0x6c, 0xc2, 0x51, 0x61, 0x38, 0x2e, - 0x9d, 0x2e, 0x40, 0x11, 0x79, 0xd7, 0x22, 0x1e, 0xa3, 0x16, 0x36, 0x8e, 0x62, 0xb1, 0x12, 0x45, - 0xe3, 0x38, 0x3d, 0x1d, 0x61, 0xd6, 0xba, 0x51, 0x3e, 0x44, 0x53, 0x96, 0x0c, 0x70, 0xc8, 0x0b, - 0x3d, 0x03, 0x33, 0x22, 0x15, 0x5e, 0xd5, 0x6d, 0x5c, 0xb4, 0xfc, 0x5d, 0x61, 0xdc, 0xab, 0xc3, - 0xc8, 0x4a, 0x04, 0x8b, 0x63, 0xd4, 0xac, 0x6d, 0x61, 0xbe, 0x41, 0xc6, 0x60, 0x3c, 0x9a, 0x6c, - 0x79, 0x25, 0x8a, 0xc6, 0x71, 0x7a, 0xf4, 0x0e, 0x6d, 0xdf, 0xe7, 0xf7, 0x74, 0x6a, 0x37, 0x48, - 0xd9, 0xfb, 0xcb, 0x30, 0xdb, 0x65, 0x67, 0xa1, 0x86, 0x44, 0x8a, 0xf5, 0xa8, 0x04, 0x5e, 0x8d, - 0xa2, 0x71, 0x9c, 0x1e, 0x3d, 0x0d, 0x27, 0x3c, 0xba, 0xbb, 0x29, 0x06, 0xfc, 0xf2, 0x4e, 0xdd, - 0xcd, 0x60, 0x1d, 0x89, 0xa3, 0xb4, 0xe8, 0x59, 0x98, 0x0f, 0x93, 0x57, 0x49, 0x06, 0xfc, 0x36, - 0x4f, 0xe5, 0x65, 0x29, 0xc7, 0x09, 0x70, 0xb2, 0x0c, 0xfa, 0x05, 0x98, 0xd3, 0x7a, 0x62, 0xcd, - 0x69, 0x90, 0xdb, 0x22, 0xc1, 0x10, 0x4b, 0x93, 0xb6, 0x12, 0xc3, 0xe1, 0x04, 0x35, 0x7a, 0x2f, - 0xcc, 0xd4, 0xdd, 0x56, 0x8b, 0xed, 0x71, 0x3c, 0xd1, 0x2f, 0xcf, 0x24, 0xc4, 0x73, 0x2e, 0x45, - 0x30, 0x38, 0x46, 0x89, 0x2e, 0x01, 0x72, 0xb7, 0x7d, 0xe2, 0xed, 0x91, 0xc6, 0xb3, 0xfc, 0xd3, - 0x96, 0x54, 0xc5, 0x9f, 0x88, 0xc6, 0xfd, 0x5e, 0x4e, 0x50, 0xe0, 0x94, 0x52, 0x2c, 0xad, 0x8b, - 0xf6, 0xa2, 0x68, 0x26, 0x8b, 0x8f, 0xb2, 0xc4, 0x4f, 0xee, 0x77, 0x7d, 0x4e, 0xe4, 0xc1, 0x38, - 0x0f, 0xc3, 0xce, 0x26, 0xa5, 0x90, 0x9e, 0xf3, 0x33, 0xd4, 0x11, 0x1c, 0x8a, 0x85, 0x24, 0xf4, - 0x71, 0x98, 0xdc, 0x96, 0x09, 0xa0, 0x17, 0xe7, 0xb2, 0xd0, 0x8b, 0xb1, 0x5c, 0xe6, 0xe1, 0xc9, - 0x54, 0x21, 0x70, 0x28, 0x12, 0x3d, 0x02, 0x53, 0x17, 0xab, 0x65, 0x35, 0x0b, 0xe7, 0xd9, 0xe8, - 0x8f, 0xd1, 0x22, 0x58, 0x47, 0xd0, 0x15, 0xa6, 0xec, 0x25, 0xc4, 0x86, 0x38, 0xd4, 0xb7, 0x49, - 0xf3, 0x87, 0x52, 0xb3, 0xeb, 0x48, 0x5c, 0x5b, 0x3c, 0x19, 0xa3, 0x16, 0x70, 0xac, 0x28, 0xd0, - 0x0b, 0x30, 0x25, 0xf4, 0x05, 0xdb, 0x9b, 0x16, 0x8e, 0xf6, 0x5a, 0x0d, 0x87, 0x2c, 0xb0, 0xce, - 0x8f, 0xdd, 0x32, 0xb1, 0xbc, 0xb8, 0xe4, 0x42, 0xb7, 0xd5, 0x5a, 0x3c, 0xc5, 0xf6, 0xcd, 0xf0, - 0x96, 0x29, 0x44, 0x61, 0x9d, 0x0e, 0x3d, 0x2e, 0x23, 0x27, 0xde, 0x12, 0xb9, 0x76, 0x53, 0x91, - 0x13, 0xca, 0xca, 0xed, 0x13, 0xd8, 0x7b, 0xfa, 0x2e, 0x21, 0x0b, 0xdb, 0xb0, 0x24, 0x4d, 0xac, - 0xe4, 0x22, 0x59, 0x5c, 0x8c, 0x78, 0x09, 0x96, 0xae, 0xf7, 0xa5, 0xc4, 0x87, 0x70, 0x41, 0xdb, - 0x90, 0xb7, 0x5a, 0xdb, 0x8b, 0xf7, 0x67, 0x61, 0x2b, 0xaa, 0x4f, 0xd5, 0xf2, 0x60, 0x9c, 0xf2, - 0x7a, 0x05, 0x53, 0xe6, 0xe6, 0x2b, 0x39, 0xe5, 0x95, 0x57, 0xa9, 0x16, 0x5f, 0xd2, 0x67, 0xb5, - 0x91, 0xc5, 0xa7, 0x18, 0x13, 0x59, 0xd2, 0xb9, 0x42, 0x4a, 0x9d, 0xd3, 0x1d, 0xb5, 0x8e, 0x33, - 0xc9, 0xa3, 0x11, 0x4d, 0x23, 0xc9, 0x4f, 0x73, 0xd1, 0x55, 0x6c, 0x7e, 0x7f, 0x5c, 0x39, 0xa1, - 0x62, 0xa1, 0x00, 0x1e, 0x14, 0x6c, 0x3f, 0xb0, 0xdd, 0x0c, 0x9f, 0x6d, 0xc5, 0xf2, 0x2f, 0xb2, - 0x00, 0x56, 0x86, 0xc0, 0x5c, 0x14, 0x95, 0xe9, 0x34, 0x6d, 0xe7, 0xb6, 0x68, 0xfe, 0x95, 0xcc, - 0xef, 0xf8, 0xb9, 0x4c, 0x86, 0xc0, 0x5c, 0x14, 0xba, 0xc1, 0x67, 0x5a, 0x36, 0x9f, 0xdd, 0x8c, - 0x7f, 0x4d, 0x37, 0x3a, 0xe3, 0xa8, 0x2c, 0xbf, 0x6d, 0x0b, 0x1b, 0x66, 0x44, 0x59, 0xb5, 0x8d, - 0xb5, 0x34, 0x59, 0xb5, 0x8d, 0x35, 0x4c, 0x85, 0xa0, 0xd7, 0x0c, 0x00, 0x4b, 0x7d, 0x56, 0x36, - 0x9b, 0x4f, 0x0a, 0xf4, 0xfb, 0x4c, 0x2d, 0x8f, 0x39, 0x0b, 0xb1, 0x58, 0x93, 0x8c, 0x5e, 0x84, - 0x09, 0x8b, 0x7f, 0x10, 0x45, 0x84, 0xf3, 0x65, 0xf3, 0x95, 0x9f, 0x58, 0x0d, 0x58, 0x1c, 0xa3, - 0x40, 0x61, 0x29, 0x90, 0xca, 0x0e, 0x3c, 0x8b, 0xec, 0xd8, 0x37, 0x45, 0x5c, 0x5f, 0x6d, 0xe4, - 0xbc, 0xc6, 0x94, 0x59, 0x9a, 0x6c, 0x81, 0xc2, 0x52, 0xa0, 0xf9, 0xaf, 0x06, 0x68, 0xdf, 0x20, - 0x0c, 0x03, 0xbd, 0x8c, 0x81, 0x03, 0xbd, 0x72, 0x43, 0x06, 0x7a, 0xe5, 0x87, 0x0a, 0xf4, 0x1a, - 0x1b, 0x3e, 0xd0, 0xab, 0xd0, 0x3f, 0xd0, 0xcb, 0x7c, 0xdd, 0x80, 0xf9, 0xc4, 0x9c, 0x8c, 0x7f, - 0xeb, 0xd9, 0x18, 0xf0, 0x5b, 0xcf, 0xab, 0x30, 0x27, 0x12, 0xb1, 0xd6, 0x3a, 0x2d, 0x3b, 0xf5, - 0x85, 0xeb, 0x56, 0x0c, 0x8f, 0x13, 0x25, 0xcc, 0x3f, 0x36, 0x60, 0x4a, 0x7b, 0x90, 0x43, 0xdb, - 0xc1, 0x1e, 0x2e, 0x89, 0x6a, 0x84, 0x39, 0x68, 0x99, 0x7b, 0x95, 0xe3, 0xb8, 0xa7, 0xbf, 0xa9, - 0x25, 0xfd, 0x0b, 0x3d, 0xfd, 0x14, 0x8a, 0x05, 0x96, 0xa7, 0x73, 0x23, 0xfc, 0x3b, 0xde, 0x79, - 0x3d, 0x9d, 0x1b, 0xe9, 0x60, 0x86, 0x61, 0xe2, 0xa8, 0x2e, 0x17, 0x31, 0x80, 0x5a, 0xca, 0x5b, - 0x8b, 0x9e, 0xd8, 0x18, 0x0e, 0x9d, 0x81, 0x3c, 0x71, 0x1a, 0xe2, 0xe0, 0xa1, 0xbe, 0xf1, 0x72, - 0xde, 0x69, 0x60, 0x0a, 0x37, 0x2f, 0xc3, 0x74, 0x8d, 0xd4, 0x3d, 0x12, 0x3c, 0x47, 0xf6, 0x07, - 0xfe, 0x68, 0xcc, 0x4d, 0xb2, 0x1f, 0xff, 0x68, 0x0c, 0x2d, 0x4e, 0xe1, 0xe6, 0xef, 0x19, 0x10, - 0x4b, 0x8a, 0xac, 0x79, 0xfd, 0x8c, 0x7e, 0x5e, 0xbf, 0x88, 0x7f, 0x2a, 0x77, 0xa8, 0x7f, 0xea, - 0x12, 0xa0, 0xb6, 0x15, 0xd4, 0x77, 0x23, 0x29, 0xc0, 0xc5, 0x99, 0x2f, 0x7c, 0xfe, 0x97, 0xa0, - 0xc0, 0x29, 0xa5, 0xcc, 0x4f, 0x1b, 0x90, 0x48, 0x99, 0x8c, 0xba, 0x50, 0x60, 0xa4, 0xe2, 0x62, - 0xa4, 0x3a, 0xda, 0x8a, 0x4e, 0x3e, 0x28, 0x0f, 0x07, 0x8a, 0xfd, 0xc5, 0x5c, 0x9a, 0xf9, 0x32, - 0xad, 0x4b, 0xfc, 0x03, 0xe0, 0x6f, 0x87, 0x09, 0x22, 0xbe, 0x0d, 0xc2, 0x8f, 0xe5, 0xca, 0x6a, - 0x92, 0x9f, 0x04, 0x91, 0x78, 0x7a, 0x76, 0x93, 0xde, 0x3f, 0xe9, 0x4b, 0xe1, 0x8f, 0xb6, 0xd4, - 0xd9, 0x6d, 0x35, 0x8a, 0xc6, 0x71, 0x7a, 0xf3, 0x13, 0x30, 0xa5, 0xbd, 0x31, 0x67, 0xcb, 0xf2, - 0xb6, 0x55, 0x0f, 0xe2, 0xd3, 0xf9, 0x3c, 0x05, 0x62, 0x8e, 0x63, 0x2e, 0x1f, 0x1e, 0xc7, 0x17, - 0x9b, 0xce, 0x22, 0x7a, 0x4f, 0x60, 0x29, 0x33, 0x8f, 0x34, 0xc9, 0x6d, 0x99, 0x6a, 0x4f, 0x32, - 0xc3, 0x14, 0x88, 0x39, 0xce, 0xbc, 0x06, 0x45, 0xf9, 0xb4, 0x96, 0xbd, 0x4f, 0x93, 0xee, 0x08, - 0xfd, 0x7d, 0x9a, 0xeb, 0x05, 0x98, 0x61, 0xe8, 0x9c, 0xf1, 0x1d, 0xfb, 0xa2, 0xeb, 0x07, 0xf2, - 0x3d, 0x30, 0x77, 0x3a, 0x6e, 0xae, 0x31, 0x18, 0x56, 0x58, 0x73, 0x1e, 0x66, 0x95, 0x37, 0x51, - 0x84, 0x4a, 0x7d, 0x3b, 0x0f, 0xd3, 0x91, 0x2f, 0x4d, 0xde, 0x7d, 0xe6, 0x0f, 0x3e, 0x47, 0x53, - 0xbc, 0x82, 0xf9, 0x21, 0xbd, 0x82, 0xba, 0x1b, 0x76, 0xec, 0x78, 0xdd, 0xb0, 0x85, 0x6c, 0xdc, - 0xb0, 0x01, 0x4c, 0x88, 0xaf, 0xf0, 0x0b, 0x3d, 0xbc, 0x91, 0x51, 0x96, 0x0f, 0xf1, 0xc0, 0x9c, - 0x69, 0x41, 0xb9, 0x9b, 0x4b, 0x51, 0xe6, 0x37, 0x0a, 0x30, 0x13, 0xcd, 0xfb, 0x31, 0xc0, 0x48, - 0xbe, 0x23, 0x31, 0x92, 0x43, 0x7a, 0x45, 0xf2, 0xa3, 0x7a, 0x45, 0xc6, 0x46, 0xf5, 0x8a, 0x14, - 0x8e, 0xe0, 0x15, 0x49, 0xfa, 0x34, 0xc6, 0x07, 0xf6, 0x69, 0xbc, 0x4f, 0x5d, 0xe9, 0x4f, 0x44, - 0xee, 0xc0, 0xc2, 0x2b, 0x7d, 0x14, 0x1d, 0x86, 0x15, 0xb7, 0x91, 0x1a, 0x1a, 0x51, 0xbc, 0xcb, - 0xe9, 0xcf, 0x4b, 0xbd, 0x81, 0x1f, 0xde, 0xf1, 0xfa, 0x96, 0x21, 0x6e, 0xdf, 0x9f, 0x84, 0x29, - 0x31, 0x9f, 0x98, 0x25, 0x00, 0x51, 0x2b, 0xa2, 0x16, 0xa2, 0xb0, 0x4e, 0xc7, 0x3e, 0x86, 0x16, - 0xfd, 0xfa, 0x1b, 0x73, 0x32, 0xe9, 0x1f, 0x43, 0x8b, 0x7d, 0x2d, 0x2e, 0x4e, 0x6f, 0x7e, 0x0c, - 0x4e, 0xa5, 0xda, 0x7c, 0xec, 0x10, 0xcc, 0x94, 0x14, 0x69, 0x08, 0x02, 0xad, 0x1a, 0xb1, 0xb4, - 0x90, 0x4b, 0xd7, 0xfb, 0x52, 0xe2, 0x43, 0xb8, 0x98, 0x5f, 0xcf, 0xc3, 0x4c, 0xf4, 0x4b, 0x1a, - 0xe8, 0x96, 0x3a, 0x21, 0x66, 0x72, 0x38, 0xe5, 0x6c, 0xb5, 0x44, 0x15, 0x7d, 0xdd, 0x3d, 0xb7, - 0xd8, 0xfc, 0xda, 0x56, 0x59, 0x33, 0x8e, 0x4f, 0xb0, 0xf0, 0xb3, 0x08, 0x71, 0xec, 0x63, 0x19, - 0x61, 0x40, 0xb5, 0x88, 0x21, 0xc8, 0x5c, 0x7a, 0x18, 0x22, 0xad, 0x44, 0x61, 0x4d, 0x2c, 0xd5, - 0x2d, 0x7b, 0xc4, 0xb3, 0x77, 0x6c, 0xf5, 0x15, 0x30, 0xb6, 0x73, 0x5f, 0x13, 0x30, 0xac, 0xb0, - 0xe6, 0xcb, 0x39, 0x08, 0xbf, 0x79, 0xc8, 0x32, 0xde, 0xfb, 0x9a, 0x01, 0x27, 0x86, 0xed, 0xd2, - 0xa8, 0x1f, 0x97, 0x08, 0x39, 0x8a, 0x70, 0x2b, 0x0d, 0x82, 0x23, 0x12, 0x7f, 0x0a, 0xdf, 0x3a, - 0xb4, 0x60, 0x36, 0xf6, 0xdc, 0x2b, 0xf3, 0x98, 0xd6, 0x2f, 0xe5, 0x61, 0x52, 0x3d, 0x98, 0x43, - 0xef, 0x61, 0x69, 0xa3, 0x77, 0x5d, 0x99, 0xcc, 0xfb, 0xad, 0x5a, 0x72, 0xe7, 0x5d, 0xb7, 0x71, - 0xa7, 0x57, 0x9a, 0x55, 0xc4, 0x1c, 0x84, 0x45, 0x01, 0x6a, 0x2e, 0x77, 0xbd, 0x56, 0xdc, 0x5c, - 0xbe, 0x8a, 0xd7, 0x31, 0x85, 0xa3, 0xdb, 0x30, 0xc1, 0x93, 0xf6, 0xc8, 0xe8, 0x95, 0x8d, 0x8c, - 0x1e, 0xf9, 0x71, 0xbb, 0x33, 0xec, 0x06, 0xfe, 0xdf, 0xc7, 0x52, 0x1c, 0xd5, 0x92, 0xdb, 0x6e, - 0x63, 0x3f, 0x9e, 0x0c, 0xba, 0xe2, 0x36, 0xf6, 0x31, 0xc3, 0xa0, 0x67, 0x60, 0x26, 0xb0, 0xdb, - 0xc4, 0xed, 0x06, 0xfa, 0x17, 0xe5, 0xf2, 0xe1, 0xf5, 0xc5, 0x56, 0x04, 0x8b, 0x63, 0xd4, 0x54, - 0xcb, 0xde, 0xf0, 0x5d, 0x87, 0x65, 0x78, 0x1a, 0x8f, 0xfa, 0x3a, 0x2f, 0xd5, 0x2e, 0x6f, 0xb2, - 0x04, 0x4f, 0x8a, 0x82, 0x52, 0xdb, 0xec, 0x55, 0x8e, 0x47, 0xc4, 0xed, 0xe1, 0x5c, 0xf8, 0x76, - 0x9a, 0xc3, 0xb1, 0xa2, 0x30, 0xaf, 0xc2, 0x6c, 0xac, 0xa9, 0xf2, 0x60, 0x62, 0xa4, 0x1f, 0x4c, - 0x06, 0xcb, 0xbc, 0xfc, 0x07, 0x06, 0xcc, 0x27, 0x16, 0xef, 0xa0, 0xc1, 0xd6, 0x71, 0x35, 0x92, - 0x3b, 0xba, 0x1a, 0xc9, 0x0f, 0xa7, 0x46, 0x2a, 0xcb, 0xdf, 0x79, 0xe3, 0xec, 0x7d, 0xdf, 0x7d, - 0xe3, 0xec, 0x7d, 0xdf, 0x7f, 0xe3, 0xec, 0x7d, 0x2f, 0x1f, 0x9c, 0x35, 0xbe, 0x73, 0x70, 0xd6, - 0xf8, 0xee, 0xc1, 0x59, 0xe3, 0xfb, 0x07, 0x67, 0x8d, 0x7f, 0x3c, 0x38, 0x6b, 0xbc, 0xfe, 0xa3, - 0xb3, 0xf7, 0x3d, 0x5f, 0x94, 0xd3, 0xe4, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x1a, 0x88, 0xc5, - 0xd7, 0x3a, 0x8d, 0x00, 0x00, + // 7507 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6b, 0x8c, 0x23, 0x59, + 0x75, 0xf0, 0x96, 0xdd, 0xee, 0xb6, 0x4f, 0xbf, 0xef, 0xf4, 0x30, 0xbd, 0xbd, 0x3b, 0xe3, 0xa5, + 0x16, 0xed, 0xb7, 0x7c, 0x1f, 0xf4, 0xc0, 0x3e, 0xbe, 0x2c, 0x2c, 0xda, 0xc4, 0xee, 0x9e, 0x47, + 0xcf, 0x76, 0xf7, 0x78, 0xae, 0x7b, 0x66, 0x60, 0x61, 0x81, 0x6a, 0xfb, 0xb6, 0xbb, 0x66, 0xec, + 0x2a, 0x53, 0x55, 0xee, 0x99, 0x5e, 0x56, 0xb0, 0x1b, 0xb4, 0x1b, 0x12, 0x81, 0xd8, 0x04, 0x50, + 0x14, 0x45, 0x44, 0x28, 0x42, 0xca, 0x03, 0x7e, 0xa1, 0x44, 0xf9, 0x83, 0x94, 0x28, 0x3c, 0x42, + 0x7e, 0x24, 0x82, 0x28, 0x09, 0x10, 0x09, 0x27, 0xdb, 0xe4, 0x4f, 0xa2, 0x44, 0x28, 0x12, 0x51, + 0xc4, 0xfc, 0x8a, 0xee, 0xb3, 0x6e, 0x95, 0xcb, 0x3d, 0x76, 0xbb, 0x7a, 0x58, 0x25, 0xfc, 0xb3, + 0xef, 0x39, 0xf7, 0x9c, 0x73, 0x9f, 0xe7, 0xdc, 0x73, 0xcf, 0x3d, 0x05, 0xeb, 0x0d, 0x3b, 0xd8, + 0xed, 0x6c, 0x2f, 0xd7, 0xdc, 0xd6, 0x59, 0xcb, 0x6b, 0xb8, 0x6d, 0xcf, 0xbd, 0xc1, 0x7e, 0xbc, + 0xdd, 0x73, 0x9b, 0x4d, 0xb7, 0x13, 0xf8, 0x67, 0xdb, 0x37, 0x1b, 0x67, 0xad, 0xb6, 0xed, 0x9f, + 0x55, 0x25, 0x7b, 0xef, 0xb4, 0x9a, 0xed, 0x5d, 0xeb, 0x9d, 0x67, 0x1b, 0xc4, 0x21, 0x9e, 0x15, + 0x90, 0xfa, 0x72, 0xdb, 0x73, 0x03, 0x17, 0xbd, 0x27, 0xa4, 0xb6, 0x2c, 0xa9, 0xb1, 0x1f, 0x1f, + 0x92, 0x75, 0x97, 0xdb, 0x37, 0x1b, 0xcb, 0x94, 0xda, 0xb2, 0x2a, 0x91, 0xd4, 0x96, 0xde, 0xae, + 0xc9, 0xd2, 0x70, 0x1b, 0xee, 0x59, 0x46, 0x74, 0xbb, 0xb3, 0xc3, 0xfe, 0xb1, 0x3f, 0xec, 0x17, + 0x67, 0xb6, 0xf4, 0xf0, 0xcd, 0xa7, 0xfc, 0x65, 0xdb, 0xa5, 0xb2, 0x9d, 0xdd, 0xb6, 0x82, 0xda, + 0xee, 0xd9, 0xbd, 0x1e, 0x89, 0x96, 0x4c, 0x0d, 0xa9, 0xe6, 0x7a, 0x24, 0x09, 0xe7, 0x89, 0x10, + 0xa7, 0x65, 0xd5, 0x76, 0x6d, 0x87, 0x78, 0xfb, 0x61, 0xab, 0x5b, 0x24, 0xb0, 0x92, 0x6a, 0x9d, + 0xed, 0x57, 0xcb, 0xeb, 0x38, 0x81, 0xdd, 0x22, 0x3d, 0x15, 0xfe, 0xff, 0xdd, 0x2a, 0xf8, 0xb5, + 0x5d, 0xd2, 0xb2, 0x7a, 0xea, 0x3d, 0xde, 0xaf, 0x5e, 0x27, 0xb0, 0x9b, 0x67, 0x6d, 0x27, 0xf0, + 0x03, 0x2f, 0x5e, 0xc9, 0xfc, 0x46, 0x16, 0x0a, 0xa5, 0xf5, 0x72, 0x35, 0xb0, 0x82, 0x8e, 0x8f, + 0x5e, 0x35, 0x60, 0xaa, 0xe9, 0x5a, 0xf5, 0xb2, 0xd5, 0xb4, 0x9c, 0x1a, 0xf1, 0x16, 0x8d, 0x87, + 0x8c, 0x47, 0x27, 0x1f, 0x5b, 0x5f, 0x1e, 0x65, 0xbc, 0x96, 0x4b, 0xb7, 0x7c, 0x4c, 0x7c, 0xb7, + 0xe3, 0xd5, 0x08, 0x26, 0x3b, 0xe5, 0x85, 0x6f, 0x77, 0x8b, 0xf7, 0x1d, 0x74, 0x8b, 0x53, 0xeb, + 0x1a, 0x27, 0x1c, 0xe1, 0x8b, 0x3e, 0x6f, 0xc0, 0x7c, 0xcd, 0x72, 0x2c, 0x6f, 0x7f, 0xcb, 0xf2, + 0x1a, 0x24, 0xb8, 0xe0, 0xb9, 0x9d, 0xf6, 0x62, 0xe6, 0x18, 0xa4, 0xb9, 0x5f, 0x48, 0x33, 0xbf, + 0x12, 0x67, 0x87, 0x7b, 0x25, 0x60, 0x72, 0xf9, 0x81, 0xb5, 0xdd, 0x24, 0xba, 0x5c, 0xd9, 0xe3, + 0x94, 0xab, 0x1a, 0x67, 0x87, 0x7b, 0x25, 0x30, 0x5f, 0xc9, 0xc2, 0x7c, 0x69, 0xbd, 0xbc, 0xe5, + 0x59, 0x3b, 0x3b, 0x76, 0x0d, 0xbb, 0x9d, 0xc0, 0x76, 0x1a, 0xe8, 0xad, 0x30, 0x61, 0x3b, 0x0d, + 0x8f, 0xf8, 0x3e, 0x1b, 0xc8, 0x42, 0x79, 0x56, 0x10, 0x9d, 0x58, 0xe3, 0xc5, 0x58, 0xc2, 0xd1, + 0x93, 0x30, 0xe9, 0x13, 0x6f, 0xcf, 0xae, 0x91, 0x8a, 0xeb, 0x05, 0xac, 0xa7, 0x73, 0xe5, 0x13, + 0x02, 0x7d, 0xb2, 0x1a, 0x82, 0xb0, 0x8e, 0x47, 0xab, 0x79, 0xae, 0x1b, 0x08, 0x38, 0xeb, 0x88, + 0x42, 0x58, 0x0d, 0x87, 0x20, 0xac, 0xe3, 0xa1, 0xd7, 0x0c, 0x98, 0xf3, 0x03, 0xbb, 0x76, 0xd3, + 0x76, 0x88, 0xef, 0xaf, 0xb8, 0xce, 0x8e, 0xdd, 0x58, 0xcc, 0xb1, 0x5e, 0xdc, 0x1c, 0xad, 0x17, + 0xab, 0x31, 0xaa, 0xe5, 0x85, 0x83, 0x6e, 0x71, 0x2e, 0x5e, 0x8a, 0x7b, 0xb8, 0xa3, 0x55, 0x98, + 0xb3, 0x1c, 0xc7, 0x0d, 0xac, 0xc0, 0x76, 0x9d, 0x8a, 0x47, 0x76, 0xec, 0xdb, 0x8b, 0x63, 0xac, + 0x39, 0x8b, 0xa2, 0x39, 0x73, 0xa5, 0x18, 0x1c, 0xf7, 0xd4, 0x30, 0x57, 0x61, 0xb1, 0xd4, 0xda, + 0xb6, 0x7c, 0xdf, 0xaa, 0xbb, 0x5e, 0x6c, 0x34, 0x1e, 0x85, 0x7c, 0xcb, 0x6a, 0xb7, 0x6d, 0xa7, + 0x41, 0x87, 0x23, 0xfb, 0x68, 0xa1, 0x3c, 0x75, 0xd0, 0x2d, 0xe6, 0x37, 0x44, 0x19, 0x56, 0x50, + 0xf3, 0x07, 0x19, 0x98, 0x2c, 0x39, 0x56, 0x73, 0xdf, 0xb7, 0x7d, 0xdc, 0x71, 0xd0, 0x87, 0x21, + 0x4f, 0x77, 0x97, 0xba, 0x15, 0x58, 0x62, 0x45, 0xbe, 0x63, 0x99, 0x2f, 0xf6, 0x65, 0x7d, 0xb1, + 0x87, 0xfd, 0x42, 0xb1, 0x97, 0xf7, 0xde, 0xb9, 0x7c, 0x79, 0xfb, 0x06, 0xa9, 0x05, 0x1b, 0x24, + 0xb0, 0xca, 0x48, 0xb4, 0x02, 0xc2, 0x32, 0xac, 0xa8, 0x22, 0x17, 0xc6, 0xfc, 0x36, 0xa9, 0x89, + 0x15, 0xb6, 0x31, 0xe2, 0x4c, 0x0e, 0x45, 0xaf, 0xb6, 0x49, 0xad, 0x3c, 0x25, 0x58, 0x8f, 0xd1, + 0x7f, 0x98, 0x31, 0x42, 0xb7, 0x60, 0xdc, 0x67, 0x7b, 0x8e, 0x58, 0x3c, 0x97, 0xd3, 0x63, 0xc9, + 0xc8, 0x96, 0x67, 0x04, 0xd3, 0x71, 0xfe, 0x1f, 0x0b, 0x76, 0xe6, 0x3f, 0x18, 0x70, 0x42, 0xc3, + 0x2e, 0x79, 0x8d, 0x4e, 0x8b, 0x38, 0x01, 0x7a, 0x08, 0xc6, 0x1c, 0xab, 0x45, 0xc4, 0x42, 0x51, + 0x22, 0x6f, 0x5a, 0x2d, 0x82, 0x19, 0x04, 0x3d, 0x0c, 0xb9, 0x3d, 0xab, 0xd9, 0x21, 0xac, 0x93, + 0x0a, 0xe5, 0x69, 0x81, 0x92, 0xbb, 0x46, 0x0b, 0x31, 0x87, 0xa1, 0x17, 0xa1, 0xc0, 0x7e, 0x9c, + 0xf7, 0xdc, 0x56, 0x4a, 0x4d, 0x13, 0x12, 0x5e, 0x93, 0x64, 0xcb, 0xd3, 0x07, 0xdd, 0x62, 0x41, + 0xfd, 0xc5, 0x21, 0x43, 0xf3, 0x1f, 0x0d, 0x98, 0xd5, 0x1a, 0xb7, 0x6e, 0xfb, 0x01, 0xfa, 0x40, + 0xcf, 0xe4, 0x59, 0x1e, 0x6c, 0xf2, 0xd0, 0xda, 0x6c, 0xea, 0xcc, 0x89, 0x96, 0xe6, 0x65, 0x89, + 0x36, 0x71, 0x1c, 0xc8, 0xd9, 0x01, 0x69, 0xf9, 0x8b, 0x99, 0x87, 0xb2, 0x8f, 0x4e, 0x3e, 0xb6, + 0x96, 0xda, 0x30, 0x86, 0xfd, 0xbb, 0x46, 0xe9, 0x63, 0xce, 0xc6, 0xfc, 0xea, 0x58, 0xa4, 0x85, + 0x74, 0x46, 0x21, 0x17, 0x26, 0x5a, 0x24, 0xf0, 0xec, 0x1a, 0x5f, 0x57, 0x93, 0x8f, 0xad, 0x8e, + 0x26, 0xc5, 0x06, 0x23, 0x16, 0x6e, 0x96, 0xfc, 0xbf, 0x8f, 0x25, 0x17, 0xb4, 0x0b, 0x63, 0x96, + 0xd7, 0x90, 0x6d, 0x3e, 0x9f, 0xce, 0xf8, 0x86, 0x73, 0xae, 0xe4, 0x35, 0x7c, 0xcc, 0x38, 0xa0, + 0xb3, 0x50, 0x08, 0x88, 0xd7, 0xb2, 0x1d, 0x2b, 0xe0, 0xbb, 0x6b, 0xbe, 0x3c, 0x2f, 0xd0, 0x0a, + 0x5b, 0x12, 0x80, 0x43, 0x1c, 0xd4, 0x84, 0xf1, 0xba, 0xb7, 0x8f, 0x3b, 0xce, 0xe2, 0x58, 0x1a, + 0x5d, 0xb1, 0xca, 0x68, 0x85, 0x8b, 0x89, 0xff, 0xc7, 0x82, 0x07, 0xfa, 0x92, 0x01, 0x0b, 0x2d, + 0x62, 0xf9, 0x1d, 0x8f, 0xd0, 0x26, 0x60, 0x12, 0x10, 0x87, 0xee, 0x86, 0x8b, 0x39, 0xc6, 0x1c, + 0x8f, 0x3a, 0x0e, 0xbd, 0x94, 0xcb, 0x0f, 0x0a, 0x51, 0x16, 0x92, 0xa0, 0x38, 0x51, 0x1a, 0xf3, + 0x07, 0x63, 0x30, 0xdf, 0xb3, 0x43, 0xa0, 0x27, 0x20, 0xd7, 0xde, 0xb5, 0x7c, 0xb9, 0xe4, 0xcf, + 0xc8, 0xf9, 0x56, 0xa1, 0x85, 0x77, 0xba, 0xc5, 0x69, 0x59, 0x85, 0x15, 0x60, 0x8e, 0x4c, 0x75, + 0x6a, 0x8b, 0xf8, 0xbe, 0xd5, 0x90, 0xfb, 0x80, 0x36, 0x4d, 0x58, 0x31, 0x96, 0x70, 0xf4, 0x2b, + 0x06, 0x4c, 0xf3, 0x29, 0x83, 0x89, 0xdf, 0x69, 0x06, 0x74, 0xaf, 0xa3, 0xdd, 0x72, 0x29, 0x8d, + 0xe9, 0xc9, 0x49, 0x96, 0x4f, 0x0a, 0xee, 0xd3, 0x7a, 0xa9, 0x8f, 0xa3, 0x7c, 0xd1, 0x75, 0x28, + 0xf8, 0x81, 0xe5, 0x05, 0xa4, 0x5e, 0x0a, 0x98, 0x56, 0x9b, 0x7c, 0xec, 0xff, 0x0e, 0xb6, 0x09, + 0x6c, 0xd9, 0x2d, 0xc2, 0x37, 0x9c, 0xaa, 0x24, 0x80, 0x43, 0x5a, 0xe8, 0x45, 0x00, 0xaf, 0xe3, + 0x54, 0x3b, 0xad, 0x96, 0xe5, 0xed, 0x0b, 0x0d, 0x7e, 0x71, 0xb4, 0xe6, 0x61, 0x45, 0x2f, 0xd4, + 0x59, 0x61, 0x19, 0xd6, 0xf8, 0xa1, 0x97, 0x0d, 0x98, 0xe6, 0x33, 0x51, 0x4a, 0x30, 0x9e, 0xb2, + 0x04, 0xf3, 0xb4, 0x6b, 0x57, 0x75, 0x16, 0x38, 0xca, 0xd1, 0xfc, 0xbb, 0xa8, 0x3e, 0xa9, 0x06, + 0xd4, 0xba, 0x6e, 0xec, 0xa3, 0xf7, 0xc3, 0xfd, 0x7e, 0xa7, 0x56, 0x23, 0xbe, 0xbf, 0xd3, 0x69, + 0xe2, 0x8e, 0x73, 0xd1, 0xf6, 0x03, 0xd7, 0xdb, 0x5f, 0xb7, 0x5b, 0x76, 0xc0, 0x66, 0x5c, 0xae, + 0x7c, 0xfa, 0xa0, 0x5b, 0xbc, 0xbf, 0xda, 0x0f, 0x09, 0xf7, 0xaf, 0x8f, 0x2c, 0x78, 0xa0, 0xe3, + 0xf4, 0x27, 0xcf, 0xad, 0xb7, 0xe2, 0x41, 0xb7, 0xf8, 0xc0, 0xd5, 0xfe, 0x68, 0xf8, 0x30, 0x1a, + 0xe6, 0xbf, 0x1a, 0x30, 0x27, 0xdb, 0xb5, 0x45, 0x5a, 0xed, 0x26, 0xdd, 0x5d, 0x8e, 0xdf, 0x10, + 0x09, 0x22, 0x86, 0x08, 0x4e, 0x47, 0x9d, 0x48, 0xf9, 0xfb, 0x59, 0x23, 0xe6, 0xbf, 0x18, 0xb0, + 0x10, 0x47, 0xbe, 0x07, 0xca, 0xd3, 0x8f, 0x2a, 0xcf, 0xcd, 0x74, 0x5b, 0xdb, 0x47, 0x83, 0xbe, + 0x3a, 0xd6, 0xdb, 0xd6, 0xff, 0xe9, 0x6a, 0x34, 0xd4, 0x8a, 0xd9, 0x9f, 0xa5, 0x56, 0x1c, 0x7b, + 0x43, 0x69, 0xc5, 0xdf, 0x1f, 0x83, 0xa9, 0x92, 0x13, 0xd8, 0xa5, 0x9d, 0x1d, 0xdb, 0xb1, 0x83, + 0x7d, 0xf4, 0xa9, 0x0c, 0x9c, 0x6d, 0x7b, 0x64, 0x87, 0x78, 0x1e, 0xa9, 0xaf, 0x76, 0x3c, 0xdb, + 0x69, 0x54, 0x6b, 0xbb, 0xa4, 0xde, 0x69, 0xda, 0x4e, 0x63, 0xad, 0xe1, 0xb8, 0xaa, 0xf8, 0xdc, + 0x6d, 0x52, 0xeb, 0xb0, 0x26, 0xf1, 0x45, 0xd1, 0x1a, 0xad, 0x49, 0x95, 0xe1, 0x98, 0x96, 0x1f, + 0x3f, 0xe8, 0x16, 0xcf, 0x0e, 0x59, 0x09, 0x0f, 0xdb, 0x34, 0xf4, 0xc9, 0x0c, 0x2c, 0x7b, 0xe4, + 0x23, 0x1d, 0x7b, 0xf0, 0xde, 0xe0, 0xbb, 0x56, 0x73, 0x44, 0xf5, 0x33, 0x14, 0xcf, 0xf2, 0x63, + 0x07, 0xdd, 0xe2, 0x90, 0x75, 0xf0, 0x90, 0xed, 0x32, 0xbf, 0x9e, 0x81, 0x93, 0xa5, 0x76, 0x7b, + 0x83, 0xf8, 0xbb, 0xb1, 0x43, 0xed, 0x67, 0x0c, 0x98, 0xd9, 0xb3, 0xbd, 0xa0, 0x63, 0x35, 0xa5, + 0x13, 0x80, 0x4f, 0x89, 0xea, 0x88, 0xcb, 0x99, 0x73, 0xbb, 0x16, 0x21, 0x5d, 0x46, 0x07, 0xdd, + 0xe2, 0x4c, 0xb4, 0x0c, 0xc7, 0xd8, 0xa3, 0xdf, 0x34, 0x60, 0x4e, 0x14, 0x6d, 0xba, 0x75, 0xa2, + 0x7b, 0x8e, 0xae, 0xa6, 0x29, 0x93, 0x22, 0xce, 0x5d, 0x0c, 0xf1, 0x52, 0xdc, 0x23, 0x84, 0xf9, + 0xef, 0x19, 0x38, 0xd5, 0x87, 0x06, 0xfa, 0x3d, 0x03, 0x16, 0xb8, 0xbb, 0x49, 0x03, 0x61, 0xb2, + 0x23, 0x7a, 0xf3, 0x7d, 0x69, 0x4b, 0x8e, 0xe9, 0x5a, 0x20, 0x4e, 0x8d, 0x94, 0x17, 0xe9, 0xb6, + 0xb1, 0x92, 0xc0, 0x1a, 0x27, 0x0a, 0xc4, 0x24, 0xe5, 0x0e, 0xa8, 0x98, 0xa4, 0x99, 0x7b, 0x22, + 0x69, 0x35, 0x81, 0x35, 0x4e, 0x14, 0xc8, 0xfc, 0x45, 0x78, 0xe0, 0x10, 0x72, 0x77, 0x3f, 0xf1, + 0x9b, 0xcf, 0xab, 0x59, 0x1f, 0x9d, 0x73, 0x03, 0x38, 0x0b, 0x4c, 0x18, 0xf7, 0xdc, 0x4e, 0x40, + 0xb8, 0x76, 0x2b, 0x94, 0x81, 0xea, 0x09, 0xcc, 0x4a, 0xb0, 0x80, 0x98, 0x5f, 0x37, 0x20, 0x3f, + 0x84, 0xff, 0xa1, 0x18, 0xf5, 0x3f, 0x14, 0x7a, 0x7c, 0x0f, 0x41, 0xaf, 0xef, 0xe1, 0xc2, 0x68, + 0xa3, 0x31, 0x88, 0xcf, 0xe1, 0xc7, 0x06, 0xcc, 0xf7, 0xf8, 0x28, 0xd0, 0x2e, 0x2c, 0xb4, 0xdd, + 0xba, 0xb4, 0x2f, 0x2e, 0x5a, 0xfe, 0x2e, 0x83, 0x89, 0xe6, 0x3d, 0x41, 0x47, 0xb2, 0x92, 0x00, + 0xbf, 0xd3, 0x2d, 0x2e, 0x2a, 0x22, 0x31, 0x04, 0x9c, 0x48, 0x11, 0xb5, 0x21, 0xbf, 0x63, 0x93, + 0x66, 0x3d, 0x9c, 0x82, 0x23, 0x5a, 0x12, 0xe7, 0x05, 0x35, 0xee, 0x9e, 0x93, 0xff, 0xb0, 0xe2, + 0x62, 0x5e, 0x81, 0x99, 0xa8, 0xb3, 0x76, 0x80, 0xc1, 0x3b, 0x0d, 0x59, 0xcb, 0x73, 0xc4, 0xd0, + 0x4d, 0x0a, 0x84, 0x6c, 0x09, 0x6f, 0x62, 0x5a, 0x6e, 0xfe, 0x74, 0x0c, 0x66, 0xcb, 0xcd, 0x0e, + 0xb9, 0xe0, 0x11, 0x22, 0xcf, 0xa7, 0x25, 0x98, 0x6d, 0x7b, 0x64, 0xcf, 0x26, 0xb7, 0xaa, 0xa4, + 0x49, 0x6a, 0x81, 0xeb, 0x09, 0xfa, 0xa7, 0x44, 0xf5, 0xd9, 0x4a, 0x14, 0x8c, 0xe3, 0xf8, 0xe8, + 0x19, 0x98, 0xb1, 0x6a, 0x81, 0xbd, 0x47, 0x14, 0x05, 0x2e, 0xc0, 0x9b, 0x04, 0x85, 0x99, 0x52, + 0x04, 0x8a, 0x63, 0xd8, 0xe8, 0x03, 0xb0, 0xe8, 0xd7, 0xac, 0x26, 0xb9, 0xda, 0x16, 0xac, 0x56, + 0x76, 0x49, 0xed, 0x66, 0xc5, 0xb5, 0x9d, 0x40, 0x78, 0x23, 0x1e, 0x12, 0x94, 0x16, 0xab, 0x7d, + 0xf0, 0x70, 0x5f, 0x0a, 0xe8, 0x4f, 0x0d, 0x38, 0xdd, 0xf6, 0x48, 0xc5, 0x73, 0x5b, 0x2e, 0x55, + 0x33, 0x3d, 0x47, 0x74, 0x71, 0x54, 0xbd, 0x36, 0xa2, 0x3e, 0xe5, 0x25, 0xbd, 0x2e, 0xc2, 0x37, + 0x1f, 0x74, 0x8b, 0xa7, 0x2b, 0x87, 0x09, 0x80, 0x0f, 0x97, 0x0f, 0xfd, 0xb9, 0x01, 0x67, 0xda, + 0xae, 0x1f, 0x1c, 0xd2, 0x84, 0xdc, 0xb1, 0x36, 0xc1, 0x3c, 0xe8, 0x16, 0xcf, 0x54, 0x0e, 0x95, + 0x00, 0xdf, 0x45, 0x42, 0xf3, 0x60, 0x12, 0xe6, 0xb5, 0xb9, 0x27, 0xce, 0xaf, 0x4f, 0xc3, 0xb4, + 0x9c, 0x0c, 0xa1, 0x5a, 0x2f, 0x84, 0xfe, 0x86, 0x92, 0x0e, 0xc4, 0x51, 0x5c, 0x3a, 0xef, 0xd4, + 0x54, 0xe4, 0xb5, 0x63, 0xf3, 0xae, 0x12, 0x81, 0xe2, 0x18, 0x36, 0x5a, 0x83, 0x13, 0xa2, 0x04, + 0x93, 0x76, 0xd3, 0xae, 0x59, 0x2b, 0x6e, 0x47, 0x4c, 0xb9, 0x5c, 0xf9, 0xd4, 0x41, 0xb7, 0x78, + 0xa2, 0xd2, 0x0b, 0xc6, 0x49, 0x75, 0xd0, 0x3a, 0x2c, 0x58, 0x9d, 0xc0, 0x55, 0xed, 0x3f, 0xe7, + 0x50, 0x4d, 0x51, 0x67, 0x53, 0x2b, 0xcf, 0x55, 0x4a, 0x29, 0x01, 0x8e, 0x13, 0x6b, 0xa1, 0x4a, + 0x8c, 0x5a, 0x95, 0xd4, 0x5c, 0xa7, 0xce, 0x47, 0x39, 0x17, 0x5a, 0xe1, 0xa5, 0x04, 0x1c, 0x9c, + 0x58, 0x13, 0x35, 0x61, 0xa6, 0x65, 0xdd, 0xbe, 0xea, 0x58, 0x7b, 0x96, 0xdd, 0xa4, 0x4c, 0x84, + 0x0f, 0xa3, 0xff, 0xc1, 0xba, 0x13, 0xd8, 0xcd, 0x65, 0x7e, 0x9d, 0xb7, 0xbc, 0xe6, 0x04, 0x97, + 0xbd, 0x6a, 0x40, 0xad, 0x35, 0x6e, 0x1c, 0x6d, 0x44, 0x68, 0xe1, 0x18, 0x6d, 0x74, 0x19, 0x4e, + 0xb2, 0xe5, 0xb8, 0xea, 0xde, 0x72, 0x56, 0x49, 0xd3, 0xda, 0x97, 0x0d, 0x98, 0x60, 0x0d, 0xb8, + 0xff, 0xa0, 0x5b, 0x3c, 0x59, 0x4d, 0x42, 0xc0, 0xc9, 0xf5, 0x90, 0x05, 0x0f, 0x44, 0x01, 0x98, + 0xec, 0xd9, 0xbe, 0xed, 0x3a, 0xdc, 0x13, 0x91, 0x0f, 0x3d, 0x11, 0xd5, 0xfe, 0x68, 0xf8, 0x30, + 0x1a, 0xe8, 0xb7, 0x0d, 0x58, 0x48, 0x5a, 0x86, 0x8b, 0x85, 0x34, 0x2e, 0x2b, 0x62, 0x4b, 0x8b, + 0xcf, 0x88, 0xc4, 0x4d, 0x21, 0x51, 0x08, 0xf4, 0x92, 0x01, 0x53, 0x96, 0x76, 0x8a, 0x5a, 0x04, + 0x26, 0xd5, 0xa5, 0x51, 0xcf, 0xf2, 0x21, 0xc5, 0xf2, 0xdc, 0x41, 0xb7, 0x18, 0x39, 0xa9, 0xe1, + 0x08, 0x47, 0xf4, 0x3b, 0x06, 0x9c, 0x4c, 0x5c, 0xe3, 0x8b, 0x93, 0xc7, 0xd1, 0x43, 0x6c, 0x92, + 0x24, 0xef, 0x39, 0xc9, 0x62, 0xa0, 0xd7, 0x0c, 0xa5, 0xca, 0x36, 0xa4, 0x37, 0x65, 0x8a, 0x89, + 0x76, 0x65, 0xc4, 0x83, 0x63, 0x68, 0x10, 0x48, 0xc2, 0xe5, 0x13, 0x9a, 0x66, 0x94, 0x85, 0x38, + 0xce, 0x1e, 0x7d, 0xda, 0x90, 0xaa, 0x51, 0x49, 0x34, 0x7d, 0x5c, 0x12, 0xa1, 0x50, 0xd3, 0x2a, + 0x81, 0x62, 0xcc, 0xd1, 0x07, 0x61, 0xc9, 0xda, 0x76, 0xbd, 0x20, 0x71, 0xf1, 0x2d, 0xce, 0xb0, + 0x65, 0x74, 0xe6, 0xa0, 0x5b, 0x5c, 0x2a, 0xf5, 0xc5, 0xc2, 0x87, 0x50, 0x30, 0xbf, 0x92, 0x83, + 0x29, 0x6e, 0xe4, 0x0b, 0xd5, 0xf5, 0x35, 0x03, 0x1e, 0xac, 0x75, 0x3c, 0x8f, 0x38, 0x41, 0x35, + 0x20, 0xed, 0x5e, 0xc5, 0x65, 0x1c, 0xab, 0xe2, 0x7a, 0xe8, 0xa0, 0x5b, 0x7c, 0x70, 0xe5, 0x10, + 0xfe, 0xf8, 0x50, 0xe9, 0xd0, 0x5f, 0x1b, 0x60, 0x0a, 0x84, 0xb2, 0x55, 0xbb, 0xd9, 0xf0, 0xdc, + 0x8e, 0x53, 0xef, 0x6d, 0x44, 0xe6, 0x58, 0x1b, 0xf1, 0xc8, 0x41, 0xb7, 0x68, 0xae, 0xdc, 0x55, + 0x0a, 0x3c, 0x80, 0xa4, 0xe8, 0x02, 0xcc, 0x0b, 0xac, 0x73, 0xb7, 0xdb, 0xc4, 0xb3, 0xa9, 0x39, + 0x2d, 0xee, 0xd3, 0xc3, 0x10, 0x85, 0x38, 0x02, 0xee, 0xad, 0x83, 0x7c, 0x98, 0xb8, 0x45, 0xec, + 0xc6, 0x6e, 0x20, 0xcd, 0xa7, 0x11, 0xe3, 0x12, 0xc4, 0x81, 0xff, 0x3a, 0xa7, 0x59, 0x9e, 0x3c, + 0xe8, 0x16, 0x27, 0xc4, 0x1f, 0x2c, 0x39, 0xa1, 0x4d, 0x98, 0xe1, 0x47, 0xb0, 0x8a, 0xed, 0x34, + 0x2a, 0xae, 0xc3, 0x6f, 0xf3, 0x0b, 0xe5, 0x47, 0xa4, 0xc2, 0xaf, 0x46, 0xa0, 0x77, 0xba, 0xc5, + 0x29, 0xf9, 0x7b, 0x6b, 0xbf, 0x4d, 0x70, 0xac, 0xb6, 0xf9, 0xad, 0x71, 0x00, 0x39, 0x5d, 0x49, + 0x1b, 0xfd, 0x3f, 0x28, 0xf8, 0x24, 0xe0, 0x5c, 0x85, 0xf3, 0x9c, 0xdf, 0x49, 0xc8, 0x42, 0x1c, + 0xc2, 0xd1, 0x4d, 0xc8, 0xb5, 0xad, 0x8e, 0x4f, 0xc4, 0xe0, 0x5f, 0x4a, 0x65, 0xf0, 0x2b, 0x94, + 0x22, 0x3f, 0x73, 0xb1, 0x9f, 0x98, 0xf3, 0x40, 0x9f, 0x30, 0x00, 0x48, 0x74, 0xc0, 0x46, 0xf6, + 0x7d, 0x08, 0x96, 0xe1, 0x98, 0xd2, 0x3e, 0x28, 0xcf, 0x1c, 0x74, 0x8b, 0xa0, 0x0d, 0xbd, 0xc6, + 0x16, 0xdd, 0x82, 0xbc, 0x25, 0xf7, 0xfc, 0xb1, 0xe3, 0xd8, 0xf3, 0xd9, 0x51, 0x48, 0x4d, 0x5a, + 0xc5, 0x0c, 0x7d, 0xd2, 0x80, 0x19, 0x9f, 0x04, 0x62, 0xa8, 0xe8, 0xce, 0x23, 0x0c, 0xde, 0x11, + 0x27, 0x5d, 0x35, 0x42, 0x93, 0xef, 0xa0, 0xd1, 0x32, 0x1c, 0xe3, 0x2b, 0x45, 0xb9, 0x48, 0xac, + 0x3a, 0xf1, 0xd8, 0x49, 0x5b, 0x58, 0x52, 0xa3, 0x8b, 0xa2, 0xd1, 0x54, 0xa2, 0x68, 0x65, 0x38, + 0xc6, 0x57, 0x8a, 0xb2, 0x61, 0x7b, 0x9e, 0x2b, 0x44, 0xc9, 0xa7, 0x24, 0x8a, 0x46, 0x53, 0x89, + 0xa2, 0x95, 0xe1, 0x18, 0x5f, 0xf3, 0x6f, 0xa6, 0x60, 0x46, 0x2e, 0xa4, 0xd0, 0xb2, 0xe7, 0x8e, + 0x9d, 0x3e, 0x96, 0xfd, 0x8a, 0x0e, 0xc4, 0x51, 0x5c, 0x5a, 0x99, 0x2f, 0xd5, 0xa8, 0x61, 0xaf, + 0x2a, 0x57, 0x75, 0x20, 0x8e, 0xe2, 0xa2, 0x16, 0xe4, 0xfc, 0x80, 0xb4, 0xe5, 0x3d, 0xe8, 0x88, + 0xd7, 0x74, 0xe1, 0xfe, 0x10, 0xde, 0x74, 0xd0, 0x7f, 0x3e, 0xe6, 0x5c, 0x98, 0x6f, 0x32, 0x88, + 0xb8, 0x2b, 0xc5, 0xe2, 0x48, 0x67, 0x7d, 0x46, 0x3d, 0xa1, 0x7c, 0x34, 0xa2, 0x65, 0x38, 0xc6, + 0x3e, 0xc1, 0xd8, 0xcf, 0x1d, 0xa3, 0xb1, 0xff, 0x1c, 0xe4, 0x5b, 0xd6, 0xed, 0x6a, 0xc7, 0x6b, + 0x1c, 0xfd, 0x50, 0x21, 0x42, 0x94, 0x38, 0x15, 0xac, 0xe8, 0xa1, 0x97, 0x0d, 0x6d, 0xcb, 0x99, + 0x60, 0xc4, 0xaf, 0xa7, 0xbb, 0xe5, 0x28, 0x5d, 0xd9, 0x77, 0xf3, 0xe9, 0x31, 0xbd, 0xf3, 0xf7, + 0xdc, 0xf4, 0xa6, 0x66, 0x24, 0x5f, 0x20, 0xca, 0x8c, 0x2c, 0x1c, 0xab, 0x19, 0xb9, 0x12, 0x61, + 0x86, 0x63, 0xcc, 0x99, 0x3c, 0x7c, 0xcd, 0x29, 0x79, 0xe0, 0x58, 0xe5, 0xa9, 0x46, 0x98, 0xe1, + 0x18, 0xf3, 0xfe, 0xe7, 0xcd, 0xc9, 0xe3, 0x39, 0x6f, 0x4e, 0xa5, 0x70, 0xde, 0x3c, 0xdc, 0x14, + 0x9f, 0x1e, 0xd5, 0x14, 0x47, 0x97, 0x00, 0xd5, 0xf7, 0x1d, 0xab, 0x65, 0xd7, 0xc4, 0x66, 0xc9, + 0xd4, 0xe6, 0x0c, 0xf3, 0x47, 0x2c, 0x89, 0x8d, 0x0c, 0xad, 0xf6, 0x60, 0xe0, 0x84, 0x5a, 0x28, + 0x80, 0x7c, 0x5b, 0x5a, 0x5c, 0xb3, 0x69, 0xcc, 0x7e, 0x69, 0x81, 0xf1, 0xab, 0x72, 0xba, 0xf0, + 0x64, 0x09, 0x56, 0x9c, 0xcc, 0xff, 0x34, 0x60, 0x6e, 0xa5, 0xe9, 0x76, 0xea, 0xd7, 0xad, 0xa0, + 0xb6, 0xcb, 0xef, 0x75, 0xd1, 0x33, 0x90, 0xb7, 0x9d, 0x80, 0x78, 0x7b, 0x56, 0x53, 0x68, 0x14, + 0x53, 0x5e, 0x7d, 0xaf, 0x89, 0xf2, 0x3b, 0xdd, 0xe2, 0xcc, 0x6a, 0xc7, 0x63, 0x01, 0x93, 0x7c, + 0x7f, 0xc1, 0xaa, 0x0e, 0xfa, 0xa2, 0x01, 0xf3, 0xfc, 0x66, 0x78, 0xd5, 0x0a, 0xac, 0x2b, 0x1d, + 0xe2, 0xd9, 0x44, 0xde, 0x0d, 0x8f, 0xb8, 0xb5, 0xc4, 0x65, 0x95, 0x0c, 0xf6, 0x43, 0xd3, 0x7a, + 0x23, 0xce, 0x19, 0xf7, 0x0a, 0x63, 0x7e, 0x36, 0x0b, 0xf7, 0xf7, 0xa5, 0x85, 0x96, 0x20, 0x63, + 0xd7, 0x45, 0xd3, 0x41, 0xd0, 0xcd, 0xac, 0xd5, 0x71, 0xc6, 0xae, 0xa3, 0x65, 0x66, 0x25, 0x7a, + 0xc4, 0xf7, 0xe5, 0x35, 0x61, 0x41, 0x19, 0x74, 0xa2, 0x14, 0x6b, 0x18, 0xa8, 0x08, 0xb9, 0xa6, + 0xb5, 0x4d, 0x9a, 0xe2, 0x04, 0xc0, 0xec, 0xce, 0x75, 0x5a, 0x80, 0x79, 0x39, 0xfa, 0x65, 0x03, + 0x80, 0x0b, 0x48, 0xcf, 0x0f, 0x42, 0xaf, 0xe1, 0x74, 0xbb, 0x89, 0x52, 0xe6, 0x52, 0x86, 0xff, + 0xb1, 0xc6, 0x15, 0x6d, 0xc1, 0x38, 0x35, 0x41, 0xdd, 0xfa, 0x91, 0xd5, 0x18, 0xbb, 0x16, 0xa9, + 0x30, 0x1a, 0x58, 0xd0, 0xa2, 0x7d, 0xe5, 0x91, 0xa0, 0xe3, 0x39, 0xb4, 0x6b, 0x99, 0xe2, 0xca, + 0x73, 0x29, 0xb0, 0x2a, 0xc5, 0x1a, 0x86, 0xf9, 0x27, 0x19, 0x58, 0x48, 0x12, 0x9d, 0xea, 0x87, + 0x71, 0x2e, 0xad, 0x38, 0xcc, 0xbe, 0x37, 0xfd, 0xfe, 0x11, 0x41, 0x0e, 0x2a, 0x14, 0x40, 0x84, + 0x61, 0x09, 0xbe, 0xe8, 0xbd, 0xaa, 0x87, 0x32, 0x47, 0xec, 0x21, 0x45, 0x39, 0xd6, 0x4b, 0x0f, + 0xc1, 0x98, 0x4f, 0x47, 0x3e, 0x1b, 0xbd, 0x72, 0x60, 0x63, 0xc4, 0x20, 0x14, 0xa3, 0xe3, 0xd8, + 0x81, 0x88, 0x62, 0x56, 0x18, 0x57, 0x1d, 0x3b, 0xc0, 0x0c, 0x62, 0x7e, 0x3e, 0x03, 0x4b, 0xfd, + 0x1b, 0x85, 0x3e, 0x6f, 0x00, 0xd4, 0xe9, 0x01, 0x83, 0x4e, 0x49, 0x19, 0x14, 0x62, 0x1d, 0x57, + 0x1f, 0xae, 0x4a, 0x4e, 0x61, 0x84, 0x90, 0x2a, 0xf2, 0xb1, 0x26, 0x08, 0x7a, 0x4c, 0x4e, 0xfd, + 0x4d, 0xab, 0x25, 0x0d, 0x50, 0x55, 0x67, 0x43, 0x41, 0xb0, 0x86, 0x45, 0x4f, 0x90, 0x8e, 0xd5, + 0x22, 0x7e, 0xdb, 0x52, 0x61, 0xea, 0xec, 0x04, 0xb9, 0x29, 0x0b, 0x71, 0x08, 0x37, 0x9b, 0xf0, + 0xf0, 0x00, 0x72, 0xa6, 0x14, 0x32, 0x6c, 0xfe, 0x87, 0x01, 0xa7, 0x56, 0x9a, 0x1d, 0x3f, 0x20, + 0xde, 0xff, 0x9a, 0x80, 0xab, 0xff, 0x32, 0xe0, 0x81, 0x3e, 0x6d, 0xbe, 0x07, 0x71, 0x57, 0x2f, + 0x44, 0xe3, 0xae, 0xae, 0x8e, 0x3a, 0xa5, 0x13, 0xdb, 0xd1, 0x27, 0xfc, 0x2a, 0x80, 0x69, 0xba, + 0x6b, 0xd5, 0xdd, 0x46, 0x4a, 0x7a, 0xf3, 0x61, 0xc8, 0x7d, 0x84, 0xea, 0x9f, 0xf8, 0x1c, 0x63, + 0x4a, 0x09, 0x73, 0x98, 0xf9, 0x1e, 0x10, 0x41, 0x4a, 0xb1, 0xc5, 0x63, 0x0c, 0xb2, 0x78, 0xcc, + 0xbf, 0xcf, 0x80, 0xe6, 0x79, 0xb8, 0x07, 0x93, 0xd2, 0x89, 0x4c, 0xca, 0x11, 0x4f, 0xcd, 0x9a, + 0x1f, 0xa5, 0xdf, 0x6b, 0x84, 0xbd, 0xd8, 0x6b, 0x84, 0xcd, 0xd4, 0x38, 0x1e, 0xfe, 0x18, 0xe1, + 0x7b, 0x06, 0x3c, 0x10, 0x22, 0xf7, 0x3a, 0x05, 0xef, 0xbe, 0xc3, 0x3c, 0x09, 0x93, 0x56, 0x58, + 0x4d, 0xcc, 0x01, 0xf5, 0x00, 0x47, 0xa3, 0x88, 0x75, 0xbc, 0x30, 0xf6, 0x39, 0x7b, 0xc4, 0xd8, + 0xe7, 0xb1, 0xc3, 0x63, 0x9f, 0xcd, 0x9f, 0x64, 0xe0, 0x74, 0x6f, 0xcb, 0xe4, 0xda, 0x18, 0xec, + 0xce, 0xfc, 0x29, 0x98, 0x0a, 0x44, 0x05, 0x6d, 0xa7, 0x57, 0xcf, 0xc7, 0xb6, 0x34, 0x18, 0x8e, + 0x60, 0xd2, 0x9a, 0x35, 0xbe, 0x2a, 0xab, 0x35, 0xb7, 0x2d, 0x23, 0xe7, 0x55, 0xcd, 0x15, 0x0d, + 0x86, 0x23, 0x98, 0x2a, 0x26, 0x71, 0xec, 0xd8, 0x63, 0x12, 0xab, 0x70, 0x52, 0x46, 0x61, 0x9d, + 0x77, 0xbd, 0x15, 0xb7, 0xd5, 0x6e, 0x12, 0x11, 0x3b, 0x4f, 0x85, 0x3d, 0x2d, 0xaa, 0x9c, 0xc4, + 0x49, 0x48, 0x38, 0xb9, 0xae, 0xf9, 0xbd, 0x2c, 0x9c, 0x08, 0xbb, 0x7d, 0xc5, 0x75, 0xea, 0x36, + 0x8b, 0x65, 0x7b, 0x1a, 0xc6, 0x82, 0xfd, 0xb6, 0xec, 0xec, 0xff, 0x23, 0xc5, 0xd9, 0xda, 0x6f, + 0xd3, 0xd1, 0x3e, 0x95, 0x50, 0x85, 0xb9, 0x65, 0x59, 0x25, 0xb4, 0xae, 0x56, 0x07, 0x1f, 0x81, + 0x27, 0xa2, 0xb3, 0xf9, 0x4e, 0xb7, 0x98, 0xf0, 0x7a, 0x72, 0x59, 0x51, 0x8a, 0xce, 0x79, 0x74, + 0x03, 0x66, 0x9a, 0x96, 0x1f, 0x5c, 0x6d, 0xd7, 0xad, 0x80, 0x6c, 0xd9, 0x2d, 0x22, 0xd6, 0xdc, + 0x30, 0x01, 0xe9, 0xea, 0x1e, 0x79, 0x3d, 0x42, 0x09, 0xc7, 0x28, 0xa3, 0x3d, 0x40, 0xb4, 0x64, + 0xcb, 0xb3, 0x1c, 0x9f, 0xb7, 0x8a, 0xf2, 0x1b, 0x3e, 0x00, 0x5e, 0x1d, 0xcb, 0xd6, 0x7b, 0xa8, + 0xe1, 0x04, 0x0e, 0xe8, 0x11, 0x18, 0xf7, 0x88, 0xe5, 0x8b, 0xc1, 0x2c, 0x84, 0xeb, 0x1f, 0xb3, + 0x52, 0x2c, 0xa0, 0xfa, 0x82, 0x1a, 0xbf, 0xcb, 0x82, 0xfa, 0xa1, 0x01, 0x33, 0xe1, 0x30, 0xdd, + 0x03, 0x25, 0xd9, 0x8a, 0x2a, 0xc9, 0x8b, 0x69, 0x6d, 0x89, 0x7d, 0xf4, 0xe2, 0x5f, 0x8c, 0xeb, + 0xed, 0x63, 0x01, 0xc9, 0x1f, 0x85, 0x82, 0x5c, 0xd5, 0xd2, 0xfa, 0x1c, 0xf1, 0x74, 0x1b, 0xb1, + 0x4b, 0xb4, 0x87, 0x34, 0x82, 0x09, 0x0e, 0xf9, 0x51, 0xb5, 0x5c, 0x17, 0x2a, 0x57, 0x4c, 0x7b, + 0xa5, 0x96, 0xa5, 0x2a, 0x4e, 0x52, 0xcb, 0xb2, 0x0e, 0xba, 0x0a, 0xa7, 0xda, 0x9e, 0xcb, 0x1e, + 0x57, 0xae, 0x12, 0xab, 0xde, 0xb4, 0x1d, 0x22, 0x5d, 0x08, 0x3c, 0x8c, 0xe1, 0x81, 0x83, 0x6e, + 0xf1, 0x54, 0x25, 0x19, 0x05, 0xf7, 0xab, 0x1b, 0x7d, 0x10, 0x34, 0x36, 0xc0, 0x83, 0xa0, 0x5f, + 0x55, 0x8e, 0x3a, 0xe2, 0x8b, 0x67, 0x39, 0xef, 0x4f, 0x6b, 0x28, 0x13, 0xb6, 0xf5, 0x70, 0x4a, + 0x95, 0x04, 0x53, 0xac, 0xd8, 0xf7, 0xf7, 0x06, 0x8d, 0x1f, 0xd1, 0x1b, 0x14, 0xc6, 0x75, 0x4f, + 0xfc, 0x2c, 0xe3, 0xba, 0xf3, 0x6f, 0xa8, 0xb8, 0xee, 0x57, 0x72, 0x30, 0x17, 0xb7, 0x40, 0x8e, + 0xff, 0xb1, 0xd3, 0x6f, 0x18, 0x30, 0x27, 0x57, 0x0f, 0xe7, 0x49, 0xa4, 0x9f, 0x7f, 0x3d, 0xa5, + 0x45, 0xcb, 0x6d, 0x29, 0xf5, 0x1c, 0x77, 0x2b, 0xc6, 0x0d, 0xf7, 0xf0, 0x47, 0xcf, 0xc3, 0xa4, + 0x72, 0x87, 0x1f, 0xe9, 0xe5, 0xd3, 0x2c, 0xb3, 0xa2, 0x42, 0x12, 0x58, 0xa7, 0x87, 0x5e, 0x31, + 0x00, 0x6a, 0x52, 0xcd, 0xc9, 0xd5, 0x75, 0x25, 0xad, 0xd5, 0xa5, 0x14, 0x68, 0x68, 0x2c, 0xab, + 0x22, 0x1f, 0x6b, 0x8c, 0xd1, 0x67, 0x99, 0x23, 0x5c, 0x59, 0x77, 0x74, 0x3d, 0x65, 0x47, 0x0f, + 0xc5, 0x3d, 0xc4, 0x30, 0x0d, 0x4d, 0x29, 0x0d, 0xe4, 0xe3, 0x88, 0x10, 0xe6, 0xd3, 0xa0, 0x82, + 0x27, 0xe9, 0xb6, 0xc5, 0xc2, 0x27, 0x2b, 0x56, 0xb0, 0x2b, 0xa6, 0xa0, 0xda, 0xb6, 0xce, 0x4b, + 0x00, 0x0e, 0x71, 0xcc, 0x0f, 0xc3, 0xcc, 0x05, 0xcf, 0x6a, 0xef, 0xda, 0xcc, 0xe1, 0x4c, 0xcf, + 0x49, 0x6f, 0x85, 0x09, 0xab, 0x5e, 0x4f, 0x7a, 0xcc, 0x5e, 0xe2, 0xc5, 0x58, 0xc2, 0x07, 0x3b, + 0x12, 0x7d, 0xcb, 0x00, 0x14, 0x5e, 0xda, 0xd9, 0x4e, 0x63, 0x83, 0x9e, 0xf6, 0xe9, 0xf9, 0x68, + 0x97, 0x95, 0x26, 0x9d, 0x8f, 0x2e, 0x2a, 0x08, 0xd6, 0xb0, 0xd0, 0x8b, 0x30, 0xc9, 0xff, 0x5d, + 0x53, 0x87, 0xfd, 0x91, 0x9f, 0xc2, 0x72, 0x85, 0xc2, 0x64, 0xe2, 0xb3, 0xf0, 0x62, 0xc8, 0x01, + 0xeb, 0xec, 0xcc, 0x6f, 0x18, 0xb0, 0xb0, 0xe6, 0x07, 0xb6, 0xbb, 0x4a, 0xfc, 0x80, 0x6e, 0xfa, + 0x74, 0x6b, 0xe8, 0x34, 0x07, 0x89, 0x52, 0x5e, 0x85, 0x39, 0x71, 0xbd, 0xd7, 0xd9, 0xf6, 0x49, + 0xa0, 0x59, 0xd9, 0x6a, 0x95, 0xad, 0xc4, 0xe0, 0xb8, 0xa7, 0x06, 0xa5, 0x22, 0xee, 0xf9, 0x42, + 0x2a, 0xd9, 0x28, 0x95, 0x6a, 0x0c, 0x8e, 0x7b, 0x6a, 0x98, 0xdf, 0xc9, 0xc2, 0x09, 0xd6, 0x8c, + 0xd8, 0x0b, 0x83, 0x4f, 0xf7, 0x7b, 0x61, 0x30, 0xe2, 0x42, 0x63, 0xbc, 0x8e, 0xf0, 0xbe, 0xe0, + 0xd7, 0x0d, 0x98, 0xad, 0x47, 0x7b, 0x3a, 0x1d, 0xe7, 0x49, 0xd2, 0x18, 0xf2, 0x68, 0xa6, 0x58, + 0x21, 0x8e, 0xf3, 0x47, 0x9f, 0x33, 0x60, 0x36, 0x2a, 0xa6, 0xdc, 0x7b, 0x8f, 0xa1, 0x93, 0x54, + 0xf8, 0x71, 0xb4, 0xdc, 0xc7, 0x71, 0x11, 0xcc, 0xbf, 0x35, 0xc4, 0x90, 0x1e, 0x47, 0xf8, 0x3c, + 0xba, 0x05, 0x85, 0xa0, 0xe9, 0xf3, 0x42, 0xd1, 0xda, 0x11, 0xcf, 0x6b, 0x5b, 0xeb, 0x55, 0x7e, + 0xb3, 0x1e, 0x9a, 0x54, 0xa2, 0x84, 0x9a, 0x86, 0x92, 0x97, 0xf9, 0x65, 0x03, 0x0a, 0x97, 0xdc, + 0x6d, 0xb1, 0x2f, 0x7d, 0x30, 0x05, 0x6f, 0x88, 0x32, 0x9a, 0xd4, 0x45, 0x5a, 0x68, 0x87, 0x3f, + 0x13, 0xf1, 0x85, 0x3c, 0xa8, 0xd1, 0x5e, 0x66, 0xd9, 0x6c, 0x28, 0xa9, 0x4b, 0xee, 0x76, 0x5f, + 0x57, 0xdb, 0xef, 0xe6, 0x60, 0xfa, 0x59, 0x6b, 0x9f, 0x38, 0x81, 0x35, 0xfc, 0x4e, 0xfa, 0x24, + 0x4c, 0x5a, 0x6d, 0x16, 0x4d, 0xab, 0x19, 0xc2, 0xa1, 0x7b, 0x21, 0x04, 0x61, 0x1d, 0x2f, 0xdc, + 0x57, 0x78, 0x72, 0x8d, 0xa4, 0x1d, 0x61, 0x25, 0x06, 0xc7, 0x3d, 0x35, 0xd0, 0x25, 0x40, 0xe2, + 0xa9, 0x60, 0xa9, 0x56, 0x73, 0x3b, 0x0e, 0xdf, 0x59, 0xb8, 0xe7, 0x41, 0x9d, 0xc8, 0x36, 0x7a, + 0x30, 0x70, 0x42, 0x2d, 0xf4, 0x01, 0x58, 0xac, 0x31, 0xca, 0xc2, 0x3e, 0xd7, 0x29, 0xf2, 0x33, + 0x9a, 0x8a, 0x64, 0x5f, 0xe9, 0x83, 0x87, 0xfb, 0x52, 0xa0, 0x92, 0xfa, 0x81, 0xeb, 0x59, 0x0d, + 0xa2, 0xd3, 0x1d, 0x8f, 0x4a, 0x5a, 0xed, 0xc1, 0xc0, 0x09, 0xb5, 0xd0, 0xc7, 0xa1, 0x10, 0xec, + 0x7a, 0xc4, 0xdf, 0x75, 0x9b, 0x75, 0x71, 0xb3, 0x3e, 0xa2, 0x3b, 0x4a, 0x8c, 0xfe, 0x96, 0xa4, + 0xaa, 0x4d, 0x6f, 0x59, 0x84, 0x43, 0x9e, 0xc8, 0x83, 0x71, 0xbf, 0xe6, 0xb6, 0x89, 0x2f, 0xec, + 0xda, 0x4b, 0xa9, 0x70, 0x67, 0xee, 0x15, 0xcd, 0x11, 0xc6, 0x38, 0x60, 0xc1, 0xc9, 0xfc, 0x66, + 0x06, 0xa6, 0x74, 0xc4, 0x01, 0xb6, 0x88, 0x4f, 0x18, 0x30, 0x55, 0x73, 0x9d, 0xc0, 0x73, 0x9b, + 0xdc, 0xc9, 0x93, 0x8e, 0xda, 0xa5, 0xa4, 0x56, 0x49, 0x60, 0xd9, 0x4d, 0xcd, 0x5f, 0xa4, 0xb1, + 0xc1, 0x11, 0xa6, 0xe8, 0x53, 0x06, 0xcc, 0x86, 0x81, 0x58, 0xa1, 0xb7, 0x29, 0x55, 0x41, 0xd4, + 0x8e, 0x7b, 0x2e, 0xca, 0x09, 0xc7, 0x59, 0x9b, 0xdb, 0x30, 0x17, 0x1f, 0x6d, 0xda, 0x95, 0x6d, + 0x4b, 0xac, 0xf5, 0x6c, 0xd8, 0x95, 0x15, 0xcb, 0xf7, 0x31, 0x83, 0xa0, 0xb7, 0x41, 0xbe, 0x65, + 0x79, 0x0d, 0xdb, 0xb1, 0x9a, 0xac, 0x17, 0xb3, 0xda, 0x86, 0x24, 0xca, 0xb1, 0xc2, 0x30, 0x1b, + 0x30, 0xb5, 0x61, 0x39, 0x0d, 0x52, 0xe7, 0xdb, 0xe1, 0x60, 0x4e, 0x4a, 0xbe, 0xb8, 0x79, 0x2c, + 0x54, 0x86, 0x1d, 0x5b, 0xd5, 0x2e, 0xb2, 0x12, 0x82, 0xb0, 0x8e, 0x67, 0xfe, 0x68, 0x0c, 0x26, + 0xb5, 0x73, 0xcf, 0xf1, 0x9f, 0x61, 0x22, 0x69, 0x12, 0xb2, 0x29, 0xa6, 0x49, 0x78, 0x0e, 0x60, + 0xc7, 0x76, 0x6c, 0x7f, 0xf7, 0x88, 0x09, 0x18, 0xd8, 0xf5, 0xe7, 0x79, 0x45, 0x01, 0x6b, 0xd4, + 0xc2, 0x3b, 0xa6, 0xdc, 0x21, 0x69, 0x69, 0x5e, 0x31, 0x34, 0x2d, 0x35, 0x9e, 0xc6, 0x9d, 0xba, + 0x36, 0x30, 0xcb, 0x52, 0x6b, 0x9d, 0x73, 0x02, 0x6f, 0xff, 0x50, 0x65, 0xb6, 0x05, 0x79, 0x8f, + 0xf8, 0x9d, 0x16, 0x3d, 0x8d, 0x4d, 0x0c, 0xdd, 0x0d, 0x2c, 0x1e, 0x01, 0x8b, 0xfa, 0x58, 0x51, + 0x5a, 0x7a, 0x1a, 0xa6, 0x23, 0x22, 0xa0, 0x39, 0xc8, 0xde, 0x24, 0xfb, 0x7c, 0x9e, 0x60, 0xfa, + 0x13, 0x2d, 0x44, 0x6e, 0xe2, 0x44, 0xb7, 0xbc, 0x3b, 0xf3, 0x94, 0x61, 0xba, 0x90, 0x78, 0xb8, + 0x3e, 0xca, 0x45, 0x09, 0x1d, 0x8b, 0xa6, 0x96, 0x81, 0x41, 0x8d, 0x05, 0x8f, 0x3a, 0xe1, 0x30, + 0xf3, 0x27, 0xe3, 0x20, 0xae, 0x89, 0x07, 0x58, 0x3a, 0xfa, 0xed, 0x50, 0xe6, 0x08, 0xb7, 0x43, + 0x97, 0x60, 0xca, 0x76, 0xec, 0xc0, 0xb6, 0x9a, 0xcc, 0x71, 0x22, 0xb4, 0xb0, 0x0c, 0xcb, 0x9d, + 0x5a, 0xd3, 0x60, 0x09, 0x74, 0x22, 0x75, 0xd1, 0x15, 0xc8, 0x31, 0x35, 0x25, 0x26, 0xf0, 0xf0, + 0x77, 0xd9, 0x2c, 0x8c, 0x81, 0xbf, 0xd5, 0xe1, 0x94, 0xd8, 0xd1, 0x81, 0xa7, 0xa0, 0x50, 0x47, + 0x5b, 0x31, 0x8f, 0xc3, 0xa3, 0x43, 0x0c, 0x8e, 0x7b, 0x6a, 0x50, 0x2a, 0x3b, 0x96, 0xdd, 0xec, + 0x78, 0x24, 0xa4, 0x32, 0x1e, 0xa5, 0x72, 0x3e, 0x06, 0xc7, 0x3d, 0x35, 0xd0, 0x0e, 0x4c, 0x89, + 0x32, 0x1e, 0x4b, 0x34, 0x71, 0xc4, 0x56, 0xb2, 0x98, 0xb1, 0xf3, 0x1a, 0x25, 0x1c, 0xa1, 0x8b, + 0x3a, 0x30, 0x6f, 0x3b, 0x35, 0xd7, 0xa9, 0x35, 0x3b, 0xbe, 0xbd, 0x47, 0xc2, 0x87, 0x32, 0x47, + 0x61, 0x76, 0xf2, 0xa0, 0x5b, 0x9c, 0x5f, 0x8b, 0x93, 0xc3, 0xbd, 0x1c, 0xd0, 0xcb, 0x06, 0x9c, + 0xac, 0xb9, 0x8e, 0xcf, 0xde, 0x74, 0xef, 0x91, 0x73, 0x9e, 0xe7, 0x7a, 0x9c, 0x77, 0xe1, 0x88, + 0xbc, 0x99, 0xbf, 0x6e, 0x25, 0x89, 0x24, 0x4e, 0xe6, 0x84, 0x5e, 0x80, 0x7c, 0xdb, 0x73, 0xf7, + 0xec, 0x3a, 0xf1, 0x44, 0x5c, 0xda, 0x7a, 0x1a, 0x39, 0x26, 0x2a, 0x82, 0x66, 0xb8, 0xf5, 0xc8, + 0x12, 0xac, 0xf8, 0x99, 0x5f, 0xc9, 0xc3, 0x4c, 0x14, 0x1d, 0x7d, 0x0c, 0xa0, 0xed, 0xb9, 0x2d, + 0x12, 0xec, 0x12, 0xf5, 0xe0, 0x61, 0x73, 0xd4, 0x54, 0x06, 0x92, 0x9e, 0x8c, 0x0c, 0xa1, 0xdb, + 0x45, 0x58, 0x8a, 0x35, 0x8e, 0xc8, 0x83, 0x89, 0x9b, 0x5c, 0x5b, 0x0b, 0xe3, 0xe5, 0xd9, 0x54, + 0x4c, 0x2d, 0xc1, 0x99, 0x45, 0xea, 0x8b, 0x22, 0x2c, 0x19, 0xa1, 0x6d, 0xc8, 0xde, 0x22, 0xdb, + 0xe9, 0x3c, 0x0f, 0xbe, 0x4e, 0xc4, 0x21, 0xa8, 0x3c, 0x71, 0xd0, 0x2d, 0x66, 0xaf, 0x93, 0x6d, + 0x4c, 0x89, 0xd3, 0x76, 0xd5, 0xf9, 0x1d, 0xb7, 0xd8, 0x2a, 0x46, 0x6c, 0x57, 0xe4, 0xc2, 0x9c, + 0xb7, 0x4b, 0x14, 0x61, 0xc9, 0x08, 0xbd, 0x00, 0x85, 0x5b, 0xd6, 0x1e, 0xd9, 0xf1, 0x5c, 0x27, + 0x10, 0xe1, 0x48, 0x23, 0xc6, 0xc0, 0x5f, 0x97, 0xe4, 0x04, 0x5f, 0xa6, 0xde, 0x55, 0x21, 0x0e, + 0xd9, 0xa1, 0x3d, 0xc8, 0x3b, 0xe4, 0x16, 0x26, 0x4d, 0xbb, 0x96, 0x4e, 0xcc, 0xf9, 0xa6, 0xa0, + 0x26, 0x38, 0x33, 0xbd, 0x27, 0xcb, 0xb0, 0xe2, 0x45, 0xc7, 0xf2, 0x86, 0xbb, 0x2d, 0x36, 0xaa, + 0x11, 0xc7, 0x52, 0x1d, 0x68, 0xf9, 0x58, 0x5e, 0x72, 0xb7, 0x31, 0x25, 0x4e, 0xd7, 0x48, 0x4d, + 0xc5, 0xc2, 0x88, 0x6d, 0x6a, 0x33, 0xdd, 0x18, 0x20, 0xbe, 0x46, 0xc2, 0x52, 0xac, 0x71, 0xa4, + 0x7d, 0xdb, 0x10, 0x8e, 0x40, 0xb1, 0x51, 0x8d, 0xd8, 0xb7, 0x51, 0xb7, 0x22, 0xef, 0x5b, 0x59, + 0x86, 0x15, 0x2f, 0xf3, 0xcb, 0xe3, 0x30, 0xa5, 0xe7, 0xd4, 0x1a, 0x40, 0x57, 0x2b, 0xfb, 0x34, + 0x33, 0x8c, 0x7d, 0x4a, 0xcf, 0x31, 0x9a, 0x1f, 0x5f, 0xba, 0x32, 0xd6, 0x52, 0x33, 0xcf, 0xc2, + 0x73, 0x8c, 0x56, 0xe8, 0xe3, 0x08, 0xd3, 0x21, 0xae, 0xf6, 0xa9, 0x91, 0xc3, 0xcd, 0x80, 0x5c, + 0xd4, 0xc8, 0x89, 0x28, 0xf6, 0xc7, 0x00, 0xc2, 0xdc, 0x52, 0xe2, 0x7e, 0x47, 0x59, 0x4f, 0x5a, + 0xce, 0x2b, 0x0d, 0x0b, 0x3d, 0x02, 0xe3, 0x54, 0x51, 0x92, 0xba, 0x78, 0x8d, 0xaa, 0x0e, 0x8b, + 0xe7, 0x59, 0x29, 0x16, 0x50, 0xf4, 0x14, 0xb5, 0x69, 0x42, 0xf5, 0x26, 0x1e, 0x99, 0x2e, 0x84, + 0x36, 0x4d, 0x08, 0xc3, 0x11, 0x4c, 0x2a, 0x3a, 0xa1, 0xda, 0x88, 0xcd, 0x24, 0x4d, 0x74, 0xa6, + 0xa2, 0x30, 0x87, 0x31, 0xe7, 0x45, 0x4c, 0x7b, 0x31, 0x65, 0x95, 0xd3, 0x9c, 0x17, 0x31, 0x38, + 0xee, 0xa9, 0x41, 0x1b, 0x23, 0xae, 0xa6, 0x26, 0x79, 0x04, 0x63, 0x9f, 0x4b, 0xa5, 0x57, 0x75, + 0xcb, 0x7c, 0x8a, 0x0d, 0xfd, 0x7b, 0xd3, 0xcb, 0x0f, 0x37, 0xb8, 0x69, 0x3e, 0x9a, 0x11, 0xfd, + 0x61, 0x98, 0x89, 0xee, 0x59, 0x74, 0x42, 0xb5, 0x3d, 0x77, 0xc7, 0x6e, 0x92, 0xb8, 0x93, 0xa9, + 0xc2, 0x8b, 0xb1, 0x84, 0x0f, 0xe6, 0xae, 0xff, 0xcb, 0x2c, 0x9c, 0xd8, 0x6c, 0xd8, 0xce, 0xed, + 0x98, 0x7b, 0x38, 0x29, 0x6f, 0xab, 0x31, 0x6c, 0xde, 0xd6, 0xf0, 0x59, 0x8b, 0x48, 0x8c, 0x9b, + 0xfc, 0xac, 0x45, 0x66, 0xcd, 0x8d, 0xe2, 0xa2, 0x1f, 0x1a, 0xf0, 0xa0, 0x55, 0xe7, 0x56, 0xa4, + 0xd5, 0x14, 0xa5, 0x21, 0x53, 0xb9, 0xa2, 0xfd, 0x11, 0x75, 0x42, 0x6f, 0xe3, 0x97, 0x4b, 0x87, + 0x70, 0xe5, 0x23, 0xfe, 0x16, 0xd1, 0x82, 0x07, 0x0f, 0x43, 0xc5, 0x87, 0x8a, 0xbf, 0x74, 0x19, + 0xde, 0x7c, 0x57, 0x46, 0x43, 0xcd, 0x96, 0x4f, 0x18, 0x50, 0xe0, 0xde, 0x4f, 0x4c, 0x76, 0xe8, + 0x56, 0x61, 0xb5, 0xed, 0x6b, 0xc4, 0xf3, 0x65, 0x42, 0x29, 0xed, 0xa0, 0x55, 0xaa, 0xac, 0x09, + 0x08, 0xd6, 0xb0, 0xe8, 0x66, 0x7c, 0xd3, 0x76, 0xea, 0x62, 0x98, 0xd4, 0x66, 0xfc, 0xac, 0xed, + 0xd4, 0x31, 0x83, 0xa8, 0xed, 0x3a, 0xdb, 0x37, 0xbb, 0xcb, 0x97, 0x0c, 0x98, 0x61, 0x6f, 0xf9, + 0xc2, 0x23, 0xc0, 0x93, 0x2a, 0x6e, 0x83, 0x8b, 0x71, 0x3a, 0x1a, 0xb7, 0x71, 0xa7, 0x5b, 0x9c, + 0xe4, 0xaf, 0xff, 0xa2, 0x61, 0x1c, 0xef, 0x17, 0x7e, 0x03, 0x16, 0x5d, 0x92, 0x19, 0xfa, 0x58, + 0xab, 0xdc, 0x71, 0x55, 0x49, 0x04, 0x87, 0xf4, 0xcc, 0x17, 0x61, 0x4a, 0x0f, 0xca, 0x47, 0x4f, + 0xc2, 0x64, 0xdb, 0x76, 0x1a, 0xd1, 0xc7, 0x5b, 0xca, 0x99, 0x52, 0x09, 0x41, 0x58, 0xc7, 0x63, + 0xd5, 0xdc, 0xb0, 0x5a, 0xcc, 0x93, 0x5b, 0x71, 0xf5, 0x6a, 0xe1, 0x1f, 0xf3, 0x8f, 0xb2, 0x70, + 0x22, 0xe1, 0xf1, 0x07, 0x7a, 0xc5, 0x80, 0x71, 0x16, 0x89, 0x2e, 0x23, 0x33, 0x9e, 0x4f, 0xfd, + 0x81, 0xc9, 0x32, 0x0b, 0x78, 0x17, 0xf3, 0x58, 0x6d, 0x9f, 0xbc, 0x10, 0x0b, 0xe6, 0xe8, 0xb7, + 0x0c, 0x98, 0xb4, 0xb4, 0xa5, 0xc6, 0x83, 0x55, 0xb6, 0xd3, 0x17, 0xa6, 0x67, 0x65, 0x69, 0x41, + 0x76, 0xe1, 0x42, 0xd2, 0x65, 0x59, 0x7a, 0x17, 0x4c, 0x6a, 0x4d, 0x18, 0x66, 0x85, 0x2c, 0x3d, + 0x03, 0x73, 0x23, 0xad, 0xb0, 0xf7, 0xc1, 0xb0, 0xf9, 0xd1, 0xa8, 0xc2, 0xba, 0xa5, 0x3f, 0xb0, + 0x55, 0x3d, 0x2e, 0x5e, 0xd8, 0x0a, 0xa8, 0xb9, 0x0d, 0x73, 0xf1, 0x43, 0x4e, 0xea, 0x77, 0xb3, + 0xef, 0x80, 0x21, 0x33, 0x9a, 0x99, 0x7f, 0x95, 0x81, 0x09, 0xf1, 0x82, 0xec, 0x1e, 0xc4, 0xa7, + 0xde, 0x8c, 0xdc, 0xc9, 0xac, 0xa5, 0xf2, 0xf0, 0xad, 0x6f, 0x70, 0xaa, 0x1f, 0x0b, 0x4e, 0x7d, + 0x36, 0x1d, 0x76, 0x87, 0x47, 0xa6, 0x7e, 0x69, 0x0c, 0x66, 0x63, 0x2f, 0xf2, 0xa8, 0xa9, 0xd2, + 0x13, 0x90, 0x75, 0x35, 0xd5, 0x47, 0x7f, 0x2a, 0x76, 0xfa, 0xf0, 0xd8, 0x2c, 0x3f, 0x92, 0x38, + 0xf2, 0x4a, 0x6a, 0x39, 0xa7, 0x7f, 0x9e, 0x43, 0x72, 0xd8, 0x58, 0xa3, 0x7f, 0x36, 0xe0, 0xfe, + 0xbe, 0x0f, 0x37, 0x59, 0xde, 0x0f, 0x2f, 0x0a, 0x15, 0x0b, 0x32, 0xe5, 0xe7, 0xe9, 0xea, 0x82, + 0x24, 0x9e, 0xaa, 0x21, 0xce, 0x1e, 0x3d, 0x01, 0x53, 0x4c, 0xb5, 0xd2, 0x3d, 0x25, 0x20, 0x6d, + 0xe1, 0xa8, 0x65, 0x2e, 0xbb, 0xaa, 0x56, 0x8e, 0x23, 0x58, 0xe6, 0x17, 0x0d, 0x58, 0xec, 0x97, + 0x05, 0x62, 0x80, 0x83, 0xe1, 0x2f, 0xc4, 0x02, 0x68, 0x8b, 0x3d, 0x01, 0xb4, 0xb1, 0xa3, 0xa1, + 0x8c, 0x95, 0xd5, 0x4e, 0x65, 0xd9, 0xbb, 0xc4, 0x87, 0x7e, 0xda, 0x80, 0x53, 0x7d, 0x56, 0x53, + 0x4f, 0x20, 0xb5, 0x71, 0xe4, 0x40, 0xea, 0xcc, 0xa0, 0x81, 0xd4, 0xe6, 0x77, 0xb3, 0x30, 0x27, + 0xe4, 0x09, 0xed, 0xab, 0xa7, 0x22, 0x61, 0xc8, 0x6f, 0x89, 0x85, 0x21, 0x2f, 0xc4, 0xf1, 0x7f, + 0x1e, 0x83, 0xfc, 0xc6, 0x8a, 0x41, 0xfe, 0x69, 0x06, 0x4e, 0x26, 0x26, 0xa7, 0x40, 0x9f, 0x4c, + 0x50, 0x0d, 0xd7, 0x53, 0xce, 0x82, 0x31, 0xa0, 0x72, 0x18, 0x35, 0x70, 0xf7, 0x73, 0x7a, 0xc0, + 0x2c, 0xdf, 0xea, 0x77, 0x8e, 0x21, 0x9f, 0xc7, 0x90, 0xb1, 0xb3, 0xe6, 0xaf, 0x65, 0xe1, 0xd1, + 0x41, 0x09, 0xbd, 0x41, 0xdf, 0x56, 0xf8, 0x91, 0xb7, 0x15, 0xf7, 0x48, 0x6d, 0x1f, 0xcb, 0x33, + 0x8b, 0x2f, 0x67, 0x95, 0xda, 0xeb, 0x9d, 0x9f, 0x03, 0xdd, 0xea, 0x4d, 0x50, 0xd3, 0x4e, 0xa6, + 0xac, 0x0c, 0xb7, 0xc2, 0x89, 0x2a, 0x2f, 0xbe, 0xd3, 0x2d, 0xce, 0x8b, 0x34, 0x76, 0x55, 0x12, + 0x88, 0x42, 0x2c, 0x2b, 0xa1, 0x47, 0x21, 0xef, 0x71, 0xa8, 0x8c, 0x26, 0x17, 0x57, 0xa3, 0xbc, + 0x0c, 0x2b, 0x28, 0xfa, 0xb8, 0x66, 0x0b, 0x8f, 0x1d, 0x57, 0x26, 0x80, 0xc3, 0x6e, 0x7c, 0x9f, + 0x87, 0xbc, 0x2f, 0x93, 0x4f, 0x72, 0xb7, 0xfc, 0xe3, 0x03, 0x3e, 0x52, 0xa0, 0x47, 0x27, 0x99, + 0x89, 0x92, 0xb7, 0x4f, 0xe5, 0xa9, 0x54, 0x24, 0x91, 0xa9, 0x4e, 0x2d, 0xdc, 0xc7, 0x08, 0x09, + 0x27, 0x96, 0xef, 0x19, 0x30, 0x29, 0x46, 0xeb, 0x1e, 0xbc, 0x9b, 0xb8, 0x11, 0x7d, 0x37, 0x71, + 0x2e, 0x95, 0xbd, 0xa3, 0xcf, 0xa3, 0x89, 0x1b, 0x30, 0xa5, 0xe7, 0x27, 0x42, 0xcf, 0x69, 0x7b, + 0x9f, 0x31, 0x4a, 0xc6, 0x0f, 0xb9, 0x3b, 0x86, 0xfb, 0xa2, 0xf9, 0x85, 0xbc, 0xea, 0x45, 0xe6, + 0x87, 0xd0, 0xe7, 0xa0, 0x71, 0xe8, 0x1c, 0xd4, 0xa7, 0x40, 0x26, 0xfd, 0x29, 0x70, 0x05, 0xf2, + 0x72, 0x83, 0x12, 0x6a, 0xfc, 0x61, 0x3d, 0x48, 0x8e, 0xda, 0x02, 0x94, 0x98, 0x36, 0x71, 0xd9, + 0x51, 0x4b, 0x8d, 0xa1, 0xda, 0x38, 0x15, 0x19, 0xf4, 0x02, 0x4c, 0xde, 0x72, 0xbd, 0x9b, 0x4d, + 0xd7, 0x62, 0x69, 0x65, 0x21, 0x8d, 0x0b, 0x16, 0xe5, 0xf0, 0xe2, 0xe1, 0xbc, 0xd7, 0x43, 0xfa, + 0x58, 0x67, 0x86, 0x4a, 0x30, 0xdb, 0xb2, 0x1d, 0x4c, 0xac, 0xba, 0x7a, 0x1e, 0x31, 0xc6, 0xf3, + 0x5e, 0x4a, 0x23, 0x77, 0x23, 0x0a, 0xc6, 0x71, 0x7c, 0xf4, 0x51, 0xc8, 0xfb, 0x22, 0xdb, 0x4f, + 0x3a, 0x57, 0x61, 0xea, 0xcc, 0xc8, 0x89, 0x86, 0x7d, 0x27, 0x4b, 0xb0, 0x62, 0x88, 0xd6, 0x61, + 0xc1, 0x13, 0xf9, 0x34, 0x22, 0x1f, 0xa5, 0xe0, 0xeb, 0x93, 0xa5, 0x57, 0xc4, 0x09, 0x70, 0x9c, + 0x58, 0x8b, 0x5a, 0x31, 0x2c, 0xd1, 0x16, 0xbf, 0x13, 0xd0, 0xdc, 0xe8, 0x6c, 0xc2, 0xd7, 0xb1, + 0x80, 0x1e, 0xf6, 0xdc, 0x26, 0x3f, 0xc2, 0x73, 0x9b, 0x2a, 0x9c, 0x8c, 0x83, 0x58, 0xd6, 0x0f, + 0x96, 0x68, 0x44, 0xd3, 0x1e, 0x95, 0x24, 0x24, 0x9c, 0x5c, 0x17, 0x5d, 0x87, 0x82, 0x47, 0xd8, + 0xf9, 0xa2, 0x24, 0x2f, 0xdf, 0x87, 0x0e, 0x33, 0xc2, 0x92, 0x00, 0x0e, 0x69, 0xd1, 0x71, 0xb7, + 0xa2, 0xa9, 0x1f, 0xaf, 0xa4, 0xf8, 0x59, 0x2d, 0x31, 0xf6, 0x7d, 0xb2, 0xf1, 0x98, 0xaf, 0xcf, + 0xc0, 0x74, 0xc4, 0xb7, 0x80, 0x1e, 0x86, 0x1c, 0x4b, 0x83, 0xc2, 0xb6, 0x87, 0x7c, 0xb8, 0x85, + 0xf1, 0xce, 0xe1, 0x30, 0xf4, 0x19, 0x03, 0x66, 0xdb, 0x11, 0x2f, 0xac, 0xdc, 0x39, 0x47, 0xbc, + 0xe7, 0x8b, 0xba, 0x76, 0xb5, 0xa4, 0xc9, 0x51, 0x66, 0x38, 0xce, 0x9d, 0x2e, 0x40, 0x11, 0xe2, + 0xd7, 0x24, 0x1e, 0xc3, 0x16, 0x36, 0x8e, 0x22, 0xb1, 0x12, 0x05, 0xe3, 0x38, 0x3e, 0x1d, 0x61, + 0xd6, 0xba, 0x51, 0xbe, 0xb7, 0x53, 0x92, 0x04, 0x70, 0x48, 0x0b, 0x3d, 0x03, 0x33, 0x22, 0xe3, + 0x5f, 0xc5, 0xad, 0x5f, 0xb4, 0xfc, 0x5d, 0x61, 0xdc, 0xab, 0xc3, 0xc8, 0x4a, 0x04, 0x8a, 0x63, + 0xd8, 0xac, 0x6d, 0x61, 0x5a, 0x45, 0x46, 0x60, 0x3c, 0x9a, 0x53, 0x7a, 0x25, 0x0a, 0xc6, 0x71, + 0x7c, 0xf4, 0x36, 0x6d, 0xdf, 0xe7, 0xf7, 0x74, 0x6a, 0x37, 0x48, 0xd8, 0xfb, 0x4b, 0x30, 0xdb, + 0x61, 0x67, 0xa1, 0xba, 0x04, 0x8a, 0xf5, 0xa8, 0x18, 0x5e, 0x8d, 0x82, 0x71, 0x1c, 0x1f, 0x3d, + 0x0d, 0xd3, 0x1e, 0xdd, 0xdd, 0x14, 0x01, 0x7e, 0x79, 0xa7, 0xee, 0x66, 0xb0, 0x0e, 0xc4, 0x51, + 0x5c, 0x74, 0x01, 0xe6, 0xc3, 0x04, 0x59, 0x92, 0x00, 0xbf, 0xcd, 0x53, 0xb9, 0x5f, 0x4a, 0x71, + 0x04, 0xdc, 0x5b, 0x07, 0xfd, 0x12, 0xcc, 0x69, 0x3d, 0xb1, 0xe6, 0xd4, 0xc9, 0x6d, 0x91, 0xc4, + 0x88, 0xa5, 0xff, 0x5f, 0x89, 0xc1, 0x70, 0x0f, 0x36, 0x7a, 0x37, 0xcc, 0xd4, 0xdc, 0x66, 0x93, + 0xed, 0x71, 0x3c, 0x9f, 0x31, 0xcf, 0x56, 0xc4, 0xf3, 0x3a, 0x45, 0x20, 0x38, 0x86, 0x89, 0x2e, + 0x01, 0x72, 0xb7, 0x7d, 0xe2, 0xed, 0x91, 0xfa, 0x05, 0xfe, 0x05, 0x4f, 0xaa, 0xe2, 0xa7, 0xa3, + 0x01, 0xc6, 0x97, 0x7b, 0x30, 0x70, 0x42, 0x2d, 0x96, 0x3a, 0x46, 0x7b, 0xb5, 0x34, 0x93, 0xc6, + 0xb7, 0x67, 0xe2, 0x27, 0xf7, 0xbb, 0x3e, 0x59, 0xf2, 0x60, 0x9c, 0x87, 0x7a, 0xa6, 0x93, 0xb6, + 0x48, 0x4f, 0x6d, 0x1a, 0xea, 0x08, 0x11, 0x59, 0x2a, 0x38, 0xa1, 0x8f, 0x41, 0x61, 0x5b, 0xe6, + 0xb9, 0x5e, 0x9c, 0x4b, 0x43, 0x2f, 0xc6, 0x52, 0xb6, 0x87, 0x27, 0x53, 0x05, 0xc0, 0x21, 0x4b, + 0xf4, 0x08, 0x4c, 0x5e, 0xac, 0x94, 0xd4, 0x2c, 0x9c, 0x67, 0xa3, 0x3f, 0x46, 0xab, 0x60, 0x1d, + 0x40, 0x57, 0x98, 0xb2, 0x97, 0x10, 0x1b, 0xe2, 0x50, 0xdf, 0xf6, 0x9a, 0x3f, 0x14, 0x9b, 0x5d, + 0x47, 0xe2, 0xea, 0xe2, 0x89, 0x18, 0xb6, 0x28, 0xc7, 0x0a, 0x03, 0x3d, 0x0f, 0x93, 0x42, 0x5f, + 0xb0, 0xbd, 0x69, 0xe1, 0x68, 0x2f, 0xe2, 0x70, 0x48, 0x02, 0xeb, 0xf4, 0xd8, 0x2d, 0x13, 0x4b, + 0xff, 0x4b, 0xce, 0x77, 0x9a, 0xcd, 0xc5, 0x93, 0xd1, 0x48, 0xdf, 0x4a, 0x08, 0xc2, 0x3a, 0x1e, + 0x7a, 0x5c, 0x46, 0x4e, 0xbc, 0x29, 0x72, 0xed, 0xa6, 0x22, 0x27, 0x94, 0x95, 0xdb, 0x27, 0xb0, + 0xf7, 0xd4, 0x5d, 0x42, 0x16, 0xb6, 0x61, 0x49, 0x9a, 0x58, 0xbd, 0x8b, 0x64, 0x71, 0x31, 0xe2, + 0x25, 0x58, 0xba, 0xde, 0x17, 0x13, 0x1f, 0x42, 0x05, 0x6d, 0x43, 0xd6, 0x6a, 0x6e, 0x2f, 0xde, + 0x9f, 0x86, 0xad, 0xa8, 0xbe, 0xc8, 0xcb, 0x83, 0x71, 0x4a, 0xeb, 0x65, 0x4c, 0x89, 0x9b, 0x2f, + 0x67, 0x94, 0x57, 0x5e, 0xa5, 0x73, 0x7c, 0x51, 0x9f, 0xd5, 0x46, 0x1a, 0x5f, 0x9c, 0xec, 0x49, + 0x06, 0xcf, 0x15, 0x52, 0xe2, 0x9c, 0x6e, 0xab, 0x75, 0x9c, 0x4a, 0xae, 0x8e, 0x68, 0xaa, 0x4a, + 0x7e, 0x9a, 0x8b, 0xae, 0x62, 0xf3, 0x60, 0x42, 0x39, 0xa1, 0x62, 0xa1, 0x00, 0x1e, 0xe4, 0x6c, + 0x3f, 0xb0, 0xdd, 0x14, 0xdf, 0x87, 0xc5, 0x72, 0x3c, 0xb2, 0x00, 0x56, 0x06, 0xc0, 0x9c, 0x15, + 0xe5, 0xe9, 0x34, 0x6c, 0xe7, 0xb6, 0x68, 0xfe, 0x95, 0xd4, 0xef, 0xf8, 0x39, 0x4f, 0x06, 0xc0, + 0x9c, 0x15, 0xba, 0xc1, 0x67, 0x5a, 0x3a, 0x5f, 0x17, 0x8d, 0x7f, 0x34, 0x38, 0x3a, 0xe3, 0x28, + 0x2f, 0xbf, 0x65, 0x0b, 0x1b, 0x66, 0x44, 0x5e, 0xd5, 0x8d, 0xb5, 0x24, 0x5e, 0xd5, 0x8d, 0x35, + 0x4c, 0x99, 0xa0, 0x57, 0x0d, 0x00, 0x4b, 0x7d, 0x3d, 0x37, 0x9d, 0x2f, 0x27, 0xf4, 0xfb, 0x1a, + 0x2f, 0x8f, 0x39, 0x0b, 0xa1, 0x58, 0xe3, 0x8c, 0x5e, 0x80, 0x09, 0x8b, 0x7f, 0xf7, 0x45, 0x84, + 0xf3, 0xa5, 0xf3, 0x31, 0xa3, 0x98, 0x04, 0x2c, 0x8e, 0x51, 0x80, 0xb0, 0x64, 0x48, 0x79, 0x07, + 0x9e, 0x45, 0x76, 0xec, 0x9b, 0x22, 0xae, 0xaf, 0x3a, 0x72, 0xfa, 0x66, 0x4a, 0x2c, 0x89, 0xb7, + 0x00, 0x61, 0xc9, 0x90, 0x7f, 0xb0, 0xd2, 0x72, 0x2c, 0xf5, 0xb6, 0x23, 0x9d, 0x17, 0x40, 0xfa, + 0x6b, 0x11, 0xed, 0x83, 0x95, 0x3a, 0x23, 0x1c, 0xe5, 0x6b, 0xfe, 0x38, 0x0b, 0xc0, 0x7e, 0xf2, + 0x47, 0xb9, 0x2d, 0x96, 0xc8, 0x6d, 0xd7, 0xad, 0x8b, 0xa5, 0x9d, 0xe2, 0xdb, 0x5a, 0x10, 0x59, + 0xdb, 0x76, 0xdd, 0x3a, 0x16, 0x4c, 0x50, 0x03, 0xc6, 0xda, 0x56, 0xb0, 0x9b, 0xfe, 0x43, 0xde, + 0x3c, 0x7f, 0x78, 0x13, 0xec, 0x62, 0xc6, 0x00, 0xbd, 0x64, 0xc0, 0x04, 0x7f, 0xca, 0x2b, 0x5d, + 0xcd, 0x23, 0xdf, 0xa7, 0xca, 0x3e, 0x5b, 0xe6, 0xef, 0x85, 0x45, 0xb0, 0x82, 0x52, 0x8d, 0xa2, + 0x14, 0x4b, 0xb6, 0x4b, 0xaf, 0x18, 0x30, 0xa5, 0xa3, 0x26, 0x84, 0x19, 0x7c, 0x48, 0x0f, 0x33, + 0x48, 0xb3, 0x3f, 0xf4, 0x88, 0x85, 0x7f, 0x33, 0x40, 0xfb, 0xcc, 0x67, 0x18, 0x64, 0x68, 0x0c, + 0x1c, 0x64, 0x98, 0x19, 0x32, 0xc8, 0x30, 0x3b, 0x54, 0x90, 0xe1, 0xd8, 0xf0, 0x41, 0x86, 0xb9, + 0xfe, 0x41, 0x86, 0xe6, 0x6b, 0x06, 0xcc, 0xf7, 0xec, 0x87, 0xf1, 0xcf, 0xa9, 0x1b, 0x03, 0x7e, + 0x4e, 0x7d, 0x15, 0xe6, 0x44, 0xa2, 0xe1, 0x6a, 0xbb, 0x69, 0x27, 0x3e, 0xe3, 0xde, 0x8a, 0xc1, + 0x71, 0x4f, 0x0d, 0xf3, 0xcf, 0x0c, 0x98, 0xd4, 0x5e, 0x9d, 0xd1, 0x76, 0xb0, 0xd7, 0x79, 0x42, + 0x8c, 0x30, 0xc7, 0x32, 0x73, 0xed, 0x73, 0x18, 0xbf, 0x65, 0x6a, 0x68, 0x49, 0x2d, 0xc3, 0x5b, + 0x26, 0x5a, 0x8a, 0x05, 0x94, 0xa7, 0x2b, 0x24, 0xfc, 0x53, 0xf9, 0x59, 0x3d, 0x5d, 0x21, 0x69, + 0x63, 0x06, 0x61, 0xec, 0xa8, 0x1d, 0x29, 0xe2, 0x4f, 0xb5, 0x94, 0xce, 0x96, 0x17, 0x60, 0x0e, + 0x43, 0xa7, 0x21, 0x4b, 0x9c, 0xba, 0x38, 0xf4, 0xaa, 0xcf, 0x28, 0x9d, 0x73, 0xea, 0x98, 0x96, + 0x9b, 0x97, 0x61, 0xaa, 0x4a, 0x6a, 0x1e, 0x09, 0x9e, 0x25, 0xfb, 0x03, 0x7f, 0x97, 0x89, 0xce, + 0xf6, 0xd8, 0x77, 0x99, 0x68, 0x75, 0x5a, 0x6e, 0xfe, 0xa1, 0x01, 0xb1, 0xbc, 0xe3, 0x9a, 0xc7, + 0xd9, 0xe8, 0xe7, 0x71, 0x8e, 0xf8, 0x46, 0x33, 0x87, 0xfa, 0x46, 0x2f, 0x01, 0x6a, 0xd1, 0xa5, + 0x10, 0xc9, 0xb2, 0x2f, 0xfc, 0x0d, 0xe1, 0x1b, 0xd7, 0x1e, 0x0c, 0x9c, 0x50, 0xcb, 0xfc, 0x03, + 0x2e, 0xac, 0x9e, 0x89, 0xfc, 0xee, 0x1d, 0xd0, 0x81, 0x1c, 0x23, 0x25, 0x9c, 0x2e, 0x95, 0xd1, + 0x16, 0x77, 0x6f, 0x42, 0x85, 0x70, 0x20, 0xc5, 0x92, 0x67, 0xdc, 0xcc, 0xef, 0x72, 0x59, 0xb5, + 0x54, 0xe5, 0x03, 0xc8, 0xda, 0x8a, 0xca, 0x7a, 0x31, 0xad, 0xbd, 0x32, 0x59, 0x46, 0xb4, 0x0c, + 0xd0, 0x26, 0x5e, 0x8d, 0x38, 0x81, 0x0c, 0x8b, 0xce, 0x89, 0xe7, 0x1c, 0xaa, 0x14, 0x6b, 0x18, + 0xe6, 0x4b, 0x06, 0xcc, 0x55, 0x03, 0xbb, 0x76, 0xd3, 0x76, 0xf8, 0xf3, 0xa4, 0x1d, 0xbb, 0x41, + 0x4f, 0x29, 0x44, 0x7c, 0x72, 0x88, 0xbb, 0xc1, 0xd4, 0x56, 0x2c, 0xbf, 0x34, 0x24, 0xe1, 0xa8, + 0x04, 0xb3, 0xd2, 0xdb, 0x2e, 0x7d, 0x97, 0xfc, 0x35, 0xa6, 0xf2, 0x95, 0xac, 0x46, 0xc1, 0x38, + 0x8e, 0x6f, 0x7e, 0x1c, 0x26, 0xb5, 0xfd, 0x95, 0x6d, 0x45, 0xb7, 0xad, 0x5a, 0x10, 0x5f, 0xc2, + 0xe7, 0x68, 0x21, 0xe6, 0x30, 0xe6, 0x62, 0xe5, 0x71, 0xb3, 0xb1, 0x25, 0x2c, 0xa2, 0x65, 0x05, + 0x94, 0x12, 0xf3, 0x48, 0x83, 0xdc, 0x96, 0xe9, 0x33, 0x25, 0x31, 0x4c, 0x0b, 0x31, 0x87, 0x99, + 0xd7, 0x20, 0x2f, 0xdf, 0xcc, 0xb3, 0x87, 0xa7, 0xd2, 0xfd, 0xa7, 0x3f, 0x3c, 0x75, 0xbd, 0x00, + 0x33, 0x08, 0x5d, 0x27, 0xbe, 0x63, 0x5f, 0x74, 0xfd, 0x40, 0x3e, 0xf4, 0xe7, 0x4e, 0xfe, 0xcd, + 0x35, 0x56, 0x86, 0x15, 0xd4, 0x9c, 0x87, 0x59, 0xe5, 0xbd, 0x17, 0xa1, 0x89, 0xdf, 0xcc, 0xc2, + 0x54, 0xe4, 0x03, 0xb6, 0x77, 0x9f, 0x40, 0x83, 0xaf, 0xcb, 0x04, 0x2f, 0x7c, 0x76, 0x48, 0x2f, + 0xbc, 0x7e, 0xed, 0x31, 0x76, 0xbc, 0xd7, 0x1e, 0xb9, 0x74, 0xae, 0x3d, 0x02, 0x98, 0xf0, 0x85, + 0xea, 0x19, 0x4f, 0xc3, 0x3d, 0x12, 0x1b, 0x31, 0x6e, 0x75, 0x4a, 0x0d, 0x26, 0x59, 0x99, 0x5f, + 0xcb, 0xc1, 0x4c, 0x34, 0x97, 0xcf, 0x00, 0x23, 0xf9, 0xb6, 0x9e, 0x91, 0x1c, 0xd2, 0x0b, 0x99, + 0x1d, 0xd5, 0x0b, 0x39, 0x36, 0xaa, 0x17, 0x32, 0x77, 0x04, 0x2f, 0x64, 0xaf, 0x0f, 0x71, 0x7c, + 0x60, 0x1f, 0xe2, 0x7b, 0x54, 0x08, 0xcd, 0x44, 0xe4, 0xce, 0x39, 0x0c, 0xa1, 0x41, 0xd1, 0x61, + 0x58, 0x71, 0xeb, 0x89, 0xa1, 0x48, 0xf9, 0xbb, 0x78, 0x5b, 0xbc, 0xc4, 0x88, 0x97, 0xe1, 0x2f, + 0x3a, 0xde, 0x34, 0x44, 0xb4, 0xcb, 0x93, 0x30, 0x29, 0xe6, 0x13, 0xb3, 0x7e, 0x20, 0x6a, 0x39, + 0x55, 0x43, 0x10, 0xd6, 0xf1, 0xd8, 0x37, 0x16, 0xa3, 0x1f, 0x95, 0x64, 0x4e, 0x5d, 0xfd, 0x1b, + 0x8b, 0xb1, 0x8f, 0x50, 0xc6, 0xf1, 0xcd, 0x8f, 0xc2, 0xc9, 0xc4, 0x33, 0x16, 0x73, 0x3a, 0x31, + 0xc5, 0x4c, 0xea, 0x02, 0x41, 0x13, 0x23, 0x96, 0xea, 0x75, 0xe9, 0x7a, 0x5f, 0x4c, 0x7c, 0x08, + 0x15, 0xf3, 0xab, 0x59, 0x98, 0x89, 0x7e, 0xa0, 0x07, 0xdd, 0x52, 0x1e, 0x99, 0x54, 0x9c, 0x41, + 0x9c, 0xac, 0x96, 0x81, 0xa6, 0xaf, 0x7b, 0xf5, 0x16, 0x9b, 0x5f, 0xdb, 0x2a, 0x1d, 0xce, 0xf1, + 0x31, 0x16, 0x7e, 0x4d, 0xc1, 0x8e, 0x7d, 0x83, 0x27, 0x7c, 0xc0, 0x20, 0x0e, 0x52, 0xa9, 0x73, + 0x0f, 0x9f, 0x24, 0x28, 0x56, 0x58, 0x63, 0x4b, 0x75, 0xcb, 0x1e, 0xf1, 0xec, 0x1d, 0x5b, 0x7d, + 0x5c, 0x90, 0xed, 0xdc, 0xd7, 0x44, 0x19, 0x56, 0x50, 0xf3, 0xa5, 0x0c, 0x84, 0x9f, 0x52, 0x65, + 0x5f, 0xb1, 0xf0, 0x35, 0xa3, 0x55, 0x0c, 0xdb, 0xa5, 0x51, 0x3f, 0x15, 0x13, 0x52, 0x14, 0xe1, + 0x8d, 0x5a, 0x09, 0x8e, 0x70, 0xfc, 0x19, 0x7c, 0x42, 0xd5, 0x82, 0xd9, 0xd8, 0xf3, 0xca, 0xd4, + 0x63, 0xc8, 0xbf, 0x90, 0x85, 0x82, 0x7a, 0xa0, 0x8a, 0xde, 0x15, 0xf1, 0x20, 0x14, 0xca, 0x6f, + 0xd6, 0x12, 0xb6, 0xef, 0xba, 0xf5, 0x3b, 0xdd, 0xe2, 0xac, 0x42, 0x8e, 0x79, 0x03, 0x4e, 0x43, + 0xb6, 0xe3, 0x35, 0xe3, 0x47, 0x84, 0xab, 0x78, 0x1d, 0xd3, 0x72, 0x74, 0x3b, 0x7e, 0x84, 0xdf, + 0x48, 0xe9, 0x51, 0x2d, 0xb7, 0xa5, 0xfb, 0x1f, 0xdd, 0xa9, 0x96, 0xdc, 0x76, 0xeb, 0xfb, 0xf1, + 0x04, 0xef, 0x65, 0xb7, 0xbe, 0x8f, 0x19, 0x04, 0x3d, 0x03, 0x33, 0x81, 0xdd, 0x22, 0x6e, 0x27, + 0xd0, 0x3f, 0x54, 0x99, 0x0d, 0xaf, 0x0b, 0xb7, 0x22, 0x50, 0x1c, 0xc3, 0xa6, 0x5a, 0xf6, 0x86, + 0xef, 0x3a, 0x2c, 0x6b, 0xdb, 0x78, 0xf4, 0x6e, 0xe1, 0x52, 0xf5, 0xf2, 0x26, 0xf3, 0x64, 0x28, + 0x0c, 0x8a, 0x6d, 0xb3, 0x57, 0x70, 0x1e, 0x11, 0xb7, 0xf5, 0x73, 0x61, 0xae, 0x02, 0x5e, 0x8e, + 0x15, 0x86, 0x79, 0x15, 0x66, 0x63, 0x4d, 0x95, 0x87, 0x31, 0x23, 0xf9, 0x30, 0x36, 0x58, 0x36, + 0xf5, 0x3f, 0x36, 0x60, 0xbe, 0x67, 0xf1, 0x0e, 0xfa, 0xb8, 0x21, 0xae, 0x46, 0x32, 0x47, 0x57, + 0x23, 0xd9, 0xe1, 0xd4, 0x48, 0x79, 0xf9, 0xdb, 0xaf, 0x9f, 0xb9, 0xef, 0x3b, 0xaf, 0x9f, 0xb9, + 0xef, 0xfb, 0xaf, 0x9f, 0xb9, 0xef, 0xa5, 0x83, 0x33, 0xc6, 0xb7, 0x0f, 0xce, 0x18, 0xdf, 0x39, + 0x38, 0x63, 0x7c, 0xff, 0xe0, 0x8c, 0xf1, 0x4f, 0x07, 0x67, 0x8c, 0xd7, 0x7e, 0x74, 0xe6, 0xbe, + 0xe7, 0xf2, 0x72, 0x9a, 0xfc, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x95, 0x6a, 0x3e, 0x03, 0x91, + 0x91, 0x00, 0x00, } func (m *ALBStatus) Marshal() (dAtA []byte, err error) { @@ -4672,9 +4772,21 @@ func (m *CanaryStep) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.SetHeaderRouting != nil { + if m.SetMirrorRoute != nil { { - size, err := m.SetHeaderRouting.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.SetMirrorRoute.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.SetHeaderRoute != nil { + { + size, err := m.SetHeaderRoute.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -5849,16 +5961,18 @@ func (m *HeaderRoutingMatch) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - { - size, err := m.HeaderValue.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + if m.HeaderValue != nil { + { + size, err := m.HeaderValue.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 } - i-- - dAtA[i] = 0x12 i -= len(m.HeaderName) copy(dAtA[i:], m.HeaderName) i = encodeVarintGenerated(dAtA, i, uint64(len(m.HeaderName))) @@ -6214,6 +6328,42 @@ func (m *KayentaThreshold) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *MangedRoutes) 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 *MangedRoutes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MangedRoutes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i-- + if m.CanaryRoute { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + 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 *Measurement) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -8015,6 +8165,20 @@ func (m *RolloutTrafficRouting) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ManagedRoutes) > 0 { + for iNdEx := len(m.ManagedRoutes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ManagedRoutes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } if m.Traefik != nil { { size, err := m.Traefik.MarshalToSizedBuffer(dAtA[:i]) @@ -8102,45 +8266,7 @@ func (m *RolloutTrafficRouting) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *RunSummary) 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 *RunSummary) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *RunSummary) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - i = encodeVarintGenerated(dAtA, i, uint64(m.Error)) - i-- - dAtA[i] = 0x28 - i = encodeVarintGenerated(dAtA, i, uint64(m.Inconclusive)) - i-- - dAtA[i] = 0x20 - i = encodeVarintGenerated(dAtA, i, uint64(m.Failed)) - i-- - dAtA[i] = 0x18 - i = encodeVarintGenerated(dAtA, i, uint64(m.Successful)) - i-- - dAtA[i] = 0x10 - i = encodeVarintGenerated(dAtA, i, uint64(m.Count)) - i-- - dAtA[i] = 0x8 - return len(dAtA) - i, nil -} - -func (m *SMITrafficRouting) Marshal() (dAtA []byte, err error) { +func (m *RouteMatch) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -8150,19 +8276,133 @@ func (m *SMITrafficRouting) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *SMITrafficRouting) MarshalTo(dAtA []byte) (int, error) { +func (m *RouteMatch) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *SMITrafficRouting) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *RouteMatch) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - i -= len(m.TrafficSplitName) - copy(dAtA[i:], m.TrafficSplitName) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.TrafficSplitName))) + if len(m.Headers) > 0 { + keysForHeaders := make([]string, 0, len(m.Headers)) + for k := range m.Headers { + keysForHeaders = append(keysForHeaders, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForHeaders) + for iNdEx := len(keysForHeaders) - 1; iNdEx >= 0; iNdEx-- { + v := m.Headers[string(keysForHeaders[iNdEx])] + baseI := i + { + size, err := (&v).MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(keysForHeaders[iNdEx]) + copy(dAtA[i:], keysForHeaders[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForHeaders[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if m.Path != nil { + { + size, err := m.Path.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Method != nil { + { + size, err := m.Method.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 *RunSummary) 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 *RunSummary) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RunSummary) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i = encodeVarintGenerated(dAtA, i, uint64(m.Error)) + i-- + dAtA[i] = 0x28 + i = encodeVarintGenerated(dAtA, i, uint64(m.Inconclusive)) + i-- + dAtA[i] = 0x20 + i = encodeVarintGenerated(dAtA, i, uint64(m.Failed)) + i-- + dAtA[i] = 0x18 + i = encodeVarintGenerated(dAtA, i, uint64(m.Successful)) + i-- + dAtA[i] = 0x10 + i = encodeVarintGenerated(dAtA, i, uint64(m.Count)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil +} + +func (m *SMITrafficRouting) 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 *SMITrafficRouting) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SMITrafficRouting) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.TrafficSplitName) + copy(dAtA[i:], m.TrafficSplitName) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.TrafficSplitName))) i-- dAtA[i] = 0x12 i -= len(m.RootService) @@ -8293,7 +8533,7 @@ func (m *SetCanaryScale) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *SetHeaderRouting) Marshal() (dAtA []byte, err error) { +func (m *SetHeaderRoute) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -8303,12 +8543,12 @@ func (m *SetHeaderRouting) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *SetHeaderRouting) MarshalTo(dAtA []byte) (int, error) { +func (m *SetHeaderRoute) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *SetHeaderRouting) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *SetHeaderRoute) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -8324,9 +8564,61 @@ func (m *SetHeaderRouting) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenerated(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0xa + 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 *SetMirrorRoute) 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 *SetMirrorRoute) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SetMirrorRoute) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Percentage != nil { + i = encodeVarintGenerated(dAtA, i, uint64(*m.Percentage)) + i-- + dAtA[i] = 0x20 + } + if len(m.Match) > 0 { + for iNdEx := len(m.Match) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Match[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + 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 } @@ -9423,8 +9715,12 @@ func (m *CanaryStep) Size() (n int) { l = m.SetCanaryScale.Size() n += 1 + l + sovGenerated(uint64(l)) } - if m.SetHeaderRouting != nil { - l = m.SetHeaderRouting.Size() + if m.SetHeaderRoute != nil { + l = m.SetHeaderRoute.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.SetMirrorRoute != nil { + l = m.SetMirrorRoute.Size() n += 1 + l + sovGenerated(uint64(l)) } return n @@ -9843,8 +10139,10 @@ func (m *HeaderRoutingMatch) Size() (n int) { _ = l l = len(m.HeaderName) n += 1 + l + sovGenerated(uint64(l)) - l = m.HeaderValue.Size() - n += 1 + l + sovGenerated(uint64(l)) + if m.HeaderValue != nil { + l = m.HeaderValue.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -9977,6 +10275,18 @@ func (m *KayentaThreshold) Size() (n int) { return n } +func (m *MangedRoutes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + n += 2 + return n +} + func (m *Measurement) Size() (n int) { if m == nil { return 0 @@ -10642,6 +10952,38 @@ func (m *RolloutTrafficRouting) Size() (n int) { l = m.Traefik.Size() n += 1 + l + sovGenerated(uint64(l)) } + if len(m.ManagedRoutes) > 0 { + for _, e := range m.ManagedRoutes { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *RouteMatch) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Method != nil { + l = m.Method.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.Path != nil { + l = m.Path.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if len(m.Headers) > 0 { + for k, v := range m.Headers { + _ = k + _ = v + l = v.Size() + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + l + sovGenerated(uint64(l)) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } return n } @@ -10719,18 +11061,40 @@ func (m *SetCanaryScale) Size() (n int) { return n } -func (m *SetHeaderRouting) Size() (n int) { +func (m *SetHeaderRoute) 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.Match) > 0 { + for _, e := range m.Match { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *SetMirrorRoute) 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.Match) > 0 { for _, e := range m.Match { l = e.Size() n += 1 + l + sovGenerated(uint64(l)) } } + if m.Percentage != nil { + n += 1 + sovGenerated(uint64(*m.Percentage)) + } return n } @@ -11311,7 +11675,8 @@ func (this *CanaryStep) String() string { `Experiment:` + strings.Replace(this.Experiment.String(), "RolloutExperimentStep", "RolloutExperimentStep", 1) + `,`, `Analysis:` + strings.Replace(this.Analysis.String(), "RolloutAnalysis", "RolloutAnalysis", 1) + `,`, `SetCanaryScale:` + strings.Replace(this.SetCanaryScale.String(), "SetCanaryScale", "SetCanaryScale", 1) + `,`, - `SetHeaderRouting:` + strings.Replace(this.SetHeaderRouting.String(), "SetHeaderRouting", "SetHeaderRouting", 1) + `,`, + `SetHeaderRoute:` + strings.Replace(this.SetHeaderRoute.String(), "SetHeaderRoute", "SetHeaderRoute", 1) + `,`, + `SetMirrorRoute:` + strings.Replace(this.SetMirrorRoute.String(), "SetMirrorRoute", "SetMirrorRoute", 1) + `,`, `}`, }, "") return s @@ -11634,7 +11999,7 @@ func (this *HeaderRoutingMatch) String() string { } s := strings.Join([]string{`&HeaderRoutingMatch{`, `HeaderName:` + fmt.Sprintf("%v", this.HeaderName) + `,`, - `HeaderValue:` + strings.Replace(strings.Replace(this.HeaderValue.String(), "StringMatch", "StringMatch", 1), `&`, ``, 1) + `,`, + `HeaderValue:` + strings.Replace(this.HeaderValue.String(), "StringMatch", "StringMatch", 1) + `,`, `}`, }, "") return s @@ -11741,6 +12106,17 @@ func (this *KayentaThreshold) String() string { }, "") return s } +func (this *MangedRoutes) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MangedRoutes{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `CanaryRoute:` + fmt.Sprintf("%v", this.CanaryRoute) + `,`, + `}`, + }, "") + return s +} func (this *Measurement) String() string { if this == nil { return "nil" @@ -12235,6 +12611,11 @@ func (this *RolloutTrafficRouting) String() string { if this == nil { return "nil" } + repeatedStringForManagedRoutes := "[]MangedRoutes{" + for _, f := range this.ManagedRoutes { + repeatedStringForManagedRoutes += strings.Replace(strings.Replace(f.String(), "MangedRoutes", "MangedRoutes", 1), `&`, ``, 1) + "," + } + repeatedStringForManagedRoutes += "}" s := strings.Join([]string{`&RolloutTrafficRouting{`, `Istio:` + strings.Replace(this.Istio.String(), "IstioTrafficRouting", "IstioTrafficRouting", 1) + `,`, `Nginx:` + strings.Replace(this.Nginx.String(), "NginxTrafficRouting", "NginxTrafficRouting", 1) + `,`, @@ -12243,6 +12624,29 @@ func (this *RolloutTrafficRouting) String() string { `Ambassador:` + strings.Replace(this.Ambassador.String(), "AmbassadorTrafficRouting", "AmbassadorTrafficRouting", 1) + `,`, `AppMesh:` + strings.Replace(this.AppMesh.String(), "AppMeshTrafficRouting", "AppMeshTrafficRouting", 1) + `,`, `Traefik:` + strings.Replace(this.Traefik.String(), "TraefikTrafficRouting", "TraefikTrafficRouting", 1) + `,`, + `ManagedRoutes:` + repeatedStringForManagedRoutes + `,`, + `}`, + }, "") + return s +} +func (this *RouteMatch) String() string { + if this == nil { + return "nil" + } + keysForHeaders := make([]string, 0, len(this.Headers)) + for k := range this.Headers { + keysForHeaders = append(keysForHeaders, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForHeaders) + mapStringForHeaders := "map[string]StringMatch{" + for _, k := range keysForHeaders { + mapStringForHeaders += fmt.Sprintf("%v: %v,", k, this.Headers[k]) + } + mapStringForHeaders += "}" + s := strings.Join([]string{`&RouteMatch{`, + `Method:` + strings.Replace(this.Method.String(), "StringMatch", "StringMatch", 1) + `,`, + `Path:` + strings.Replace(this.Path.String(), "StringMatch", "StringMatch", 1) + `,`, + `Headers:` + mapStringForHeaders + `,`, `}`, }, "") return s @@ -12309,7 +12713,7 @@ func (this *SetCanaryScale) String() string { }, "") return s } -func (this *SetHeaderRouting) String() string { +func (this *SetHeaderRoute) String() string { if this == nil { return "nil" } @@ -12318,8 +12722,26 @@ func (this *SetHeaderRouting) String() string { repeatedStringForMatch += strings.Replace(strings.Replace(f.String(), "HeaderRoutingMatch", "HeaderRoutingMatch", 1), `&`, ``, 1) + "," } repeatedStringForMatch += "}" - s := strings.Join([]string{`&SetHeaderRouting{`, + s := strings.Join([]string{`&SetHeaderRoute{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Match:` + repeatedStringForMatch + `,`, + `}`, + }, "") + return s +} +func (this *SetMirrorRoute) String() string { + if this == nil { + return "nil" + } + repeatedStringForMatch := "[]RouteMatch{" + for _, f := range this.Match { + repeatedStringForMatch += strings.Replace(strings.Replace(f.String(), "RouteMatch", "RouteMatch", 1), `&`, ``, 1) + "," + } + repeatedStringForMatch += "}" + s := strings.Join([]string{`&SetMirrorRoute{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Match:` + repeatedStringForMatch + `,`, + `Percentage:` + valueToStringGenerated(this.Percentage) + `,`, `}`, }, "") return s @@ -16339,7 +16761,7 @@ func (m *CanaryStep) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SetHeaderRouting", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SetHeaderRoute", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -16366,28 +16788,64 @@ func (m *CanaryStep) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.SetHeaderRouting == nil { - m.SetHeaderRouting = &SetHeaderRouting{} + if m.SetHeaderRoute == nil { + m.SetHeaderRoute = &SetHeaderRoute{} } - if err := m.SetHeaderRouting.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.SetHeaderRoute.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 + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SetMirrorRoute", wireType) } - iNdEx += skippy - } - } + 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.SetMirrorRoute == nil { + m.SetMirrorRoute = &SetMirrorRoute{} + } + if err := m.SetMirrorRoute.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 @@ -19831,6 +20289,9 @@ func (m *HeaderRoutingMatch) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } + if m.HeaderValue == nil { + m.HeaderValue = &StringMatch{} + } if err := m.HeaderValue.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -20967,6 +21428,108 @@ func (m *KayentaThreshold) Unmarshal(dAtA []byte) error { } return nil } +func (m *MangedRoutes) 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: MangedRoutes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MangedRoutes: 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 != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CanaryRoute", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CanaryRoute = bool(v != 0) + 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 *Measurement) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -27137,6 +27700,40 @@ func (m *RolloutTrafficRouting) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ManagedRoutes", 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 + } + m.ManagedRoutes = append(m.ManagedRoutes, MangedRoutes{}) + if err := m.ManagedRoutes[len(m.ManagedRoutes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -27158,7 +27755,7 @@ func (m *RolloutTrafficRouting) Unmarshal(dAtA []byte) error { } return nil } -func (m *RunSummary) Unmarshal(dAtA []byte) error { +func (m *RouteMatch) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -27181,17 +27778,17 @@ func (m *RunSummary) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RunSummary: wiretype end group for non-group") + return fmt.Errorf("proto: RouteMatch: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RunSummary: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RouteMatch: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType) } - m.Count = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -27201,16 +27798,33 @@ func (m *RunSummary) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Count |= int32(b&0x7F) << shift + 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.Method == nil { + m.Method = &StringMatch{} + } + if err := m.Method.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Successful", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) } - m.Successful = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -27220,16 +27834,33 @@ func (m *RunSummary) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Successful |= int32(b&0x7F) << shift + 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.Path == nil { + m.Path = &StringMatch{} + } + if err := m.Path.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Failed", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Headers", wireType) } - m.Failed = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated @@ -27239,36 +27870,253 @@ func (m *RunSummary) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Failed |= int32(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Inconclusive", wireType) + if msglen < 0 { + return ErrInvalidLengthGenerated } - m.Inconclusive = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Inconclusive |= int32(b&0x7F) << shift - if b < 0x80 { - break - } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated } - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + if postIndex > l { + return io.ErrUnexpectedEOF } - m.Error = 0 - for shift := uint(0); ; shift += 7 { + if m.Headers == nil { + m.Headers = make(map[string]StringMatch) + } + var mapkey string + mapvalue := &StringMatch{} + for iNdEx < postIndex { + entryPreIndex := 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) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthGenerated + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthGenerated + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &StringMatch{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Headers[mapkey] = *mapvalue + 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 *RunSummary) 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: RunSummary: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RunSummary: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType) + } + m.Count = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Count |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Successful", wireType) + } + m.Successful = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Successful |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Failed", wireType) + } + m.Failed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Failed |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Inconclusive", wireType) + } + m.Inconclusive = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Inconclusive |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + m.Error = 0 + for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowGenerated } @@ -27838,7 +28686,7 @@ func (m *SetCanaryScale) Unmarshal(dAtA []byte) error { } return nil } -func (m *SetHeaderRouting) Unmarshal(dAtA []byte) error { +func (m *SetHeaderRoute) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -27861,13 +28709,45 @@ func (m *SetHeaderRouting) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: SetHeaderRouting: wiretype end group for non-group") + return fmt.Errorf("proto: SetHeaderRoute: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: SetHeaderRouting: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: SetHeaderRoute: 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 Match", wireType) } @@ -27922,6 +28802,142 @@ func (m *SetHeaderRouting) Unmarshal(dAtA []byte) error { } return nil } +func (m *SetMirrorRoute) 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: SetMirrorRoute: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SetMirrorRoute: 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 Match", 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 + } + m.Match = append(m.Match, RouteMatch{}) + if err := m.Match[len(m.Match)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Percentage", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Percentage = &v + 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 *StickinessConfig) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apis/rollouts/v1alpha1/generated.proto b/pkg/apis/rollouts/v1alpha1/generated.proto index d035b8aecc..e9731cceab 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.proto +++ b/pkg/apis/rollouts/v1alpha1/generated.proto @@ -403,8 +403,13 @@ message CanaryStep { // +optional optional SetCanaryScale setCanaryScale = 5; - // SetHeaderRouting defines the route with specified header name to send 100% of traffic to the canary service - optional SetHeaderRouting setHeaderRouting = 6; + // SetHeaderRoute defines the route with specified header name to send 100% of traffic to the canary service + // +optional + optional SetHeaderRoute setHeaderRoute = 6; + + // SetMirrorRoutes Mirrors traffic that matches rules to a particular destination + // +optional + optional SetMirrorRoute setMirrorRoute = 8; } // CanaryStrategy defines parameters for a Replica Based Canary @@ -824,6 +829,12 @@ message KayentaThreshold { optional int64 marginal = 2; } +message MangedRoutes { + optional string name = 1; + + optional bool canaryRoute = 2; +} + // Measurement is a point in time result value of a single metric, and the time it was measured message Measurement { // Phase is the status of this single measurement @@ -1412,6 +1423,24 @@ message RolloutTrafficRouting { // Traefik holds specific configuration to use Traefik to route traffic optional TraefikTrafficRouting traefik = 7; + + // 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; +} + +message RouteMatch { + // Method What http methods should be mirrored + // +optional + optional StringMatch method = 1; + + // Path What url paths should be mirrored + // +optional + optional StringMatch path = 2; + + // Headers What request with matching headers should be mirrored + // +optional + map headers = 3; } // RunSummary contains the final results from the metric executions @@ -1478,9 +1507,28 @@ message SetCanaryScale { optional bool matchTrafficWeight = 3; } -// SetHeaderRouting defines the route with specified header name to send 100% of traffic to the canary service -message SetHeaderRouting { - repeated HeaderRoutingMatch match = 1; +// SetHeaderRoute defines the route with specified header name to send 100% of traffic to the canary service +message SetHeaderRoute { + // Name this is the name of the route to use for the mirroring of traffic this also needs + // to be included in the `spec.strategy.canary.trafficRouting.managedRoutes` field + optional string name = 1; + + repeated HeaderRoutingMatch match = 2; +} + +message SetMirrorRoute { + // Name this is the name of the route to use for the mirroring of traffic this also needs + // to be included in the `spec.strategy.canary.trafficRouting.managedRoutes` field + optional string name = 1; + + // Match Contains a list of rules that if mated will mirror the traffic to the services + // +optional + repeated RouteMatch match = 2; + + // Services The list of services to mirror the traffic to if the method, path, headers match + // Service string `json:"service" protobuf:"bytes,3,opt,name=service"` + // Percentage What percent of the traffic that matched the rules should be mirrored + optional int32 percentage = 4; } message StickinessConfig { diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index 977aae1e8e..cdbeb3c981 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -81,6 +81,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.KayentaMetric": schema_pkg_apis_rollouts_v1alpha1_KayentaMetric(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.KayentaScope": schema_pkg_apis_rollouts_v1alpha1_KayentaScope(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.KayentaThreshold": schema_pkg_apis_rollouts_v1alpha1_KayentaThreshold(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MangedRoutes": schema_pkg_apis_rollouts_v1alpha1_MangedRoutes(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.Measurement": schema_pkg_apis_rollouts_v1alpha1_Measurement(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MeasurementRetention": schema_pkg_apis_rollouts_v1alpha1_MeasurementRetention(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.Metric": schema_pkg_apis_rollouts_v1alpha1_Metric(ref), @@ -110,12 +111,14 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutStatus": schema_pkg_apis_rollouts_v1alpha1_RolloutStatus(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutStrategy": schema_pkg_apis_rollouts_v1alpha1_RolloutStrategy(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutTrafficRouting": schema_pkg_apis_rollouts_v1alpha1_RolloutTrafficRouting(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RouteMatch": schema_pkg_apis_rollouts_v1alpha1_RouteMatch(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RunSummary": schema_pkg_apis_rollouts_v1alpha1_RunSummary(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SMITrafficRouting": schema_pkg_apis_rollouts_v1alpha1_SMITrafficRouting(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ScopeDetail": schema_pkg_apis_rollouts_v1alpha1_ScopeDetail(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SecretKeyRef": schema_pkg_apis_rollouts_v1alpha1_SecretKeyRef(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetCanaryScale": schema_pkg_apis_rollouts_v1alpha1_SetCanaryScale(ref), - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetHeaderRouting": schema_pkg_apis_rollouts_v1alpha1_SetHeaderRouting(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetHeaderRoute": schema_pkg_apis_rollouts_v1alpha1_SetHeaderRoute(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetMirrorRoute": schema_pkg_apis_rollouts_v1alpha1_SetMirrorRoute(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.StickinessConfig": schema_pkg_apis_rollouts_v1alpha1_StickinessConfig(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.StringMatch": schema_pkg_apis_rollouts_v1alpha1_StringMatch(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.TLSRoute": schema_pkg_apis_rollouts_v1alpha1_TLSRoute(ref), @@ -1233,17 +1236,23 @@ func schema_pkg_apis_rollouts_v1alpha1_CanaryStep(ref common.ReferenceCallback) Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetCanaryScale"), }, }, - "setHeaderRouting": { + "setHeaderRoute": { SchemaProps: spec.SchemaProps{ - Description: "SetHeaderRouting defines the route with specified header name to send 100% of traffic to the canary service", - Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetHeaderRouting"), + Description: "SetHeaderRoute defines the route with specified header name to send 100% of traffic to the canary service", + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetHeaderRoute"), + }, + }, + "setMirrorRoute": { + SchemaProps: spec.SchemaProps{ + Description: "SetMirrorRoutes Mirrors traffic that matches rules to a particular destination", + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetMirrorRoute"), }, }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutAnalysis", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutExperimentStep", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutPause", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetCanaryScale", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetHeaderRouting"}, + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutAnalysis", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutExperimentStep", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutPause", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetCanaryScale", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetHeaderRoute", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.SetMirrorRoute"}, } } @@ -2224,7 +2233,6 @@ func schema_pkg_apis_rollouts_v1alpha1_HeaderRoutingMatch(ref common.ReferenceCa "headerValue": { SchemaProps: spec.SchemaProps{ Description: "HeaderValue the value of the header", - Default: map[string]interface{}{}, Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.StringMatch"), }, }, @@ -2534,6 +2542,32 @@ func schema_pkg_apis_rollouts_v1alpha1_KayentaThreshold(ref common.ReferenceCall } } +func schema_pkg_apis_rollouts_v1alpha1_MangedRoutes(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "canaryRoute": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + func schema_pkg_apis_rollouts_v1alpha1_Measurement(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4159,11 +4193,66 @@ func schema_pkg_apis_rollouts_v1alpha1_RolloutTrafficRouting(ref common.Referenc Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.TraefikTrafficRouting"), }, }, + "managedRoutes": { + SchemaProps: spec.SchemaProps{ + Description: "A list of HTTP routes that Argo Rollouts manages the order of this array also becomes the precedence in the upstream traffic router.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MangedRoutes"), + }, + }, + }, + }, + }, }, }, }, 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.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.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"}, + } +} + +func schema_pkg_apis_rollouts_v1alpha1_RouteMatch(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "method": { + SchemaProps: spec.SchemaProps{ + Description: "Method What http methods should be mirrored", + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.StringMatch"), + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path What url paths should be mirrored", + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.StringMatch"), + }, + }, + "headers": { + SchemaProps: spec.SchemaProps{ + Description: "Headers What request with matching headers should be mirrored", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.StringMatch"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.StringMatch"}, } } @@ -4353,13 +4442,19 @@ func schema_pkg_apis_rollouts_v1alpha1_SetCanaryScale(ref common.ReferenceCallba } } -func schema_pkg_apis_rollouts_v1alpha1_SetHeaderRouting(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_rollouts_v1alpha1_SetHeaderRoute(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "SetHeaderRouting defines the route with specified header name to send 100% of traffic to the canary service", + Description: "SetHeaderRoute defines the route with specified header name to send 100% of traffic to the canary service", Type: []string{"object"}, Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "match": { SchemaProps: spec.SchemaProps{ Type: []string{"array"}, @@ -4381,6 +4476,50 @@ func schema_pkg_apis_rollouts_v1alpha1_SetHeaderRouting(ref common.ReferenceCall } } +func schema_pkg_apis_rollouts_v1alpha1_SetMirrorRoute(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name this is the name of the route to use for the mirroring of traffic this also needs to be included in the `trafficRouting.istio.virtualService.routes` spec", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "match": { + SchemaProps: spec.SchemaProps{ + Description: "Match Contains a list of rules that if mated will mirror the traffic to the services", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RouteMatch"), + }, + }, + }, + }, + }, + "percentage": { + SchemaProps: spec.SchemaProps{ + Description: "Services The list of services to mirror the traffic to if the method, path, headers match Service string `json:\"service\" protobuf:\"bytes,3,opt,name=service\"` Percentage What percent of the traffic that matched the rules should be mirrored", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RouteMatch"}, + } +} + func schema_pkg_apis_rollouts_v1alpha1_StickinessConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/rollouts/v1alpha1/types.go b/pkg/apis/rollouts/v1alpha1/types.go index 04d45fddcd..029c7020ef 100644 --- a/pkg/apis/rollouts/v1alpha1/types.go +++ b/pkg/apis/rollouts/v1alpha1/types.go @@ -363,6 +363,14 @@ type RolloutTrafficRouting struct { AppMesh *AppMeshTrafficRouting `json:"appMesh,omitempty" protobuf:"bytes,6,opt,name=appMesh"` // Traefik holds specific configuration to use Traefik to route traffic Traefik *TraefikTrafficRouting `json:"traefik,omitempty" protobuf:"bytes,7,opt,name=traefik"` + // 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"` +} + +type MangedRoutes struct { + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + CanaryRoute bool `json:"canaryRoute,omitempty" protobuf:"varint,2,opt,name=canaryRoute"` } // TraefikTrafficRouting defines the configuration required to use Traefik as traffic router @@ -554,20 +562,38 @@ type CanaryStep struct { // SetCanaryScale defines how to scale the newRS without changing traffic weight // +optional SetCanaryScale *SetCanaryScale `json:"setCanaryScale,omitempty" protobuf:"bytes,5,opt,name=setCanaryScale"` - // SetHeaderRouting defines the route with specified header name to send 100% of traffic to the canary service - SetHeaderRouting *SetHeaderRouting `json:"setHeaderRouting,omitempty" protobuf:"bytes,6,opt,name=setHeaderRouting"` + // SetHeaderRoute defines the route with specified header name to send 100% of traffic to the canary service + // +optional + SetHeaderRoute *SetHeaderRoute `json:"setHeaderRoute,omitempty" protobuf:"bytes,6,opt,name=setHeaderRoute"` + // SetMirrorRoutes Mirrors traffic that matches rules to a particular destination + // +optional + SetMirrorRoute *SetMirrorRoute `json:"setMirrorRoute,omitempty" protobuf:"bytes,8,opt,name=setMirrorRoute"` } -// SetHeaderRouting defines the route with specified header name to send 100% of traffic to the canary service -type SetHeaderRouting struct { - Match []HeaderRoutingMatch `json:"match,omitempty" protobuf:"bytes,1,rep,name=match"` +type SetMirrorRoute struct { + // Name this is the name of the route to use for the mirroring of traffic this also needs + // to be included in the `spec.strategy.canary.trafficRouting.managedRoutes` field + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + // Match Contains a list of rules that if mated will mirror the traffic to the services + // +optional + Match []RouteMatch `json:"match,omitempty" protobuf:"bytes,2,opt,name=match"` + + // Services The list of services to mirror the traffic to if the method, path, headers match + //Service string `json:"service" protobuf:"bytes,3,opt,name=service"` + // Percentage What percent of the traffic that matched the rules should be mirrored + Percentage *int32 `json:"percentage,omitempty" protobuf:"varint,4,opt,name=percentage"` } -type HeaderRoutingMatch struct { - // HeaderName the name of the request header - HeaderName string `json:"headerName" protobuf:"bytes,1,opt,name=headerName"` - // HeaderValue the value of the header - HeaderValue StringMatch `json:"headerValue" protobuf:"bytes,2,opt,name=headerValue"` +type RouteMatch struct { + // Method What http methods should be mirrored + // +optional + Method *StringMatch `json:"method,omitempty" protobuf:"bytes,1,opt,name=method"` + // Path What url paths should be mirrored + // +optional + Path *StringMatch `json:"path,omitempty" protobuf:"bytes,2,opt,name=path"` + // Headers What request with matching headers should be mirrored + // +optional + Headers map[string]StringMatch `json:"headers,omitempty" protobuf:"bytes,3,opt,name=headers"` } // StringMatch Used to define what type of matching we will use exact, prefix, or regular expression @@ -580,6 +606,21 @@ type StringMatch struct { Regex string `json:"regex,omitempty" protobuf:"bytes,3,opt,name=regex"` } +// SetHeaderRoute defines the route with specified header name to send 100% of traffic to the canary service +type SetHeaderRoute struct { + // Name this is the name of the route to use for the mirroring of traffic this also needs + // to be included in the `spec.strategy.canary.trafficRouting.managedRoutes` field + Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` + Match []HeaderRoutingMatch `json:"match,omitempty" protobuf:"bytes,2,rep,name=match"` +} + +type HeaderRoutingMatch struct { + // HeaderName the name of the request header + HeaderName string `json:"headerName" protobuf:"bytes,1,opt,name=headerName"` + // HeaderValue the value of the header + HeaderValue *StringMatch `json:"headerValue" protobuf:"bytes,2,opt,name=headerValue"` +} + // SetCanaryScale defines how to scale the newRS without changing traffic weight type SetCanaryScale struct { // Weight sets the percentage of replicas the newRS should have diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go index e45cf0dba6..531381c60e 100644 --- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go @@ -708,9 +708,14 @@ func (in *CanaryStep) DeepCopyInto(out *CanaryStep) { *out = new(SetCanaryScale) (*in).DeepCopyInto(*out) } - if in.SetHeaderRouting != nil { - in, out := &in.SetHeaderRouting, &out.SetHeaderRouting - *out = new(SetHeaderRouting) + if in.SetHeaderRoute != nil { + in, out := &in.SetHeaderRoute, &out.SetHeaderRoute + *out = new(SetHeaderRoute) + (*in).DeepCopyInto(*out) + } + if in.SetMirrorRoute != nil { + in, out := &in.SetMirrorRoute, &out.SetMirrorRoute + *out = new(SetMirrorRoute) (*in).DeepCopyInto(*out) } return @@ -1262,7 +1267,11 @@ func (in *GraphiteMetric) DeepCopy() *GraphiteMetric { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HeaderRoutingMatch) DeepCopyInto(out *HeaderRoutingMatch) { *out = *in - out.HeaderValue = in.HeaderValue + if in.HeaderValue != nil { + in, out := &in.HeaderValue, &out.HeaderValue + *out = new(StringMatch) + **out = **in + } return } @@ -1427,6 +1436,22 @@ func (in *KayentaThreshold) DeepCopy() *KayentaThreshold { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MangedRoutes) DeepCopyInto(out *MangedRoutes) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MangedRoutes. +func (in *MangedRoutes) DeepCopy() *MangedRoutes { + if in == nil { + return nil + } + out := new(MangedRoutes) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Measurement) DeepCopyInto(out *Measurement) { *out = *in @@ -2220,6 +2245,11 @@ func (in *RolloutTrafficRouting) DeepCopyInto(out *RolloutTrafficRouting) { *out = new(TraefikTrafficRouting) **out = **in } + if in.ManagedRoutes != nil { + in, out := &in.ManagedRoutes, &out.ManagedRoutes + *out = make([]MangedRoutes, len(*in)) + copy(*out, *in) + } return } @@ -2233,6 +2263,39 @@ func (in *RolloutTrafficRouting) DeepCopy() *RolloutTrafficRouting { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteMatch) DeepCopyInto(out *RouteMatch) { + *out = *in + if in.Method != nil { + in, out := &in.Method, &out.Method + *out = new(StringMatch) + **out = **in + } + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(StringMatch) + **out = **in + } + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make(map[string]StringMatch, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteMatch. +func (in *RouteMatch) DeepCopy() *RouteMatch { + if in == nil { + return nil + } + out := new(RouteMatch) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RunSummary) DeepCopyInto(out *RunSummary) { *out = *in @@ -2324,22 +2387,52 @@ func (in *SetCanaryScale) DeepCopy() *SetCanaryScale { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SetHeaderRouting) DeepCopyInto(out *SetHeaderRouting) { +func (in *SetHeaderRoute) DeepCopyInto(out *SetHeaderRoute) { *out = *in if in.Match != nil { in, out := &in.Match, &out.Match *out = make([]HeaderRoutingMatch, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SetHeaderRoute. +func (in *SetHeaderRoute) DeepCopy() *SetHeaderRoute { + if in == nil { + return nil + } + out := new(SetHeaderRoute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SetMirrorRoute) DeepCopyInto(out *SetMirrorRoute) { + *out = *in + if in.Match != nil { + in, out := &in.Match, &out.Match + *out = make([]RouteMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Percentage != nil { + in, out := &in.Percentage, &out.Percentage + *out = new(int32) + **out = **in } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SetHeaderRouting. -func (in *SetHeaderRouting) DeepCopy() *SetHeaderRouting { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SetMirrorRoute. +func (in *SetMirrorRoute) DeepCopy() *SetMirrorRoute { if in == nil { return nil } - out := new(SetHeaderRouting) + out := new(SetMirrorRoute) in.DeepCopyInto(out) return out } diff --git a/pkg/apis/rollouts/validation/validation.go b/pkg/apis/rollouts/validation/validation.go index d71af46ed4..decc6c932e 100644 --- a/pkg/apis/rollouts/validation/validation.go +++ b/pkg/apis/rollouts/validation/validation.go @@ -34,11 +34,13 @@ const ( // InvalidSetCanaryScaleTrafficPolicy indicates that TrafficRouting, required for SetCanaryScale, is missing InvalidSetCanaryScaleTrafficPolicy = "SetCanaryScale requires TrafficRouting to be set" // InvalidSetHeaderRoutingTrafficPolicy indicates that TrafficRouting, required for SetCanaryScale, is missing - InvalidSetHeaderRoutingTrafficPolicy = "SetHeaderRouting requires TrafficRouting, supports Istio only" - // InvalidSetHeaderRoutingMultipleValuePolicy indicates that SetCanaryScale, has multiple values set - InvalidSetHeaderRoutingMultipleValuePolicy = "SetHeaderRouting match value must have one of the following: exact, regex, prefix" - // InvalidSetHeaderRoutingMissedValuePolicy indicates that SetCanaryScale, has multiple values set - InvalidSetHeaderRoutingMissedValuePolicy = "SetHeaderRouting value missed, match value must have one of the following: exact, regex, prefix" + InvalidSetHeaderRoutingTrafficPolicy = "SetHeaderRoute requires TrafficRouting, supports Istio only" + // InvalidSetMirrorRouteTrafficPolicy indicates that TrafficRouting, required for SetCanaryScale, is missing + InvalidSetMirrorRouteTrafficPolicy = "SetMirrorRoute requires TrafficRouting, supports Istio only" + // InvalidStringMatchMultipleValuePolicy indicates that SetCanaryScale, has multiple values set + InvalidStringMatchMultipleValuePolicy = "StringMatch match value must have exactly one of the following: exact, regex, prefix" + // InvalidStringMatchMissedValuePolicy indicates that SetCanaryScale, has multiple values set + InvalidStringMatchMissedValuePolicy = "StringMatch value missed, match value must have one of the following: exact, regex, prefix" // InvalidDurationMessage indicates the Duration value needs to be greater than 0 InvalidDurationMessage = "Duration needs to be greater than 0" // InvalidMaxSurgeMaxUnavailable indicates both maxSurge and MaxUnavailable can not be set to zero @@ -76,6 +78,11 @@ const ( MissedAlbRootServiceMessage = "Root service field is required for the configuration with ALB and ping-pong feature enabled" // PingPongWithAlbOnlyMessage At this moment ping-pong feature works with the ALB traffic routing only PingPongWithAlbOnlyMessage = "Ping-pong feature works with the ALB traffic routing only" + // InvalidStepMissingManagedRoutesField We have a step configured that requires managedRoutes to be configured which is not. + InvalidStepMissingManagedRoutesField = "Step requires spec.strategy.canary.trafficRouting.managedRoutes to be configured" + // InvalideStepRouteNameNotFoundInManagedRoutes A step has been configured that requires managedRoutes and the route name + // is missing from managedRoutes + InvalideStepRouteNameNotFoundInManagedRoutes = "Steps define a route that dose not exist in spec.strategy.canary.trafficRouting.managedRoutes" ) // allowAllPodValidationOptions allows all pod options to be true for the purposes of rollout pod @@ -280,9 +287,10 @@ func ValidateRolloutStrategyCanary(rollout *v1alpha1.Rollout, fldPath *field.Pat for i, step := range canary.Steps { stepFldPath := fldPath.Child("steps").Index(i) allErrs = append(allErrs, hasMultipleStepsType(step, stepFldPath)...) - if step.Experiment == nil && step.Pause == nil && step.SetWeight == nil && step.Analysis == nil && step.SetCanaryScale == nil && step.SetHeaderRouting == nil { - errVal := fmt.Sprintf("step.Experiment: %t step.Pause: %t step.SetWeight: %t step.Analysis: %t step.SetCanaryScale: %t step.SetHeaderRouting: %t", - step.Experiment == nil, step.Pause == nil, step.SetWeight == nil, step.Analysis == nil, step.SetCanaryScale == nil, step.SetHeaderRouting == nil) + if step.Experiment == nil && step.Pause == nil && step.SetWeight == nil && step.Analysis == nil && step.SetCanaryScale == nil && + step.SetHeaderRoute == nil && step.SetMirrorRoute == nil { + errVal := fmt.Sprintf("step.Experiment: %t step.Pause: %t step.SetWeight: %t step.Analysis: %t step.SetCanaryScale: %t step.SetHeaderRoute: %t step.SetMirrorRoutes: %t", + step.Experiment == nil, step.Pause == nil, step.SetWeight == nil, step.Analysis == nil, step.SetCanaryScale == nil, step.SetHeaderRoute == nil, step.SetMirrorRoute == nil) allErrs = append(allErrs, field.Invalid(stepFldPath, errVal, InvalidStepMessage)) } if step.SetWeight != nil && (*step.SetWeight < 0 || *step.SetWeight > 100) { @@ -294,19 +302,57 @@ func ValidateRolloutStrategyCanary(rollout *v1alpha1.Rollout, fldPath *field.Pat if rollout.Spec.Strategy.Canary != nil && rollout.Spec.Strategy.Canary.TrafficRouting == nil && step.SetCanaryScale != nil { allErrs = append(allErrs, field.Invalid(stepFldPath.Child("setCanaryScale"), step.SetCanaryScale, InvalidSetCanaryScaleTrafficPolicy)) } - if step.SetHeaderRouting != nil { + + if step.SetHeaderRoute != nil { trafficRouting := rollout.Spec.Strategy.Canary.TrafficRouting if trafficRouting == nil || trafficRouting.Istio == nil { - allErrs = append(allErrs, field.Invalid(stepFldPath.Child("setHeaderRouting"), step.SetHeaderRouting, InvalidSetHeaderRoutingTrafficPolicy)) + allErrs = append(allErrs, field.Invalid(stepFldPath.Child("setHeaderRoute"), step.SetHeaderRoute, InvalidSetHeaderRoutingTrafficPolicy)) } - if step.SetHeaderRouting.Match != nil && len(step.SetHeaderRouting.Match) > 0 { - for j, match := range step.SetHeaderRouting.Match { - matchFld := stepFldPath.Child("setHeaderRouting").Child("match").Index(j) + if step.SetHeaderRoute.Match != nil && len(step.SetHeaderRoute.Match) > 0 { + for j, match := range step.SetHeaderRoute.Match { + matchFld := stepFldPath.Child("setHeaderRoute").Child("match").Index(j) allErrs = append(allErrs, hasMultipleMatchValues(match.HeaderValue, matchFld)...) } } } + if step.SetMirrorRoute != nil { + trafficRouting := rollout.Spec.Strategy.Canary.TrafficRouting + if trafficRouting == nil || trafficRouting.Istio == nil { + allErrs = append(allErrs, field.Invalid(stepFldPath.Child("setMirrorRoute"), step.SetMirrorRoute, "SetMirrorRoute requires TrafficRouting, supports Istio only")) + } + if step.SetMirrorRoute.Match != nil && len(step.SetMirrorRoute.Match) > 0 { + for j, match := range step.SetMirrorRoute.Match { + matchFld := stepFldPath.Child("setMirrorRoute").Child("match").Index(j) + if match.Method != nil { + allErrs = append(allErrs, hasMultipleMatchValues(match.Method, matchFld)...) + } + if match.Path != nil { + allErrs = append(allErrs, hasMultipleMatchValues(match.Path, matchFld)...) + } + if match.Method != nil { + allErrs = append(allErrs, hasMultipleMatchValues(match.Method, matchFld)...) + } + } + } + } + + if rollout.Spec.Strategy.Canary.TrafficRouting != nil { + if step.SetHeaderRoute != nil || step.SetMirrorRoute != nil { + if rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes == nil { + allErrs = append(allErrs, field.Invalid(stepFldPath, step, InvalidStepMissingManagedRoutesField)) + } + } + } + if rollout.Spec.Strategy.Canary.TrafficRouting != nil && rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes != nil { + if step.SetHeaderRoute != nil { + allErrs = append(allErrs, ValidateStepRouteFoundInManagedRoute(stepFldPath.Child("setHeaderRoute"), step.SetHeaderRoute.Name, rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes)...) + } + if step.SetMirrorRoute != nil { + allErrs = append(allErrs, ValidateStepRouteFoundInManagedRoute(stepFldPath.Child("setMirrorRoute"), step.SetMirrorRoute.Name, rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes)...) + } + } + analysisRunArgs := make([]v1alpha1.AnalysisRunArgument, 0) if step.Experiment != nil { for tmplIndex, template := range step.Experiment.Templates { @@ -346,6 +392,20 @@ func ValidateRolloutStrategyCanary(rollout *v1alpha1.Rollout, fldPath *field.Pat return allErrs } +func ValidateStepRouteFoundInManagedRoute(stepFldPath *field.Path, stepRoutName string, roManagedRoutes []v1alpha1.MangedRoutes) field.ErrorList { + allErrs := field.ErrorList{} + found := false + for _, managedRoute := range roManagedRoutes { + if stepRoutName == managedRoute.Name { + found = true + } + } + if !found { + allErrs = append(allErrs, field.Invalid(stepFldPath, stepRoutName, InvalideStepRouteNameNotFoundInManagedRoutes)) + } + return allErrs +} + func ValidateRolloutStrategyAntiAffinity(antiAffinity *v1alpha1.AntiAffinity, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if antiAffinity != nil { @@ -413,9 +473,16 @@ func hasMultipleStepsType(s v1alpha1.CanaryStep, fldPath *field.Path) field.Erro return allErrs } -func hasMultipleMatchValues(match v1alpha1.StringMatch, fldPath *field.Path) field.ErrorList { +func hasMultipleMatchValues(match *v1alpha1.StringMatch, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - oneOf := make([]bool, 3) + + if match == nil { + e := field.Invalid(fldPath, match, InvalidStringMatchMissedValuePolicy) + allErrs = append(allErrs, e) + return allErrs + } + + var oneOf []bool oneOf = append(oneOf, match.Exact != "") oneOf = append(oneOf, match.Regex != "") oneOf = append(oneOf, match.Prefix != "") @@ -423,7 +490,7 @@ func hasMultipleMatchValues(match v1alpha1.StringMatch, fldPath *field.Path) fie for i := range oneOf { if oneOf[i] { if hasValue { - e := field.Invalid(fldPath, match, InvalidSetHeaderRoutingMultipleValuePolicy) + e := field.Invalid(fldPath, match, InvalidStringMatchMultipleValuePolicy) allErrs = append(allErrs, e) break } @@ -431,7 +498,7 @@ func hasMultipleMatchValues(match v1alpha1.StringMatch, fldPath *field.Path) fie } } if !hasValue { - e := field.Invalid(fldPath, match, InvalidSetHeaderRoutingMissedValuePolicy) + e := field.Invalid(fldPath, match, InvalidStringMatchMissedValuePolicy) allErrs = append(allErrs, e) } return allErrs diff --git a/pkg/apis/rollouts/validation/validation_test.go b/pkg/apis/rollouts/validation/validation_test.go index fead009023..8888ac29ac 100644 --- a/pkg/apis/rollouts/validation/validation_test.go +++ b/pkg/apis/rollouts/validation/validation_test.go @@ -281,7 +281,7 @@ func TestValidateRolloutStrategyAntiAffinity(t *testing.T) { assert.Equal(t, InvalidAntiAffinityWeightMessage, allErrs[0].Detail) } -func TestValidateRolloutStrategyCanary_SetHeaderRoutingIstio(t *testing.T) { +func TestValidateRolloutStrategyCanarySetHeaderRouteIstio(t *testing.T) { ro := &v1alpha1.Rollout{} ro.Spec.Strategy.Canary = &v1alpha1.CanaryStrategy{ CanaryService: "canary", @@ -293,15 +293,15 @@ func TestValidateRolloutStrategyCanary_SetHeaderRoutingIstio(t *testing.T) { }, } - t.Run("using SetHeaderRouting step without the traffic routing", func(t *testing.T) { + t.Run("using SetHeaderRoute step without the traffic routing", func(t *testing.T) { invalidRo := ro.DeepCopy() invalidRo.Spec.Strategy.Canary.TrafficRouting = nil invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ - SetHeaderRouting: &v1alpha1.SetHeaderRouting{ + SetHeaderRoute: &v1alpha1.SetHeaderRoute{ Match: []v1alpha1.HeaderRoutingMatch{ { HeaderName: "agent", - HeaderValue: v1alpha1.StringMatch{Exact: "chrome"}, + HeaderValue: &v1alpha1.StringMatch{Exact: "chrome"}, }, }, }, @@ -310,14 +310,14 @@ func TestValidateRolloutStrategyCanary_SetHeaderRoutingIstio(t *testing.T) { assert.Equal(t, InvalidSetHeaderRoutingTrafficPolicy, allErrs[0].Detail) }) - t.Run("using SetHeaderRouting step with multiple values", func(t *testing.T) { + t.Run("using SetHeaderRoute step with multiple values", func(t *testing.T) { invalidRo := ro.DeepCopy() invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ - SetHeaderRouting: &v1alpha1.SetHeaderRouting{ + SetHeaderRoute: &v1alpha1.SetHeaderRoute{ Match: []v1alpha1.HeaderRoutingMatch{ { HeaderName: "agent", - HeaderValue: v1alpha1.StringMatch{ + HeaderValue: &v1alpha1.StringMatch{ Exact: "chrome", Regex: "chrome(.*)", }, @@ -326,13 +326,13 @@ func TestValidateRolloutStrategyCanary_SetHeaderRoutingIstio(t *testing.T) { }, }} allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) - assert.Equal(t, InvalidSetHeaderRoutingMultipleValuePolicy, allErrs[0].Detail) + assert.Equal(t, InvalidStringMatchMultipleValuePolicy, allErrs[0].Detail) }) - t.Run("using SetHeaderRouting step with missed values", func(t *testing.T) { + t.Run("using SetHeaderRoute step with missed values", func(t *testing.T) { invalidRo := ro.DeepCopy() invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ - SetHeaderRouting: &v1alpha1.SetHeaderRouting{ + SetHeaderRoute: &v1alpha1.SetHeaderRoute{ Match: []v1alpha1.HeaderRoutingMatch{ { HeaderName: "agent", @@ -341,7 +341,153 @@ func TestValidateRolloutStrategyCanary_SetHeaderRoutingIstio(t *testing.T) { }, }} allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) - assert.Equal(t, InvalidSetHeaderRoutingMissedValuePolicy, allErrs[0].Detail) + assert.Equal(t, InvalidStringMatchMissedValuePolicy, allErrs[0].Detail) + }) + + t.Run("using SetHeaderRoute step without managedRoutes defined but missing route", func(t *testing.T) { + invalidRo := ro.DeepCopy() + invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ + SetHeaderRoute: &v1alpha1.SetHeaderRoute{ + Match: []v1alpha1.HeaderRoutingMatch{ + { + HeaderName: "agent", + HeaderValue: &v1alpha1.StringMatch{Exact: "exact"}, + }, + }, + }, + }} + invalidRo.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(invalidRo.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, v1alpha1.MangedRoutes{ + Name: "not-in-steps", + }) + allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) + assert.Equal(t, InvalideStepRouteNameNotFoundInManagedRoutes, allErrs[0].Detail) + }) +} + +func TestValidateRolloutStrategyCanarySetMirrorRouteIstio(t *testing.T) { + ro := &v1alpha1.Rollout{} + ro.Spec.Strategy.Canary = &v1alpha1.CanaryStrategy{ + CanaryService: "canary", + StableService: "stable", + TrafficRouting: &v1alpha1.RolloutTrafficRouting{ + Istio: &v1alpha1.IstioTrafficRouting{ + VirtualService: &v1alpha1.IstioVirtualService{Name: "virtual-service"}, + }, + }, + } + + t.Run("using SetMirrorRoute step without the traffic routing", func(t *testing.T) { + invalidRo := ro.DeepCopy() + invalidRo.Spec.Strategy.Canary.TrafficRouting = nil + invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ + SetMirrorRoute: &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: nil, + Percentage: nil, + }, + }} + allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) + assert.Equal(t, InvalidSetMirrorRouteTrafficPolicy, allErrs[0].Detail) + }) + + t.Run("using SetMirrorRoute step with multiple values", func(t *testing.T) { + invalidRo := ro.DeepCopy() + invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ + SetMirrorRoute: &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "test", + Prefix: "test", + }, + Path: nil, + Headers: nil, + }}, + Percentage: nil, + }, + }} + allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) + assert.Equal(t, InvalidStringMatchMultipleValuePolicy, allErrs[0].Detail) + }) + + t.Run("using SetMirrorRoute step with missed match and no kind", func(t *testing.T) { + invalidRo := ro.DeepCopy() + invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ + SetMirrorRoute: &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{}, + Path: nil, + Headers: nil, + }}, + Percentage: nil, + }, + }} + allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) + assert.Equal(t, InvalidStringMatchMissedValuePolicy, allErrs[0].Detail) + }) + + t.Run("using SetMirrorRoute step without managedRoutes not defined", func(t *testing.T) { + invalidRo := ro.DeepCopy() + invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ + SetMirrorRoute: &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "exact", + }, + }}, + Percentage: nil, + }, + }} + allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) + assert.Equal(t, InvalidStepMissingManagedRoutesField, allErrs[0].Detail) + }) + + t.Run("using SetMirrorRoute step without managedRoutes defined but missing route", func(t *testing.T) { + invalidRo := ro.DeepCopy() + invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ + SetMirrorRoute: &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + Path: &v1alpha1.StringMatch{ + Prefix: "/", + }, + }}, + Percentage: nil, + }, + }} + invalidRo.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(invalidRo.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, v1alpha1.MangedRoutes{ + Name: "not-in-steps", + }) + allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) + assert.Equal(t, InvalideStepRouteNameNotFoundInManagedRoutes, allErrs[0].Detail) + }) + + t.Run("using SetMirrorRoute step with managedRoutes defined", func(t *testing.T) { + invalidRo := ro.DeepCopy() + invalidRo.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{{ + SetMirrorRoute: &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + Path: &v1alpha1.StringMatch{ + Prefix: "/", + }, + }}, + Percentage: nil, + }, + }} + invalidRo.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(invalidRo.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, v1alpha1.MangedRoutes{ + Name: "test-mirror-1", + }) + allErrs := ValidateRolloutStrategyCanary(invalidRo, field.NewPath("")) + assert.Len(t, allErrs, 0) }) } diff --git a/rollout/canary.go b/rollout/canary.go index d428ca49b9..87228499ab 100644 --- a/rollout/canary.go +++ b/rollout/canary.go @@ -337,7 +337,9 @@ func (c *rolloutContext) completedCurrentCanaryStep() bool { currentStepAr := c.currentArs.CanaryStep analysisExistsAndCompleted := currentStepAr != nil && currentStepAr.Status.Phase.Completed() return analysisExistsAndCompleted && currentStepAr.Status.Phase == v1alpha1.AnalysisPhaseSuccessful - case currentStep.SetHeaderRouting != nil: + case currentStep.SetHeaderRoute != nil: + return true + case currentStep.SetMirrorRoute != nil: return true } return false diff --git a/rollout/mocks/TrafficRoutingReconciler.go b/rollout/mocks/TrafficRoutingReconciler.go index 0e4d6ab1cc..278fbbd37c 100644 --- a/rollout/mocks/TrafficRoutingReconciler.go +++ b/rollout/mocks/TrafficRoutingReconciler.go @@ -13,13 +13,41 @@ type TrafficRoutingReconciler struct { mock.Mock } -// SetHeaderRouting provides a mock function with given fields: headerRouting -func (_m *TrafficRoutingReconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error { - ret := _m.Called(headerRouting) +// RemoveManagedRoutes provides a mock function with given fields: +func (_m *TrafficRoutingReconciler) RemoveManagedRoutes() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetHeaderRoute provides a mock function with given fields: setHeaderRoute +func (_m *TrafficRoutingReconciler) SetHeaderRoute(setHeaderRoute *v1alpha1.SetHeaderRoute) error { + ret := _m.Called(setHeaderRoute) + + var r0 error + if rf, ok := ret.Get(0).(func(*v1alpha1.SetHeaderRoute) error); ok { + r0 = rf(setHeaderRoute) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetMirrorRoute provides a mock function with given fields: setMirrorRoute +func (_m *TrafficRoutingReconciler) SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error { + ret := _m.Called(setMirrorRoute) var r0 error - if rf, ok := ret.Get(0).(func(*v1alpha1.SetHeaderRouting) error); ok { - r0 = rf(headerRouting) + if rf, ok := ret.Get(0).(func(*v1alpha1.SetMirrorRoute) error); ok { + r0 = rf(setMirrorRoute) } else { r0 = ret.Error(0) } diff --git a/rollout/trafficrouting.go b/rollout/trafficrouting.go index 4e1ff4d305..c57ed367c2 100644 --- a/rollout/trafficrouting.go +++ b/rollout/trafficrouting.go @@ -126,7 +126,6 @@ func (c *rolloutContext) reconcileTrafficRouting() error { currentStep, index := replicasetutil.GetCurrentCanaryStep(c.rollout) desiredWeight := int32(0) - var setHeaderRouting *v1alpha1.SetHeaderRouting weightDestinations := make([]v1alpha1.WeightDestination, 0) var canaryHash, stableHash string @@ -151,6 +150,11 @@ func (c *rolloutContext) reconcileTrafficRouting() error { desiredWeight = minInt(desiredWeight, c.rollout.Status.Canary.Weights.Canary.Weight) } } + err := reconciler.RemoveManagedRoutes() + if err != nil { + return err + } + } else if c.newRS == nil || c.newRS.Status.AvailableReplicas == 0 { // when newRS is not available or replicas num is 0. never weight to canary weightDestinations = append(weightDestinations, c.calculateWeightDestinationsFromExperiment()...) @@ -163,9 +167,13 @@ func (c *rolloutContext) reconcileTrafficRouting() error { } else if c.rollout.Status.Canary.Weights != nil { desiredWeight = c.rollout.Status.Canary.Weights.Canary.Weight } + + err := reconciler.RemoveManagedRoutes() + if err != nil { + return err + } } else if index != nil { atDesiredReplicaCount := replicasetutil.AtDesiredReplicaCountsForCanary(c.rollout, c.newRS, c.stableRS, c.otherRSs, nil) - setHeaderRouting = replicasetutil.GetCurrentSetHeaderRouting(c.rollout, *index) if !atDesiredReplicaCount && !c.rollout.Status.PromoteFull { // Use the previous weight since the new RS is not ready for a new weight for i := *index - 1; i >= 0; i-- { @@ -187,6 +195,21 @@ func (c *rolloutContext) reconcileTrafficRouting() error { } } + // We need to check for Generation > 1 because when we first install the rollout we run step 0 this prevents that. + // We could also probably use c.newRS == nil || c.newRS.Status.AvailableReplicas == 0 + if currentStep != nil && c.rollout.ObjectMeta.Generation > 1 { + if currentStep.SetHeaderRoute != nil { + if err = reconciler.SetHeaderRoute(currentStep.SetHeaderRoute); err != nil { + return err + } + } + if currentStep.SetMirrorRoute != nil { + if err = reconciler.SetMirrorRoute(currentStep.SetMirrorRoute); err != nil { + return err + } + } + } + err = reconciler.UpdateHash(canaryHash, stableHash, weightDestinations...) if err != nil { return err @@ -198,10 +221,6 @@ func (c *rolloutContext) reconcileTrafficRouting() error { return err } - if err = reconciler.SetHeaderRouting(setHeaderRouting); err != nil { - return err - } - if modified, newWeights := calculateWeightStatus(c.rollout, canaryHash, stableHash, desiredWeight, weightDestinations...); modified { c.log.Infof("Previous weights: %v", c.rollout.Status.Canary.Weights) c.log.Infof("New weights: %v", newWeights) diff --git a/rollout/trafficrouting/alb/alb.go b/rollout/trafficrouting/alb/alb.go index bc1f4c7de9..55fd0b83be 100644 --- a/rollout/trafficrouting/alb/alb.go +++ b/rollout/trafficrouting/alb/alb.go @@ -113,7 +113,7 @@ func (r *Reconciler) SetWeight(desiredWeight int32, additionalDestinations ...v1 return nil } -func (r *Reconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error { +func (r *Reconciler) SetHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute) error { return nil } @@ -292,3 +292,11 @@ func getDesiredAnnotations(current *ingressutil.Ingress, r *v1alpha1.Rollout, po func (r *Reconciler) UpdateHash(canaryHash, stableHash string, additionalDestinations ...v1alpha1.WeightDestination) error { return nil } + +func (r *Reconciler) SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error { + return nil +} + +func (r *Reconciler) RemoveManagedRoutes() error { + return nil +} diff --git a/rollout/trafficrouting/alb/alb_test.go b/rollout/trafficrouting/alb/alb_test.go index 469be0c4b9..e455926836 100644 --- a/rollout/trafficrouting/alb/alb_test.go +++ b/rollout/trafficrouting/alb/alb_test.go @@ -829,3 +829,69 @@ func TestVerifyWeightWithAdditionalDestinations(t *testing.T) { assert.Equal(t, *status.ALB, *fakeClient.getAlbStatus()) } } + +func TestSetHeaderRoute(t *testing.T) { + ro := fakeRollout(STABLE_SVC, CANARY_SVC, nil, "ingress", 443) + i := ingress("ingress", STABLE_SVC, CANARY_SVC, STABLE_SVC, 443, 10, ro.Name, false) + client := fake.NewSimpleClientset() + k8sI := kubeinformers.NewSharedInformerFactory(client, 0) + k8sI.Extensions().V1beta1().Ingresses().Informer().GetIndexer().Add(i) + ingressWrapper, err := ingressutil.NewIngressWrapper(ingressutil.IngressModeExtensions, client, k8sI) + if err != nil { + t.Fatal(err) + } + r, err := NewReconciler(ReconcilerConfig{ + Rollout: ro, + Client: client, + Recorder: record.NewFakeEventRecorder(), + ControllerKind: schema.GroupVersionKind{Group: "foo", Version: "v1", Kind: "Bar"}, + IngressWrapper: ingressWrapper, + }) + assert.NoError(t, err) + err = r.SetHeaderRoute(&v1alpha1.SetHeaderRoute{ + Name: "set-header", + Match: []v1alpha1.HeaderRoutingMatch{{ + HeaderName: "header-name", + HeaderValue: &v1alpha1.StringMatch{ + Exact: "value", + }, + }}, + }) + assert.Nil(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) + + assert.Len(t, client.Actions(), 0) +} + +func TestSetMirrorRoute(t *testing.T) { + ro := fakeRollout(STABLE_SVC, CANARY_SVC, nil, "ingress", 443) + i := ingress("ingress", STABLE_SVC, CANARY_SVC, STABLE_SVC, 443, 10, ro.Name, false) + client := fake.NewSimpleClientset() + k8sI := kubeinformers.NewSharedInformerFactory(client, 0) + k8sI.Extensions().V1beta1().Ingresses().Informer().GetIndexer().Add(i) + ingressWrapper, err := ingressutil.NewIngressWrapper(ingressutil.IngressModeExtensions, client, k8sI) + if err != nil { + t.Fatal(err) + } + r, err := NewReconciler(ReconcilerConfig{ + Rollout: ro, + Client: client, + Recorder: record.NewFakeEventRecorder(), + ControllerKind: schema.GroupVersionKind{Group: "foo", Version: "v1", Kind: "Bar"}, + IngressWrapper: ingressWrapper, + }) + assert.NoError(t, err) + err = r.SetMirrorRoute(&v1alpha1.SetMirrorRoute{ + Name: "mirror-route", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{Exact: "GET"}, + }}, + }) + assert.Nil(t, err) + err = r.RemoveManagedRoutes() + assert.Nil(t, err) + + assert.Len(t, client.Actions(), 0) +} diff --git a/rollout/trafficrouting/ambassador/ambassador.go b/rollout/trafficrouting/ambassador/ambassador.go index 6ed6c56743..5c50f38f26 100644 --- a/rollout/trafficrouting/ambassador/ambassador.go +++ b/rollout/trafficrouting/ambassador/ambassador.go @@ -115,7 +115,7 @@ func (r *Reconciler) SetWeight(desiredWeight int32, additionalDestinations ...v1 return formatErrors(errs) } -func (r *Reconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error { +func (r *Reconciler) SetHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute) error { return nil } @@ -331,3 +331,11 @@ func (r *Reconciler) sendEvent(eventType, id, msg string) { func (r *Reconciler) UpdateHash(canaryHash, stableHash string, additionalDestinations ...v1alpha1.WeightDestination) error { return nil } + +func (r *Reconciler) SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error { + return nil +} + +func (r *Reconciler) RemoveManagedRoutes() error { + return nil +} diff --git a/rollout/trafficrouting/ambassador/ambassador_test.go b/rollout/trafficrouting/ambassador/ambassador_test.go index 5a558ff25a..bd6e4613de 100644 --- a/rollout/trafficrouting/ambassador/ambassador_test.go +++ b/rollout/trafficrouting/ambassador/ambassador_test.go @@ -519,6 +519,105 @@ func TestReconciler_SetWeight(t *testing.T) { }) } +func TestReconcilerSetHeaderRoute(t *testing.T) { + type fixture struct { + rollout *v1alpha1.Rollout + fakeClient *fakeClient + recorder record.EventRecorder + reconciler *ambassador.Reconciler + } + + setup := func() *fixture { + r := rollout("main-service", "canary-service", []string{"myapp-mapping"}) + fakeClient := &fakeClient{} + rec := record.NewFakeEventRecorder() + l, _ := test.NewNullLogger() + return &fixture{ + rollout: r, + fakeClient: fakeClient, + recorder: rec, + reconciler: &ambassador.Reconciler{ + Rollout: r, + Client: fakeClient, + Recorder: rec, + Log: l.WithContext(context.TODO()), + }, + } + } + t.Run("SetHeaderRoute", func(t *testing.T) { + t.Run("will always return nil", func(t *testing.T) { + // given + t.Parallel() + f := setup() + + // when + err := f.reconciler.SetHeaderRoute(&v1alpha1.SetHeaderRoute{ + Name: "set-header", + Match: []v1alpha1.HeaderRoutingMatch{{ + HeaderName: "header-name", + HeaderValue: &v1alpha1.StringMatch{ + Exact: "value", + }, + }}, + }) + + // then + assert.Nil(t, err) + + err = f.reconciler.RemoveManagedRoutes() + assert.Nil(t, err) + }) + }) +} + +func TestReconcilerSetMirrorRoute(t *testing.T) { + type fixture struct { + rollout *v1alpha1.Rollout + fakeClient *fakeClient + recorder record.EventRecorder + reconciler *ambassador.Reconciler + } + + setup := func() *fixture { + r := rollout("main-service", "canary-service", []string{"myapp-mapping"}) + fakeClient := &fakeClient{} + rec := record.NewFakeEventRecorder() + l, _ := test.NewNullLogger() + return &fixture{ + rollout: r, + fakeClient: fakeClient, + recorder: rec, + reconciler: &ambassador.Reconciler{ + Rollout: r, + Client: fakeClient, + Recorder: rec, + Log: l.WithContext(context.TODO()), + }, + } + } + t.Run("SetMirrorRoute", func(t *testing.T) { + t.Run("will always return nil", func(t *testing.T) { + // given + t.Parallel() + f := setup() + + // when + err := f.reconciler.SetMirrorRoute(&v1alpha1.SetMirrorRoute{ + Name: "mirror-route", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{Exact: "GET"}, + }}, + }) + + // then + assert.Nil(t, err) + + err = f.reconciler.RemoveManagedRoutes() + assert.Nil(t, err) + }) + }) +} + func TestGetMappingService(t *testing.T) { t.Run("will return empty string if service not found", func(t *testing.T) { // given diff --git a/rollout/trafficrouting/appmesh/appmesh.go b/rollout/trafficrouting/appmesh/appmesh.go index cbf2d589f6..f0528f779a 100644 --- a/rollout/trafficrouting/appmesh/appmesh.go +++ b/rollout/trafficrouting/appmesh/appmesh.go @@ -136,7 +136,7 @@ func (r *Reconciler) SetWeight(desiredWeight int32, additionalDestinations ...v1 return nil } -func (r *Reconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error { +func (r *Reconciler) SetHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute) error { return nil } @@ -391,3 +391,11 @@ func GetRouteRule(route map[string]interface{}) (map[string]interface{}, string, return routeRule, routeType, nil } + +func (r *Reconciler) SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error { + return nil +} + +func (r *Reconciler) RemoveManagedRoutes() error { + return nil +} diff --git a/rollout/trafficrouting/appmesh/appmesh_test.go b/rollout/trafficrouting/appmesh/appmesh_test.go index d64974bcc7..5f7ed41843 100644 --- a/rollout/trafficrouting/appmesh/appmesh_test.go +++ b/rollout/trafficrouting/appmesh/appmesh_test.go @@ -482,6 +482,63 @@ func TestUpdateHash(t *testing.T) { } } +func TestSetHeaderRoute(t *testing.T) { + t.Run("not implemented check", func(t *testing.T) { + t.Parallel() + client := testutil.NewFakeDynamicClient() + cfg := ReconcilerConfig{ + Rollout: fakeRollout(), + Client: client, + Recorder: record.NewFakeEventRecorder(), + } + r := NewReconciler(cfg) + + err := r.SetHeaderRoute(&v1alpha1.SetHeaderRoute{ + Name: "set-header", + Match: []v1alpha1.HeaderRoutingMatch{{ + HeaderName: "header-name", + HeaderValue: &v1alpha1.StringMatch{ + Exact: "value", + }, + }}, + }) + assert.Nil(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) + + actions := client.Actions() + assert.Len(t, actions, 0) + }) +} + +func TestSetMirrorRoute(t *testing.T) { + t.Run("not implemented check", func(t *testing.T) { + t.Parallel() + client := testutil.NewFakeDynamicClient() + cfg := ReconcilerConfig{ + Rollout: fakeRollout(), + Client: client, + Recorder: record.NewFakeEventRecorder(), + } + r := NewReconciler(cfg) + + err := r.SetMirrorRoute(&v1alpha1.SetMirrorRoute{ + Name: "mirror-route", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{Exact: "GET"}, + }}, + }) + assert.Nil(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) + + actions := client.Actions() + assert.Len(t, actions, 0) + }) +} + func TestUpdateHashWhenGetStableVirtualNodeFails(t *testing.T) { canaryHash := sampleNewCanaryHash stableHash := sampleNewStableHash diff --git a/rollout/trafficrouting/istio/istio.go b/rollout/trafficrouting/istio/istio.go index b6a63251dd..290a040bef 100644 --- a/rollout/trafficrouting/istio/istio.go +++ b/rollout/trafficrouting/istio/istio.go @@ -25,7 +25,8 @@ import ( const Http = "http" const Tls = "tls" const Type = "Istio" -const HeaderRouteName = "argo-rollouts-header-based-route" + +const SpecHttpNotFound = "spec.http not found" // NewReconciler returns a reconciler struct that brings the Virtual Service into the desired state func NewReconciler(r *v1alpha1.Rollout, client dynamic.Interface, recorder record.EventRecorder, virtualServiceLister, destinationRuleLister dynamiclister.Lister) *Reconciler { @@ -142,6 +143,21 @@ func (r *Reconciler) generateVirtualServicePatches(rolloutVsvcRouteNames []strin stableSubset = r.rollout.Spec.Strategy.Canary.TrafficRouting.Istio.DestinationRule.StableSubsetName } + // Go through all the routes on the Istio Virtual Service looking for routes that are Istio mirror routes as well as on the + // managedRoutes field on the rollout object so that we can update the Istio mirror upon set weight calls + if r.rollout.Spec.Strategy.Canary.TrafficRouting != nil && r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes != nil { + for _, httpRoute := range httpRoutes { + if httpRoute.Mirror != nil { + for _, managedRoute := range r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes { + //Make sure we only add mirror routes from the managedRoutes field to the list of routes to update for setWeight + if managedRoute.Name == httpRoute.Name { + rolloutVsvcRouteNames = append(rolloutVsvcRouteNames, httpRoute.Name) + } + } + } + } + } + // err can be ignored because we already called ValidateHTTPRoutes earlier httpRouteIndexesToPatch, _ := getHttpRouteIndexesToPatch(rolloutVsvcRouteNames, httpRoutes) tlsRouteIndexesToPatch, _ := getTlsRouteIndexesToPatch(rolloutVsvcTLSRoutes, tlsRoutes) @@ -268,8 +284,7 @@ func (r *Reconciler) reconcileVirtualService(obj *unstructured.Unstructured, vsv // Set HTTP Route Slice if len(httpRoutes) > 0 { - err = unstructured.SetNestedSlice(newObj.Object, httpRoutesI, "spec", Http) - if err != nil { + if err := unstructured.SetNestedSlice(newObj.Object, httpRoutesI, "spec", Http); err != nil { return newObj, len(patches) > 0, err } } @@ -536,7 +551,7 @@ func jsonBytesToDestinationRule(dRuleBytes []byte) (*DestinationRule, error) { func GetHttpRoutesI(obj *unstructured.Unstructured) ([]interface{}, error) { httpRoutesI, notFound, err := unstructured.NestedSlice(obj.Object, "spec", Http) if !notFound { - return nil, fmt.Errorf(".spec.http is not defined") + return nil, fmt.Errorf(SpecHttpNotFound) } if err != nil { return nil, err @@ -547,7 +562,7 @@ func GetHttpRoutesI(obj *unstructured.Unstructured) ([]interface{}, error) { func GetTlsRoutesI(obj *unstructured.Unstructured) ([]interface{}, error) { tlsRoutesI, notFound, err := unstructured.NestedSlice(obj.Object, "spec", Tls) if !notFound { - return nil, fmt.Errorf(".spec.tls is not defined") + return nil, fmt.Errorf(SpecHttpNotFound) } if err != nil { return nil, err @@ -613,6 +628,10 @@ func (r *Reconciler) SetWeight(desiredWeight int32, additionalDestinations ...v1 if !modified { continue } + + if err := r.orderRoutes(modifiedVirtualService); err != nil && err.Error() != SpecHttpNotFound { + return err + } _, err = client.Update(ctx, modifiedVirtualService, metav1.UpdateOptions{}) if err == nil { r.log.Debugf("Updated VirtualService: %s", modifiedVirtualService) @@ -649,7 +668,7 @@ func (r *Reconciler) getVirtualService(namespace string, vsvcName string, client return vsvc, err } -func (r *Reconciler) reconcileVirtualServiceRoutes(obj *unstructured.Unstructured, headerRouting *v1alpha1.SetHeaderRouting) (*unstructured.Unstructured, bool, error) { +func (r *Reconciler) reconcileVirtualServiceHeaderRoutes(obj *unstructured.Unstructured, headerRouting *v1alpha1.SetHeaderRoute) (*unstructured.Unstructured, bool, error) { newObj := obj.DeepCopy() // HTTP Routes @@ -687,7 +706,7 @@ func (r *Reconciler) reconcileVirtualServiceRoutes(obj *unstructured.Unstructure return newObj, len(patches) > 0, err } -func (r *Reconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error { +func (r *Reconciler) SetHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute) error { ctx := context.TODO() virtualServices := r.getVirtualServices() for _, virtualService := range virtualServices { @@ -702,13 +721,18 @@ func (r *Reconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) if err != nil { return err } - modifiedVirtualService, modified, err := r.reconcileVirtualServiceRoutes(vsvc, headerRouting) + + modifiedVirtualService, modified, err := r.reconcileVirtualServiceHeaderRoutes(vsvc, headerRouting) if err != nil { return err } if !modified { continue } + + if err := r.orderRoutes(modifiedVirtualService); err != nil && err.Error() != SpecHttpNotFound { + return err + } _, err = client.Update(ctx, modifiedVirtualService, metav1.UpdateOptions{}) if err == nil { r.log.Debugf("Updated VirtualService: %s", modifiedVirtualService) @@ -754,7 +778,7 @@ func (r *Reconciler) getDestinationRule(dRuleSpec *v1alpha1.IstioDestinationRule return origBytes, dRule, dRuleNew, nil } -func (r *Reconciler) generateHeaderBasedPatches(httpRoutes []VirtualServiceHTTPRoute, headerRouting *v1alpha1.SetHeaderRouting, destRuleHost string) virtualServiceRoutePatches { +func (r *Reconciler) generateHeaderBasedPatches(httpRoutes []VirtualServiceHTTPRoute, headerRouting *v1alpha1.SetHeaderRoute, destRuleHost string) virtualServiceRoutePatches { canarySvc := r.rollout.Spec.Strategy.Canary.CanaryService if destRuleHost != "" { canarySvc = destRuleHost @@ -765,13 +789,13 @@ func (r *Reconciler) generateHeaderBasedPatches(httpRoutes []VirtualServiceHTTPR } patches := virtualServiceRoutePatches{} - headerRouteExist := hasHeaderRoute(httpRoutes) + headerRouteExist, index := hasHeaderRoute(headerRouting, httpRoutes) if headerRouteExist { if headerRouting == nil || headerRouting.Match == nil { - deleteHeaderRoute(&patches) + deleteHeaderRoute(index, &patches) } else { - deleteHeaderRoute(&patches) + deleteHeaderRoute(index, &patches) insertHeaderRoute(&patches, canarySvc, canarySubset) } } else if headerRouting != nil && headerRouting.Match != nil { @@ -780,18 +804,18 @@ func (r *Reconciler) generateHeaderBasedPatches(httpRoutes []VirtualServiceHTTPR return patches } -func hasHeaderRoute(httpRoutes []VirtualServiceHTTPRoute) bool { - for _, route := range httpRoutes { - if route.Name == HeaderRouteName { - return true +func hasHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute, httpRoutes []VirtualServiceHTTPRoute) (bool, int) { + for i, route := range httpRoutes { + if headerRouting != nil && route.Name == headerRouting.Name { + return true, i } } - return false + return false, -1 } -func deleteHeaderRoute(patches *virtualServiceRoutePatches) { +func deleteHeaderRoute(index int, patches *virtualServiceRoutePatches) { patch := virtualServiceRoutePatch{ - routeIndex: 0, + routeIndex: index, patchAction: DeleteRoute, } *patches = append(*patches, patch) @@ -807,20 +831,20 @@ func insertHeaderRoute(patches *virtualServiceRoutePatches, host, subset string) *patches = append(*patches, insertPatch) } -func createHeaderRoute(headerRouting *v1alpha1.SetHeaderRouting, patch virtualServiceRoutePatch) map[string]interface{} { +func createHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute, patch virtualServiceRoutePatch) map[string]interface{} { var routeMatches []interface{} for _, hrm := range headerRouting.Match { - routeMatches = append(routeMatches, createRouteMatch(hrm)) + routeMatches = append(routeMatches, createHeaderRouteMatch(hrm)) } canaryDestination := routeDestination(patch.host, patch.subset, 100) return map[string]interface{}{ - "name": HeaderRouteName, + "name": headerRouting.Name, "match": routeMatches, "route": []interface{}{canaryDestination}, } } -func createRouteMatch(hrm v1alpha1.HeaderRoutingMatch) interface{} { +func createHeaderRouteMatch(hrm v1alpha1.HeaderRoutingMatch) interface{} { res := map[string]interface{}{} value := hrm.HeaderValue setMapValueIfNotEmpty(res, "exact", value.Exact) @@ -1029,3 +1053,354 @@ func validateDestinationRule(dRule *v1alpha1.IstioDestinationRule, hasCanarySubs } return nil } + +func (r *Reconciler) SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error { + ctx := context.TODO() + virtualServices := r.getVirtualServices() + + for _, virtualService := range virtualServices { + name := virtualService.Name + namespace, vsvcName := istioutil.GetVirtualServiceNamespaceName(name) + if namespace == "" { + namespace = r.rollout.Namespace + } + + client := r.client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(namespace) + istioVirtualSvc, err := r.getVirtualService(namespace, vsvcName, client, ctx) + if err != nil { + return err + } + + err = r.reconcileVirtualServiceMirrorRoutes(virtualService, istioVirtualSvc, setMirrorRoute) + if err != nil { + return err + } + + if err := r.orderRoutes(istioVirtualSvc); err != nil && err.Error() != SpecHttpNotFound { + return err + } + _, err = client.Update(ctx, istioVirtualSvc, metav1.UpdateOptions{}) + if err == nil { + r.log.Debugf("Updated VirtualService: %s", istioVirtualSvc) + r.recorder.Eventf(r.rollout, record.EventOptions{EventReason: "Updated VirtualService"}, "VirtualService `%s` set mirrorRoute '%v'", vsvcName, setMirrorRoute) + } else { + return err + } + + } + return nil +} + +func (r *Reconciler) reconcileVirtualServiceMirrorRoutes(virtualService v1alpha1.IstioVirtualService, istioVirtualService *unstructured.Unstructured, mirrorRoute *v1alpha1.SetMirrorRoute) error { + destRuleHost, err := r.getDestinationRuleHost() + if err != nil { + return err + } + canarySvc := r.rollout.Spec.Strategy.Canary.CanaryService + if destRuleHost != "" { + canarySvc = destRuleHost + } + var canarySubset string + if r.rollout.Spec.Strategy.Canary.TrafficRouting.Istio.DestinationRule != nil { + canarySubset = r.rollout.Spec.Strategy.Canary.TrafficRouting.Istio.DestinationRule.CanarySubsetName + } + + //Remove mirror route when there is no match rules we require a match on routes for safety so a none listed match + //acts like a removal of the route instead of say routing all traffic + if mirrorRoute.Match == nil { + //Remove mirror route + removeRoute(istioVirtualService, mirrorRoute.Name) + return nil + } + + //Remove route first to avoid duplicates + err = removeRoute(istioVirtualService, mirrorRoute.Name) + if err != nil { + return err + } + + httpRoutes, _, err := getVirtualServiceHttpRoutes(istioVirtualService) + if err != nil { + return err + } + + mR, err := createMirrorRoute(virtualService, httpRoutes, mirrorRoute, canarySvc, canarySubset) + if err != nil { + return err + } + + vsRoutes, found, err := unstructured.NestedSlice(istioVirtualService.Object, "spec", Http) + if err != nil { + return err + } + if !found { + return fmt.Errorf(SpecHttpNotFound) + } + vsRoutes = append([]interface{}{mR}, vsRoutes...) + if err := unstructured.SetNestedSlice(istioVirtualService.Object, vsRoutes, "spec", Http); err != nil { + return err + } + + return nil +} + +// getVirtualServiceHttpRoutes This returns all the http routes from an istio virtual service as both a rollouts wrapped type +// []VirtualServiceHTTPRoute and a []interface{} of VirtualServiceHTTPRoute +func getVirtualServiceHttpRoutes(obj *unstructured.Unstructured) ([]VirtualServiceHTTPRoute, []interface{}, error) { + httpRoutesI, err := GetHttpRoutesI(obj) + if err != nil { + return nil, nil, err + } + routes, err := GetHttpRoutes(httpRoutesI) + if err != nil { + return nil, httpRoutesI, err + } + return routes, httpRoutesI, nil +} + +// createMirrorRoute This returns a map[string]interface{} of an istio virtual service mirror route configuration using the last +// set weight as values for the non-matching destinations and canary service for the matching destination. +func createMirrorRoute(virtualService v1alpha1.IstioVirtualService, httpRoutes []VirtualServiceHTTPRoute, mirrorRouting *v1alpha1.SetMirrorRoute, canarySvc string, subset string) (map[string]interface{}, error) { + var percent int32 + if mirrorRouting.Percentage == nil { + percent = 100 + } else { + percent = *mirrorRouting.Percentage + } + + route, err := getVirtualServiceSetWeightRoute(virtualService.Routes, httpRoutes) + if err != nil { + return nil, err + } + + mirrorRoute := map[string]interface{}{ + "name": mirrorRouting.Name, + "match": mirrorRouting.Match, + "route": route, + "mirror": VirtualServiceDestination{ + Host: canarySvc, + Subset: subset, + }, + "mirrorPercentage": map[string]interface{}{"value": float64(percent)}, + } + + mirrorRouteBytes, err := json.Marshal(mirrorRoute) + if err != nil { + return nil, err + } + + var mirrorRouteI map[string]interface{} + err = json.Unmarshal(mirrorRouteBytes, &mirrorRouteI) + if err != nil { + return nil, err + } + + return mirrorRouteI, nil +} + +// getVirtualServiceSetWeightRoute This functions goes through the list of Istio Virtual service routes and finds the first +// match from the trafficRouting.istio.virtualService[s].routes field and returns the []VirtualServiceRouteDestination array +// from the istio virtual service this can be useful to get the last set destination percentages on the canary route. +func getVirtualServiceSetWeightRoute(rolloutVsvcRouteNames []string, httpRoutes []VirtualServiceHTTPRoute) ([]VirtualServiceRouteDestination, error) { + routeIndexesToPatch, err := getHttpRouteIndexesToPatch(rolloutVsvcRouteNames, httpRoutes) + if err != nil { + return nil, nil + } + for _, routeIndex := range routeIndexesToPatch { + route := httpRoutes[routeIndex] + return route.Route, nil + } + return nil, nil +} + +// removeRoute This functions removes the `routeName` route from the Istio Virtual Service +func removeRoute(istioVirtualService *unstructured.Unstructured, routeName string) error { + vsRoutes, found, err := unstructured.NestedSlice(istioVirtualService.Object, "spec", Http) + if err != nil { + return err + } + if !found { + return fmt.Errorf(SpecHttpNotFound) + } + + var newVsRoutes []interface{} + for _, route := range vsRoutes { + routeMap, ok := route.(map[string]interface{}) + if !ok { + return fmt.Errorf("Could not cast type to map[string]interface{} to find route name in Istio Virtual Service") + } + routeNameIstioSvc, ok := routeMap["name"].(string) + if !ok { + log.Debugf("Could not cast type to string to find route name in Istio Virtual Service, route probably has no name set") + } + if routeName != routeNameIstioSvc { + newVsRoutes = append(newVsRoutes, route) + } + } + if err := unstructured.SetNestedSlice(istioVirtualService.Object, newVsRoutes, "spec", Http); err != nil { + return err + } + return nil +} + +// orderRoutes Is a function that orders the routes based on the managedRoute field in the rollout spec. It then places +// the sorted routes ontop of any other route that is already defined on the Istio Virtual Service. +func (r *Reconciler) orderRoutes(istioVirtualService *unstructured.Unstructured) error { + httpRouteI, found, err := unstructured.NestedSlice(istioVirtualService.Object, "spec", Http) + if err != nil { + return err + } + if !found { + return fmt.Errorf(SpecHttpNotFound) + } + + if r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes == nil { + return nil //Not really and error there is just nothing to sort on + } + + managedRoutes := r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes + httpRoutesWithinManagedRoutes, httpRoutesNotWithinManagedRoutes, err := splitManagedRoutesAndNonManagedRoutes(managedRoutes, httpRouteI) + if err != nil { + return err + } + + finalRoutes, err := getOrderedVirtualServiceRoutes(managedRoutes, httpRoutesWithinManagedRoutes, httpRoutesNotWithinManagedRoutes) + if err != nil { + return err + } + + if err := unstructured.SetNestedSlice(istioVirtualService.Object, finalRoutes, "spec", Http); err != nil { + return err + } + + return nil +} + +// splitManagedRoutesAndNonManagedRoutes This splits the routes from an istio virtual service into two slices +// one slice contains all the routes that are also in the rollouts managedRoutes object and one that contains routes +// that where only in the virtual service (aka routes that where manually added by user) +func splitManagedRoutesAndNonManagedRoutes(managedRoutes []v1alpha1.MangedRoutes, httpRouteI []interface{}) (httpRoutesWithinManagedRoutes []VirtualServiceHTTPRoute, httpRoutesNotWithinManagedRoutes []VirtualServiceHTTPRoute, err error) { + var httpRoutes []VirtualServiceHTTPRoute + + jsonHttpRoutes, err := json.Marshal(httpRouteI) + if err != nil { + return nil, nil, err + } + + if err := json.Unmarshal(jsonHttpRoutes, &httpRoutes); err != nil { + return nil, nil, err + } + + for _, route := range httpRoutes { + var found bool = false + for _, managedRoute := range managedRoutes { + if route.Name == managedRoute.Name { + httpRoutesWithinManagedRoutes = append(httpRoutesWithinManagedRoutes, route) + found = true + break + } + } + if !found { + httpRoutesNotWithinManagedRoutes = append(httpRoutesNotWithinManagedRoutes, route) + } + } + + return httpRoutesWithinManagedRoutes, httpRoutesNotWithinManagedRoutes, nil +} + +// getOrderedVirtualServiceRoutes This returns an []interface{} of istio virtual routes where the routes are ordered based +// on the rollouts managedRoutes field. We take the routes from the rollouts managedRoutes field order them and place them on top +// of routes that are manually defined within the virtual service (aka. routes that users have defined manually) +func getOrderedVirtualServiceRoutes(managedRoutes []v1alpha1.MangedRoutes, httpRoutesWithinManagedRoutes []VirtualServiceHTTPRoute, httpRoutesNotWithinManagedRoutes []VirtualServiceHTTPRoute) ([]interface{}, error) { + var orderedManagedRoutes []VirtualServiceHTTPRoute + for _, route := range managedRoutes { + for _, managedRoute := range httpRoutesWithinManagedRoutes { + if route.Name == managedRoute.Name { + orderedManagedRoutes = append(orderedManagedRoutes, managedRoute) + } + } + } + + allIstioRoutes := append(orderedManagedRoutes, httpRoutesNotWithinManagedRoutes...) + + jsonAllIstioRoutes, err := json.Marshal(allIstioRoutes) + if err != nil { + return nil, err + } + var orderedRoutes []interface{} + if err := json.Unmarshal(jsonAllIstioRoutes, &orderedRoutes); err != nil { + return nil, err + } + + return orderedRoutes, nil +} + +// RemoveManagedRoutes this removes all the routes in all the istio virtual services rollouts is managing by getting two slices +// from the splitManagedRoutesAndNonManagedRoutes function and setting the Istio Virtual Service routes to just the ones not managed +// by rollouts +func (r *Reconciler) RemoveManagedRoutes() error { + ctx := context.TODO() + virtualServices := r.getVirtualServices() + + for _, virtualService := range virtualServices { + name := virtualService.Name + namespace, vsvcName := istioutil.GetVirtualServiceNamespaceName(name) + if namespace == "" { + namespace = r.rollout.Namespace + } + + client := r.client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(namespace) + istioVirtualService, err := r.getVirtualService(namespace, vsvcName, client, ctx) + if err != nil { + return err + } + + httpRouteI, found, err := unstructured.NestedSlice(istioVirtualService.Object, "spec", Http) + if err != nil { + return err + } + if !found { + return fmt.Errorf(SpecHttpNotFound) + } + + if r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes == nil { + return nil //Not really and error there is just nothing to remove + } + + managedRoutes := r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes + if len(managedRoutes) == 0 { + return nil + } + httpRoutesWithinManagedRoutes, httpRoutesNotWithinManagedRoutes, err := splitManagedRoutesAndNonManagedRoutes(managedRoutes, httpRouteI) + if err != nil { + return err + } + + if len(httpRoutesWithinManagedRoutes) == 0 { + //no routes to remove + return nil + } + + jsonNonManagedRoutes, err := json.Marshal(httpRoutesNotWithinManagedRoutes) + if err != nil { + return err + } + var nonManagedRoutesI []interface{} + if err := json.Unmarshal(jsonNonManagedRoutes, &nonManagedRoutesI); err != nil { + return err + } + + if err := unstructured.SetNestedSlice(istioVirtualService.Object, nonManagedRoutesI, "spec", Http); err != nil { + return err + } + + _, err = client.Update(ctx, istioVirtualService, metav1.UpdateOptions{}) + if err == nil { + r.log.Debugf("Updated VirtualService: %s", istioVirtualService) + r.recorder.Eventf(r.rollout, record.EventOptions{EventReason: "Updated VirtualService"}, "VirtualService `%s` removed all managed routes.", vsvcName) + } else { + return err + } + } + return nil +} diff --git a/rollout/trafficrouting/istio/istio_test.go b/rollout/trafficrouting/istio/istio_test.go index 939c16beb0..a006682862 100644 --- a/rollout/trafficrouting/istio/istio_test.go +++ b/rollout/trafficrouting/istio/istio_test.go @@ -486,22 +486,24 @@ func TestHttpReconcileWeightsBaseCase(t *testing.T) { } } -func TestHttpReconcileHeaderRoute_HostBased(t *testing.T) { +func TestHttpReconcileHeaderRouteHostBased(t *testing.T) { + const headerName = "test-header-route" r := &Reconciler{ rollout: rolloutWithHttpRoutes("stable", "canary", "vsvc", []string{"primary"}), } // Test for both the HTTP VS & Mixed VS vsObj := unstructuredutil.StrToUnstructuredUnsafe(regularVsvc) - hr := &v1alpha1.SetHeaderRouting{ + hr := &v1alpha1.SetHeaderRoute{ + Name: headerName, Match: []v1alpha1.HeaderRoutingMatch{ { HeaderName: "agent", - HeaderValue: v1alpha1.StringMatch{Exact: "firefox"}, + HeaderValue: &v1alpha1.StringMatch{Exact: "firefox"}, }, }, } - modifiedVsObj, _, err := r.reconcileVirtualServiceRoutes(vsObj, hr) + modifiedVsObj, _, err := r.reconcileVirtualServiceHeaderRoutes(vsObj, hr) assert.Nil(t, err) assert.NotNil(t, modifiedVsObj) @@ -509,7 +511,7 @@ func TestHttpReconcileHeaderRoute_HostBased(t *testing.T) { httpRoutes := extractHttpRoutes(t, modifiedVsObj) // Assertions - assert.Equal(t, httpRoutes[0].Name, HeaderRouteName) + assert.Equal(t, httpRoutes[0].Name, headerName) checkDestination(t, httpRoutes[0].Route, "canary", 100) assert.Equal(t, len(httpRoutes[0].Route), 1) assert.Equal(t, httpRoutes[1].Name, "primary") @@ -517,8 +519,7 @@ func TestHttpReconcileHeaderRoute_HostBased(t *testing.T) { assert.Equal(t, httpRoutes[2].Name, "secondary") // Reset header routing, expecting removing of the header route - - modifiedVsObj, _, err = r.reconcileVirtualServiceRoutes(vsObj, nil) + modifiedVsObj, _, err = r.reconcileVirtualServiceHeaderRoutes(vsObj, nil) assert.Nil(t, err) assert.NotNil(t, modifiedVsObj) // HTTP Routes @@ -528,7 +529,7 @@ func TestHttpReconcileHeaderRoute_HostBased(t *testing.T) { assert.Equal(t, httpRoutes[1].Name, "secondary") } -func TestHttpReconcileHeaderRoute_SubsetBased(t *testing.T) { +func TestHttpReconcileHeaderRouteSubsetBased(t *testing.T) { ro := rolloutWithDestinationRule() obj := unstructuredutil.StrToUnstructuredUnsafe(` apiVersion: networking.istio.io/v1alpha3 @@ -543,23 +544,25 @@ spec: - name: canary `) + const headerName = "test-header-route" client := testutil.NewFakeDynamicClient(obj) r := NewReconciler(ro, client, record.NewFakeEventRecorder(), nil, nil) client.ClearActions() // Test for both the HTTP VS & Mixed VS vsObj := unstructuredutil.StrToUnstructuredUnsafe(singleRouteSubsetVsvc) - hr := &v1alpha1.SetHeaderRouting{ + hr := &v1alpha1.SetHeaderRoute{ + Name: headerName, Match: []v1alpha1.HeaderRoutingMatch{ { HeaderName: "agent", - HeaderValue: v1alpha1.StringMatch{ + HeaderValue: &v1alpha1.StringMatch{ Regex: "firefox", }, }, }, } - modifiedVsObj, _, err := r.reconcileVirtualServiceRoutes(vsObj, hr) + modifiedVsObj, _, err := r.reconcileVirtualServiceHeaderRoutes(vsObj, hr) assert.Nil(t, err) assert.NotNil(t, modifiedVsObj) @@ -567,11 +570,46 @@ spec: httpRoutes := extractHttpRoutes(t, modifiedVsObj) // Assertions - assert.Equal(t, httpRoutes[0].Name, HeaderRouteName) + assert.Equal(t, httpRoutes[0].Name, headerName) assert.Equal(t, httpRoutes[0].Route[0].Destination.Host, "root-service") assert.Equal(t, httpRoutes[0].Route[0].Destination.Subset, "canary") } +func TestReconcileUpdateHeader(t *testing.T) { + ro := rolloutWithHttpRoutes("stable", "canary", "vsvc", []string{"primary"}) + ro.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(ro.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, v1alpha1.MangedRoutes{ + Name: "test-mirror-1", + }) + AssertReconcileUpdateHeader(t, regularVsvc, ro) +} +func AssertReconcileUpdateHeader(t *testing.T, vsvc string, ro *v1alpha1.Rollout) *dynamicfake.FakeDynamicClient { + obj := unstructuredutil.StrToUnstructuredUnsafe(vsvc) + client := testutil.NewFakeDynamicClient(obj) + vsvcLister, druleLister := getIstioListers(client) + r := NewReconciler(ro, client, record.NewFakeEventRecorder(), vsvcLister, druleLister) + client.ClearActions() + + var setHeader = &v1alpha1.SetHeaderRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.HeaderRoutingMatch{ + { + HeaderName: "browser", + HeaderValue: &v1alpha1.StringMatch{ + Prefix: "Firefox", + }, + }, + }, + } + err := r.SetHeaderRoute(setHeader) + + assert.Nil(t, err) + + actions := client.Actions() + assert.Len(t, actions, 1) + assert.Equal(t, "update", actions[0].GetVerb()) + return client +} + func TestTlsReconcileWeightsBaseCase(t *testing.T) { r := &Reconciler{ rollout: rolloutWithTlsRoutes("stable", "canary", "vsvc", @@ -1719,3 +1757,370 @@ func TestMultipleVirtualServiceReconcileInferredSingleRoute(t *testing.T) { assertHttpRouteWeightChanges(t, httpRoutes[0], "", 10, 90) } } + +func TestHttpReconcileMirrorRoute(t *testing.T) { + ro := rolloutWithHttpRoutes("stable", "canary", "vsvc", []string{"primary"}) + obj := unstructuredutil.StrToUnstructuredUnsafe(regularVsvc) + client := testutil.NewFakeDynamicClient(obj) + vsvcLister, druleLister := getIstioListers(client) + r := NewReconciler(ro, client, record.NewFakeEventRecorder(), vsvcLister, druleLister) + client.ClearActions() + + // Test for both the HTTP VS & Mixed VS + setMirror1 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + }}, + } + var percentage int32 = 90 + setMirror2 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-2", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + }}, + Percentage: &percentage, + } + r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, []v1alpha1.MangedRoutes{{ + Name: "test-mirror-1", + }, { + Name: "test-mirror-2", + }, + }...) + + err := r.SetMirrorRoute(setMirror1) + assert.Nil(t, err) + iVirtualService, err := client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + // HTTP Routes + httpRoutes := extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 3) + + // Assertions + assert.Equal(t, httpRoutes[0].Name, "test-mirror-1") + checkDestination(t, httpRoutes[0].Route, "canary", 0) + assert.Equal(t, httpRoutes[0].Mirror.Host, "canary") + assert.Equal(t, httpRoutes[0].Mirror.Subset, "") + assert.Equal(t, httpRoutes[0].MirrorPercentage.Value, float64(100)) + assert.Equal(t, len(httpRoutes[0].Route), 2) + assert.Equal(t, httpRoutes[1].Name, "primary") + checkDestination(t, httpRoutes[1].Route, "stable", 100) + assert.Equal(t, httpRoutes[2].Name, "secondary") + checkDestination(t, httpRoutes[2].Route, "stable", 100) + + //Delete mirror route + deleteSetMirror := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + } + err = r.SetMirrorRoute(deleteSetMirror) + assert.Nil(t, err) + iVirtualService, err = client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + httpRoutes = extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 2) + assert.Equal(t, httpRoutes[0].Name, "primary") + assert.Equal(t, httpRoutes[1].Name, "secondary") + + //Test adding two routes using fake client then cleaning them up with RemoveManagedRoutes + err = r.SetMirrorRoute(setMirror1) + assert.Nil(t, err) + err = r.SetMirrorRoute(setMirror2) + assert.Nil(t, err) + iVirtualService, err = client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + + httpRoutes = extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 4) + assert.Equal(t, httpRoutes[1].MirrorPercentage.Value, float64(90)) + + r.RemoveManagedRoutes() + iVirtualService, err = client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + + httpRoutes = extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 2) + +} + +func TestHttpReconcileMirrorRouteOrder(t *testing.T) { + ro := rolloutWithHttpRoutes("stable", "canary", "vsvc", []string{"primary"}) + obj := unstructuredutil.StrToUnstructuredUnsafe(regularVsvc) + client := testutil.NewFakeDynamicClient(obj) + vsvcLister, druleLister := getIstioListers(client) + r := NewReconciler(ro, client, record.NewFakeEventRecorder(), vsvcLister, druleLister) + client.ClearActions() + + setMirror1 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + }}, + } + var percentage int32 = 90 + setMirror2 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-2", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "POST", + }, + }}, + Percentage: &percentage, + } + setMirror3 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-3", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + }}, + Percentage: &percentage, + } + r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, []v1alpha1.MangedRoutes{{ + Name: "test-mirror-2", + }, { + Name: "test-mirror-3", + }, { + Name: "test-mirror-1", + }, + }...) + + err := r.SetMirrorRoute(setMirror1) + assert.Nil(t, err) + err = r.SetMirrorRoute(setMirror2) + assert.Nil(t, err) + err = r.SetMirrorRoute(setMirror3) + assert.Nil(t, err) + err = r.SetWeight(40) + assert.Nil(t, err) + iVirtualService, err := client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + // HTTP Routes + httpRoutes := extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 5) + assert.Equal(t, httpRoutes[0].Name, "test-mirror-2") + assert.Equal(t, httpRoutes[0].Route[0].Weight, int64(60)) + assert.Equal(t, httpRoutes[0].Route[1].Weight, int64(40)) + assert.Equal(t, httpRoutes[1].Name, "test-mirror-3") + assert.Equal(t, httpRoutes[2].Name, "test-mirror-1") + assert.Equal(t, httpRoutes[3].Name, "primary") + assert.Equal(t, httpRoutes[4].Name, "secondary") + + //Delete mirror route + deleteSetMirror := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-3", + } + err = r.SetMirrorRoute(deleteSetMirror) + assert.Nil(t, err) + iVirtualService, err = client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + httpRoutes = extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 4) + assert.Equal(t, httpRoutes[0].Name, "test-mirror-2") + assert.Equal(t, httpRoutes[1].Name, "test-mirror-1") + assert.Equal(t, httpRoutes[2].Name, "primary") + assert.Equal(t, httpRoutes[3].Name, "secondary") + + r.RemoveManagedRoutes() + iVirtualService, err = client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + + httpRoutes = extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 2) +} + +func TestHttpReconcileMirrorRouteOrderSingleRouteNoName(t *testing.T) { + ro := rolloutWithHttpRoutes("stable", "canary", "vsvc", []string{"primary"}) + + obj := unstructuredutil.StrToUnstructuredUnsafe(singleRouteVsvc) + client := testutil.NewFakeDynamicClient(obj) + vsvcLister, druleLister := getIstioListers(client) + r := NewReconciler(ro, client, record.NewFakeEventRecorder(), vsvcLister, druleLister) + client.ClearActions() + + setMirror1 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + }}, + } + var percentage int32 = 90 + setMirror2 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-2", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "POST", + }, + }}, + Percentage: &percentage, + } + setMirror3 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-3", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + }}, + Percentage: &percentage, + } + r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, []v1alpha1.MangedRoutes{{ + Name: "test-mirror-2", + }, { + Name: "test-mirror-3", + }, { + Name: "test-mirror-1", + }, + }...) + + err := r.SetMirrorRoute(setMirror1) + assert.Nil(t, err) + err = r.SetMirrorRoute(setMirror2) + assert.Nil(t, err) + err = r.SetMirrorRoute(setMirror3) + assert.Nil(t, err) + iVirtualService, err := client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + // HTTP Routes + httpRoutes := extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 4) + assert.Equal(t, httpRoutes[0].Name, "test-mirror-2") + assert.Equal(t, httpRoutes[1].Name, "test-mirror-3") + assert.Equal(t, httpRoutes[2].Name, "test-mirror-1") + assert.Equal(t, httpRoutes[3].Name, "") + + //Delete mirror route + deleteSetMirror := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-3", + } + err = r.SetMirrorRoute(deleteSetMirror) + assert.Nil(t, err) + iVirtualService, err = client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + httpRoutes = extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 3) + assert.Equal(t, httpRoutes[0].Name, "test-mirror-2") + assert.Equal(t, httpRoutes[1].Name, "test-mirror-1") + assert.Equal(t, httpRoutes[2].Name, "") + + r.RemoveManagedRoutes() + iVirtualService, err = client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + + httpRoutes = extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 1) +} + +func TestHttpReconcileMirrorRouteSubset(t *testing.T) { + + ro := rolloutWithDestinationRule() + const RolloutService = "rollout-service" + const StableSubsetName = "stable-subset" + const CanarySubsetName = "canary-subset" + ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name = "vsvc" + ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Routes = nil + ro.Spec.Strategy.Canary.TrafficRouting.Istio.DestinationRule.StableSubsetName = StableSubsetName + ro.Spec.Strategy.Canary.TrafficRouting.Istio.DestinationRule.CanarySubsetName = CanarySubsetName + dRule := unstructuredutil.StrToUnstructuredUnsafe(` +apiVersion: networking.istio.io/v1alpha3 +kind: DestinationRule +metadata: + name: istio-destrule + namespace: default +spec: + host: rollout-service + subsets: + - name: stable-subset + - name: canary-subset +`) + + //ro := rolloutWithHttpRoutes("stable", "canary", "vsvc", []string{"primary"}) + obj := unstructuredutil.StrToUnstructuredUnsafe(singleRouteSubsetVsvc) + client := testutil.NewFakeDynamicClient(obj, dRule) + vsvcLister, druleLister := getIstioListers(client) + r := NewReconciler(ro, client, record.NewFakeEventRecorder(), vsvcLister, druleLister) + client.ClearActions() + + // Test for both the HTTP VS & Mixed VS + setMirror1 := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + }}, + } + r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(r.rollout.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, []v1alpha1.MangedRoutes{{ + Name: "test-mirror-1", + }, + }...) + + err := r.SetMirrorRoute(setMirror1) + assert.Nil(t, err) + iVirtualService, err := client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + // HTTP Routes + httpRoutes := extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 2) + + // Assertions + assert.Equal(t, httpRoutes[0].Name, "test-mirror-1") + assert.Equal(t, httpRoutes[0].Mirror.Host, RolloutService) + assert.Equal(t, httpRoutes[0].Mirror.Subset, CanarySubsetName) + assert.Equal(t, httpRoutes[0].Route[0].Destination.Host, RolloutService) + assert.Equal(t, httpRoutes[0].Route[0].Destination.Subset, StableSubsetName) + assert.Equal(t, httpRoutes[0].Route[1].Destination.Host, RolloutService) + assert.Equal(t, httpRoutes[0].Route[1].Destination.Subset, CanarySubsetName) + assert.Equal(t, len(httpRoutes[0].Route), 2) + + assert.Equal(t, httpRoutes[1].Name, "") + assert.Nil(t, httpRoutes[1].Mirror) + assert.Equal(t, httpRoutes[1].Route[0].Destination.Host, RolloutService) + assert.Equal(t, httpRoutes[1].Route[0].Destination.Subset, StableSubsetName) + assert.Equal(t, httpRoutes[1].Route[1].Destination.Host, RolloutService) + assert.Equal(t, httpRoutes[1].Route[1].Destination.Subset, CanarySubsetName) + assert.Equal(t, len(httpRoutes[1].Route), 2) + + r.RemoveManagedRoutes() + iVirtualService, err = client.Resource(istioutil.GetIstioVirtualServiceGVR()).Namespace(r.rollout.Namespace).Get(context.TODO(), ro.Spec.Strategy.Canary.TrafficRouting.Istio.VirtualService.Name, metav1.GetOptions{}) + assert.NoError(t, err) + httpRoutes = extractHttpRoutes(t, iVirtualService) + assert.Equal(t, len(httpRoutes), 1) +} + +func TestReconcileUpdateMirror(t *testing.T) { + ro := rolloutWithHttpRoutes("stable", "canary", "vsvc", []string{"primary"}) + ro.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes = append(ro.Spec.Strategy.Canary.TrafficRouting.ManagedRoutes, v1alpha1.MangedRoutes{ + Name: "test-mirror-1", + }) + AssertReconcileUpdateMirror(t, regularVsvc, ro) +} +func AssertReconcileUpdateMirror(t *testing.T, vsvc string, ro *v1alpha1.Rollout) *dynamicfake.FakeDynamicClient { + obj := unstructuredutil.StrToUnstructuredUnsafe(vsvc) + client := testutil.NewFakeDynamicClient(obj) + vsvcLister, druleLister := getIstioListers(client) + r := NewReconciler(ro, client, record.NewFakeEventRecorder(), vsvcLister, druleLister) + client.ClearActions() + + setMirror := &v1alpha1.SetMirrorRoute{ + Name: "test-mirror-1", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{ + Exact: "GET", + }, + }}, + } + err := r.SetMirrorRoute(setMirror) + assert.Nil(t, err) + + actions := client.Actions() + assert.Len(t, actions, 1) + assert.Equal(t, "update", actions[0].GetVerb()) + return client +} diff --git a/rollout/trafficrouting/istio/istio_types.go b/rollout/trafficrouting/istio/istio_types.go index 4e14a60bdf..44e4110419 100644 --- a/rollout/trafficrouting/istio/istio_types.go +++ b/rollout/trafficrouting/istio/istio_types.go @@ -1,6 +1,7 @@ package istio import ( + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -17,8 +18,15 @@ type VirtualServiceSpec struct { // VirtualServiceHTTPRoute is a HTTP route in a VirtualService type VirtualServiceHTTPRoute struct { - Name string `json:"name,omitempty"` - Route []VirtualServiceRouteDestination `json:"route,omitempty"` + Name string `json:"name,omitempty"` + Match []v1alpha1.RouteMatch `json:"match,omitempty"` + Route []VirtualServiceRouteDestination `json:"route,omitempty"` + Mirror *VirtualServiceDestination `json:"mirror,omitempty"` + MirrorPercentage *Percent `json:"mirrorPercentage,omitempty"` +} + +type Percent struct { + Value float64 `json:"value,omitempty"` } // VirtualServiceTLSRoute is a TLS route in a VirtualService diff --git a/rollout/trafficrouting/nginx/nginx.go b/rollout/trafficrouting/nginx/nginx.go index a0d291c121..8bb6353ef3 100644 --- a/rollout/trafficrouting/nginx/nginx.go +++ b/rollout/trafficrouting/nginx/nginx.go @@ -307,7 +307,7 @@ func (r *Reconciler) SetWeight(desiredWeight int32, additionalDestinations ...v1 return nil } -func (r *Reconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error { +func (r *Reconciler) SetHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute) error { return nil } @@ -319,3 +319,11 @@ func (r *Reconciler) VerifyWeight(desiredWeight int32, additionalDestinations .. func (r *Reconciler) UpdateHash(canaryHash, stableHash string, additionalDestinations ...v1alpha1.WeightDestination) error { return nil } + +func (r *Reconciler) SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error { + return nil +} + +func (r *Reconciler) RemoveManagedRoutes() error { + return nil +} diff --git a/rollout/trafficrouting/nginx/nginx_test.go b/rollout/trafficrouting/nginx/nginx_test.go index 2b3a7cf328..a8d3517d8d 100644 --- a/rollout/trafficrouting/nginx/nginx_test.go +++ b/rollout/trafficrouting/nginx/nginx_test.go @@ -678,3 +678,42 @@ func TestReconcileCanaryCreateErrorAlreadyExistsPatch(t *testing.T) { assert.Equal(t, schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "ingresses"}, actions[2].GetResource(), "action: patch canary ingress") } } + +func TestSetHeaderRoute(t *testing.T) { + r := Reconciler{ + cfg: ReconcilerConfig{ + Rollout: fakeRollout("stable-service", "canary-service", "stable-ingress"), + }, + } + err := r.SetHeaderRoute(&v1alpha1.SetHeaderRoute{ + Name: "set-header", + Match: []v1alpha1.HeaderRoutingMatch{{ + HeaderName: "header-name", + HeaderValue: &v1alpha1.StringMatch{ + Exact: "value", + }, + }}, + }) + assert.Nil(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) +} + +func TestSetMirrorRoute(t *testing.T) { + r := Reconciler{ + cfg: ReconcilerConfig{ + Rollout: fakeRollout("stable-service", "canary-service", "stable-ingress"), + }, + } + err := r.SetMirrorRoute(&v1alpha1.SetMirrorRoute{ + Name: "mirror-route", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{Exact: "GET"}, + }}, + }) + assert.Nil(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) +} diff --git a/rollout/trafficrouting/smi/smi.go b/rollout/trafficrouting/smi/smi.go index 8be3584abf..c781799f87 100644 --- a/rollout/trafficrouting/smi/smi.go +++ b/rollout/trafficrouting/smi/smi.go @@ -218,7 +218,7 @@ func (r *Reconciler) SetWeight(desiredWeight int32, additionalDestinations ...v1 return r.patchTrafficSplit(existingTrafficSplit, trafficSplits) } -func (r *Reconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error { +func (r *Reconciler) SetHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute) error { return nil } @@ -351,3 +351,11 @@ func trafficSplitV1Alpha3(ro *v1alpha1.Rollout, objectMeta metav1.ObjectMeta, ro func (r *Reconciler) UpdateHash(canaryHash, stableHash string, additionalDestinations ...v1alpha1.WeightDestination) error { return nil } + +func (r *Reconciler) SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error { + return nil +} + +func (r *Reconciler) RemoveManagedRoutes() error { + return nil +} diff --git a/rollout/trafficrouting/smi/smi_test.go b/rollout/trafficrouting/smi/smi_test.go index 22a69d6098..136e006a14 100644 --- a/rollout/trafficrouting/smi/smi_test.go +++ b/rollout/trafficrouting/smi/smi_test.go @@ -576,3 +576,62 @@ func TestCreateTrafficSplitForMultipleBackends(t *testing.T) { assert.Equal(t, 80, ts3.Spec.Backends[3].Weight) }) } + +func TestReconcileSetHeaderRoute(t *testing.T) { + t.Run("not implemented", func(t *testing.T) { + ro := fakeRollout("stable-service", "canary-service", "", "") + client := fake.NewSimpleClientset() + r, err := NewReconciler(ReconcilerConfig{ + Rollout: ro, + Client: client, + Recorder: record.NewFakeEventRecorder(), + ControllerKind: schema.GroupVersionKind{}, + }) + assert.Nil(t, err) + + err = r.SetHeaderRoute(&v1alpha1.SetHeaderRoute{ + Name: "set-header", + Match: []v1alpha1.HeaderRoutingMatch{{ + HeaderName: "header-name", + HeaderValue: &v1alpha1.StringMatch{ + Exact: "value", + }, + }}, + }) + assert.Nil(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) + + actions := client.Actions() + assert.Len(t, actions, 0) + }) +} + +func TestReconcileSetMirrorRoute(t *testing.T) { + t.Run("not implemented", func(t *testing.T) { + ro := fakeRollout("stable-service", "canary-service", "", "") + client := fake.NewSimpleClientset() + r, err := NewReconciler(ReconcilerConfig{ + Rollout: ro, + Client: client, + Recorder: record.NewFakeEventRecorder(), + ControllerKind: schema.GroupVersionKind{}, + }) + assert.Nil(t, err) + + err = r.SetMirrorRoute(&v1alpha1.SetMirrorRoute{ + Name: "mirror-route", + Match: []v1alpha1.RouteMatch{{ + Method: &v1alpha1.StringMatch{Exact: "GET"}, + }}, + }) + assert.Nil(t, err) + + err = r.RemoveManagedRoutes() + assert.Nil(t, err) + + actions := client.Actions() + assert.Len(t, actions, 0) + }) +} diff --git a/rollout/trafficrouting/traefik/traefik.go b/rollout/trafficrouting/traefik/traefik.go index 22c3f56875..f5b507207f 100644 --- a/rollout/trafficrouting/traefik/traefik.go +++ b/rollout/trafficrouting/traefik/traefik.go @@ -156,7 +156,7 @@ func getService(serviceName string, services []interface{}) (map[string]interfac return selectedService, nil } -func (r *Reconciler) SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error { +func (r *Reconciler) SetHeaderRoute(headerRouting *v1alpha1.SetHeaderRoute) error { return nil } @@ -167,3 +167,11 @@ func (r *Reconciler) VerifyWeight(desiredWeight int32, additionalDestinations .. 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/traefik/traefik_test.go b/rollout/trafficrouting/traefik/traefik_test.go index e3c0b5e774..d7dbbbd297 100644 --- a/rollout/trafficrouting/traefik/traefik_test.go +++ b/rollout/trafficrouting/traefik/traefik_test.go @@ -196,6 +196,61 @@ func TestSetWeight(t *testing.T) { }) } +func TestSetHeaderRoute(t *testing.T) { + t.Run("SetHeaderRoute", func(t *testing.T) { + // Given + t.Parallel() + cfg := ReconcilerConfig{ + Rollout: newRollout(stableServiceName, canaryServiceName, traefikServiceName), + 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, traefikServiceName), + 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{} diff --git a/rollout/trafficrouting/trafficroutingutil.go b/rollout/trafficrouting/trafficroutingutil.go index 08569c4a16..684d10c49f 100644 --- a/rollout/trafficrouting/trafficroutingutil.go +++ b/rollout/trafficrouting/trafficroutingutil.go @@ -11,10 +11,14 @@ type TrafficRoutingReconciler interface { // SetWeight sets the canary weight to the desired weight SetWeight(desiredWeight int32, additionalDestinations ...v1alpha1.WeightDestination) error // SetHeaderRouting sets the header routing step - SetHeaderRouting(headerRouting *v1alpha1.SetHeaderRouting) error + SetHeaderRoute(setHeaderRoute *v1alpha1.SetHeaderRoute) error + // SetMirror sets up the traffic router to mirror traffic to a service + SetMirrorRoute(setMirrorRoute *v1alpha1.SetMirrorRoute) error // VerifyWeight returns true if the canary is at the desired weight and additionalDestinations are at the weights specified // Returns nil if weight verification is not supported or not applicable VerifyWeight(desiredWeight int32, additionalDestinations ...v1alpha1.WeightDestination) (*bool, error) + // RemoveAllRoutes Removes all routes that are managed by rollouts by looking at spec.strategy.canary.trafficRouting.managedRoutes + RemoveManagedRoutes() error // Type returns the type of the traffic routing reconciler Type() string } diff --git a/rollout/trafficrouting_test.go b/rollout/trafficrouting_test.go index 11941a9aea..022e223a2f 100644 --- a/rollout/trafficrouting_test.go +++ b/rollout/trafficrouting_test.go @@ -35,7 +35,7 @@ func newFakeSingleTrafficRoutingReconciler() *mocks.TrafficRoutingReconciler { trafficRoutingReconciler := mocks.TrafficRoutingReconciler{} trafficRoutingReconciler.On("Type").Return("fake") trafficRoutingReconciler.On("SetWeight", mock.Anything, mock.Anything).Return(nil) - trafficRoutingReconciler.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + trafficRoutingReconciler.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) trafficRoutingReconciler.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(true), nil) trafficRoutingReconciler.On("UpdateHash", mock.Anything, mock.Anything, mock.Anything).Return(nil) return &trafficRoutingReconciler @@ -99,7 +99,7 @@ func TestReconcileTrafficRoutingVerifyWeightErr(t *testing.T) { f.fakeTrafficRouting = newUnmockedFakeTrafficRoutingReconciler() f.fakeTrafficRouting.On("UpdateHash", mock.Anything, mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("SetWeight", mock.Anything, mock.Anything).Return(nil) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(false), errors.New("Error message")) f.expectPatchRolloutAction(ro) f.run(getKey(ro, t)) @@ -112,7 +112,7 @@ func TestReconcileTrafficRoutingVerifyWeightFalse(t *testing.T) { f.fakeTrafficRouting = newUnmockedFakeTrafficRoutingReconciler() f.fakeTrafficRouting.On("UpdateHash", mock.Anything, mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("SetWeight", mock.Anything, mock.Anything).Return(nil) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(false), nil) c, i, k8sI := f.newController(noResyncPeriodFunc) enqueued := false @@ -174,7 +174,7 @@ func TestRolloutUseDesiredWeight(t *testing.T) { assert.Equal(t, int32(10), desiredWeight) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(true, nil) f.run(getKey(r2, t)) } @@ -223,7 +223,7 @@ func TestRolloutUseDesiredWeight100(t *testing.T) { assert.Equal(t, int32(100), desiredWeight) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(true, nil) f.run(getKey(r2, t)) } @@ -291,7 +291,7 @@ func TestRolloutWithExperimentStep(t *testing.T) { assert.Equal(t, ex.Status.TemplateStatuses[0].PodTemplateHash, weightDestinations[0].PodTemplateHash) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(func(desiredWeight int32, weightDestinations ...v1alpha1.WeightDestination) error { assert.Equal(t, int32(10), desiredWeight) assert.Equal(t, int32(5), weightDestinations[0].Weight) @@ -312,7 +312,7 @@ func TestRolloutWithExperimentStep(t *testing.T) { assert.Len(t, weightDestinations, 0) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(func(desiredWeight int32, weightDestinations ...v1alpha1.WeightDestination) error { assert.Equal(t, int32(10), desiredWeight) assert.Len(t, weightDestinations, 0) @@ -367,7 +367,7 @@ func TestRolloutUsePreviousSetWeight(t *testing.T) { assert.Equal(t, int32(10), desiredWeight) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything, mock.Anything).Return(pointer.BoolPtr(true), nil) f.fakeTrafficRouting.On("error patching alb ingress", mock.Anything, mock.Anything).Return(true, nil) f.run(getKey(r2, t)) @@ -433,7 +433,8 @@ func TestRolloutUseDynamicWeightOnPromoteFull(t *testing.T) { assert.Equal(t, int32(50), desiredWeight) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("RemoveManagedRoutes", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(true), nil) f.run(getKey(r2, t)) }) @@ -446,7 +447,8 @@ func TestRolloutUseDynamicWeightOnPromoteFull(t *testing.T) { assert.Equal(t, int32(5), desiredWeight) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("RemoveManagedRoutes", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(true), nil) f.run(getKey(r2, t)) }) @@ -490,7 +492,7 @@ func TestRolloutSetWeightToZeroWhenFullyRolledOut(t *testing.T) { assert.Equal(t, int32(0), desiredWeight) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(true), nil) f.run(getKey(r1, t)) } @@ -863,7 +865,8 @@ func TestDynamicScalingDontIncreaseWeightWhenAborted(t *testing.T) { assert.Equal(t, int32(0), desiredWeight) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("RemoveManagedRoutes", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(true), nil) f.run(getKey(r1, t)) } @@ -933,7 +936,8 @@ func TestDynamicScalingDecreaseWeightAccordingToStableAvailabilityWhenAborted(t assert.Equal(t, int32(80), desiredWeight) return nil }) - f.fakeTrafficRouting.On("SetHeaderRouting", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("SetHeaderRoute", mock.Anything, mock.Anything).Return(nil) + f.fakeTrafficRouting.On("RemoveManagedRoutes", mock.Anything, mock.Anything).Return(nil) f.fakeTrafficRouting.On("VerifyWeight", mock.Anything).Return(pointer.BoolPtr(true), nil) f.run(getKey(r1, t)) } diff --git a/test/e2e/header-routing/istio-hr-host.yaml b/test/e2e/header-routing/istio-hr-host.yaml index 6c3fa050bb..8b1036fb42 100644 --- a/test/e2e/header-routing/istio-hr-host.yaml +++ b/test/e2e/header-routing/istio-hr-host.yaml @@ -76,6 +76,8 @@ spec: canaryService: canary-service stableService: stable-service trafficRouting: + managedRoutes: + - name: set-header-1 istio: virtualService: name: rollouts-demo-vsvc @@ -83,20 +85,23 @@ spec: - primary steps: - setWeight: 20 - - setHeaderRouting: + - setHeaderRoute: + name: set-header-1 match: - headerName: agent headerValue: regex: firefox(.*) - pause: { } - - setHeaderRouting: + - setHeaderRoute: + name: set-header-1 match: - headerName: agent headerValue: regex: chrome(.*) - pause: { } - setWeight: 40 - - setHeaderRouting: {} + - setHeaderRoute: + name: set-header-1 - pause: {} --- apiVersion: networking.istio.io/v1alpha3 diff --git a/test/e2e/header_routing_test.go b/test/e2e/header_route_test.go similarity index 85% rename from test/e2e/header_routing_test.go rename to test/e2e/header_route_test.go index a1aab16999..5b9fe7cd16 100644 --- a/test/e2e/header_routing_test.go +++ b/test/e2e/header_route_test.go @@ -15,22 +15,22 @@ import ( "github.com/argoproj/argo-rollouts/test/fixtures" ) -type HeaderRoutingSuite struct { +type HeaderRouteSuite struct { fixtures.E2ESuite } func TestHeaderRoutingSuite(t *testing.T) { - suite.Run(t, new(HeaderRoutingSuite)) + suite.Run(t, new(HeaderRouteSuite)) } -func (s *HeaderRoutingSuite) SetupSuite() { +func (s *HeaderRouteSuite) SetupSuite() { s.E2ESuite.SetupSuite() if !s.IstioEnabled { s.T().SkipNow() } } -func (s *HeaderRoutingSuite) TestIstioHostHeaderRoute() { +func (s *HeaderRouteSuite) TestIstioHostHeaderRoute() { s.Given(). RolloutObjects("@header-routing/istio-hr-host.yaml"). When(). @@ -48,7 +48,7 @@ func (s *HeaderRoutingSuite) TestIstioHostHeaderRoute() { Then(). Assert(func(t *fixtures.Then) { vsvc := t.GetVirtualService() - assert.Equal(s.T(), istio.HeaderRouteName, vsvc.Spec.HTTP[0].Name) + assert.Equal(s.T(), "set-header-1", vsvc.Spec.HTTP[0].Name) assertDestination(s, vsvc.Spec.HTTP[0], "canary-service", int64(100)) assertDestination(s, vsvc.Spec.HTTP[1], "stable-service", int64(80)) assertDestination(s, vsvc.Spec.HTTP[1], "canary-service", int64(20)) @@ -80,7 +80,7 @@ func (s *HeaderRoutingSuite) TestIstioHostHeaderRoute() { }) } -func assertDestination(s *HeaderRoutingSuite, route istio.VirtualServiceHTTPRoute, service string, weight int64) { +func assertDestination(s *HeaderRouteSuite, route istio.VirtualServiceHTTPRoute, service string, weight int64) { for _, destination := range route.Route { if destination.Destination.Host == service { assert.Equal(s.T(), weight, destination.Weight) diff --git a/test/e2e/mirror-route/istio-mirror-host.yaml b/test/e2e/mirror-route/istio-mirror-host.yaml new file mode 100644 index 0000000000..d3f20b0352 --- /dev/null +++ b/test/e2e/mirror-route/istio-mirror-host.yaml @@ -0,0 +1,124 @@ +apiVersion: v1 +kind: Service +metadata: + name: canary-service +spec: + selector: + app: rollouts-demo + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http +--- +apiVersion: v1 +kind: Service +metadata: + name: stable-service +spec: + selector: + app: rollouts-demo + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + +--- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: rollouts-demo-vsvc +spec: + gateways: + - rollouts-demo-gateway + hosts: + - rollouts-demo.com + http: + - name: primary + route: + - destination: + host: stable-service + weight: 80 + - destination: + host: canary-service + weight: 20 + +--- +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollouts-demo +spec: + replicas: 5 + selector: + matchLabels: + app: rollouts-demo + template: + metadata: + labels: + app: rollouts-demo + spec: + containers: + - name: rollouts-demo + image: "nginx:1.19-alpine" + ports: + - name: http + containerPort: 8080 + protocol: TCP + strategy: + canary: + canaryService: canary-service + stableService: stable-service + trafficRouting: + managedRoutes: + - name: mirror-route-1 + - name: mirror-route-2 + istio: + virtualService: + name: rollouts-demo-vsvc + routes: + - primary + steps: + - setWeight: 20 + - setMirrorRoute: + name: mirror-route-1 + percentage: 100 + match: + - path: + prefix: / + - setMirrorRoute: + name: mirror-route-2 + percentage: 80 + match: + - path: + prefix: / + method: + exact: GET + - pause: { } + - setMirrorRoute: + name: mirror-route-1 + percentage: 100 + match: + - path: + prefix: /rewrite + - pause: { } + - setWeight: 40 + - setMirrorRoute: + name: mirror-route-1 + - pause: {} +--- +apiVersion: networking.istio.io/v1alpha3 +kind: Gateway +metadata: + name: rollouts-demo-gateway +spec: + selector: + istio: ingressgateway + servers: + - hosts: + - '*' + port: + name: http + number: 80 + protocol: HTTP diff --git a/test/e2e/mirror_route_test.go b/test/e2e/mirror_route_test.go new file mode 100644 index 0000000000..5190a985f0 --- /dev/null +++ b/test/e2e/mirror_route_test.go @@ -0,0 +1,102 @@ +//go:build e2e +// +build e2e + +package e2e + +import ( + "github.com/argoproj/argo-rollouts/rollout/trafficrouting/istio" + "testing" + "time" + + "github.com/stretchr/testify/suite" + "github.com/tj/assert" + + "github.com/argoproj/argo-rollouts/test/fixtures" +) + +type MirrorRouteSuite struct { + fixtures.E2ESuite +} + +func TestMirrorRouteSuite(t *testing.T) { + suite.Run(t, new(MirrorRouteSuite)) +} + +func (s *MirrorRouteSuite) SetupSuite() { + s.E2ESuite.SetupSuite() + if !s.IstioEnabled { + s.T().SkipNow() + } +} + +func (s *MirrorRouteSuite) TestIstioHostMirrorRoute() { + s.Given(). + RolloutObjects("@mirror-route/istio-mirror-host.yaml"). + When(). + ApplyManifests(). + WaitForRolloutStatus("Healthy"). + Then(). + Assert(func(t *fixtures.Then) { + vsvc := t.GetVirtualService() + assert.Equal(s.T(), "primary", vsvc.Spec.HTTP[0].Name) + }). + When(). + UpdateSpec(). + WaitForRolloutStatus("Paused"). + Sleep(1 * time.Second). + Then(). + Assert(func(t *fixtures.Then) { + vsvc := t.GetVirtualService() + assert.Equal(s.T(), "mirror-route-1", vsvc.Spec.HTTP[0].Name) + assert.Equal(s.T(), float64(100), vsvc.Spec.HTTP[0].MirrorPercentage.Value) + assert.Equal(s.T(), "mirror-route-2", vsvc.Spec.HTTP[1].Name) + assert.Equal(s.T(), float64(80), vsvc.Spec.HTTP[1].MirrorPercentage.Value) + assertMirrorDestination(s, vsvc.Spec.HTTP[0], "stable-service", int64(80)) + assertMirrorDestination(s, vsvc.Spec.HTTP[0], "canary-service", int64(20)) + assertMirrorDestination(s, vsvc.Spec.HTTP[1], "stable-service", int64(80)) + assertMirrorDestination(s, vsvc.Spec.HTTP[1], "canary-service", int64(20)) + + assertMirrorDestination(s, vsvc.Spec.HTTP[2], "stable-service", int64(80)) + assertMirrorDestination(s, vsvc.Spec.HTTP[2], "canary-service", int64(20)) + }). + When(). + PromoteRollout(). + WaitForRolloutStatus("Paused"). + Sleep(1 * time.Second). + Then(). + When(). + PromoteRollout(). + WaitForRolloutStatus("Paused"). + Sleep(1 * time.Second). + Then(). + Assert(func(t *fixtures.Then) { + vsvc := t.GetVirtualService() + assert.Equal(s.T(), "mirror-route-2", vsvc.Spec.HTTP[0].Name) + assertMirrorDestination(s, vsvc.Spec.HTTP[0], "stable-service", int64(60)) + assertMirrorDestination(s, vsvc.Spec.HTTP[0], "canary-service", int64(40)) + assert.Equal(s.T(), "primary", vsvc.Spec.HTTP[1].Name) + assertMirrorDestination(s, vsvc.Spec.HTTP[1], "stable-service", int64(60)) + assertMirrorDestination(s, vsvc.Spec.HTTP[1], "canary-service", int64(40)) + }). + When(). + PromoteRolloutFull(). + WaitForRolloutStatus("Healthy"). + Sleep(1 * time.Second). + Then(). + Assert(func(t *fixtures.Then) { + vsvc := t.GetVirtualService() + assert.Equal(s.T(), 1, len(vsvc.Spec.HTTP)) + assertMirrorDestination(s, vsvc.Spec.HTTP[0], "stable-service", int64(100)) + assertMirrorDestination(s, vsvc.Spec.HTTP[0], "canary-service", int64(0)) + }) +} + +func assertMirrorDestination(s *MirrorRouteSuite, route istio.VirtualServiceHTTPRoute, service string, weight int64) { + for _, destination := range route.Route { + if destination.Destination.Host == service { + assert.Equal(s.T(), weight, destination.Weight) + return + } + } + assert.Fail(s.T(), "Could not find the destination for service: %s", service) +} diff --git a/ui/package.json b/ui/package.json index 898f96cf6b..15627c8da8 100644 --- a/ui/package.json +++ b/ui/package.json @@ -5,6 +5,7 @@ "dependencies": { "argo-ui": "git+https://github.com/argoproj/argo-ui.git", "classnames": "2.2.6", + "isomorphic-fetch": "^3.0.0", "moment": "^2.29.1", "moment-timezone": "^0.5.33", "portable-fetch": "^3.0.0", diff --git a/ui/src/app/components/rollout/rollout.tsx b/ui/src/app/components/rollout/rollout.tsx index 9b2a962244..6c3f3a0393 100644 --- a/ui/src/app/components/rollout/rollout.tsx +++ b/ui/src/app/components/rollout/rollout.tsx @@ -5,7 +5,9 @@ import {Key, KeybindingContext} from 'react-keyhooks'; import {useHistory, useParams} from 'react-router-dom'; import { GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CanaryStep, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1HeaderRoutingMatch, GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RolloutExperimentTemplate, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute, RolloutReplicaSetInfo, RolloutRolloutInfo, RolloutServiceApi, @@ -128,11 +130,11 @@ export const RolloutWidget = (props: {rollout: RolloutRolloutInfo; interactive?: interactive={ interactive ? { - editState: interactive.editState, - setImage: (container, image, tag) => { - interactive.api.rolloutServiceSetRolloutImage({}, interactive.namespace, rollout.objectMeta?.name, container, image, tag); - }, - } + editState: interactive.editState, + setImage: (container, image, tag) => { + interactive.api.rolloutServiceSetRolloutImage({}, interactive.namespace, rollout.objectMeta?.name, container, image, tag); + }, + } : null } /> @@ -279,6 +281,8 @@ const Step = (props: {step: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1 const [openedTemplate, setOpenedTemplate] = React.useState(''); const [openCanary, setOpenCanary] = React.useState(false); const [openAnalysis, setOpenAnalysis] = React.useState(false); + const [openHeader, setOpenHeader] = React.useState(false); + const [openMirror, setOpenMirror] = React.useState(false); let icon: string; let content = ''; @@ -308,12 +312,27 @@ const Step = (props: {step: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1 icon = 'fa-flask'; } + if (props.step.setMirrorRoute) { + content = `Set Mirror: ${props.step.setMirrorRoute.name}`; + if(!props.step.setMirrorRoute.match) { + content = `Remove Mirror: ${props.step.setMirrorRoute.name}`; + } + } + + if (props.step.setHeaderRoute) { + content = `Set Header: ${props.step.setHeaderRoute.name}`; + if (!props.step.setHeaderRoute.match) { + content = `Remove Header: ${props.step.setHeaderRoute.name}`; + } + } + return (
{icon && } {content} {unit} @@ -327,6 +346,17 @@ const Step = (props: {step: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1 )} + + {props.step.setHeaderRoute && props.step.setHeaderRoute.match &&( + setOpenHeader(!openHeader)}> + + + )} + {props.step.setMirrorRoute && props.step.setMirrorRoute.match && ( + setOpenMirror(!openMirror)}> + + + )}
{props.step.experiment?.templates && (
@@ -351,17 +381,15 @@ const Step = (props: {step: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1
)} {props.step?.setCanaryScale && openCanary && } + {props.step?.setHeaderRoute && openHeader && } + {props.step?.setMirrorRoute && openMirror && }
{!props.last && }
); }; -const ExperimentWidget = ({ - template, - opened, - onToggle, -}: { +const ExperimentWidget = ({template, opened, onToggle}: { template: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RolloutExperimentTemplate; opened: boolean; onToggle: (name: string) => void; @@ -395,3 +423,81 @@ const WidgetItem = ({values}: {values: Record}) => { ); }; + +const WidgetItemSetMirror = ({value}: {value: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute}) => { + if (!value) return null; + return ( + + +
Name
+
{value.name}
+
Percentage
+
{value.percentage}
+ {Object.values(value.match).map((val, index) => { + if (!val) return null; + let stringMatcherValue = "" + let stringMatcherType = "" + let fragments = [] + if (val.path != null) { + if(val.path.exact != null) {stringMatcherValue = val.path.exact; stringMatcherType="Exact"} + if(val.path.prefix != null) {stringMatcherValue = val.path.prefix; stringMatcherType="Prefix"} + if(val.path.regex != null) {stringMatcherValue = val.path.regex; stringMatcherType="Regex"} + fragments.push( + +
{index} - Path ({stringMatcherType})
+
{stringMatcherValue}
+
+ ); + } + if (val.method != null) { + if(val.method.exact != null) {stringMatcherValue = val.method.exact; stringMatcherType="Exact"} + if(val.method.prefix != null) {stringMatcherValue = val.method.prefix; stringMatcherType="Prefix"} + if(val.method.regex != null) {stringMatcherValue = val.method.regex; stringMatcherType="Regex"} + fragments.push( + +
{index} - Method ({stringMatcherType})
+
{stringMatcherValue}
+
+ ); + } + return fragments + })} +
+
+ ); +}; + +const WidgetItemSetHeader = ({values}: {values: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1HeaderRoutingMatch[]}) => { + if (!values) return null; + return ( + + {values.map((record) => { + if (!record.headerName) return null; + if (!record.headerValue) return null; + + let headerValue = "" + let headerValueType = "" + if (record.headerValue.regex) { + headerValue = record.headerValue.regex + headerValueType = "Regex" + } + if (record.headerValue.prefix) { + headerValue = record.headerValue.prefix + headerValueType = "Prefix" + } + if (record.headerValue.exact) { + headerValue = record.headerValue.exact + headerValueType = "Exact" + } + return ( + +
Name
+
{record.headerName}
+
{headerValueType}
+
{headerValue}
+
+ ); + })} +
+ ); +}; diff --git a/ui/src/models/rollout/generated/api.ts b/ui/src/models/rollout/generated/api.ts index 1b4336be76..f5b15cb5e9 100644 --- a/ui/src/models/rollout/generated/api.ts +++ b/ui/src/models/rollout/generated/api.ts @@ -526,10 +526,16 @@ export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CanaryStep setCanaryScale?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetCanaryScale; /** * - * @type {GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRouting} + * @type {GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRoute} * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CanaryStep */ - setHeaderRouting?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRouting; + setHeaderRoute?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRoute; + /** + * + * @type {GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CanaryStep + */ + setMirrorRoute?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute; } /** * @@ -748,6 +754,25 @@ export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1IstioVirtua */ tlsRoutes?: Array; } +/** + * + * @export + * @interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MangedRoutes + */ +export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MangedRoutes { + /** + * + * @type {string} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MangedRoutes + */ + name?: string; + /** + * + * @type {boolean} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MangedRoutes + */ + canaryRoute?: boolean; +} /** * MeasurementRetention defines the settings for retaining the number of measurements during the analysis. * @export @@ -1477,6 +1502,37 @@ export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RolloutTraf * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RolloutTrafficRouting */ traefik?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1TraefikTrafficRouting; + /** + * A list of HTTP routes that Argo Rollouts manages, the order of this array also becomes the precedence in the upstream traffic router. + * @type {Array} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RolloutTrafficRouting + */ + managedRoutes?: Array; +} +/** + * + * @export + * @interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RouteMatch + */ +export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RouteMatch { + /** + * + * @type {GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1StringMatch} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RouteMatch + */ + method?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1StringMatch; + /** + * + * @type {GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1StringMatch} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RouteMatch + */ + path?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1StringMatch; + /** + * + * @type {{ [key: string]: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1StringMatch; }} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1RouteMatch + */ + headers?: { [key: string]: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1StringMatch; }; } /** * @@ -1525,16 +1581,47 @@ export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetCanarySc /** * * @export - * @interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRouting + * @interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRoute */ -export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRouting { +export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRoute { + /** + * + * @type {string} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRoute + */ + name?: string; /** * * @type {Array} - * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRouting + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetHeaderRoute */ match?: Array; } +/** + * + * @export + * @interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute + */ +export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute { + /** + * + * @type {string} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute + */ + name?: string; + /** + * + * @type {Array} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute + */ + match?: Array; + /** + * + * @type {number} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1SetMirrorRoute + */ + percentage?: number; +} /** * * @export diff --git a/utils/replicaset/canary.go b/utils/replicaset/canary.go index 8a015f7b7d..bfe5146a51 100644 --- a/utils/replicaset/canary.go +++ b/utils/replicaset/canary.go @@ -474,14 +474,14 @@ func GetCurrentSetWeight(rollout *v1alpha1.Rollout) int32 { return 0 } -func GetCurrentSetHeaderRouting(rollout *v1alpha1.Rollout, index int32) *v1alpha1.SetHeaderRouting { +func GetCurrentSetHeaderRouting(rollout *v1alpha1.Rollout, index int32) *v1alpha1.SetHeaderRoute { if int32(len(rollout.Spec.Strategy.Canary.Steps)) == index { index-- } for i := index; i >= 0; i-- { step := rollout.Spec.Strategy.Canary.Steps[i] - if step.SetHeaderRouting != nil { - return step.SetHeaderRouting + if step.SetHeaderRoute != nil { + return step.SetHeaderRoute } } return nil diff --git a/utils/replicaset/canary_test.go b/utils/replicaset/canary_test.go index e111043362..04aa91391c 100644 --- a/utils/replicaset/canary_test.go +++ b/utils/replicaset/canary_test.go @@ -1017,28 +1017,28 @@ func TestGetCurrentSetWeight(t *testing.T) { func TestGetCurrentSetHeaderRouting(t *testing.T) { rollout := newRollout(10, 10, intstr.FromInt(0), intstr.FromInt(1), "", "", nil, nil) - setHeaderRoutingStep := v1alpha1.SetHeaderRouting{ + setHeaderRoutingStep := v1alpha1.SetHeaderRoute{ Match: []v1alpha1.HeaderRoutingMatch{ { HeaderName: "agent", }, { HeaderName: "agent2", - HeaderValue: v1alpha1.StringMatch{Exact: "value"}, + HeaderValue: &v1alpha1.StringMatch{Exact: "value"}, }, { HeaderName: "agent3", - HeaderValue: v1alpha1.StringMatch{Regex: "regexValue(.*)"}, + HeaderValue: &v1alpha1.StringMatch{Regex: "regexValue(.*)"}, }, }, } rollout.Spec.Strategy.Canary.Steps = []v1alpha1.CanaryStep{ {SetWeight: ptr.Int32(20)}, {Pause: &v1alpha1.RolloutPause{}}, - {SetHeaderRouting: &setHeaderRoutingStep}, + {SetHeaderRoute: &setHeaderRoutingStep}, {SetWeight: ptr.Int32(40)}, {Pause: &v1alpha1.RolloutPause{}}, - {SetHeaderRouting: &v1alpha1.SetHeaderRouting{}}, + {SetHeaderRoute: &v1alpha1.SetHeaderRoute{}}, } assert.Nil(t, GetCurrentSetHeaderRouting(rollout, 0)) diff --git a/utils/rollout/rolloututil.go b/utils/rollout/rolloututil.go index c9243c3215..399b0ef44d 100644 --- a/utils/rollout/rolloututil.go +++ b/utils/rollout/rolloututil.go @@ -150,7 +150,7 @@ func waitingForBlueGreenPostPromotionAnalysis(ro *v1alpha1.Rollout) bool { return false } -// CanaryStepString returns a string representation of a canary step +// CanaryStepString returns a string representation of a canary step spec.strategy func CanaryStepString(c v1alpha1.CanaryStep) string { if c.SetWeight != nil { return fmt.Sprintf("setWeight: %d", *c.SetWeight)