diff --git a/.gitignore b/.gitignore index 94c8d9cea7ba..022d165ce1d9 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ git-ask-pass.sh /pkg/apiclient/_.secondary.swagger.json /pkg/apiclient/clusterworkflowtemplate/cluster-workflow-template.swagger.json /pkg/apiclient/cronworkflow/cron-workflow.swagger.json +/pkg/apiclient/event/event.swagger.json /pkg/apiclient/info/info.swagger.json /pkg/apiclient/workflow/workflow.swagger.json /pkg/apiclient/workflowarchive/workflow-archive.swagger.json diff --git a/Makefile b/Makefile index af2929cf5ffd..d0b31da317e7 100644 --- a/Makefile +++ b/Makefile @@ -96,6 +96,7 @@ SWAGGER_FILES := pkg/apiclient/_.primary.swagger.json \ pkg/apiclient/_.secondary.swagger.json \ pkg/apiclient/clusterworkflowtemplate/cluster-workflow-template.swagger.json \ pkg/apiclient/cronworkflow/cron-workflow.swagger.json \ + pkg/apiclient/event/event.swagger.json \ pkg/apiclient/info/info.swagger.json \ pkg/apiclient/workflow/workflow.swagger.json \ pkg/apiclient/workflowarchive/workflow-archive.swagger.json \ @@ -456,8 +457,7 @@ dist/kubeified.swagger.json: dist/swaggifed.swagger.json dist/kubernetes.swagger go run ./hack kubeifyswagger dist/swaggifed.swagger.json dist/kubeified.swagger.json api/openapi-spec/swagger.json: dist/kubeified.swagger.json - swagger flatten --with-flatten minimal --with-flatten remove-unused dist/kubeified.swagger.json > dist/swagger.json - mv dist/swagger.json api/openapi-spec/swagger.json + swagger flatten --with-flatten minimal --with-flatten remove-unused dist/kubeified.swagger.json -o api/openapi-spec/swagger.json swagger validate api/openapi-spec/swagger.json go test ./api/openapi-spec diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 486e64db817c..303df80cd371 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -658,6 +658,47 @@ } } }, + "/api/v1/events/{namespace}/{discriminator}": { + "post": { + "tags": [ + "EventService" + ], + "operationId": "ReceiveEvent", + "parameters": [ + { + "type": "string", + "description": "The namespace for the io.argoproj.workflow.v1alpha1. This can be empty if the client has cluster scoped permissions.\nIf empty, then the event is \"broadcast\" to workflow event binding in all namespaces.", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Optional discriminator for the io.argoproj.workflow.v1alpha1. This should almost always be empty.\nUsed for edge-cases where the event payload alone is not provide enough information to discriminate the event.\nThis MUST NOT be used as security mechanism, e.g. to allow two clients to use the same access token, or\nto support webhooks on unsecured server. Instead, use access tokens.\nThis is made available as `discriminator` in the event binding selector (`/spec/event/selector)`", + "name": "discriminator", + "in": "path", + "required": true + }, + { + "description": "The event itself can be any data.", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Item" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.EventResponse" + } + } + } + } + }, "/api/v1/info": { "get": { "tags": [ @@ -2364,6 +2405,21 @@ } } }, + "io.argoproj.workflow.v1alpha1.Event": { + "type": "object", + "required": [ + "selector" + ], + "properties": { + "selector": { + "description": "Selector (https://github.com/antonmedv/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"`", + "type": "string" + } + } + }, + "io.argoproj.workflow.v1alpha1.EventResponse": { + "type": "object" + }, "io.argoproj.workflow.v1alpha1.ExecutorConfig": { "description": "ExecutorConfig holds configurations of an executor container.", "type": "object", @@ -3336,6 +3392,22 @@ } } }, + "io.argoproj.workflow.v1alpha1.Submit": { + "type": "object", + "required": [ + "workflowTemplateRef" + ], + "properties": { + "arguments": { + "description": "Arguments extracted from the event and then set as arguments to the workflow created.", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Arguments" + }, + "workflowTemplateRef": { + "description": "WorkflowTemplateRef the workflow template to submit", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.WorkflowTemplateRef" + } + } + }, "io.argoproj.workflow.v1alpha1.SubmitOpts": { "description": "SubmitOpts are workflow submission options", "type": "object", @@ -3821,6 +3893,10 @@ "description": "Default specifies a value to be used if retrieving the value from the specified source fails", "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString" }, + "event": { + "description": "Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`", + "type": "string" + }, "jqFilter": { "description": "JQFilter expression against the resource object in resource templates", "type": "string" @@ -3934,6 +4010,46 @@ "io.argoproj.workflow.v1alpha1.WorkflowDeleteResponse": { "type": "object" }, + "io.argoproj.workflow.v1alpha1.WorkflowEventBinding": { + "description": "WorkflowEventBinding is the definition of an event resource", + "type": "object", + "required": [ + "metadata", + "spec" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.io.k8s.community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.io.k8s.community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.WorkflowEventBindingSpec" + } + } + }, + "io.argoproj.workflow.v1alpha1.WorkflowEventBindingSpec": { + "type": "object", + "required": [ + "event" + ], + "properties": { + "event": { + "description": "Event is the event to bind to", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Event" + }, + "submit": { + "description": "Submit is the workflow template to submit", + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.Submit" + } + } + }, "io.argoproj.workflow.v1alpha1.WorkflowLintRequest": { "type": "object", "properties": { @@ -7386,4 +7502,4 @@ "type": "basic" } } -} +} \ No newline at end of file diff --git a/cmd/argo/commands/server.go b/cmd/argo/commands/server.go index cd93179b295b..690e9f0a8654 100644 --- a/cmd/argo/commands/server.go +++ b/cmd/argo/commands/server.go @@ -25,15 +25,17 @@ import ( func NewServerCommand() *cobra.Command { var ( - authModes []string - configMap string - port int - baseHRef string - secure bool - htst bool - namespaced bool // --namespaced - managedNamespace string // --managed-namespace - enableOpenBrowser bool + authModes []string + configMap string + port int + baseHRef string + secure bool + htst bool + namespaced bool // --namespaced + managedNamespace string // --managed-namespace + enableOpenBrowser bool + eventOperationQueueSize int + eventWorkerCount int ) var command = cobra.Command{ @@ -94,16 +96,18 @@ See %s`, help.ArgoSever), } opts := apiserver.ArgoServerOpts{ - BaseHRef: baseHRef, - TLSConfig: tlsConfig, - HSTS: htst, - Namespace: namespace, - WfClientSet: wflientset, - KubeClientset: kubeConfig, - RestConfig: config, - AuthModes: modes, - ManagedNamespace: managedNamespace, - ConfigName: configMap, + BaseHRef: baseHRef, + TLSConfig: tlsConfig, + HSTS: htst, + Namespace: namespace, + WfClientSet: wflientset, + KubeClientset: kubeConfig, + RestConfig: config, + AuthModes: modes, + ManagedNamespace: managedNamespace, + ConfigName: configMap, + EventOperationQueueSize: eventOperationQueueSize, + EventWorkerCount: eventWorkerCount, } browserOpenFunc := func(url string) {} if enableOpenBrowser { @@ -135,5 +139,7 @@ See %s`, help.ArgoSever), command.Flags().BoolVar(&namespaced, "namespaced", false, "run as namespaced mode") command.Flags().StringVar(&managedNamespace, "managed-namespace", "", "namespace that watches, default to the installation namespace") command.Flags().BoolVarP(&enableOpenBrowser, "browser", "b", false, "enable automatic launching of the browser [local mode]") + command.Flags().IntVar(&eventOperationQueueSize, "event-operation-queue-size", 16, "how many events operations that can be queued at once") + command.Flags().IntVar(&eventWorkerCount, "event-worker-count", 4, "how many event workers to run") return &command } diff --git a/docs/cli/argo_server.md b/docs/cli/argo_server.md index e3945b7c51a3..57daddecc6d7 100644 --- a/docs/cli/argo_server.md +++ b/docs/cli/argo_server.md @@ -20,15 +20,17 @@ See https://github.com/argoproj/argo/blob/master/docs/argo-server.md ### Options ``` - --auth-mode stringArray API server authentication mode. Any 1 or more length permutation of: client,server,sso (default [server]) - --basehref string Value for base href in index.html. Used if the server is running behind reverse proxy under subpath different from /. Defaults to the environment variable BASE_HREF. (default "/") - -b, --browser enable automatic launching of the browser [local mode] - --configmap string Name of K8s configmap to retrieve workflow controller configuration (default "workflow-controller-configmap") - -h, --help help for server - --hsts Whether or not we should add a HTTP Secure Transport Security header. This only has effect if secure is enabled. (default true) - --managed-namespace string namespace that watches, default to the installation namespace - --namespaced run as namespaced mode - -p, --port int Port to listen on (default 2746) + --auth-mode stringArray API server authentication mode. Any 1 or more length permutation of: client,server,sso (default [server]) + --basehref string Value for base href in index.html. Used if the server is running behind reverse proxy under subpath different from /. Defaults to the environment variable BASE_HREF. (default "/") + -b, --browser enable automatic launching of the browser [local mode] + --configmap string Name of K8s configmap to retrieve workflow controller configuration (default "workflow-controller-configmap") + --event-operation-queue-size int how many events operations that can be queued at once (default 16) + --event-worker-count int how many event workers to run (default 4) + -h, --help help for server + --hsts Whether or not we should add a HTTP Secure Transport Security header. This only has effect if secure is enabled. (default true) + --managed-namespace string namespace that watches, default to the installation namespace + --namespaced run as namespaced mode + -p, --port int Port to listen on (default 2746) ``` ### Options inherited from parent commands diff --git a/docs/events.md b/docs/events.md new file mode 100644 index 000000000000..f8088e17d17c --- /dev/null +++ b/docs/events.md @@ -0,0 +1,150 @@ +# Events + +> v2.11 and after + +![alpha](assets/alpha.svg) + +## Overview + +To support external webhooks, we have this endpoint `/api/v1/events/{namespace}/{descriminator}`. Events can be sent to that can be any JSON data. + +These events can submit *workflow templates* or *cluster workflow templates*. + +You may also wish to read about [webhooks](webhooks.md). + +## Authentication and Security + +Clients wanting to send events to the endpoint need an [access token](access-token.md). + +It is only possible to submit workflow templates your access token has access to: [example role](manifests/quick-start/base/webhooks/submit-workflow-template-role.yaml). + +Example (note the trailing slash): + +```bash +curl https://localhost:2746/api/v1/events/argo/ \ + -H "Authorization: $ARGO_TOKEN" \ + -d '{"message": "hello"}' +``` + +With a **discriminator**: + +```bash +curl https://localhost:2746/api/v1/events/argo/my-discriminator \ + -H "Authorization: $ARGO_TOKEN" \ + -d '{"message": "hello"}' +``` + +The event endpoint will always return in under 10 seconds because the event will be queued and processed asynchronously. This means you will not be notified synchronously of failure. It will return a failure (503) if the event processing queue is full. + +!!! Warning "Processing Order" + Events may not always be processed in the order they are received. + +## Submitting A Workflow From A Workflow Template + +A workflow template will be submitted (i.e. workflow created from it) and that can be created using parameters from the event itself. +The following example will be trigger by an event with "message" in the payload. That message will be used as an argument for the created workflow. + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowEventBinding +metadata: + name: event-consumer +spec: + event: + selector: payload.message != "" && metadata["x-argo"] == ["true"] && discriminator == "my-discriminator" + submit: + workflowTemplateRef: + name: my-wf-tmple + arguments: + parameters: + - name: message + valueFrom: + event: payload.message +``` + +Event: + +```bash +curl $ARGO_SERVER/api/v1/events/argo/my-discriminator \ + -H "Authorization: $ARGO_TOKEN" \ + -H "X-Argo-E2E: true" \ + -d '{"message": "hello events"}' +``` + +!!! Warning "Malformed Expressions" + If the expression is malformed, this is logged. It is not visible in logs or the UI. + +## Event Expression Syntax and the Event Expression Environment + +**Event expressions** are expressions that are evaluated over the **event expression environment**. + +### Expression Syntax + +Because the endpoint accepts any JSON data, it is the user's responsibility to write a suitable expression to correctly filter the events they are interested in. Therefore, DO NOT assume the existence of any fields, and guard against them using a nil check. + +[Learn more about expression syntax](https://github.com/antonmedv/expr). + +### Expression Environment + +The event environment contains: + +* `payload` the event payload. +* `metadata` event metadata, including HTTP headers. +* `discriminator` the discriminator from the URL. + +### Payload + +This is the JSON payload of the event. + +Example: + +``` +payload.repository.clone_url == "http://gihub.com/argoproj/argo" +``` + +### MetaData + +Metadata is data about the event, this includes **headers**: + +#### Headers + +HTTP header names are lowercase and only include those that have `x-` as their prefix. Their values are lists, not single values. + +* Wrong: `metadata["X-Github-Event"] = "push"` +* Wrong: `metadata["x-github-event"] = "push"` +* Wrong: `metadata["X-Github-Event"] = ["push"]` +* Wrong: `metadata["github-event"] = ["push"]` +* Wrong: `metadata["authorization"] = ["push"]` +* Right: `metadata["x-github-event"] = ["push"]` + +Example: + +``` +metadata["x-argo"] == ["yes"] +``` + +### Discriminator + +This is only for edge-cases where neither the payload, or metadata provide enough information to discriminate. Typically, it should be empty and ignored. + +Example: + +``` +discriminator == "my-discriminator" +``` + +## High-Availability + +!!! Warning "Run Minimum 2 Replicas" + You MUST run a minimum of two Argo Server replicas if you do not want to lose events. + +If you are processing large numbers of events, you may need to scale up the Argo Server to handle them. By default, a single Argo Server can be processing 64 events before the endpoint will start returning 503 errors. + +Vertically you can: + +* Increase the size of the event operation queue `--event-operation-queue-size` (good for temporary event bursts). +* Increase the number of workers `--event-worker-count` (good for sustained numbers of events). + +Horizontally you can: + +* Run more Argo Servers (good for sustained numbers of events AND high-availability). diff --git a/docs/fields.md b/docs/fields.md index 16727f8f14c7..0f9f65e8f4f5 100644 --- a/docs/fields.md +++ b/docs/fields.md @@ -3395,6 +3395,7 @@ ValueFrom describes a location in which to obtain the value to a parameter | Field Name | Field Type | Description | |:----------:|:----------:|---------------| |`default`|[`IntOrString`](#intorstring)|Default specifies a value to be used if retrieving the value from the specified source fails| +|`event`|`string`|Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`| |`jqFilter`|`string`|JQFilter expression against the resource object in resource templates| |`jsonPath`|`string`|JSONPath of a resource to retrieve an output parameter value from in resource templates| |`parameter`|`string`|Parameter reference to a step or dag task in which to retrieve an output parameter value from (e.g. '{{steps.mystep.outputs.myparam}}')| diff --git a/docs/manifests b/docs/manifests new file mode 120000 index 000000000000..5f2a77a3d25d --- /dev/null +++ b/docs/manifests @@ -0,0 +1 @@ +../manifests \ No newline at end of file diff --git a/docs/submit-workflow-via-automation.md b/docs/submit-workflow-via-automation.md index ebb24a241185..934bb5420f30 100644 --- a/docs/submit-workflow-via-automation.md +++ b/docs/submit-workflow-via-automation.md @@ -4,6 +4,8 @@ > v2.8 and after +You may want to consider using [events](events.md) or [webhooks](webhooks.md) instead. + Firstly, to do any automation, you'll need an ([access token](access-token.md)). For this example, our role needs extra permissions: ```sh @@ -59,6 +61,8 @@ See also: See also: * [access token](access-token.md) +* [events](events.md) +* [webhooks](webhooks.md) * [resuming a workflow via automation](resuming-workflow-via-automation.md) * [one workflow submitting another](workflow-submitting-workflow.md) * [async pattern](async-pattern.md) diff --git a/docs/swagger.md b/docs/swagger.md index e6f6be98b10c..3363152af368 100644 --- a/docs/swagger.md +++ b/docs/swagger.md @@ -272,6 +272,23 @@ Argo | ---- | ----------- | ------ | | 200 | A successful response. | [io.argoproj.workflow.v1alpha1.CronWorkflowDeletedResponse](#io.argoproj.workflow.v1alpha1.cronworkflowdeletedresponse) | +### /api/v1/events/{namespace}/{discriminator} + +#### POST +##### Parameters + +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| namespace | path | The namespace for the io.argoproj.workflow.v1alpha1. This can be empty if the client has cluster scoped permissions. If empty, then the event is "broadcast" to workflow event binding in all namespaces. | Yes | string | +| discriminator | path | Optional discriminator for the io.argoproj.workflow.v1alpha1. This should almost always be empty. Used for edge-cases where the event payload alone is not provide enough information to discriminate the event. This MUST NOT be used as security mechanism, e.g. to allow two clients to use the same access token, or to support webhooks on unsecured server. Instead, use access tokens. This is made available as `discriminator` in the event binding selector (`/spec/event/selector)` | Yes | string | +| body | body | The event itself can be any data. | Yes | [io.argoproj.workflow.v1alpha1.Item](#io.argoproj.workflow.v1alpha1.item) | + +##### Responses + +| Code | Description | Schema | +| ---- | ----------- | ------ | +| 200 | A successful response. | [io.argoproj.workflow.v1alpha1.EventResponse](#io.argoproj.workflow.v1alpha1.eventresponse) | + ### /api/v1/info #### GET @@ -970,6 +987,18 @@ DAGTemplate is a template subtype for directed acyclic graph templates | target | string | Target are one or more names of targets to execute in a DAG | No | | tasks | [ [io.argoproj.workflow.v1alpha1.DAGTask](#io.argoproj.workflow.v1alpha1.dagtask) ] | Tasks are a list of DAG tasks | Yes | +#### io.argoproj.workflow.v1alpha1.Event + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| selector | string | Selector () that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == "test"` | Yes | + +#### io.argoproj.workflow.v1alpha1.EventResponse + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| io.argoproj.workflow.v1alpha1.EventResponse | object | | | + #### io.argoproj.workflow.v1alpha1.ExecutorConfig ExecutorConfig holds configurations of an executor container. @@ -1362,6 +1391,13 @@ Sequence expands a workflow step into numeric range | format | string | Format is a printf format string to format the value in the sequence | No | | start | [io.k8s.apimachinery.pkg.util.intstr.IntOrString](#io.k8s.apimachinery.pkg.util.intstr.intorstring) | Number at which to start the sequence (default: 0) | No | +#### io.argoproj.workflow.v1alpha1.Submit + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| arguments | [io.argoproj.workflow.v1alpha1.Arguments](#io.argoproj.workflow.v1alpha1.arguments) | Arguments extracted from the event and then set as arguments to the workflow created. | No | +| workflowTemplateRef | [io.argoproj.workflow.v1alpha1.WorkflowTemplateRef](#io.argoproj.workflow.v1alpha1.workflowtemplateref) | WorkflowTemplateRef the workflow template to submit | Yes | + #### io.argoproj.workflow.v1alpha1.SubmitOpts SubmitOpts are workflow submission options @@ -1527,6 +1563,7 @@ ValueFrom describes a location in which to obtain the value to a parameter | Name | Type | Description | Required | | ---- | ---- | ----------- | -------- | | default | [io.k8s.apimachinery.pkg.util.intstr.IntOrString](#io.k8s.apimachinery.pkg.util.intstr.intorstring) | Default specifies a value to be used if retrieving the value from the specified source fails | No | +| event | string | Selector () that is evaluated against the event to get the value of the parameter. E.g. `payload.message` | No | | jqFilter | string | JQFilter expression against the resource object in resource templates | No | | jsonPath | string | JSONPath of a resource to retrieve an output parameter value from in resource templates | No | | parameter | string | Parameter reference to a step or dag task in which to retrieve an output parameter value from (e.g. '{{steps.mystep.outputs.myparam}}') | No | @@ -1574,6 +1611,24 @@ Workflow is the definition of a workflow resource | ---- | ---- | ----------- | -------- | | io.argoproj.workflow.v1alpha1.WorkflowDeleteResponse | object | | | +#### io.argoproj.workflow.v1alpha1.WorkflowEventBinding + +WorkflowEventBinding is the definition of an event resource + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| apiVersion | string | APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: | No | +| kind | string | Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: | No | +| metadata | [io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta](#io.k8s.apimachinery.pkg.apis.meta.v1.objectmeta) | | Yes | +| spec | [io.argoproj.workflow.v1alpha1.WorkflowEventBindingSpec](#io.argoproj.workflow.v1alpha1.workfloweventbindingspec) | | Yes | + +#### io.argoproj.workflow.v1alpha1.WorkflowEventBindingSpec + +| Name | Type | Description | Required | +| ---- | ---- | ----------- | -------- | +| event | [io.argoproj.workflow.v1alpha1.Event](#io.argoproj.workflow.v1alpha1.event) | Event is the event to bind to | Yes | +| submit | [io.argoproj.workflow.v1alpha1.Submit](#io.argoproj.workflow.v1alpha1.submit) | Submit is the workflow template to submit | No | + #### io.argoproj.workflow.v1alpha1.WorkflowLintRequest | Name | Type | Description | Required | diff --git a/docs/webhooks.md b/docs/webhooks.md new file mode 100644 index 000000000000..30e557d1c2de --- /dev/null +++ b/docs/webhooks.md @@ -0,0 +1,23 @@ +# Webhooks + +> v2.11 and after + +![alpha](assets/alpha.svg) + +Many clients can send events via the [events](events.md) API endpoint using a standard authorization header. However, for clients that are unable to do so (e.g. because they use signature verification as proof of origin), additional configuration is required. + +In the namespace that will receive the event, create [access token](access-token.md) resources for your client: + +* A role with permissions to get workflow templates and to create a workflow: [example](manifests/quick-start/base/webhooks/submit-workflow-template-role.yaml) +* A service account for the client: [example](manifests/quick-start/base/webhooks/github.com-sa.yaml). +* A binding of the account to the role: [example](manifests/quick-start/base/webhooks/github.com-rolebinding.yaml) + +Additionally create: + +* A secret named "argo-workflows-webhook-clients" listing the service accounts: [example](manifests/quick-start/base/webhooks/argo-workflows-webhook-clients-secret.yaml) + +The secret "argo-workflows-webhook-clients" tells Argo: + +* What type of webhook the account can be used for, e.g. "github" +* What "secret" that webhook is configured for, e.g. in your [Github settings page](https://github.com/alexec/argo/settings/hooks) + diff --git a/go.mod b/go.mod index 1f319df70031..b90d5e86a2de 100644 --- a/go.mod +++ b/go.mod @@ -68,6 +68,7 @@ require ( google.golang.org/api v0.20.0 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/grpc v1.28.0 + gopkg.in/go-playground/webhooks.v5 v5.15.0 gopkg.in/jcmturner/goidentity.v2 v2.0.0 // indirect gopkg.in/jcmturner/gokrb5.v5 v5.3.0 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect diff --git a/go.sum b/go.sum index 08c17756dcfc..463a711af6f4 100644 --- a/go.sum +++ b/go.sum @@ -1017,6 +1017,8 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/webhooks.v5 v5.15.0 h1:vnD8c5hN/w8qs0K3fQvIDAeYAHBnCclJHPnxNSJeBCw= +gopkg.in/go-playground/webhooks.v5 v5.15.0/go.mod h1:LZbya/qLVdbqDR1aKrGuWV6qbia2zCYSR5dpom2SInQ= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1061,6 +1063,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/hack/crds.go b/hack/crds.go index 44de363db6d9..b4d34814c678 100644 --- a/hack/crds.go +++ b/hack/crds.go @@ -31,6 +31,8 @@ func cleanCRD(filename string) { properties := schema["properties"].(obj)["spec"].(obj)["properties"].(obj)["templates"].(obj)["items"].(obj)["properties"] properties.(obj)["container"].(obj)["required"] = []string{"image"} properties.(obj)["script"].(obj)["required"] = []string{"image", "source"} + case "workfloweventbindings.argoproj.io": + // noop default: panic(name) } diff --git a/hack/swaggify.sh b/hack/swaggify.sh index 41d6348f199a..9efdc675b1b8 100755 --- a/hack/swaggify.sh +++ b/hack/swaggify.sh @@ -5,6 +5,7 @@ set -eu -o pipefail cat \ | sed 's/github.com.argoproj.argo.pkg.apis.workflow.v1alpha1./io.argoproj.REPLACEME.v1alpha1./' \ | sed 's/cronworkflow\./io.argoproj.REPLACEME.v1alpha1./' \ + | sed 's/event\./io.argoproj.REPLACEME.v1alpha1./' \ | sed 's/info\./io.argoproj.REPLACEME.v1alpha1./' \ | sed 's/workflowarchive\./io.argoproj.REPLACEME.v1alpha1./' \ | sed 's/clusterworkflowtemplate\./io.argoproj.REPLACEME.v1alpha1./' \ diff --git a/manifests/base/crds/full/argoproj.io_clusterworkflowtemplates.yaml b/manifests/base/crds/full/argoproj.io_clusterworkflowtemplates.yaml index 3f642e49a6f8..f86f9c048fcc 100644 --- a/manifests/base/crds/full/argoproj.io_clusterworkflowtemplates.yaml +++ b/manifests/base/crds/full/argoproj.io_clusterworkflowtemplates.yaml @@ -601,6 +601,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -1713,6 +1715,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -2587,6 +2591,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -3540,6 +3546,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -3976,6 +3984,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -5492,6 +5502,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: diff --git a/manifests/base/crds/full/argoproj.io_cronworkflows.yaml b/manifests/base/crds/full/argoproj.io_cronworkflows.yaml index 2dad8a0956ff..2f056a74b631 100644 --- a/manifests/base/crds/full/argoproj.io_cronworkflows.yaml +++ b/manifests/base/crds/full/argoproj.io_cronworkflows.yaml @@ -622,6 +622,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -1734,6 +1736,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -2608,6 +2612,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -3561,6 +3567,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -3997,6 +4005,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -5513,6 +5523,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: diff --git a/manifests/base/crds/full/argoproj.io_workfloweventbindings.yaml b/manifests/base/crds/full/argoproj.io_workfloweventbindings.yaml new file mode 100644 index 000000000000..da7ee6f1ad1e --- /dev/null +++ b/manifests/base/crds/full/argoproj.io_workfloweventbindings.yaml @@ -0,0 +1,386 @@ +# This is an auto-generated file. DO NOT EDIT +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: workfloweventbindings.argoproj.io +spec: + group: argoproj.io + names: + kind: WorkflowEventBinding + listKind: WorkflowEventBindingList + plural: workfloweventbindings + shortNames: + - wfeb + singular: workfloweventbinding + scope: Namespaced + validation: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + event: + properties: + selector: + type: string + required: + - selector + type: object + submit: + properties: + arguments: + properties: + artifacts: + items: + properties: + archive: + properties: + none: + type: object + tar: + properties: + compressionLevel: + format: int32 + type: integer + type: object + type: object + archiveLogs: + type: boolean + artifactory: + properties: + passwordSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + url: + type: string + usernameSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + required: + - url + type: object + from: + type: string + gcs: + properties: + bucket: + type: string + key: + type: string + serviceAccountKeySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + required: + - bucket + - key + type: object + git: + properties: + depth: + format: int64 + type: integer + fetch: + items: + type: string + type: array + insecureIgnoreHostKey: + type: boolean + passwordSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + repo: + type: string + revision: + type: string + sshPrivateKeySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + usernameSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + required: + - repo + type: object + globalName: + type: string + hdfs: + properties: + addresses: + items: + type: string + type: array + force: + type: boolean + hdfsUser: + type: string + krbCCacheSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + krbConfigConfigMap: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + krbKeytabSecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + krbRealm: + type: string + krbServicePrincipalName: + type: string + krbUsername: + type: string + path: + type: string + required: + - addresses + - path + type: object + http: + properties: + url: + type: string + required: + - url + type: object + mode: + format: int32 + type: integer + name: + type: string + optional: + type: boolean + oss: + properties: + accessKeySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + bucket: + type: string + endpoint: + type: string + key: + type: string + secretKeySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + required: + - accessKeySecret + - bucket + - endpoint + - key + - secretKeySecret + type: object + path: + type: string + raw: + properties: + data: + type: string + required: + - data + type: object + s3: + properties: + accessKeySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + bucket: + type: string + endpoint: + type: string + insecure: + type: boolean + key: + type: string + region: + type: string + roleARN: + type: string + secretKeySecret: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + useSDKCreds: + type: boolean + required: + - accessKeySecret + - bucket + - endpoint + - key + - secretKeySecret + type: object + required: + - name + type: object + type: array + parameters: + items: + properties: + default: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + globalName: + type: string + name: + type: string + value: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + valueFrom: + properties: + default: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + event: + type: string + jqFilter: + type: string + jsonPath: + type: string + parameter: + type: string + path: + type: string + supplied: + type: object + type: object + required: + - name + type: object + type: array + type: object + workflowTemplateRef: + properties: + clusterScope: + type: boolean + name: + type: string + type: object + required: + - workflowTemplateRef + type: object + required: + - event + type: object + required: + - metadata + - spec + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true diff --git a/manifests/base/crds/full/argoproj.io_workflows.yaml b/manifests/base/crds/full/argoproj.io_workflows.yaml index c4ea18862747..78307aa9e9f6 100644 --- a/manifests/base/crds/full/argoproj.io_workflows.yaml +++ b/manifests/base/crds/full/argoproj.io_workflows.yaml @@ -611,6 +611,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -1723,6 +1725,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -2597,6 +2601,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -3550,6 +3556,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -3986,6 +3994,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -5502,6 +5512,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -7328,6 +7340,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -7678,6 +7692,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -8050,6 +8066,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -9507,6 +9525,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -10381,6 +10401,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -11334,6 +11356,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -11770,6 +11794,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -13286,6 +13312,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -14590,6 +14618,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -15702,6 +15732,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -16576,6 +16608,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -17529,6 +17563,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -17965,6 +18001,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -19481,6 +19519,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: diff --git a/manifests/base/crds/full/argoproj.io_workflowtemplates.yaml b/manifests/base/crds/full/argoproj.io_workflowtemplates.yaml index 178e7b906cb6..ce0e6cbbb3f2 100644 --- a/manifests/base/crds/full/argoproj.io_workflowtemplates.yaml +++ b/manifests/base/crds/full/argoproj.io_workflowtemplates.yaml @@ -600,6 +600,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -1712,6 +1714,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -2586,6 +2590,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -3539,6 +3545,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -3975,6 +3983,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: @@ -5491,6 +5501,8 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + event: + type: string jqFilter: type: string jsonPath: diff --git a/manifests/base/crds/full/kustomization.yaml b/manifests/base/crds/full/kustomization.yaml index 38001c89205f..a593d88d02d4 100644 --- a/manifests/base/crds/full/kustomization.yaml +++ b/manifests/base/crds/full/kustomization.yaml @@ -5,4 +5,5 @@ resources: - argoproj.io_clusterworkflowtemplates.yaml - argoproj.io_cronworkflows.yaml - argoproj.io_workflows.yaml -- argoproj.io_clusterworkflowtemplates.yaml +- argoproj.io_workflowtemplates.yaml +- argoproj.io_workfloweventbindings.yaml diff --git a/manifests/base/crds/minimal/argoproj.io_workfloweventbindings.yaml b/manifests/base/crds/minimal/argoproj.io_workfloweventbindings.yaml new file mode 100644 index 000000000000..a58de8e76bf4 --- /dev/null +++ b/manifests/base/crds/minimal/argoproj.io_workfloweventbindings.yaml @@ -0,0 +1,19 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: workfloweventbindings.argoproj.io +spec: + group: argoproj.io + names: + kind: WorkflowEventBinding + listKind: WorkflowEventBindingList + plural: workfloweventbindings + shortNames: + - wfeb + singular: workfloweventbinding + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true diff --git a/manifests/base/crds/minimal/kustomization.yaml b/manifests/base/crds/minimal/kustomization.yaml index cca8d8f2d1f7..a593d88d02d4 100644 --- a/manifests/base/crds/minimal/kustomization.yaml +++ b/manifests/base/crds/minimal/kustomization.yaml @@ -6,3 +6,4 @@ resources: - argoproj.io_cronworkflows.yaml - argoproj.io_workflows.yaml - argoproj.io_workflowtemplates.yaml +- argoproj.io_workfloweventbindings.yaml diff --git a/manifests/cluster-install/argo-server-rbac/argo-server-clusterole.yaml b/manifests/cluster-install/argo-server-rbac/argo-server-clusterole.yaml index c012b029fa54..2fe6d1a4fb76 100644 --- a/manifests/cluster-install/argo-server-rbac/argo-server-clusterole.yaml +++ b/manifests/cluster-install/argo-server-rbac/argo-server-clusterole.yaml @@ -32,6 +32,7 @@ rules: - argoproj.io resources: - workflows + - workfloweventbindings - workflowtemplates - cronworkflows - clusterworkflowtemplates diff --git a/manifests/install.yaml b/manifests/install.yaml index a88b87c67e97..e9b3271970f4 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -43,6 +43,26 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: workfloweventbindings.argoproj.io +spec: + group: argoproj.io + names: + kind: WorkflowEventBinding + listKind: WorkflowEventBindingList + plural: workfloweventbindings + shortNames: + - wfeb + singular: workfloweventbinding + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: workflows.argoproj.io spec: @@ -319,6 +339,7 @@ rules: - argoproj.io resources: - workflows + - workfloweventbindings - workflowtemplates - cronworkflows - clusterworkflowtemplates diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 3853a3205cfa..d0d4957e03f5 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -43,6 +43,26 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: workfloweventbindings.argoproj.io +spec: + group: argoproj.io + names: + kind: WorkflowEventBinding + listKind: WorkflowEventBindingList + plural: workfloweventbindings + shortNames: + - wfeb + singular: workfloweventbinding + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: workflows.argoproj.io spec: @@ -238,6 +258,7 @@ rules: - argoproj.io resources: - workflows + - workfloweventbindings - workflowtemplates - cronworkflows verbs: diff --git a/manifests/namespace-install/argo-server-rbac/argo-server-role.yaml b/manifests/namespace-install/argo-server-rbac/argo-server-role.yaml index 541f9c6668d3..4948683c9cd1 100644 --- a/manifests/namespace-install/argo-server-rbac/argo-server-role.yaml +++ b/manifests/namespace-install/argo-server-rbac/argo-server-role.yaml @@ -38,6 +38,7 @@ rules: - argoproj.io resources: - workflows + - workfloweventbindings - workflowtemplates - cronworkflows verbs: diff --git a/manifests/quick-start-minimal.yaml b/manifests/quick-start-minimal.yaml index 426f490d28cc..fa6917b7d835 100644 --- a/manifests/quick-start-minimal.yaml +++ b/manifests/quick-start-minimal.yaml @@ -43,6 +43,26 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: workfloweventbindings.argoproj.io +spec: + group: argoproj.io + names: + kind: WorkflowEventBinding + listKind: WorkflowEventBindingList + plural: workfloweventbindings + shortNames: + - wfeb + singular: workfloweventbinding + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: workflows.argoproj.io spec: @@ -102,6 +122,11 @@ kind: ServiceAccount metadata: name: argo-server --- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: github.com +--- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -238,6 +263,7 @@ rules: - argoproj.io resources: - workflows + - workfloweventbindings - workflowtemplates - cronworkflows verbs: @@ -251,6 +277,30 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role +metadata: + name: submit-workflow-template +rules: +- apiGroups: + - argoproj.io + resources: + - workfloweventbindings + verbs: + - list +- apiGroups: + - argoproj.io + resources: + - workflowtemplates + verbs: + - get +- apiGroups: + - argoproj.io + resources: + - workflows + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role metadata: name: workflow-role rules: @@ -329,6 +379,19 @@ subjects: --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding +metadata: + name: github.com +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: submit-workflow-template +subjects: +- kind: ServiceAccount + name: github.com + namespace: argo +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding metadata: name: workflow-default-binding roleRef: @@ -422,6 +485,24 @@ stringData: --- apiVersion: v1 kind: Secret +metadata: + name: argo-workflows-webhook-clients +stringData: + bitbucket.org: | + type: bitbucket + secret: "my-uuid" + bitbucketserver: | + type: bitbucketserver + secret: "shh!" + github.com: | + type: github + secret: "shh!" + gitlab.com: | + type: gitlab + secret: "shh!" +--- +apiVersion: v1 +kind: Secret metadata: labels: app: minio diff --git a/manifests/quick-start-mysql.yaml b/manifests/quick-start-mysql.yaml index 4a4210207180..f4dc55866a38 100644 --- a/manifests/quick-start-mysql.yaml +++ b/manifests/quick-start-mysql.yaml @@ -43,6 +43,26 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: workfloweventbindings.argoproj.io +spec: + group: argoproj.io + names: + kind: WorkflowEventBinding + listKind: WorkflowEventBindingList + plural: workfloweventbindings + shortNames: + - wfeb + singular: workfloweventbinding + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: workflows.argoproj.io spec: @@ -102,6 +122,11 @@ kind: ServiceAccount metadata: name: argo-server --- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: github.com +--- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -238,6 +263,7 @@ rules: - argoproj.io resources: - workflows + - workfloweventbindings - workflowtemplates - cronworkflows verbs: @@ -251,6 +277,30 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role +metadata: + name: submit-workflow-template +rules: +- apiGroups: + - argoproj.io + resources: + - workfloweventbindings + verbs: + - list +- apiGroups: + - argoproj.io + resources: + - workflowtemplates + verbs: + - get +- apiGroups: + - argoproj.io + resources: + - workflows + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role metadata: name: workflow-role rules: @@ -329,6 +379,19 @@ subjects: --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding +metadata: + name: github.com +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: submit-workflow-template +subjects: +- kind: ServiceAccount + name: github.com + namespace: argo +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding metadata: name: workflow-default-binding roleRef: @@ -452,6 +515,24 @@ stringData: --- apiVersion: v1 kind: Secret +metadata: + name: argo-workflows-webhook-clients +stringData: + bitbucket.org: | + type: bitbucket + secret: "my-uuid" + bitbucketserver: | + type: bitbucketserver + secret: "shh!" + github.com: | + type: github + secret: "shh!" + gitlab.com: | + type: gitlab + secret: "shh!" +--- +apiVersion: v1 +kind: Secret metadata: labels: app: minio diff --git a/manifests/quick-start-postgres.yaml b/manifests/quick-start-postgres.yaml index 75f18fbd9520..f737f300fd95 100644 --- a/manifests/quick-start-postgres.yaml +++ b/manifests/quick-start-postgres.yaml @@ -43,6 +43,26 @@ spec: --- apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition +metadata: + name: workfloweventbindings.argoproj.io +spec: + group: argoproj.io + names: + kind: WorkflowEventBinding + listKind: WorkflowEventBindingList + plural: workfloweventbindings + shortNames: + - wfeb + singular: workfloweventbinding + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition metadata: name: workflows.argoproj.io spec: @@ -102,6 +122,11 @@ kind: ServiceAccount metadata: name: argo-server --- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: github.com +--- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -238,6 +263,7 @@ rules: - argoproj.io resources: - workflows + - workfloweventbindings - workflowtemplates - cronworkflows verbs: @@ -251,6 +277,30 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role +metadata: + name: submit-workflow-template +rules: +- apiGroups: + - argoproj.io + resources: + - workfloweventbindings + verbs: + - list +- apiGroups: + - argoproj.io + resources: + - workflowtemplates + verbs: + - get +- apiGroups: + - argoproj.io + resources: + - workflows + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role metadata: name: workflow-role rules: @@ -329,6 +379,19 @@ subjects: --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding +metadata: + name: github.com +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: submit-workflow-template +subjects: +- kind: ServiceAccount + name: github.com + namespace: argo +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding metadata: name: workflow-default-binding roleRef: @@ -452,6 +515,24 @@ stringData: --- apiVersion: v1 kind: Secret +metadata: + name: argo-workflows-webhook-clients +stringData: + bitbucket.org: | + type: bitbucket + secret: "my-uuid" + bitbucketserver: | + type: bitbucketserver + secret: "shh!" + github.com: | + type: github + secret: "shh!" + gitlab.com: | + type: gitlab + secret: "shh!" +--- +apiVersion: v1 +kind: Secret metadata: labels: app: minio diff --git a/manifests/quick-start/base/kustomization.yaml b/manifests/quick-start/base/kustomization.yaml index a059066d133b..add9be781dbd 100644 --- a/manifests/quick-start/base/kustomization.yaml +++ b/manifests/quick-start/base/kustomization.yaml @@ -4,6 +4,7 @@ kind: Kustomization resources: - ../../namespace-install - minio + - webhooks - argo-server-sso-secret.yaml - workflow-role.yaml - workflow-default-rolebinding.yaml diff --git a/manifests/quick-start/base/webhooks/argo-workflows-webhook-clients-secret.yaml b/manifests/quick-start/base/webhooks/argo-workflows-webhook-clients-secret.yaml new file mode 100644 index 000000000000..566b49951f27 --- /dev/null +++ b/manifests/quick-start/base/webhooks/argo-workflows-webhook-clients-secret.yaml @@ -0,0 +1,22 @@ +kind: Secret +apiVersion: v1 +metadata: + name: argo-workflows-webhook-clients +# The data keys must be the name of a service account. +stringData: + # https://support.atlassian.com/bitbucket-cloud/docs/manage-webhooks/ + bitbucket.org: | + type: bitbucket + secret: "my-uuid" + # https://confluence.atlassian.com/bitbucketserver/managing-webhooks-in-bitbucket-server-938025878.html + bitbucketserver: | + type: bitbucketserver + secret: "shh!" + # https://developer.github.com/webhooks/securing/ + github.com: | + type: github + secret: "shh!" + # https://docs.gitlab.com/ee/user/project/integrations/webhooks.html + gitlab.com: | + type: gitlab + secret: "shh!" \ No newline at end of file diff --git a/manifests/quick-start/base/webhooks/github.com-rolebinding.yaml b/manifests/quick-start/base/webhooks/github.com-rolebinding.yaml new file mode 100644 index 000000000000..6477163c4ec4 --- /dev/null +++ b/manifests/quick-start/base/webhooks/github.com-rolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: github.com +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: submit-workflow-template +subjects: + - kind: ServiceAccount + name: github.com + namespace: argo \ No newline at end of file diff --git a/manifests/quick-start/base/webhooks/github.com-sa.yaml b/manifests/quick-start/base/webhooks/github.com-sa.yaml new file mode 100644 index 000000000000..7e42d02edb2e --- /dev/null +++ b/manifests/quick-start/base/webhooks/github.com-sa.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: github.com diff --git a/manifests/quick-start/base/webhooks/kustomization.yaml b/manifests/quick-start/base/webhooks/kustomization.yaml new file mode 100644 index 000000000000..ffef982a7067 --- /dev/null +++ b/manifests/quick-start/base/webhooks/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - submit-workflow-template-role.yaml + - github.com-sa.yaml + - github.com-rolebinding.yaml + - argo-workflows-webhook-clients-secret.yaml diff --git a/manifests/quick-start/base/webhooks/submit-workflow-template-role.yaml b/manifests/quick-start/base/webhooks/submit-workflow-template-role.yaml new file mode 100644 index 000000000000..82dd187abd31 --- /dev/null +++ b/manifests/quick-start/base/webhooks/submit-workflow-template-role.yaml @@ -0,0 +1,25 @@ +# Just enough permissions to submit a workflow template. +# You could tighten this further (but perhaps impractically) by using `resourceNames` +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: submit-workflow-template +rules: + - apiGroups: + - argoproj.io + resources: + - workfloweventbindings + verbs: + - list + - apiGroups: + - argoproj.io + resources: + - workflowtemplates + verbs: + - get + - apiGroups: + - argoproj.io + resources: + - workflows + verbs: + - create \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 946064ab5bc5..cec87d5b2086 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -51,6 +51,8 @@ nav: - access-token.md - rest-api.md - rest-examples.md + - events.md + - webhooks.md - submit-workflow-via-automation.md - workflow-submitting-workflow.md - resuming-workflow-via-automation.md diff --git a/pkg/apiclient/event/event.pb.go b/pkg/apiclient/event/event.pb.go new file mode 100644 index 000000000000..1d936adb226e --- /dev/null +++ b/pkg/apiclient/event/event.pb.go @@ -0,0 +1,683 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pkg/apiclient/event/event.proto + +package event + +import ( + context "context" + fmt "fmt" + v1alpha1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + _ "k8s.io/apimachinery/pkg/apis/meta/v1" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type EventRequest struct { + // The namespace for the event. This can be empty if the client has cluster scoped permissions. + // If empty, then the event is "broadcast" to workflow event binding in all namespaces. + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + // Optional discriminator for the event. This should almost always be empty. + // Used for edge-cases where the event payload alone is not provide enough information to discriminate the event. + // This MUST NOT be used as security mechanism, e.g. to allow two clients to use the same access token, or + // to support webhooks on unsecured server. Instead, use access tokens. + // This is made available as `discriminator` in the event binding selector (`/spec/event/selector)` + Discriminator string `protobuf:"bytes,2,opt,name=discriminator,proto3" json:"discriminator,omitempty"` + // The event itself can be any data. + Payload *v1alpha1.Item `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EventRequest) Reset() { *m = EventRequest{} } +func (m *EventRequest) String() string { return proto.CompactTextString(m) } +func (*EventRequest) ProtoMessage() {} +func (*EventRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d80a0d2509a47d1c, []int{0} +} +func (m *EventRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventRequest.Merge(m, src) +} +func (m *EventRequest) XXX_Size() int { + return m.Size() +} +func (m *EventRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EventRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_EventRequest proto.InternalMessageInfo + +func (m *EventRequest) GetNamespace() string { + if m != nil { + return m.Namespace + } + return "" +} + +func (m *EventRequest) GetDiscriminator() string { + if m != nil { + return m.Discriminator + } + return "" +} + +func (m *EventRequest) GetPayload() *v1alpha1.Item { + if m != nil { + return m.Payload + } + return nil +} + +type EventResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EventResponse) Reset() { *m = EventResponse{} } +func (m *EventResponse) String() string { return proto.CompactTextString(m) } +func (*EventResponse) ProtoMessage() {} +func (*EventResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d80a0d2509a47d1c, []int{1} +} +func (m *EventResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventResponse.Merge(m, src) +} +func (m *EventResponse) XXX_Size() int { + return m.Size() +} +func (m *EventResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EventResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EventResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*EventRequest)(nil), "event.EventRequest") + proto.RegisterType((*EventResponse)(nil), "event.EventResponse") +} + +func init() { proto.RegisterFile("pkg/apiclient/event/event.proto", fileDescriptor_d80a0d2509a47d1c) } + +var fileDescriptor_d80a0d2509a47d1c = []byte{ + // 388 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x4f, 0x4b, 0xe3, 0x40, + 0x18, 0xc6, 0x49, 0x97, 0xdd, 0xa5, 0xd9, 0x96, 0x85, 0xec, 0x1e, 0x4a, 0x29, 0xdd, 0x12, 0xf6, + 0x50, 0x96, 0x75, 0x86, 0x56, 0x05, 0xad, 0x37, 0xc5, 0x83, 0xd7, 0xf4, 0xe6, 0x6d, 0x9a, 0xbe, + 0x9d, 0x8e, 0x49, 0x66, 0xc6, 0x99, 0x69, 0x4a, 0x29, 0xbd, 0x78, 0xf0, 0x0b, 0xf8, 0x21, 0xfc, + 0x2a, 0x1e, 0x05, 0xbf, 0x80, 0x14, 0x3f, 0x88, 0x64, 0x92, 0xf4, 0x0f, 0x1e, 0xbc, 0x84, 0xf7, + 0x4f, 0xde, 0x27, 0xbf, 0x87, 0x27, 0xee, 0x1f, 0x19, 0x51, 0x4c, 0x24, 0x0b, 0x63, 0x06, 0xdc, + 0x60, 0x48, 0x37, 0x4f, 0x24, 0x95, 0x30, 0xc2, 0xfb, 0x6a, 0x9b, 0xe6, 0x01, 0x65, 0x66, 0x3a, + 0x1b, 0xa1, 0x50, 0x24, 0x98, 0x0a, 0x2a, 0xb0, 0xdd, 0x8e, 0x66, 0x13, 0xdb, 0xd9, 0xc6, 0x56, + 0xf9, 0x55, 0xb3, 0x45, 0x85, 0xa0, 0x31, 0x64, 0xca, 0x98, 0x70, 0x2e, 0x0c, 0x31, 0x4c, 0x70, + 0x5d, 0x6c, 0x8f, 0xa2, 0x13, 0x8d, 0x98, 0xc8, 0xb6, 0x09, 0x09, 0xa7, 0x8c, 0x83, 0x5a, 0xe0, + 0x02, 0x44, 0xe3, 0x04, 0x0c, 0xc1, 0x69, 0x0f, 0x53, 0xe0, 0xa0, 0x88, 0x81, 0x71, 0x71, 0x75, + 0xb1, 0x83, 0x40, 0x94, 0xfd, 0xe8, 0x8d, 0x2d, 0xb6, 0xa7, 0x73, 0xa1, 0xa2, 0x49, 0x2c, 0xe6, + 0x38, 0xed, 0x91, 0x58, 0x4e, 0xc9, 0x07, 0x11, 0xff, 0xd1, 0x71, 0x6b, 0x97, 0x99, 0xa3, 0x00, + 0x6e, 0x67, 0xa0, 0x8d, 0xd7, 0x72, 0xab, 0x9c, 0x24, 0xa0, 0x25, 0x09, 0xa1, 0xe1, 0x74, 0x9c, + 0x6e, 0x35, 0xd8, 0x0e, 0xbc, 0xbf, 0x6e, 0x7d, 0xcc, 0x74, 0xa8, 0x58, 0xc2, 0x38, 0x31, 0x42, + 0x35, 0x2a, 0xf6, 0x8d, 0xfd, 0xa1, 0x37, 0x74, 0xbf, 0x4b, 0xb2, 0x88, 0x05, 0x19, 0x37, 0xbe, + 0x74, 0x9c, 0xee, 0x8f, 0xfe, 0x29, 0xda, 0xb2, 0xa2, 0x92, 0xd5, 0x16, 0x48, 0x46, 0x14, 0x65, + 0xac, 0xa8, 0x64, 0x45, 0x25, 0x2b, 0xba, 0x32, 0x90, 0x04, 0xa5, 0x92, 0xff, 0xd3, 0xad, 0x17, + 0xa0, 0x5a, 0x0a, 0xae, 0xa1, 0x7f, 0x5f, 0xa2, 0x0f, 0x41, 0xa5, 0x2c, 0x04, 0x2f, 0x75, 0x6b, + 0x01, 0x84, 0xc0, 0x52, 0xb0, 0x63, 0xef, 0x17, 0xca, 0x83, 0xdb, 0xf5, 0xd7, 0xfc, 0xbd, 0x3f, + 0xcc, 0xb5, 0xfc, 0xb3, 0xbb, 0x97, 0xb7, 0x87, 0xca, 0xb1, 0xff, 0xcf, 0x26, 0x94, 0xf6, 0xf2, + 0xc8, 0x35, 0x5e, 0x6e, 0xac, 0xaf, 0xf0, 0x72, 0xcf, 0xe4, 0x6a, 0x50, 0x92, 0x9d, 0x0f, 0x9e, + 0xd6, 0x6d, 0xe7, 0x79, 0xdd, 0x76, 0x5e, 0xd7, 0x6d, 0xe7, 0xfa, 0xff, 0x67, 0xb1, 0xec, 0xfe, + 0x5a, 0xa3, 0x6f, 0x36, 0x86, 0xc3, 0xf7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa4, 0xb9, 0x2c, 0xe3, + 0x78, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// EventServiceClient is the client API for EventService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type EventServiceClient interface { + ReceiveEvent(ctx context.Context, in *EventRequest, opts ...grpc.CallOption) (*EventResponse, error) +} + +type eventServiceClient struct { + cc *grpc.ClientConn +} + +func NewEventServiceClient(cc *grpc.ClientConn) EventServiceClient { + return &eventServiceClient{cc} +} + +func (c *eventServiceClient) ReceiveEvent(ctx context.Context, in *EventRequest, opts ...grpc.CallOption) (*EventResponse, error) { + out := new(EventResponse) + err := c.cc.Invoke(ctx, "/event.EventService/ReceiveEvent", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// EventServiceServer is the server API for EventService service. +type EventServiceServer interface { + ReceiveEvent(context.Context, *EventRequest) (*EventResponse, error) +} + +// UnimplementedEventServiceServer can be embedded to have forward compatible implementations. +type UnimplementedEventServiceServer struct { +} + +func (*UnimplementedEventServiceServer) ReceiveEvent(ctx context.Context, req *EventRequest) (*EventResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReceiveEvent not implemented") +} + +func RegisterEventServiceServer(s *grpc.Server, srv EventServiceServer) { + s.RegisterService(&_EventService_serviceDesc, srv) +} + +func _EventService_ReceiveEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EventRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EventServiceServer).ReceiveEvent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/event.EventService/ReceiveEvent", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EventServiceServer).ReceiveEvent(ctx, req.(*EventRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _EventService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "event.EventService", + HandlerType: (*EventServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ReceiveEvent", + Handler: _EventService_ReceiveEvent_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pkg/apiclient/event/event.proto", +} + +func (m *EventRequest) 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 *EventRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Payload != nil { + { + size, err := m.Payload.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvent(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Discriminator) > 0 { + i -= len(m.Discriminator) + copy(dAtA[i:], m.Discriminator) + i = encodeVarintEvent(dAtA, i, uint64(len(m.Discriminator))) + i-- + dAtA[i] = 0x12 + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = encodeVarintEvent(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventResponse) 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 *EventResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil +} + +func encodeVarintEvent(dAtA []byte, offset int, v uint64) int { + offset -= sovEvent(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *EventRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Namespace) + if l > 0 { + n += 1 + l + sovEvent(uint64(l)) + } + l = len(m.Discriminator) + if l > 0 { + n += 1 + l + sovEvent(uint64(l)) + } + if m.Payload != nil { + l = m.Payload.Size() + n += 1 + l + sovEvent(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *EventResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovEvent(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvent(x uint64) (n int) { + return sovEvent(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *EventRequest) 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 ErrIntOverflowEvent + } + 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: EventRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + 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 ErrInvalidLengthEvent + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Namespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Discriminator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + 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 ErrInvalidLengthEvent + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Discriminator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvent + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Payload == nil { + m.Payload = &v1alpha1.Item{} + } + if err := m.Payload.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvent(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvent + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvent + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventResponse) 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 ErrIntOverflowEvent + } + 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: EventResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipEvent(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvent + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvent + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvent(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthEvent + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvent + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvent + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvent = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvent = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvent = fmt.Errorf("proto: unexpected end of group") +) diff --git a/pkg/apiclient/event/event.pb.gw.go b/pkg/apiclient/event/event.pb.gw.go new file mode 100644 index 000000000000..84975bb21578 --- /dev/null +++ b/pkg/apiclient/event/event.pb.gw.go @@ -0,0 +1,221 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: pkg/apiclient/event/event.proto + +/* +Package event is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package event + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +func request_EventService_ReceiveEvent_0(ctx context.Context, marshaler runtime.Marshaler, client EventServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EventRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Payload); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + + protoReq.Namespace, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + + val, ok = pathParams["discriminator"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "discriminator") + } + + protoReq.Discriminator, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "discriminator", err) + } + + msg, err := client.ReceiveEvent(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_EventService_ReceiveEvent_0(ctx context.Context, marshaler runtime.Marshaler, server EventServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EventRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Payload); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["namespace"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "namespace") + } + + protoReq.Namespace, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "namespace", err) + } + + val, ok = pathParams["discriminator"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "discriminator") + } + + protoReq.Discriminator, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "discriminator", err) + } + + msg, err := server.ReceiveEvent(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterEventServiceHandlerServer registers the http handlers for service EventService to "mux". +// UnaryRPC :call EventServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +func RegisterEventServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server EventServiceServer) error { + + mux.Handle("POST", pattern_EventService_ReceiveEvent_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_EventService_ReceiveEvent_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_EventService_ReceiveEvent_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterEventServiceHandlerFromEndpoint is same as RegisterEventServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterEventServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterEventServiceHandler(ctx, mux, conn) +} + +// RegisterEventServiceHandler registers the http handlers for service EventService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterEventServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterEventServiceHandlerClient(ctx, mux, NewEventServiceClient(conn)) +} + +// RegisterEventServiceHandlerClient registers the http handlers for service EventService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "EventServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "EventServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "EventServiceClient" to call the correct interceptors. +func RegisterEventServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client EventServiceClient) error { + + mux.Handle("POST", pattern_EventService_ReceiveEvent_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_EventService_ReceiveEvent_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_EventService_ReceiveEvent_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_EventService_ReceiveEvent_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4}, []string{"api", "v1", "events", "namespace", "discriminator"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_EventService_ReceiveEvent_0 = runtime.ForwardResponseMessage +) diff --git a/pkg/apiclient/event/event.proto b/pkg/apiclient/event/event.proto new file mode 100644 index 000000000000..77abce354911 --- /dev/null +++ b/pkg/apiclient/event/event.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; +option go_package = "github.com/argoproj/argo/pkg/apiclient/event"; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; +import "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1/generated.proto"; + +package event; + +message EventRequest { + // The namespace for the event. This can be empty if the client has cluster scoped permissions. + // If empty, then the event is "broadcast" to workflow event binding in all namespaces. + string namespace = 1; + // Optional discriminator for the event. This should almost always be empty. + // Used for edge-cases where the event payload alone is not provide enough information to discriminate the event. + // This MUST NOT be used as security mechanism, e.g. to allow two clients to use the same access token, or + // to support webhooks on unsecured server. Instead, use access tokens. + // This is made available as `discriminator` in the event binding selector (`/spec/event/selector)` + string discriminator = 2; + // The event itself can be any data. + github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.Item payload = 3; +} + +message EventResponse { +} + +service EventService { + rpc ReceiveEvent (EventRequest) returns (EventResponse) { + option (google.api.http) = { + post: "/api/v1/events/{namespace}/{discriminator}" + body: "payload" + }; + } +} \ No newline at end of file diff --git a/pkg/apis/api-rules/violation_exceptions.list b/pkg/apis/api-rules/violation_exceptions.list index 35436934a95b..e5c8df3d776d 100644 --- a/pkg/apis/api-rules/violation_exceptions.list +++ b/pkg/apis/api-rules/violation_exceptions.list @@ -25,6 +25,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo/pkg/apis/workflow API rule violation: list_type_missing,github.com/argoproj/argo/pkg/apis/workflow/v1alpha1,Template,Steps API rule violation: list_type_missing,github.com/argoproj/argo/pkg/apis/workflow/v1alpha1,Template,Tolerations API rule violation: list_type_missing,github.com/argoproj/argo/pkg/apis/workflow/v1alpha1,Template,Volumes +API rule violation: list_type_missing,github.com/argoproj/argo/pkg/apis/workflow/v1alpha1,WorkflowEventBindingList,Items API rule violation: list_type_missing,github.com/argoproj/argo/pkg/apis/workflow/v1alpha1,WorkflowSpec,HostAliases API rule violation: list_type_missing,github.com/argoproj/argo/pkg/apis/workflow/v1alpha1,WorkflowSpec,ImagePullSecrets API rule violation: list_type_missing,github.com/argoproj/argo/pkg/apis/workflow/v1alpha1,WorkflowSpec,Templates diff --git a/pkg/apis/workflow/v1alpha1/cluster_workflow_template_types.go b/pkg/apis/workflow/v1alpha1/cluster_workflow_template_types.go index 87e5e1b258ba..71bb02b1b2a8 100644 --- a/pkg/apis/workflow/v1alpha1/cluster_workflow_template_types.go +++ b/pkg/apis/workflow/v1alpha1/cluster_workflow_template_types.go @@ -57,6 +57,10 @@ func (cwftmpl *ClusterWorkflowTemplate) GetResourceScope() ResourceScope { return ResourceScopeCluster } +func (cwftmpl *ClusterWorkflowTemplate) GetWorkflowMetadata() *metav1.ObjectMeta { + return cwftmpl.Spec.WorkflowMetadata +} + // GetWorkflowSpec returns the WorkflowSpec of cluster workflow template. func (cwftmpl *ClusterWorkflowTemplate) GetWorkflowSpec() *WorkflowSpec { return &cwftmpl.Spec.WorkflowSpec diff --git a/pkg/apis/workflow/v1alpha1/common.go b/pkg/apis/workflow/v1alpha1/common.go index f1d474c5312e..7121e05d8979 100644 --- a/pkg/apis/workflow/v1alpha1/common.go +++ b/pkg/apis/workflow/v1alpha1/common.go @@ -24,6 +24,8 @@ type TemplateHolder interface { // WorkflowSpecHolder is an object that holds a WorkflowSpec; e.g., WorkflowTemplate, and ClusterWorkflowTemplate type WorkflowSpecHolder interface { + metav1.Object + GetWorkflowMetadata() *metav1.ObjectMeta GetWorkflowSpec() *WorkflowSpec } diff --git a/pkg/apis/workflow/v1alpha1/event_types.go b/pkg/apis/workflow/v1alpha1/event_types.go new file mode 100644 index 000000000000..aac5b86d32d3 --- /dev/null +++ b/pkg/apis/workflow/v1alpha1/event_types.go @@ -0,0 +1,45 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// WorkflowEventBinding is the definition of an event resource +// +genclient +// +genclient:noStatus +// +kubebuilder:resource:shortName=wfeb +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type WorkflowEventBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata" protobuf:"bytes,1,opt,name=metadata"` + Spec WorkflowEventBindingSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` +} + +// WorkflowEventBindingList is list of event resources +// +kubebuilder:resource:shortName=wfebs +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type WorkflowEventBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata" protobuf:"bytes,1,opt,name=metadata"` + Items []WorkflowEventBinding `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +type WorkflowEventBindingSpec struct { + // Event is the event to bind to + Event Event `json:"event" protobuf:"bytes,1,opt,name=event"` + // Submit is the workflow template to submit + Submit *Submit `json:"submit,omitempty" protobuf:"bytes,2,opt,name=submit"` +} + +type Event struct { + // Selector (https://github.com/antonmedv/expr) that we must must match the event. E.g. `payload.message == "test"` + Selector string `json:"selector" protobuf:"bytes,1,opt,name=selector"` +} + +type Submit struct { + // WorkflowTemplateRef the workflow template to submit + WorkflowTemplateRef WorkflowTemplateRef `json:"workflowTemplateRef" protobuf:"bytes,1,opt,name=workflowTemplateRef"` + + // Arguments extracted from the event and then set as arguments to the workflow created. + Arguments *Arguments `json:"arguments,omitempty" protobuf:"bytes,2,opt,name=arguments"` +} diff --git a/pkg/apis/workflow/v1alpha1/generated.pb.go b/pkg/apis/workflow/v1alpha1/generated.pb.go index cd57ff281269..24656248570c 100644 --- a/pkg/apis/workflow/v1alpha1/generated.pb.go +++ b/pkg/apis/workflow/v1alpha1/generated.pb.go @@ -623,10 +623,38 @@ func (m *DAGTemplate) XXX_DiscardUnknown() { var xxx_messageInfo_DAGTemplate proto.InternalMessageInfo +func (m *Event) Reset() { *m = Event{} } +func (*Event) ProtoMessage() {} +func (*Event) Descriptor() ([]byte, []int) { + return fileDescriptor_c23edafa7e7ea072, []int{21} +} +func (m *Event) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event) 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 *Event) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event.Merge(m, src) +} +func (m *Event) XXX_Size() int { + return m.Size() +} +func (m *Event) XXX_DiscardUnknown() { + xxx_messageInfo_Event.DiscardUnknown(m) +} + +var xxx_messageInfo_Event proto.InternalMessageInfo + func (m *ExecutorConfig) Reset() { *m = ExecutorConfig{} } func (*ExecutorConfig) ProtoMessage() {} func (*ExecutorConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{21} + return fileDescriptor_c23edafa7e7ea072, []int{22} } func (m *ExecutorConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -654,7 +682,7 @@ var xxx_messageInfo_ExecutorConfig proto.InternalMessageInfo func (m *GCSArtifact) Reset() { *m = GCSArtifact{} } func (*GCSArtifact) ProtoMessage() {} func (*GCSArtifact) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{22} + return fileDescriptor_c23edafa7e7ea072, []int{23} } func (m *GCSArtifact) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -682,7 +710,7 @@ var xxx_messageInfo_GCSArtifact proto.InternalMessageInfo func (m *GCSBucket) Reset() { *m = GCSBucket{} } func (*GCSBucket) ProtoMessage() {} func (*GCSBucket) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{23} + return fileDescriptor_c23edafa7e7ea072, []int{24} } func (m *GCSBucket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -710,7 +738,7 @@ var xxx_messageInfo_GCSBucket proto.InternalMessageInfo func (m *Gauge) Reset() { *m = Gauge{} } func (*Gauge) ProtoMessage() {} func (*Gauge) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{24} + return fileDescriptor_c23edafa7e7ea072, []int{25} } func (m *Gauge) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -738,7 +766,7 @@ var xxx_messageInfo_Gauge proto.InternalMessageInfo func (m *GitArtifact) Reset() { *m = GitArtifact{} } func (*GitArtifact) ProtoMessage() {} func (*GitArtifact) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{25} + return fileDescriptor_c23edafa7e7ea072, []int{26} } func (m *GitArtifact) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -766,7 +794,7 @@ var xxx_messageInfo_GitArtifact proto.InternalMessageInfo func (m *HDFSArtifact) Reset() { *m = HDFSArtifact{} } func (*HDFSArtifact) ProtoMessage() {} func (*HDFSArtifact) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{26} + return fileDescriptor_c23edafa7e7ea072, []int{27} } func (m *HDFSArtifact) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -794,7 +822,7 @@ var xxx_messageInfo_HDFSArtifact proto.InternalMessageInfo func (m *HDFSConfig) Reset() { *m = HDFSConfig{} } func (*HDFSConfig) ProtoMessage() {} func (*HDFSConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{27} + return fileDescriptor_c23edafa7e7ea072, []int{28} } func (m *HDFSConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -822,7 +850,7 @@ var xxx_messageInfo_HDFSConfig proto.InternalMessageInfo func (m *HDFSKrbConfig) Reset() { *m = HDFSKrbConfig{} } func (*HDFSKrbConfig) ProtoMessage() {} func (*HDFSKrbConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{28} + return fileDescriptor_c23edafa7e7ea072, []int{29} } func (m *HDFSKrbConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -850,7 +878,7 @@ var xxx_messageInfo_HDFSKrbConfig proto.InternalMessageInfo func (m *HTTPArtifact) Reset() { *m = HTTPArtifact{} } func (*HTTPArtifact) ProtoMessage() {} func (*HTTPArtifact) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{29} + return fileDescriptor_c23edafa7e7ea072, []int{30} } func (m *HTTPArtifact) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -878,7 +906,7 @@ var xxx_messageInfo_HTTPArtifact proto.InternalMessageInfo func (m *Histogram) Reset() { *m = Histogram{} } func (*Histogram) ProtoMessage() {} func (*Histogram) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{30} + return fileDescriptor_c23edafa7e7ea072, []int{31} } func (m *Histogram) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -906,7 +934,7 @@ var xxx_messageInfo_Histogram proto.InternalMessageInfo func (m *HolderNames) Reset() { *m = HolderNames{} } func (*HolderNames) ProtoMessage() {} func (*HolderNames) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{31} + return fileDescriptor_c23edafa7e7ea072, []int{32} } func (m *HolderNames) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -934,7 +962,7 @@ var xxx_messageInfo_HolderNames proto.InternalMessageInfo func (m *Inputs) Reset() { *m = Inputs{} } func (*Inputs) ProtoMessage() {} func (*Inputs) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{32} + return fileDescriptor_c23edafa7e7ea072, []int{33} } func (m *Inputs) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -962,7 +990,7 @@ var xxx_messageInfo_Inputs proto.InternalMessageInfo func (m *Item) Reset() { *m = Item{} } func (*Item) ProtoMessage() {} func (*Item) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{33} + return fileDescriptor_c23edafa7e7ea072, []int{34} } func (m *Item) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -990,7 +1018,7 @@ var xxx_messageInfo_Item proto.InternalMessageInfo func (m *Link) Reset() { *m = Link{} } func (*Link) ProtoMessage() {} func (*Link) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{34} + return fileDescriptor_c23edafa7e7ea072, []int{35} } func (m *Link) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1018,7 +1046,7 @@ var xxx_messageInfo_Link proto.InternalMessageInfo func (m *MemoizationStatus) Reset() { *m = MemoizationStatus{} } func (*MemoizationStatus) ProtoMessage() {} func (*MemoizationStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{35} + return fileDescriptor_c23edafa7e7ea072, []int{36} } func (m *MemoizationStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1046,7 +1074,7 @@ var xxx_messageInfo_MemoizationStatus proto.InternalMessageInfo func (m *Memoize) Reset() { *m = Memoize{} } func (*Memoize) ProtoMessage() {} func (*Memoize) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{36} + return fileDescriptor_c23edafa7e7ea072, []int{37} } func (m *Memoize) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1074,7 +1102,7 @@ var xxx_messageInfo_Memoize proto.InternalMessageInfo func (m *Metadata) Reset() { *m = Metadata{} } func (*Metadata) ProtoMessage() {} func (*Metadata) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{37} + return fileDescriptor_c23edafa7e7ea072, []int{38} } func (m *Metadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1102,7 +1130,7 @@ var xxx_messageInfo_Metadata proto.InternalMessageInfo func (m *MetricLabel) Reset() { *m = MetricLabel{} } func (*MetricLabel) ProtoMessage() {} func (*MetricLabel) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{38} + return fileDescriptor_c23edafa7e7ea072, []int{39} } func (m *MetricLabel) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1130,7 +1158,7 @@ var xxx_messageInfo_MetricLabel proto.InternalMessageInfo func (m *Metrics) Reset() { *m = Metrics{} } func (*Metrics) ProtoMessage() {} func (*Metrics) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{39} + return fileDescriptor_c23edafa7e7ea072, []int{40} } func (m *Metrics) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1158,7 +1186,7 @@ var xxx_messageInfo_Metrics proto.InternalMessageInfo func (m *NodeStatus) Reset() { *m = NodeStatus{} } func (*NodeStatus) ProtoMessage() {} func (*NodeStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{40} + return fileDescriptor_c23edafa7e7ea072, []int{41} } func (m *NodeStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1186,7 +1214,7 @@ var xxx_messageInfo_NodeStatus proto.InternalMessageInfo func (m *NoneStrategy) Reset() { *m = NoneStrategy{} } func (*NoneStrategy) ProtoMessage() {} func (*NoneStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{41} + return fileDescriptor_c23edafa7e7ea072, []int{42} } func (m *NoneStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1214,7 +1242,7 @@ var xxx_messageInfo_NoneStrategy proto.InternalMessageInfo func (m *OSSArtifact) Reset() { *m = OSSArtifact{} } func (*OSSArtifact) ProtoMessage() {} func (*OSSArtifact) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{42} + return fileDescriptor_c23edafa7e7ea072, []int{43} } func (m *OSSArtifact) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1242,7 +1270,7 @@ var xxx_messageInfo_OSSArtifact proto.InternalMessageInfo func (m *OSSBucket) Reset() { *m = OSSBucket{} } func (*OSSBucket) ProtoMessage() {} func (*OSSBucket) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{43} + return fileDescriptor_c23edafa7e7ea072, []int{44} } func (m *OSSBucket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1270,7 +1298,7 @@ var xxx_messageInfo_OSSBucket proto.InternalMessageInfo func (m *Outputs) Reset() { *m = Outputs{} } func (*Outputs) ProtoMessage() {} func (*Outputs) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{44} + return fileDescriptor_c23edafa7e7ea072, []int{45} } func (m *Outputs) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1298,7 +1326,7 @@ var xxx_messageInfo_Outputs proto.InternalMessageInfo func (m *ParallelSteps) Reset() { *m = ParallelSteps{} } func (*ParallelSteps) ProtoMessage() {} func (*ParallelSteps) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{45} + return fileDescriptor_c23edafa7e7ea072, []int{46} } func (m *ParallelSteps) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1326,7 +1354,7 @@ var xxx_messageInfo_ParallelSteps proto.InternalMessageInfo func (m *Parameter) Reset() { *m = Parameter{} } func (*Parameter) ProtoMessage() {} func (*Parameter) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{46} + return fileDescriptor_c23edafa7e7ea072, []int{47} } func (m *Parameter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1354,7 +1382,7 @@ var xxx_messageInfo_Parameter proto.InternalMessageInfo func (m *PodGC) Reset() { *m = PodGC{} } func (*PodGC) ProtoMessage() {} func (*PodGC) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{47} + return fileDescriptor_c23edafa7e7ea072, []int{48} } func (m *PodGC) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1382,7 +1410,7 @@ var xxx_messageInfo_PodGC proto.InternalMessageInfo func (m *Prometheus) Reset() { *m = Prometheus{} } func (*Prometheus) ProtoMessage() {} func (*Prometheus) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{48} + return fileDescriptor_c23edafa7e7ea072, []int{49} } func (m *Prometheus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1410,7 +1438,7 @@ var xxx_messageInfo_Prometheus proto.InternalMessageInfo func (m *RawArtifact) Reset() { *m = RawArtifact{} } func (*RawArtifact) ProtoMessage() {} func (*RawArtifact) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{49} + return fileDescriptor_c23edafa7e7ea072, []int{50} } func (m *RawArtifact) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1438,7 +1466,7 @@ var xxx_messageInfo_RawArtifact proto.InternalMessageInfo func (m *ResourceTemplate) Reset() { *m = ResourceTemplate{} } func (*ResourceTemplate) ProtoMessage() {} func (*ResourceTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{50} + return fileDescriptor_c23edafa7e7ea072, []int{51} } func (m *ResourceTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1466,7 +1494,7 @@ var xxx_messageInfo_ResourceTemplate proto.InternalMessageInfo func (m *RetryStrategy) Reset() { *m = RetryStrategy{} } func (*RetryStrategy) ProtoMessage() {} func (*RetryStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{51} + return fileDescriptor_c23edafa7e7ea072, []int{52} } func (m *RetryStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1494,7 +1522,7 @@ var xxx_messageInfo_RetryStrategy proto.InternalMessageInfo func (m *S3Artifact) Reset() { *m = S3Artifact{} } func (*S3Artifact) ProtoMessage() {} func (*S3Artifact) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{52} + return fileDescriptor_c23edafa7e7ea072, []int{53} } func (m *S3Artifact) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1522,7 +1550,7 @@ var xxx_messageInfo_S3Artifact proto.InternalMessageInfo func (m *S3Bucket) Reset() { *m = S3Bucket{} } func (*S3Bucket) ProtoMessage() {} func (*S3Bucket) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{53} + return fileDescriptor_c23edafa7e7ea072, []int{54} } func (m *S3Bucket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1550,7 +1578,7 @@ var xxx_messageInfo_S3Bucket proto.InternalMessageInfo func (m *ScriptTemplate) Reset() { *m = ScriptTemplate{} } func (*ScriptTemplate) ProtoMessage() {} func (*ScriptTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{54} + return fileDescriptor_c23edafa7e7ea072, []int{55} } func (m *ScriptTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1578,7 +1606,7 @@ var xxx_messageInfo_ScriptTemplate proto.InternalMessageInfo func (m *SemaphoreHolding) Reset() { *m = SemaphoreHolding{} } func (*SemaphoreHolding) ProtoMessage() {} func (*SemaphoreHolding) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{55} + return fileDescriptor_c23edafa7e7ea072, []int{56} } func (m *SemaphoreHolding) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1606,7 +1634,7 @@ var xxx_messageInfo_SemaphoreHolding proto.InternalMessageInfo func (m *SemaphoreRef) Reset() { *m = SemaphoreRef{} } func (*SemaphoreRef) ProtoMessage() {} func (*SemaphoreRef) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{56} + return fileDescriptor_c23edafa7e7ea072, []int{57} } func (m *SemaphoreRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1634,7 +1662,7 @@ var xxx_messageInfo_SemaphoreRef proto.InternalMessageInfo func (m *SemaphoreStatus) Reset() { *m = SemaphoreStatus{} } func (*SemaphoreStatus) ProtoMessage() {} func (*SemaphoreStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{57} + return fileDescriptor_c23edafa7e7ea072, []int{58} } func (m *SemaphoreStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1662,7 +1690,7 @@ var xxx_messageInfo_SemaphoreStatus proto.InternalMessageInfo func (m *Sequence) Reset() { *m = Sequence{} } func (*Sequence) ProtoMessage() {} func (*Sequence) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{58} + return fileDescriptor_c23edafa7e7ea072, []int{59} } func (m *Sequence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1687,10 +1715,38 @@ func (m *Sequence) XXX_DiscardUnknown() { var xxx_messageInfo_Sequence proto.InternalMessageInfo +func (m *Submit) Reset() { *m = Submit{} } +func (*Submit) ProtoMessage() {} +func (*Submit) Descriptor() ([]byte, []int) { + return fileDescriptor_c23edafa7e7ea072, []int{60} +} +func (m *Submit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Submit) 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 *Submit) XXX_Merge(src proto.Message) { + xxx_messageInfo_Submit.Merge(m, src) +} +func (m *Submit) XXX_Size() int { + return m.Size() +} +func (m *Submit) XXX_DiscardUnknown() { + xxx_messageInfo_Submit.DiscardUnknown(m) +} + +var xxx_messageInfo_Submit proto.InternalMessageInfo + func (m *SubmitOpts) Reset() { *m = SubmitOpts{} } func (*SubmitOpts) ProtoMessage() {} func (*SubmitOpts) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{59} + return fileDescriptor_c23edafa7e7ea072, []int{61} } func (m *SubmitOpts) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1718,7 +1774,7 @@ var xxx_messageInfo_SubmitOpts proto.InternalMessageInfo func (m *SuppliedValueFrom) Reset() { *m = SuppliedValueFrom{} } func (*SuppliedValueFrom) ProtoMessage() {} func (*SuppliedValueFrom) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{60} + return fileDescriptor_c23edafa7e7ea072, []int{62} } func (m *SuppliedValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1746,7 +1802,7 @@ var xxx_messageInfo_SuppliedValueFrom proto.InternalMessageInfo func (m *SuspendTemplate) Reset() { *m = SuspendTemplate{} } func (*SuspendTemplate) ProtoMessage() {} func (*SuspendTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{61} + return fileDescriptor_c23edafa7e7ea072, []int{63} } func (m *SuspendTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1774,7 +1830,7 @@ var xxx_messageInfo_SuspendTemplate proto.InternalMessageInfo func (m *Synchronization) Reset() { *m = Synchronization{} } func (*Synchronization) ProtoMessage() {} func (*Synchronization) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{62} + return fileDescriptor_c23edafa7e7ea072, []int{64} } func (m *Synchronization) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1802,7 +1858,7 @@ var xxx_messageInfo_Synchronization proto.InternalMessageInfo func (m *SynchronizationStatus) Reset() { *m = SynchronizationStatus{} } func (*SynchronizationStatus) ProtoMessage() {} func (*SynchronizationStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{63} + return fileDescriptor_c23edafa7e7ea072, []int{65} } func (m *SynchronizationStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1830,7 +1886,7 @@ var xxx_messageInfo_SynchronizationStatus proto.InternalMessageInfo func (m *TTLStrategy) Reset() { *m = TTLStrategy{} } func (*TTLStrategy) ProtoMessage() {} func (*TTLStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{64} + return fileDescriptor_c23edafa7e7ea072, []int{66} } func (m *TTLStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1858,7 +1914,7 @@ var xxx_messageInfo_TTLStrategy proto.InternalMessageInfo func (m *TarStrategy) Reset() { *m = TarStrategy{} } func (*TarStrategy) ProtoMessage() {} func (*TarStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{65} + return fileDescriptor_c23edafa7e7ea072, []int{67} } func (m *TarStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1886,7 +1942,7 @@ var xxx_messageInfo_TarStrategy proto.InternalMessageInfo func (m *Template) Reset() { *m = Template{} } func (*Template) ProtoMessage() {} func (*Template) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{66} + return fileDescriptor_c23edafa7e7ea072, []int{68} } func (m *Template) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1914,7 +1970,7 @@ var xxx_messageInfo_Template proto.InternalMessageInfo func (m *TemplateRef) Reset() { *m = TemplateRef{} } func (*TemplateRef) ProtoMessage() {} func (*TemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{67} + return fileDescriptor_c23edafa7e7ea072, []int{69} } func (m *TemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1942,7 +1998,7 @@ var xxx_messageInfo_TemplateRef proto.InternalMessageInfo func (m *UserContainer) Reset() { *m = UserContainer{} } func (*UserContainer) ProtoMessage() {} func (*UserContainer) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{68} + return fileDescriptor_c23edafa7e7ea072, []int{70} } func (m *UserContainer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1970,7 +2026,7 @@ var xxx_messageInfo_UserContainer proto.InternalMessageInfo func (m *ValueFrom) Reset() { *m = ValueFrom{} } func (*ValueFrom) ProtoMessage() {} func (*ValueFrom) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{69} + return fileDescriptor_c23edafa7e7ea072, []int{71} } func (m *ValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1998,7 +2054,7 @@ var xxx_messageInfo_ValueFrom proto.InternalMessageInfo func (m *Version) Reset() { *m = Version{} } func (*Version) ProtoMessage() {} func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{70} + return fileDescriptor_c23edafa7e7ea072, []int{72} } func (m *Version) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2026,7 +2082,7 @@ var xxx_messageInfo_Version proto.InternalMessageInfo func (m *WaitingStatus) Reset() { *m = WaitingStatus{} } func (*WaitingStatus) ProtoMessage() {} func (*WaitingStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{71} + return fileDescriptor_c23edafa7e7ea072, []int{73} } func (m *WaitingStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2054,7 +2110,7 @@ var xxx_messageInfo_WaitingStatus proto.InternalMessageInfo func (m *Workflow) Reset() { *m = Workflow{} } func (*Workflow) ProtoMessage() {} func (*Workflow) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{72} + return fileDescriptor_c23edafa7e7ea072, []int{74} } func (m *Workflow) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2079,10 +2135,94 @@ func (m *Workflow) XXX_DiscardUnknown() { var xxx_messageInfo_Workflow proto.InternalMessageInfo +func (m *WorkflowEventBinding) Reset() { *m = WorkflowEventBinding{} } +func (*WorkflowEventBinding) ProtoMessage() {} +func (*WorkflowEventBinding) Descriptor() ([]byte, []int) { + return fileDescriptor_c23edafa7e7ea072, []int{75} +} +func (m *WorkflowEventBinding) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WorkflowEventBinding) 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 *WorkflowEventBinding) XXX_Merge(src proto.Message) { + xxx_messageInfo_WorkflowEventBinding.Merge(m, src) +} +func (m *WorkflowEventBinding) XXX_Size() int { + return m.Size() +} +func (m *WorkflowEventBinding) XXX_DiscardUnknown() { + xxx_messageInfo_WorkflowEventBinding.DiscardUnknown(m) +} + +var xxx_messageInfo_WorkflowEventBinding proto.InternalMessageInfo + +func (m *WorkflowEventBindingList) Reset() { *m = WorkflowEventBindingList{} } +func (*WorkflowEventBindingList) ProtoMessage() {} +func (*WorkflowEventBindingList) Descriptor() ([]byte, []int) { + return fileDescriptor_c23edafa7e7ea072, []int{76} +} +func (m *WorkflowEventBindingList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WorkflowEventBindingList) 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 *WorkflowEventBindingList) XXX_Merge(src proto.Message) { + xxx_messageInfo_WorkflowEventBindingList.Merge(m, src) +} +func (m *WorkflowEventBindingList) XXX_Size() int { + return m.Size() +} +func (m *WorkflowEventBindingList) XXX_DiscardUnknown() { + xxx_messageInfo_WorkflowEventBindingList.DiscardUnknown(m) +} + +var xxx_messageInfo_WorkflowEventBindingList proto.InternalMessageInfo + +func (m *WorkflowEventBindingSpec) Reset() { *m = WorkflowEventBindingSpec{} } +func (*WorkflowEventBindingSpec) ProtoMessage() {} +func (*WorkflowEventBindingSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_c23edafa7e7ea072, []int{77} +} +func (m *WorkflowEventBindingSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WorkflowEventBindingSpec) 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 *WorkflowEventBindingSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_WorkflowEventBindingSpec.Merge(m, src) +} +func (m *WorkflowEventBindingSpec) XXX_Size() int { + return m.Size() +} +func (m *WorkflowEventBindingSpec) XXX_DiscardUnknown() { + xxx_messageInfo_WorkflowEventBindingSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_WorkflowEventBindingSpec proto.InternalMessageInfo + func (m *WorkflowList) Reset() { *m = WorkflowList{} } func (*WorkflowList) ProtoMessage() {} func (*WorkflowList) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{73} + return fileDescriptor_c23edafa7e7ea072, []int{78} } func (m *WorkflowList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2110,7 +2250,7 @@ var xxx_messageInfo_WorkflowList proto.InternalMessageInfo func (m *WorkflowSpec) Reset() { *m = WorkflowSpec{} } func (*WorkflowSpec) ProtoMessage() {} func (*WorkflowSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{74} + return fileDescriptor_c23edafa7e7ea072, []int{79} } func (m *WorkflowSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2138,7 +2278,7 @@ var xxx_messageInfo_WorkflowSpec proto.InternalMessageInfo func (m *WorkflowStatus) Reset() { *m = WorkflowStatus{} } func (*WorkflowStatus) ProtoMessage() {} func (*WorkflowStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{75} + return fileDescriptor_c23edafa7e7ea072, []int{80} } func (m *WorkflowStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2166,7 +2306,7 @@ var xxx_messageInfo_WorkflowStatus proto.InternalMessageInfo func (m *WorkflowStep) Reset() { *m = WorkflowStep{} } func (*WorkflowStep) ProtoMessage() {} func (*WorkflowStep) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{76} + return fileDescriptor_c23edafa7e7ea072, []int{81} } func (m *WorkflowStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2194,7 +2334,7 @@ var xxx_messageInfo_WorkflowStep proto.InternalMessageInfo func (m *WorkflowTemplate) Reset() { *m = WorkflowTemplate{} } func (*WorkflowTemplate) ProtoMessage() {} func (*WorkflowTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{77} + return fileDescriptor_c23edafa7e7ea072, []int{82} } func (m *WorkflowTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2222,7 +2362,7 @@ var xxx_messageInfo_WorkflowTemplate proto.InternalMessageInfo func (m *WorkflowTemplateList) Reset() { *m = WorkflowTemplateList{} } func (*WorkflowTemplateList) ProtoMessage() {} func (*WorkflowTemplateList) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{78} + return fileDescriptor_c23edafa7e7ea072, []int{83} } func (m *WorkflowTemplateList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2250,7 +2390,7 @@ var xxx_messageInfo_WorkflowTemplateList proto.InternalMessageInfo func (m *WorkflowTemplateRef) Reset() { *m = WorkflowTemplateRef{} } func (*WorkflowTemplateRef) ProtoMessage() {} func (*WorkflowTemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{79} + return fileDescriptor_c23edafa7e7ea072, []int{84} } func (m *WorkflowTemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2278,7 +2418,7 @@ var xxx_messageInfo_WorkflowTemplateRef proto.InternalMessageInfo func (m *WorkflowTemplateSpec) Reset() { *m = WorkflowTemplateSpec{} } func (*WorkflowTemplateSpec) ProtoMessage() {} func (*WorkflowTemplateSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_c23edafa7e7ea072, []int{80} + return fileDescriptor_c23edafa7e7ea072, []int{85} } func (m *WorkflowTemplateSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2325,6 +2465,7 @@ func init() { proto.RegisterType((*CronWorkflowStatus)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.CronWorkflowStatus") proto.RegisterType((*DAGTask)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.DAGTask") proto.RegisterType((*DAGTemplate)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.DAGTemplate") + proto.RegisterType((*Event)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.Event") proto.RegisterType((*ExecutorConfig)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.ExecutorConfig") proto.RegisterType((*GCSArtifact)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.GCSArtifact") proto.RegisterType((*GCSBucket)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.GCSBucket") @@ -2366,6 +2507,7 @@ func init() { proto.RegisterType((*SemaphoreRef)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.SemaphoreRef") proto.RegisterType((*SemaphoreStatus)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.SemaphoreStatus") proto.RegisterType((*Sequence)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.Sequence") + proto.RegisterType((*Submit)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.Submit") proto.RegisterType((*SubmitOpts)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.SubmitOpts") proto.RegisterType((*SuppliedValueFrom)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.SuppliedValueFrom") proto.RegisterType((*SuspendTemplate)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.SuspendTemplate") @@ -2381,6 +2523,9 @@ func init() { proto.RegisterType((*Version)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.Version") proto.RegisterType((*WaitingStatus)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.WaitingStatus") proto.RegisterType((*Workflow)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.Workflow") + proto.RegisterType((*WorkflowEventBinding)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.WorkflowEventBinding") + proto.RegisterType((*WorkflowEventBindingList)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.WorkflowEventBindingList") + proto.RegisterType((*WorkflowEventBindingSpec)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.WorkflowEventBindingSpec") proto.RegisterType((*WorkflowList)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.WorkflowList") proto.RegisterType((*WorkflowSpec)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.WorkflowSpec") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo.pkg.apis.workflow.v1alpha1.WorkflowSpec.NodeSelectorEntry") @@ -2400,434 +2545,444 @@ func init() { } var fileDescriptor_c23edafa7e7ea072 = []byte{ - // 6828 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x5c, 0xd7, - 0x71, 0xb0, 0x97, 0xe4, 0x92, 0xbb, 0xb3, 0xa4, 0x48, 0x1e, 0xfd, 0xad, 0x69, 0x59, 0xab, 0x5c, - 0xc7, 0x86, 0xfc, 0x25, 0x21, 0x63, 0x29, 0xfe, 0x3e, 0x27, 0x8e, 0x7f, 0xb8, 0xa4, 0x48, 0xc9, - 0xfa, 0x21, 0x3d, 0x4b, 0x49, 0x5f, 0x62, 0xc3, 0xe9, 0xe5, 0xee, 0xd9, 0xdd, 0x2b, 0xee, 0xde, - 0xbb, 0xbe, 0xe7, 0xae, 0x68, 0xc6, 0x29, 0xea, 0x04, 0x0d, 0xd2, 0x24, 0x08, 0xd0, 0x22, 0x40, - 0x90, 0x22, 0x28, 0x90, 0x97, 0xa2, 0x2f, 0x7d, 0x6d, 0x5f, 0x0a, 0xe4, 0xa1, 0x40, 0x8b, 0x34, - 0x2f, 0x4d, 0x9f, 0x9a, 0x87, 0x82, 0x89, 0x59, 0xa0, 0x08, 0x90, 0xb6, 0x79, 0x0a, 0x0a, 0x08, - 0x05, 0x5a, 0x9c, 0xdf, 0xfb, 0xb3, 0x77, 0x25, 0xea, 0x2e, 0xa5, 0x06, 0x4d, 0xde, 0xf6, 0xce, - 0xcc, 0x99, 0x39, 0xbf, 0x73, 0x66, 0xe6, 0xcc, 0x39, 0x0b, 0x2b, 0x2d, 0x27, 0x68, 0xf7, 0xb7, - 0x17, 0xeb, 0x5e, 0x77, 0xc9, 0xf6, 0x5b, 0x5e, 0xcf, 0xf7, 0xee, 0x88, 0x1f, 0x4b, 0xbd, 0x9d, - 0xd6, 0x92, 0xdd, 0x73, 0xd8, 0xd2, 0xae, 0xe7, 0xef, 0x34, 0x3b, 0xde, 0xee, 0xd2, 0xdd, 0x17, - 0xec, 0x4e, 0xaf, 0x6d, 0xbf, 0xb0, 0xd4, 0xa2, 0x2e, 0xf5, 0xed, 0x80, 0x36, 0x16, 0x7b, 0xbe, - 0x17, 0x78, 0xe4, 0x62, 0xc8, 0x64, 0x51, 0x33, 0x11, 0x3f, 0x16, 0x7b, 0x3b, 0xad, 0x45, 0xce, - 0x64, 0x51, 0x33, 0x59, 0xd4, 0x4c, 0x16, 0x3e, 0x11, 0x91, 0xdc, 0xf2, 0xb8, 0x40, 0xce, 0x6b, - 0xbb, 0xdf, 0x14, 0x5f, 0xe2, 0x43, 0xfc, 0x92, 0x32, 0x16, 0xac, 0x9d, 0x97, 0xd8, 0xa2, 0xe3, - 0xf1, 0x2a, 0x2d, 0xd5, 0x3d, 0x9f, 0x2e, 0xdd, 0x1d, 0xa8, 0xc7, 0xc2, 0xf3, 0x11, 0x9a, 0x9e, - 0xd7, 0x71, 0xea, 0x7b, 0x4b, 0x77, 0x5f, 0xd8, 0xa6, 0xc1, 0x60, 0x95, 0x17, 0x3e, 0x15, 0x92, - 0x76, 0xed, 0x7a, 0xdb, 0x71, 0xa9, 0xbf, 0x17, 0x36, 0xb9, 0x4b, 0x03, 0x3b, 0x4d, 0xc0, 0xd2, - 0xb0, 0x52, 0x7e, 0xdf, 0x0d, 0x9c, 0x2e, 0x1d, 0x28, 0xf0, 0x7f, 0x1f, 0x54, 0x80, 0xd5, 0xdb, - 0xb4, 0x6b, 0x0f, 0x94, 0xbb, 0x38, 0xac, 0x5c, 0x3f, 0x70, 0x3a, 0x4b, 0x8e, 0x1b, 0xb0, 0xc0, - 0x4f, 0x16, 0xb2, 0x9e, 0x87, 0xc9, 0xe5, 0xae, 0xd7, 0x77, 0x03, 0x52, 0x81, 0xfc, 0x5d, 0xbb, - 0xd3, 0xa7, 0xe5, 0xdc, 0xb9, 0xdc, 0xf9, 0xe9, 0x6a, 0xf1, 0x60, 0xbf, 0x92, 0xbf, 0xc5, 0x01, - 0x28, 0xe1, 0xd6, 0xdf, 0xe7, 0x60, 0x76, 0xd9, 0xaf, 0xb7, 0x9d, 0xbb, 0xb4, 0x16, 0x70, 0x1e, - 0xad, 0x3d, 0xf2, 0x16, 0x8c, 0x07, 0xb6, 0x2f, 0x8a, 0x94, 0x2e, 0xbc, 0xbe, 0x98, 0x61, 0x4c, - 0x17, 0xb7, 0x6c, 0x5f, 0xb3, 0xab, 0x4e, 0x1d, 0xec, 0x57, 0xc6, 0xb7, 0x6c, 0x1f, 0x39, 0x57, - 0xf2, 0x05, 0x98, 0x70, 0x3d, 0x97, 0x96, 0xc7, 0x04, 0xf7, 0xe5, 0x4c, 0xdc, 0x6f, 0x78, 0xae, - 0xa9, 0x6d, 0xb5, 0x70, 0xb0, 0x5f, 0x99, 0xe0, 0x10, 0x14, 0x8c, 0xad, 0x5f, 0xe6, 0xa0, 0xb8, - 0xec, 0xb7, 0xfa, 0x5d, 0xea, 0x06, 0x8c, 0xf8, 0x00, 0x3d, 0xdb, 0xb7, 0xbb, 0x34, 0xa0, 0x3e, - 0x2b, 0xe7, 0xce, 0x8d, 0x9f, 0x2f, 0x5d, 0x78, 0x35, 0x93, 0xd0, 0x4d, 0xcd, 0xa6, 0x4a, 0x7e, - 0xb8, 0x5f, 0x79, 0xe2, 0x60, 0xbf, 0x02, 0x06, 0xc4, 0x30, 0x22, 0x85, 0xb8, 0x50, 0xb4, 0xfd, - 0xc0, 0x69, 0xda, 0xf5, 0x80, 0x95, 0xc7, 0x84, 0xc8, 0x57, 0x32, 0x89, 0x5c, 0x56, 0x5c, 0xaa, - 0xf3, 0x4a, 0x62, 0x51, 0x43, 0x18, 0x86, 0x22, 0xac, 0x5f, 0x8c, 0x43, 0x41, 0x23, 0xc8, 0x39, - 0x98, 0x70, 0xed, 0xae, 0x1c, 0xf0, 0x62, 0x75, 0x5a, 0x15, 0x9c, 0xb8, 0x61, 0x77, 0x79, 0x07, - 0xd9, 0x5d, 0xca, 0x29, 0x7a, 0x76, 0xd0, 0x16, 0x23, 0x10, 0xa1, 0xd8, 0xb4, 0x83, 0x36, 0x0a, - 0x0c, 0x39, 0x03, 0x13, 0x5d, 0xaf, 0x41, 0xcb, 0xe3, 0xe7, 0x72, 0xe7, 0xf3, 0xb2, 0x83, 0xaf, - 0x7b, 0x0d, 0x8a, 0x02, 0xca, 0xcb, 0x37, 0x7d, 0xaf, 0x5b, 0x9e, 0x88, 0x97, 0x5f, 0xf3, 0xbd, - 0x2e, 0x0a, 0x0c, 0xf9, 0x66, 0x0e, 0xe6, 0x74, 0xf5, 0xae, 0x79, 0x75, 0x3b, 0x70, 0x3c, 0xb7, - 0x9c, 0x17, 0x03, 0x7e, 0x69, 0xa4, 0x8e, 0xd0, 0xcc, 0xaa, 0x65, 0x25, 0x75, 0x2e, 0x89, 0xc1, - 0x01, 0xc1, 0xe4, 0x02, 0x40, 0xab, 0xe3, 0x6d, 0xdb, 0x1d, 0xde, 0x07, 0xe5, 0x49, 0x51, 0x6b, - 0x33, 0x84, 0xeb, 0x06, 0x83, 0x11, 0x2a, 0xb2, 0x03, 0x53, 0xb6, 0x5c, 0x15, 0xe5, 0x29, 0x51, - 0xef, 0xd5, 0x8c, 0xf5, 0x8e, 0xad, 0xac, 0x6a, 0xe9, 0x60, 0xbf, 0x32, 0xa5, 0x80, 0xa8, 0x25, - 0x90, 0x8f, 0x43, 0xc1, 0xeb, 0xf1, 0xaa, 0xda, 0x9d, 0x72, 0xe1, 0x5c, 0xee, 0x7c, 0xa1, 0x3a, - 0xa7, 0xaa, 0x57, 0xd8, 0x50, 0x70, 0x34, 0x14, 0xd6, 0x3f, 0x4c, 0xc2, 0x40, 0xab, 0xc9, 0x0b, - 0x50, 0x52, 0xdc, 0xae, 0x79, 0x2d, 0x26, 0x06, 0xbf, 0x50, 0x9d, 0x3d, 0xd8, 0xaf, 0x94, 0x96, - 0x43, 0x30, 0x46, 0x69, 0xc8, 0x6d, 0x18, 0x63, 0x17, 0xd5, 0x32, 0x7c, 0x2d, 0x53, 0xeb, 0x6a, - 0x17, 0xcd, 0x04, 0x9d, 0x3c, 0xd8, 0xaf, 0x8c, 0xd5, 0x2e, 0xe2, 0x18, 0xbb, 0xc8, 0xd5, 0x47, - 0xcb, 0x09, 0xc4, 0xe4, 0xc9, 0xaa, 0x3e, 0xd6, 0x9d, 0xc0, 0xb0, 0x16, 0xea, 0x63, 0xdd, 0x09, - 0x90, 0x73, 0xe5, 0xea, 0xa3, 0x1d, 0x04, 0x3d, 0x31, 0xf9, 0xb2, 0xaa, 0x8f, 0xcb, 0x5b, 0x5b, - 0x9b, 0x86, 0xbd, 0x98, 0xdd, 0x1c, 0x82, 0x82, 0x31, 0x79, 0x9f, 0xf7, 0xa4, 0xc4, 0x79, 0xfe, - 0x9e, 0x9a, 0xb5, 0x97, 0x47, 0x9a, 0xb5, 0x9e, 0xbf, 0x67, 0xc4, 0xa9, 0x31, 0x31, 0x08, 0x8c, - 0x4a, 0x13, 0xad, 0x6b, 0x34, 0x99, 0x98, 0xa4, 0x99, 0x5b, 0xb7, 0xba, 0x56, 0x4b, 0xb4, 0x6e, - 0x75, 0xad, 0x86, 0x82, 0x31, 0x1f, 0x1b, 0xdf, 0xde, 0x55, 0x73, 0x3a, 0xdb, 0xd8, 0xa0, 0xbd, - 0x1b, 0x1f, 0x1b, 0xb4, 0x77, 0x91, 0x73, 0xe5, 0xcc, 0x3d, 0xc6, 0xc4, 0x14, 0xce, 0xca, 0x7c, - 0xa3, 0x56, 0x8b, 0x33, 0xdf, 0xa8, 0xd5, 0x90, 0x73, 0x15, 0xb3, 0xaa, 0xce, 0xca, 0xc5, 0x51, - 0x66, 0xd5, 0x4a, 0x82, 0xf9, 0xfa, 0x4a, 0x0d, 0x39, 0x57, 0xab, 0x05, 0x27, 0x35, 0x06, 0x69, - 0xcf, 0x63, 0x8e, 0x18, 0x1a, 0xda, 0x24, 0x4b, 0x50, 0xac, 0x7b, 0x6e, 0xd3, 0x69, 0x5d, 0xb7, - 0x7b, 0x4a, 0xa5, 0x1a, 0x5d, 0xbc, 0xa2, 0x11, 0x18, 0xd2, 0x90, 0xa7, 0x61, 0x7c, 0x87, 0xee, - 0x29, 0xdd, 0x5a, 0x52, 0xa4, 0xe3, 0x57, 0xe9, 0x1e, 0x72, 0xb8, 0xf5, 0x83, 0x1c, 0x1c, 0x4f, - 0x99, 0x16, 0xbc, 0x58, 0xdf, 0xef, 0x28, 0x09, 0xa6, 0xd8, 0x4d, 0xbc, 0x86, 0x1c, 0x4e, 0xbe, - 0x96, 0x83, 0xd9, 0xc8, 0x3c, 0x59, 0xee, 0x2b, 0xf5, 0x9d, 0x5d, 0x2f, 0xc5, 0x78, 0x55, 0x4f, - 0x2b, 0x89, 0xb3, 0x09, 0x04, 0x26, 0xa5, 0x5a, 0xff, 0x28, 0xec, 0x85, 0x18, 0x8c, 0xd8, 0x70, - 0xac, 0xcf, 0xa8, 0xcf, 0x37, 0x97, 0x1a, 0xad, 0xfb, 0x34, 0x50, 0xa6, 0xc3, 0xb3, 0x8b, 0xd2, - 0x78, 0xe1, 0xb5, 0x58, 0xe4, 0xa6, 0xda, 0xe2, 0xdd, 0x17, 0x16, 0x25, 0xc5, 0x55, 0xba, 0x57, - 0xa3, 0x1d, 0xca, 0x79, 0x54, 0xc9, 0xc1, 0x7e, 0xe5, 0xd8, 0xcd, 0x18, 0x03, 0x4c, 0x30, 0xe4, - 0x22, 0x7a, 0x36, 0x63, 0xbb, 0x9e, 0xdf, 0x50, 0x22, 0xc6, 0x1e, 0x5a, 0xc4, 0x66, 0x8c, 0x01, - 0x26, 0x18, 0x5a, 0xdf, 0xc9, 0xc1, 0x54, 0xd5, 0xae, 0xef, 0x78, 0xcd, 0x26, 0xd7, 0xc8, 0x8d, - 0xbe, 0x2f, 0xf7, 0x2d, 0x39, 0x26, 0x46, 0x23, 0xaf, 0x2a, 0x38, 0x1a, 0x0a, 0xf2, 0x1c, 0x4c, - 0xca, 0xee, 0x10, 0x95, 0xca, 0x57, 0x8f, 0x29, 0xda, 0xc9, 0x35, 0x01, 0x45, 0x85, 0x25, 0x2f, - 0x42, 0xa9, 0x6b, 0xbf, 0xa7, 0x19, 0x08, 0x05, 0x59, 0xac, 0x1e, 0x57, 0xc4, 0xa5, 0xeb, 0x21, - 0x0a, 0xa3, 0x74, 0xd6, 0x3b, 0x90, 0x5f, 0xb1, 0xeb, 0x6d, 0x4a, 0x6e, 0x26, 0x27, 0x63, 0xe9, - 0xc2, 0xf9, 0xb4, 0xf6, 0x9b, 0x89, 0x19, 0xed, 0x82, 0x99, 0x61, 0x53, 0xd6, 0xfa, 0x79, 0x0e, - 0x4e, 0xaf, 0x74, 0xfa, 0x2c, 0xa0, 0xfe, 0x6d, 0x35, 0x53, 0xb6, 0x68, 0xb7, 0xd7, 0xb1, 0x03, - 0x4a, 0x7e, 0x07, 0x0a, 0xdc, 0x04, 0x6e, 0xd8, 0x81, 0xad, 0x24, 0x7e, 0x32, 0x22, 0xd1, 0x58, - 0xa4, 0xe1, 0x5c, 0xe3, 0xd4, 0xbc, 0x0e, 0x1b, 0xdb, 0x77, 0x68, 0x3d, 0xb8, 0x4e, 0x03, 0x3b, - 0xdc, 0x6b, 0x43, 0x18, 0x1a, 0xae, 0x64, 0x07, 0x26, 0x58, 0x8f, 0xd6, 0xd5, 0x78, 0x5e, 0xc9, - 0x34, 0x9d, 0x93, 0xd5, 0xae, 0xf5, 0x68, 0x3d, 0x34, 0x4c, 0xf8, 0x17, 0x0a, 0x21, 0xd6, 0xbf, - 0xe7, 0xe0, 0xa9, 0x21, 0x4d, 0xbd, 0xe6, 0xb0, 0x80, 0xbc, 0x3d, 0xd0, 0xdc, 0xc5, 0xc3, 0x35, - 0x97, 0x97, 0x16, 0x8d, 0x35, 0xf3, 0x44, 0x43, 0x22, 0x4d, 0x7d, 0x17, 0xf2, 0x4e, 0x40, 0xbb, - 0xda, 0x26, 0xbc, 0x96, 0xa9, 0xad, 0x43, 0xaa, 0x5f, 0x9d, 0x51, 0x82, 0xf3, 0x57, 0xb8, 0x08, - 0x94, 0x92, 0xac, 0xbf, 0xcb, 0x01, 0x1f, 0xf4, 0x86, 0xa3, 0xac, 0x84, 0x89, 0x60, 0xaf, 0xa7, - 0x6d, 0xc3, 0xa7, 0x75, 0x07, 0x6d, 0xed, 0xf5, 0xe8, 0xbd, 0xfd, 0xca, 0x8c, 0x21, 0xe4, 0x00, - 0x14, 0xa4, 0xe4, 0x1d, 0x98, 0x64, 0x81, 0x1d, 0xf4, 0x99, 0x52, 0x69, 0x6b, 0x7a, 0x6e, 0xd7, - 0x04, 0xf4, 0xde, 0x7e, 0xe5, 0x50, 0x0e, 0xd4, 0xa2, 0xe1, 0x2d, 0xcb, 0xa1, 0xe2, 0x4a, 0x9e, - 0x87, 0xa9, 0x2e, 0x65, 0xcc, 0x6e, 0x51, 0xb5, 0x1e, 0x66, 0x95, 0x80, 0xa9, 0xeb, 0x12, 0x8c, - 0x1a, 0x6f, 0x7d, 0x0e, 0x60, 0xc5, 0x73, 0x03, 0xc7, 0xed, 0xd3, 0x0d, 0x97, 0x3c, 0x03, 0x79, - 0xea, 0xfb, 0x9e, 0xaf, 0x6c, 0x1d, 0xd3, 0xfc, 0x4b, 0x1c, 0x88, 0x12, 0x27, 0x57, 0xa6, 0xd3, - 0xa1, 0x0d, 0x51, 0xfb, 0x42, 0x74, 0x65, 0x72, 0x28, 0x2a, 0xac, 0xb5, 0x08, 0x53, 0x2b, 0xdc, - 0x5f, 0xa2, 0x3e, 0xe7, 0x1b, 0x7a, 0x4c, 0xc5, 0x90, 0x6f, 0xcc, 0x6b, 0xfa, 0xd1, 0x18, 0x4c, - 0xaf, 0xf8, 0x9e, 0xab, 0x47, 0xe1, 0x31, 0xac, 0x93, 0x56, 0x6c, 0x9d, 0x64, 0x33, 0xa3, 0xa3, - 0x55, 0x1e, 0xb6, 0x46, 0x88, 0x67, 0x46, 0x5c, 0x5a, 0x70, 0xeb, 0xa3, 0x8b, 0x12, 0xec, 0xc2, - 0xce, 0x8f, 0x4f, 0x01, 0xeb, 0x27, 0x39, 0x98, 0x8b, 0x92, 0x3f, 0x86, 0x95, 0xd8, 0x8c, 0xaf, - 0xc4, 0xe5, 0x91, 0x9b, 0x38, 0x64, 0xf9, 0xfd, 0x67, 0x3e, 0xde, 0x34, 0xde, 0xcd, 0xdc, 0x3b, - 0x9a, 0xde, 0x8d, 0x00, 0x54, 0xfb, 0x96, 0x47, 0x52, 0x7d, 0x62, 0x38, 0x3f, 0xaa, 0x2a, 0x31, - 0x1d, 0x85, 0xde, 0x4b, 0x7c, 0x63, 0x4c, 0x38, 0xdf, 0xea, 0x58, 0xbd, 0x4d, 0x1b, 0xfd, 0x0e, - 0x55, 0x4b, 0xdc, 0x74, 0x5c, 0x4d, 0xc1, 0xd1, 0x50, 0x90, 0xb7, 0x61, 0xbe, 0xee, 0xb9, 0xf5, - 0xbe, 0xef, 0x53, 0xb7, 0xbe, 0xb7, 0x29, 0x42, 0x2b, 0x6a, 0xe1, 0x2e, 0xaa, 0x62, 0xf3, 0x2b, - 0x49, 0x82, 0x7b, 0x69, 0x40, 0x1c, 0x64, 0xc4, 0x95, 0x01, 0xeb, 0xb3, 0x1e, 0x75, 0x1b, 0xc2, - 0xbe, 0x2f, 0x84, 0xca, 0xa0, 0x26, 0xc1, 0xa8, 0xf1, 0xe4, 0x26, 0x9c, 0x66, 0x01, 0x37, 0x4e, - 0xdc, 0xd6, 0x2a, 0xb5, 0x1b, 0x1d, 0xc7, 0xe5, 0xa6, 0x82, 0xe7, 0x36, 0x98, 0x30, 0xd9, 0xc7, - 0xab, 0x4f, 0x1d, 0xec, 0x57, 0x4e, 0xd7, 0xd2, 0x49, 0x70, 0x58, 0x59, 0xf2, 0x0e, 0x2c, 0xb0, - 0x7e, 0xbd, 0x4e, 0x19, 0x6b, 0xf6, 0x3b, 0x6f, 0x78, 0xdb, 0xec, 0xb2, 0xc3, 0xb8, 0x9d, 0x73, - 0xcd, 0xe9, 0x3a, 0x81, 0x30, 0xcb, 0xf3, 0xd5, 0xb3, 0x07, 0xfb, 0x95, 0x85, 0xda, 0x50, 0x2a, - 0xbc, 0x0f, 0x07, 0x82, 0x70, 0x4a, 0xaa, 0x9c, 0x01, 0xde, 0x53, 0x82, 0xf7, 0xc2, 0xc1, 0x7e, - 0xe5, 0xd4, 0x5a, 0x2a, 0x05, 0x0e, 0x29, 0xc9, 0x47, 0x30, 0x70, 0xba, 0xf4, 0x8b, 0x9e, 0x4b, - 0x85, 0xed, 0x1d, 0x19, 0xc1, 0x2d, 0x05, 0x47, 0x43, 0x41, 0xee, 0x84, 0x93, 0x8f, 0x2f, 0x0a, - 0x65, 0x50, 0x3f, 0xbc, 0xb6, 0x3a, 0xc1, 0xbd, 0xef, 0xdb, 0x11, 0x4e, 0x7c, 0x61, 0x61, 0x8c, - 0xb7, 0xf5, 0xb7, 0x63, 0x40, 0x06, 0x15, 0x01, 0xb9, 0x0a, 0x93, 0x76, 0x3d, 0xe0, 0xbe, 0xb5, - 0x8c, 0xc7, 0x3c, 0x93, 0x66, 0xc4, 0x48, 0x51, 0x48, 0x9b, 0x94, 0xcf, 0x10, 0x1a, 0x6a, 0x8f, - 0x65, 0x51, 0x14, 0x15, 0x0b, 0xe2, 0xc1, 0x7c, 0xc7, 0x66, 0x81, 0x9e, 0xab, 0x0d, 0xde, 0x64, - 0xa5, 0x24, 0xff, 0xcf, 0xe1, 0x1a, 0xc5, 0x4b, 0x54, 0x4f, 0xf2, 0x99, 0x7b, 0x2d, 0xc9, 0x08, - 0x07, 0x79, 0x13, 0x1f, 0xa0, 0xae, 0x37, 0x33, 0xae, 0x23, 0xb3, 0x47, 0x94, 0xcc, 0x9e, 0x18, - 0xaa, 0x7e, 0x03, 0x62, 0x18, 0x91, 0x62, 0xfd, 0xdb, 0x24, 0x4c, 0xad, 0x2e, 0xaf, 0x6f, 0xd9, - 0x6c, 0xe7, 0x10, 0x01, 0x1e, 0x3e, 0x21, 0x94, 0x59, 0x90, 0x5c, 0xd2, 0xda, 0x5c, 0x40, 0x43, - 0x41, 0x3c, 0x28, 0xda, 0x3a, 0x5c, 0xa6, 0x54, 0xfe, 0xab, 0x19, 0x9d, 0x0a, 0xc5, 0x25, 0x1a, - 0xae, 0x52, 0x20, 0x0c, 0x65, 0x10, 0x06, 0x25, 0x2d, 0x1c, 0x69, 0x53, 0x79, 0xf2, 0x19, 0xc3, - 0x8c, 0x21, 0x1f, 0xe9, 0x59, 0x47, 0x00, 0x18, 0x95, 0x42, 0x3e, 0x05, 0xd3, 0x0d, 0xca, 0x35, - 0x07, 0x75, 0xeb, 0x0e, 0xe5, 0x4a, 0x62, 0x9c, 0xf7, 0x0b, 0x57, 0x96, 0xab, 0x11, 0x38, 0xc6, - 0xa8, 0xc8, 0x1d, 0x28, 0xee, 0x3a, 0x41, 0x5b, 0xe8, 0xf4, 0xf2, 0xa4, 0x18, 0xea, 0x4f, 0x67, - 0xaa, 0x28, 0xe7, 0x10, 0x76, 0xcb, 0x6d, 0xcd, 0x13, 0x43, 0xf6, 0xdc, 0xd5, 0xe4, 0x1f, 0x22, - 0xa6, 0x28, 0xb4, 0x41, 0x31, 0x5e, 0x40, 0x20, 0x30, 0xa4, 0x21, 0x0c, 0xa6, 0xf9, 0x47, 0x8d, - 0xbe, 0xdb, 0xe7, 0x2b, 0x44, 0xf9, 0xdd, 0xd9, 0x22, 0x8d, 0x9a, 0x89, 0xec, 0x91, 0xdb, 0x11, - 0xb6, 0x18, 0x13, 0xc2, 0x67, 0xdf, 0x6e, 0x9b, 0xba, 0x42, 0x6d, 0x44, 0x66, 0xdf, 0xed, 0x36, - 0x75, 0x51, 0x60, 0x88, 0x27, 0xd6, 0x87, 0x32, 0xd3, 0xca, 0x30, 0x42, 0x7c, 0x29, 0xb4, 0xf6, - 0xaa, 0xc7, 0xd4, 0xe2, 0x50, 0xdf, 0x18, 0x11, 0xc1, 0x8d, 0x3c, 0xcf, 0xbd, 0xf4, 0x9e, 0x13, - 0x94, 0x4b, 0xa2, 0x52, 0x46, 0x53, 0x6c, 0x08, 0x28, 0x2a, 0x2c, 0xdf, 0x5d, 0xe4, 0xe0, 0xb2, - 0xf2, 0x74, 0xdc, 0xd4, 0x94, 0x33, 0x80, 0xa1, 0xc6, 0x5b, 0x7f, 0x9d, 0x83, 0x12, 0x5f, 0x6f, - 0x7a, 0x8d, 0x3c, 0x07, 0x93, 0x81, 0xed, 0xb7, 0x94, 0x67, 0x1b, 0x11, 0xb1, 0x25, 0xa0, 0xa8, - 0xb0, 0xc4, 0x86, 0x7c, 0x60, 0xb3, 0x1d, 0x6d, 0x57, 0x7c, 0x36, 0x53, 0xb3, 0xd5, 0x42, 0x0f, - 0x4d, 0x0a, 0xfe, 0xc5, 0x50, 0x72, 0x26, 0xe7, 0xa1, 0xc0, 0xf7, 0x81, 0x35, 0x9b, 0xc9, 0x10, - 0x5b, 0xa1, 0x3a, 0xcd, 0x17, 0xf6, 0x9a, 0x82, 0xa1, 0xc1, 0x5a, 0x6f, 0xc3, 0xb1, 0x4b, 0xef, - 0xd1, 0x7a, 0x3f, 0xf0, 0x7c, 0xe9, 0xf7, 0x91, 0x37, 0x80, 0x30, 0xea, 0xdf, 0x75, 0xea, 0x74, - 0xb9, 0x5e, 0xe7, 0xf6, 0xee, 0x8d, 0x50, 0x91, 0x2c, 0x28, 0x69, 0xa4, 0x36, 0x40, 0x81, 0x29, - 0xa5, 0xac, 0x3f, 0xc9, 0x41, 0x29, 0x12, 0x50, 0xe1, 0x6a, 0xa4, 0xb5, 0x52, 0xab, 0xf6, 0xeb, - 0x3b, 0xc6, 0xff, 0x7f, 0x35, 0x6b, 0x94, 0x46, 0x72, 0x09, 0xa7, 0xbf, 0x01, 0x61, 0x28, 0xe3, - 0x41, 0x91, 0x96, 0xbf, 0xcc, 0x41, 0x58, 0x8e, 0x0f, 0xe0, 0x76, 0x58, 0xb5, 0xc8, 0x00, 0x2a, - 0xbe, 0x0a, 0x4b, 0x3e, 0xc8, 0xc1, 0xe9, 0x78, 0x63, 0x85, 0x0f, 0xfd, 0xf0, 0x11, 0x87, 0x8a, - 0x12, 0x70, 0xba, 0x96, 0xce, 0x0d, 0x87, 0x89, 0xb1, 0x6e, 0x41, 0x7e, 0xdd, 0xee, 0xb7, 0xe8, - 0xa1, 0x3c, 0x11, 0x3e, 0x1d, 0x7c, 0x6a, 0x77, 0x02, 0xbd, 0xeb, 0xa9, 0xe9, 0x80, 0x0a, 0x86, - 0x06, 0x6b, 0xfd, 0xf9, 0x04, 0x94, 0x22, 0x71, 0x55, 0xbe, 0x92, 0x7d, 0xda, 0xf3, 0x92, 0xfb, - 0x08, 0xd2, 0x9e, 0x87, 0x02, 0xc3, 0xf7, 0x11, 0x9f, 0xde, 0x75, 0x98, 0xe3, 0xb9, 0xc9, 0x7d, - 0x04, 0x15, 0x1c, 0x0d, 0x05, 0xa9, 0x40, 0xbe, 0x41, 0x7b, 0x41, 0x5b, 0xcc, 0xca, 0x09, 0x79, - 0xd4, 0xb4, 0xca, 0x01, 0x28, 0xe1, 0x9c, 0xa0, 0x49, 0x83, 0x7a, 0xbb, 0x3c, 0x21, 0x74, 0xaf, - 0x20, 0x58, 0xe3, 0x00, 0x94, 0xf0, 0x94, 0x38, 0x52, 0xfe, 0xd1, 0xc7, 0x91, 0x26, 0x8f, 0x38, - 0x8e, 0x44, 0x7a, 0x70, 0x9c, 0xb1, 0xf6, 0xa6, 0xef, 0xdc, 0xb5, 0x03, 0x1a, 0xce, 0x9e, 0xa9, - 0x87, 0x91, 0x73, 0xfa, 0x60, 0xbf, 0x72, 0xbc, 0x56, 0xbb, 0x9c, 0xe4, 0x82, 0x69, 0xac, 0x49, - 0x0d, 0x4e, 0x3a, 0x2e, 0xa3, 0xf5, 0xbe, 0x4f, 0xaf, 0xb4, 0x5c, 0xcf, 0xa7, 0x97, 0x3d, 0xc6, - 0xd9, 0xa9, 0xc3, 0x04, 0xed, 0xe7, 0x9f, 0xbc, 0x92, 0x46, 0x84, 0xe9, 0x65, 0xad, 0x1f, 0xe5, - 0x60, 0x3a, 0x1a, 0x4a, 0x26, 0x0c, 0xa0, 0xbd, 0xba, 0x56, 0x93, 0xaa, 0x44, 0xad, 0xf0, 0xd7, - 0x32, 0x47, 0xa8, 0x25, 0x9b, 0xd0, 0xf0, 0x09, 0x61, 0x18, 0x11, 0x73, 0x88, 0xb3, 0xaa, 0x67, - 0x20, 0xdf, 0xf4, 0xfc, 0x3a, 0x55, 0xca, 0xd0, 0xac, 0x92, 0x35, 0x0e, 0x44, 0x89, 0xb3, 0x7e, - 0x9e, 0x83, 0x88, 0x04, 0xf2, 0x7b, 0x30, 0xc3, 0x65, 0x5c, 0xf5, 0xb7, 0x63, 0xad, 0xa9, 0x66, - 0x6e, 0x8d, 0xe1, 0x54, 0x3d, 0xa9, 0xe4, 0xcf, 0xc4, 0xc0, 0x18, 0x97, 0x47, 0x3e, 0x06, 0x45, - 0xbb, 0xd1, 0xf0, 0x29, 0x63, 0x54, 0xee, 0x15, 0x45, 0x19, 0x9f, 0x5b, 0xd6, 0x40, 0x0c, 0xf1, - 0x7c, 0x19, 0xb6, 0x1b, 0x4d, 0xc6, 0x67, 0xb6, 0x72, 0xb5, 0xcc, 0x32, 0xe4, 0x42, 0x38, 0x1c, - 0x0d, 0x85, 0xf5, 0xad, 0x09, 0x88, 0xcb, 0x26, 0x0d, 0x98, 0xdd, 0xf1, 0xb7, 0x57, 0x44, 0x0c, - 0x31, 0x4b, 0x7c, 0xf6, 0xf8, 0xc1, 0x7e, 0x65, 0xf6, 0x6a, 0x9c, 0x03, 0x26, 0x59, 0x2a, 0x29, - 0x57, 0xe9, 0x5e, 0x60, 0x6f, 0x67, 0x51, 0x98, 0x5a, 0x4a, 0x94, 0x03, 0x26, 0x59, 0x92, 0x17, - 0xa1, 0xb4, 0xe3, 0x6f, 0xeb, 0x45, 0x9e, 0x0c, 0xa1, 0x5e, 0x0d, 0x51, 0x18, 0xa5, 0xe3, 0x5d, - 0xb8, 0xe3, 0x6f, 0x73, 0xa5, 0xa8, 0x8f, 0x2d, 0x4d, 0x17, 0x5e, 0x55, 0x70, 0x34, 0x14, 0xa4, - 0x07, 0x64, 0x47, 0xf7, 0x9e, 0x89, 0x98, 0x2a, 0x5d, 0x74, 0xf8, 0x80, 0xeb, 0x29, 0xbe, 0x99, - 0x5e, 0x1d, 0xe0, 0x83, 0x29, 0xbc, 0xc9, 0xe7, 0xe0, 0xf4, 0x8e, 0xbf, 0xad, 0xb6, 0x8a, 0x4d, - 0xdf, 0x71, 0xeb, 0x4e, 0x2f, 0x76, 0x5e, 0x69, 0xb6, 0x93, 0xab, 0xe9, 0x64, 0x38, 0xac, 0xbc, - 0xf5, 0x09, 0x98, 0x8e, 0x9e, 0x77, 0x3d, 0xe0, 0xa4, 0xc1, 0xfa, 0x6e, 0x0e, 0x8a, 0xc2, 0xbb, - 0x6c, 0x71, 0x13, 0xd3, 0x6c, 0x41, 0xe3, 0xf7, 0xd9, 0x82, 0x9a, 0x30, 0x25, 0x77, 0x4f, 0x26, - 0x34, 0x7b, 0xe9, 0xc2, 0xcb, 0xd9, 0xdc, 0x07, 0x91, 0xb1, 0x10, 0x1a, 0x65, 0x72, 0x67, 0x66, - 0xa8, 0x99, 0x5b, 0x1f, 0x83, 0xd2, 0x65, 0xaf, 0xd3, 0xa0, 0x3e, 0x6f, 0x17, 0x23, 0x67, 0x8c, - 0x1f, 0xc4, 0x97, 0x4f, 0x21, 0xee, 0x03, 0x59, 0xff, 0x9a, 0x83, 0xc9, 0x2b, 0x6e, 0xaf, 0xff, - 0x1b, 0x92, 0x02, 0x70, 0x1d, 0x26, 0xb8, 0x17, 0x41, 0x5e, 0x8e, 0xe7, 0x7b, 0x3c, 0x6b, 0x06, - 0xeb, 0xde, 0x7e, 0xa5, 0x4c, 0xdd, 0xba, 0xd7, 0x70, 0xdc, 0xd6, 0xd2, 0x1d, 0xe6, 0xb9, 0x8b, - 0x68, 0xef, 0xea, 0x10, 0xab, 0x2c, 0xf3, 0x99, 0xc2, 0x77, 0xbf, 0x5f, 0x79, 0xe2, 0x83, 0x7f, - 0x3a, 0xf7, 0x84, 0xd5, 0x81, 0x89, 0x6b, 0x8e, 0x7b, 0x18, 0x5f, 0xf3, 0x19, 0xc8, 0xb3, 0xba, - 0xd7, 0xd3, 0x8e, 0xa6, 0x99, 0x21, 0x35, 0x0e, 0x44, 0x89, 0xd3, 0x73, 0x6e, 0x7c, 0xc8, 0x9c, - 0xfb, 0x4a, 0x0e, 0xe6, 0xaf, 0xd3, 0xae, 0xe7, 0x7c, 0xd1, 0x0e, 0x23, 0xc4, 0xbc, 0x50, 0xdb, - 0x09, 0x54, 0x78, 0xd7, 0x14, 0xba, 0xec, 0x04, 0xc8, 0xe1, 0x0f, 0x30, 0xff, 0xc4, 0xc1, 0x1d, - 0xd7, 0x4e, 0x37, 0x42, 0x35, 0x11, 0x1e, 0xdc, 0x69, 0x04, 0x86, 0x34, 0xd6, 0x57, 0x73, 0x30, - 0x25, 0x2b, 0x41, 0x35, 0xef, 0xdc, 0x10, 0xde, 0x6f, 0x41, 0x5e, 0x94, 0x53, 0x0a, 0xee, 0x33, - 0xd9, 0x9c, 0x1b, 0xce, 0x41, 0x1a, 0x41, 0xe2, 0x27, 0x4a, 0x9e, 0xd6, 0x07, 0xe3, 0x50, 0xd0, - 0xe1, 0x14, 0xf2, 0xd5, 0x1c, 0x94, 0x6c, 0xd7, 0xf5, 0x02, 0x5b, 0x46, 0x1b, 0xe4, 0xe4, 0xbd, - 0x91, 0x49, 0xa0, 0x66, 0xba, 0xb8, 0x1c, 0x32, 0xbc, 0xe4, 0x06, 0xfe, 0x5e, 0xa8, 0x3f, 0x23, - 0x18, 0x8c, 0xca, 0x25, 0xef, 0xc2, 0x64, 0xc7, 0xde, 0xa6, 0x1d, 0x3d, 0x97, 0xaf, 0x8c, 0x56, - 0x83, 0x6b, 0x82, 0x97, 0x14, 0x6e, 0x2c, 0x71, 0x09, 0x44, 0x25, 0x68, 0xe1, 0x55, 0x98, 0x4b, - 0x56, 0x94, 0xcc, 0x45, 0xc6, 0x45, 0x0e, 0xc5, 0x09, 0x3d, 0xdf, 0xc5, 0x3c, 0xd0, 0x13, 0x79, - 0xec, 0xa5, 0xdc, 0xc2, 0xa7, 0xa1, 0x14, 0x11, 0xf3, 0x30, 0x45, 0xad, 0x37, 0xa1, 0x74, 0x9d, - 0x06, 0xbe, 0x53, 0x17, 0x0c, 0x1e, 0x34, 0x1b, 0x9e, 0x89, 0xf1, 0x19, 0x72, 0x60, 0xf0, 0x45, - 0x3e, 0xb9, 0x38, 0x4b, 0xc6, 0xfd, 0xe3, 0x9e, 0xef, 0x75, 0x69, 0xd0, 0xa6, 0x7d, 0x3d, 0xa2, - 0xd9, 0xec, 0xa8, 0x4d, 0xc3, 0x46, 0xfa, 0xc7, 0xe1, 0x37, 0x46, 0x44, 0x58, 0xbf, 0x9a, 0x01, - 0xb8, 0xe1, 0x35, 0xa8, 0x5a, 0x57, 0x0b, 0x30, 0xe6, 0x34, 0x54, 0x6b, 0x40, 0x55, 0x76, 0xec, - 0xca, 0x2a, 0x8e, 0x39, 0x0d, 0xb3, 0xde, 0xc7, 0x86, 0xae, 0xf7, 0x17, 0xa1, 0xd4, 0x70, 0x58, - 0xaf, 0x63, 0xef, 0xdd, 0x48, 0xd9, 0x80, 0x57, 0x43, 0x14, 0x46, 0xe9, 0xc8, 0xc7, 0xd5, 0xc9, - 0x93, 0xdc, 0x7c, 0xcb, 0x89, 0x93, 0xa7, 0x02, 0xaf, 0x5e, 0xe4, 0xd0, 0xe9, 0x25, 0x98, 0xd6, - 0xb1, 0x1b, 0x21, 0x25, 0x2f, 0x4a, 0x9d, 0xd0, 0xd1, 0xed, 0xad, 0x08, 0x0e, 0x63, 0x94, 0xc9, - 0xd8, 0xd2, 0xe4, 0x63, 0x89, 0x2d, 0xad, 0xc2, 0x1c, 0x0b, 0x3c, 0x9f, 0x36, 0x34, 0xc5, 0x95, - 0xd5, 0x32, 0x89, 0x35, 0x74, 0xae, 0x96, 0xc0, 0xe3, 0x40, 0x09, 0xb2, 0x09, 0x27, 0x76, 0x13, - 0x87, 0x7a, 0xa2, 0xf1, 0xc7, 0x05, 0xa7, 0x33, 0x8a, 0xd3, 0x89, 0xdb, 0x29, 0x34, 0x98, 0x5a, - 0x92, 0xbc, 0x0c, 0x33, 0xba, 0x9a, 0x42, 0x1d, 0x97, 0x4f, 0x08, 0x56, 0xc6, 0x44, 0xdd, 0x8a, - 0x22, 0x31, 0x4e, 0x4b, 0x3e, 0x09, 0xf9, 0x5e, 0xdb, 0x66, 0x54, 0x85, 0xa2, 0x74, 0x78, 0x20, - 0xbf, 0xc9, 0x81, 0xf7, 0xf6, 0x2b, 0x45, 0x3e, 0x66, 0xe2, 0x03, 0x25, 0x21, 0xb9, 0x00, 0xb0, - 0xed, 0xf5, 0xdd, 0x86, 0xed, 0xef, 0x5d, 0x59, 0x55, 0x91, 0x68, 0xb3, 0x4f, 0x56, 0x0d, 0x06, - 0x23, 0x54, 0xd1, 0xe3, 0xbf, 0xe2, 0xfd, 0x8f, 0xff, 0xc8, 0x5b, 0x50, 0x14, 0x51, 0x7b, 0xda, - 0x58, 0x0e, 0x54, 0x58, 0xe9, 0x61, 0x02, 0xbc, 0x46, 0xfb, 0xd7, 0x34, 0x13, 0x0c, 0xf9, 0x91, - 0x77, 0x00, 0x9a, 0x8e, 0xeb, 0xb0, 0xb6, 0xe0, 0x5e, 0x7a, 0x68, 0xee, 0xa6, 0x9d, 0x6b, 0x86, - 0x0b, 0x46, 0x38, 0x92, 0x5f, 0xe4, 0x60, 0xde, 0xa7, 0xcc, 0xeb, 0xfb, 0x75, 0xca, 0x4c, 0x06, - 0xc0, 0x49, 0xb1, 0xf8, 0x6f, 0x65, 0xcc, 0x81, 0xd4, 0x2b, 0x7a, 0x11, 0x93, 0x8c, 0xa5, 0x66, - 0xa5, 0xfa, 0x40, 0x66, 0x00, 0x7f, 0x2f, 0x0d, 0xf8, 0x95, 0x9f, 0x56, 0x2a, 0x83, 0xa9, 0xb5, - 0x86, 0x39, 0x9f, 0x51, 0xdf, 0xf8, 0x69, 0x65, 0x4e, 0x7f, 0x9b, 0x5c, 0x85, 0xc1, 0x76, 0x71, - 0x95, 0xd8, 0xf3, 0x1a, 0x57, 0x36, 0x55, 0x9c, 0xcd, 0xa8, 0xc4, 0x4d, 0x0e, 0x44, 0x89, 0x23, - 0xe7, 0xa1, 0xd0, 0xb0, 0x69, 0xd7, 0x73, 0x69, 0xa3, 0x3c, 0x13, 0x46, 0x2e, 0x56, 0x15, 0x0c, - 0x0d, 0x96, 0x7c, 0x01, 0x26, 0x1d, 0x61, 0xca, 0x95, 0x8f, 0x89, 0x81, 0xc9, 0x66, 0x5f, 0x4a, - 0x6b, 0xb0, 0x0a, 0x7c, 0xaf, 0x91, 0xbf, 0x51, 0xb1, 0x25, 0x75, 0x98, 0xf2, 0xfa, 0x81, 0x90, - 0x30, 0x2b, 0x24, 0x64, 0x0b, 0xdc, 0x6d, 0x48, 0x1e, 0x32, 0xcb, 0x4f, 0x7d, 0xa0, 0xe6, 0xcc, - 0xdb, 0x5b, 0x6f, 0x3b, 0x9d, 0x86, 0x4f, 0xdd, 0xf2, 0x9c, 0xb0, 0x59, 0x45, 0x7b, 0x57, 0x14, - 0x0c, 0x0d, 0x96, 0xfc, 0x3f, 0x98, 0xf1, 0xfa, 0x81, 0x58, 0x25, 0x7c, 0x94, 0x59, 0x79, 0x5e, - 0x90, 0xcf, 0xf3, 0x35, 0xbb, 0x11, 0x45, 0x60, 0x9c, 0x8e, 0xeb, 0xcd, 0xb6, 0xc7, 0x02, 0xfe, - 0x21, 0x54, 0xc7, 0xa9, 0xb8, 0xde, 0xbc, 0x1c, 0xc1, 0x61, 0x8c, 0x92, 0x7c, 0x33, 0x07, 0xf3, - 0xdd, 0xa4, 0x09, 0x56, 0x3e, 0x2d, 0x3a, 0x63, 0x2d, 0xe3, 0x66, 0x9f, 0xe0, 0x26, 0x8f, 0x58, - 0x06, 0xc0, 0x38, 0x28, 0x77, 0x61, 0x15, 0x4e, 0xa5, 0xcf, 0xe9, 0x07, 0x6d, 0xe3, 0xe3, 0xd1, - 0x6d, 0xfc, 0x18, 0x4c, 0x47, 0x13, 0x85, 0x45, 0xc4, 0x32, 0x92, 0x5f, 0x46, 0x3c, 0x28, 0x7a, - 0xb5, 0xa3, 0x88, 0x58, 0x6e, 0xd4, 0x06, 0x22, 0x96, 0x06, 0x84, 0xa1, 0x8c, 0x07, 0x45, 0x2c, - 0xff, 0x62, 0x0c, 0xc2, 0x72, 0xdc, 0x65, 0xa5, 0x6e, 0xa3, 0xe7, 0x39, 0x6e, 0x90, 0x4c, 0x41, - 0xba, 0xa4, 0xe0, 0x68, 0x28, 0x22, 0xf1, 0xcd, 0xb1, 0xfb, 0xc6, 0x37, 0xdb, 0x30, 0x6b, 0x8b, - 0xc3, 0xc9, 0x30, 0x30, 0x35, 0xfe, 0x50, 0x81, 0x29, 0x93, 0x28, 0x16, 0xe7, 0x82, 0x49, 0xb6, - 0x5c, 0x12, 0x0b, 0x8b, 0x0b, 0x49, 0x13, 0x99, 0x24, 0xd5, 0xe2, 0x5c, 0x30, 0xc9, 0xd6, 0xfa, - 0xab, 0x31, 0xd0, 0xab, 0xed, 0x37, 0xc1, 0xd7, 0x23, 0x16, 0x4c, 0xfa, 0x94, 0xf5, 0x3b, 0x81, - 0xb2, 0xbe, 0x84, 0x46, 0x43, 0x01, 0x41, 0x85, 0xe1, 0xca, 0x86, 0xbe, 0xe7, 0x04, 0x2b, 0x5e, - 0x43, 0xdb, 0x5c, 0x42, 0xd9, 0x5c, 0x52, 0x30, 0x34, 0x58, 0x6b, 0x17, 0x66, 0x78, 0xbb, 0x3a, - 0x1d, 0xda, 0xa9, 0x05, 0xb4, 0xc7, 0x48, 0x13, 0xf2, 0x8c, 0xff, 0x50, 0xbd, 0x37, 0x62, 0x5a, - 0x42, 0x40, 0x7b, 0x11, 0xa7, 0x90, 0xf3, 0x45, 0xc9, 0xde, 0xba, 0x37, 0x06, 0x45, 0xd3, 0xa3, - 0x87, 0xf0, 0x34, 0x6f, 0xc3, 0x54, 0x83, 0x36, 0x6d, 0xde, 0xee, 0xb1, 0x07, 0x9c, 0x59, 0xf7, - 0x03, 0xa7, 0xb3, 0x28, 0xef, 0x46, 0x2c, 0x5e, 0x71, 0x83, 0x0d, 0xbf, 0x16, 0xf8, 0x8e, 0xdb, - 0x92, 0x8a, 0x79, 0x55, 0x32, 0x41, 0xcd, 0x8d, 0xbc, 0x19, 0x0d, 0x72, 0x64, 0x61, 0x3b, 0x70, - 0xab, 0x82, 0xec, 0x40, 0x51, 0xfc, 0x58, 0xd3, 0x79, 0xf2, 0x59, 0x67, 0xe1, 0x2d, 0xcd, 0x45, - 0xc6, 0x07, 0xcd, 0x27, 0x86, 0xfc, 0x13, 0xf9, 0xed, 0xf9, 0xc3, 0xe4, 0xb7, 0x5b, 0x6b, 0xc0, - 0x37, 0xe3, 0xf5, 0x15, 0xf2, 0x0a, 0x14, 0x98, 0x52, 0x90, 0xaa, 0xef, 0x3f, 0x62, 0xd2, 0x3f, - 0x14, 0xfc, 0xde, 0x7e, 0x65, 0x46, 0x10, 0x6b, 0x00, 0x9a, 0x22, 0xd6, 0xd7, 0x26, 0x20, 0xe2, - 0x76, 0x1c, 0x62, 0x14, 0x1b, 0x09, 0x4f, 0xf2, 0xf5, 0xac, 0x9e, 0xa4, 0x76, 0xcf, 0xe4, 0xf4, - 0x8f, 0x3b, 0x8f, 0xbc, 0x1e, 0x6d, 0xda, 0xe9, 0xa9, 0x05, 0x62, 0xea, 0x71, 0x99, 0x76, 0x7a, - 0x28, 0x30, 0xe6, 0x1c, 0x73, 0x62, 0xe8, 0x39, 0xe6, 0x5b, 0x90, 0x6f, 0xd9, 0xfd, 0x16, 0x55, - 0x81, 0xbf, 0x6c, 0x5e, 0xbe, 0x38, 0xc9, 0x91, 0x13, 0x44, 0xfc, 0x44, 0xc9, 0x93, 0x4f, 0x90, - 0xb6, 0x8e, 0xb2, 0x29, 0x2f, 0x25, 0xdb, 0x04, 0x31, 0xb1, 0x3a, 0x39, 0x41, 0xcc, 0x27, 0x86, - 0xfc, 0xb9, 0x79, 0x53, 0x97, 0xd9, 0x6d, 0xea, 0x14, 0xe2, 0xb3, 0x19, 0x8f, 0x63, 0x05, 0x0f, - 0xb9, 0x8a, 0xd4, 0x07, 0x6a, 0xce, 0xd6, 0x12, 0x94, 0x22, 0x99, 0xe1, 0xbc, 0x7f, 0x4d, 0xee, - 0x56, 0xa4, 0x7f, 0x57, 0xed, 0xc0, 0x46, 0x81, 0xb1, 0xbe, 0x37, 0x0e, 0xc6, 0x98, 0x8c, 0x1e, - 0xb4, 0xda, 0xf5, 0x48, 0xda, 0x6d, 0x2c, 0xeb, 0xc3, 0x73, 0x51, 0x61, 0xb9, 0x6b, 0xd3, 0xa5, - 0x7e, 0xcb, 0x6c, 0xee, 0x6a, 0xdb, 0x33, 0xae, 0xcd, 0xf5, 0x28, 0x12, 0xe3, 0xb4, 0x7c, 0x6b, - 0xed, 0xda, 0xae, 0xd3, 0xa4, 0x2c, 0x48, 0x06, 0xd4, 0xaf, 0x2b, 0x38, 0x1a, 0x0a, 0xb2, 0x0e, - 0xf3, 0x8c, 0x06, 0x1b, 0xbb, 0x2e, 0xf5, 0x4d, 0x36, 0x8a, 0x4a, 0x4f, 0x7a, 0x52, 0x5b, 0xd8, - 0xb5, 0x24, 0x01, 0x0e, 0x96, 0x11, 0x6e, 0xa2, 0xcc, 0x0c, 0x32, 0x59, 0x1e, 0x6a, 0xb5, 0x86, - 0x6e, 0x62, 0x02, 0x8f, 0x03, 0x25, 0x38, 0x97, 0xa6, 0xed, 0x74, 0xfa, 0x3e, 0x0d, 0xb9, 0x4c, - 0xc6, 0xb9, 0xac, 0x25, 0xf0, 0x38, 0x50, 0x42, 0x9c, 0xc5, 0x75, 0xec, 0x16, 0x2b, 0x4f, 0x45, - 0xce, 0xe2, 0x38, 0x00, 0x25, 0xdc, 0xfa, 0x97, 0x1c, 0xcc, 0x20, 0x0d, 0xfc, 0x3d, 0xd3, 0x6b, - 0x15, 0xc8, 0x77, 0x44, 0xa6, 0x52, 0x4e, 0x64, 0x2a, 0x89, 0x22, 0x32, 0x31, 0x49, 0xc2, 0xc9, - 0x2a, 0x94, 0x7c, 0x5e, 0x42, 0x65, 0x85, 0xc9, 0x11, 0xb1, 0x74, 0x68, 0x00, 0x43, 0xd4, 0xbd, - 0xf8, 0x27, 0x46, 0x8b, 0x11, 0x17, 0xa6, 0xb6, 0x65, 0x16, 0xb6, 0xd2, 0xc7, 0xd9, 0x26, 0xab, - 0xca, 0xe4, 0x16, 0x51, 0x78, 0x9d, 0xd6, 0x7d, 0x2f, 0xfc, 0x89, 0x5a, 0x88, 0xf5, 0xdd, 0x1c, - 0x40, 0x78, 0x91, 0x85, 0xec, 0x40, 0x81, 0x5d, 0x8c, 0xd9, 0x84, 0x19, 0x13, 0x2a, 0x14, 0x93, - 0x48, 0x36, 0x9d, 0x82, 0xa0, 0x11, 0xf0, 0x20, 0x83, 0xf0, 0xe7, 0xe3, 0x60, 0x4a, 0x3d, 0x22, - 0x7b, 0xf0, 0x39, 0x6e, 0x4b, 0xb4, 0xc2, 0x6c, 0x74, 0x43, 0x87, 0x02, 0x8a, 0x0a, 0xcb, 0xed, - 0x09, 0x7d, 0x4c, 0xa8, 0xe6, 0xbe, 0xb0, 0x27, 0xf4, 0x89, 0x22, 0x1a, 0x6c, 0x9a, 0x85, 0x99, - 0x7f, 0x6c, 0x16, 0xe6, 0xe4, 0x23, 0xb1, 0x30, 0xc9, 0xf3, 0x30, 0xe5, 0x7b, 0x1d, 0xba, 0x8c, - 0x37, 0x54, 0x34, 0xc4, 0x44, 0x29, 0x50, 0x82, 0x51, 0xe3, 0xc9, 0x8b, 0x50, 0xea, 0x33, 0x5a, - 0x5b, 0xbd, 0xba, 0xe2, 0xd3, 0x06, 0x53, 0x27, 0xb0, 0x26, 0x3e, 0x76, 0x33, 0x44, 0x61, 0x94, - 0xce, 0xfa, 0x83, 0x1c, 0x1c, 0xab, 0xd5, 0x7d, 0xa7, 0x17, 0x18, 0x55, 0x78, 0x43, 0x64, 0xfb, - 0x07, 0x36, 0xb7, 0x3f, 0xd4, 0x54, 0x7c, 0x7a, 0xc8, 0xe1, 0x93, 0x24, 0x8a, 0xdd, 0x4c, 0x91, - 0x20, 0x0c, 0x59, 0xf0, 0xa1, 0x96, 0xca, 0x36, 0x39, 0x25, 0x6a, 0x02, 0x8a, 0x0a, 0x6b, 0xdd, - 0x81, 0xb9, 0x1a, 0xed, 0xda, 0xbd, 0xb6, 0x38, 0x0c, 0xee, 0x34, 0x1c, 0xb7, 0x45, 0x96, 0xa0, - 0xc8, 0x34, 0x2c, 0x79, 0x0d, 0xc6, 0x10, 0x63, 0x48, 0x43, 0x9e, 0x85, 0xa9, 0xb6, 0x38, 0xab, - 0xd1, 0xc7, 0x9b, 0x62, 0xd3, 0x90, 0xc7, 0x37, 0x0c, 0x35, 0xce, 0xda, 0x85, 0xe9, 0xb0, 0x38, - 0x6d, 0x92, 0x16, 0xcc, 0xd6, 0x23, 0x67, 0x69, 0x48, 0x9b, 0x0f, 0x7d, 0xcf, 0x41, 0x9c, 0x23, - 0xae, 0xc4, 0x99, 0x60, 0x92, 0xab, 0xf5, 0xab, 0x1c, 0xcc, 0x1a, 0xc9, 0x2a, 0x30, 0xda, 0x93, - 0x75, 0x76, 0xdc, 0x96, 0x32, 0x7d, 0x2f, 0x65, 0x4c, 0xa5, 0x8a, 0x77, 0x5e, 0x38, 0x59, 0x14, - 0x00, 0xb5, 0x18, 0x2e, 0x71, 0xd7, 0x76, 0x02, 0x2e, 0x71, 0xec, 0x91, 0x48, 0xbc, 0x2d, 0xb9, - 0xa3, 0x16, 0x63, 0xfd, 0xe9, 0x18, 0x14, 0x4c, 0x2e, 0xd7, 0x9b, 0x90, 0x17, 0xbb, 0xf7, 0x03, - 0x33, 0xd6, 0xef, 0x6b, 0xf8, 0x0a, 0x4b, 0x00, 0x25, 0x27, 0xce, 0x52, 0x04, 0xd5, 0x32, 0x9b, - 0xe8, 0x45, 0xe9, 0x27, 0xd8, 0x7e, 0x80, 0x92, 0x13, 0xb9, 0x0a, 0xe3, 0xd4, 0x6d, 0x64, 0x36, - 0xce, 0xc5, 0x45, 0xaf, 0x4b, 0x6e, 0x03, 0x39, 0x17, 0x71, 0x21, 0xc0, 0xf3, 0xbb, 0x76, 0xa0, - 0x0c, 0xbf, 0xf0, 0x42, 0x80, 0x80, 0xa2, 0xc2, 0x5a, 0x7f, 0x33, 0x01, 0x50, 0xeb, 0x6f, 0x77, - 0x9d, 0x60, 0xa3, 0x17, 0x1c, 0xc6, 0xae, 0x7d, 0x09, 0xa6, 0xf5, 0x2d, 0xec, 0x1b, 0x61, 0x04, - 0xdd, 0x84, 0x5e, 0xd6, 0x23, 0x38, 0x8c, 0x51, 0x72, 0xf3, 0x9d, 0xba, 0x81, 0xbf, 0x27, 0x55, - 0xfb, 0x44, 0xdc, 0x7c, 0xbf, 0x64, 0x30, 0x18, 0xa1, 0x22, 0x8b, 0x31, 0x37, 0x57, 0xe6, 0x32, - 0x1e, 0xbb, 0x8f, 0x8b, 0xfa, 0x32, 0xcc, 0x98, 0xaf, 0x35, 0xa7, 0xa3, 0x4f, 0x95, 0x8d, 0xb9, - 0xb4, 0x19, 0x45, 0x62, 0x9c, 0x96, 0xbc, 0x0a, 0xc7, 0xe2, 0xb9, 0x4a, 0x4a, 0x09, 0x9e, 0x52, - 0xa5, 0x8f, 0xc5, 0x53, 0x9c, 0x30, 0x41, 0xcd, 0xfb, 0xbc, 0xe1, 0xef, 0x61, 0xdf, 0x55, 0xda, - 0xd0, 0xf4, 0xf9, 0xaa, 0x80, 0xa2, 0xc2, 0xf2, 0x2e, 0xe4, 0x25, 0xa9, 0x2f, 0xe1, 0x22, 0x20, - 0x5c, 0x08, 0xbb, 0xb0, 0x16, 0xc1, 0x61, 0x8c, 0x92, 0x4b, 0x50, 0x4e, 0x05, 0xc4, 0x47, 0x35, - 0xe1, 0x16, 0xf4, 0xe0, 0x98, 0x17, 0xb7, 0xe3, 0x64, 0xa4, 0xf7, 0x53, 0x87, 0xcc, 0x7e, 0x8e, - 0x95, 0x95, 0xc9, 0x40, 0x09, 0xb3, 0x2f, 0xc1, 0xdf, 0x3a, 0x0e, 0xf3, 0xb5, 0x7e, 0xaf, 0xd7, - 0x71, 0x68, 0xc3, 0xf8, 0x6e, 0xd6, 0x6b, 0x30, 0xab, 0xf2, 0xd9, 0x8d, 0xb2, 0x7f, 0xa8, 0x0b, - 0x67, 0xd6, 0x97, 0xb9, 0xf6, 0xda, 0x73, 0xeb, 0x6d, 0xdf, 0x73, 0x55, 0xe4, 0x8c, 0xb8, 0x49, - 0x15, 0x9d, 0xd5, 0x75, 0x8f, 0x2a, 0x64, 0xe9, 0x54, 0xa4, 0x69, 0x78, 0xeb, 0x1b, 0x39, 0x38, - 0x99, 0xa8, 0x83, 0xd2, 0xa3, 0xef, 0x0e, 0xd6, 0x64, 0x75, 0xb4, 0x9a, 0xa8, 0x00, 0xe2, 0xf0, - 0xca, 0xfc, 0x47, 0x0e, 0x4a, 0x5b, 0x5b, 0xd7, 0x8c, 0xad, 0x8a, 0x70, 0x8a, 0xc9, 0x8c, 0xfe, - 0xe5, 0x66, 0x40, 0xfd, 0x15, 0xaf, 0xdb, 0xeb, 0x50, 0xd3, 0xb9, 0x2a, 0xcd, 0xbe, 0x96, 0x4a, - 0x81, 0x43, 0x4a, 0x92, 0x2b, 0x70, 0x3c, 0x8a, 0x51, 0xa6, 0xba, 0xba, 0xf2, 0x27, 0x13, 0xb6, - 0x06, 0xd1, 0x98, 0x56, 0x26, 0xc9, 0x4a, 0xd9, 0xeb, 0xea, 0xba, 0xfd, 0x00, 0x2b, 0x85, 0xc6, - 0xb4, 0x32, 0xd6, 0x06, 0x94, 0x22, 0x6f, 0x2d, 0x90, 0xd7, 0x61, 0xae, 0xee, 0x75, 0x7b, 0x3e, - 0x65, 0xcc, 0xf1, 0xdc, 0x6b, 0xf4, 0x2e, 0xed, 0xa8, 0x26, 0x8b, 0x7c, 0xfd, 0x95, 0x04, 0x0e, - 0x07, 0xa8, 0xad, 0xef, 0x3f, 0x05, 0x26, 0x4b, 0xfc, 0xb7, 0xb9, 0xe6, 0x99, 0xce, 0x03, 0xeb, - 0xe6, 0xbc, 0x22, 0x3f, 0xfa, 0x79, 0x85, 0xd1, 0x65, 0x89, 0x33, 0x8b, 0x56, 0x78, 0x66, 0x31, - 0x79, 0x04, 0x67, 0x16, 0xc6, 0x64, 0x18, 0x38, 0xb7, 0xf8, 0x7a, 0x0e, 0xa6, 0x5d, 0xaf, 0x41, - 0xb5, 0x85, 0x25, 0x5c, 0xc6, 0xd2, 0x85, 0x8d, 0x91, 0x3a, 0x51, 0x1e, 0x5f, 0x29, 0x8e, 0xf2, - 0xb8, 0xca, 0x28, 0xfa, 0x28, 0x0a, 0x63, 0xa2, 0xc9, 0x1a, 0x14, 0xec, 0x66, 0xd3, 0x71, 0x9d, - 0x60, 0x4f, 0xa5, 0xbb, 0x9f, 0x49, 0x33, 0x0c, 0x97, 0x15, 0x8d, 0x74, 0x52, 0xf4, 0x17, 0x9a, - 0xb2, 0xdc, 0xcb, 0x33, 0xb7, 0xcb, 0x8a, 0x23, 0x78, 0x79, 0x3a, 0xa3, 0x21, 0x12, 0x40, 0xd0, - 0x37, 0x61, 0xc2, 0xcb, 0x66, 0x16, 0x4c, 0xca, 0xa3, 0x2c, 0xb1, 0x3b, 0x15, 0x64, 0xc0, 0x4a, - 0x1e, 0x73, 0xa1, 0xc2, 0x90, 0x96, 0x0e, 0xba, 0x96, 0x44, 0xe7, 0x56, 0x33, 0x87, 0xac, 0x4d, - 0x1c, 0x37, 0x3d, 0xea, 0x4a, 0xde, 0x88, 0x7a, 0x15, 0xd3, 0x87, 0xf1, 0x2a, 0x66, 0x86, 0x7a, - 0x14, 0x2d, 0x98, 0x64, 0xc2, 0x67, 0x11, 0xe7, 0x77, 0xa5, 0x0b, 0x2b, 0xd9, 0xb4, 0x7c, 0xcc, - 0xed, 0x91, 0xbd, 0x23, 0x61, 0xa8, 0xd8, 0x13, 0x0f, 0x0a, 0xfa, 0x90, 0x51, 0x1d, 0x01, 0x66, - 0x33, 0x94, 0x93, 0xe1, 0x26, 0x9d, 0x2b, 0x2d, 0xa1, 0x68, 0x84, 0x90, 0xb7, 0x60, 0xbc, 0x61, - 0xb7, 0xd4, 0x61, 0xe0, 0xeb, 0x99, 0xb3, 0xf8, 0xb5, 0x18, 0x61, 0x83, 0xae, 0x2e, 0xaf, 0x23, - 0xe7, 0x4a, 0x76, 0xc2, 0x5b, 0x6e, 0x73, 0xa3, 0xec, 0x8e, 0x71, 0x13, 0x42, 0x7a, 0x58, 0x03, - 0xf7, 0xe4, 0x2e, 0xc1, 0xd4, 0x5d, 0xaf, 0xd3, 0xef, 0xaa, 0x53, 0xc4, 0xd2, 0x85, 0x85, 0xb4, - 0xd1, 0xbe, 0x25, 0x48, 0x42, 0x25, 0x20, 0xbf, 0x19, 0xea, 0xb2, 0xe4, 0x2b, 0x39, 0x38, 0xc6, - 0x97, 0x8e, 0x99, 0x07, 0xac, 0x4c, 0x46, 0x98, 0xa9, 0x37, 0x19, 0xdf, 0x5a, 0xf5, 0x0c, 0x33, - 0x86, 0xe4, 0x95, 0x98, 0x04, 0x4c, 0x48, 0x24, 0x3d, 0x28, 0x30, 0xa7, 0x41, 0xeb, 0xb6, 0xcf, - 0xca, 0xc7, 0x8f, 0x4c, 0x7a, 0x18, 0xa0, 0x51, 0xbc, 0xd1, 0x48, 0x21, 0xbf, 0x2f, 0xde, 0x5d, - 0x50, 0x6f, 0xa6, 0xa8, 0x77, 0x6c, 0x4e, 0x1c, 0xe5, 0x3b, 0x36, 0xc7, 0xe5, 0xa3, 0x0b, 0x31, - 0x09, 0x98, 0x14, 0x49, 0x36, 0xe0, 0xa4, 0xbc, 0xed, 0x96, 0xbc, 0xea, 0x78, 0x52, 0x5c, 0x75, - 0x7c, 0xf2, 0x60, 0xbf, 0x72, 0x72, 0x39, 0x8d, 0x00, 0xd3, 0xcb, 0x91, 0xf7, 0x61, 0xc6, 0x8f, - 0x06, 0xf7, 0xc4, 0x49, 0x71, 0xd6, 0xee, 0x8c, 0x85, 0x09, 0xe5, 0x29, 0x75, 0x0c, 0x84, 0x71, - 0x59, 0xe4, 0x05, 0x28, 0xf5, 0x94, 0xa6, 0x72, 0x58, 0x57, 0x1c, 0x32, 0x8f, 0xcb, 0x1d, 0x75, - 0x33, 0x04, 0x63, 0x94, 0x86, 0xdc, 0x84, 0x52, 0xe0, 0x75, 0xa8, 0xaf, 0xd2, 0xe0, 0xca, 0x62, - 0xf0, 0xcf, 0xa6, 0xcd, 0xe4, 0x2d, 0x43, 0x16, 0x46, 0x5d, 0x42, 0x18, 0xc3, 0x28, 0x1f, 0xee, - 0x16, 0xe9, 0x9b, 0xad, 0x22, 0xa9, 0xb4, 0xfc, 0x64, 0xdc, 0x2d, 0xaa, 0x45, 0x91, 0x18, 0xa7, - 0x25, 0xeb, 0x30, 0xdf, 0xf3, 0x1d, 0xcf, 0x77, 0x82, 0xbd, 0x95, 0x8e, 0xcd, 0x98, 0x60, 0xb0, - 0x20, 0x18, 0x98, 0xb8, 0xf0, 0x66, 0x92, 0x00, 0x07, 0xcb, 0x90, 0xf3, 0x50, 0xd0, 0xc0, 0xf2, - 0x53, 0xc2, 0x56, 0x13, 0x6a, 0x49, 0x97, 0x45, 0x83, 0x1d, 0x72, 0x7f, 0xe7, 0x4c, 0x96, 0xfb, - 0x3b, 0xa4, 0x01, 0x67, 0xec, 0x7e, 0xe0, 0x89, 0xa4, 0xdb, 0x78, 0x91, 0x2d, 0x6f, 0x87, 0xba, - 0xe5, 0x73, 0x62, 0xaf, 0x3a, 0x77, 0xb0, 0x5f, 0x39, 0xb3, 0x7c, 0x1f, 0x3a, 0xbc, 0x2f, 0x17, - 0xd2, 0x85, 0x02, 0x55, 0x77, 0x90, 0xca, 0x1f, 0x19, 0x61, 0x93, 0x88, 0x5f, 0x64, 0xd2, 0x87, - 0x99, 0x12, 0x86, 0x46, 0x04, 0xd9, 0x82, 0x52, 0xdb, 0x63, 0xc1, 0x72, 0xc7, 0xb1, 0x19, 0x65, - 0xe5, 0xa7, 0xc5, 0x3c, 0x49, 0xdd, 0xdf, 0x2e, 0x6b, 0xb2, 0x70, 0x9a, 0x5c, 0x0e, 0x4b, 0x62, - 0x94, 0x0d, 0xa1, 0x22, 0xd0, 0xd8, 0x17, 0xa3, 0xe6, 0xb9, 0x01, 0x7d, 0x2f, 0x28, 0x9f, 0x15, - 0x6d, 0x79, 0x2e, 0x8d, 0xf3, 0xa6, 0xd7, 0xa8, 0xc5, 0xa9, 0xe5, 0x2a, 0x4f, 0x00, 0x31, 0xc9, - 0x93, 0xfb, 0xbf, 0x3d, 0xaf, 0x51, 0xeb, 0xd1, 0xfa, 0xa6, 0x1d, 0xd4, 0xdb, 0xe5, 0x4a, 0x3c, - 0x84, 0xb0, 0x19, 0xc1, 0x61, 0x8c, 0x92, 0xfb, 0x13, 0x3e, 0x65, 0x22, 0x5c, 0xb1, 0x49, 0xdd, - 0x86, 0xe3, 0xb6, 0x36, 0xbd, 0x06, 0x2b, 0x5b, 0x62, 0x08, 0x85, 0x3f, 0x81, 0x83, 0x68, 0x4c, - 0x2b, 0x43, 0xea, 0x30, 0xd5, 0x95, 0x89, 0x8a, 0xe5, 0x67, 0x46, 0x30, 0x2b, 0x55, 0xb2, 0xa3, - 0xdc, 0x94, 0xd4, 0x07, 0x6a, 0xce, 0xe4, 0x3b, 0x39, 0x98, 0x65, 0x71, 0xdf, 0xb1, 0xfc, 0xd1, - 0x51, 0xb6, 0xc2, 0x38, 0xaf, 0xea, 0x73, 0xa2, 0xbf, 0xe3, 0xc0, 0x7b, 0x83, 0x20, 0x4c, 0x56, - 0x42, 0xb6, 0x5e, 0xe4, 0x00, 0x97, 0x9f, 0x1d, 0xa9, 0xf5, 0x82, 0x87, 0x6e, 0xbd, 0xf8, 0x40, - 0xcd, 0x79, 0xe1, 0x35, 0x98, 0x1f, 0xb0, 0x7e, 0x1f, 0x2a, 0x3f, 0xf5, 0x67, 0xdc, 0xdb, 0x8d, - 0xf8, 0x1b, 0x47, 0xed, 0xa5, 0xad, 0xc3, 0xbc, 0x7a, 0x95, 0x90, 0x9b, 0x46, 0x9d, 0xbe, 0x79, - 0xad, 0x26, 0x72, 0xe2, 0x85, 0x49, 0x02, 0x1c, 0x2c, 0xc3, 0x67, 0x74, 0x5d, 0x3e, 0x57, 0x22, - 0xf3, 0x0f, 0x27, 0xe2, 0x11, 0x9d, 0x95, 0x08, 0x0e, 0x63, 0x94, 0xd6, 0x9f, 0xe5, 0x60, 0x26, - 0xb6, 0x4d, 0x1f, 0x79, 0x38, 0x7c, 0x0d, 0x48, 0xd7, 0xf1, 0x7d, 0xcf, 0x97, 0xb6, 0xce, 0x75, - 0xae, 0xb3, 0x98, 0xba, 0x42, 0x27, 0xae, 0x6e, 0x5c, 0x1f, 0xc0, 0x62, 0x4a, 0x09, 0xeb, 0xab, - 0xe3, 0x10, 0x1e, 0xcb, 0x9b, 0xfb, 0x4a, 0xb9, 0xa1, 0xf7, 0x95, 0x3e, 0x0e, 0x85, 0x3b, 0xcc, - 0x73, 0x37, 0xc3, 0x5b, 0x4d, 0x66, 0x28, 0xde, 0xa8, 0x6d, 0xdc, 0x10, 0x94, 0x86, 0x42, 0x50, - 0xbf, 0xbb, 0xe6, 0x74, 0x82, 0xc1, 0xbb, 0x3f, 0x6f, 0xbc, 0x29, 0xe1, 0x68, 0x28, 0xc8, 0x12, - 0x14, 0x4d, 0xe8, 0x4e, 0x45, 0x12, 0x4d, 0x27, 0x98, 0x10, 0x1f, 0x86, 0x34, 0xc2, 0xa2, 0x52, - 0xe1, 0x29, 0xe5, 0x45, 0xae, 0x65, 0xb4, 0x45, 0x13, 0x31, 0x2e, 0xa9, 0xa1, 0x35, 0x18, 0x8d, - 0x94, 0x68, 0x16, 0x47, 0xfe, 0x28, 0xb3, 0x38, 0xf8, 0x38, 0x4c, 0xdd, 0xa2, 0xbe, 0xb8, 0x8a, - 0xf8, 0x3c, 0x4c, 0xdd, 0x95, 0x3f, 0xd5, 0x40, 0x84, 0x86, 0xad, 0x04, 0xa3, 0xc6, 0xf3, 0x2e, - 0xdb, 0xee, 0x3b, 0x9d, 0xc6, 0x6a, 0xb8, 0x34, 0x4c, 0x97, 0x55, 0x35, 0x02, 0x43, 0x1a, 0x5e, - 0xa0, 0xc5, 0xad, 0xd2, 0x6e, 0xd7, 0x09, 0x92, 0x17, 0x0b, 0xd6, 0x35, 0x02, 0x43, 0x1a, 0xf2, - 0x1c, 0x4c, 0xb6, 0x9c, 0x60, 0xcb, 0x6e, 0x25, 0x43, 0xce, 0xeb, 0x02, 0x8a, 0x0a, 0x2b, 0x22, - 0xc8, 0x4e, 0xb0, 0xe5, 0x53, 0x11, 0xee, 0x1a, 0x48, 0x7a, 0x5e, 0x8f, 0xe0, 0x30, 0x46, 0x29, - 0xaa, 0xe4, 0xa9, 0x96, 0xa9, 0xc8, 0x6e, 0x58, 0x25, 0x8d, 0xc0, 0x90, 0x86, 0xcf, 0xaa, 0xba, - 0xd7, 0xed, 0x39, 0x1d, 0x95, 0x11, 0x10, 0x99, 0x55, 0x2b, 0x0a, 0x8e, 0x86, 0x82, 0x53, 0x73, - 0xbd, 0xd0, 0xf4, 0xfc, 0x6e, 0xf2, 0x7d, 0x89, 0x4d, 0x05, 0x47, 0x43, 0x61, 0x7d, 0x09, 0x66, - 0xd4, 0xa9, 0x83, 0x0a, 0x07, 0xee, 0x84, 0x47, 0x41, 0xa3, 0xbc, 0x28, 0x1a, 0xb9, 0xfa, 0x13, - 0x3f, 0x51, 0x89, 0x1d, 0x28, 0xfd, 0x60, 0x0c, 0x0a, 0x8f, 0xf1, 0x51, 0x9e, 0x7a, 0xec, 0x51, - 0x9e, 0x23, 0x78, 0xc1, 0x25, 0xed, 0x41, 0x9e, 0x9d, 0xc4, 0x83, 0x3c, 0x2b, 0x23, 0x66, 0x64, - 0xdd, 0xf7, 0x31, 0x9e, 0x1f, 0xe7, 0x60, 0xfa, 0x31, 0x3e, 0xc4, 0xb3, 0x1d, 0x7f, 0x88, 0xe7, - 0x95, 0x91, 0x9a, 0x36, 0xe4, 0x11, 0x9e, 0xaf, 0x3f, 0x09, 0xb1, 0x07, 0x70, 0x88, 0x0b, 0x45, - 0xbd, 0xd7, 0xe9, 0x2c, 0xb7, 0x57, 0x46, 0x8a, 0x66, 0x85, 0xcb, 0x4d, 0x43, 0x18, 0x86, 0x22, - 0x12, 0x27, 0x3c, 0x63, 0x87, 0x3a, 0xe1, 0x79, 0xec, 0x91, 0xd2, 0x74, 0xdf, 0x62, 0xe2, 0x91, - 0xf8, 0x16, 0x67, 0x8e, 0xdc, 0xb7, 0x78, 0xfa, 0xd1, 0xfb, 0x16, 0x91, 0x48, 0x4a, 0x7e, 0x84, - 0x48, 0xca, 0xfb, 0x70, 0x42, 0xfe, 0x5c, 0xe9, 0xd8, 0x4e, 0xd7, 0xcc, 0x17, 0xf5, 0xba, 0xc8, - 0xf3, 0xa9, 0x1e, 0x05, 0x57, 0xdb, 0x2c, 0xa0, 0x6e, 0x70, 0x2b, 0x2c, 0x19, 0xde, 0x08, 0xb9, - 0x95, 0xc2, 0x0e, 0x53, 0x85, 0x24, 0x5d, 0xef, 0xa9, 0x43, 0xb8, 0xde, 0xdf, 0xcb, 0xc1, 0x49, - 0x3b, 0xed, 0x6d, 0x4c, 0x15, 0x80, 0x7d, 0x63, 0xa4, 0x40, 0x48, 0x8c, 0xa3, 0x0a, 0x64, 0xa4, - 0xa1, 0x30, 0xbd, 0x0e, 0xe4, 0xd9, 0x30, 0x96, 0x26, 0x8f, 0x0b, 0xd3, 0xa3, 0x60, 0xdf, 0x4a, - 0xc6, 0xb0, 0x41, 0xf4, 0x76, 0x6d, 0x64, 0x85, 0x7d, 0x04, 0x71, 0xec, 0xd2, 0x08, 0x71, 0xec, - 0x44, 0x5c, 0x64, 0xfa, 0x88, 0xe2, 0x22, 0x2e, 0xcc, 0x39, 0x5d, 0xbb, 0x45, 0x37, 0xfb, 0x9d, - 0x8e, 0x4c, 0x81, 0x61, 0xe5, 0x19, 0xc1, 0x3b, 0x35, 0x0f, 0xe3, 0x9a, 0x57, 0xb7, 0x3b, 0xc9, - 0xf7, 0x9a, 0x4c, 0x36, 0xda, 0x95, 0x04, 0x27, 0x1c, 0xe0, 0xcd, 0xa7, 0xa5, 0xb8, 0x8d, 0x40, - 0x03, 0xde, 0xdb, 0x22, 0xc4, 0xab, 0x5e, 0x2f, 0xbe, 0x1c, 0x82, 0x31, 0x4a, 0x43, 0xae, 0x42, - 0xb1, 0xe1, 0x32, 0x95, 0x6a, 0x36, 0x2b, 0xb4, 0xd4, 0x27, 0xb8, 0x6e, 0x5b, 0xbd, 0x51, 0x33, - 0x49, 0x66, 0x67, 0x52, 0xae, 0xb3, 0x18, 0x3c, 0x86, 0xe5, 0xc9, 0x75, 0xc1, 0x4c, 0xbd, 0x05, - 0x20, 0x63, 0xb2, 0xe7, 0x86, 0xb8, 0xf6, 0xab, 0x37, 0xf4, 0xd3, 0x05, 0x33, 0x4a, 0x9c, 0xba, - 0xe1, 0x1f, 0x72, 0x88, 0x3c, 0x48, 0x33, 0x7f, 0xdf, 0x07, 0x69, 0x6e, 0xc2, 0xe9, 0x20, 0xe8, - 0xc4, 0x8e, 0xfa, 0xd4, 0x8d, 0x21, 0x71, 0x7d, 0x2c, 0x2f, 0xdf, 0x30, 0xdb, 0xda, 0xba, 0x96, - 0x46, 0x82, 0xc3, 0xca, 0x8a, 0x33, 0xaf, 0xa0, 0x63, 0x42, 0x7b, 0x67, 0x47, 0x39, 0xf3, 0x0a, - 0xcf, 0x54, 0xd5, 0x99, 0x57, 0x08, 0xc0, 0xa8, 0x94, 0xe1, 0x21, 0xca, 0xe3, 0x19, 0x43, 0x94, - 0xd1, 0xa8, 0xd8, 0x89, 0xfb, 0x46, 0xc5, 0x06, 0xa2, 0x78, 0x27, 0x1f, 0x22, 0x8a, 0xf7, 0x96, - 0xb8, 0xaa, 0xb4, 0xbe, 0xa2, 0x22, 0xa0, 0xd9, 0xb2, 0x7c, 0x45, 0x76, 0xb4, 0x4c, 0x5d, 0x11, - 0x3f, 0x51, 0xf2, 0x24, 0x9b, 0x70, 0xa2, 0xe7, 0x35, 0x06, 0x82, 0x80, 0x22, 0xe4, 0x19, 0xb9, - 0xd2, 0xb7, 0x99, 0x42, 0x83, 0xa9, 0x25, 0x85, 0x02, 0x0f, 0xe1, 0xe5, 0xb2, 0xe8, 0x18, 0xa9, - 0xc0, 0x43, 0x30, 0x46, 0x69, 0x92, 0x31, 0xb1, 0x27, 0x1f, 0x59, 0x4c, 0x6c, 0xe1, 0x31, 0xc4, - 0xc4, 0x9e, 0x3a, 0x74, 0x4c, 0xec, 0x77, 0xe1, 0x78, 0xcf, 0x6b, 0xac, 0x3a, 0xcc, 0xef, 0x8b, - 0x97, 0xd3, 0xab, 0xfd, 0x46, 0x8b, 0x06, 0x22, 0xa8, 0x56, 0xba, 0x70, 0x21, 0x5a, 0x49, 0xf9, - 0x07, 0x11, 0x8b, 0xea, 0x0f, 0x22, 0xc4, 0x22, 0x4f, 0x94, 0x12, 0xa6, 0xb9, 0x88, 0xa3, 0xa5, - 0x20, 0x31, 0x4d, 0x4e, 0x34, 0x8e, 0x76, 0xee, 0x91, 0xc5, 0xd1, 0x5e, 0x87, 0x02, 0x6b, 0xf7, - 0x83, 0x86, 0xb7, 0xeb, 0x8a, 0xe8, 0x6a, 0xd1, 0xbc, 0x00, 0x59, 0xa8, 0x29, 0xf8, 0xbd, 0xfd, - 0xca, 0x9c, 0xfe, 0x1d, 0xc9, 0xdf, 0x57, 0x10, 0xf2, 0xed, 0x1c, 0x1c, 0x4f, 0xde, 0x1d, 0xe5, - 0x7b, 0xbb, 0x35, 0xc2, 0xb3, 0xe7, 0xb7, 0x07, 0xf9, 0xc9, 0xce, 0x4b, 0x41, 0x60, 0x9a, 0xf4, - 0xd4, 0xf8, 0xe0, 0x33, 0xbf, 0x06, 0xf1, 0xc1, 0xd1, 0x43, 0x77, 0xdf, 0x9e, 0x85, 0x63, 0x89, - 0xd7, 0x10, 0xcd, 0x45, 0xdb, 0xdc, 0x61, 0x2f, 0xda, 0xc6, 0x6e, 0xc2, 0x8e, 0x3d, 0xd2, 0x9b, - 0xb0, 0xe3, 0x47, 0x7e, 0x13, 0x36, 0x72, 0xe3, 0x77, 0xe2, 0x01, 0x37, 0x7e, 0x97, 0x61, 0x56, - 0xa7, 0xa7, 0x50, 0x75, 0x13, 0x52, 0x06, 0x45, 0x4c, 0xe6, 0xee, 0x4a, 0x1c, 0x8d, 0x49, 0x7a, - 0xf2, 0x25, 0xc8, 0xbb, 0xa2, 0xe0, 0xe4, 0x08, 0x2f, 0x27, 0xc4, 0x07, 0x4c, 0x98, 0x7c, 0xea, - 0xf1, 0x02, 0x7d, 0x72, 0x99, 0x17, 0xb0, 0x7b, 0xfa, 0x07, 0x4a, 0xa1, 0xe4, 0x6d, 0x28, 0x7b, - 0xcd, 0x66, 0xc7, 0xb3, 0x1b, 0xe1, 0x6d, 0x5d, 0x1d, 0xa7, 0x91, 0x99, 0x6a, 0xe7, 0x14, 0x83, - 0xf2, 0xc6, 0x10, 0x3a, 0x1c, 0xca, 0x81, 0x5b, 0xe6, 0xb3, 0xf1, 0x5b, 0xe4, 0xac, 0x5c, 0x14, - 0xcd, 0xfc, 0xff, 0x47, 0xd1, 0xcc, 0xf8, 0x95, 0x75, 0xd5, 0xe0, 0x30, 0x67, 0x3a, 0x8e, 0xc5, - 0x64, 0x4d, 0x88, 0x0f, 0xa7, 0x7a, 0x69, 0x7e, 0x0b, 0x53, 0xf9, 0x23, 0xf7, 0xf3, 0x9e, 0xce, - 0x2a, 0x29, 0xa7, 0x52, 0x3d, 0x1f, 0x86, 0x43, 0x38, 0x47, 0xef, 0xf1, 0x16, 0x1e, 0xd9, 0x3d, - 0xde, 0xf8, 0xfb, 0x9f, 0x33, 0x8f, 0xe3, 0xfd, 0x4f, 0xf2, 0xcb, 0xd4, 0xeb, 0xe3, 0xd2, 0xdc, - 0xff, 0xfc, 0x51, 0x0c, 0xf6, 0xaf, 0xdd, 0x15, 0xf2, 0x3f, 0xce, 0xc1, 0x82, 0x9c, 0x52, 0x69, - 0x8f, 0xbb, 0xab, 0x2c, 0x90, 0x23, 0x08, 0xb8, 0x89, 0x50, 0x7f, 0x2d, 0x26, 0x48, 0x84, 0xde, - 0xee, 0x23, 0x9c, 0x7c, 0x3d, 0x65, 0x7b, 0x9a, 0x1d, 0xc1, 0x19, 0x4e, 0x4d, 0xa3, 0x54, 0x06, - 0xd2, 0x83, 0x76, 0xa4, 0x3d, 0xf9, 0xb6, 0xc7, 0xd0, 0x57, 0x4e, 0x6e, 0x46, 0xb7, 0xa2, 0xac, - 0x0f, 0x8d, 0x84, 0xba, 0x27, 0xfa, 0xc2, 0xca, 0x97, 0x73, 0x70, 0x22, 0x4d, 0x49, 0xa4, 0xd4, - 0xa2, 0x16, 0xaf, 0xc5, 0x68, 0xd1, 0xb6, 0x68, 0x1d, 0x8e, 0xe6, 0xa6, 0xf8, 0x1f, 0x4d, 0x46, - 0x22, 0x84, 0x01, 0xed, 0xfd, 0x36, 0xef, 0x31, 0x53, 0xde, 0x63, 0xec, 0xb5, 0xdc, 0xfc, 0x63, - 0x7c, 0x2d, 0x77, 0x32, 0xc3, 0x6b, 0xb9, 0x53, 0x8f, 0xf3, 0xb5, 0xdc, 0xc2, 0x21, 0x5f, 0xcb, - 0x2d, 0xfe, 0xda, 0xbc, 0x96, 0x6b, 0x7d, 0x98, 0x83, 0xb9, 0xff, 0xed, 0x7f, 0x07, 0xf2, 0xb3, - 0x1c, 0x9c, 0xf8, 0x1f, 0xf8, 0x1f, 0x90, 0x3b, 0xf1, 0x43, 0x8f, 0x4b, 0x47, 0xd2, 0xc8, 0x21, - 0x87, 0x1f, 0xef, 0x42, 0x9a, 0xdb, 0x75, 0xb8, 0x0b, 0x2d, 0xb1, 0xb3, 0xfb, 0xb1, 0x43, 0x9f, - 0xdd, 0xff, 0x57, 0x4a, 0xaf, 0x8a, 0x7d, 0xf3, 0xfd, 0x47, 0xf5, 0xbf, 0x07, 0x27, 0xd2, 0xfe, - 0xf7, 0x20, 0xf1, 0x3f, 0x07, 0xc9, 0x77, 0xef, 0xc7, 0x1e, 0xdd, 0xbb, 0xf7, 0xd5, 0xc5, 0x1f, - 0x7e, 0x78, 0xf6, 0x89, 0x1f, 0x7f, 0x78, 0xf6, 0x89, 0x9f, 0x7c, 0x78, 0xf6, 0x89, 0x0f, 0x0e, - 0xce, 0xe6, 0x7e, 0x78, 0x70, 0x36, 0xf7, 0xe3, 0x83, 0xb3, 0xb9, 0x9f, 0x1c, 0x9c, 0xcd, 0xfd, - 0xec, 0xe0, 0x6c, 0xee, 0x0f, 0xff, 0xf9, 0xec, 0x13, 0x9f, 0x2f, 0xe8, 0xc6, 0xfc, 0x77, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x57, 0x96, 0x3e, 0x8d, 0x7c, 0x73, 0x00, 0x00, + // 6990 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x23, 0xd7, + 0x75, 0xb0, 0x29, 0x89, 0x14, 0x79, 0x28, 0xad, 0xa4, 0xbb, 0x7f, 0xb4, 0xbc, 0x5e, 0x6e, 0xc6, + 0xb1, 0xb1, 0xfe, 0x92, 0x48, 0xf1, 0x6e, 0xfc, 0x7d, 0x4e, 0x1c, 0xff, 0x88, 0xd2, 0x4a, 0x2b, + 0xef, 0x8f, 0xe4, 0x43, 0xed, 0xee, 0x97, 0xd8, 0xb0, 0x3b, 0xe2, 0x5c, 0x92, 0xb3, 0x22, 0x67, + 0xe8, 0x99, 0xe1, 0xca, 0x8a, 0x53, 0xd4, 0x09, 0x1a, 0xa4, 0x49, 0x10, 0xb4, 0x45, 0x80, 0x34, + 0x45, 0x50, 0x20, 0x2f, 0x45, 0x81, 0xa2, 0xaf, 0xed, 0x4b, 0x81, 0x3c, 0x14, 0x68, 0x91, 0xe6, + 0xa5, 0xe9, 0x53, 0xf3, 0x10, 0x28, 0xb1, 0x0a, 0x14, 0x2e, 0xd2, 0x36, 0x4f, 0x41, 0x81, 0x45, + 0x81, 0x16, 0xf7, 0x77, 0x7e, 0x38, 0xdc, 0xd5, 0x0e, 0xb5, 0x8b, 0xa0, 0xc9, 0x1b, 0xe7, 0x9c, + 0x73, 0xcf, 0xb9, 0xff, 0xf7, 0xfc, 0xdd, 0x4b, 0x58, 0x6e, 0xd9, 0x41, 0xbb, 0xbf, 0xbd, 0xd0, + 0x70, 0xbb, 0x8b, 0xa6, 0xd7, 0x72, 0x7b, 0x9e, 0x7b, 0x9b, 0xff, 0x58, 0xec, 0xed, 0xb4, 0x16, + 0xcd, 0x9e, 0xed, 0x2f, 0xee, 0xba, 0xde, 0x4e, 0xb3, 0xe3, 0xee, 0x2e, 0xde, 0x79, 0xce, 0xec, + 0xf4, 0xda, 0xe6, 0x73, 0x8b, 0x2d, 0xea, 0x50, 0xcf, 0x0c, 0xa8, 0xb5, 0xd0, 0xf3, 0xdc, 0xc0, + 0x25, 0x17, 0x43, 0x26, 0x0b, 0x8a, 0x09, 0xff, 0xb1, 0xd0, 0xdb, 0x69, 0x2d, 0x30, 0x26, 0x0b, + 0x8a, 0xc9, 0x82, 0x62, 0x32, 0xff, 0x89, 0x88, 0xe4, 0x96, 0xcb, 0x04, 0x32, 0x5e, 0xdb, 0xfd, + 0x26, 0xff, 0xe2, 0x1f, 0xfc, 0x97, 0x90, 0x31, 0x6f, 0xec, 0xbc, 0xe0, 0x2f, 0xd8, 0x2e, 0xab, + 0xd2, 0x62, 0xc3, 0xf5, 0xe8, 0xe2, 0x9d, 0x81, 0x7a, 0xcc, 0x3f, 0x1b, 0xa1, 0xe9, 0xb9, 0x1d, + 0xbb, 0xb1, 0xb7, 0x78, 0xe7, 0xb9, 0x6d, 0x1a, 0x0c, 0x56, 0x79, 0xfe, 0x53, 0x21, 0x69, 0xd7, + 0x6c, 0xb4, 0x6d, 0x87, 0x7a, 0x7b, 0x61, 0x93, 0xbb, 0x34, 0x30, 0xd3, 0x04, 0x2c, 0x0e, 0x2b, + 0xe5, 0xf5, 0x9d, 0xc0, 0xee, 0xd2, 0x81, 0x02, 0xff, 0xf7, 0x7e, 0x05, 0xfc, 0x46, 0x9b, 0x76, + 0xcd, 0x81, 0x72, 0x17, 0x87, 0x95, 0xeb, 0x07, 0x76, 0x67, 0xd1, 0x76, 0x02, 0x3f, 0xf0, 0x92, + 0x85, 0x8c, 0x67, 0xa1, 0xb0, 0xd4, 0x75, 0xfb, 0x4e, 0x40, 0xaa, 0x90, 0xbf, 0x63, 0x76, 0xfa, + 0xb4, 0x92, 0x3b, 0x97, 0x3b, 0x3f, 0x55, 0x2b, 0x1d, 0xec, 0x57, 0xf3, 0x37, 0x19, 0x00, 0x05, + 0xdc, 0xf8, 0x87, 0x1c, 0xcc, 0x2c, 0x79, 0x8d, 0xb6, 0x7d, 0x87, 0xd6, 0x03, 0xc6, 0xa3, 0xb5, + 0x47, 0xde, 0x80, 0xf1, 0xc0, 0xf4, 0x78, 0x91, 0xf2, 0x85, 0x57, 0x17, 0x32, 0x8c, 0xe9, 0xc2, + 0x96, 0xe9, 0x29, 0x76, 0xb5, 0xc9, 0x83, 0xfd, 0xea, 0xf8, 0x96, 0xe9, 0x21, 0xe3, 0x4a, 0xde, + 0x86, 0x09, 0xc7, 0x75, 0x68, 0x65, 0x8c, 0x73, 0x5f, 0xca, 0xc4, 0xfd, 0xba, 0xeb, 0xe8, 0xda, + 0xd6, 0x8a, 0x07, 0xfb, 0xd5, 0x09, 0x06, 0x41, 0xce, 0xd8, 0xf8, 0x45, 0x0e, 0x4a, 0x4b, 0x5e, + 0xab, 0xdf, 0xa5, 0x4e, 0xe0, 0x13, 0x0f, 0xa0, 0x67, 0x7a, 0x66, 0x97, 0x06, 0xd4, 0xf3, 0x2b, + 0xb9, 0x73, 0xe3, 0xe7, 0xcb, 0x17, 0x5e, 0xce, 0x24, 0x74, 0x53, 0xb1, 0xa9, 0x91, 0x1f, 0xec, + 0x57, 0x1f, 0x3b, 0xd8, 0xaf, 0x82, 0x06, 0xf9, 0x18, 0x91, 0x42, 0x1c, 0x28, 0x99, 0x5e, 0x60, + 0x37, 0xcd, 0x46, 0xe0, 0x57, 0xc6, 0xb8, 0xc8, 0x97, 0x32, 0x89, 0x5c, 0x92, 0x5c, 0x6a, 0x73, + 0x52, 0x62, 0x49, 0x41, 0x7c, 0x0c, 0x45, 0x18, 0x3f, 0x1f, 0x87, 0xa2, 0x42, 0x90, 0x73, 0x30, + 0xe1, 0x98, 0x5d, 0x31, 0xe0, 0xa5, 0xda, 0x94, 0x2c, 0x38, 0x71, 0xdd, 0xec, 0xb2, 0x0e, 0x32, + 0xbb, 0x94, 0x51, 0xf4, 0xcc, 0xa0, 0xcd, 0x47, 0x20, 0x42, 0xb1, 0x69, 0x06, 0x6d, 0xe4, 0x18, + 0x72, 0x06, 0x26, 0xba, 0xae, 0x45, 0x2b, 0xe3, 0xe7, 0x72, 0xe7, 0xf3, 0xa2, 0x83, 0xaf, 0xb9, + 0x16, 0x45, 0x0e, 0x65, 0xe5, 0x9b, 0x9e, 0xdb, 0xad, 0x4c, 0xc4, 0xcb, 0xaf, 0x7a, 0x6e, 0x17, + 0x39, 0x86, 0x7c, 0x23, 0x07, 0xb3, 0xaa, 0x7a, 0x57, 0xdd, 0x86, 0x19, 0xd8, 0xae, 0x53, 0xc9, + 0xf3, 0x01, 0xbf, 0x34, 0x52, 0x47, 0x28, 0x66, 0xb5, 0x8a, 0x94, 0x3a, 0x9b, 0xc4, 0xe0, 0x80, + 0x60, 0x72, 0x01, 0xa0, 0xd5, 0x71, 0xb7, 0xcd, 0x0e, 0xeb, 0x83, 0x4a, 0x81, 0xd7, 0x5a, 0x0f, + 0xe1, 0x9a, 0xc6, 0x60, 0x84, 0x8a, 0xec, 0xc0, 0xa4, 0x29, 0x56, 0x45, 0x65, 0x92, 0xd7, 0x7b, + 0x25, 0x63, 0xbd, 0x63, 0x2b, 0xab, 0x56, 0x3e, 0xd8, 0xaf, 0x4e, 0x4a, 0x20, 0x2a, 0x09, 0xe4, + 0xe3, 0x50, 0x74, 0x7b, 0xac, 0xaa, 0x66, 0xa7, 0x52, 0x3c, 0x97, 0x3b, 0x5f, 0xac, 0xcd, 0xca, + 0xea, 0x15, 0x37, 0x24, 0x1c, 0x35, 0x85, 0xf1, 0x8f, 0x05, 0x18, 0x68, 0x35, 0x79, 0x0e, 0xca, + 0x92, 0xdb, 0x55, 0xb7, 0xe5, 0xf3, 0xc1, 0x2f, 0xd6, 0x66, 0x0e, 0xf6, 0xab, 0xe5, 0xa5, 0x10, + 0x8c, 0x51, 0x1a, 0x72, 0x0b, 0xc6, 0xfc, 0x8b, 0x72, 0x19, 0xbe, 0x92, 0xa9, 0x75, 0xf5, 0x8b, + 0x7a, 0x82, 0x16, 0x0e, 0xf6, 0xab, 0x63, 0xf5, 0x8b, 0x38, 0xe6, 0x5f, 0x64, 0xdb, 0x47, 0xcb, + 0x0e, 0xf8, 0xe4, 0xc9, 0xba, 0x7d, 0xac, 0xd9, 0x81, 0x66, 0xcd, 0xb7, 0x8f, 0x35, 0x3b, 0x40, + 0xc6, 0x95, 0x6d, 0x1f, 0xed, 0x20, 0xe8, 0xf1, 0xc9, 0x97, 0x75, 0xfb, 0xb8, 0xbc, 0xb5, 0xb5, + 0xa9, 0xd9, 0xf3, 0xd9, 0xcd, 0x20, 0xc8, 0x19, 0x93, 0xf7, 0x58, 0x4f, 0x0a, 0x9c, 0xeb, 0xed, + 0xc9, 0x59, 0x7b, 0x79, 0xa4, 0x59, 0xeb, 0x7a, 0x7b, 0x5a, 0x9c, 0x1c, 0x13, 0x8d, 0xc0, 0xa8, + 0x34, 0xde, 0x3a, 0xab, 0xe9, 0xf3, 0x49, 0x9a, 0xb9, 0x75, 0x2b, 0xab, 0xf5, 0x44, 0xeb, 0x56, + 0x56, 0xeb, 0xc8, 0x19, 0xb3, 0xb1, 0xf1, 0xcc, 0x5d, 0x39, 0xa7, 0xb3, 0x8d, 0x0d, 0x9a, 0xbb, + 0xf1, 0xb1, 0x41, 0x73, 0x17, 0x19, 0x57, 0xc6, 0xdc, 0xf5, 0x7d, 0x3e, 0x85, 0xb3, 0x32, 0xdf, + 0xa8, 0xd7, 0xe3, 0xcc, 0x37, 0xea, 0x75, 0x64, 0x5c, 0xf9, 0xac, 0x6a, 0xf8, 0x95, 0xd2, 0x28, + 0xb3, 0x6a, 0x39, 0xc1, 0x7c, 0x6d, 0xb9, 0x8e, 0x8c, 0xab, 0xd1, 0x82, 0x93, 0x0a, 0x83, 0xb4, + 0xe7, 0xfa, 0x36, 0x1f, 0x1a, 0xda, 0x24, 0x8b, 0x50, 0x6a, 0xb8, 0x4e, 0xd3, 0x6e, 0x5d, 0x33, + 0x7b, 0x72, 0x4b, 0xd5, 0x7b, 0xf1, 0xb2, 0x42, 0x60, 0x48, 0x43, 0x9e, 0x84, 0xf1, 0x1d, 0xba, + 0x27, 0xf7, 0xd6, 0xb2, 0x24, 0x1d, 0xbf, 0x42, 0xf7, 0x90, 0xc1, 0x8d, 0xef, 0xe7, 0xe0, 0x78, + 0xca, 0xb4, 0x60, 0xc5, 0xfa, 0x5e, 0x47, 0x4a, 0xd0, 0xc5, 0x6e, 0xe0, 0x55, 0x64, 0x70, 0xf2, + 0xd5, 0x1c, 0xcc, 0x44, 0xe6, 0xc9, 0x52, 0x5f, 0x6e, 0xdf, 0xd9, 0xf7, 0xa5, 0x18, 0xaf, 0xda, + 0x69, 0x29, 0x71, 0x26, 0x81, 0xc0, 0xa4, 0x54, 0xe3, 0x9f, 0xb8, 0xbe, 0x10, 0x83, 0x11, 0x13, + 0x8e, 0xf5, 0x7d, 0xea, 0xb1, 0xc3, 0xa5, 0x4e, 0x1b, 0x1e, 0x0d, 0xa4, 0xea, 0xf0, 0xf4, 0x82, + 0x50, 0x5e, 0x58, 0x2d, 0x16, 0x98, 0xaa, 0xb6, 0x70, 0xe7, 0xb9, 0x05, 0x41, 0x71, 0x85, 0xee, + 0xd5, 0x69, 0x87, 0x32, 0x1e, 0x35, 0x72, 0xb0, 0x5f, 0x3d, 0x76, 0x23, 0xc6, 0x00, 0x13, 0x0c, + 0x99, 0x88, 0x9e, 0xe9, 0xfb, 0xbb, 0xae, 0x67, 0x49, 0x11, 0x63, 0x0f, 0x2c, 0x62, 0x33, 0xc6, + 0x00, 0x13, 0x0c, 0x8d, 0x6f, 0xe7, 0x60, 0xb2, 0x66, 0x36, 0x76, 0xdc, 0x66, 0x93, 0xed, 0xc8, + 0x56, 0xdf, 0x13, 0xe7, 0x96, 0x18, 0x13, 0xbd, 0x23, 0xaf, 0x48, 0x38, 0x6a, 0x0a, 0xf2, 0x0c, + 0x14, 0x44, 0x77, 0xf0, 0x4a, 0xe5, 0x6b, 0xc7, 0x24, 0x6d, 0x61, 0x95, 0x43, 0x51, 0x62, 0xc9, + 0xf3, 0x50, 0xee, 0x9a, 0xef, 0x2a, 0x06, 0x7c, 0x83, 0x2c, 0xd5, 0x8e, 0x4b, 0xe2, 0xf2, 0xb5, + 0x10, 0x85, 0x51, 0x3a, 0xe3, 0x2d, 0xc8, 0x2f, 0x9b, 0x8d, 0x36, 0x25, 0x37, 0x92, 0x93, 0xb1, + 0x7c, 0xe1, 0x7c, 0x5a, 0xfb, 0xf5, 0xc4, 0x8c, 0x76, 0xc1, 0xf4, 0xb0, 0x29, 0x6b, 0x7c, 0x98, + 0x83, 0xd3, 0xcb, 0x9d, 0xbe, 0x1f, 0x50, 0xef, 0x96, 0x9c, 0x29, 0x5b, 0xb4, 0xdb, 0xeb, 0x98, + 0x01, 0x25, 0xbf, 0x05, 0x45, 0xa6, 0x02, 0x5b, 0x66, 0x60, 0x4a, 0x89, 0x9f, 0x8c, 0x48, 0xd4, + 0x1a, 0x69, 0x38, 0xd7, 0x18, 0x35, 0xab, 0xc3, 0xc6, 0xf6, 0x6d, 0xda, 0x08, 0xae, 0xd1, 0xc0, + 0x0c, 0xcf, 0xda, 0x10, 0x86, 0x9a, 0x2b, 0xd9, 0x81, 0x09, 0xbf, 0x47, 0x1b, 0x72, 0x3c, 0xd7, + 0x33, 0x4d, 0xe7, 0x64, 0xb5, 0xeb, 0x3d, 0xda, 0x08, 0x15, 0x13, 0xf6, 0x85, 0x5c, 0x88, 0xf1, + 0x1f, 0x39, 0x78, 0x62, 0x48, 0x53, 0xaf, 0xda, 0x7e, 0x40, 0xde, 0x1c, 0x68, 0xee, 0xc2, 0xe1, + 0x9a, 0xcb, 0x4a, 0xf3, 0xc6, 0xea, 0x79, 0xa2, 0x20, 0x91, 0xa6, 0xbe, 0x03, 0x79, 0x3b, 0xa0, + 0x5d, 0xa5, 0x13, 0x5e, 0xcd, 0xd4, 0xd6, 0x21, 0xd5, 0xaf, 0x4d, 0x4b, 0xc1, 0xf9, 0x75, 0x26, + 0x02, 0x85, 0x24, 0xe3, 0xef, 0x73, 0xc0, 0x06, 0xdd, 0xb2, 0xa5, 0x96, 0x30, 0x11, 0xec, 0xf5, + 0x94, 0x6e, 0xf8, 0xa4, 0xea, 0xa0, 0xad, 0xbd, 0x1e, 0xbd, 0xbb, 0x5f, 0x9d, 0xd6, 0x84, 0x0c, + 0x80, 0x9c, 0x94, 0xbc, 0x05, 0x05, 0x3f, 0x30, 0x83, 0xbe, 0x2f, 0xb7, 0xb4, 0x55, 0x35, 0xb7, + 0xeb, 0x1c, 0x7a, 0x77, 0xbf, 0x7a, 0x28, 0x03, 0x6a, 0x41, 0xf3, 0x16, 0xe5, 0x50, 0x72, 0x25, + 0xcf, 0xc2, 0x64, 0x97, 0xfa, 0xbe, 0xd9, 0xa2, 0x72, 0x3d, 0xcc, 0x48, 0x01, 0x93, 0xd7, 0x04, + 0x18, 0x15, 0xde, 0xf8, 0x1c, 0xc0, 0xb2, 0xeb, 0x04, 0xb6, 0xd3, 0xa7, 0x1b, 0x0e, 0x79, 0x0a, + 0xf2, 0xd4, 0xf3, 0x5c, 0x4f, 0xea, 0x3a, 0xba, 0xf9, 0x97, 0x18, 0x10, 0x05, 0x4e, 0xac, 0x4c, + 0xbb, 0x43, 0x2d, 0x5e, 0xfb, 0x62, 0x74, 0x65, 0x32, 0x28, 0x4a, 0xac, 0xb1, 0x00, 0x93, 0xcb, + 0xcc, 0x5e, 0xa2, 0x1e, 0xe3, 0x1b, 0x5a, 0x4c, 0xa5, 0x90, 0x6f, 0xcc, 0x6a, 0xfa, 0xe1, 0x18, + 0x4c, 0x2d, 0x7b, 0xae, 0xa3, 0x46, 0xe1, 0x11, 0xac, 0x93, 0x56, 0x6c, 0x9d, 0x64, 0x53, 0xa3, + 0xa3, 0x55, 0x1e, 0xb6, 0x46, 0x88, 0xab, 0x47, 0x5c, 0x68, 0x70, 0x6b, 0xa3, 0x8b, 0xe2, 0xec, + 0xc2, 0xce, 0x8f, 0x4f, 0x01, 0xe3, 0xc7, 0x39, 0x98, 0x8d, 0x92, 0x3f, 0x82, 0x95, 0xd8, 0x8c, + 0xaf, 0xc4, 0xa5, 0x91, 0x9b, 0x38, 0x64, 0xf9, 0xfd, 0x57, 0x3e, 0xde, 0x34, 0xd6, 0xcd, 0xcc, + 0x3a, 0x9a, 0xda, 0x8d, 0x00, 0x64, 0xfb, 0x96, 0x46, 0xda, 0xfa, 0xf8, 0x70, 0x7e, 0x54, 0x56, + 0x62, 0x2a, 0x0a, 0xbd, 0x9b, 0xf8, 0xc6, 0x98, 0x70, 0x76, 0xd4, 0xf9, 0x8d, 0x36, 0xb5, 0xfa, + 0x1d, 0x2a, 0x97, 0xb8, 0xee, 0xb8, 0xba, 0x84, 0xa3, 0xa6, 0x20, 0x6f, 0xc2, 0x5c, 0xc3, 0x75, + 0x1a, 0x7d, 0xcf, 0xa3, 0x4e, 0x63, 0x6f, 0x93, 0xbb, 0x56, 0xe4, 0xc2, 0x5d, 0x90, 0xc5, 0xe6, + 0x96, 0x93, 0x04, 0x77, 0xd3, 0x80, 0x38, 0xc8, 0x88, 0x6d, 0x06, 0x7e, 0xdf, 0xef, 0x51, 0xc7, + 0xe2, 0xfa, 0x7d, 0x31, 0xdc, 0x0c, 0xea, 0x02, 0x8c, 0x0a, 0x4f, 0x6e, 0xc0, 0x69, 0x3f, 0x60, + 0xca, 0x89, 0xd3, 0x5a, 0xa1, 0xa6, 0xd5, 0xb1, 0x1d, 0xa6, 0x2a, 0xb8, 0x8e, 0xe5, 0x73, 0x95, + 0x7d, 0xbc, 0xf6, 0xc4, 0xc1, 0x7e, 0xf5, 0x74, 0x3d, 0x9d, 0x04, 0x87, 0x95, 0x25, 0x6f, 0xc1, + 0xbc, 0xdf, 0x6f, 0x34, 0xa8, 0xef, 0x37, 0xfb, 0x9d, 0xd7, 0xdc, 0x6d, 0xff, 0xb2, 0xed, 0x33, + 0x3d, 0xe7, 0xaa, 0xdd, 0xb5, 0x03, 0xae, 0x96, 0xe7, 0x6b, 0x67, 0x0f, 0xf6, 0xab, 0xf3, 0xf5, + 0xa1, 0x54, 0x78, 0x0f, 0x0e, 0x04, 0xe1, 0x94, 0xd8, 0x72, 0x06, 0x78, 0x4f, 0x72, 0xde, 0xf3, + 0x07, 0xfb, 0xd5, 0x53, 0xab, 0xa9, 0x14, 0x38, 0xa4, 0x24, 0x1b, 0xc1, 0xc0, 0xee, 0xd2, 0x2f, + 0xb8, 0x0e, 0xe5, 0xba, 0x77, 0x64, 0x04, 0xb7, 0x24, 0x1c, 0x35, 0x05, 0xb9, 0x1d, 0x4e, 0x3e, + 0xb6, 0x28, 0xa4, 0x42, 0xfd, 0xe0, 0xbb, 0xd5, 0x09, 0x66, 0x7d, 0xdf, 0x8a, 0x70, 0x62, 0x0b, + 0x0b, 0x63, 0xbc, 0x8d, 0xbf, 0x1b, 0x03, 0x32, 0xb8, 0x11, 0x90, 0x2b, 0x50, 0x30, 0x1b, 0x01, + 0xb3, 0xad, 0x85, 0x3f, 0xe6, 0xa9, 0x34, 0x25, 0x46, 0x88, 0x42, 0xda, 0xa4, 0x6c, 0x86, 0xd0, + 0x70, 0xf7, 0x58, 0xe2, 0x45, 0x51, 0xb2, 0x20, 0x2e, 0xcc, 0x75, 0x4c, 0x3f, 0x50, 0x73, 0xd5, + 0x62, 0x4d, 0x96, 0x9b, 0xe4, 0xff, 0x39, 0x5c, 0xa3, 0x58, 0x89, 0xda, 0x49, 0x36, 0x73, 0xaf, + 0x26, 0x19, 0xe1, 0x20, 0x6f, 0xe2, 0x01, 0x34, 0xd4, 0x61, 0xc6, 0xf6, 0xc8, 0xec, 0x1e, 0x25, + 0x7d, 0x26, 0x86, 0x5b, 0xbf, 0x06, 0xf9, 0x18, 0x91, 0x62, 0xfc, 0x7b, 0x01, 0x26, 0x57, 0x96, + 0xd6, 0xb6, 0x4c, 0x7f, 0xe7, 0x10, 0x0e, 0x1e, 0x36, 0x21, 0xa4, 0x5a, 0x90, 0x5c, 0xd2, 0x4a, + 0x5d, 0x40, 0x4d, 0x41, 0x5c, 0x28, 0x99, 0xca, 0x5d, 0x26, 0xb7, 0xfc, 0x97, 0x33, 0x1a, 0x15, + 0x92, 0x4b, 0xd4, 0x5d, 0x25, 0x41, 0x18, 0xca, 0x20, 0x3e, 0x94, 0x95, 0x70, 0xa4, 0x4d, 0x69, + 0xc9, 0x67, 0x74, 0x33, 0x86, 0x7c, 0x84, 0x65, 0x1d, 0x01, 0x60, 0x54, 0x0a, 0xf9, 0x14, 0x4c, + 0x59, 0x94, 0xed, 0x1c, 0xd4, 0x69, 0xd8, 0x94, 0x6d, 0x12, 0xe3, 0xac, 0x5f, 0xd8, 0x66, 0xb9, + 0x12, 0x81, 0x63, 0x8c, 0x8a, 0xdc, 0x86, 0xd2, 0xae, 0x1d, 0xb4, 0xf9, 0x9e, 0x5e, 0x29, 0xf0, + 0xa1, 0xfe, 0x74, 0xa6, 0x8a, 0x32, 0x0e, 0x61, 0xb7, 0xdc, 0x52, 0x3c, 0x31, 0x64, 0xcf, 0x4c, + 0x4d, 0xf6, 0xc1, 0x7d, 0x8a, 0x7c, 0x37, 0x28, 0xc5, 0x0b, 0x70, 0x04, 0x86, 0x34, 0xc4, 0x87, + 0x29, 0xf6, 0x51, 0xa7, 0xef, 0xf4, 0xd9, 0x0a, 0x91, 0x76, 0x77, 0x36, 0x4f, 0xa3, 0x62, 0x22, + 0x7a, 0xe4, 0x56, 0x84, 0x2d, 0xc6, 0x84, 0xb0, 0xd9, 0xb7, 0xdb, 0xa6, 0x0e, 0xdf, 0x36, 0x22, + 0xb3, 0xef, 0x56, 0x9b, 0x3a, 0xc8, 0x31, 0xc4, 0xe5, 0xeb, 0x43, 0xaa, 0x69, 0x15, 0x18, 0xc1, + 0xbf, 0x14, 0x6a, 0x7b, 0xb5, 0x63, 0x72, 0x71, 0xc8, 0x6f, 0x8c, 0x88, 0x60, 0x4a, 0x9e, 0xeb, + 0x5c, 0x7a, 0xd7, 0x0e, 0x2a, 0x65, 0x5e, 0x29, 0xbd, 0x53, 0x6c, 0x70, 0x28, 0x4a, 0x2c, 0x3b, + 0x5d, 0xc4, 0xe0, 0xfa, 0x95, 0xa9, 0xb8, 0xaa, 0x29, 0x66, 0x80, 0x8f, 0x0a, 0x6f, 0xfc, 0x4d, + 0x0e, 0xca, 0x6c, 0xbd, 0xa9, 0x35, 0xf2, 0x0c, 0x14, 0x02, 0xd3, 0x6b, 0x49, 0xcb, 0x36, 0x22, + 0x62, 0x8b, 0x43, 0x51, 0x62, 0x89, 0x09, 0xf9, 0xc0, 0xf4, 0x77, 0x94, 0x5e, 0xf1, 0xd9, 0x4c, + 0xcd, 0x96, 0x0b, 0x3d, 0x54, 0x29, 0xd8, 0x97, 0x8f, 0x82, 0x33, 0x39, 0x0f, 0x45, 0x76, 0x0e, + 0xac, 0x9a, 0xbe, 0x70, 0xb1, 0x15, 0x6b, 0x53, 0x6c, 0x61, 0xaf, 0x4a, 0x18, 0x6a, 0xac, 0xf1, + 0x3c, 0xe4, 0x2f, 0xdd, 0xa1, 0x0e, 0x3f, 0x20, 0x7c, 0x69, 0x06, 0x26, 0xad, 0x59, 0x65, 0x1e, + 0xa2, 0xa6, 0x30, 0xde, 0x84, 0x63, 0x97, 0xde, 0xa5, 0x8d, 0x7e, 0xe0, 0x7a, 0xc2, 0x5c, 0x24, + 0xaf, 0x01, 0xf1, 0xa9, 0x77, 0xc7, 0x6e, 0xd0, 0xa5, 0x46, 0x83, 0xa9, 0xc9, 0xd7, 0xc3, 0xfd, + 0x67, 0x5e, 0x72, 0x22, 0xf5, 0x01, 0x0a, 0x4c, 0x29, 0x65, 0xfc, 0x49, 0x0e, 0xca, 0x11, 0x3f, + 0x0c, 0xdb, 0x7d, 0x5a, 0xcb, 0xf5, 0x5a, 0xbf, 0xb1, 0xa3, 0xdd, 0x06, 0x2f, 0x67, 0x75, 0xee, + 0x08, 0x2e, 0xe1, 0xaa, 0xd1, 0x20, 0x0c, 0x65, 0xdc, 0xcf, 0x41, 0xf3, 0x57, 0x39, 0x08, 0xcb, + 0xb1, 0x71, 0xdf, 0x0e, 0xab, 0x16, 0x19, 0x77, 0xc9, 0x57, 0x62, 0xc9, 0xfb, 0x39, 0x38, 0x1d, + 0x6f, 0x2c, 0x37, 0xbd, 0x1f, 0xdc, 0x51, 0x51, 0x95, 0x02, 0x4e, 0xd7, 0xd3, 0xb9, 0xe1, 0x30, + 0x31, 0xc6, 0x4d, 0xc8, 0xaf, 0x99, 0xfd, 0x16, 0x3d, 0x94, 0x01, 0xc3, 0x66, 0x91, 0x47, 0xcd, + 0x4e, 0xa0, 0x0e, 0x4b, 0x39, 0x8b, 0x50, 0xc2, 0x50, 0x63, 0x8d, 0xbf, 0x98, 0x80, 0x72, 0xc4, + 0x1d, 0xcb, 0x36, 0x00, 0x8f, 0xf6, 0xdc, 0xe4, 0xf1, 0x83, 0xb4, 0xe7, 0x22, 0xc7, 0xb0, 0xe9, + 0xe6, 0xd1, 0x3b, 0xb6, 0x6f, 0xbb, 0x4e, 0xf2, 0xf8, 0x41, 0x09, 0x47, 0x4d, 0x41, 0xaa, 0x90, + 0xb7, 0x68, 0x2f, 0x68, 0xf3, 0xc9, 0x3c, 0x21, 0x22, 0x54, 0x2b, 0x0c, 0x80, 0x02, 0xce, 0x08, + 0x9a, 0x34, 0x68, 0xb4, 0x2b, 0x13, 0x7c, 0xcb, 0xe6, 0x04, 0xab, 0x0c, 0x80, 0x02, 0x9e, 0xe2, + 0x7e, 0xca, 0x3f, 0x7c, 0xf7, 0x53, 0xe1, 0x88, 0xdd, 0x4f, 0xa4, 0x07, 0xc7, 0x7d, 0xbf, 0xbd, + 0xe9, 0xd9, 0x77, 0xcc, 0x80, 0x86, 0xb3, 0x67, 0xf2, 0x41, 0xe4, 0x9c, 0x3e, 0xd8, 0xaf, 0x1e, + 0xaf, 0xd7, 0x2f, 0x27, 0xb9, 0x60, 0x1a, 0x6b, 0x52, 0x87, 0x93, 0xb6, 0xe3, 0xd3, 0x46, 0xdf, + 0xa3, 0xeb, 0x2d, 0xc7, 0xf5, 0xe8, 0x65, 0xd7, 0x67, 0xec, 0x64, 0x0c, 0x42, 0xb9, 0x07, 0x4e, + 0xae, 0xa7, 0x11, 0x61, 0x7a, 0x59, 0xe3, 0x87, 0x39, 0x98, 0x8a, 0x7a, 0xa0, 0x89, 0x0f, 0xd0, + 0x5e, 0x59, 0xad, 0x8b, 0xad, 0x44, 0xae, 0xf0, 0x57, 0x32, 0x3b, 0xb6, 0x05, 0x9b, 0x50, 0x5f, + 0x0a, 0x61, 0x18, 0x11, 0x73, 0x88, 0x10, 0xd7, 0x53, 0x90, 0x6f, 0xba, 0x5e, 0x83, 0xca, 0x3d, + 0x54, 0xaf, 0x92, 0x55, 0x06, 0x44, 0x81, 0x33, 0x3e, 0xcc, 0x41, 0x44, 0x02, 0xf9, 0x1d, 0x98, + 0x66, 0x32, 0xae, 0x78, 0xdb, 0xb1, 0xd6, 0xd4, 0x32, 0xb7, 0x46, 0x73, 0xaa, 0x9d, 0x94, 0xf2, + 0xa7, 0x63, 0x60, 0x8c, 0xcb, 0x23, 0x1f, 0x83, 0x92, 0x69, 0x59, 0x1e, 0xf5, 0x7d, 0x2a, 0x8e, + 0x98, 0x92, 0x70, 0xeb, 0x2d, 0x29, 0x20, 0x86, 0x78, 0xb6, 0x0c, 0xdb, 0x56, 0xd3, 0x67, 0x33, + 0x5b, 0x5a, 0x68, 0x7a, 0x19, 0x32, 0x21, 0x0c, 0x8e, 0x9a, 0xc2, 0xf8, 0xe6, 0x04, 0xc4, 0x65, + 0x13, 0x0b, 0x66, 0x76, 0xbc, 0xed, 0x65, 0xee, 0x7a, 0xcc, 0xe2, 0xd6, 0x3d, 0x7e, 0xb0, 0x5f, + 0x9d, 0xb9, 0x12, 0xe7, 0x80, 0x49, 0x96, 0x52, 0xca, 0x15, 0xba, 0x17, 0x98, 0xdb, 0x59, 0x36, + 0x4c, 0x25, 0x25, 0xca, 0x01, 0x93, 0x2c, 0xc9, 0xf3, 0x50, 0xde, 0xf1, 0xb6, 0xd5, 0x22, 0x4f, + 0x7a, 0x5e, 0xaf, 0x84, 0x28, 0x8c, 0xd2, 0xb1, 0x2e, 0xdc, 0xf1, 0xb6, 0xd9, 0xa6, 0xa8, 0xa2, + 0x9d, 0xba, 0x0b, 0xaf, 0x48, 0x38, 0x6a, 0x0a, 0xd2, 0x03, 0xb2, 0xa3, 0x7a, 0x4f, 0x3b, 0x5a, + 0xe5, 0x5e, 0x74, 0x78, 0x3f, 0xed, 0x29, 0x76, 0x98, 0x5e, 0x19, 0xe0, 0x83, 0x29, 0xbc, 0xc9, + 0xe7, 0xe0, 0xf4, 0x8e, 0xb7, 0x2d, 0x8f, 0x8a, 0x4d, 0xcf, 0x76, 0x1a, 0x76, 0x2f, 0x16, 0xe6, + 0xd4, 0xc7, 0xc9, 0x95, 0x74, 0x32, 0x1c, 0x56, 0xde, 0xf8, 0x04, 0x4c, 0x45, 0xc3, 0x64, 0xf7, + 0x09, 0x50, 0x18, 0xdf, 0xc9, 0x41, 0x89, 0x1b, 0xa5, 0x2d, 0xa6, 0x99, 0xea, 0x23, 0x68, 0xfc, + 0x1e, 0x47, 0x50, 0x13, 0x26, 0xc5, 0xe9, 0xe9, 0xf3, 0x9d, 0xbd, 0x7c, 0xe1, 0xc5, 0x6c, 0x56, + 0x07, 0x4f, 0x74, 0x08, 0x75, 0x39, 0x71, 0x32, 0xfb, 0xa8, 0x98, 0x1b, 0x1f, 0x83, 0xf2, 0x65, + 0xb7, 0x63, 0x51, 0x8f, 0xb5, 0xcb, 0x27, 0x67, 0xb4, 0xf9, 0xc4, 0x96, 0x4f, 0x31, 0x6e, 0x3a, + 0x19, 0xff, 0x96, 0x83, 0xc2, 0xba, 0xd3, 0xeb, 0xff, 0x9a, 0x64, 0x0e, 0x5c, 0x83, 0x09, 0x66, + 0x7c, 0x90, 0x17, 0xe3, 0x69, 0x22, 0x4f, 0xeb, 0xc1, 0xba, 0xbb, 0x5f, 0xad, 0x50, 0xa7, 0xe1, + 0x5a, 0xb6, 0xd3, 0x5a, 0xbc, 0xed, 0xbb, 0xce, 0x02, 0x9a, 0xbb, 0xca, 0x33, 0x2b, 0xca, 0x7c, + 0xa6, 0xf8, 0x9d, 0xef, 0x55, 0x1f, 0x7b, 0xff, 0x27, 0xe7, 0x1e, 0x33, 0x3a, 0x30, 0x71, 0xd5, + 0x76, 0x0e, 0x63, 0xa2, 0x3e, 0x05, 0x79, 0xbf, 0xe1, 0xf6, 0x94, 0x7d, 0xaa, 0x67, 0x48, 0x9d, + 0x01, 0x51, 0xe0, 0xd4, 0x9c, 0x1b, 0x1f, 0x32, 0xe7, 0xbe, 0x9c, 0x83, 0xb9, 0x6b, 0xb4, 0xeb, + 0xda, 0x5f, 0x30, 0x43, 0xc7, 0x32, 0x2b, 0xd4, 0xb6, 0x03, 0xe9, 0x15, 0xd6, 0x85, 0x2e, 0xdb, + 0x01, 0x32, 0xf8, 0x7d, 0xd4, 0x3f, 0x1e, 0xef, 0x63, 0xbb, 0xd3, 0xf5, 0x70, 0x9b, 0x08, 0xe3, + 0x7d, 0x0a, 0x81, 0x21, 0x8d, 0xf1, 0x95, 0x1c, 0x4c, 0x8a, 0x4a, 0x50, 0xc5, 0x3b, 0x37, 0x84, + 0xf7, 0x1b, 0x90, 0xe7, 0xe5, 0xe4, 0x06, 0xf7, 0x99, 0x6c, 0x36, 0x11, 0xe3, 0x20, 0x94, 0x20, + 0xfe, 0x13, 0x05, 0x4f, 0xe3, 0xfd, 0x71, 0x28, 0x2a, 0x2f, 0x0c, 0xf9, 0x4a, 0x0e, 0xca, 0xa6, + 0xe3, 0xb8, 0x81, 0x29, 0x9c, 0x14, 0x62, 0xf2, 0x5e, 0xcf, 0x24, 0x50, 0x31, 0x5d, 0x58, 0x0a, + 0x19, 0x5e, 0x72, 0x02, 0x6f, 0x2f, 0xdc, 0x3f, 0x23, 0x18, 0x8c, 0xca, 0x25, 0xef, 0x40, 0xa1, + 0x63, 0x6e, 0xd3, 0x8e, 0x9a, 0xcb, 0xeb, 0xa3, 0xd5, 0xe0, 0x2a, 0xe7, 0x25, 0x84, 0x6b, 0x4d, + 0x5c, 0x00, 0x51, 0x0a, 0x9a, 0x7f, 0x19, 0x66, 0x93, 0x15, 0x25, 0xb3, 0x91, 0x71, 0x11, 0x43, + 0x71, 0x42, 0xcd, 0x77, 0x3e, 0x0f, 0xd4, 0x44, 0x1e, 0x7b, 0x21, 0x37, 0xff, 0x69, 0x28, 0x47, + 0xc4, 0x3c, 0x48, 0x51, 0xe3, 0x75, 0x28, 0x5f, 0xa3, 0x81, 0x67, 0x37, 0x38, 0x83, 0xfb, 0xcd, + 0x86, 0xa7, 0x62, 0x7c, 0x86, 0xc4, 0x19, 0xbe, 0xc0, 0x26, 0x17, 0x63, 0xe9, 0x33, 0xb3, 0xba, + 0xe7, 0xb9, 0x5d, 0x1a, 0xb4, 0x69, 0x5f, 0x8d, 0x68, 0x36, 0x3d, 0x6a, 0x53, 0xb3, 0x11, 0x66, + 0x75, 0xf8, 0x8d, 0x11, 0x11, 0xc6, 0x2f, 0xa7, 0x01, 0xae, 0xbb, 0x16, 0x95, 0xeb, 0x6a, 0x1e, + 0xc6, 0x6c, 0x4b, 0xb6, 0x06, 0x64, 0x65, 0xc7, 0xd6, 0x57, 0x70, 0xcc, 0xb6, 0xf4, 0x7a, 0x1f, + 0x1b, 0xba, 0xde, 0x9f, 0x87, 0xb2, 0x65, 0xfb, 0xbd, 0x8e, 0xb9, 0x77, 0x3d, 0xe5, 0x00, 0x5e, + 0x09, 0x51, 0x18, 0xa5, 0x23, 0x1f, 0x97, 0x01, 0x2b, 0x71, 0xf8, 0x56, 0x12, 0x01, 0xab, 0x22, + 0xab, 0x5e, 0x24, 0x56, 0xf5, 0x02, 0x4c, 0x29, 0x97, 0x0f, 0x97, 0x92, 0xe7, 0xa5, 0x4e, 0x28, + 0xa7, 0xf8, 0x56, 0x04, 0x87, 0x31, 0xca, 0xa4, 0x4b, 0xaa, 0xf0, 0x48, 0x5c, 0x52, 0x2b, 0x30, + 0xeb, 0x07, 0xae, 0x47, 0x2d, 0x45, 0xb1, 0xbe, 0x52, 0x21, 0xb1, 0x86, 0xce, 0xd6, 0x13, 0x78, + 0x1c, 0x28, 0x41, 0x36, 0xe1, 0xc4, 0x6e, 0x22, 0x16, 0xc8, 0x1b, 0x7f, 0x9c, 0x73, 0x3a, 0x23, + 0x39, 0x9d, 0xb8, 0x95, 0x42, 0x83, 0xa9, 0x25, 0xc9, 0x8b, 0x30, 0xad, 0xaa, 0xc9, 0xb7, 0xe3, + 0xca, 0x09, 0xce, 0x4a, 0xab, 0xa8, 0x5b, 0x51, 0x24, 0xc6, 0x69, 0xc9, 0x27, 0x21, 0xdf, 0x6b, + 0x9b, 0x3e, 0x95, 0x1e, 0x2c, 0xe5, 0x1e, 0xc8, 0x6f, 0x32, 0xe0, 0xdd, 0xfd, 0x6a, 0x89, 0x8d, + 0x19, 0xff, 0x40, 0x41, 0x48, 0x2e, 0x00, 0x6c, 0xbb, 0x7d, 0xc7, 0x32, 0xbd, 0xbd, 0xf5, 0x15, + 0xe9, 0xc0, 0xd6, 0xe7, 0x64, 0x4d, 0x63, 0x30, 0x42, 0x15, 0x8d, 0x1a, 0x96, 0xee, 0x1d, 0x35, + 0x24, 0x6f, 0x40, 0x89, 0x3b, 0xfb, 0xa9, 0xb5, 0x14, 0x48, 0x6f, 0xd4, 0x83, 0xf8, 0x85, 0xf5, + 0xee, 0x5f, 0x57, 0x4c, 0x30, 0xe4, 0x47, 0xde, 0x02, 0x68, 0xda, 0x8e, 0xed, 0xb7, 0x39, 0xf7, + 0xf2, 0x03, 0x73, 0xd7, 0xed, 0x5c, 0xd5, 0x5c, 0x30, 0xc2, 0x91, 0xfc, 0x3c, 0x07, 0x73, 0x1e, + 0xf5, 0xdd, 0xbe, 0xd7, 0xa0, 0xbe, 0x4e, 0x1c, 0x38, 0xc9, 0x17, 0xff, 0xcd, 0x8c, 0xa9, 0x93, + 0x6a, 0x45, 0x2f, 0x60, 0x92, 0xb1, 0xd8, 0x59, 0xa9, 0x8a, 0xe3, 0x0c, 0xe0, 0xef, 0xa6, 0x01, + 0xbf, 0xfc, 0xd3, 0x6a, 0x75, 0x30, 0x23, 0x57, 0x33, 0x67, 0x33, 0xea, 0xeb, 0x3f, 0xad, 0xce, + 0xaa, 0x6f, 0x9d, 0xe2, 0x30, 0xd8, 0x2e, 0xb6, 0x25, 0xf6, 0x5c, 0x6b, 0x7d, 0x53, 0xba, 0xe7, + 0xf4, 0x96, 0xb8, 0xc9, 0x80, 0x28, 0x70, 0xe4, 0x3c, 0x14, 0x2d, 0x93, 0x76, 0x5d, 0x87, 0x5a, + 0x95, 0xe9, 0xd0, 0x73, 0xb1, 0x22, 0x61, 0xa8, 0xb1, 0xe4, 0x6d, 0x28, 0xd8, 0x5c, 0x95, 0xab, + 0x1c, 0xe3, 0x03, 0x93, 0x4d, 0xbf, 0x14, 0xda, 0x60, 0x0d, 0xd8, 0x59, 0x23, 0x7e, 0xa3, 0x64, + 0x4b, 0x1a, 0x30, 0xe9, 0xf6, 0x03, 0x2e, 0x61, 0x86, 0x4b, 0xc8, 0xe6, 0xef, 0xdb, 0x10, 0x3c, + 0x44, 0x72, 0xa0, 0xfc, 0x40, 0xc5, 0x99, 0xb5, 0xb7, 0xd1, 0xb6, 0x3b, 0x96, 0x47, 0x9d, 0xca, + 0x2c, 0xd7, 0x59, 0x79, 0x7b, 0x97, 0x25, 0x0c, 0x35, 0x96, 0xfc, 0x3f, 0x98, 0x76, 0xfb, 0x01, + 0x5f, 0x25, 0x6c, 0x94, 0xfd, 0xca, 0x1c, 0x27, 0x9f, 0x63, 0x6b, 0x76, 0x23, 0x8a, 0xc0, 0x38, + 0x1d, 0xdb, 0x37, 0xdb, 0xae, 0x1f, 0xb0, 0x0f, 0xbe, 0x75, 0x9c, 0x8a, 0xef, 0x9b, 0x97, 0x23, + 0x38, 0x8c, 0x51, 0x92, 0x6f, 0xe4, 0x60, 0xae, 0x9b, 0x54, 0xc1, 0x2a, 0xa7, 0x79, 0x67, 0xac, + 0x66, 0x3c, 0xec, 0x13, 0xdc, 0x44, 0x64, 0x66, 0x00, 0x8c, 0x83, 0x72, 0xe7, 0x57, 0xe0, 0x54, + 0xfa, 0x9c, 0xbe, 0xdf, 0x31, 0x3e, 0x1e, 0x3d, 0xc6, 0x8f, 0xc1, 0x54, 0x34, 0xbf, 0x98, 0x7b, + 0x2c, 0x23, 0x69, 0x69, 0xc4, 0x85, 0x92, 0x5b, 0x3f, 0x0a, 0x8f, 0xe5, 0x46, 0x7d, 0xc0, 0x63, + 0xa9, 0x41, 0x18, 0xca, 0xb8, 0x9f, 0xc7, 0xf2, 0x2f, 0xc7, 0x20, 0x2c, 0xc7, 0x4c, 0x56, 0xea, + 0x58, 0x3d, 0xd7, 0x76, 0x82, 0xa4, 0xaf, 0xf7, 0x92, 0x84, 0xa3, 0xa6, 0x88, 0xf8, 0x37, 0xc7, + 0xee, 0xe9, 0xdf, 0x6c, 0xc3, 0x8c, 0xc9, 0x63, 0x9a, 0xa1, 0x63, 0x6a, 0xfc, 0x81, 0x1c, 0x53, + 0x3a, 0xbf, 0x2c, 0xce, 0x05, 0x93, 0x6c, 0x99, 0x24, 0x3f, 0x2c, 0xce, 0x25, 0x4d, 0x64, 0x92, + 0x54, 0x8f, 0x73, 0xc1, 0x24, 0x5b, 0xe3, 0xaf, 0xc7, 0x40, 0xad, 0xb6, 0x5f, 0x07, 0x5b, 0x8f, + 0x18, 0x50, 0xf0, 0xa8, 0xdf, 0xef, 0x04, 0x52, 0xfb, 0xe2, 0x3b, 0x1a, 0x72, 0x08, 0x4a, 0x0c, + 0xdb, 0x6c, 0xe8, 0xbb, 0x76, 0xb0, 0xec, 0x5a, 0x4a, 0xe7, 0xe2, 0x9b, 0xcd, 0x25, 0x09, 0x43, + 0x8d, 0x35, 0x76, 0x61, 0x9a, 0xb5, 0xab, 0xd3, 0xa1, 0x9d, 0x7a, 0x40, 0x7b, 0x3e, 0x69, 0x42, + 0xde, 0x67, 0x3f, 0x64, 0xef, 0x8d, 0x98, 0xcd, 0x10, 0xd0, 0x5e, 0xc4, 0x28, 0x64, 0x7c, 0x51, + 0xb0, 0x37, 0xee, 0x8e, 0x41, 0x49, 0xf7, 0xe8, 0x21, 0x2c, 0xcd, 0x5b, 0x30, 0x69, 0xd1, 0xa6, + 0xc9, 0xda, 0x3d, 0x76, 0x9f, 0x50, 0x77, 0x3f, 0xb0, 0x3b, 0x0b, 0xe2, 0x4a, 0xc5, 0xc2, 0xba, + 0x13, 0x6c, 0x78, 0xf5, 0xc0, 0xb3, 0x9d, 0x96, 0xd8, 0x98, 0x57, 0x04, 0x13, 0x54, 0xdc, 0xc8, + 0xeb, 0x51, 0x27, 0x47, 0x16, 0xb6, 0x03, 0x97, 0x31, 0xc8, 0x0e, 0x94, 0xf8, 0x8f, 0x55, 0x95, + 0x5e, 0x9f, 0x75, 0x16, 0xde, 0x54, 0x5c, 0x84, 0x7f, 0x50, 0x7f, 0x62, 0xc8, 0x3f, 0x91, 0x16, + 0x9f, 0x3f, 0x4c, 0x5a, 0xbc, 0xb1, 0x0a, 0xec, 0x30, 0x5e, 0x5b, 0x26, 0x2f, 0x41, 0xd1, 0x97, + 0x1b, 0xa4, 0xec, 0xfb, 0x8f, 0xe8, 0x90, 0x92, 0x84, 0xdf, 0xdd, 0xaf, 0x4e, 0x73, 0x62, 0x05, + 0x40, 0x5d, 0xc4, 0xf8, 0xea, 0x04, 0x44, 0xcc, 0x8e, 0x43, 0x8c, 0xa2, 0x95, 0xb0, 0x24, 0x5f, + 0xcd, 0x6a, 0x49, 0x2a, 0xf3, 0x4c, 0x4c, 0xff, 0xb8, 0xf1, 0xc8, 0xea, 0xd1, 0xa6, 0x9d, 0x9e, + 0x5c, 0x20, 0xba, 0x1e, 0x97, 0x69, 0xa7, 0x87, 0x1c, 0xa3, 0xc3, 0x9f, 0x13, 0x43, 0xc3, 0x9f, + 0x6f, 0x40, 0xbe, 0x65, 0xf6, 0x5b, 0x54, 0x3a, 0xfe, 0xb2, 0x59, 0xf9, 0x3c, 0x92, 0x23, 0x26, + 0x08, 0xff, 0x89, 0x82, 0x27, 0x9b, 0x20, 0x6d, 0xe5, 0x65, 0x93, 0x56, 0x4a, 0xb6, 0x09, 0xa2, + 0x7d, 0x75, 0x62, 0x82, 0xe8, 0x4f, 0x0c, 0xf9, 0x33, 0xf5, 0xa6, 0x21, 0x92, 0xe2, 0x64, 0x14, + 0xe2, 0xb3, 0x19, 0xa3, 0xb8, 0x9c, 0x87, 0x58, 0x45, 0xf2, 0x03, 0x15, 0x67, 0x63, 0x11, 0xca, + 0x91, 0x84, 0x72, 0xd6, 0xbf, 0x3a, 0xe5, 0x2b, 0xd2, 0xbf, 0x2b, 0x66, 0x60, 0x22, 0xc7, 0x18, + 0xdf, 0x1d, 0x07, 0xad, 0x4c, 0x46, 0xe3, 0xb3, 0x66, 0x23, 0x92, 0xad, 0x1b, 0x4b, 0x16, 0x71, + 0x1d, 0x94, 0x58, 0x66, 0xda, 0x74, 0xa9, 0xd7, 0xd2, 0x87, 0xbb, 0x3c, 0xf6, 0xb4, 0x69, 0x73, + 0x2d, 0x8a, 0xc4, 0x38, 0x2d, 0x3b, 0x5a, 0xbb, 0xa6, 0x63, 0x37, 0xa9, 0x1f, 0x24, 0x1d, 0xea, + 0xd7, 0x24, 0x1c, 0x35, 0x05, 0x59, 0x83, 0x39, 0x9f, 0x06, 0x1b, 0xbb, 0x0e, 0xf5, 0x74, 0x12, + 0x8b, 0xcc, 0x6a, 0x7a, 0x5c, 0x69, 0xd8, 0xf5, 0x24, 0x01, 0x0e, 0x96, 0xe1, 0x66, 0xa2, 0x48, + 0x28, 0xd2, 0xc9, 0x21, 0x72, 0xb5, 0x86, 0x66, 0x62, 0x02, 0x8f, 0x03, 0x25, 0x18, 0x97, 0xa6, + 0x69, 0x77, 0xfa, 0x1e, 0x0d, 0xb9, 0x14, 0xe2, 0x5c, 0x56, 0x13, 0x78, 0x1c, 0x28, 0xc1, 0x63, + 0x71, 0x1d, 0xb3, 0xe5, 0x57, 0x26, 0x23, 0xb1, 0x38, 0x06, 0x40, 0x01, 0x37, 0xfe, 0x25, 0x07, + 0xd3, 0x48, 0x03, 0x6f, 0x4f, 0xf7, 0x5a, 0x15, 0xf2, 0x1d, 0x9e, 0xe0, 0x94, 0xe3, 0x09, 0x4e, + 0xbc, 0x88, 0xc8, 0x67, 0x12, 0x70, 0xb2, 0x02, 0x65, 0x8f, 0x95, 0x90, 0xc9, 0x64, 0x62, 0x44, + 0x0c, 0xe5, 0x1a, 0xc0, 0x10, 0x75, 0x37, 0xfe, 0x89, 0xd1, 0x62, 0xc4, 0x81, 0xc9, 0x6d, 0x91, + 0xbc, 0x2d, 0xf7, 0xe3, 0x6c, 0x93, 0x55, 0x26, 0x80, 0x73, 0x2f, 0xbc, 0xca, 0x06, 0xbf, 0x1b, + 0xfe, 0x44, 0x25, 0xc4, 0xf8, 0x4e, 0x0e, 0x20, 0xbc, 0xff, 0x42, 0x76, 0xa0, 0xe8, 0x5f, 0x8c, + 0xe9, 0x84, 0x19, 0xf3, 0x30, 0x24, 0x93, 0x48, 0x84, 0x5e, 0x42, 0x50, 0x0b, 0xb8, 0x9f, 0x42, + 0xf8, 0xe1, 0x38, 0xe8, 0x52, 0x0f, 0x49, 0x1f, 0x7c, 0x86, 0xe9, 0x12, 0xad, 0x30, 0x89, 0x5d, + 0xd3, 0x21, 0x87, 0xa2, 0xc4, 0x32, 0x7d, 0x42, 0x85, 0x09, 0xe5, 0xdc, 0xe7, 0xfa, 0x84, 0x8a, + 0x28, 0xa2, 0xc6, 0xa6, 0x69, 0x98, 0xf9, 0x47, 0xa6, 0x61, 0x16, 0x1e, 0x8a, 0x86, 0x49, 0x9e, + 0x85, 0x49, 0xcf, 0xed, 0xd0, 0x25, 0xbc, 0x2e, 0xbd, 0x21, 0xda, 0x4b, 0x81, 0x02, 0x8c, 0x0a, + 0x4f, 0x9e, 0x87, 0x72, 0xdf, 0xa7, 0xf5, 0x95, 0x2b, 0xcb, 0x1e, 0xb5, 0x7c, 0x19, 0x81, 0xd5, + 0xfe, 0xb1, 0x1b, 0x21, 0x0a, 0xa3, 0x74, 0xc6, 0xef, 0xe5, 0xe0, 0x58, 0xbd, 0xe1, 0xd9, 0xbd, + 0x40, 0x6f, 0x85, 0xd7, 0xf9, 0x25, 0x81, 0xc0, 0x64, 0xfa, 0x87, 0x9c, 0x8a, 0x4f, 0x0e, 0x09, + 0x3e, 0x09, 0xa2, 0xd8, 0x85, 0x16, 0x01, 0xc2, 0x90, 0x05, 0x1b, 0x6a, 0xb1, 0xd9, 0x26, 0xa7, + 0x44, 0x9d, 0x43, 0x51, 0x62, 0x8d, 0xdb, 0x30, 0x5b, 0xa7, 0x5d, 0xb3, 0xd7, 0xe6, 0xc1, 0xe0, + 0x8e, 0x65, 0x3b, 0x2d, 0xb2, 0x08, 0x25, 0x5f, 0xc1, 0x92, 0xb7, 0x67, 0x34, 0x31, 0x86, 0x34, + 0xe4, 0x69, 0x98, 0x6c, 0xf3, 0x58, 0x8d, 0x0a, 0x6f, 0xf2, 0x43, 0x43, 0x84, 0x6f, 0x7c, 0x54, + 0x38, 0x63, 0x17, 0xa6, 0xc2, 0xe2, 0xb4, 0x49, 0x5a, 0x30, 0xd3, 0x88, 0xc4, 0xd2, 0x90, 0x36, + 0x1f, 0xf8, 0x7a, 0x04, 0x8f, 0x23, 0x2e, 0xc7, 0x99, 0x60, 0x92, 0xab, 0xf1, 0xcb, 0x1c, 0xcc, + 0x68, 0xc9, 0xd2, 0x31, 0xda, 0x13, 0x75, 0xb6, 0x9d, 0x96, 0x54, 0x7d, 0x2f, 0x65, 0xcc, 0xc0, + 0x8a, 0x77, 0x5e, 0x38, 0x59, 0x24, 0x00, 0x95, 0x18, 0x26, 0x71, 0xd7, 0xb4, 0x03, 0x26, 0x71, + 0xec, 0xa1, 0x48, 0xbc, 0x25, 0xb8, 0xa3, 0x12, 0x63, 0xfc, 0xe9, 0x18, 0x14, 0x75, 0x0a, 0xd8, + 0xeb, 0x90, 0xe7, 0xa7, 0xf7, 0x7d, 0x13, 0xdd, 0xef, 0xa9, 0xf8, 0x72, 0x4d, 0x00, 0x05, 0x27, + 0xc6, 0x92, 0x3b, 0xd5, 0x32, 0xab, 0xe8, 0x25, 0x61, 0x27, 0x98, 0x5e, 0x80, 0x82, 0x13, 0xb9, + 0x02, 0xe3, 0xd4, 0xb1, 0x32, 0x2b, 0xe7, 0xfc, 0x7e, 0xd8, 0x25, 0xc7, 0x42, 0xc6, 0x85, 0xdf, + 0x23, 0x70, 0xbd, 0xae, 0x19, 0x48, 0xc5, 0x2f, 0xbc, 0x47, 0xc0, 0xa1, 0x28, 0xb1, 0xc6, 0xef, + 0x8f, 0x41, 0xa1, 0xde, 0xdf, 0x66, 0xc7, 0xda, 0x1f, 0xe5, 0xe0, 0x78, 0xd2, 0xbd, 0x1a, 0x4e, + 0xcc, 0xcb, 0x47, 0x72, 0xcf, 0x05, 0x69, 0xb3, 0xf6, 0x84, 0xac, 0xca, 0xf1, 0x14, 0x24, 0xa6, + 0xd5, 0x80, 0x29, 0x91, 0x61, 0xc2, 0xe7, 0xd8, 0x91, 0x24, 0x7c, 0x4e, 0x0f, 0x4b, 0xf6, 0x34, + 0xfe, 0x76, 0x02, 0x40, 0xf4, 0xc8, 0x46, 0x2f, 0x38, 0x8c, 0xa6, 0xff, 0x02, 0x4c, 0xa9, 0xeb, + 0xec, 0xd7, 0xc3, 0x98, 0x82, 0x76, 0x46, 0xad, 0x45, 0x70, 0x18, 0xa3, 0x64, 0x06, 0x0d, 0x75, + 0x02, 0x6f, 0x4f, 0x1c, 0x76, 0x13, 0x71, 0x83, 0xe6, 0x92, 0xc6, 0x60, 0x84, 0x8a, 0x2c, 0xc4, + 0x0c, 0x7f, 0x91, 0x14, 0x7a, 0xec, 0x1e, 0x46, 0xfb, 0x8b, 0x30, 0xad, 0xbf, 0x56, 0xed, 0x8e, + 0x8a, 0xb3, 0x6b, 0x05, 0x72, 0x33, 0x8a, 0xc4, 0x38, 0x2d, 0x79, 0x19, 0x8e, 0xc5, 0xb3, 0xb7, + 0xe4, 0xb1, 0x70, 0x4a, 0x96, 0x3e, 0x16, 0x4f, 0xfa, 0xc2, 0x04, 0x35, 0x9b, 0x85, 0x96, 0xb7, + 0x87, 0x7d, 0x47, 0x9e, 0x0f, 0x7a, 0x16, 0xae, 0x70, 0x28, 0x4a, 0x2c, 0xeb, 0x42, 0x56, 0x92, + 0x7a, 0x02, 0xce, 0x5d, 0xe4, 0xc5, 0xb0, 0x0b, 0xeb, 0x11, 0x1c, 0xc6, 0x28, 0x99, 0x04, 0x69, + 0x66, 0x41, 0x7c, 0x9e, 0x27, 0x0c, 0xa5, 0x1e, 0x1c, 0x73, 0xe3, 0x9a, 0xad, 0xf0, 0x7d, 0x7f, + 0xea, 0x90, 0x69, 0xe4, 0xb1, 0xb2, 0x22, 0x3d, 0x2a, 0xa1, 0x08, 0x27, 0xf8, 0x1b, 0xc7, 0x61, + 0xae, 0xde, 0xef, 0xf5, 0x3a, 0x36, 0xb5, 0xb4, 0x35, 0x6b, 0xbc, 0x02, 0x33, 0xf2, 0x62, 0x80, + 0x3e, 0xfe, 0x1e, 0xe8, 0xe6, 0x9e, 0xf1, 0x25, 0xb6, 0x9f, 0xef, 0x39, 0x8d, 0xb6, 0xe7, 0x3a, + 0xd2, 0x97, 0x48, 0x9c, 0xe4, 0xa1, 0x95, 0xd5, 0x99, 0x11, 0x3d, 0xa2, 0xc4, 0x0a, 0x49, 0x3b, + 0xf3, 0x8c, 0xaf, 0xe7, 0xe0, 0x64, 0xa2, 0x0e, 0xf2, 0x64, 0x79, 0x67, 0xb0, 0x26, 0x2b, 0xa3, + 0xd5, 0x44, 0xba, 0x54, 0x87, 0x57, 0xe6, 0x3f, 0x73, 0x50, 0xde, 0xda, 0xba, 0xaa, 0xb5, 0x77, + 0x84, 0x53, 0xbe, 0xb8, 0x1a, 0xb1, 0xd4, 0x0c, 0xa8, 0xb7, 0xec, 0x76, 0x7b, 0x1d, 0xaa, 0x3b, + 0x57, 0xde, 0x57, 0xa8, 0xa7, 0x52, 0xe0, 0x90, 0x92, 0x64, 0x1d, 0x8e, 0x47, 0x31, 0xd2, 0x78, + 0x91, 0x77, 0x27, 0x45, 0x0a, 0xdb, 0x20, 0x1a, 0xd3, 0xca, 0x24, 0x59, 0x49, 0x0b, 0x46, 0xbe, + 0x5b, 0x30, 0xc0, 0x4a, 0xa2, 0x31, 0xad, 0x8c, 0xb1, 0x01, 0xe5, 0xc8, 0xa3, 0x15, 0xe4, 0x55, + 0x98, 0x6d, 0xb8, 0xdd, 0x9e, 0x47, 0x7d, 0xdf, 0x76, 0x9d, 0xab, 0xf4, 0x0e, 0xed, 0xc8, 0x26, + 0xf3, 0x8b, 0x0f, 0xcb, 0x09, 0x1c, 0x0e, 0x50, 0x1b, 0xdf, 0x7b, 0x02, 0x74, 0xba, 0xfd, 0x6f, + 0x92, 0xf6, 0x33, 0x45, 0x48, 0x1b, 0x3a, 0x82, 0x93, 0x1f, 0x3d, 0x82, 0xa3, 0xf7, 0xb2, 0x44, + 0x14, 0xa7, 0x15, 0x46, 0x71, 0x0a, 0x47, 0x10, 0xc5, 0xd1, 0x4a, 0xd4, 0x40, 0x24, 0xe7, 0x6b, + 0x39, 0x98, 0x72, 0x5c, 0x8b, 0x2a, 0x9d, 0x93, 0x1b, 0xd1, 0xe5, 0x0b, 0x1b, 0x23, 0x75, 0xa2, + 0x08, 0xe8, 0x49, 0x8e, 0x22, 0x80, 0xa7, 0x37, 0xfa, 0x28, 0x0a, 0x63, 0xa2, 0xc9, 0x2a, 0x14, + 0xcd, 0x66, 0xd3, 0x76, 0xec, 0x60, 0x4f, 0xde, 0x1b, 0x38, 0x93, 0xa6, 0x2a, 0x2f, 0x49, 0x1a, + 0x61, 0xb6, 0xa9, 0x2f, 0xd4, 0x65, 0x99, 0xdd, 0xab, 0xaf, 0xe9, 0x95, 0x46, 0xb0, 0x7b, 0x55, + 0x8e, 0x47, 0xc4, 0xa5, 0xa2, 0xae, 0x14, 0x85, 0xb7, 0xf6, 0x0c, 0x28, 0x88, 0xe0, 0x1e, 0x3f, + 0x9d, 0x8a, 0xc2, 0x85, 0x27, 0x02, 0x7f, 0x28, 0x31, 0xa4, 0xa5, 0xdc, 0xd0, 0x65, 0xde, 0xb9, + 0xb5, 0xcc, 0x4e, 0x7c, 0xed, 0xd9, 0x4e, 0xf7, 0x43, 0x93, 0xd7, 0xa2, 0x76, 0xd6, 0xd4, 0x61, + 0xec, 0xac, 0xe9, 0xa1, 0x36, 0x56, 0x0b, 0x0a, 0x3e, 0xb7, 0xe2, 0x78, 0x44, 0xb3, 0x7c, 0x61, + 0x39, 0xdb, 0x2e, 0x1f, 0x33, 0x04, 0x45, 0xef, 0x08, 0x18, 0x4a, 0xf6, 0xc4, 0x85, 0xa2, 0x0a, + 0xbb, 0xca, 0xa0, 0x68, 0x36, 0xd3, 0x21, 0xe9, 0x80, 0x53, 0xd9, 0xe3, 0x02, 0x8a, 0x5a, 0x08, + 0x79, 0x03, 0xc6, 0x2d, 0xb3, 0x25, 0xc3, 0xa3, 0xaf, 0x66, 0xbe, 0x0e, 0xa1, 0xc4, 0x70, 0xad, + 0x7c, 0x65, 0x69, 0x0d, 0x19, 0x57, 0xb2, 0x13, 0x5e, 0x17, 0x9c, 0x1d, 0xe5, 0x74, 0x8c, 0xab, + 0x10, 0xc2, 0xe6, 0x1c, 0xb8, 0x70, 0x78, 0x09, 0x26, 0xef, 0xb8, 0x9d, 0x7e, 0x57, 0xc6, 0x55, + 0xcb, 0x17, 0xe6, 0xd3, 0x46, 0xfb, 0x26, 0x27, 0x09, 0x37, 0x01, 0xf1, 0xed, 0xa3, 0x2a, 0x4b, + 0xbe, 0x9c, 0x83, 0x63, 0x6c, 0xe9, 0xe8, 0x79, 0xe0, 0x57, 0xc8, 0x08, 0x33, 0xf5, 0x86, 0xcf, + 0x8e, 0x56, 0x35, 0xc3, 0xb4, 0x22, 0xb9, 0x1e, 0x93, 0x80, 0x09, 0x89, 0xa4, 0x07, 0x45, 0xdf, + 0xb6, 0x68, 0xc3, 0xf4, 0xfc, 0xca, 0xf1, 0x23, 0x93, 0x1e, 0xba, 0xac, 0x24, 0x6f, 0xd4, 0x52, + 0xc8, 0xef, 0xf2, 0x07, 0x2c, 0xe4, 0xe3, 0x33, 0xf2, 0x41, 0xa0, 0x13, 0x47, 0xf9, 0x20, 0xd0, + 0x71, 0xf1, 0x7a, 0x45, 0x4c, 0x02, 0x26, 0x45, 0x92, 0x0d, 0x38, 0x29, 0xae, 0x0d, 0x26, 0xef, + 0x8c, 0x9e, 0xe4, 0x77, 0x46, 0x1f, 0x3f, 0xd8, 0xaf, 0x9e, 0x5c, 0x4a, 0x23, 0xc0, 0xf4, 0x72, + 0xe4, 0x3d, 0x98, 0xf6, 0xa2, 0xee, 0x4e, 0x1e, 0x3b, 0xcf, 0xda, 0x9d, 0x31, 0xc7, 0xa9, 0x88, + 0xdb, 0xc7, 0x40, 0x18, 0x97, 0x45, 0x9e, 0x83, 0x72, 0x4f, 0xee, 0x54, 0xb6, 0xdf, 0xe5, 0x61, + 0xf7, 0x71, 0x71, 0xa2, 0x6e, 0x86, 0x60, 0x8c, 0xd2, 0x90, 0x1b, 0x50, 0x0e, 0xdc, 0x0e, 0xf5, + 0x64, 0x62, 0x60, 0x85, 0x0f, 0xfe, 0xd9, 0xb4, 0x99, 0xbc, 0xa5, 0xc9, 0x42, 0x3f, 0x54, 0x08, + 0xf3, 0x31, 0xca, 0x87, 0x99, 0x45, 0xea, 0x8a, 0x30, 0x4f, 0xb3, 0xad, 0x3c, 0x1e, 0x37, 0x8b, + 0xea, 0x51, 0x24, 0xc6, 0x69, 0xc9, 0x1a, 0xcc, 0xf5, 0x3c, 0xdb, 0xf5, 0xec, 0x60, 0x6f, 0xb9, + 0x63, 0xfa, 0x3e, 0x67, 0x30, 0xcf, 0x19, 0x68, 0x4f, 0xf9, 0x66, 0x92, 0x00, 0x07, 0xcb, 0x90, + 0xf3, 0x50, 0x54, 0xc0, 0xca, 0x13, 0x5c, 0x57, 0xe3, 0xdb, 0x92, 0x2a, 0x8b, 0x1a, 0x3b, 0xe4, + 0x46, 0xd3, 0x99, 0x2c, 0x37, 0x9a, 0x88, 0x05, 0x67, 0xcc, 0x7e, 0xe0, 0xf2, 0x34, 0xe4, 0x78, + 0x91, 0x2d, 0x77, 0x87, 0x3a, 0x95, 0x73, 0xfc, 0xac, 0x3a, 0x77, 0xb0, 0x5f, 0x3d, 0xb3, 0x74, + 0x0f, 0x3a, 0xbc, 0x27, 0x17, 0xd2, 0x85, 0x22, 0x95, 0xb7, 0xb2, 0x2a, 0x1f, 0x19, 0xe1, 0x90, + 0x88, 0x5f, 0xed, 0x52, 0xe1, 0x5d, 0x01, 0x43, 0x2d, 0x82, 0x6c, 0x41, 0xb9, 0xed, 0xfa, 0xc1, + 0x52, 0xc7, 0x36, 0x7d, 0xea, 0x57, 0x9e, 0xe4, 0xf3, 0x24, 0xf5, 0x7c, 0xbb, 0xac, 0xc8, 0xc2, + 0x69, 0x72, 0x39, 0x2c, 0x89, 0x51, 0x36, 0x84, 0x72, 0xd7, 0x6b, 0x9f, 0x8f, 0x9a, 0xeb, 0x04, + 0xf4, 0xdd, 0xa0, 0x72, 0x96, 0xb7, 0xe5, 0x99, 0x34, 0xce, 0x9b, 0xae, 0x55, 0x8f, 0x53, 0x8b, + 0x55, 0x9e, 0x00, 0x62, 0x92, 0x27, 0xb3, 0x7f, 0x7b, 0xae, 0x55, 0xef, 0xd1, 0xc6, 0xa6, 0x19, + 0x34, 0xda, 0x95, 0x6a, 0xdc, 0x85, 0xb0, 0x19, 0xc1, 0x61, 0x8c, 0x92, 0xd9, 0x13, 0x1e, 0xf5, + 0xb9, 0xbb, 0x62, 0x93, 0x3a, 0x96, 0xed, 0xb4, 0x36, 0x5d, 0xcb, 0xaf, 0x18, 0x7c, 0x08, 0xb9, + 0x3d, 0x81, 0x83, 0x68, 0x4c, 0x2b, 0x43, 0x1a, 0x30, 0xd9, 0x15, 0xa9, 0x9b, 0x95, 0xa7, 0x46, + 0x50, 0x2b, 0x65, 0xfa, 0xa7, 0x38, 0x94, 0xe4, 0x07, 0x2a, 0xce, 0xe4, 0xdb, 0x39, 0x98, 0xf1, + 0xe3, 0xb6, 0x63, 0xe5, 0xa3, 0xa3, 0x1c, 0x85, 0x71, 0x5e, 0xb5, 0x67, 0x78, 0x7f, 0xc7, 0x81, + 0x77, 0x07, 0x41, 0x98, 0xac, 0x84, 0x68, 0x3d, 0xcf, 0x8a, 0xae, 0x3c, 0x3d, 0x52, 0xeb, 0x39, + 0x0f, 0xd5, 0x7a, 0xfe, 0x81, 0x8a, 0xf3, 0xfc, 0x2b, 0x30, 0x37, 0xa0, 0xfd, 0x3e, 0x50, 0xc6, + 0xee, 0xcf, 0x98, 0xb5, 0x1b, 0xb1, 0x37, 0x8e, 0xda, 0x4a, 0x5b, 0x83, 0x39, 0xf9, 0xbc, 0x23, + 0x53, 0x8d, 0x3a, 0x7d, 0xfd, 0xec, 0x4f, 0x24, 0x06, 0x88, 0x49, 0x02, 0x1c, 0x2c, 0xc3, 0x66, + 0x74, 0x43, 0xbc, 0xfb, 0x22, 0x32, 0x32, 0x27, 0xe2, 0x1e, 0x9d, 0xe5, 0x08, 0x0e, 0x63, 0x94, + 0xc6, 0x9f, 0xe5, 0x60, 0x3a, 0x76, 0x4c, 0x1f, 0x79, 0x80, 0x60, 0x15, 0x48, 0xd7, 0xf6, 0x3c, + 0xd7, 0x13, 0xba, 0xce, 0x35, 0xb6, 0x67, 0xf9, 0xf2, 0x52, 0x21, 0xbf, 0xcc, 0x72, 0x6d, 0x00, + 0x8b, 0x29, 0x25, 0x8c, 0x3f, 0x1f, 0x87, 0x30, 0x51, 0x41, 0xdf, 0xe0, 0xca, 0x0d, 0xbd, 0xc1, + 0xf5, 0x71, 0x28, 0xde, 0xf6, 0x5d, 0x67, 0x33, 0xbc, 0xe7, 0xa5, 0x87, 0xe2, 0xb5, 0xfa, 0xc6, + 0x75, 0x4e, 0xa9, 0x29, 0x38, 0xf5, 0x3b, 0xab, 0x76, 0x27, 0x18, 0xbc, 0x0d, 0xf5, 0xda, 0xeb, + 0x02, 0x8e, 0x9a, 0x82, 0x3f, 0x2e, 0x73, 0x87, 0x6a, 0x07, 0x5d, 0xf8, 0xb8, 0x0c, 0x03, 0xa2, + 0xc0, 0x91, 0x45, 0x28, 0x69, 0xff, 0x9e, 0x74, 0x37, 0xea, 0x9e, 0xd2, 0x7e, 0x40, 0x0c, 0x69, + 0xb8, 0xda, 0x25, 0x7d, 0x58, 0xd2, 0xd4, 0x5c, 0xcd, 0xa8, 0xb0, 0x26, 0x1c, 0x61, 0x62, 0x1b, + 0x57, 0x60, 0xd4, 0x52, 0xa2, 0xc9, 0x2f, 0xf9, 0xa3, 0x4c, 0x7e, 0x31, 0xbe, 0x32, 0x0e, 0x93, + 0x37, 0xa9, 0xc7, 0x6f, 0x70, 0x3e, 0x0b, 0x93, 0x77, 0xc4, 0x4f, 0x39, 0x5a, 0xa1, 0xf6, 0x2b, + 0xc0, 0xa8, 0xf0, 0xac, 0xcb, 0xb6, 0xfb, 0x76, 0xc7, 0x5a, 0x09, 0xd7, 0x8f, 0xee, 0xb2, 0x9a, + 0x42, 0x60, 0x48, 0xc3, 0x0a, 0xb4, 0x98, 0xea, 0xda, 0xed, 0xda, 0x41, 0xf2, 0x3e, 0xc6, 0x9a, + 0x42, 0x60, 0x48, 0x43, 0x9e, 0x81, 0x42, 0xcb, 0x0e, 0xb6, 0xcc, 0x56, 0xd2, 0x53, 0xbf, 0xc6, + 0xa1, 0x28, 0xb1, 0xdc, 0xcd, 0x6c, 0x07, 0x5b, 0x1e, 0xe5, 0x3e, 0xb1, 0x81, 0x5c, 0xf1, 0xb5, + 0x08, 0x0e, 0x63, 0x94, 0xbc, 0x4a, 0xae, 0x6c, 0x99, 0x74, 0xff, 0x86, 0x55, 0x52, 0x08, 0x0c, + 0x69, 0xd8, 0xd4, 0x6b, 0xb8, 0xdd, 0x9e, 0xdd, 0x91, 0x89, 0x14, 0x91, 0xa9, 0xb7, 0x2c, 0xe1, + 0xa8, 0x29, 0x18, 0x35, 0xdb, 0x3c, 0x9a, 0xae, 0xd7, 0x4d, 0xbe, 0xe6, 0xb1, 0x29, 0xe1, 0xa8, + 0x29, 0x8c, 0x2f, 0xc2, 0xb4, 0x0c, 0xd6, 0x48, 0x9f, 0xe1, 0x4e, 0x18, 0x41, 0x1b, 0xe5, 0xfd, + 0xd6, 0xc8, 0x8d, 0xa9, 0x78, 0x20, 0x2a, 0x16, 0x87, 0xfb, 0xfe, 0x18, 0x14, 0x1f, 0xe1, 0x13, + 0x48, 0x8d, 0xd8, 0x13, 0x48, 0x47, 0xf0, 0x5e, 0x4e, 0xda, 0xf3, 0x47, 0x3b, 0x89, 0xe7, 0x8f, + 0x96, 0x47, 0x4c, 0x64, 0xbb, 0xe7, 0xd3, 0x47, 0xff, 0x9a, 0x03, 0x9d, 0x99, 0xcf, 0xf7, 0x96, + 0x9a, 0xcd, 0x35, 0x88, 0x47, 0xd0, 0x99, 0x6e, 0xac, 0x33, 0xaf, 0x8d, 0xd4, 0xca, 0x68, 0xd5, + 0x87, 0xbe, 0xbd, 0xf6, 0x61, 0x0e, 0x2a, 0x69, 0x05, 0x1e, 0xc1, 0x73, 0x4f, 0x4e, 0xfc, 0xb9, + 0xa7, 0xf5, 0x23, 0x6b, 0xec, 0x90, 0x67, 0x9f, 0x7e, 0x32, 0xa4, 0xa9, 0xfc, 0xc1, 0xa5, 0xb7, + 0xd5, 0xd9, 0x92, 0x1b, 0x21, 0x41, 0x4c, 0x70, 0x4d, 0x3f, 0x97, 0xde, 0x86, 0x82, 0x50, 0x47, + 0xe5, 0xd8, 0xbe, 0x98, 0xf1, 0x90, 0x61, 0x2c, 0xa4, 0x17, 0x89, 0xff, 0x46, 0xc9, 0xd6, 0xf8, + 0x51, 0x0e, 0xa6, 0x1e, 0xe1, 0x63, 0x5d, 0xdb, 0xf1, 0xd1, 0x7b, 0x69, 0xa4, 0xd1, 0x1b, 0x32, + 0x62, 0x5f, 0x7b, 0x1c, 0x62, 0x8f, 0x64, 0x11, 0x07, 0x4a, 0x4a, 0x8d, 0x53, 0x29, 0xad, 0x2f, + 0x8d, 0xe4, 0xa8, 0x0d, 0x0f, 0x09, 0x05, 0xf1, 0x31, 0x14, 0x91, 0x08, 0x5e, 0x8e, 0x1d, 0x2a, + 0x78, 0xf9, 0xc8, 0x83, 0x00, 0xe9, 0x66, 0xf3, 0xc4, 0x43, 0x31, 0x9b, 0xcf, 0x1c, 0xb9, 0xd9, + 0xfc, 0xe4, 0xc3, 0x37, 0x9b, 0x23, 0x4e, 0xc2, 0xfc, 0x08, 0x4e, 0xc2, 0xf7, 0xe0, 0x84, 0xf8, + 0xb9, 0xdc, 0x31, 0xed, 0xae, 0x9e, 0x2f, 0xf2, 0x05, 0xa2, 0x67, 0x53, 0x8d, 0x65, 0xa6, 0x6c, + 0xf8, 0x01, 0x75, 0x82, 0x9b, 0x61, 0xc9, 0xf0, 0xfa, 0xd7, 0xcd, 0x14, 0x76, 0x98, 0x2a, 0x24, + 0xe9, 0x55, 0x9a, 0x3c, 0x84, 0x57, 0xe9, 0xbb, 0x39, 0x38, 0x69, 0xa6, 0xbd, 0x9f, 0x2b, 0x63, + 0x0b, 0xaf, 0x8d, 0xe4, 0xe3, 0x8b, 0x71, 0x94, 0x3e, 0xba, 0x34, 0x14, 0xa6, 0xd7, 0x81, 0x3c, + 0x1d, 0xba, 0x89, 0x45, 0x24, 0x3c, 0xdd, 0xc1, 0xfb, 0xcd, 0x64, 0x78, 0x06, 0x78, 0x6f, 0xd7, + 0x47, 0x56, 0x33, 0x8e, 0x20, 0x44, 0x53, 0x1e, 0x21, 0x44, 0x93, 0x70, 0xf9, 0x4d, 0x1d, 0x91, + 0xcb, 0xcf, 0x81, 0x59, 0xbb, 0x6b, 0xb6, 0xe8, 0x66, 0xbf, 0xd3, 0x11, 0xf9, 0x6e, 0x7e, 0x65, + 0x9a, 0xf3, 0x4e, 0x4d, 0xba, 0xba, 0xea, 0x36, 0xcc, 0x4e, 0xf2, 0x4d, 0x37, 0x9d, 0x7a, 0xba, + 0x9e, 0xe0, 0x84, 0x03, 0xbc, 0xd9, 0xb4, 0xe4, 0x57, 0x8f, 0x68, 0xc0, 0x7a, 0x9b, 0x47, 0x2f, + 0xe4, 0x0b, 0xe7, 0x97, 0x43, 0x30, 0x46, 0x69, 0xc8, 0x15, 0x28, 0x59, 0x8e, 0x2f, 0xf3, 0x4a, + 0x67, 0xf8, 0x2e, 0xf5, 0x09, 0xb6, 0xb7, 0xad, 0x5c, 0xaf, 0xeb, 0x8c, 0xd2, 0x33, 0x29, 0x77, + 0xd7, 0x34, 0x1e, 0xc3, 0xf2, 0xe4, 0x1a, 0x67, 0x26, 0x1f, 0xfe, 0x10, 0xe1, 0x86, 0x73, 0x43, + 0xbc, 0x56, 0x2b, 0xd7, 0xd5, 0x3b, 0x25, 0xd3, 0x52, 0x9c, 0x7c, 0xce, 0x23, 0xe4, 0x10, 0x79, + 0xb4, 0x6a, 0xee, 0x9e, 0x8f, 0x56, 0xdd, 0x80, 0xd3, 0x41, 0xd0, 0x89, 0x45, 0xb1, 0xe5, 0xf5, + 0x40, 0x7e, 0x57, 0x34, 0x2f, 0xde, 0x39, 0xdc, 0xda, 0xba, 0x9a, 0x46, 0x82, 0xc3, 0xca, 0xf2, + 0x70, 0x6e, 0xd0, 0xd1, 0x5e, 0xeb, 0xb3, 0xa3, 0x84, 0x73, 0xc3, 0x74, 0x01, 0x19, 0xce, 0x0d, + 0x01, 0x18, 0x95, 0x32, 0xdc, 0xfb, 0x7e, 0x3c, 0xa3, 0xf7, 0x3d, 0xea, 0xf0, 0x3d, 0x71, 0x4f, + 0x87, 0xef, 0x80, 0x83, 0xfa, 0xe4, 0x03, 0x38, 0xa8, 0xdf, 0xe0, 0xf7, 0x12, 0xd7, 0x96, 0xa5, + 0x73, 0x3f, 0x9b, 0xc6, 0xc6, 0xaf, 0x42, 0x88, 0x3c, 0x35, 0xfe, 0x13, 0x05, 0x4f, 0xb2, 0x09, + 0x27, 0x7a, 0xae, 0x35, 0xe0, 0xdf, 0xe6, 0xde, 0xfc, 0xc8, 0xfd, 0xdd, 0xcd, 0x14, 0x1a, 0x4c, + 0x2d, 0xc9, 0x37, 0xf0, 0x10, 0x5e, 0xa9, 0xf0, 0x8e, 0x11, 0x1b, 0x78, 0x08, 0xc6, 0x28, 0x4d, + 0xd2, 0xdd, 0xfb, 0xf8, 0x43, 0x73, 0xf7, 0xce, 0x3f, 0x02, 0x77, 0xef, 0x13, 0x87, 0x76, 0xf7, + 0xfe, 0x36, 0x1c, 0xef, 0xb9, 0xd6, 0x8a, 0xed, 0x7b, 0x7d, 0xfe, 0xef, 0x0a, 0xb5, 0xbe, 0xd5, + 0xa2, 0x01, 0xf7, 0x17, 0x97, 0x2f, 0x5c, 0x88, 0x56, 0x52, 0xfc, 0x89, 0xcc, 0x82, 0xfc, 0x13, + 0x19, 0xbe, 0xc8, 0x13, 0xa5, 0xb8, 0xdd, 0xc3, 0x5d, 0xc4, 0x29, 0x48, 0x4c, 0x93, 0x13, 0x75, + 0x11, 0x9f, 0x7b, 0x68, 0x2e, 0xe2, 0x57, 0xa1, 0xe8, 0xb7, 0xfb, 0x81, 0xe5, 0xee, 0x3a, 0x3c, + 0x70, 0x50, 0xd2, 0xaf, 0xc4, 0x16, 0xeb, 0x12, 0x7e, 0x77, 0xbf, 0x3a, 0xab, 0x7e, 0x47, 0x2e, + 0xeb, 0x48, 0x08, 0xf9, 0xd6, 0x90, 0x4c, 0x46, 0xe3, 0x88, 0x33, 0x19, 0x4f, 0x3f, 0x50, 0x16, + 0x63, 0x9a, 0xeb, 0xfb, 0xa9, 0x5f, 0x01, 0xd7, 0xf7, 0xe8, 0x5e, 0xe9, 0x6f, 0xcd, 0xc0, 0xb1, + 0xc4, 0x8b, 0xa9, 0xfa, 0x56, 0x7d, 0xee, 0xb0, 0xb7, 0xea, 0x63, 0xd7, 0xde, 0xc7, 0x1e, 0xea, + 0xb5, 0xf7, 0xf1, 0x23, 0xbf, 0xf6, 0x1e, 0xb9, 0xde, 0x3f, 0x71, 0x9f, 0xeb, 0xfd, 0x4b, 0x30, + 0xa3, 0x32, 0xaf, 0xa8, 0xbc, 0xf6, 0x2c, 0x5c, 0x79, 0x3a, 0x4d, 0x7f, 0x39, 0x8e, 0xc6, 0x24, + 0x3d, 0xf9, 0x22, 0xe4, 0x1d, 0x5e, 0xb0, 0x30, 0xc2, 0x33, 0x29, 0xf1, 0x01, 0xe3, 0x2a, 0x9f, + 0x7c, 0xa9, 0x44, 0x05, 0xe5, 0xf3, 0x1c, 0x76, 0x57, 0xfd, 0x40, 0x21, 0x94, 0xbc, 0x09, 0x15, + 0xb7, 0xd9, 0xec, 0xb8, 0xa6, 0x15, 0x5e, 0xcd, 0x57, 0xde, 0x45, 0x91, 0x84, 0x79, 0x4e, 0x32, + 0xa8, 0x6c, 0x0c, 0xa1, 0xc3, 0xa1, 0x1c, 0x98, 0x66, 0x3e, 0x13, 0x7f, 0x32, 0xc2, 0xaf, 0x94, + 0x78, 0x33, 0xff, 0xff, 0x51, 0x34, 0x33, 0xfe, 0x3e, 0x85, 0x6c, 0x70, 0x78, 0x41, 0x22, 0x8e, + 0xc5, 0x64, 0x4d, 0x88, 0x07, 0xa7, 0x7a, 0x69, 0x76, 0x8b, 0x2f, 0x53, 0xa3, 0xee, 0x65, 0x3d, + 0x9d, 0x95, 0x52, 0x4e, 0xa5, 0x5a, 0x3e, 0x3e, 0x0e, 0xe1, 0x1c, 0xbd, 0xb4, 0x5f, 0x7c, 0x68, + 0x97, 0xf6, 0xe3, 0x6f, 0x04, 0x4f, 0x3f, 0x8a, 0x37, 0x82, 0xc9, 0x2f, 0x52, 0xdf, 0x8a, 0x10, + 0xea, 0xfe, 0xe7, 0x8f, 0x62, 0xb0, 0x7f, 0xe5, 0xde, 0x8b, 0xf8, 0xe3, 0x1c, 0xcc, 0x8b, 0x29, + 0x95, 0xf6, 0x07, 0x10, 0x32, 0xc1, 0xe9, 0x08, 0xdc, 0xc4, 0x3c, 0x8a, 0x55, 0x8f, 0x09, 0xe2, + 0x7e, 0xcd, 0x7b, 0x08, 0x27, 0x5f, 0x4b, 0x39, 0x9e, 0x66, 0x46, 0x30, 0x86, 0x53, 0x33, 0x84, + 0xa5, 0x82, 0x74, 0xbf, 0x13, 0x69, 0x4f, 0x3c, 0xe4, 0x33, 0xf4, 0x49, 0xa3, 0x1b, 0xd1, 0xa3, + 0x28, 0xeb, 0xab, 0x42, 0xe1, 0xde, 0x13, 0x7d, 0x4e, 0xe9, 0x4b, 0x39, 0x38, 0x91, 0xb6, 0x49, + 0xa4, 0xd4, 0xa2, 0x1e, 0xaf, 0xc5, 0x68, 0xde, 0xb6, 0x68, 0x1d, 0x8e, 0xe6, 0x59, 0x88, 0x3f, + 0x2c, 0x44, 0x3c, 0x84, 0x01, 0xed, 0xfd, 0x26, 0xa5, 0x37, 0x53, 0x4a, 0x6f, 0xec, 0x45, 0xed, + 0xfc, 0x23, 0x7c, 0x51, 0xbb, 0x90, 0xe1, 0x45, 0xed, 0xc9, 0x47, 0xf9, 0xa2, 0x76, 0xf1, 0x90, + 0x2f, 0x6a, 0x97, 0x7e, 0x65, 0x5e, 0xd4, 0x36, 0x3e, 0xc8, 0xc1, 0xec, 0xff, 0xf6, 0xbf, 0x0c, + 0xfa, 0x59, 0x24, 0x44, 0xf7, 0x08, 0xff, 0x2b, 0xe8, 0x76, 0x3c, 0xe8, 0x71, 0xe9, 0x48, 0x1a, + 0x39, 0x24, 0xf8, 0xf1, 0x0e, 0xa4, 0x99, 0x5d, 0x87, 0xbb, 0xab, 0x15, 0x4b, 0x4b, 0x19, 0x3b, + 0x74, 0x5a, 0xca, 0x7f, 0xa7, 0xf4, 0x2a, 0x3f, 0x37, 0xdf, 0x7b, 0x58, 0xff, 0x8d, 0x72, 0x22, + 0xed, 0xbf, 0x51, 0x12, 0xff, 0x85, 0x92, 0xfc, 0x6f, 0x8c, 0xb1, 0x87, 0xf7, 0xdf, 0x18, 0xb5, + 0x85, 0x1f, 0x7c, 0x70, 0xf6, 0xb1, 0x1f, 0x7d, 0x70, 0xf6, 0xb1, 0x1f, 0x7f, 0x70, 0xf6, 0xb1, + 0xf7, 0x0f, 0xce, 0xe6, 0x7e, 0x70, 0x70, 0x36, 0xf7, 0xa3, 0x83, 0xb3, 0xb9, 0x1f, 0x1f, 0x9c, + 0xcd, 0xfd, 0xec, 0xe0, 0x6c, 0xee, 0x0f, 0xfe, 0xf9, 0xec, 0x63, 0x9f, 0x2f, 0xaa, 0xc6, 0xfc, + 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbb, 0xec, 0xc9, 0xf1, 0xa0, 0x77, 0x00, 0x00, } func (m *Amount) Marshal() (dAtA []byte, err error) { @@ -3969,6 +4124,34 @@ func (m *DAGTemplate) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Event) 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 *Event) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Selector) + copy(dAtA[i:], m.Selector) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Selector))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *ExecutorConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5917,7 +6100,7 @@ func (m *Sequence) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *SubmitOpts) Marshal() (dAtA []byte, err error) { +func (m *Submit) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -5927,19 +6110,19 @@ func (m *SubmitOpts) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *SubmitOpts) MarshalTo(dAtA []byte) (int, error) { +func (m *Submit) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *SubmitOpts) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *Submit) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if m.OwnerReference != nil { + if m.Arguments != nil { { - size, err := m.OwnerReference.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Arguments.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -5947,31 +6130,76 @@ func (m *SubmitOpts) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenerated(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x5a - } - i -= len(m.Labels) - copy(dAtA[i:], m.Labels) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.Labels))) - i-- - dAtA[i] = 0x52 - i-- - if m.ServerDryRun { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + dAtA[i] = 0x12 } - i-- - dAtA[i] = 0x48 - i-- - if m.DryRun { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + { + size, err := m.WorkflowTemplateRef.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x40 - i -= len(m.ServiceAccount) - copy(dAtA[i:], m.ServiceAccount) + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *SubmitOpts) 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 *SubmitOpts) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubmitOpts) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.OwnerReference != nil { + { + size, err := m.OwnerReference.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + i -= len(m.Labels) + copy(dAtA[i:], m.Labels) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Labels))) + i-- + dAtA[i] = 0x52 + i-- + if m.ServerDryRun { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + i-- + if m.DryRun { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + i -= len(m.ServiceAccount) + copy(dAtA[i:], m.ServiceAccount) i = encodeVarintGenerated(dAtA, i, uint64(len(m.ServiceAccount))) i-- dAtA[i] = 0x3a @@ -6764,6 +6992,11 @@ func (m *ValueFrom) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + i -= len(m.Event) + copy(dAtA[i:], m.Event) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Event))) + i-- + dAtA[i] = 0x3a if m.Supplied != nil { { size, err := m.Supplied.MarshalToSizedBuffer(dAtA[:i]) @@ -6960,6 +7193,141 @@ func (m *Workflow) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *WorkflowEventBinding) 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 *WorkflowEventBinding) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WorkflowEventBinding) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ObjectMeta.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 *WorkflowEventBindingList) 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 *WorkflowEventBindingList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WorkflowEventBindingList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ListMeta.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 *WorkflowEventBindingSpec) 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 *WorkflowEventBindingSpec) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WorkflowEventBindingSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Submit != nil { + { + size, err := m.Submit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Event.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 *WorkflowList) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -8328,6 +8696,17 @@ func (m *DAGTemplate) Size() (n int) { return n } +func (m *Event) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Selector) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + func (m *ExecutorConfig) Size() (n int) { if m == nil { return 0 @@ -9058,6 +9437,21 @@ func (m *Sequence) Size() (n int) { return n } +func (m *Submit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.WorkflowTemplateRef.Size() + n += 1 + l + sovGenerated(uint64(l)) + if m.Arguments != nil { + l = m.Arguments.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + func (m *SubmitOpts) Size() (n int) { if m == nil { return 0 @@ -9365,6 +9759,8 @@ func (m *ValueFrom) Size() (n int) { l = m.Supplied.Size() n += 1 + l + sovGenerated(uint64(l)) } + l = len(m.Event) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -9419,6 +9815,51 @@ func (m *Workflow) Size() (n int) { return n } +func (m *WorkflowEventBinding) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Spec.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *WorkflowEventBindingList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *WorkflowEventBindingSpec) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Event.Size() + n += 1 + l + sovGenerated(uint64(l)) + if m.Submit != nil { + l = m.Submit.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + func (m *WorkflowList) Size() (n int) { if m == nil { return 0 @@ -10053,6 +10494,16 @@ func (this *DAGTemplate) String() string { }, "") return s } +func (this *Event) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Event{`, + `Selector:` + fmt.Sprintf("%v", this.Selector) + `,`, + `}`, + }, "") + return s +} func (this *ExecutorConfig) String() string { if this == nil { return "nil" @@ -10591,14 +11042,25 @@ func (this *Sequence) String() string { }, "") return s } -func (this *SubmitOpts) String() string { +func (this *Submit) String() string { if this == nil { return "nil" } - s := strings.Join([]string{`&SubmitOpts{`, - `Name:` + fmt.Sprintf("%v", this.Name) + `,`, - `GenerateName:` + fmt.Sprintf("%v", this.GenerateName) + `,`, - `Entrypoint:` + fmt.Sprintf("%v", this.Entrypoint) + `,`, + s := strings.Join([]string{`&Submit{`, + `WorkflowTemplateRef:` + strings.Replace(strings.Replace(this.WorkflowTemplateRef.String(), "WorkflowTemplateRef", "WorkflowTemplateRef", 1), `&`, ``, 1) + `,`, + `Arguments:` + strings.Replace(this.Arguments.String(), "Arguments", "Arguments", 1) + `,`, + `}`, + }, "") + return s +} +func (this *SubmitOpts) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&SubmitOpts{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `GenerateName:` + fmt.Sprintf("%v", this.GenerateName) + `,`, + `Entrypoint:` + fmt.Sprintf("%v", this.Entrypoint) + `,`, `Parameters:` + fmt.Sprintf("%v", this.Parameters) + `,`, `ParameterFile:` + fmt.Sprintf("%v", this.ParameterFile) + `,`, `ServiceAccount:` + fmt.Sprintf("%v", this.ServiceAccount) + `,`, @@ -10792,6 +11254,7 @@ func (this *ValueFrom) String() string { `Parameter:` + fmt.Sprintf("%v", this.Parameter) + `,`, `Default:` + strings.Replace(fmt.Sprintf("%v", this.Default), "IntOrString", "intstr.IntOrString", 1) + `,`, `Supplied:` + strings.Replace(this.Supplied.String(), "SuppliedValueFrom", "SuppliedValueFrom", 1) + `,`, + `Event:` + fmt.Sprintf("%v", this.Event) + `,`, `}`, }, "") return s @@ -10835,6 +11298,44 @@ func (this *Workflow) String() string { }, "") return s } +func (this *WorkflowEventBinding) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&WorkflowEventBinding{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v11.ObjectMeta", 1), `&`, ``, 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "WorkflowEventBindingSpec", "WorkflowEventBindingSpec", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *WorkflowEventBindingList) String() string { + if this == nil { + return "nil" + } + repeatedStringForItems := "[]WorkflowEventBinding{" + for _, f := range this.Items { + repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "WorkflowEventBinding", "WorkflowEventBinding", 1), `&`, ``, 1) + "," + } + repeatedStringForItems += "}" + s := strings.Join([]string{`&WorkflowEventBindingList{`, + `ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v11.ListMeta", 1), `&`, ``, 1) + `,`, + `Items:` + repeatedStringForItems + `,`, + `}`, + }, "") + return s +} +func (this *WorkflowEventBindingSpec) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&WorkflowEventBindingSpec{`, + `Event:` + strings.Replace(strings.Replace(this.Event.String(), "Event", "Event", 1), `&`, ``, 1) + `,`, + `Submit:` + strings.Replace(this.Submit.String(), "Submit", "Submit", 1) + `,`, + `}`, + }, "") + return s +} func (this *WorkflowList) String() string { if this == nil { return "nil" @@ -14534,6 +15035,91 @@ func (m *DAGTemplate) Unmarshal(dAtA []byte) error { } return nil } +func (m *Event) 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: Event: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Event: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Selector", 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.Selector = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ExecutorConfig) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -20859,6 +21445,128 @@ func (m *Sequence) Unmarshal(dAtA []byte) error { } return nil } +func (m *Submit) 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: Submit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Submit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkflowTemplateRef", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.WorkflowTemplateRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Arguments", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Arguments == nil { + m.Arguments = &Arguments{} + } + if err := m.Arguments.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 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *SubmitOpts) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -23542,25 +24250,57 @@ func (m *ValueFrom) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Event", wireType) } - iNdEx += skippy - } - } - + 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.Event = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + if iNdEx > l { return io.ErrUnexpectedEOF } @@ -24113,6 +24853,367 @@ func (m *Workflow) Unmarshal(dAtA []byte) error { } return nil } +func (m *WorkflowEventBinding) 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: WorkflowEventBinding: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WorkflowEventBinding: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Spec.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 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WorkflowEventBindingList) 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: WorkflowEventBindingList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WorkflowEventBindingList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", 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.Items = append(m.Items, WorkflowEventBinding{}) + if err := m.Items[len(m.Items)-1].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 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WorkflowEventBindingSpec) 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: WorkflowEventBindingSpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WorkflowEventBindingSpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Event", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Event.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Submit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Submit == nil { + m.Submit = &Submit{} + } + if err := m.Submit.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 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *WorkflowList) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apis/workflow/v1alpha1/generated.proto b/pkg/apis/workflow/v1alpha1/generated.proto index bb751085d0df..266187197a54 100644 --- a/pkg/apis/workflow/v1alpha1/generated.proto +++ b/pkg/apis/workflow/v1alpha1/generated.proto @@ -314,6 +314,11 @@ message DAGTemplate { optional bool failFast = 3; } +message Event { + // Selector (https://github.com/antonmedv/expr) that we must must match the event. E.g. `payload.message == "test"` + optional string selector = 1; +} + // ExecutorConfig holds configurations of an executor container. message ExecutorConfig { // ServiceAccountName specifies the service account name of the executor container. @@ -839,6 +844,14 @@ message Sequence { optional string format = 4; } +message Submit { + // WorkflowTemplateRef the workflow template to submit + optional WorkflowTemplateRef workflowTemplateRef = 1; + + // Arguments extracted from the event and then set as arguments to the workflow created. + optional Arguments arguments = 2; +} + // SubmitOpts are workflow submission options message SubmitOpts { // Name overrides metadata.name @@ -1094,6 +1107,9 @@ message ValueFrom { // JQFilter expression against the resource object in resource templates optional string jqFilter = 3; + // Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` + optional string event = 7; + // Parameter reference to a step or dag task in which to retrieve an output parameter value from // (e.g. '{{steps.mystep.outputs.myparam}}') optional string parameter = 4; @@ -1143,6 +1159,34 @@ message Workflow { optional WorkflowStatus status = 3; } +// WorkflowEventBinding is the definition of an event resource +// +genclient +// +genclient:noStatus +// +kubebuilder:resource:shortName=wfeb +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +message WorkflowEventBinding { + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + + optional WorkflowEventBindingSpec spec = 2; +} + +// WorkflowEventBindingList is list of event resources +// +kubebuilder:resource:shortName=wfebs +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +message WorkflowEventBindingList { + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + + repeated WorkflowEventBinding items = 2; +} + +message WorkflowEventBindingSpec { + // Event is the event to bind to + optional Event event = 1; + + // Submit is the workflow template to submit + optional Submit submit = 2; +} + // WorkflowList is list of Workflow resources // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object message WorkflowList { diff --git a/pkg/apis/workflow/v1alpha1/openapi_generated.go b/pkg/apis/workflow/v1alpha1/openapi_generated.go index 01e8404cf5ca..18482218a91f 100644 --- a/pkg/apis/workflow/v1alpha1/openapi_generated.go +++ b/pkg/apis/workflow/v1alpha1/openapi_generated.go @@ -34,6 +34,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.CronWorkflowStatus": schema_pkg_apis_workflow_v1alpha1_CronWorkflowStatus(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.DAGTask": schema_pkg_apis_workflow_v1alpha1_DAGTask(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.DAGTemplate": schema_pkg_apis_workflow_v1alpha1_DAGTemplate(ref), + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Event": schema_pkg_apis_workflow_v1alpha1_Event(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.ExecutorConfig": schema_pkg_apis_workflow_v1alpha1_ExecutorConfig(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.GCSArtifact": schema_pkg_apis_workflow_v1alpha1_GCSArtifact(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.GCSBucket": schema_pkg_apis_workflow_v1alpha1_GCSBucket(ref), @@ -72,6 +73,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SemaphoreRef": schema_pkg_apis_workflow_v1alpha1_SemaphoreRef(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SemaphoreStatus": schema_pkg_apis_workflow_v1alpha1_SemaphoreStatus(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Sequence": schema_pkg_apis_workflow_v1alpha1_Sequence(ref), + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Submit": schema_pkg_apis_workflow_v1alpha1_Submit(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SubmitOpts": schema_pkg_apis_workflow_v1alpha1_SubmitOpts(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SuppliedValueFrom": schema_pkg_apis_workflow_v1alpha1_SuppliedValueFrom(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.SuspendTemplate": schema_pkg_apis_workflow_v1alpha1_SuspendTemplate(ref), @@ -86,6 +88,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Version": schema_pkg_apis_workflow_v1alpha1_Version(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WaitingStatus": schema_pkg_apis_workflow_v1alpha1_WaitingStatus(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Workflow": schema_pkg_apis_workflow_v1alpha1_Workflow(ref), + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowEventBinding": schema_pkg_apis_workflow_v1alpha1_WorkflowEventBinding(ref), + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowEventBindingList": schema_pkg_apis_workflow_v1alpha1_WorkflowEventBindingList(ref), + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowEventBindingSpec": schema_pkg_apis_workflow_v1alpha1_WorkflowEventBindingSpec(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowList": schema_pkg_apis_workflow_v1alpha1_WorkflowList(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowSpec": schema_pkg_apis_workflow_v1alpha1_WorkflowSpec(ref), "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowStatus": schema_pkg_apis_workflow_v1alpha1_WorkflowStatus(ref), @@ -1056,6 +1061,26 @@ func schema_pkg_apis_workflow_v1alpha1_DAGTemplate(ref common.ReferenceCallback) } } +func schema_pkg_apis_workflow_v1alpha1_Event(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "Selector (https://github.com/antonmedv/expr) that we must must match the event. E.g. `payload.message == \"test\"`", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"selector"}, + }, + }, + } +} + func schema_pkg_apis_workflow_v1alpha1_ExecutorConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2978,6 +3003,33 @@ func schema_pkg_apis_workflow_v1alpha1_Sequence(ref common.ReferenceCallback) co } } +func schema_pkg_apis_workflow_v1alpha1_Submit(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "workflowTemplateRef": { + SchemaProps: spec.SchemaProps{ + Description: "WorkflowTemplateRef the workflow template to submit", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowTemplateRef"), + }, + }, + "arguments": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments extracted from the event and then set as arguments to the workflow created.", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Arguments"), + }, + }, + }, + Required: []string{"workflowTemplateRef"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Arguments", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowTemplateRef"}, + } +} + func schema_pkg_apis_workflow_v1alpha1_SubmitOpts(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -3839,6 +3891,13 @@ func schema_pkg_apis_workflow_v1alpha1_ValueFrom(ref common.ReferenceCallback) c Format: "", }, }, + "event": { + SchemaProps: spec.SchemaProps{ + Description: "Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`", + Type: []string{"string"}, + Format: "", + }, + }, "parameter": { SchemaProps: spec.SchemaProps{ Description: "Parameter reference to a step or dag task in which to retrieve an output parameter value from (e.g. '{{steps.mystep.outputs.myparam}}')", @@ -3992,6 +4051,120 @@ func schema_pkg_apis_workflow_v1alpha1_Workflow(ref common.ReferenceCallback) co } } +func schema_pkg_apis_workflow_v1alpha1_WorkflowEventBinding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WorkflowEventBinding is the definition of an event resource", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowEventBindingSpec"), + }, + }, + }, + Required: []string{"metadata", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowEventBindingSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_workflow_v1alpha1_WorkflowEventBindingList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WorkflowEventBindingList is list of event resources", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowEventBinding"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.WorkflowEventBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_workflow_v1alpha1_WorkflowEventBindingSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "event": { + SchemaProps: spec.SchemaProps{ + Description: "Event is the event to bind to", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Event"), + }, + }, + "submit": { + SchemaProps: spec.SchemaProps{ + Description: "Submit is the workflow template to submit", + Ref: ref("github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Submit"), + }, + }, + }, + Required: []string{"event"}, + }, + }, + Dependencies: []string{ + "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Event", "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1.Submit"}, + } +} + func schema_pkg_apis_workflow_v1alpha1_WorkflowList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/workflow/v1alpha1/register.go b/pkg/apis/workflow/v1alpha1/register.go index 76b1a8cabe42..85a1913c6c96 100644 --- a/pkg/apis/workflow/v1alpha1/register.go +++ b/pkg/apis/workflow/v1alpha1/register.go @@ -34,6 +34,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &Workflow{}, &WorkflowList{}, + &WorkflowEventBinding{}, + &WorkflowEventBindingList{}, &WorkflowTemplate{}, &WorkflowTemplateList{}, &CronWorkflow{}, diff --git a/pkg/apis/workflow/v1alpha1/workflow_template_types.go b/pkg/apis/workflow/v1alpha1/workflow_template_types.go index bc007c232e08..5cc858d092df 100644 --- a/pkg/apis/workflow/v1alpha1/workflow_template_types.go +++ b/pkg/apis/workflow/v1alpha1/workflow_template_types.go @@ -63,6 +63,10 @@ func (wftmpl *WorkflowTemplate) GetResourceScope() ResourceScope { return ResourceScopeNamespaced } +func (wftmpl *WorkflowTemplate) GetWorkflowMetadata() *metav1.ObjectMeta { + return wftmpl.Spec.WorkflowMetadata +} + // GetWorkflowSpec returns the WorkflowSpec of workflow template. func (wftmpl *WorkflowTemplate) GetWorkflowSpec() *WorkflowSpec { return &wftmpl.Spec.WorkflowSpec diff --git a/pkg/apis/workflow/v1alpha1/workflow_types.go b/pkg/apis/workflow/v1alpha1/workflow_types.go index 3e7d513d121a..8c99cf665600 100644 --- a/pkg/apis/workflow/v1alpha1/workflow_types.go +++ b/pkg/apis/workflow/v1alpha1/workflow_types.go @@ -630,6 +630,9 @@ type ValueFrom struct { // JQFilter expression against the resource object in resource templates JQFilter string `json:"jqFilter,omitempty" protobuf:"bytes,3,opt,name=jqFilter"` + // Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` + Event string `json:"event,omitempty" protobuf:"bytes,7,opt,name=event"` + // Parameter reference to a step or dag task in which to retrieve an output parameter value from // (e.g. '{{steps.mystep.outputs.myparam}}') Parameter string `json:"parameter,omitempty" protobuf:"bytes,4,opt,name=parameter"` diff --git a/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go index 571033f26caf..731a3cb521b1 100644 --- a/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go @@ -645,6 +645,22 @@ func (in *DAGTemplate) DeepCopy() *DAGTemplate { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Event) DeepCopyInto(out *Event) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Event. +func (in *Event) DeepCopy() *Event { + if in == nil { + return nil + } + out := new(Event) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExecutorConfig) DeepCopyInto(out *ExecutorConfig) { *out = *in @@ -1583,6 +1599,28 @@ func (in *Sequence) DeepCopy() *Sequence { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Submit) DeepCopyInto(out *Submit) { + *out = *in + out.WorkflowTemplateRef = in.WorkflowTemplateRef + if in.Arguments != nil { + in, out := &in.Arguments, &out.Arguments + *out = new(Arguments) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Submit. +func (in *Submit) DeepCopy() *Submit { + if in == nil { + return nil + } + out := new(Submit) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SubmitOpts) DeepCopyInto(out *SubmitOpts) { *out = *in @@ -2029,6 +2067,88 @@ func (in *Workflow) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkflowEventBinding) DeepCopyInto(out *WorkflowEventBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkflowEventBinding. +func (in *WorkflowEventBinding) DeepCopy() *WorkflowEventBinding { + if in == nil { + return nil + } + out := new(WorkflowEventBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkflowEventBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkflowEventBindingList) DeepCopyInto(out *WorkflowEventBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WorkflowEventBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkflowEventBindingList. +func (in *WorkflowEventBindingList) DeepCopy() *WorkflowEventBindingList { + if in == nil { + return nil + } + out := new(WorkflowEventBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkflowEventBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkflowEventBindingSpec) DeepCopyInto(out *WorkflowEventBindingSpec) { + *out = *in + out.Event = in.Event + if in.Submit != nil { + in, out := &in.Submit, &out.Submit + *out = new(Submit) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkflowEventBindingSpec. +func (in *WorkflowEventBindingSpec) DeepCopy() *WorkflowEventBindingSpec { + if in == nil { + return nil + } + out := new(WorkflowEventBindingSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkflowList) DeepCopyInto(out *WorkflowList) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/workflow/v1alpha1/fake/fake_workflow_client.go b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/fake/fake_workflow_client.go index 0c838b82b2df..a15d9bc8c63e 100644 --- a/pkg/client/clientset/versioned/typed/workflow/v1alpha1/fake/fake_workflow_client.go +++ b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/fake/fake_workflow_client.go @@ -24,6 +24,10 @@ func (c *FakeArgoprojV1alpha1) Workflows(namespace string) v1alpha1.WorkflowInte return &FakeWorkflows{c, namespace} } +func (c *FakeArgoprojV1alpha1) WorkflowEventBindings(namespace string) v1alpha1.WorkflowEventBindingInterface { + return &FakeWorkflowEventBindings{c, namespace} +} + func (c *FakeArgoprojV1alpha1) WorkflowTemplates(namespace string) v1alpha1.WorkflowTemplateInterface { return &FakeWorkflowTemplates{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/workflow/v1alpha1/fake/fake_workfloweventbinding.go b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/fake/fake_workfloweventbinding.go new file mode 100644 index 000000000000..fc223a648fee --- /dev/null +++ b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/fake/fake_workfloweventbinding.go @@ -0,0 +1,112 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeWorkflowEventBindings implements WorkflowEventBindingInterface +type FakeWorkflowEventBindings struct { + Fake *FakeArgoprojV1alpha1 + ns string +} + +var workfloweventbindingsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "workfloweventbindings"} + +var workfloweventbindingsKind = schema.GroupVersionKind{Group: "argoproj.io", Version: "v1alpha1", Kind: "WorkflowEventBinding"} + +// Get takes name of the workflowEventBinding, and returns the corresponding workflowEventBinding object, and an error if there is any. +func (c *FakeWorkflowEventBindings) Get(name string, options v1.GetOptions) (result *v1alpha1.WorkflowEventBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(workfloweventbindingsResource, c.ns, name), &v1alpha1.WorkflowEventBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.WorkflowEventBinding), err +} + +// List takes label and field selectors, and returns the list of WorkflowEventBindings that match those selectors. +func (c *FakeWorkflowEventBindings) List(opts v1.ListOptions) (result *v1alpha1.WorkflowEventBindingList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(workfloweventbindingsResource, workfloweventbindingsKind, c.ns, opts), &v1alpha1.WorkflowEventBindingList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.WorkflowEventBindingList{ListMeta: obj.(*v1alpha1.WorkflowEventBindingList).ListMeta} + for _, item := range obj.(*v1alpha1.WorkflowEventBindingList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested workflowEventBindings. +func (c *FakeWorkflowEventBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(workfloweventbindingsResource, c.ns, opts)) + +} + +// Create takes the representation of a workflowEventBinding and creates it. Returns the server's representation of the workflowEventBinding, and an error, if there is any. +func (c *FakeWorkflowEventBindings) Create(workflowEventBinding *v1alpha1.WorkflowEventBinding) (result *v1alpha1.WorkflowEventBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(workfloweventbindingsResource, c.ns, workflowEventBinding), &v1alpha1.WorkflowEventBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.WorkflowEventBinding), err +} + +// Update takes the representation of a workflowEventBinding and updates it. Returns the server's representation of the workflowEventBinding, and an error, if there is any. +func (c *FakeWorkflowEventBindings) Update(workflowEventBinding *v1alpha1.WorkflowEventBinding) (result *v1alpha1.WorkflowEventBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(workfloweventbindingsResource, c.ns, workflowEventBinding), &v1alpha1.WorkflowEventBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.WorkflowEventBinding), err +} + +// Delete takes name of the workflowEventBinding and deletes it. Returns an error if one occurs. +func (c *FakeWorkflowEventBindings) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(workfloweventbindingsResource, c.ns, name), &v1alpha1.WorkflowEventBinding{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeWorkflowEventBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(workfloweventbindingsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.WorkflowEventBindingList{}) + return err +} + +// Patch applies the patch and returns the patched workflowEventBinding. +func (c *FakeWorkflowEventBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkflowEventBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(workfloweventbindingsResource, c.ns, name, pt, data, subresources...), &v1alpha1.WorkflowEventBinding{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.WorkflowEventBinding), err +} diff --git a/pkg/client/clientset/versioned/typed/workflow/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/generated_expansion.go index bdc2433ab1bd..2656da0d3b2f 100644 --- a/pkg/client/clientset/versioned/typed/workflow/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/generated_expansion.go @@ -8,4 +8,6 @@ type CronWorkflowExpansion interface{} type WorkflowExpansion interface{} +type WorkflowEventBindingExpansion interface{} + type WorkflowTemplateExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/workflow/v1alpha1/workflow_client.go b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/workflow_client.go index c22f7ed3a6c5..2f2dc8e048f4 100644 --- a/pkg/client/clientset/versioned/typed/workflow/v1alpha1/workflow_client.go +++ b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/workflow_client.go @@ -13,6 +13,7 @@ type ArgoprojV1alpha1Interface interface { ClusterWorkflowTemplatesGetter CronWorkflowsGetter WorkflowsGetter + WorkflowEventBindingsGetter WorkflowTemplatesGetter } @@ -33,6 +34,10 @@ func (c *ArgoprojV1alpha1Client) Workflows(namespace string) WorkflowInterface { return newWorkflows(c, namespace) } +func (c *ArgoprojV1alpha1Client) WorkflowEventBindings(namespace string) WorkflowEventBindingInterface { + return newWorkflowEventBindings(c, namespace) +} + func (c *ArgoprojV1alpha1Client) WorkflowTemplates(namespace string) WorkflowTemplateInterface { return newWorkflowTemplates(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/workflow/v1alpha1/workfloweventbinding.go b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/workfloweventbinding.go new file mode 100644 index 000000000000..bd41fa3a9e49 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/workflow/v1alpha1/workfloweventbinding.go @@ -0,0 +1,158 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" + scheme "github.com/argoproj/argo/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// WorkflowEventBindingsGetter has a method to return a WorkflowEventBindingInterface. +// A group's client should implement this interface. +type WorkflowEventBindingsGetter interface { + WorkflowEventBindings(namespace string) WorkflowEventBindingInterface +} + +// WorkflowEventBindingInterface has methods to work with WorkflowEventBinding resources. +type WorkflowEventBindingInterface interface { + Create(*v1alpha1.WorkflowEventBinding) (*v1alpha1.WorkflowEventBinding, error) + Update(*v1alpha1.WorkflowEventBinding) (*v1alpha1.WorkflowEventBinding, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.WorkflowEventBinding, error) + List(opts v1.ListOptions) (*v1alpha1.WorkflowEventBindingList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkflowEventBinding, err error) + WorkflowEventBindingExpansion +} + +// workflowEventBindings implements WorkflowEventBindingInterface +type workflowEventBindings struct { + client rest.Interface + ns string +} + +// newWorkflowEventBindings returns a WorkflowEventBindings +func newWorkflowEventBindings(c *ArgoprojV1alpha1Client, namespace string) *workflowEventBindings { + return &workflowEventBindings{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the workflowEventBinding, and returns the corresponding workflowEventBinding object, and an error if there is any. +func (c *workflowEventBindings) Get(name string, options v1.GetOptions) (result *v1alpha1.WorkflowEventBinding, err error) { + result = &v1alpha1.WorkflowEventBinding{} + err = c.client.Get(). + Namespace(c.ns). + Resource("workfloweventbindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of WorkflowEventBindings that match those selectors. +func (c *workflowEventBindings) List(opts v1.ListOptions) (result *v1alpha1.WorkflowEventBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.WorkflowEventBindingList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("workfloweventbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested workflowEventBindings. +func (c *workflowEventBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("workfloweventbindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a workflowEventBinding and creates it. Returns the server's representation of the workflowEventBinding, and an error, if there is any. +func (c *workflowEventBindings) Create(workflowEventBinding *v1alpha1.WorkflowEventBinding) (result *v1alpha1.WorkflowEventBinding, err error) { + result = &v1alpha1.WorkflowEventBinding{} + err = c.client.Post(). + Namespace(c.ns). + Resource("workfloweventbindings"). + Body(workflowEventBinding). + Do(). + Into(result) + return +} + +// Update takes the representation of a workflowEventBinding and updates it. Returns the server's representation of the workflowEventBinding, and an error, if there is any. +func (c *workflowEventBindings) Update(workflowEventBinding *v1alpha1.WorkflowEventBinding) (result *v1alpha1.WorkflowEventBinding, err error) { + result = &v1alpha1.WorkflowEventBinding{} + err = c.client.Put(). + Namespace(c.ns). + Resource("workfloweventbindings"). + Name(workflowEventBinding.Name). + Body(workflowEventBinding). + Do(). + Into(result) + return +} + +// Delete takes name of the workflowEventBinding and deletes it. Returns an error if one occurs. +func (c *workflowEventBindings) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("workfloweventbindings"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *workflowEventBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("workfloweventbindings"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched workflowEventBinding. +func (c *workflowEventBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkflowEventBinding, err error) { + result = &v1alpha1.WorkflowEventBinding{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("workfloweventbindings"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index f6980cf7b881..4bbae4336e15 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -43,6 +43,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Argoproj().V1alpha1().CronWorkflows().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("workflows"): return &genericInformer{resource: resource.GroupResource(), informer: f.Argoproj().V1alpha1().Workflows().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("workfloweventbindings"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Argoproj().V1alpha1().WorkflowEventBindings().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("workflowtemplates"): return &genericInformer{resource: resource.GroupResource(), informer: f.Argoproj().V1alpha1().WorkflowTemplates().Informer()}, nil diff --git a/pkg/client/informers/externalversions/workflow/v1alpha1/interface.go b/pkg/client/informers/externalversions/workflow/v1alpha1/interface.go index 2e3eaf8b0085..e9c6df6eeee8 100644 --- a/pkg/client/informers/externalversions/workflow/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/workflow/v1alpha1/interface.go @@ -14,6 +14,8 @@ type Interface interface { CronWorkflows() CronWorkflowInformer // Workflows returns a WorkflowInformer. Workflows() WorkflowInformer + // WorkflowEventBindings returns a WorkflowEventBindingInformer. + WorkflowEventBindings() WorkflowEventBindingInformer // WorkflowTemplates returns a WorkflowTemplateInformer. WorkflowTemplates() WorkflowTemplateInformer } @@ -44,6 +46,11 @@ func (v *version) Workflows() WorkflowInformer { return &workflowInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// WorkflowEventBindings returns a WorkflowEventBindingInformer. +func (v *version) WorkflowEventBindings() WorkflowEventBindingInformer { + return &workflowEventBindingInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // WorkflowTemplates returns a WorkflowTemplateInformer. func (v *version) WorkflowTemplates() WorkflowTemplateInformer { return &workflowTemplateInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/workflow/v1alpha1/workfloweventbinding.go b/pkg/client/informers/externalversions/workflow/v1alpha1/workfloweventbinding.go new file mode 100644 index 000000000000..eb270011d12d --- /dev/null +++ b/pkg/client/informers/externalversions/workflow/v1alpha1/workfloweventbinding.go @@ -0,0 +1,73 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + workflowv1alpha1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" + versioned "github.com/argoproj/argo/pkg/client/clientset/versioned" + internalinterfaces "github.com/argoproj/argo/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/argoproj/argo/pkg/client/listers/workflow/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// WorkflowEventBindingInformer provides access to a shared informer and lister for +// WorkflowEventBindings. +type WorkflowEventBindingInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.WorkflowEventBindingLister +} + +type workflowEventBindingInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewWorkflowEventBindingInformer constructs a new informer for WorkflowEventBinding type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewWorkflowEventBindingInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWorkflowEventBindingInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredWorkflowEventBindingInformer constructs a new informer for WorkflowEventBinding type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredWorkflowEventBindingInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ArgoprojV1alpha1().WorkflowEventBindings(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ArgoprojV1alpha1().WorkflowEventBindings(namespace).Watch(options) + }, + }, + &workflowv1alpha1.WorkflowEventBinding{}, + resyncPeriod, + indexers, + ) +} + +func (f *workflowEventBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWorkflowEventBindingInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *workflowEventBindingInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&workflowv1alpha1.WorkflowEventBinding{}, f.defaultInformer) +} + +func (f *workflowEventBindingInformer) Lister() v1alpha1.WorkflowEventBindingLister { + return v1alpha1.NewWorkflowEventBindingLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/listers/workflow/v1alpha1/expansion_generated.go b/pkg/client/listers/workflow/v1alpha1/expansion_generated.go index a57d415bdf9e..729a337fa9fa 100644 --- a/pkg/client/listers/workflow/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/workflow/v1alpha1/expansion_generated.go @@ -22,6 +22,14 @@ type WorkflowListerExpansion interface{} // WorkflowNamespaceLister. type WorkflowNamespaceListerExpansion interface{} +// WorkflowEventBindingListerExpansion allows custom methods to be added to +// WorkflowEventBindingLister. +type WorkflowEventBindingListerExpansion interface{} + +// WorkflowEventBindingNamespaceListerExpansion allows custom methods to be added to +// WorkflowEventBindingNamespaceLister. +type WorkflowEventBindingNamespaceListerExpansion interface{} + // WorkflowTemplateListerExpansion allows custom methods to be added to // WorkflowTemplateLister. type WorkflowTemplateListerExpansion interface{} diff --git a/pkg/client/listers/workflow/v1alpha1/workfloweventbinding.go b/pkg/client/listers/workflow/v1alpha1/workfloweventbinding.go new file mode 100644 index 000000000000..8e2adb1cd650 --- /dev/null +++ b/pkg/client/listers/workflow/v1alpha1/workfloweventbinding.go @@ -0,0 +1,78 @@ +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// WorkflowEventBindingLister helps list WorkflowEventBindings. +type WorkflowEventBindingLister interface { + // List lists all WorkflowEventBindings in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.WorkflowEventBinding, err error) + // WorkflowEventBindings returns an object that can list and get WorkflowEventBindings. + WorkflowEventBindings(namespace string) WorkflowEventBindingNamespaceLister + WorkflowEventBindingListerExpansion +} + +// workflowEventBindingLister implements the WorkflowEventBindingLister interface. +type workflowEventBindingLister struct { + indexer cache.Indexer +} + +// NewWorkflowEventBindingLister returns a new WorkflowEventBindingLister. +func NewWorkflowEventBindingLister(indexer cache.Indexer) WorkflowEventBindingLister { + return &workflowEventBindingLister{indexer: indexer} +} + +// List lists all WorkflowEventBindings in the indexer. +func (s *workflowEventBindingLister) List(selector labels.Selector) (ret []*v1alpha1.WorkflowEventBinding, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.WorkflowEventBinding)) + }) + return ret, err +} + +// WorkflowEventBindings returns an object that can list and get WorkflowEventBindings. +func (s *workflowEventBindingLister) WorkflowEventBindings(namespace string) WorkflowEventBindingNamespaceLister { + return workflowEventBindingNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// WorkflowEventBindingNamespaceLister helps list and get WorkflowEventBindings. +type WorkflowEventBindingNamespaceLister interface { + // List lists all WorkflowEventBindings in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.WorkflowEventBinding, err error) + // Get retrieves the WorkflowEventBinding from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.WorkflowEventBinding, error) + WorkflowEventBindingNamespaceListerExpansion +} + +// workflowEventBindingNamespaceLister implements the WorkflowEventBindingNamespaceLister +// interface. +type workflowEventBindingNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all WorkflowEventBindings in the indexer for a given namespace. +func (s workflowEventBindingNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.WorkflowEventBinding, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.WorkflowEventBinding)) + }) + return ret, err +} + +// Get retrieves the WorkflowEventBinding from the indexer for a given namespace and name. +func (s workflowEventBindingNamespaceLister) Get(name string) (*v1alpha1.WorkflowEventBinding, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("workfloweventbinding"), name) + } + return obj.(*v1alpha1.WorkflowEventBinding), nil +} diff --git a/server/apiserver/argoserver.go b/server/apiserver/argoserver.go index 0ab851d6dc86..2ef246776ddc 100644 --- a/server/apiserver/argoserver.go +++ b/server/apiserver/argoserver.go @@ -24,6 +24,7 @@ import ( "github.com/argoproj/argo/persist/sqldb" clusterwftemplatepkg "github.com/argoproj/argo/pkg/apiclient/clusterworkflowtemplate" cronworkflowpkg "github.com/argoproj/argo/pkg/apiclient/cronworkflow" + eventpkg "github.com/argoproj/argo/pkg/apiclient/event" infopkg "github.com/argoproj/argo/pkg/apiclient/info" workflowpkg "github.com/argoproj/argo/pkg/apiclient/workflow" workflowarchivepkg "github.com/argoproj/argo/pkg/apiclient/workflowarchive" @@ -33,8 +34,10 @@ import ( "github.com/argoproj/argo/server/artifacts" "github.com/argoproj/argo/server/auth" "github.com/argoproj/argo/server/auth/sso" + "github.com/argoproj/argo/server/auth/webhook" "github.com/argoproj/argo/server/clusterworkflowtemplate" "github.com/argoproj/argo/server/cronworkflow" + "github.com/argoproj/argo/server/event" "github.com/argoproj/argo/server/info" "github.com/argoproj/argo/server/static" "github.com/argoproj/argo/server/workflow" @@ -59,10 +62,13 @@ type argoServer struct { namespace string managedNamespace string kubeClientset *kubernetes.Clientset + wfClientSet *versioned.Clientset authenticator auth.Gatekeeper oAuth2Service sso.Interface configController config.Controller stopCh chan struct{} + eventQueueSize int + eventWorkerCount int } type ArgoServerOpts struct { @@ -74,9 +80,11 @@ type ArgoServerOpts struct { RestConfig *rest.Config AuthModes auth.Modes // config map name - ConfigName string - ManagedNamespace string - HSTS bool + ConfigName string + ManagedNamespace string + HSTS bool + EventOperationQueueSize int + EventWorkerCount int } func NewArgoServer(opts ArgoServerOpts) (*argoServer, error) { @@ -105,11 +113,14 @@ func NewArgoServer(opts ArgoServerOpts) (*argoServer, error) { hsts: opts.HSTS, namespace: opts.Namespace, managedNamespace: opts.ManagedNamespace, + wfClientSet: opts.WfClientSet, kubeClientset: opts.KubeClientset, authenticator: gatekeeper, oAuth2Service: ssoIf, configController: configController, stopCh: make(chan struct{}), + eventQueueSize: opts.EventOperationQueueSize, + eventWorkerCount: opts.EventWorkerCount, }, nil } @@ -121,12 +132,11 @@ var backoff = wait.Backoff{ } func (as *argoServer) Run(ctx context.Context, port int, browserOpenFunc func(string)) { - log.WithField("version", argo.GetVersion().Version).Info("Starting Argo Server") - configMap, err := as.configController.Get() if err != nil { log.Fatal(err) } + log.WithFields(log.Fields{"version": argo.GetVersion().Version, "instanceID": configMap.InstanceID}).Info("Starting Argo Server") instanceIDService := instanceid.NewService(configMap.InstanceID) var offloadRepo = sqldb.ExplosiveOffloadNodeStatusRepo var wfArchive = sqldb.NullWorkflowArchive @@ -147,7 +157,8 @@ func (as *argoServer) Run(ctx context.Context, port int, browserOpenFunc func(st wfArchive = sqldb.NewWorkflowArchive(session, persistence.GetClusterName(), as.managedNamespace, instanceIDService) } artifactServer := artifacts.NewArtifactServer(as.authenticator, hydrator.New(offloadRepo), wfArchive, instanceIDService) - grpcServer := as.newGRPCServer(instanceIDService, offloadRepo, wfArchive, configMap.Links) + eventServer := event.NewController(instanceIDService, as.eventQueueSize, as.eventWorkerCount) + grpcServer := as.newGRPCServer(instanceIDService, offloadRepo, wfArchive, eventServer, configMap.Links) httpServer := as.newHTTPServer(ctx, port, artifactServer) // Start listener @@ -177,6 +188,7 @@ func (as *argoServer) Run(ctx context.Context, port int, browserOpenFunc func(st grpcL := tcpm.Match(cmux.Any()) go as.configController.Run(as.stopCh, as.restartOnConfigChange) + go eventServer.Run(as.stopCh) go func() { as.checkServeErr("grpcServer", grpcServer.Serve(grpcL)) }() go func() { as.checkServeErr("httpServer", httpServer.Serve(httpL)) }() go func() { as.checkServeErr("tcpm", tcpm.Serve()) }() @@ -190,9 +202,8 @@ func (as *argoServer) Run(ctx context.Context, port int, browserOpenFunc func(st <-as.stopCh } -func (as *argoServer) newGRPCServer(instanceIDService instanceid.Service, offloadNodeStatusRepo sqldb.OffloadNodeStatusRepo, wfArchive sqldb.WorkflowArchive, links []*v1alpha1.Link) *grpc.Server { +func (as *argoServer) newGRPCServer(instanceIDService instanceid.Service, offloadNodeStatusRepo sqldb.OffloadNodeStatusRepo, wfArchive sqldb.WorkflowArchive, eventServer *event.Controller, links []*v1alpha1.Link) *grpc.Server { serverLog := log.NewEntry(log.StandardLogger()) - sOpts := []grpc.ServerOption{ // Set both the send and receive the bytes limit to be 100MB // The proper way to achieve high performance is to have pagination @@ -217,6 +228,7 @@ func (as *argoServer) newGRPCServer(instanceIDService instanceid.Service, offloa grpcServer := grpc.NewServer(sOpts...) infopkg.RegisterInfoServiceServer(grpcServer, info.NewInfoServer(as.managedNamespace, links)) + eventpkg.RegisterEventServiceServer(grpcServer, eventServer) workflowpkg.RegisterWorkflowServiceServer(grpcServer, workflow.NewWorkflowServer(instanceIDService, offloadNodeStatusRepo)) workflowtemplatepkg.RegisterWorkflowTemplateServiceServer(grpcServer, workflowtemplate.NewWorkflowTemplateServer(instanceIDService)) cronworkflowpkg.RegisterCronWorkflowServiceServer(grpcServer, cronworkflow.NewCronWorkflowServer(instanceIDService)) @@ -246,6 +258,8 @@ func (as *argoServer) newHTTPServer(ctx context.Context, port int, artifactServe dialOpts = append(dialOpts, grpc.WithInsecure()) } + webhookInterceptor := webhook.Interceptor(as.kubeClientset) + // HTTP 1.1+JSON Server // grpc-ecosystem/grpc-gateway is used to proxy HTTP requests to the corresponding gRPC call // NOTE: if a marshaller option is not supplied, grpc-gateway will default to the jsonpb from @@ -253,15 +267,19 @@ func (as *argoServer) newHTTPServer(ctx context.Context, port int, artifactServe // time.Time, but does not support custom UnmarshalJSON() and MarshalJSON() methods. Therefore // we use our own Marshaler gwMuxOpts := runtime.WithMarshalerOption(runtime.MIMEWildcard, new(json.JSONMarshaler)) - gwmux := runtime.NewServeMux(gwMuxOpts) + gwmux := runtime.NewServeMux(gwMuxOpts, + runtime.WithIncomingHeaderMatcher(func(key string) (string, bool) { return key, true }), + runtime.WithProtoErrorHandler(runtime.DefaultHTTPProtoErrorHandler), + ) mustRegisterGWHandler(infopkg.RegisterInfoServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dialOpts) + mustRegisterGWHandler(eventpkg.RegisterEventServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dialOpts) mustRegisterGWHandler(workflowpkg.RegisterWorkflowServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dialOpts) mustRegisterGWHandler(workflowtemplatepkg.RegisterWorkflowTemplateServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dialOpts) mustRegisterGWHandler(cronworkflowpkg.RegisterCronWorkflowServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dialOpts) mustRegisterGWHandler(workflowarchivepkg.RegisterArchivedWorkflowServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dialOpts) mustRegisterGWHandler(clusterwftemplatepkg.RegisterClusterWorkflowTemplateServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dialOpts) - mux.Handle("/api/", gwmux) + mux.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) { webhookInterceptor(w, r, gwmux) }) mux.HandleFunc("/artifacts/", artifactServer.GetArtifact) mux.HandleFunc("/artifacts-by-uid/", artifactServer.GetArtifactByUID) mux.HandleFunc("/oauth2/redirect", as.oAuth2Service.HandleRedirect) diff --git a/server/auth/gatekeeper.go b/server/auth/gatekeeper.go index 486ca3223fa4..4c9e3be48383 100644 --- a/server/auth/gatekeeper.go +++ b/server/auth/gatekeeper.go @@ -99,7 +99,7 @@ func getAuthHeader(md metadata.MD) string { return t } // check the HTTP cookie - for _, t := range md.Get("grpcgateway-cookie") { + for _, t := range md.Get("cookie") { header := http.Header{} header.Add("Cookie", t) request := http.Request{Header: header} diff --git a/server/auth/webhook/bitbucket.go b/server/auth/webhook/bitbucket.go new file mode 100644 index 000000000000..b769b82f70eb --- /dev/null +++ b/server/auth/webhook/bitbucket.go @@ -0,0 +1,35 @@ +package webhook + +import ( + "net/http" + + "gopkg.in/go-playground/webhooks.v5/bitbucket" +) + +func bitbucketMatch(secret string, r *http.Request) bool { + hook, err := bitbucket.New(bitbucket.Options.UUID(secret)) + if err != nil { + return false + } + _, err = hook.Parse(r, + bitbucket.RepoPushEvent, + bitbucket.RepoForkEvent, + bitbucket.RepoUpdatedEvent, + bitbucket.RepoCommitCommentCreatedEvent, + bitbucket.RepoCommitStatusCreatedEvent, + bitbucket.RepoCommitStatusUpdatedEvent, + bitbucket.IssueCreatedEvent, + bitbucket.IssueUpdatedEvent, + bitbucket.IssueCommentCreatedEvent, + bitbucket.PullRequestCreatedEvent, + bitbucket.PullRequestUpdatedEvent, + bitbucket.PullRequestApprovedEvent, + bitbucket.PullRequestUnapprovedEvent, + bitbucket.PullRequestMergedEvent, + bitbucket.PullRequestDeclinedEvent, + bitbucket.PullRequestCommentCreatedEvent, + bitbucket.PullRequestCommentUpdatedEvent, + bitbucket.PullRequestCommentDeletedEvent, + ) + return err == nil +} diff --git a/server/auth/webhook/bitbucketserver.go b/server/auth/webhook/bitbucketserver.go new file mode 100644 index 000000000000..26dd27c873fe --- /dev/null +++ b/server/auth/webhook/bitbucketserver.go @@ -0,0 +1,36 @@ +package webhook + +import ( + "net/http" + + bitbucketserver "gopkg.in/go-playground/webhooks.v5/bitbucket-server" +) + +func bitbucketserverMatch(secret string, r *http.Request) bool { + hook, err := bitbucketserver.New(bitbucketserver.Options.Secret(secret)) + if err != nil { + return false + } + _, err = hook.Parse(r, + bitbucketserver.RepositoryReferenceChangedEvent, + bitbucketserver.RepositoryModifiedEvent, + bitbucketserver.RepositoryForkedEvent, + bitbucketserver.RepositoryCommentAddedEvent, + bitbucketserver.RepositoryCommentEditedEvent, + bitbucketserver.RepositoryCommentDeletedEvent, + bitbucketserver.PullRequestOpenedEvent, + bitbucketserver.PullRequestFromReferenceUpdatedEvent, + bitbucketserver.PullRequestModifiedEvent, + bitbucketserver.PullRequestMergedEvent, + bitbucketserver.PullRequestDeclinedEvent, + bitbucketserver.PullRequestDeletedEvent, + bitbucketserver.PullRequestReviewerUpdatedEvent, + bitbucketserver.PullRequestReviewerApprovedEvent, + bitbucketserver.PullRequestReviewerUnapprovedEvent, + bitbucketserver.PullRequestReviewerNeedsWorkEvent, + bitbucketserver.PullRequestCommentAddedEvent, + bitbucketserver.PullRequestCommentEditedEvent, + bitbucketserver.PullRequestCommentDeletedEvent, + ) + return err == nil +} diff --git a/server/auth/webhook/github.go b/server/auth/webhook/github.go new file mode 100644 index 000000000000..7060bca0ef23 --- /dev/null +++ b/server/auth/webhook/github.go @@ -0,0 +1,57 @@ +package webhook + +import ( + "net/http" + + "gopkg.in/go-playground/webhooks.v5/github" +) + +func githubMatch(secret string, r *http.Request) bool { + hook, err := github.New(github.Options.Secret(secret)) + if err != nil { + return false + } + _, err = hook.Parse(r, + github.CheckRunEvent, + github.CheckSuiteEvent, + github.CommitCommentEvent, + github.CreateEvent, + github.DeleteEvent, + github.DeploymentEvent, + github.DeploymentStatusEvent, + github.ForkEvent, + github.GollumEvent, + github.InstallationEvent, + github.InstallationRepositoriesEvent, + github.IntegrationInstallationEvent, + github.IntegrationInstallationRepositoriesEvent, + github.IssueCommentEvent, + github.IssuesEvent, + github.LabelEvent, + github.MemberEvent, + github.MembershipEvent, + github.MilestoneEvent, + github.MetaEvent, + github.OrganizationEvent, + github.OrgBlockEvent, + github.PageBuildEvent, + github.PingEvent, + github.ProjectCardEvent, + github.ProjectColumnEvent, + github.ProjectEvent, + github.PublicEvent, + github.PullRequestEvent, + github.PullRequestReviewEvent, + github.PullRequestReviewCommentEvent, + github.PushEvent, + github.ReleaseEvent, + github.RepositoryEvent, + github.RepositoryVulnerabilityAlertEvent, + github.SecurityAdvisoryEvent, + github.StatusEvent, + github.TeamEvent, + github.TeamAddEvent, + github.WatchEvent, + ) + return err == nil +} diff --git a/server/auth/webhook/gitlab.go b/server/auth/webhook/gitlab.go new file mode 100644 index 000000000000..3e5d794f7fee --- /dev/null +++ b/server/auth/webhook/gitlab.go @@ -0,0 +1,28 @@ +package webhook + +import ( + "net/http" + + "gopkg.in/go-playground/webhooks.v5/gitlab" +) + +func gitlabMatch(secret string, r *http.Request) bool { + hook, err := gitlab.New(gitlab.Options.Secret(secret)) + if err != nil { + return false + } + _, err = hook.Parse(r, + gitlab.PushEvents, + gitlab.TagEvents, + gitlab.IssuesEvents, + gitlab.ConfidentialIssuesEvents, + gitlab.CommentEvents, + gitlab.MergeRequestEvents, + gitlab.WikiPageEvents, + gitlab.PipelineEvents, + gitlab.BuildEvents, + gitlab.JobEvents, + gitlab.SystemHookEvents, + ) + return err == nil +} diff --git a/server/auth/webhook/interceptor.go b/server/auth/webhook/interceptor.go new file mode 100644 index 000000000000..2d43b81403dc --- /dev/null +++ b/server/auth/webhook/interceptor.go @@ -0,0 +1,97 @@ +package webhook + +import ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "strings" + + log "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/yaml" +) + +type webhookClient struct { + // e.g "github" + Type string `json:"type"` + // e.g. "shh!" + Secret string `json:"secret"` +} + +type matcher = func(secret string, r *http.Request) bool + +// parser for each types, these should be fast, i.e. no database or API interactions +var webhookParsers = map[string]matcher{ + "bitbucket": bitbucketMatch, + "bitbucketserver": bitbucketserverMatch, + "github": githubMatch, + "gitlab": gitlabMatch, +} + +const pathPrefix = "/api/v1/events/" + +// Interceptor creates an annotator that verifies webhook signatures and adds the appropriate access token to the request. +func Interceptor(client kubernetes.Interface) func(w http.ResponseWriter, r *http.Request, next http.Handler) { + return func(w http.ResponseWriter, r *http.Request, next http.Handler) { + err := addWebhookAuthorization(r, client) + if err != nil { + log.WithError(err).Error("Failed to process webhook request") + w.WriteHeader(403) + // hide the message from the user, because it could help them attack us + _, _ = w.Write([]byte(`{"message": "failed to process webhook request"}`)) + } else { + next.ServeHTTP(w, r) + } + } +} + +func addWebhookAuthorization(r *http.Request, kube kubernetes.Interface) error { + // try and exit quickly before we do anything API calls + if r.Method != "POST" || len(r.Header["Authorization"]) > 0 || !strings.HasPrefix(r.URL.Path, pathPrefix) { + return nil + } + parts := strings.SplitN(strings.TrimPrefix(r.URL.Path, pathPrefix), "/", 2) + if len(parts) != 2 { + return nil + } + namespace := parts[0] + secretsInterface := kube.CoreV1().Secrets(namespace) + webhookClients, err := secretsInterface.Get("argo-workflows-webhook-clients", metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("failed to get webhook clients: %w", err) + } + // we need to read the request body to check the signature, but we still need it for the GRPC request, + // so read it all now, and then reinstate when we are done + buf, _ := ioutil.ReadAll(r.Body) + defer func() { r.Body = ioutil.NopCloser(bytes.NewBuffer(buf)) }() + serviceAccountInterface := kube.CoreV1().ServiceAccounts(namespace) + for serviceAccountName, data := range webhookClients.Data { + r.Body = ioutil.NopCloser(bytes.NewBuffer(buf)) + client := &webhookClient{} + err := yaml.Unmarshal(data, client) + if err != nil { + return fmt.Errorf("failed to unmarshal webhook client \"%s\": %w", serviceAccountName, err) + } + log.WithFields(log.Fields{"serviceAccountName": serviceAccountName, "webhookType": client.Type}).Debug("Attempting to match webhook request") + ok := webhookParsers[client.Type](client.Secret, r) + if ok { + log.WithField("serviceAccountName", serviceAccountName).Debug("Matched webhook request") + serviceAccount, err := serviceAccountInterface.Get(serviceAccountName, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("failed to get service account \"%s\": %w", serviceAccountName, err) + } + if len(serviceAccount.Secrets) == 0 { + return fmt.Errorf("failed to get secret for service account \"%s\": no secrets", serviceAccountName) + } + tokenSecret, err := secretsInterface.Get(serviceAccount.Secrets[0].Name, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("failed to get token secret \"%s\": %w", tokenSecret, err) + } + r.Header["Authorization"] = []string{"Bearer " + string(tokenSecret.Data["token"])} + return nil + } + } + return nil +} diff --git a/server/auth/webhook/interceptor_test.go b/server/auth/webhook/interceptor_test.go new file mode 100644 index 000000000000..cf8af53f6a92 --- /dev/null +++ b/server/auth/webhook/interceptor_test.go @@ -0,0 +1,137 @@ +package webhook + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +type testHTTPHandler struct{} + +func (t testHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +} + +func TestInterceptor(t *testing.T) { + // we ignore these + t.Run("WrongMethod", func(t *testing.T) { + r, _ := intercept("GET", "/api/v1/events/", nil) + assert.Empty(t, r.Header["Authorization"]) + }) + t.Run("ExistingAuthorization", func(t *testing.T) { + r, _ := intercept("POST", "/api/v1/events/my-ns/my-d", map[string]string{"Authorization": "existing"}) + assert.Equal(t, []string{"existing"}, r.Header["Authorization"]) + }) + t.Run("WrongPathPrefix", func(t *testing.T) { + r, _ := intercept("POST", "/api/v1/xxx/", nil) + assert.Empty(t, r.Header["Authorization"]) + }) + t.Run("NoNamespace", func(t *testing.T) { + r, w := intercept("POST", "/api/v1/events//my-d", nil) + assert.Empty(t, r.Header["Authorization"]) + // we check the status code here - because we get a 403 + assert.Equal(t, 403, w.Code) + assert.Equal(t, `{"message": "failed to process webhook request"}`, w.Body.String()) + }) + t.Run("NoDiscriminator", func(t *testing.T) { + r, _ := intercept("POST", "/api/v1/events/my-ns/", nil) + assert.Empty(t, r.Header["Authorization"]) + }) + // we accept these + t.Run("Bitbucket", func(t *testing.T) { + r, _ := intercept("POST", "/api/v1/events/my-ns/my-d", map[string]string{ + "X-Event-Key": "repo:push", + "X-Hook-UUID": "sh!", + }) + assert.Equal(t, []string{"Bearer my-bitbucket-token"}, r.Header["Authorization"]) + }) + t.Run("Bitbucketserver", func(t *testing.T) { + r, _ := intercept("POST", "/api/v1/events/my-ns/my-d", map[string]string{ + "X-Event-Key": "pr:modified", + "X-Hub-Signature": "0000000926ceeb8dcd67d5979fd7d726e3905af6d220f7fd6b2d8cce946906f7cf35963", + }) + assert.Equal(t, []string{"Bearer my-bitbucketserver-token"}, r.Header["Authorization"]) + }) + t.Run("Github", func(t *testing.T) { + r, _ := intercept("POST", "/api/v1/events/my-ns/my-d", map[string]string{ + "X-Github-Event": "push", + "X-Hub-Signature": "00000ba880174336fbe22d4723a67ba5c4c356ec9c696", + }) + assert.Equal(t, []string{"Bearer my-github-token"}, r.Header["Authorization"]) + }) + t.Run("Gitlab", func(t *testing.T) { + r, _ := intercept("POST", "/api/v1/events/my-ns/my-d", map[string]string{ + "X-Gitlab-Event": "Push Hook", + "X-Gitlab-Token": "sh!", + }) + assert.Equal(t, []string{"Bearer my-gitlab-token"}, r.Header["Authorization"]) + }) +} + +func intercept(method string, target string, headers map[string]string) (*http.Request, *httptest.ResponseRecorder) { + // set-up + k := fake.NewSimpleClientset( + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "argo-workflows-webhook-clients", Namespace: "my-ns"}, + Data: map[string][]byte{ + "bitbucket": []byte("type: bitbucket\nsecret: sh!"), + "bitbucketserver": []byte("type: bitbucketserver\nsecret: sh!"), + "github": []byte("type: github\nsecret: sh!"), + "gitlab": []byte("type: gitlab\nsecret: sh!"), + }, + }, + // bitbucket + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{Name: "bitbucket", Namespace: "my-ns"}, + Secrets: []corev1.ObjectReference{{Name: "bitbucket-token"}}, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "bitbucket-token", Namespace: "my-ns"}, + Data: map[string][]byte{"token": []byte("my-bitbucket-token")}, + }, + // bitbucketserver + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{Name: "bitbucketserver", Namespace: "my-ns"}, + Secrets: []corev1.ObjectReference{{Name: "bitbucketserver-token"}}, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "bitbucketserver-token", Namespace: "my-ns"}, + Data: map[string][]byte{"token": []byte("my-bitbucketserver-token")}, + }, + // github + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{Name: "github", Namespace: "my-ns"}, + Secrets: []corev1.ObjectReference{{Name: "github-token"}}, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "github-token", Namespace: "my-ns"}, + Data: map[string][]byte{"token": []byte("my-github-token")}, + }, + // gitlab + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{Name: "gitlab", Namespace: "my-ns"}, + Secrets: []corev1.ObjectReference{{Name: "gitlab-token"}}, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "gitlab-token", Namespace: "my-ns"}, + Data: map[string][]byte{"token": []byte("my-gitlab-token")}, + }, + ) + i := Interceptor(k) + w := httptest.NewRecorder() + b := &bytes.Buffer{} + b.Write([]byte("{}")) + r := httptest.NewRequest(method, target, b) + for k, v := range headers { + r.Header.Set(k, v) + } + h := &testHTTPHandler{} + // act + i(w, r, h) + return r, w +} diff --git a/server/event/dispatch/operation.go b/server/event/dispatch/operation.go new file mode 100644 index 000000000000..720fc2e4e689 --- /dev/null +++ b/server/event/dispatch/operation.go @@ -0,0 +1,151 @@ +package dispatch + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/antonmedv/expr" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc/metadata" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/retry" + + wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" + "github.com/argoproj/argo/server/auth" + "github.com/argoproj/argo/util/instanceid" + "github.com/argoproj/argo/util/labels" + "github.com/argoproj/argo/workflow/common" + "github.com/argoproj/argo/workflow/creator" +) + +type Operation struct { + ctx context.Context + instanceIDService instanceid.Service + events []wfv1.WorkflowEventBinding + env map[string]interface{} +} + +func NewOperation(ctx context.Context, instanceIDService instanceid.Service, events []wfv1.WorkflowEventBinding, namespace, discriminator string, payload *wfv1.Item) (*Operation, error) { + env, err := expressionEnvironment(ctx, namespace, discriminator, payload) + if err != nil { + return nil, fmt.Errorf("failed to create workflow template expression environment: %w", err) + } + return &Operation{ + ctx: ctx, + instanceIDService: instanceIDService, + events: events, + env: env, + }, nil +} + +func (o *Operation) Dispatch() { + log.Debug("Executing event dispatch") + + data, _ := json.MarshalIndent(o.env, "", " ") + log.Debugln(string(data)) + + for _, event := range o.events { + // we use a predicable suffix for the name so that lost connections cannot result in the same workflow being created twice + // being created twice + nameSuffix := fmt.Sprintf("%v", time.Now().Unix()) + err := wait.ExponentialBackoff(retry.DefaultRetry, func() (bool, error) { + _, err := o.dispatch(event, nameSuffix) + return err == nil, err + }) + if err != nil { + log.WithError(err).WithFields(log.Fields{"namespace": event.Namespace, "event": event.Name}).Error("failed to dispatch from event") + } + } +} + +func (o *Operation) dispatch(wfeb wfv1.WorkflowEventBinding, nameSuffix string) (*wfv1.Workflow, error) { + selector := wfeb.Spec.Event.Selector + result, err := expr.Eval(selector, o.env) + if err != nil { + return nil, fmt.Errorf("failed to evaluate workflow template expression: %w", err) + } + matched, boolExpr := result.(bool) + log.WithFields(log.Fields{"namespace": wfeb.Namespace, "event": wfeb.Name, "selector": selector, "matched": matched, "boolExpr": boolExpr}).Debug("Selector evaluation") + submit := wfeb.Spec.Submit + if !boolExpr { + return nil, errors.New("malformed workflow template expression: did not evaluate to boolean") + } else if matched && submit != nil { + client := auth.GetWfClient(o.ctx) + ref := wfeb.Spec.Submit.WorkflowTemplateRef + var tmpl wfv1.WorkflowSpecHolder + var err error + if ref.ClusterScope { + tmpl, err = client.ArgoprojV1alpha1().ClusterWorkflowTemplates().Get(ref.Name, metav1.GetOptions{}) + } else { + tmpl, err = client.ArgoprojV1alpha1().WorkflowTemplates(wfeb.Namespace).Get(ref.Name, metav1.GetOptions{}) + } + if err != nil { + return nil, fmt.Errorf("failed to get workflow template: %w", err) + } + err = o.instanceIDService.Validate(tmpl) + if err != nil { + return nil, fmt.Errorf("failed to validate workflow template instanceid: %w", err) + } + wf := common.NewWorkflowFromWorkflowTemplate(tmpl.GetName(), tmpl.GetWorkflowMetadata(), ref.ClusterScope) + o.instanceIDService.Label(wf) + // make sure we have a predicable name, so re-creation doesn't create two workflows + wf.SetName(wf.GetGenerateName() + nameSuffix) + // users will always want to know why a workflow was submitted, + // so we label with creator (which is a standard) and the name of the triggering event + creator.Label(o.ctx, wf) + labels.Label(wf, common.LabelKeyWorkflowEventBinding, wfeb.Name) + if submit.Arguments != nil { + for _, p := range submit.Arguments.Parameters { + if p.ValueFrom == nil { + return nil, fmt.Errorf("malformed workflow template parameter \"%s\": validFrom is nil", p.Name) + } + result, err := expr.Eval(p.ValueFrom.Event, o.env) + if err != nil { + return nil, fmt.Errorf("failed to evaluate workflow template parameter \"%s\" expression: %w", p.Name, err) + } + intOrString := intstr.Parse(fmt.Sprintf("%v", result)) + wf.Spec.Arguments.Parameters = append(wf.Spec.Arguments.Parameters, wfv1.Parameter{Name: p.Name, Value: &intOrString}) + } + } + wf, err = client.ArgoprojV1alpha1().Workflows(wfeb.Namespace).Create(wf) + if err != nil { + return nil, fmt.Errorf("failed to create workflow: %w", err) + } + return wf, nil + } + return nil, nil +} + +func expressionEnvironment(ctx context.Context, namespace, discriminator string, payload *wfv1.Item) (map[string]interface{}, error) { + src := map[string]interface{}{ + "namespace": namespace, + "discriminator": discriminator, + "metadata": metaData(ctx), + "payload": payload, + } + data, err := json.Marshal(src) + if err != nil { + return nil, err + } + env := make(map[string]interface{}) + return env, json.Unmarshal(data, &env) +} + +func metaData(ctx context.Context) map[string]interface{} { + meta := make(map[string]interface{}) + md, _ := metadata.FromIncomingContext(ctx) + for k, v := range md { + // only allow headers `X-` headers, e.g. `X-Github-Action` + // otherwise, deny, e.g. deny `authorization` as this would leak security credentials + if strings.HasPrefix(k, "x-") { + meta[k] = v + } + } + return meta +} diff --git a/server/event/dispatch/operation_test.go b/server/event/dispatch/operation_test.go new file mode 100644 index 000000000000..b9ff76a60bdd --- /dev/null +++ b/server/event/dispatch/operation_test.go @@ -0,0 +1,97 @@ +package dispatch + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/metadata" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" + "github.com/argoproj/argo/pkg/client/clientset/versioned/fake" + "github.com/argoproj/argo/server/auth" + "github.com/argoproj/argo/server/auth/jws" + "github.com/argoproj/argo/util/instanceid" + "github.com/argoproj/argo/workflow/common" +) + +func Test_metaData(t *testing.T) { + t.Run("Empty", func(t *testing.T) { + data := metaData(context.TODO()) + assert.Empty(t, data) + }) + t.Run("Headers", func(t *testing.T) { + ctx := metadata.NewIncomingContext(context.TODO(), metadata.MD{ + "x-valid": []string{"true"}, + "ignored": []string{"false"}, + }) + data := metaData(ctx) + if assert.Len(t, data, 1) { + assert.Equal(t, []string{"true"}, data["x-valid"]) + } + }) +} + +func TestNewOperation(t *testing.T) { + // set-up + client := fake.NewSimpleClientset( + &wfv1.ClusterWorkflowTemplate{ + ObjectMeta: metav1.ObjectMeta{Name: "my-cwft", Labels: map[string]string{common.LabelKeyControllerInstanceID: "my-instanceid"}}, + }, + &wfv1.WorkflowTemplate{ + ObjectMeta: metav1.ObjectMeta{Name: "my-wft", Namespace: "my-ns", Labels: map[string]string{common.LabelKeyControllerInstanceID: "my-instanceid"}}, + }, + ) + ctx := context.WithValue(context.WithValue(context.Background(), auth.WfKey, client), auth.ClaimSetKey, &jws.ClaimSet{Sub: "my-sub"}) + + // act + operation, err := NewOperation(ctx, instanceid.NewService("my-instanceid"), []wfv1.WorkflowEventBinding{ + { + ObjectMeta: metav1.ObjectMeta{Name: "my-wfeb-1", Namespace: "my-ns"}, + Spec: wfv1.WorkflowEventBindingSpec{ + Event: wfv1.Event{Selector: "true"}, + Submit: &wfv1.Submit{ + WorkflowTemplateRef: wfv1.WorkflowTemplateRef{Name: "my-cwft", ClusterScope: true}, + Arguments: &wfv1.Arguments{Parameters: []wfv1.Parameter{{Name: "my-param", ValueFrom: &wfv1.ValueFrom{Event: `"foo"`}}}}, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "my-wfeb-2", Namespace: "my-ns"}, + Spec: wfv1.WorkflowEventBindingSpec{ + Event: wfv1.Event{Selector: "true"}, + Submit: &wfv1.Submit{ + WorkflowTemplateRef: wfv1.WorkflowTemplateRef{Name: "my-wft"}, + Arguments: &wfv1.Arguments{Parameters: []wfv1.Parameter{{Name: "my-param", ValueFrom: &wfv1.ValueFrom{Event: `"foo"`}}}}, + }, + }, + }, + }, "my-ns", "my-discriminator", &wfv1.Item{}) + assert.NoError(t, err) + operation.Dispatch() + + // assert + list, err := client.ArgoprojV1alpha1().Workflows("my-ns").List(metav1.ListOptions{}) + if assert.NoError(t, err) && assert.Len(t, list.Items, 2) { + for _, wf := range list.Items { + assert.Equal(t, "my-instanceid", wf.Labels[common.LabelKeyControllerInstanceID]) + assert.Equal(t, "my-sub", wf.Labels[common.LabelKeyCreator]) + assert.Contains(t, wf.Labels, common.LabelKeyWorkflowEventBinding) + fromString := intstr.FromString(`foo`) + assert.Equal(t, []wfv1.Parameter{{Name: "my-param", Value: &fromString}}, wf.Spec.Arguments.Parameters) + + } + } +} + +func Test_expressionEnvironment(t *testing.T) { + env, err := expressionEnvironment(context.TODO(), "my-ns", "my-d", &wfv1.Item{Value: []byte(`"foo"`)}) + if assert.NoError(t, err) { + assert.Equal(t, "my-ns", env["namespace"]) + assert.Equal(t, "my-d", env["discriminator"]) + assert.Contains(t, env, "metadata") + assert.Equal(t, "foo", env["payload"]) + } +} diff --git a/server/event/event_server.go b/server/event/event_server.go new file mode 100644 index 000000000000..d1d59815d774 --- /dev/null +++ b/server/event/event_server.go @@ -0,0 +1,84 @@ +package event + +import ( + "context" + "sync" + + log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + eventpkg "github.com/argoproj/argo/pkg/apiclient/event" + "github.com/argoproj/argo/server/auth" + "github.com/argoproj/argo/server/event/dispatch" + "github.com/argoproj/argo/util/instanceid" +) + +type Controller struct { + instanceIDService instanceid.Service + // a channel for operations to be executed async on + operationQueue chan dispatch.Operation + workerCount int +} + +var _ eventpkg.EventServiceServer = &Controller{} + +func NewController(instanceIDService instanceid.Service, operationQueueSize, workerCount int) *Controller { + log.WithFields(log.Fields{"workerCount": workerCount, "operationQueueSize": operationQueueSize}).Info("Creating event controller") + + return &Controller{ + instanceIDService: instanceIDService, + // so we can have `operationQueueSize` operations outstanding before we start putting back pressure on the senders + operationQueue: make(chan dispatch.Operation, operationQueueSize), + workerCount: workerCount, + } +} + +func (s *Controller) Run(stopCh <-chan struct{}) { + + // this `WaitGroup` allows us to wait for all events to dispatch before exiting + wg := sync.WaitGroup{} + + for w := 0; w < s.workerCount; w++ { + go func() { + defer wg.Done() + for operation := range s.operationQueue { + operation.Dispatch() + } + }() + wg.Add(1) + } + + <-stopCh + + // stop accepting new events + close(s.operationQueue) + + log.WithFields(log.Fields{"operations": len(s.operationQueue)}).Info("Waiting until all remaining events are processed") + + // no more new events, process the existing events + wg.Wait() +} + +func (s *Controller) ReceiveEvent(ctx context.Context, req *eventpkg.EventRequest) (*eventpkg.EventResponse, error) { + + options := metav1.ListOptions{} + s.instanceIDService.With(&options) + + list, err := auth.GetWfClient(ctx).ArgoprojV1alpha1().WorkflowEventBindings(req.Namespace).List(options) + if err != nil { + return nil, err + } + + operation, err := dispatch.NewOperation(ctx, s.instanceIDService, list.Items, req.Namespace, req.Discriminator, req.Payload) + if err != nil { + return nil, err + } + + select { + case s.operationQueue <- *operation: + return &eventpkg.EventResponse{}, nil + default: + return nil, errors.NewServiceUnavailable("operation queue full") + } +} diff --git a/server/event/event_server_test.go b/server/event/event_server_test.go new file mode 100644 index 000000000000..41de46fdac52 --- /dev/null +++ b/server/event/event_server_test.go @@ -0,0 +1,35 @@ +package event + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" + + eventpkg "github.com/argoproj/argo/pkg/apiclient/event" + wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" + "github.com/argoproj/argo/pkg/client/clientset/versioned/fake" + "github.com/argoproj/argo/server/auth" + "github.com/argoproj/argo/util/instanceid" +) + +func TestController(t *testing.T) { + clientset := fake.NewSimpleClientset() + s := NewController(instanceid.NewService("my-instanceid"), 1, 1) + + ctx := context.WithValue(context.TODO(), auth.WfKey, clientset) + _, err := s.ReceiveEvent(ctx, &eventpkg.EventRequest{Namespace: "my-ns", Payload: &wfv1.Item{}}) + assert.NoError(t, err) + + assert.Len(t, s.operationQueue, 1, "one event to be processed") + + _, err = s.ReceiveEvent(ctx, &eventpkg.EventRequest{}) + assert.EqualError(t, err, "operation queue full", "backpressure when queue is full") + + stopCh := make(chan struct{}, 1) + stopCh <- struct{}{} + s.Run(stopCh) + + assert.Len(t, s.operationQueue, 0, "all events were processed") + +} diff --git a/server/workflow/workflow_server_test.go b/server/workflow/workflow_server_test.go index d51bc4a9091a..5d574dcc2a8d 100644 --- a/server/workflow/workflow_server_test.go +++ b/server/workflow/workflow_server_test.go @@ -886,6 +886,7 @@ func TestSubmitWorkflowFromResource(t *testing.T) { if assert.NoError(t, err) { assert.NotNil(t, wf) assert.Contains(t, wf.Labels, common.LabelKeyControllerInstanceID) + assert.Contains(t, wf.Labels, common.LabelKeyCreator) assert.Contains(t, wf.Labels, "labelTest") assert.Contains(t, wf.Annotations, "annotationTest") } @@ -899,6 +900,7 @@ func TestSubmitWorkflowFromResource(t *testing.T) { if assert.NoError(t, err) { assert.NotNil(t, wf) assert.Contains(t, wf.Labels, common.LabelKeyControllerInstanceID) + assert.Contains(t, wf.Labels, common.LabelKeyCreator) } }) t.Run("SubmitFromClusterWorkflowTemplate", func(t *testing.T) { @@ -910,6 +912,7 @@ func TestSubmitWorkflowFromResource(t *testing.T) { if assert.NoError(t, err) { assert.NotNil(t, wf) assert.Contains(t, wf.Labels, common.LabelKeyControllerInstanceID) + assert.Contains(t, wf.Labels, common.LabelKeyCreator) assert.Contains(t, wf.Labels, "labelTest") assert.Contains(t, wf.Annotations, "annotationTest") } diff --git a/test/e2e/argo_server_test.go b/test/e2e/argo_server_test.go index 4200986dad5f..2d720fdeae38 100644 --- a/test/e2e/argo_server_test.go +++ b/test/e2e/argo_server_test.go @@ -5,6 +5,7 @@ package e2e import ( "bufio" "crypto/tls" + "io/ioutil" "net/http" "strings" "testing" @@ -21,6 +22,7 @@ import ( wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" "github.com/argoproj/argo/test/e2e/fixtures" + "github.com/argoproj/argo/workflow/common" ) const baseUrl = "http://localhost:2746" @@ -102,6 +104,178 @@ func (s *ArgoServerSuite) TestVersion() { }) } +func (s *ArgoServerSuite) TestSubmitWorkflowTemplateFromGithubWebhook() { + s.bearerToken = "" + + data, err := ioutil.ReadFile("testdata/github-webhook-payload.json") + assert.NoError(s.T(), err) + + s.Given(). + WorkflowTemplate(` +metadata: + name: github-webhook + labels: + argo-e2e: true +spec: + entrypoint: main + workflowMetadata: + labels: + argo-e2e: "true" + templates: + - name: main + container: + image: argoproj/argosay:v2 +`). + WorkflowEventBinding(` +metadata: + name: github-webhook + labels: + argo-e2e: true +spec: + event: + selector: metadata["x-github-event"] == ["push"] + submit: + workflowTemplateRef: + name: github-webhook +`). + When(). + CreateWorkflowTemplates(). + CreateWorkflowEventBinding(). + And(func() { + s.e(). + POST("/api/v1/events/argo/"). + WithHeader("X-Github-Event", "push"). + WithHeader("X-Hub-Signature", "sha1=c09e61386e81c2669e015049350500448148205c"). + WithBytes(data). + Expect(). + Status(200) + }). + WaitForWorkflow(30 * time.Second). + Then(). + ExpectWorkflow(func(t *testing.T, metadata *metav1.ObjectMeta, _ *wfv1.WorkflowStatus) { + assert.Equal(t, "github-webhook", metadata.GetLabels()[common.LabelKeyWorkflowTemplate]) + }) +} + +func (s *ArgoServerSuite) TestSubmitWorkflowTemplateFromEvent() { + s.Given(). + WorkflowTemplate(` +metadata: + name: event-consumer + labels: + argo-e2e: true +spec: + entrypoint: main + workflowMetadata: + labels: + argo-e2e: "true" + arguments: + parameters: + - name: salutation + value: "hello" + templates: + - name: main + steps: + - - name: a + template: argosay + arguments: + parameters: + - name: salutation + value: "{{workflow.parameters.salutation}}" + - name: appellation + value: "{{workflow.parameters.appellation}}" + + - name: argosay + inputs: + parameters: + - name: salutation + - name: appellation + container: + image: argoproj/argosay:v2 + args: [echo, "{{inputs.parameters.salutation}} {{inputs.parameters.appellation}}"] +`). + WorkflowEventBinding(` +metadata: + name: event-consumer + labels: + argo-e2e: true +spec: + event: + selector: payload.appellation != "" && metadata["x-argo-e2e"] == ["true"] + submit: + workflowTemplateRef: + name: event-consumer + arguments: + parameters: + - name: appellation + valueFrom: + event: payload.appellation +`). + When(). + CreateWorkflowEventBinding(). + CreateWorkflowTemplates(). + And(func() { + s.e(). + POST("/api/v1/events/argo/"). + WithHeader("X-Argo-E2E", "true"). + WithBytes([]byte(`{"appellation": "Mr Chips"}`)). + Expect(). + Status(200) + }). + WaitForWorkflow(30 * time.Second). + Then(). + ExpectWorkflow(func(t *testing.T, metadata *metav1.ObjectMeta, _ *wfv1.WorkflowStatus) { + assert.Equal(t, "event-consumer", metadata.GetLabels()[common.LabelKeyWorkflowTemplate]) + }) +} + +func (s *ArgoServerSuite) TestSubmitClusterWorkflowTemplateFromEvent() { + s.Given(). + ClusterWorkflowTemplate(` +metadata: + name: event-consumer + labels: + argo-e2e: true +spec: + entrypoint: main + workflowMetadata: + labels: + argo-e2e: "true" + templates: + - name: main + container: + image: argoproj/argosay:v2 +`). + WorkflowEventBinding(` +metadata: + name: event-consumer + labels: + argo-e2e: true +spec: + event: + selector: true + submit: + workflowTemplateRef: + name: event-consumer + clusterScope: true +`). + When(). + CreateWorkflowEventBinding(). + CreateClusterWorkflowTemplates(). + And(func() { + s.e(). + POST("/api/v1/events/argo/"). + WithBytes([]byte(`{}`)). + Expect(). + Status(200) + }). + WaitForWorkflow(30 * time.Second). + Then(). + ExpectWorkflow(func(t *testing.T, metadata *metav1.ObjectMeta, _ *wfv1.WorkflowStatus) { + assert.Equal(t, "event-consumer", metadata.GetLabels()[common.LabelKeyClusterWorkflowTemplate]) + }) +} + func (s *ArgoServerSuite) TestGetUserInfo() { s.e().GET("/api/v1/userinfo"). Expect(). diff --git a/test/e2e/fixtures/e2e_suite.go b/test/e2e/fixtures/e2e_suite.go index 542e2e7ac619..e08f45eaa1c3 100644 --- a/test/e2e/fixtures/e2e_suite.go +++ b/test/e2e/fixtures/e2e_suite.go @@ -41,6 +41,7 @@ type E2ESuite struct { Persistence *Persistence RestConfig *rest.Config wfClient v1alpha1.WorkflowInterface + wfebClient v1alpha1.WorkflowEventBindingInterface wfTemplateClient v1alpha1.WorkflowTemplateInterface cwfTemplateClient v1alpha1.ClusterWorkflowTemplateInterface cronClient v1alpha1.CronWorkflowInterface @@ -58,6 +59,7 @@ func (s *E2ESuite) SetupSuite() { s.KubeClient, err = kubernetes.NewForConfig(s.RestConfig) s.CheckError(err) s.wfClient = versioned.NewForConfigOrDie(s.RestConfig).ArgoprojV1alpha1().Workflows(Namespace) + s.wfebClient = versioned.NewForConfigOrDie(s.RestConfig).ArgoprojV1alpha1().WorkflowEventBindings(Namespace) s.wfTemplateClient = versioned.NewForConfigOrDie(s.RestConfig).ArgoprojV1alpha1().WorkflowTemplates(Namespace) s.cronClient = versioned.NewForConfigOrDie(s.RestConfig).ArgoprojV1alpha1().CronWorkflows(Namespace) s.Persistence = newPersistence(s.KubeClient) @@ -189,6 +191,16 @@ func (s *E2ESuite) DeleteResources(label string) { } } + // delete all workflow events + events, err := s.wfebClient.List(metav1.ListOptions{LabelSelector: label}) + s.CheckError(err) + + for _, item := range events.Items { + log.WithField("template", item.Name).Debug("Deleting workflow event") + err = s.wfebClient.Delete(item.Name, nil) + s.CheckError(err) + } + // delete all workflow templates wfTmpl, err := s.wfTemplateClient.List(metav1.ListOptions{LabelSelector: label}) s.CheckError(err) @@ -351,6 +363,7 @@ func (s *E2ESuite) Given() *Given { return &Given{ t: s.T(), client: s.wfClient, + wfebClient: s.wfebClient, wfTemplateClient: s.wfTemplateClient, cwfTemplateClient: s.cwfTemplateClient, cronClient: s.cronClient, diff --git a/test/e2e/fixtures/given.go b/test/e2e/fixtures/given.go index e246fda6ba0e..12a5f7c97e79 100644 --- a/test/e2e/fixtures/given.go +++ b/test/e2e/fixtures/given.go @@ -17,11 +17,13 @@ import ( type Given struct { t *testing.T client v1alpha1.WorkflowInterface + wfebClient v1alpha1.WorkflowEventBindingInterface wfTemplateClient v1alpha1.WorkflowTemplateInterface cwfTemplateClient v1alpha1.ClusterWorkflowTemplateInterface cronClient v1alpha1.CronWorkflowInterface hydrator hydrator.Interface wf *wfv1.Workflow + wfeb *wfv1.WorkflowEventBinding wfTemplates []*wfv1.WorkflowTemplate cwfTemplates []*wfv1.ClusterWorkflowTemplate cronWf *wfv1.CronWorkflow @@ -34,6 +36,14 @@ type Given struct { // 1. A file name if it starts with "@" // 2. Raw YAML. func (g *Given) Workflow(text string) *Given { + g.t.Helper() + g.wf = &wfv1.Workflow{} + g.readResource(text, g.wf) + g.checkImages(g.wf.Spec.Templates) + return g +} + +func (g *Given) readResource(text string, v metav1.Object) { g.t.Helper() var file string if strings.HasPrefix(text, "@") { @@ -53,24 +63,22 @@ func (g *Given) Workflow(text string) *Given { } file = f.Name() } - // read the file in + { file, err := ioutil.ReadFile(file) if err != nil { g.t.Fatal(err) } - g.wf = &wfv1.Workflow{} - err = yaml.Unmarshal(file, g.wf) + err = yaml.Unmarshal(file, v) if err != nil { g.t.Fatal(err) } - g.checkImages(g.wf.Spec.Templates) - g.checkLabels(g.wf.ObjectMeta) + g.checkLabels(v) } - return g } func (g *Given) checkImages(templates []wfv1.Template) { + g.t.Helper() // Using an arbitrary image will result in slow and flakey tests as we can't really predict when they'll be // downloaded or evicted. To keep tests fast and reliable you must use whitelisted images. imageWhitelist := func(image string) bool { @@ -90,91 +98,45 @@ func (g *Given) checkImages(templates []wfv1.Template) { } } -func (g *Given) checkLabels(m metav1.ObjectMeta) { +func (g *Given) checkLabels(m metav1.Object) { + g.t.Helper() if m.GetLabels()[Label] == "" && m.GetLabels()[LabelCron] == "" { - g.t.Fatalf("%s%s does not have one of {%s, %s} labels", m.Name, m.GenerateName, Label, LabelCron) + g.t.Fatalf("%s%s does not have one of {%s, %s} labels", m.GetName(), m.GetGenerateName(), Label, LabelCron) } } func (g *Given) WorkflowName(name string) *Given { + g.t.Helper() g.workflowName = name return g } +func (g *Given) WorkflowEventBinding(text string) *Given { + g.t.Helper() + g.wfeb = &wfv1.WorkflowEventBinding{} + g.readResource(text, g.wfeb) + return g +} + func (g *Given) WorkflowTemplate(text string) *Given { - var file string - if strings.HasPrefix(text, "@") { - file = strings.TrimPrefix(text, "@") - } else { - f, err := ioutil.TempFile("", "argo_e2e") - if err != nil { - g.t.Fatal(err) - } - _, err = f.Write([]byte(text)) - if err != nil { - g.t.Fatal(err) - } - err = f.Close() - if err != nil { - g.t.Fatal(err) - } - file = f.Name() - } - // read the file in - { - file, err := ioutil.ReadFile(file) - if err != nil { - g.t.Fatal(err) - } - wfTemplate := &wfv1.WorkflowTemplate{} - err = yaml.Unmarshal(file, wfTemplate) - if err != nil { - g.t.Fatal(err) - } - g.checkImages(wfTemplate.Spec.Templates) - g.checkLabels(wfTemplate.ObjectMeta) - g.wfTemplates = append(g.wfTemplates, wfTemplate) - } + g.t.Helper() + wfTemplate := &wfv1.WorkflowTemplate{} + g.readResource(text, wfTemplate) + g.checkImages(wfTemplate.Spec.Templates) + g.wfTemplates = append(g.wfTemplates, wfTemplate) return g } func (g *Given) CronWorkflow(text string) *Given { - var file string - if strings.HasPrefix(text, "@") { - file = strings.TrimPrefix(text, "@") - } else { - f, err := ioutil.TempFile("", "argo_e2e") - if err != nil { - g.t.Fatal(err) - } - _, err = f.Write([]byte(text)) - if err != nil { - g.t.Fatal(err) - } - err = f.Close() - if err != nil { - g.t.Fatal(err) - } - file = f.Name() - } - // read the file in - { - file, err := ioutil.ReadFile(file) - if err != nil { - g.t.Fatal(err) - } - g.cronWf = &wfv1.CronWorkflow{} - err = yaml.Unmarshal(file, g.cronWf) - if err != nil { - g.t.Fatal(err) - } - g.checkImages(g.cronWf.Spec.WorkflowSpec.Templates) - g.checkLabels(g.cronWf.ObjectMeta) - } + g.t.Helper() + g.cronWf = &wfv1.CronWorkflow{} + g.readResource(text, g.cronWf) + g.checkImages(g.cronWf.Spec.WorkflowSpec.Templates) return g } func (g *Given) RunCli(args []string, block func(t *testing.T, output string, err error)) *Given { + g.t.Helper() output, err := runCli("../../dist/argo", append([]string{"-n", Namespace}, args...)...) block(g.t, output, err) if g.t.Failed() { @@ -184,38 +146,10 @@ func (g *Given) RunCli(args []string, block func(t *testing.T, output string, er } func (g *Given) ClusterWorkflowTemplate(text string) *Given { - var file string - if strings.HasPrefix(text, "@") { - file = strings.TrimPrefix(text, "@") - } else { - f, err := ioutil.TempFile("", "argo_e2e") - if err != nil { - g.t.Fatal(err) - } - _, err = f.Write([]byte(text)) - if err != nil { - g.t.Fatal(err) - } - err = f.Close() - if err != nil { - g.t.Fatal(err) - } - file = f.Name() - } - // read the file in - { - file, err := ioutil.ReadFile(file) - if err != nil { - g.t.Fatal(err) - } - cwfTemplate := &wfv1.ClusterWorkflowTemplate{} - err = yaml.Unmarshal(file, cwfTemplate) - if err != nil { - g.t.Fatal(err) - } - g.checkLabels(cwfTemplate.ObjectMeta) - g.cwfTemplates = append(g.cwfTemplates, cwfTemplate) - } + g.t.Helper() + cwfTemplate := &wfv1.ClusterWorkflowTemplate{} + g.readResource(text, cwfTemplate) + g.cwfTemplates = append(g.cwfTemplates, cwfTemplate) return g } @@ -223,10 +157,12 @@ func (g *Given) When() *When { return &When{ t: g.t, wf: g.wf, + wfeb: g.wfeb, wfTemplates: g.wfTemplates, cwfTemplates: g.cwfTemplates, cronWf: g.cronWf, client: g.client, + wfebClient: g.wfebClient, wfTemplateClient: g.wfTemplateClient, cwfTemplateClient: g.cwfTemplateClient, cronClient: g.cronClient, diff --git a/test/e2e/fixtures/then.go b/test/e2e/fixtures/then.go index f87273e36715..001c184c262f 100644 --- a/test/e2e/fixtures/then.go +++ b/test/e2e/fixtures/then.go @@ -27,14 +27,17 @@ type Then struct { } func (t *Then) ExpectWorkflow(block func(t *testing.T, metadata *metav1.ObjectMeta, status *wfv1.WorkflowStatus)) *Then { + t.t.Helper() return t.expectWorkflow(t.workflowName, block) } func (t *Then) ExpectWorkflowName(workflowName string, block func(t *testing.T, metadata *metav1.ObjectMeta, status *wfv1.WorkflowStatus)) *Then { + t.t.Helper() return t.expectWorkflow(workflowName, block) } func (t *Then) expectWorkflow(workflowName string, block func(t *testing.T, metadata *metav1.ObjectMeta, status *wfv1.WorkflowStatus)) *Then { + t.t.Helper() if workflowName == "" { t.t.Fatal("No workflow to test") } @@ -56,6 +59,7 @@ func (t *Then) expectWorkflow(workflowName string, block func(t *testing.T, meta } func (t *Then) ExpectCron(block func(t *testing.T, cronWf *wfv1.CronWorkflow)) *Then { + t.t.Helper() if t.cronWorkflowName == "" { t.t.Fatal("No cron workflow to test") } @@ -72,6 +76,7 @@ func (t *Then) ExpectCron(block func(t *testing.T, cronWf *wfv1.CronWorkflow)) * } func (t *Then) ExpectWorkflowList(listOptions metav1.ListOptions, block func(t *testing.T, wfList *wfv1.WorkflowList)) *Then { + t.t.Helper() log.Info("Listing workflows") wfList, err := t.client.List(listOptions) if err != nil { @@ -86,6 +91,7 @@ func (t *Then) ExpectWorkflowList(listOptions metav1.ListOptions, block func(t * } func (t *Then) ExpectAuditEvents(blocks ...func(*testing.T, apiv1.Event)) *Then { + t.t.Helper() eventList, err := t.kubeClient.CoreV1().Events(Namespace).Watch(metav1.ListOptions{}) if err != nil { t.t.Fatal(err) @@ -114,6 +120,7 @@ func (t *Then) ExpectAuditEvents(blocks ...func(*testing.T, apiv1.Event)) *Then } func (t *Then) RunCli(args []string, block func(t *testing.T, output string, err error)) *Then { + t.t.Helper() output, err := runCli("../../dist/argo", append([]string{"-n", Namespace}, args...)...) block(t.t, output, err) if t.t.Failed() { diff --git a/test/e2e/fixtures/when.go b/test/e2e/fixtures/when.go index bceef8536026..76948c89d601 100644 --- a/test/e2e/fixtures/when.go +++ b/test/e2e/fixtures/when.go @@ -1,7 +1,6 @@ package fixtures import ( - "fmt" "testing" "time" @@ -10,7 +9,6 @@ import ( corev1 "k8s.io/api/core/v1" apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" "k8s.io/client-go/kubernetes" wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1" @@ -22,10 +20,12 @@ import ( type When struct { t *testing.T wf *wfv1.Workflow + wfeb *wfv1.WorkflowEventBinding wfTemplates []*wfv1.WorkflowTemplate cwfTemplates []*wfv1.ClusterWorkflowTemplate cronWf *wfv1.CronWorkflow client v1alpha1.WorkflowInterface + wfebClient v1alpha1.WorkflowEventBindingInterface wfTemplateClient v1alpha1.WorkflowTemplateInterface cwfTemplateClient v1alpha1.ClusterWorkflowTemplateInterface cronClient v1alpha1.CronWorkflowInterface @@ -40,6 +40,7 @@ type When struct { } func (w *When) SubmitWorkflow() *When { + w.t.Helper() if w.wf == nil { w.t.Fatal("No workflow to submit") } @@ -54,7 +55,21 @@ func (w *When) SubmitWorkflow() *When { return w } +func (w *When) CreateWorkflowEventBinding() *When { + w.t.Helper() + if w.wfeb == nil { + w.t.Fatal("No workflow event to create") + } + log.WithField("event", w.wfeb.Name).Info("Creating workflow event") + _, err := w.wfebClient.Create(w.wfeb) + if err != nil { + w.t.Fatal(err) + } + return w +} + func (w *When) CreateWorkflowTemplates() *When { + w.t.Helper() if len(w.wfTemplates) == 0 { w.t.Fatal("No workflow templates to create") } @@ -72,6 +87,7 @@ func (w *When) CreateWorkflowTemplates() *When { } func (w *When) CreateClusterWorkflowTemplates() *When { + w.t.Helper() if len(w.cwfTemplates) == 0 { w.t.Fatal("No cluster workflow templates to create") } @@ -89,6 +105,7 @@ func (w *When) CreateClusterWorkflowTemplates() *When { } func (w *When) CreateCronWorkflow() *When { + w.t.Helper() if w.cronWf == nil { w.t.Fatal("No cron workflow to create") } @@ -104,14 +121,22 @@ func (w *When) CreateCronWorkflow() *When { } func (w *When) WaitForWorkflowCondition(test func(wf *wfv1.Workflow) bool, condition string, duration time.Duration) *When { + w.t.Helper() return w.waitForWorkflow(w.workflowName, test, condition, duration) } func (w *When) waitForWorkflow(workflowName string, test func(wf *wfv1.Workflow) bool, condition string, timeout time.Duration) *When { + w.t.Helper() start := time.Now() - logCtx := log.WithFields(log.Fields{"workflow": workflowName, "condition": condition, "timeout": timeout}) + + fieldSelector := "" + if workflowName != "" { + fieldSelector = "metadata.name=" + workflowName + } + + logCtx := log.WithFields(log.Fields{"fieldSelector": fieldSelector, "condition": condition, "timeout": timeout}) logCtx.Info("Waiting for condition") - opts := metav1.ListOptions{FieldSelector: fields.ParseSelectorOrDie(fmt.Sprintf("metadata.name=%s", workflowName)).String()} + opts := metav1.ListOptions{LabelSelector: Label, FieldSelector: fieldSelector} watch, err := w.client.Watch(opts) if err != nil { w.t.Fatal(err) @@ -127,10 +152,11 @@ func (w *When) waitForWorkflow(workflowName string, test func(wf *wfv1.Workflow) case event := <-watch.ResultChan(): wf, ok := event.Object.(*wfv1.Workflow) if ok { - logCtx.WithFields(log.Fields{"type": event.Type, "phase": wf.Status.Phase, "message": wf.Status.Message}).Info("...") + logCtx.WithFields(log.Fields{"workflow": wf.Name, "type": event.Type, "phase": wf.Status.Phase, "message": wf.Status.Message}).Info("...") w.hydrateWorkflow(wf) if test(wf) { logCtx.Infof("Condition met after %v", time.Since(start).Truncate(time.Second)) + w.workflowName = wf.Name return w } } else { @@ -143,38 +169,43 @@ func (w *When) waitForWorkflow(workflowName string, test func(wf *wfv1.Workflow) } func (w *When) hydrateWorkflow(wf *wfv1.Workflow) { + w.t.Helper() err := w.hydrator.Hydrate(wf) if err != nil { w.t.Fatal(err) } } func (w *When) WaitForWorkflowToStart(timeout time.Duration) *When { + w.t.Helper() return w.waitForWorkflow(w.workflowName, func(wf *wfv1.Workflow) bool { return !wf.Status.StartedAt.IsZero() }, "to start", timeout) } func (w *When) WaitForWorkflow(timeout time.Duration) *When { + w.t.Helper() return w.waitForWorkflow(w.workflowName, func(wf *wfv1.Workflow) bool { return !wf.Status.FinishedAt.IsZero() }, "to finish", timeout) } func (w *When) WaitForWorkflowName(workflowName string, timeout time.Duration) *When { + w.t.Helper() return w.waitForWorkflow(workflowName, func(wf *wfv1.Workflow) bool { return !wf.Status.FinishedAt.IsZero() }, "to finish", timeout) } func (w *When) Wait(timeout time.Duration) *When { - logCtx := log.WithFields(log.Fields{"cronWorkflow": w.cronWorkflowName}) - logCtx.Infof("Waiting for %s", humanize.Duration(timeout)) + w.t.Helper() + log.Infof("Waiting for %s", humanize.Duration(timeout)) time.Sleep(timeout) - logCtx.Infof("Done waiting") + log.Infof("Done waiting") return w } func (w *When) DeleteWorkflow() *When { + w.t.Helper() log.WithField("workflow", w.workflowName).Info("Deleting") err := w.client.Delete(w.workflowName, nil) if err != nil { @@ -183,7 +214,17 @@ func (w *When) DeleteWorkflow() *When { return w } +func (w *When) And(block func()) *When { + w.t.Helper() + block() + if w.t.Failed() { + w.t.FailNow() + } + return w +} + func (w *When) RunCli(args []string, block func(t *testing.T, output string, err error)) *When { + w.t.Helper() output, err := runCli("../../dist/argo", append([]string{"-n", Namespace}, args...)...) block(w.t, output, err) if w.t.Failed() { @@ -193,6 +234,7 @@ func (w *When) RunCli(args []string, block func(t *testing.T, output string, err } func (w *When) CreateConfigMap(name string, data map[string]string) *When { + w.t.Helper() //Clean if same map is already exist err := w.kubeClient.CoreV1().ConfigMaps("argo").Delete(name, &metav1.DeleteOptions{}) if err != nil { @@ -209,6 +251,7 @@ func (w *When) CreateConfigMap(name string, data map[string]string) *When { } func (w *When) DeleteConfigMap() *When { + w.t.Helper() err := util.DeleteConfigMap(w.kubeClient, w.configMap) if err != nil { if !apierr.IsNotFound(err) { @@ -220,6 +263,7 @@ func (w *When) DeleteConfigMap() *When { } func (w *When) MemoryQuota(quota string) *When { + w.t.Helper() obj, err := util.CreateHardMemoryQuota(w.kubeClient, "argo", "memory-quota", quota) if err != nil { w.t.Fatal(err) @@ -229,6 +273,7 @@ func (w *When) MemoryQuota(quota string) *When { } func (w *When) StorageQuota(quota string) *When { + w.t.Helper() obj, err := util.CreateHardStorageQuota(w.kubeClient, "argo", "storage-quota", quota) if err != nil { w.t.Fatal(err) @@ -238,6 +283,7 @@ func (w *When) StorageQuota(quota string) *When { } func (w *When) DeleteStorageQuota() *When { + w.t.Helper() err := util.DeleteQuota(w.kubeClient, w.storageQuota) if err != nil { w.t.Fatal(err) @@ -247,6 +293,7 @@ func (w *When) DeleteStorageQuota() *When { } func (w *When) DeleteQuota() *When { + w.t.Helper() err := util.DeleteQuota(w.kubeClient, w.resourceQuota) if err != nil { w.t.Fatal(err) @@ -272,11 +319,13 @@ func (w *When) Given() *Given { return &Given{ t: w.t, client: w.client, + wfebClient: w.wfebClient, wfTemplateClient: w.wfTemplateClient, cwfTemplateClient: w.cwfTemplateClient, cronClient: w.cronClient, hydrator: w.hydrator, wf: w.wf, + wfeb: w.wfeb, wfTemplates: w.wfTemplates, cwfTemplates: w.cwfTemplates, cronWf: w.cronWf, diff --git a/test/e2e/testdata/github-webhook-payload.json b/test/e2e/testdata/github-webhook-payload.json new file mode 100644 index 000000000000..0a3c6cc89f18 --- /dev/null +++ b/test/e2e/testdata/github-webhook-payload.json @@ -0,0 +1 @@ +{"ref":"refs/heads/no-response","before":"0000000000000000000000000000000000000000","after":"9e07a37873923a21d5fb1ba07042758a39b5efce","repository":{"id":227203645,"node_id":"MDEwOlJlcG9zaXRvcnkyMjcyMDM2NDU=","name":"argo","full_name":"alexec/argo","private":false,"owner":{"name":"alexec","email":"alexec@users.noreply.github.com","login":"alexec","id":1142830,"node_id":"MDQ6VXNlcjExNDI4MzA=","avatar_url":"https://avatars1.githubusercontent.com/u/1142830?v=4","gravatar_id":"","url":"https://api.github.com/users/alexec","html_url":"https://github.com/alexec","followers_url":"https://api.github.com/users/alexec/followers","following_url":"https://api.github.com/users/alexec/following{/other_user}","gists_url":"https://api.github.com/users/alexec/gists{/gist_id}","starred_url":"https://api.github.com/users/alexec/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexec/subscriptions","organizations_url":"https://api.github.com/users/alexec/orgs","repos_url":"https://api.github.com/users/alexec/repos","events_url":"https://api.github.com/users/alexec/events{/privacy}","received_events_url":"https://api.github.com/users/alexec/received_events","type":"User","site_admin":false},"html_url":"https://github.com/alexec/argo","description":"Argo Workflows: Get stuff done with Kubernetes.","fork":true,"url":"https://github.com/alexec/argo","forks_url":"https://api.github.com/repos/alexec/argo/forks","keys_url":"https://api.github.com/repos/alexec/argo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/alexec/argo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/alexec/argo/teams","hooks_url":"https://api.github.com/repos/alexec/argo/hooks","issue_events_url":"https://api.github.com/repos/alexec/argo/issues/events{/number}","events_url":"https://api.github.com/repos/alexec/argo/events","assignees_url":"https://api.github.com/repos/alexec/argo/assignees{/user}","branches_url":"https://api.github.com/repos/alexec/argo/branches{/branch}","tags_url":"https://api.github.com/repos/alexec/argo/tags","blobs_url":"https://api.github.com/repos/alexec/argo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/alexec/argo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/alexec/argo/git/refs{/sha}","trees_url":"https://api.github.com/repos/alexec/argo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/alexec/argo/statuses/{sha}","languages_url":"https://api.github.com/repos/alexec/argo/languages","stargazers_url":"https://api.github.com/repos/alexec/argo/stargazers","contributors_url":"https://api.github.com/repos/alexec/argo/contributors","subscribers_url":"https://api.github.com/repos/alexec/argo/subscribers","subscription_url":"https://api.github.com/repos/alexec/argo/subscription","commits_url":"https://api.github.com/repos/alexec/argo/commits{/sha}","git_commits_url":"https://api.github.com/repos/alexec/argo/git/commits{/sha}","comments_url":"https://api.github.com/repos/alexec/argo/comments{/number}","issue_comment_url":"https://api.github.com/repos/alexec/argo/issues/comments{/number}","contents_url":"https://api.github.com/repos/alexec/argo/contents/{+path}","compare_url":"https://api.github.com/repos/alexec/argo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/alexec/argo/merges","archive_url":"https://api.github.com/repos/alexec/argo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/alexec/argo/downloads","issues_url":"https://api.github.com/repos/alexec/argo/issues{/number}","pulls_url":"https://api.github.com/repos/alexec/argo/pulls{/number}","milestones_url":"https://api.github.com/repos/alexec/argo/milestones{/number}","notifications_url":"https://api.github.com/repos/alexec/argo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/alexec/argo/labels{/name}","releases_url":"https://api.github.com/repos/alexec/argo/releases{/id}","deployments_url":"https://api.github.com/repos/alexec/argo/deployments","created_at":1576007663,"updated_at":"2020-06-03T00:33:23Z","pushed_at":1595017689,"git_url":"git://github.com/alexec/argo.git","ssh_url":"git@github.com:alexec/argo.git","clone_url":"https://github.com/alexec/argo.git","svn_url":"https://github.com/alexec/argo","homepage":"https://argoproj.github.io/argo","size":30497,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master","stargazers":0,"master_branch":"master"},"pusher":{"name":"alexec","email":"alexec@users.noreply.github.com"},"sender":{"login":"alexec","id":1142830,"node_id":"MDQ6VXNlcjExNDI4MzA=","avatar_url":"https://avatars1.githubusercontent.com/u/1142830?v=4","gravatar_id":"","url":"https://api.github.com/users/alexec","html_url":"https://github.com/alexec","followers_url":"https://api.github.com/users/alexec/followers","following_url":"https://api.github.com/users/alexec/following{/other_user}","gists_url":"https://api.github.com/users/alexec/gists{/gist_id}","starred_url":"https://api.github.com/users/alexec/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexec/subscriptions","organizations_url":"https://api.github.com/users/alexec/orgs","repos_url":"https://api.github.com/users/alexec/repos","events_url":"https://api.github.com/users/alexec/events{/privacy}","received_events_url":"https://api.github.com/users/alexec/received_events","type":"User","site_admin":false},"created":true,"deleted":false,"forced":false,"base_ref":null,"compare":"https://github.com/alexec/argo/commit/9e07a3787392","commits":[{"id":"9e07a37873923a21d5fb1ba07042758a39b5efce","tree_id":"dd63589d64bf4cbd5b68b48f3d05b7f726bb5dfc","distinct":true,"message":"chore: Enable no-response bot","timestamp":"2020-07-17T13:27:59-07:00","url":"https://github.com/alexec/argo/commit/9e07a37873923a21d5fb1ba07042758a39b5efce","author":{"name":"Alex Collins","email":"alex_collins@intuit.com","username":"alexec"},"committer":{"name":"Alex Collins","email":"alex_collins@intuit.com","username":"alexec"},"added":[".github/no-response.yml"],"removed":[],"modified":[]}],"head_commit":{"id":"9e07a37873923a21d5fb1ba07042758a39b5efce","tree_id":"dd63589d64bf4cbd5b68b48f3d05b7f726bb5dfc","distinct":true,"message":"chore: Enable no-response bot","timestamp":"2020-07-17T13:27:59-07:00","url":"https://github.com/alexec/argo/commit/9e07a37873923a21d5fb1ba07042758a39b5efce","author":{"name":"Alex Collins","email":"alex_collins@intuit.com","username":"alexec"},"committer":{"name":"Alex Collins","email":"alex_collins@intuit.com","username":"alexec"},"added":[".github/no-response.yml"],"removed":[],"modified":[]}} \ No newline at end of file diff --git a/workflow/common/common.go b/workflow/common/common.go index 8de9823663b9..1d5e927726be 100644 --- a/workflow/common/common.go +++ b/workflow/common/common.go @@ -65,6 +65,8 @@ const ( LabelKeyCronWorkflow = workflow.WorkflowFullName + "/cron-workflow" // LabelKeyWorkflowTemplate is a label applied to Workflows that are submitted from Workflowtemplate LabelKeyWorkflowTemplate = workflow.WorkflowFullName + "/workflow-template" + // LabelKeyWorkflowEventBinding is a label applied to Workflows that are submitted from a WorkflowEventBinding + LabelKeyWorkflowEventBinding = workflow.WorkflowFullName + "/workflow-event-binding" // LabelKeyWorkflowTemplate is a label applied to Workflows that are submitted from ClusterWorkflowtemplate LabelKeyClusterWorkflowTemplate = workflow.WorkflowFullName + "/cluster-workflow-template" // LabelKeyOnExit is a label applied to Pods that are run from onExit nodes, so that they are not shut down when stopping a Workflow