From 2e170113d0ae6bbd9d8d1e6f2ac0409086bd7456 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 20 Jan 2025 09:57:59 -0800 Subject: [PATCH 01/11] build: upgrade `kit` Signed-off-by: Alex Collins --- .devcontainer/pre-build.sh | 2 +- Makefile | 7 +- hack/port-forward.sh | 65 +------------- tasks.yaml | 175 ++++++++++++++++--------------------- 4 files changed, 81 insertions(+), 168 deletions(-) diff --git a/.devcontainer/pre-build.sh b/.devcontainer/pre-build.sh index 252dfa0fe909..24116ec4af33 100755 --- a/.devcontainer/pre-build.sh +++ b/.devcontainer/pre-build.sh @@ -13,7 +13,7 @@ sudo mv ./kubectl /usr/local/bin/kubectl kubectl cluster-info # install kit -curl -q https://raw.githubusercontent.com/kitproj/kit/main/install.sh | sh +go install kitproj/kit@v0.1.72 # install protocol buffer compiler (protoc) sudo apt update diff --git a/Makefile b/Makefile index e4d13a7c461c..5bd7e961e194 100644 --- a/Makefile +++ b/Makefile @@ -533,13 +533,8 @@ dist/argosay: .PHONY: kit kit: Makefile ifeq ($(shell command -v kit),) -ifeq ($(shell uname),Darwin) - brew tap kitproj/kit --custom-remote https://github.com/kitproj/kit - brew install kit -else @echo "Downloading Kit" - curl -fsL --retry 99 "https://github.com/kitproj/kit/releases/download/v0.1.8/kit_0.1.8_$$(uname)_$$(uname -m | sed 's/aarch64/arm64/').tar.gz" | sudo tar -C /usr/local/bin -xzf - kit -endif + go install kitproj/kit@v0.1.72 endif diff --git a/hack/port-forward.sh b/hack/port-forward.sh index e2e7cce7e1e7..01b5d78855a3 100755 --- a/hack/port-forward.sh +++ b/hack/port-forward.sh @@ -1,67 +1,6 @@ #!/usr/bin/env bash set -eu -o pipefail -pf() { - set -eu -o pipefail - resource=$1 - port=$2 - dest_port=${3:-"$port"} - ./hack/free-port.sh $port - echo "port-forward $resource $port" - kubectl -n argo port-forward "svc/$resource" "$port:$dest_port" & - until lsof -i ":$port" > /dev/null ; do sleep 1 ; done -} +go install github.com/kitproj/kubeauto@v0.0.6 -wait-for() { - set -eu -o pipefail - echo "wait-for $1" - kubectl -n argo wait --timeout 2m --for=condition=Available deploy/$1 -} - - -dex=$(kubectl -n argo get pod -l app=dex -o name) -if [[ "$dex" != "" ]]; then - wait-for dex - pf dex 5556 -fi - -postgres=$(kubectl -n argo get pod -l app=postgres -o name) -if [[ "$postgres" != "" ]]; then - wait-for postgres - pf postgres 5432 -fi - -mysql=$(kubectl -n argo get pod -l app=mysql -o name) -if [[ "$mysql" != "" ]]; then - wait-for mysql - pf mysql 3306 -fi - -if [[ "$(kubectl -n argo get pod -l app=argo-server -o name)" != "" ]]; then - wait-for argo-server - pf argo-server 2746 -fi - -if [[ "$(kubectl -n argo get pod -l app=workflow-controller -o name)" != "" ]]; then - wait-for workflow-controller - pf workflow-controller-metrics 9090 - if [[ "$(kubectl -n argo get svc workflow-controller-pprof -o name)" != "" ]]; then - pf workflow-controller-pprof 6060 - fi -fi - -if [[ "$(kubectl -n argo get pod -l app=prometheus -o name)" != "" ]]; then - wait-for prometheus - pf prometheus 9091 9090 -fi - -azurite=$(kubectl -n argo get pod -l app=azurite -o name) -if [[ "$azurite" != "" ]]; then - wait-for azurite - pf azurite 10000 -fi - -# forward MinIO last, so we can just wait for port 9000, and know that all ports are ready -wait-for minio -pf minio 9000 -pf minio 9001 \ No newline at end of file +kubeauto -p 0 diff --git a/tasks.yaml b/tasks.yaml index 246f1993ebe5..e708b3db7b56 100644 --- a/tasks.yaml +++ b/tasks.yaml @@ -1,98 +1,77 @@ -apiVersion: kit/v1 -kind: Tasks -metadata: - annotations: - help: | - Install `kit` by following https://github.com/kitproj/kit#install. - - Run `kit up` to start argo. - - - `env PROFILE=mysql kit up` to start with MySQL. - - `env PROFILE=plugins ARGO_EXECUTOR_PLUGINS=true kit up` to start with plugins. - - `env PROFILE=sso ARGO_AUTH_MODE=sso kit up` to start with SSO. - - The app will be up-and-running between 15s and 1m later (if hot compiled or cold). - Any changes made to the source code will be automatically recompiled and the app restarted, typically within a few seconds. - name: argo-workflows -spec: - tasks: - - name: go-deps - command: go mod download - - name: install - command: sh -c "make install PROFILE=$PROFILE" - env: - - PROFILE=minimal - dependencies: go-deps - watch: manifests - mutex: docker - - name: build-controller - command: make ./dist/workflow-controller - watch: cmd/workflow-controller config errors persist pkg util workflow - dependencies: go-deps - mutex: build - - name: port-forward - command: ./hack/port-forward.sh - ports: 9000 - dependencies: install - - name: controller - command: ./dist/workflow-controller - dependencies: install build-controller port-forward - env: - - ARGO_EXECUTOR_PLUGINS=false - - ARGO_NAMESPACE=argo - - ARGO_NAMESPACED=true - - ARGO_MANAGED_NAMESPACE=argo - - ARGO_LOGLEVEL=info - - ARGO_REMOVE_PVC_PROTECTION_FINALIZER=true - - ARGO_PROGRESS_PATCH_TICK_DURATION=7s - - DEFAULT_REQUEUE_TIME=1s - - LEADER_ELECTION_IDENTITY=local - - ALWAYS_OFFLOAD_NODE_STATUS=false - - OFFLOAD_NODE_STATUS_TTL=30s - - WORKFLOW_GC_PERIOD=30s - - UPPERIO_DB_DEBUG=1 - - ARCHIVED_WORKFLOW_GC_PERIOD=30s - ports: "9090" - - name: build-argo - command: make ./dist/argo - dependencies: go-deps - env: - - STATIC_FILES=false - watch: cmd/argo config errors persist pkg util server workflow - mutex: build - - name: server - command: ./dist/argo server - dependencies: build-argo port-forward - env: - - ARGO_X_FRAME_OPTIONS=SAMEORIGIN - - ARGO_SECURE=false - - ARGO_NAMESPACE=argo - - ARGO_NAMESPACED=true - - ARGO_LOGLEVEL=info - - ARGO_AUTH_MODE=hybrid - - ARGO_MANAGED_NAMESPACE=argo - - UPPERIO_DB_DEBUG=1 - ports: "2746" - - name: ui-deps - command: yarn install - workingDir: ui - watch: ui/package.json ui/yarn.lock - - name: ui - command: yarn start - workingDir: ui - dependencies: ui-deps - ports: "8080" - - name: executor - command: make argoexec-image - watch: cmd/argoexec config errors pkg util workflow - mutex: docker - - name: example - command: kubectl create -f examples/hello-world.yaml - dependencies: install - mutex: docker - - name: build - dependencies: build-controller build-argo - - name: pre-up - dependencies: build install executor example - - name: up - dependencies: pre-up controller server ui +tasks: + build: + dependencies: build-controller build-argo + build-argo: + command: make ./dist/argo + dependencies: go-deps + env: + STATIC_FILES: "false" + mutex: build + build-controller: + command: make ./dist/workflow-controller + dependencies: go-deps + mutex: build + controller: + command: ./dist/workflow-controller + dependencies: install build-controller port-forward + env: + ALWAYS_OFFLOAD_NODE_STATUS: "false" + ARCHIVED_WORKFLOW_GC_PERIOD: 30s + ARGO_EXECUTOR_PLUGINS: "false" + ARGO_LOGLEVEL: info + ARGO_MANAGED_NAMESPACE: argo + ARGO_NAMESPACE: argo + ARGO_NAMESPACED: "true" + ARGO_PROGRESS_PATCH_TICK_DURATION: 7s + ARGO_REMOVE_PVC_PROTECTION_FINALIZER: "true" + DEFAULT_REQUEUE_TIME: 1s + LEADER_ELECTION_IDENTITY: local + OFFLOAD_NODE_STATUS_TTL: 30s + UPPERIO_DB_DEBUG: "1" + WORKFLOW_GC_PERIOD: 30s + ports: "9090" + example: + command: kubectl create -f examples/hello-world.yaml + dependencies: install + mutex: docker + executor: + command: make argoexec-image + mutex: docker + go-deps: + command: go mod download + install: + command: sh -c "make install PROFILE=$PROFILE" + dependencies: go-deps + env: + PROFILE: minimal + mutex: docker + port-forward: + command: ./hack/port-forward.sh + dependencies: install + ports: "9000" + pre-up: + dependencies: build install executor example + server: + command: ./dist/argo server + dependencies: build-argo port-forward + env: + ARGO_AUTH_MODE: hybrid + ARGO_LOGLEVEL: info + ARGO_MANAGED_NAMESPACE: argo + ARGO_NAMESPACE: argo + ARGO_NAMESPACED: "true" + ARGO_SECURE: "false" + ARGO_X_FRAME_OPTIONS: SAMEORIGIN + UPPERIO_DB_DEBUG: "1" + ports: "2746" + ui: + command: yarn start + dependencies: ui-deps + ports: "8080" + workingDir: ui + ui-deps: + command: yarn install + workingDir: ui + up: + command: sleep 999999 + dependencies: pre-up controller server ui From f072cd2598e62d0d8d41f4ee9bcd33d9b03254ce Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 20 Jan 2025 15:49:05 -0800 Subject: [PATCH 02/11] fix: update pinned versions Signed-off-by: Alex Collins --- .devcontainer/pre-build.sh | 4 ++-- Makefile | 6 +----- hack/port-forward.sh | 2 +- tasks.yaml | 21 +++++++-------------- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/.devcontainer/pre-build.sh b/.devcontainer/pre-build.sh index 24116ec4af33..54c008918e00 100755 --- a/.devcontainer/pre-build.sh +++ b/.devcontainer/pre-build.sh @@ -13,7 +13,7 @@ sudo mv ./kubectl /usr/local/bin/kubectl kubectl cluster-info # install kit -go install kitproj/kit@v0.1.72 +go install github.com/kitproj/kit@v0.1.73 # install protocol buffer compiler (protoc) sudo apt update @@ -25,7 +25,7 @@ sudo chown vscode:vscode /home/vscode/go/src || true sudo chown vscode:vscode /home/vscode/go/src/github.com || true # download dependencies and do first-pass compile -CI=1 kit pre-up +kit build # Patch CoreDNS to have host.docker.internal inside the cluster available kubectl get cm coredns -n kube-system -o yaml | sed "s/ NodeHosts: |/ NodeHosts: |\n `grep host.docker.internal /etc/hosts`/" | kubectl apply -f - diff --git a/Makefile b/Makefile index 5bd7e961e194..a65116d7d832 100644 --- a/Makefile +++ b/Makefile @@ -532,11 +532,7 @@ dist/argosay: .PHONY: kit kit: Makefile -ifeq ($(shell command -v kit),) - @echo "Downloading Kit" - go install kitproj/kit@v0.1.72 -endif - + go install github.com/kitproj/kit@v0.1.72 .PHONY: start ifeq ($(RUN_MODE),local) diff --git a/hack/port-forward.sh b/hack/port-forward.sh index 01b5d78855a3..b07bf522983f 100755 --- a/hack/port-forward.sh +++ b/hack/port-forward.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -eu -o pipefail -go install github.com/kitproj/kubeauto@v0.0.6 +go install github.com/kitproj/kubeauto@v0.0.7 kubeauto -p 0 diff --git a/tasks.yaml b/tasks.yaml index e708b3db7b56..294085fc884b 100644 --- a/tasks.yaml +++ b/tasks.yaml @@ -1,19 +1,17 @@ tasks: build: - dependencies: build-controller build-argo - build-argo: + dependencies: build-controller build-cli build-executor + build-cli: command: make ./dist/argo - dependencies: go-deps env: STATIC_FILES: "false" mutex: build build-controller: command: make ./dist/workflow-controller - dependencies: go-deps mutex: build controller: command: ./dist/workflow-controller - dependencies: install build-controller port-forward + dependencies: build-controller port-forward env: ALWAYS_OFFLOAD_NODE_STATUS: "false" ARCHIVED_WORKFLOW_GC_PERIOD: 30s @@ -34,14 +32,11 @@ tasks: command: kubectl create -f examples/hello-world.yaml dependencies: install mutex: docker - executor: + build-executor: command: make argoexec-image mutex: docker - go-deps: - command: go mod download install: - command: sh -c "make install PROFILE=$PROFILE" - dependencies: go-deps + sh: make install PROFILE=$PROFILE env: PROFILE: minimal mutex: docker @@ -49,11 +44,9 @@ tasks: command: ./hack/port-forward.sh dependencies: install ports: "9000" - pre-up: - dependencies: build install executor example server: command: ./dist/argo server - dependencies: build-argo port-forward + dependencies: build-cli port-forward env: ARGO_AUTH_MODE: hybrid ARGO_LOGLEVEL: info @@ -74,4 +67,4 @@ tasks: workingDir: ui up: command: sleep 999999 - dependencies: pre-up controller server ui + dependencies: example controller server ui build-executor From 39a1971db7d1583c342b23cda03df6a5378fe5f3 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 27 Jan 2025 10:28:28 -0800 Subject: [PATCH 03/11] Update .devcontainer/pre-build.sh Co-authored-by: Mason Malone <651224+MasonM@users.noreply.github.com> Signed-off-by: Alex Collins Signed-off-by: Alex Collins --- .devcontainer/pre-build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/pre-build.sh b/.devcontainer/pre-build.sh index 54c008918e00..8a2dc48c2094 100755 --- a/.devcontainer/pre-build.sh +++ b/.devcontainer/pre-build.sh @@ -13,7 +13,7 @@ sudo mv ./kubectl /usr/local/bin/kubectl kubectl cluster-info # install kit -go install github.com/kitproj/kit@v0.1.73 +make kit # install protocol buffer compiler (protoc) sudo apt update From 7ef006ff8741eb76c5a1da67ce63dbd3abd69705 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 27 Jan 2025 10:29:33 -0800 Subject: [PATCH 04/11] Update Makefile Signed-off-by: Alex Collins Signed-off-by: Alex Collins --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a65116d7d832..518130e97901 100644 --- a/Makefile +++ b/Makefile @@ -532,7 +532,7 @@ dist/argosay: .PHONY: kit kit: Makefile - go install github.com/kitproj/kit@v0.1.72 + go install github.com/kitproj/kit@v0.1.79 .PHONY: start ifeq ($(RUN_MODE),local) From 581d401ba0d3cf11d25755bc32256a45cf14ec68 Mon Sep 17 00:00:00 2001 From: Tim Collins <45351296+tico24@users.noreply.github.com> Date: Wed, 22 Jan 2025 02:42:53 +0000 Subject: [PATCH 05/11] docs: Add all the template types to the core concepts page (#14110) Signed-off-by: Tim Collins Signed-off-by: Alex Collins --- docs/workflow-concepts.md | 60 ++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/docs/workflow-concepts.md b/docs/workflow-concepts.md index f245fdd8eb90..c706038f1ef1 100644 --- a/docs/workflow-concepts.md +++ b/docs/workflow-concepts.md @@ -1,19 +1,22 @@ # Core Concepts -This page serves as an introduction to the core concepts of Argo. +This page serves as an introduction to the core concepts of Argo Workflows. ## The `Workflow` -The [`Workflow`](fields.md#workflow) is the most important resource in Argo and serves two important functions: +The [`Workflow`](fields.md#workflow) is the most important resource in Argo Workflows and serves two important functions: 1. It defines the workflow to be executed. 1. It stores the state of the workflow. -Because of these dual responsibilities, a `Workflow` should be treated as a "live" object. It is not only a static definition, but is also an "instance" of said definition. (If it isn't clear what this means, it will be explained below). +Because of these dual responsibilities, a `Workflow` should be treated as a "live" object. +It is not only a static definition, but is also an "instance" of said definition. +(If it isn't clear what this means, it will be explained below). ### Workflow Spec -The workflow to be executed is defined in the [`Workflow.spec`](fields.md#workflowspec) field. The core structure of a Workflow spec is a list of [`templates`](fields.md#template) and an `entrypoint`. +The workflow to be executed is defined in the [`Workflow.spec`](fields.md#workflowspec) field. +The core structure of a Workflow spec is a list of [`templates`](fields.md#template) and an `entrypoint`. [`templates`](fields.md#template) can be loosely thought of as "functions": they define instructions to be executed. The `entrypoint` field defines what the "main" function will be – that is, the template that will be executed first. @@ -37,7 +40,7 @@ spec: ### `template` Types -There are 6 types of templates, divided into two different categories. +There are 9 types of templates, divided into two different categories. #### Template Definitions @@ -45,7 +48,8 @@ These templates _define_ work to be done, usually in a Container. ##### [Container](fields.md#container) -Perhaps the most common template type, it will schedule a Container. The spec of the template is the same as the [Kubernetes container spec](https://v1-26.docs.kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#Container), so you can define a container here the same way you do anywhere else in Kubernetes. +Perhaps the most common template type, it will schedule a Container. +The spec of the template is the same as the [Kubernetes container spec](https://v1-26.docs.kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#Container), so you can define a container here the same way you do anywhere else in Kubernetes. Example: @@ -59,8 +63,10 @@ Example: ##### [Script](fields.md#scripttemplate) -A convenience wrapper around a `container`. The spec is the same as a container, but adds the `source:` field which allows you to define a script in-place. -The script will be saved into a file and executed for you. The result of the script is automatically exported into an [Argo variable](./variables.md) either `{{tasks..outputs.result}}` or `{{steps..outputs.result}}`, depending how it was called. +A convenience wrapper around a `container`. +The spec is the same as a container, but adds the `source:` field which allows you to define a script in-place. +The script will be saved into a file and executed for you. +The result of the script is automatically exported into an [Argo variable](./variables.md) either `{{tasks..outputs.result}}` or `{{steps..outputs.result}}`, depending how it was called. Example: @@ -77,7 +83,8 @@ Example: ##### [Resource](fields.md#resourcetemplate) -Performs operations on cluster Resources directly. It can be used to get, create, apply, delete, replace, or patch resources on your cluster. +Performs operations on cluster Resources directly. +It can be used to get, create, apply, delete, replace, or patch resources on your cluster. This example creates a `ConfigMap` resource on the cluster: @@ -96,7 +103,8 @@ This example creates a `ConfigMap` resource on the cluster: ##### [Suspend](fields.md#suspendtemplate) -A suspend template will suspend execution, either for a duration or until it is resumed manually. Suspend templates can be resumed from the CLI (with `argo resume`), the API endpoint, or the UI. +A suspend template will suspend execution, either for a duration or until it is resumed manually. +Suspend templates can be resumed from the CLI (with `argo resume`), the [API endpoint](swagger.md), or the UI. Example: @@ -106,13 +114,37 @@ Example: duration: "20s" ``` +##### [Plugin](fields.md#plugin) + +A [Plugin Template](plugins.md) allows you to reference an executor plugin. + +Example: + +```yaml + - name: main + plugin: + slack: + text: "{{workflow.name}} finished!" +``` + +##### [Container Set](fields.md#containersettemplate) + +A [container set template](container-set-template.md) is similar to a normal container or script template, but allows you to specify multiple containers to run within a single pod. + +##### [HTTP](fields.md#http) + +An [HTTP Template](http-template.md) is a type of template which can execute HTTP Requests. The body of the response is automatically exported into the result output parameter. + #### Template Invocators These templates are used to invoke/call other templates and provide execution control. ##### [Steps](fields.md#workflowstep) -A steps template allows you to define your tasks in a series of steps. The structure of the template is a "list of lists". Outer lists will run sequentially and inner lists will run in parallel. If you want to run inner lists one by one, use the [Synchronization](fields.md#synchronization) feature. You can set a wide array of options to control execution, such as [`when:` clauses to conditionally execute a step](https://raw.githubusercontent.com/argoproj/argo-workflows/main/examples/coinflip.yaml). +A steps template allows you to define your tasks in a series of steps. +The structure of the template is a "list of lists". Outer lists will run sequentially and inner lists will run in parallel. +If you want to run inner lists one by one, use the [Synchronization](fields.md#synchronization) feature. +You can set a wide array of options to control execution, such as [`when:` clauses to conditionally execute a step](https://raw.githubusercontent.com/argoproj/argo-workflows/main/examples/coinflip.yaml). In this example `step1` runs first. Once it is completed, `step2a` and `step2b` will run in parallel: @@ -129,7 +161,9 @@ In this example `step1` runs first. Once it is completed, `step2a` and `step2b` ##### [DAG](fields.md#dagtemplate) -A dag template allows you to define your tasks as a graph of dependencies. In a DAG, you list all your tasks and set which other tasks must complete before a particular task can begin. Tasks without any dependencies will be run immediately. +A dag template allows you to define your tasks as a graph of dependencies. +In a DAG, you list all your tasks and set which other tasks must complete before a particular task can begin. +Tasks without any dependencies will be run immediately. In this example `A` runs first. Once it is completed, `B` and `C` will run in parallel and once they both complete, `D` will run: @@ -152,4 +186,4 @@ In this example `A` runs first. Once it is completed, `B` and `C` will run in pa ## Architecture -If you are interested in Argo's underlying architecture, see [Architecture](architecture.md). +If you are interested in Argo Workflows' underlying architecture, see [Architecture](architecture.md). From 3077c06a11354e329c8bdb47a81a81112ef26cd3 Mon Sep 17 00:00:00 2001 From: jswxstw Date: Wed, 22 Jan 2025 11:04:43 +0800 Subject: [PATCH 06/11] chore: remove leftover code (#14109) Signed-off-by: oninowang Signed-off-by: Alex Collins --- go.mod | 2 +- workflow/common/util.go | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/go.mod b/go.mod index c0009a9eaa39..9196edd92fb1 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,6 @@ require ( github.com/google/go-containerregistry v0.17.0 github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220720195016-31786c6cbb82 github.com/gorilla/handlers v1.5.2 - github.com/gorilla/websocket v1.5.1 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -105,6 +104,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/s2a-go v0.1.7 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.3 // indirect diff --git a/workflow/common/util.go b/workflow/common/util.go index b7ffbe291494..ba1c892f9633 100644 --- a/workflow/common/util.go +++ b/workflow/common/util.go @@ -5,11 +5,9 @@ import ( "context" "encoding/json" "fmt" - "net/http" "sort" "strings" - "github.com/gorilla/websocket" log "github.com/sirupsen/logrus" apiv1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -19,7 +17,6 @@ import ( "github.com/argoproj/argo-workflows/v3/errors" wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" - "github.com/argoproj/argo-workflows/v3/util" "github.com/argoproj/argo-workflows/v3/util/template" ) @@ -44,21 +41,6 @@ func isSubPath(path string, normalizedMountPath string) bool { return strings.HasPrefix(path, normalizedMountPath+"/") } -type RoundTripCallback func(conn *websocket.Conn, resp *http.Response, err error) error - -type WebsocketRoundTripper struct { - Dialer *websocket.Dialer - Do RoundTripCallback -} - -func (d *WebsocketRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { - conn, resp, err := d.Dialer.Dial(r.URL.String(), r.Header) - if err == nil { - defer util.Close(conn) - } - return resp, d.Do(conn, resp, err) -} - // ExecPodContainer runs a command in a container in a pod and returns the remotecommand.Executor func ExecPodContainer(restConfig *rest.Config, namespace string, pod string, container string, stdout bool, stderr bool, command ...string) (exec remotecommand.Executor, err error) { defer func() { From bbb72b4b92209527893857da6309c610ff99532f Mon Sep 17 00:00:00 2001 From: Tianchu Zhao Date: Thu, 23 Jan 2025 22:44:10 +1100 Subject: [PATCH 07/11] feat: label actor action when making change to workflow/template. Fixes #14102 (#14104) Signed-off-by: Tianchu Zhao Signed-off-by: Alex Collins --- .../cluster_workflow_template_server.go | 5 +- .../cluster_workflow_template_server_test.go | 3 + server/cronworkflow/cron_workflow_server.go | 5 +- .../cronworkflow/cron_workflow_server_test.go | 3 + server/event/dispatch/operation.go | 2 +- server/workflow/workflow_server.go | 6 +- server/workflow/workflow_server_test.go | 9 +++ .../workflow_template_server.go | 5 +- .../workflow_template_server_test.go | 3 + workflow/common/common.go | 5 ++ workflow/creator/creator.go | 66 ++++++++++++++++--- workflow/creator/creator_test.go | 33 +++++++--- workflow/util/util.go | 30 +++++++-- workflow/util/util_test.go | 12 ++-- 14 files changed, 145 insertions(+), 42 deletions(-) diff --git a/server/clusterworkflowtemplate/cluster_workflow_template_server.go b/server/clusterworkflowtemplate/cluster_workflow_template_server.go index 63d0cf6aebc2..5453e51c4ddd 100644 --- a/server/clusterworkflowtemplate/cluster_workflow_template_server.go +++ b/server/clusterworkflowtemplate/cluster_workflow_template_server.go @@ -38,7 +38,7 @@ func (cwts *ClusterWorkflowTemplateServer) CreateClusterWorkflowTemplate(ctx con return nil, serverutils.ToStatusError(fmt.Errorf("cluster workflow template was not found in the request body"), codes.InvalidArgument) } cwts.instanceIDService.Label(req.Template) - creator.Label(ctx, req.Template) + creator.LabelCreator(ctx, req.Template) cwftmplGetter := cwts.cwftmplStore.Getter(ctx) err := validate.ValidateClusterWorkflowTemplate(nil, cwftmplGetter, req.Template, cwts.wfDefaults, validate.ValidateOpts{}) if err != nil { @@ -104,7 +104,7 @@ func (cwts *ClusterWorkflowTemplateServer) DeleteClusterWorkflowTemplate(ctx con func (cwts *ClusterWorkflowTemplateServer) LintClusterWorkflowTemplate(ctx context.Context, req *clusterwftmplpkg.ClusterWorkflowTemplateLintRequest) (*v1alpha1.ClusterWorkflowTemplate, error) { cwts.instanceIDService.Label(req.Template) - creator.Label(ctx, req.Template) + creator.LabelCreator(ctx, req.Template) cwftmplGetter := cwts.cwftmplStore.Getter(ctx) err := validate.ValidateClusterWorkflowTemplate(nil, cwftmplGetter, req.Template, cwts.wfDefaults, validate.ValidateOpts{Lint: true}) @@ -119,6 +119,7 @@ func (cwts *ClusterWorkflowTemplateServer) UpdateClusterWorkflowTemplate(ctx con if req.Template == nil { return nil, serverutils.ToStatusError(fmt.Errorf("ClusterWorkflowTemplate is not found in Request body"), codes.InvalidArgument) } + creator.LabelActor(ctx, req.Template, creator.ActionUpdate) err := cwts.instanceIDService.Validate(req.Template) if err != nil { return nil, serverutils.ToStatusError(err, codes.InvalidArgument) diff --git a/server/clusterworkflowtemplate/cluster_workflow_template_server_test.go b/server/clusterworkflowtemplate/cluster_workflow_template_server_test.go index 38a205650c7e..637cc23debb4 100644 --- a/server/clusterworkflowtemplate/cluster_workflow_template_server_test.go +++ b/server/clusterworkflowtemplate/cluster_workflow_template_server_test.go @@ -16,6 +16,7 @@ import ( "github.com/argoproj/argo-workflows/v3/server/auth/types" "github.com/argoproj/argo-workflows/v3/util/instanceid" "github.com/argoproj/argo-workflows/v3/workflow/common" + "github.com/argoproj/argo-workflows/v3/workflow/creator" ) var unlabelled, cwftObj2, cwftObj3 v1alpha1.ClusterWorkflowTemplate @@ -263,6 +264,8 @@ func TestWorkflowTemplateServer_UpdateClusterWorkflowTemplate(t *testing.T) { req.Template.Spec.Templates[0].Container.Image = "alpine:latest" cwftRsp, err := server.UpdateClusterWorkflowTemplate(ctx, req) require.NoError(t, err) + assert.Contains(t, cwftRsp.Labels, common.LabelKeyActor) + assert.Equal(t, string(creator.ActionUpdate), cwftRsp.Labels[common.LabelKeyAction]) assert.Equal(t, "alpine:latest", cwftRsp.Spec.Templates[0].Container.Image) }) t.Run("Unlabelled", func(t *testing.T) { diff --git a/server/cronworkflow/cron_workflow_server.go b/server/cronworkflow/cron_workflow_server.go index 2a076a22461f..dae6948058ac 100644 --- a/server/cronworkflow/cron_workflow_server.go +++ b/server/cronworkflow/cron_workflow_server.go @@ -36,7 +36,7 @@ func (c *cronWorkflowServiceServer) LintCronWorkflow(ctx context.Context, req *c wftmplGetter := c.wftmplStore.Getter(ctx, req.Namespace) cwftmplGetter := c.cwftmplStore.Getter(ctx) c.instanceIDService.Label(req.CronWorkflow) - creator.Label(ctx, req.CronWorkflow) + creator.LabelCreator(ctx, req.CronWorkflow) err := validate.ValidateCronWorkflow(ctx, wftmplGetter, cwftmplGetter, req.CronWorkflow, c.wfDefaults) if err != nil { return nil, sutils.ToStatusError(err, codes.InvalidArgument) @@ -63,7 +63,7 @@ func (c *cronWorkflowServiceServer) CreateCronWorkflow(ctx context.Context, req return nil, sutils.ToStatusError(fmt.Errorf("cron workflow was not found in the request body"), codes.NotFound) } c.instanceIDService.Label(req.CronWorkflow) - creator.Label(ctx, req.CronWorkflow) + creator.LabelCreator(ctx, req.CronWorkflow) wftmplGetter := c.wftmplStore.Getter(ctx, req.Namespace) cwftmplGetter := c.cwftmplStore.Getter(ctx) err := validate.ValidateCronWorkflow(ctx, wftmplGetter, cwftmplGetter, req.CronWorkflow, c.wfDefaults) @@ -90,6 +90,7 @@ func (c *cronWorkflowServiceServer) UpdateCronWorkflow(ctx context.Context, req if err != nil { return nil, sutils.ToStatusError(err, codes.Internal) } + creator.LabelActor(ctx, req.CronWorkflow, creator.ActionUpdate) wftmplGetter := c.wftmplStore.Getter(ctx, req.Namespace) cwftmplGetter := c.cwftmplStore.Getter(ctx) if err := validate.ValidateCronWorkflow(ctx, wftmplGetter, cwftmplGetter, req.CronWorkflow, c.wfDefaults); err != nil { diff --git a/server/cronworkflow/cron_workflow_server_test.go b/server/cronworkflow/cron_workflow_server_test.go index dc327c05bb92..91d69852b590 100644 --- a/server/cronworkflow/cron_workflow_server_test.go +++ b/server/cronworkflow/cron_workflow_server_test.go @@ -17,6 +17,7 @@ import ( "github.com/argoproj/argo-workflows/v3/server/workflowtemplate" "github.com/argoproj/argo-workflows/v3/util/instanceid" "github.com/argoproj/argo-workflows/v3/workflow/common" + "github.com/argoproj/argo-workflows/v3/workflow/creator" ) func Test_cronWorkflowServiceServer(t *testing.T) { @@ -104,6 +105,8 @@ metadata: }) t.Run("Labelled", func(t *testing.T) { cronWf, err := server.UpdateCronWorkflow(ctx, &cronworkflowpkg.UpdateCronWorkflowRequest{Namespace: "my-ns", CronWorkflow: &cronWf}) + assert.Contains(t, cronWf.Labels, common.LabelKeyActor) + assert.Equal(t, string(creator.ActionUpdate), cronWf.Labels[common.LabelKeyAction]) require.NoError(t, err) assert.NotNil(t, cronWf) }) diff --git a/server/event/dispatch/operation.go b/server/event/dispatch/operation.go index c24a95a4e79f..8f2c50cabbdb 100644 --- a/server/event/dispatch/operation.go +++ b/server/event/dispatch/operation.go @@ -114,7 +114,7 @@ func (o *Operation) dispatch(ctx context.Context, wfeb wfv1.WorkflowEventBinding // 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) + creator.LabelCreator(o.ctx, wf) labels.Label(wf, common.LabelKeyWorkflowEventBinding, wfeb.Name) if submit.Arguments != nil { for _, p := range submit.Arguments.Parameters { diff --git a/server/workflow/workflow_server.go b/server/workflow/workflow_server.go index ea96b44258aa..62c41e8fcabd 100644 --- a/server/workflow/workflow_server.go +++ b/server/workflow/workflow_server.go @@ -107,7 +107,7 @@ func (s *workflowServer) CreateWorkflow(ctx context.Context, req *workflowpkg.Wo } s.instanceIDService.Label(req.Workflow) - creator.Label(ctx, req.Workflow) + creator.LabelCreator(ctx, req.Workflow) wftmplGetter := s.wftmplStore.Getter(ctx, req.Workflow.Namespace) cwftmplGetter := s.cwftmplStore.Getter(ctx) @@ -669,7 +669,7 @@ func (s *workflowServer) LintWorkflow(ctx context.Context, req *workflowpkg.Work wftmplGetter := s.wftmplStore.Getter(ctx, req.Workflow.Namespace) cwftmplGetter := s.cwftmplStore.Getter(ctx) s.instanceIDService.Label(req.Workflow) - creator.Label(ctx, req.Workflow) + creator.LabelCreator(ctx, req.Workflow) err := validate.ValidateWorkflow(wftmplGetter, cwftmplGetter, req.Workflow, s.wfDefaults, validate.ValidateOpts{Lint: true}) if err != nil { @@ -785,7 +785,7 @@ func (s *workflowServer) SubmitWorkflow(ctx context.Context, req *workflowpkg.Wo } s.instanceIDService.Label(wf) - creator.Label(ctx, wf) + creator.LabelCreator(ctx, wf) err := util.ApplySubmitOpts(wf, req.SubmitOptions) if err != nil { return nil, sutils.ToStatusError(err, codes.Internal) diff --git a/server/workflow/workflow_server_test.go b/server/workflow/workflow_server_test.go index 7098219310ef..aeb6550ac947 100644 --- a/server/workflow/workflow_server_test.go +++ b/server/workflow/workflow_server_test.go @@ -34,6 +34,7 @@ import ( "github.com/argoproj/argo-workflows/v3/util" "github.com/argoproj/argo-workflows/v3/util/instanceid" "github.com/argoproj/argo-workflows/v3/workflow/common" + "github.com/argoproj/argo-workflows/v3/workflow/creator" ) const unlabelled = `{ @@ -811,9 +812,13 @@ func TestSuspendResumeWorkflow(t *testing.T) { require.NoError(t, err) assert.NotNil(t, wf) assert.True(t, *wf.Spec.Suspend) + assert.Contains(t, wf.Labels, common.LabelKeyActor) + assert.Equal(t, string(creator.ActionSuspend), wf.Labels[common.LabelKeyAction]) wf, err = server.ResumeWorkflow(ctx, &workflowpkg.WorkflowResumeRequest{Name: wf.Name, Namespace: wf.Namespace}) require.NoError(t, err) assert.NotNil(t, wf) + assert.Contains(t, wf.Labels, common.LabelKeyActor) + assert.Equal(t, string(creator.ActionResume), wf.Labels[common.LabelKeyAction]) assert.Nil(t, wf.Spec.Suspend) } @@ -848,6 +853,8 @@ func TestTerminateWorkflow(t *testing.T) { wf, err = server.TerminateWorkflow(ctx, &rsmWfReq) assert.NotNil(t, wf) assert.Equal(t, v1alpha1.ShutdownStrategyTerminate, wf.Spec.Shutdown) + assert.Contains(t, wf.Labels, common.LabelKeyActor) + assert.Equal(t, string(creator.ActionTerminate), wf.Labels[common.LabelKeyAction]) require.NoError(t, err) rsmWfReq = workflowpkg.WorkflowTerminateRequest{ @@ -868,6 +875,8 @@ func TestStopWorkflow(t *testing.T) { require.NoError(t, err) assert.NotNil(t, wf) assert.Equal(t, v1alpha1.WorkflowRunning, wf.Status.Phase) + assert.Contains(t, wf.Labels, common.LabelKeyActor) + assert.Equal(t, string(creator.ActionStop), wf.Labels[common.LabelKeyAction]) } func TestResubmitWorkflow(t *testing.T) { diff --git a/server/workflowtemplate/workflow_template_server.go b/server/workflowtemplate/workflow_template_server.go index a7de48fcc729..6e1adc4cb15e 100644 --- a/server/workflowtemplate/workflow_template_server.go +++ b/server/workflowtemplate/workflow_template_server.go @@ -43,7 +43,7 @@ func (wts *WorkflowTemplateServer) CreateWorkflowTemplate(ctx context.Context, r return nil, sutils.ToStatusError(fmt.Errorf("workflow template was not found in the request body"), codes.InvalidArgument) } wts.instanceIDService.Label(req.Template) - creator.Label(ctx, req.Template) + creator.LabelCreator(ctx, req.Template) wftmplGetter := wts.wftmplStore.Getter(ctx, req.Namespace) cwftmplGetter := wts.cwftmplStore.Getter(ctx) err := validate.ValidateWorkflowTemplate(wftmplGetter, cwftmplGetter, req.Template, nil, validate.ValidateOpts{}) @@ -177,7 +177,7 @@ func (wts *WorkflowTemplateServer) DeleteWorkflowTemplate(ctx context.Context, r func (wts *WorkflowTemplateServer) LintWorkflowTemplate(ctx context.Context, req *workflowtemplatepkg.WorkflowTemplateLintRequest) (*v1alpha1.WorkflowTemplate, error) { wts.instanceIDService.Label(req.Template) - creator.Label(ctx, req.Template) + creator.LabelCreator(ctx, req.Template) wftmplGetter := wts.wftmplStore.Getter(ctx, req.Namespace) cwftmplGetter := wts.cwftmplStore.Getter(ctx) err := validate.ValidateWorkflowTemplate(wftmplGetter, cwftmplGetter, req.Template, nil, validate.ValidateOpts{Lint: true}) @@ -195,6 +195,7 @@ func (wts *WorkflowTemplateServer) UpdateWorkflowTemplate(ctx context.Context, r if err != nil { return nil, sutils.ToStatusError(err, codes.InvalidArgument) } + creator.LabelActor(ctx, req.Template, creator.ActionUpdate) wfClient := auth.GetWfClient(ctx) wftmplGetter := wts.wftmplStore.Getter(ctx, req.Namespace) cwftmplGetter := wts.cwftmplStore.Getter(ctx) diff --git a/server/workflowtemplate/workflow_template_server_test.go b/server/workflowtemplate/workflow_template_server_test.go index 1c14d2ba06c2..eedf23fe6bd8 100644 --- a/server/workflowtemplate/workflow_template_server_test.go +++ b/server/workflowtemplate/workflow_template_server_test.go @@ -18,6 +18,7 @@ import ( "github.com/argoproj/argo-workflows/v3/server/clusterworkflowtemplate" "github.com/argoproj/argo-workflows/v3/util/instanceid" "github.com/argoproj/argo-workflows/v3/workflow/common" + "github.com/argoproj/argo-workflows/v3/workflow/creator" ) const unlabelled = `{ @@ -266,6 +267,8 @@ func TestWorkflowTemplateServer_UpdateWorkflowTemplate(t *testing.T) { Template: &wftObj1, }) require.NoError(t, err) + assert.Contains(t, wftRsp.Labels, common.LabelKeyActor) + assert.Equal(t, string(creator.ActionUpdate), wftRsp.Labels[common.LabelKeyAction]) assert.Equal(t, "alpine:latest", wftRsp.Spec.Templates[0].Container.Image) }) t.Run("Unlabelled", func(t *testing.T) { diff --git a/workflow/common/common.go b/workflow/common/common.go index 42b241b2a5be..cc1bf8e01932 100644 --- a/workflow/common/common.go +++ b/workflow/common/common.go @@ -60,6 +60,11 @@ const ( LabelKeyCreator = workflow.WorkflowFullName + "/creator" LabelKeyCreatorEmail = workflow.WorkflowFullName + "/creator-email" LabelKeyCreatorPreferredUsername = workflow.WorkflowFullName + "/creator-preferred-username" + // Who action on this workflow. + LabelKeyActor = workflow.WorkflowFullName + "/actor" + LabelKeyActorEmail = workflow.WorkflowFullName + "/actor-email" + LabelKeyActorPreferredUsername = workflow.WorkflowFullName + "/actor-preferred-username" + LabelKeyAction = workflow.WorkflowFullName + "/action" // LabelKeyCompleted is the metadata label applied on workflows and workflow pods to indicates if resource is completed // Workflows and pods with a completed=true label will be ignored by the controller. // See also `LabelKeyWorkflowArchivingStatus`. diff --git a/workflow/creator/creator.go b/workflow/creator/creator.go index a8a84004623d..7e88f0273198 100644 --- a/workflow/creator/creator.go +++ b/workflow/creator/creator.go @@ -12,33 +12,58 @@ import ( "github.com/argoproj/argo-workflows/v3/workflow/common" ) -func Label(ctx context.Context, obj metav1.Object) { +type ActionType string + +const ( + ActionUpdate ActionType = "Update" + ActionSuspend ActionType = "Suspend" + ActionStop ActionType = "Stop" + ActionTerminate ActionType = "Terminate" + ActionResume ActionType = "Resume" + ActionNone ActionType = "" +) + +func Label(ctx context.Context, obj metav1.Object, userLabelKey string, userEmailLabelKey string, preferredUsernameLabelKey string, action ActionType) { claims := auth.GetClaims(ctx) if claims != nil { if claims.Subject != "" { - labels.Label(obj, common.LabelKeyCreator, dnsFriendly(claims.Subject)) + labels.Label(obj, userLabelKey, dnsFriendly(claims.Subject)) } else { - labels.UnLabel(obj, common.LabelKeyCreator) + labels.UnLabel(obj, userLabelKey) } if claims.Email != "" { - labels.Label(obj, common.LabelKeyCreatorEmail, dnsFriendly(strings.Replace(claims.Email, "@", ".at.", 1))) + labels.Label(obj, userEmailLabelKey, dnsFriendly(strings.Replace(claims.Email, "@", ".at.", 1))) } else { - labels.UnLabel(obj, common.LabelKeyCreatorEmail) + labels.UnLabel(obj, userEmailLabelKey) } if claims.PreferredUsername != "" { - labels.Label(obj, common.LabelKeyCreatorPreferredUsername, dnsFriendly(claims.PreferredUsername)) + labels.Label(obj, preferredUsernameLabelKey, dnsFriendly(claims.PreferredUsername)) } else { - labels.UnLabel(obj, common.LabelKeyCreatorPreferredUsername) + labels.UnLabel(obj, preferredUsernameLabelKey) + } + if action != ActionNone { + labels.Label(obj, common.LabelKeyAction, dnsFriendly(string(action))) + } else { + labels.UnLabel(obj, common.LabelKeyAction) } } else { // If the object already has creator-related labels, but the actual request lacks auth information, // remove the creator-related labels from the object. - labels.UnLabel(obj, common.LabelKeyCreator) - labels.UnLabel(obj, common.LabelKeyCreatorEmail) - labels.UnLabel(obj, common.LabelKeyCreatorPreferredUsername) + labels.UnLabel(obj, userLabelKey) + labels.UnLabel(obj, userEmailLabelKey) + labels.UnLabel(obj, preferredUsernameLabelKey) + labels.UnLabel(obj, common.LabelKeyAction) } } +func LabelCreator(ctx context.Context, obj metav1.Object) { + Label(ctx, obj, common.LabelKeyCreator, common.LabelKeyCreatorEmail, common.LabelKeyCreatorPreferredUsername, ActionNone) +} + +func LabelActor(ctx context.Context, obj metav1.Object, action ActionType) { + Label(ctx, obj, common.LabelKeyActor, common.LabelKeyActorEmail, common.LabelKeyActorPreferredUsername, action) +} + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set func dnsFriendly(s string) string { value := regexp.MustCompile("[^-_.a-z0-9A-Z]").ReplaceAllString(s, "-") @@ -67,3 +92,24 @@ func UserInfoMap(ctx context.Context) map[string]string { } return res } + +func UserActionLabel(ctx context.Context, action ActionType) map[string]string { + claims := auth.GetClaims(ctx) + if claims == nil { + return nil + } + res := map[string]string{} + if claims.Subject != "" { + res[common.LabelKeyActor] = dnsFriendly(claims.Subject) + } + if claims.Email != "" { + res[common.LabelKeyActorEmail] = dnsFriendly(claims.Email) + } + if claims.PreferredUsername != "" { + res[common.LabelKeyActorPreferredUsername] = dnsFriendly(claims.PreferredUsername) + } + if action != ActionNone { + res[common.LabelKeyAction] = string(action) + } + return res +} diff --git a/workflow/creator/creator_test.go b/workflow/creator/creator_test.go index 05e1ce5fbba0..43160fb65e7f 100644 --- a/workflow/creator/creator_test.go +++ b/workflow/creator/creator_test.go @@ -16,29 +16,44 @@ import ( "github.com/argoproj/argo-workflows/v3/workflow/common" ) -func TestLabel(t *testing.T) { - t.Run("Empty", func(t *testing.T) { +func TestLabelCreator(t *testing.T) { + t.Run("EmptyCreator", func(t *testing.T) { wf := &wfv1.Workflow{} - Label(context.TODO(), wf) + LabelCreator(context.TODO(), wf) assert.Empty(t, wf.Labels) }) - t.Run("NotEmpty", func(t *testing.T) { + t.Run("EmptyActor", func(t *testing.T) { + wf := &wfv1.Workflow{} + LabelActor(context.TODO(), wf, ActionResume) + assert.Empty(t, wf.Labels) + }) + t.Run("NotEmptyCreator", func(t *testing.T) { wf := &wfv1.Workflow{} - Label(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: strings.Repeat("x", 63) + "y"}, Email: "my@email", PreferredUsername: "username"}), wf) + LabelCreator(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: strings.Repeat("x", 63) + "y"}, Email: "my@email", PreferredUsername: "username"}), wf) require.NotEmpty(t, wf.Labels) assert.Equal(t, strings.Repeat("x", 62)+"y", wf.Labels[common.LabelKeyCreator], "creator is truncated") assert.Equal(t, "my.at.email", wf.Labels[common.LabelKeyCreatorEmail], "'@' is replaced by '.at.'") assert.Equal(t, "username", wf.Labels[common.LabelKeyCreatorPreferredUsername], "username is matching") + assert.Empty(t, wf.Labels[common.LabelKeyAction]) + }) + t.Run("NotEmptyActor", func(t *testing.T) { + wf := &wfv1.Workflow{} + LabelActor(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: strings.Repeat("x", 63) + "y"}, Email: "my@email", PreferredUsername: "username"}), wf, ActionResume) + require.NotEmpty(t, wf.Labels) + assert.Equal(t, strings.Repeat("x", 62)+"y", wf.Labels[common.LabelKeyActor], "creator is truncated") + assert.Equal(t, "my.at.email", wf.Labels[common.LabelKeyActorEmail], "'@' is replaced by '.at.'") + assert.Equal(t, "username", wf.Labels[common.LabelKeyActorPreferredUsername], "username is matching") + assert.Equal(t, "Resume", wf.Labels[common.LabelKeyAction]) }) t.Run("TooLongHyphen", func(t *testing.T) { wf := &wfv1.Workflow{} - Label(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: strings.Repeat("-", 63) + "y"}}), wf) + LabelCreator(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: strings.Repeat("-", 63) + "y"}}), wf) require.NotEmpty(t, wf.Labels) assert.Equal(t, "y", wf.Labels[common.LabelKeyCreator]) }) t.Run("InvalidDNSNames", func(t *testing.T) { wf := &wfv1.Workflow{} - Label(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: "!@#$%^&*()--__" + strings.Repeat("y", 35) + "__--!@#$%^&*()"}, PreferredUsername: "us#er@name#"}), wf) + LabelCreator(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: "!@#$%^&*()--__" + strings.Repeat("y", 35) + "__--!@#$%^&*()"}, PreferredUsername: "us#er@name#"}), wf) require.NotEmpty(t, wf.Labels) assert.Equal(t, strings.Repeat("y", 35), wf.Labels[common.LabelKeyCreator]) assert.Equal(t, "us-er-name", wf.Labels[common.LabelKeyCreatorPreferredUsername], "username is truncated") @@ -46,7 +61,7 @@ func TestLabel(t *testing.T) { t.Run("InvalidDNSNamesWithMidDashes", func(t *testing.T) { wf := &wfv1.Workflow{} sub := strings.Repeat("x", 20) + strings.Repeat("-", 70) + strings.Repeat("x", 20) - Label(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: sub}}), wf) + LabelCreator(context.WithValue(context.TODO(), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: sub}}), wf) require.NotEmpty(t, wf.Labels) assert.Equal(t, strings.Repeat("x", 20), wf.Labels[common.LabelKeyCreator]) }) @@ -113,7 +128,7 @@ func TestLabel(t *testing.T) { }, } { t.Run(testCase.name, func(t *testing.T) { - Label(context.WithValue(context.TODO(), auth.ClaimsKey, testCase.input.claims), testCase.input.wf) + LabelCreator(context.WithValue(context.TODO(), auth.ClaimsKey, testCase.input.claims), testCase.input.wf) labels := testCase.input.wf.GetLabels() for k, expectedValue := range testCase.output.creatorLabelsToHave { assert.Equal(t, expectedValue, labels[k]) diff --git a/workflow/util/util.go b/workflow/util/util.go index 1813bf631ae2..6293551cfc4e 100644 --- a/workflow/util/util.go +++ b/workflow/util/util.go @@ -374,6 +374,7 @@ func SuspendWorkflow(ctx context.Context, wfIf v1alpha1.WorkflowInterface, workf } if wf.Spec.Suspend == nil || !*wf.Spec.Suspend { wf.Spec.Suspend = ptr.To(true) + creator.LabelActor(ctx, wf, creator.ActionSuspend) _, err := wfIf.Update(ctx, wf, metav1.UpdateOptions{}) if apierr.IsConflict(err) { return false, nil @@ -411,7 +412,7 @@ func ResumeWorkflow(ctx context.Context, wfIf v1alpha1.WorkflowInterface, hydrat uiMsg = fmt.Sprintf("Resumed by: %v", uim) } if len(nodeFieldSelector) > 0 { - return updateSuspendedNode(ctx, wfIf, hydrator, workflowName, nodeFieldSelector, SetOperationValues{Phase: wfv1.NodeSucceeded, Message: uiMsg}) + return updateSuspendedNode(ctx, wfIf, hydrator, workflowName, nodeFieldSelector, SetOperationValues{Phase: wfv1.NodeSucceeded, Message: uiMsg}, creator.ActionResume) } else { err := waitutil.Backoff(retry.DefaultRetry, func() (bool, error) { wf, err := wfIf.Get(ctx, workflowName, metav1.GetOptions{}) @@ -452,7 +453,7 @@ func ResumeWorkflow(ctx context.Context, wfIf v1alpha1.WorkflowInterface, hydrat if err != nil { return false, fmt.Errorf("unable to compress or offload workflow nodes: %s", err) } - + creator.LabelActor(ctx, wf, creator.ActionResume) _, err = wfIf.Update(ctx, wf, metav1.UpdateOptions{}) if err != nil { if apierr.IsConflict(err) { @@ -527,7 +528,7 @@ func AddParamToGlobalScope(wf *wfv1.Workflow, log *log.Entry, param wfv1.Paramet return wfUpdated } -func updateSuspendedNode(ctx context.Context, wfIf v1alpha1.WorkflowInterface, hydrator hydrator.Interface, workflowName string, nodeFieldSelector string, values SetOperationValues) error { +func updateSuspendedNode(ctx context.Context, wfIf v1alpha1.WorkflowInterface, hydrator hydrator.Interface, workflowName string, nodeFieldSelector string, values SetOperationValues, action creator.ActionType) error { selector, err := fields.ParseSelector(nodeFieldSelector) if err != nil { return err @@ -601,7 +602,7 @@ func updateSuspendedNode(ctx context.Context, wfIf v1alpha1.WorkflowInterface, h if err != nil { return true, fmt.Errorf("unable to compress or offload workflow nodes: %s", err) } - + creator.LabelActor(ctx, wf, action) _, err = wfIf.Update(ctx, wf, metav1.UpdateOptions{}) if err != nil { if apierr.IsConflict(err) { @@ -680,7 +681,7 @@ func FormulateResubmitWorkflow(ctx context.Context, wf *wfv1.Workflow, memoized } // Apply creator labels based on the authentication information of the current request, // regardless of the creator labels of the original Workflow. - creator.Label(ctx, &newWF) + creator.LabelCreator(ctx, &newWF) // Append an additional label so it's easy for user to see the // name of the original workflow that has been resubmitted. newWF.ObjectMeta.Labels[common.LabelKeyPreviousWorkflowName] = wf.ObjectMeta.Name @@ -1449,7 +1450,7 @@ func TerminateWorkflow(ctx context.Context, wfClient v1alpha1.WorkflowInterface, // Or terminates a single resume step referenced by nodeFieldSelector func StopWorkflow(ctx context.Context, wfClient v1alpha1.WorkflowInterface, hydrator hydrator.Interface, name string, nodeFieldSelector string, message string) error { if len(nodeFieldSelector) > 0 { - return updateSuspendedNode(ctx, wfClient, hydrator, name, nodeFieldSelector, SetOperationValues{Phase: wfv1.NodeFailed, Message: message}) + return updateSuspendedNode(ctx, wfClient, hydrator, name, nodeFieldSelector, SetOperationValues{Phase: wfv1.NodeFailed, Message: message}, creator.ActionStop) } return patchShutdownStrategy(ctx, wfClient, name, wfv1.ShutdownStrategyStop) } @@ -1470,6 +1471,21 @@ func patchShutdownStrategy(ctx context.Context, wfClient v1alpha1.WorkflowInterf "shutdown": strategy, }, } + var action creator.ActionType + switch strategy { + case wfv1.ShutdownStrategyTerminate: + action = creator.ActionTerminate + case wfv1.ShutdownStrategyStop: + action = creator.ActionStop + default: + action = creator.ActionNone + } + userActionLabel := creator.UserActionLabel(ctx, action) + if userActionLabel != nil { + patchObj["metadata"] = map[string]interface{}{ + "labels": userActionLabel, + } + } var err error patch, err := json.Marshal(patchObj) if err != nil { @@ -1494,7 +1510,7 @@ func patchShutdownStrategy(ctx context.Context, wfClient v1alpha1.WorkflowInterf func SetWorkflow(ctx context.Context, wfClient v1alpha1.WorkflowInterface, hydrator hydrator.Interface, name string, nodeFieldSelector string, values SetOperationValues) error { if nodeFieldSelector != "" { - return updateSuspendedNode(ctx, wfClient, hydrator, name, nodeFieldSelector, values) + return updateSuspendedNode(ctx, wfClient, hydrator, name, nodeFieldSelector, values, creator.ActionNone) } return fmt.Errorf("'set' currently only targets suspend nodes, use a node field selector to target them") } diff --git a/workflow/util/util_test.go b/workflow/util/util_test.go index aad12f74227c..4dbd409e95fe 100644 --- a/workflow/util/util_test.go +++ b/workflow/util/util_test.go @@ -466,15 +466,15 @@ func TestUpdateSuspendedNode(t *testing.T) { ctx := context.Background() _, err := wfIf.Create(ctx, origWf, metav1.CreateOptions{}) require.NoError(t, err) - err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "does-not-exist", "displayName=approve", SetOperationValues{OutputParameters: map[string]string{"message": "Hello World"}}) + err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "does-not-exist", "displayName=approve", SetOperationValues{OutputParameters: map[string]string{"message": "Hello World"}}, creator.ActionNone) require.EqualError(t, err, "workflows.argoproj.io \"does-not-exist\" not found") - err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "displayName=does-not-exists", SetOperationValues{OutputParameters: map[string]string{"message": "Hello World"}}) + err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "displayName=does-not-exists", SetOperationValues{OutputParameters: map[string]string{"message": "Hello World"}}, creator.ActionNone) require.EqualError(t, err, "currently, set only targets suspend nodes: no suspend nodes matching nodeFieldSelector: displayName=does-not-exists") - err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "displayName=approve", SetOperationValues{OutputParameters: map[string]string{"does-not-exist": "Hello World"}}) + err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "displayName=approve", SetOperationValues{OutputParameters: map[string]string{"does-not-exist": "Hello World"}}, creator.ActionNone) require.EqualError(t, err, "node is not expecting output parameter 'does-not-exist'") - err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "displayName=approve", SetOperationValues{OutputParameters: map[string]string{"message": "Hello World"}}) + err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "displayName=approve", SetOperationValues{OutputParameters: map[string]string{"message": "Hello World"}}, creator.ActionNone) require.NoError(t, err) - err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "name=suspend-template-kgfn7[0].approve", SetOperationValues{OutputParameters: map[string]string{"message2": "Hello World 2"}}) + err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template", "name=suspend-template-kgfn7[0].approve", SetOperationValues{OutputParameters: map[string]string{"message2": "Hello World 2"}}, creator.ActionNone) require.NoError(t, err) // make sure global variable was updated @@ -489,7 +489,7 @@ func TestUpdateSuspendedNode(t *testing.T) { noSpaceWf.Status.Nodes["suspend-template-kgfn7-2667278707"] = node _, err = wfIf.Create(ctx, noSpaceWf, metav1.CreateOptions{}) require.NoError(t, err) - err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template-no-outputs", "displayName=approve", SetOperationValues{OutputParameters: map[string]string{"message": "Hello World"}}) + err = updateSuspendedNode(ctx, wfIf, hydratorfake.Noop, "suspend-template-no-outputs", "displayName=approve", SetOperationValues{OutputParameters: map[string]string{"message": "Hello World"}}, creator.ActionNone) require.EqualError(t, err, "cannot set output parameters because node is not expecting any raw parameters") } From c88e94306c731e12d3108d61c88cec5fa847c22b Mon Sep 17 00:00:00 2001 From: "Kaan C. Fidan" Date: Fri, 24 Jan 2025 18:43:13 +0300 Subject: [PATCH 08/11] fix(examples): map-reduce parts artifact path. Fixes #14091 (#14096) Signed-off-by: Kaan C. Fidan Signed-off-by: Alex Collins --- examples/map-reduce.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/map-reduce.yaml b/examples/map-reduce.yaml index 8a9b444d15ca..74352509f266 100644 --- a/examples/map-reduce.yaml +++ b/examples/map-reduce.yaml @@ -69,7 +69,7 @@ spec: archive: none: { } s3: - key: "{{workflow.name}}/parts" + key: "{{workflow.name}}/parts/" # One `map` per part ID is started. Finds its own "part file" under `/mnt/in/part.json`. # Each `map` task has an output artifact saved with a unique name for the part into to a common "results directory". - name: map From b2dd1df7d5737f8415d89b4937909f443ef94595 Mon Sep 17 00:00:00 2001 From: Paul Watts Date: Mon, 27 Jan 2025 01:08:34 -0800 Subject: [PATCH 09/11] docs: Pod Security Context: Volumes aren't required in >= v3.3 and runAsNonRoot (#14072) Signed-off-by: Paul Watts Co-authored-by: Mason Malone <651224+MasonM@users.noreply.github.com> Signed-off-by: Alex Collins --- docs/workflow-pod-security-context.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/workflow-pod-security-context.md b/docs/workflow-pod-security-context.md index 722406ce51ad..d8cb30c03284 100644 --- a/docs/workflow-pod-security-context.md +++ b/docs/workflow-pod-security-context.md @@ -23,6 +23,3 @@ You can configure this globally using [workflow defaults](default-workflow-specs !!! Warning "It is easy to make a workflow need root unintentionally" You may find that user's workflows have been written to require root with seemingly innocuous code. E.g. `mkdir /my-dir` would require root. - -!!! Note "You must use volumes for output artifacts" - If you use `runAsNonRoot` - you cannot have output artifacts on base layer (e.g. `/tmp`). You must use a volume (e.g. [empty dir](empty-dir.md)). From 4eff7adfe5310fe07a99d8253a87420d3084fd6e Mon Sep 17 00:00:00 2001 From: MenD32 <92942774+MenD32@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:59:59 +0200 Subject: [PATCH 10/11] feat: set template display name in yaml (#14077) Signed-off-by: MenD32 Co-authored-by: MenD32 Signed-off-by: Alex Collins --- api/jsonschema/schema.json | 7 + api/openapi-spec/swagger.json | 7 + docs/executor_swagger.md | 1 + docs/fields.md | 1 + docs/walk-through/annotations.md | 66 ++ docs/walk-through/loops.md | 2 + examples/loops-param-argument.yaml | 2 + .../argoproj.io_clusterworkflowtemplates.yaml | 8 + .../crds/full/argoproj.io_cronworkflows.yaml | 8 + .../base/crds/full/argoproj.io_workflows.yaml | 20 + .../full/argoproj.io_workflowtasksets.yaml | 4 + .../full/argoproj.io_workflowtemplates.yaml | 8 + mkdocs.yml | 1 + pkg/apis/workflow/v1alpha1/generated.pb.go | 576 ++++++++++++------ pkg/apis/workflow/v1alpha1/generated.proto | 3 + .../workflow/v1alpha1/openapi_generated.go | 16 + pkg/apis/workflow/v1alpha1/workflow_types.go | 32 + .../v1alpha1/zz_generated.deepcopy.go | 7 + pkg/plugins/executor/swagger.yml | 5 + .../IoArgoprojWorkflowV1alpha1Template.md | 1 + .../io_argoproj_workflow_v1alpha1_template.py | 4 + .../docs/ClusterWorkflowTemplateServiceApi.md | 18 + .../client/docs/CronWorkflowServiceApi.md | 18 + .../IoArgoprojWorkflowV1alpha1Template.md | 1 + sdks/python/client/docs/WorkflowServiceApi.md | 30 + .../client/docs/WorkflowTemplateServiceApi.md | 18 + workflow/controller/operator.go | 23 +- 27 files changed, 684 insertions(+), 203 deletions(-) create mode 100644 docs/walk-through/annotations.md diff --git a/api/jsonschema/schema.json b/api/jsonschema/schema.json index 263d55f6e6fe..5b4f5c1a8b72 100644 --- a/api/jsonschema/schema.json +++ b/api/jsonschema/schema.json @@ -7027,6 +7027,13 @@ "$ref": "#/definitions/io.k8s.api.core.v1.Affinity", "description": "Affinity sets the pod's scheduling constraints Overrides the affinity set at the workflow level (if any)" }, + "annotations": { + "additionalProperties": { + "type": "string" + }, + "description": "Annotations is a list of annotations to add to the template at runtime", + "type": "object" + }, "archiveLocation": { "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.ArtifactLocation", "description": "Location in which all files related to the step will be stored (logs, artifacts, etc...). Can be overridden by individual items in Outputs. If omitted, will use the default artifact repository location configured in the controller, appended with the \u003cworkflowname\u003e/\u003cnodename\u003e in the key." diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 03c148e002c6..06c5b113ea10 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -11052,6 +11052,13 @@ "description": "Affinity sets the pod's scheduling constraints Overrides the affinity set at the workflow level (if any)", "$ref": "#/definitions/io.k8s.api.core.v1.Affinity" }, + "annotations": { + "description": "Annotations is a list of annotations to add to the template at runtime", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "archiveLocation": { "description": "Location in which all files related to the step will be stored (logs, artifacts, etc...). Can be overridden by individual items in Outputs. If omitted, will use the default artifact repository location configured in the controller, appended with the \u003cworkflowname\u003e/\u003cnodename\u003e in the key.", "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.ArtifactLocation" diff --git a/docs/executor_swagger.md b/docs/executor_swagger.md index 75bb5f26bf7e..5284aa37923c 100644 --- a/docs/executor_swagger.md +++ b/docs/executor_swagger.md @@ -3807,6 +3807,7 @@ of the first container processes are calculated. |------|------|---------|:--------:| ------- |-------------|---------| | activeDeadlineSeconds | [IntOrString](#int-or-string)| `IntOrString` | | | | | | affinity | [Affinity](#affinity)| `Affinity` | | | | | +| annotations | map of string| `map[string]string` | | | Annotations is a list of annotations to add to the template at runtime | | | archiveLocation | [ArtifactLocation](#artifact-location)| `ArtifactLocation` | | | | | | automountServiceAccountToken | boolean| `bool` | | | AutomountServiceAccountToken indicates whether a service account token should be automatically mounted in pods.
ServiceAccountName of ExecutorConfig must be specified if this value is false. | | | container | [Container](#container)| `Container` | | | | | diff --git a/docs/fields.md b/docs/fields.md index ae7b1b69f399..fab792116d9c 100644 --- a/docs/fields.md +++ b/docs/fields.md @@ -1723,6 +1723,7 @@ Template is a reusable and composable unit of execution in a workflow |:----------:|:----------:|---------------| |`activeDeadlineSeconds`|[`IntOrString`](#intorstring)|Optional duration in seconds relative to the StartTime that the pod may be active on a node before the system actively tries to terminate the pod; value must be positive integer This field is only applicable to container and script templates.| |`affinity`|[`Affinity`](#affinity)|Affinity sets the pod's scheduling constraints Overrides the affinity set at the workflow level (if any)| +|`annotations`|`Map< string , string >`|Annotations is a list of annotations to add to the template at runtime| |`archiveLocation`|[`ArtifactLocation`](#artifactlocation)|Location in which all files related to the step will be stored (logs, artifacts, etc...). Can be overridden by individual items in Outputs. If omitted, will use the default artifact repository location configured in the controller, appended with the / in the key.| |`automountServiceAccountToken`|`boolean`|AutomountServiceAccountToken indicates whether a service account token should be automatically mounted in pods. ServiceAccountName of ExecutorConfig must be specified if this value is false.| |`container`|[`Container`](#container)|Container is the main container image to run in the pod| diff --git a/docs/walk-through/annotations.md b/docs/walk-through/annotations.md new file mode 100644 index 000000000000..dcec3aafe237 --- /dev/null +++ b/docs/walk-through/annotations.md @@ -0,0 +1,66 @@ +# Annotations + +Argo Workflows now supports annotations as a new field in workflow templates. + +## Adding Annotations to a template + +To add annotations to a workflow template, include the `annotations` field in template definition, for example: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: example-workflow-template +spec: + entrypoint: whalesay + templates: + - name: whalesay + annotations: + workflows.argoproj.io/display-name: "my-custom-display-name" + container: + image: docker/whalesay + command: [cowsay] + args: ["hello world"] +``` + +In this example, the annotation `workflows.argoproj.io/display-name` is used to change the node name in the UI to "my-custom-display-name". + +## Annotation Templates + +Annotations can also be created dynamically using parameters. This allows you to dynamically set annotation values based on input parameters. + +Here is an example: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: templated-annotations-workflow +spec: + entrypoint: whalesay + arguments: + parameters: + - name: display-name + value: "default-display-name" + templates: + - name: whalesay + annotations: + workflows.argoproj.io/display-name: "{{inputs.parameters.display-name}}" + inputs: + parameters: + - name: display-name + container: + image: docker/whalesay + command: [cowsay] + args: ["hello world"] +``` + +In this example, the annotation `workflows.argoproj.io/display-name` is set using the `display-name` parameter. You can override this parameter when submitting the workflow to dynamically change the annotation value. + +## Supported Annotation Types + +Here is a table of all supported annotation types in Argo Workflows: + +| Annotation Key | Description | +|----------------------------------------------|-----------------------------------------------------------------------------| +| `workflows.argoproj.io/display-name` | Changes the node name in the UI. | diff --git a/docs/walk-through/loops.md b/docs/walk-through/loops.md index 3874946f2639..9efd62a7aca3 100644 --- a/docs/walk-through/loops.md +++ b/docs/walk-through/loops.md @@ -150,6 +150,8 @@ spec: # This template is the same as in the previous example - name: cat-os-release + annotations: + workflows.argoproj.io/display-name: "os-{{inputs.parameters.image}}-{{inputs.parameters.tag}}" # this sets a custom name for the node in the UI, based on the template's parameters inputs: parameters: - name: image diff --git a/examples/loops-param-argument.yaml b/examples/loops-param-argument.yaml index 077d43ce34aa..e8f1730b64c1 100644 --- a/examples/loops-param-argument.yaml +++ b/examples/loops-param-argument.yaml @@ -34,6 +34,8 @@ spec: withParam: "{{inputs.parameters.os-list}}" - name: cat-os-release + annotations: + workflows.argoproj.io/display-name: "os-{{inputs.parameters.image}}-{{inputs.parameters.tag}}" inputs: parameters: - name: image diff --git a/manifests/base/crds/full/argoproj.io_clusterworkflowtemplates.yaml b/manifests/base/crds/full/argoproj.io_clusterworkflowtemplates.yaml index dd96de7c5462..95f053fe248d 100644 --- a/manifests/base/crds/full/argoproj.io_clusterworkflowtemplates.yaml +++ b/manifests/base/crds/full/argoproj.io_clusterworkflowtemplates.yaml @@ -2615,6 +2615,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: @@ -13289,6 +13293,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: diff --git a/manifests/base/crds/full/argoproj.io_cronworkflows.yaml b/manifests/base/crds/full/argoproj.io_cronworkflows.yaml index d5df056acc4a..3e350b8cdf13 100644 --- a/manifests/base/crds/full/argoproj.io_cronworkflows.yaml +++ b/manifests/base/crds/full/argoproj.io_cronworkflows.yaml @@ -2668,6 +2668,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: @@ -13342,6 +13346,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: diff --git a/manifests/base/crds/full/argoproj.io_workflows.yaml b/manifests/base/crds/full/argoproj.io_workflows.yaml index 45c88c907ea5..6dd50dd1dfb7 100644 --- a/manifests/base/crds/full/argoproj.io_workflows.yaml +++ b/manifests/base/crds/full/argoproj.io_workflows.yaml @@ -2629,6 +2629,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: @@ -13303,6 +13307,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: @@ -28147,6 +28155,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: @@ -40965,6 +40977,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: @@ -51639,6 +51655,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: diff --git a/manifests/base/crds/full/argoproj.io_workflowtasksets.yaml b/manifests/base/crds/full/argoproj.io_workflowtasksets.yaml index 860d7740eef0..621bb418982b 100644 --- a/manifests/base/crds/full/argoproj.io_workflowtasksets.yaml +++ b/manifests/base/crds/full/argoproj.io_workflowtasksets.yaml @@ -473,6 +473,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: diff --git a/manifests/base/crds/full/argoproj.io_workflowtemplates.yaml b/manifests/base/crds/full/argoproj.io_workflowtemplates.yaml index 4fe2fd0cdff8..40de142c88a2 100644 --- a/manifests/base/crds/full/argoproj.io_workflowtemplates.yaml +++ b/manifests/base/crds/full/argoproj.io_workflowtemplates.yaml @@ -2614,6 +2614,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: @@ -13288,6 +13292,10 @@ spec: x-kubernetes-list-type: atomic type: object type: object + annotations: + additionalProperties: + type: string + type: object archiveLocation: properties: archiveLogs: diff --git a/mkdocs.yml b/mkdocs.yml index 25f2564ea380..25db52df159a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,6 +88,7 @@ nav: - walk-through/exit-handlers.md - walk-through/timeouts.md # template types + - walk-through/annotations.md - walk-through/suspending.md - walk-through/kubernetes-resources.md # container configuration diff --git a/pkg/apis/workflow/v1alpha1/generated.pb.go b/pkg/apis/workflow/v1alpha1/generated.pb.go index 5dad0aa3df54..12b7b1d11b5f 100644 --- a/pkg/apis/workflow/v1alpha1/generated.pb.go +++ b/pkg/apis/workflow/v1alpha1/generated.pb.go @@ -4400,6 +4400,7 @@ func init() { proto.RegisterType((*TTLStrategy)(nil), "github.com.argoproj.argo_workflows.v3.pkg.apis.workflow.v1alpha1.TTLStrategy") proto.RegisterType((*TarStrategy)(nil), "github.com.argoproj.argo_workflows.v3.pkg.apis.workflow.v1alpha1.TarStrategy") proto.RegisterType((*Template)(nil), "github.com.argoproj.argo_workflows.v3.pkg.apis.workflow.v1alpha1.Template") + proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_workflows.v3.pkg.apis.workflow.v1alpha1.Template.AnnotationsEntry") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_workflows.v3.pkg.apis.workflow.v1alpha1.Template.NodeSelectorEntry") proto.RegisterType((*TemplateRef)(nil), "github.com.argoproj.argo_workflows.v3.pkg.apis.workflow.v1alpha1.TemplateRef") proto.RegisterType((*TransformationStep)(nil), "github.com.argoproj.argo_workflows.v3.pkg.apis.workflow.v1alpha1.TransformationStep") @@ -4448,15 +4449,15 @@ func init() { } var fileDescriptor_724696e352c3df5f = []byte{ - // 11177 bytes of a gzipped FileDescriptorProto + // 11195 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0xbd, 0x6b, 0x70, 0x64, 0xc7, 0x75, 0x18, 0xcc, 0x3b, 0xc0, 0xe0, 0x71, 0xf0, 0x58, 0x6c, 0xef, 0x6b, 0x88, 0x25, 0x17, 0xf4, 0xa5, 0xc8, 0x8f, 0xb4, 0x28, 0xac, 0xb9, 0x94, 0xbe, 0x30, 0x52, 0x22, 0x09, 0x8f, 0x05, 0x16, 0x04, 0xb0, 0x00, 0x7b, 0xb0, 0xbb, 0x26, 0x45, 0x4b, 0xba, 0x98, 0x69, 0xcc, 0x5c, 0x62, 0xe6, 0xde, 0xe1, 0xbd, 0x77, 0xb0, 0x0b, 0x3e, 0x24, 0x85, 0x7a, 0xc7, 0xb2, 0x15, 0xcb, 0x92, 0x2c, - 0x29, 0x49, 0x95, 0xa2, 0x48, 0x09, 0x4b, 0x76, 0x25, 0x65, 0xff, 0x4a, 0xd9, 0xff, 0xf2, 0xc3, - 0xa5, 0x94, 0x53, 0x89, 0x5c, 0x51, 0xca, 0xfa, 0x61, 0x83, 0xd1, 0x3a, 0x51, 0xa5, 0x92, 0xa8, - 0x2a, 0x56, 0xc5, 0x49, 0xbc, 0x79, 0x54, 0xaa, 0x9f, 0xb7, 0xfb, 0xce, 0x1d, 0xec, 0x00, 0xdb, + 0x29, 0x49, 0x95, 0xa2, 0x48, 0x09, 0x4b, 0x76, 0x25, 0x65, 0xff, 0x4a, 0xd9, 0xff, 0x52, 0x15, + 0x97, 0x52, 0x4e, 0x25, 0x72, 0x45, 0x29, 0xeb, 0x87, 0x0d, 0x46, 0xeb, 0x44, 0x95, 0x4a, 0xa2, + 0x1f, 0x56, 0xc5, 0x49, 0xbc, 0x79, 0x54, 0xaa, 0x9f, 0xb7, 0xfb, 0xce, 0x1d, 0xec, 0x00, 0xdb, 0xc0, 0xaa, 0xec, 0x5f, 0xc0, 0x9c, 0x3e, 0x7d, 0x4e, 0x77, 0xdf, 0xee, 0xd3, 0xa7, 0xcf, 0x39, 0x7d, 0x1a, 0xd6, 0x6b, 0x7e, 0x52, 0x6f, 0x6f, 0x4e, 0x57, 0xc2, 0xe6, 0x45, 0x2f, 0xaa, 0x85, 0xad, 0x28, 0x7c, 0x99, 0xfd, 0xf3, 0xae, 0x9b, 0x61, 0xb4, 0xbd, 0xd5, 0x08, 0x6f, 0xc6, 0x17, @@ -4474,7 +4475,7 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0x71, 0xc7, 0x6b, 0xb4, 0x49, 0xc9, 0x79, 0xc4, 0x79, 0x62, 0x78, 0xf6, 0xb1, 0xef, 0xed, 0x4d, 0x3d, 0x70, 0x7b, 0x6f, 0xaa, 0x78, 0x9d, 0x02, 0xef, 0xec, 0x4d, 0x9d, 0x26, 0x41, 0x25, 0xac, 0xfa, 0x41, 0xed, 0xe2, 0xcb, 0x71, 0x18, 0x4c, 0x5f, 0x6d, 0x37, 0x37, 0x49, 0x84, 0x79, 0x1d, - 0xf7, 0xdf, 0x14, 0xe0, 0xc4, 0x4c, 0x54, 0xa9, 0xfb, 0x3b, 0xa4, 0x9c, 0x50, 0xfa, 0xb5, 0x5d, + 0xf7, 0xdf, 0x16, 0xe0, 0xc4, 0x4c, 0x54, 0xa9, 0xfb, 0x3b, 0xa4, 0x9c, 0x50, 0xfa, 0xb5, 0x5d, 0x54, 0x87, 0xbe, 0xc4, 0x8b, 0x18, 0xb9, 0x91, 0x4b, 0xab, 0xd3, 0xf7, 0xfa, 0xdd, 0xa7, 0x37, 0xbc, 0x48, 0xd2, 0x9e, 0x1d, 0xbc, 0xbd, 0x37, 0xd5, 0xb7, 0xe1, 0x45, 0x98, 0xb2, 0x40, 0x0d, 0xe8, 0x0f, 0xc2, 0x80, 0x94, 0x0a, 0x8c, 0xd5, 0xd5, 0x7b, 0x67, 0x75, 0x35, 0x0c, 0x54, 0x3f, @@ -4619,7 +4620,7 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0xb5, 0x59, 0x8f, 0x95, 0xbc, 0x90, 0x10, 0xad, 0xbf, 0x1f, 0x83, 0xa2, 0x9f, 0x90, 0xa6, 0xb4, 0x52, 0x5b, 0xb0, 0x27, 0x75, 0xe9, 0xcb, 0xec, 0x98, 0x0c, 0x01, 0x5c, 0xa2, 0xfc, 0x30, 0x67, 0xeb, 0x6e, 0xc3, 0xc0, 0x5c, 0xd8, 0x68, 0x37, 0x83, 0xde, 0x02, 0x69, 0x92, 0xdd, 0x16, 0xc9, - 0x6e, 0xa1, 0xec, 0x74, 0xc0, 0x4a, 0xa4, 0x5d, 0xa9, 0x2f, 0xdf, 0xae, 0xe4, 0xfe, 0x0b, 0x07, + 0x6e, 0xa1, 0xec, 0x74, 0xc0, 0x4a, 0xa4, 0x5d, 0xa9, 0x2f, 0xdf, 0xae, 0xe4, 0xfe, 0x4b, 0x07, 0xe8, 0xaa, 0xaa, 0xfa, 0xc2, 0xd1, 0xc8, 0xc9, 0x71, 0x86, 0x0f, 0xeb, 0xe4, 0xee, 0xec, 0x4d, 0x8d, 0x29, 0x44, 0x8d, 0xfe, 0x87, 0x61, 0x20, 0x66, 0x27, 0x76, 0xd1, 0x86, 0x05, 0xa9, 0x5e, 0xf3, 0x73, 0xfc, 0x9d, 0xbd, 0xa9, 0x9e, 0xa2, 0x3a, 0xa7, 0x15, 0x6d, 0xe1, 0x13, 0x15, 0x54, @@ -4641,9 +4642,9 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0x08, 0x4a, 0x96, 0xc1, 0xc7, 0x1b, 0x70, 0x66, 0x2e, 0x22, 0x5e, 0x42, 0xca, 0xcf, 0xcc, 0xb6, 0x2b, 0xdb, 0x24, 0xe1, 0x91, 0x5f, 0x31, 0x7a, 0x1f, 0x8c, 0x85, 0x6c, 0xcb, 0x58, 0x09, 0x2b, 0xdb, 0x7e, 0x50, 0x13, 0x16, 0xd9, 0x33, 0x82, 0xca, 0xd8, 0x9a, 0x5e, 0x88, 0x4d, 0x5c, 0xf7, - 0xdf, 0x17, 0x60, 0x74, 0x2e, 0x0a, 0x03, 0x29, 0x16, 0x8f, 0x61, 0x2b, 0x4b, 0x8c, 0xad, 0xcc, + 0x3f, 0x14, 0x60, 0x74, 0x2e, 0x0a, 0x03, 0x29, 0x16, 0x8f, 0x61, 0x2b, 0x4b, 0x8c, 0xad, 0xcc, 0x82, 0x37, 0x54, 0x6f, 0x7f, 0xb7, 0xed, 0x0c, 0xbd, 0xae, 0x44, 0x64, 0x9f, 0xad, 0x13, 0x8a, - 0xc1, 0x97, 0xd1, 0x4e, 0x3f, 0xb6, 0x29, 0x40, 0xdd, 0xff, 0xe0, 0xc0, 0x84, 0x8e, 0x7e, 0x0c, + 0xc1, 0x97, 0xd1, 0x4e, 0x3f, 0xb6, 0x29, 0x40, 0xdd, 0xff, 0xe8, 0xc0, 0x84, 0x8e, 0x7e, 0x0c, 0x3b, 0x68, 0x6c, 0xee, 0xa0, 0x57, 0xed, 0xf6, 0xb7, 0xcb, 0xb6, 0xf9, 0xf6, 0xa0, 0xd9, 0x4f, 0xe6, 0x0a, 0xff, 0x9a, 0x03, 0xa3, 0x37, 0x35, 0x80, 0xe8, 0xac, 0x6d, 0x25, 0xe6, 0x1d, 0x52, 0xcc, 0xe8, 0xd0, 0x3b, 0x99, 0xdf, 0xd8, 0x68, 0x09, 0x95, 0xfb, 0x71, 0xa5, 0x4e, 0xaa, 0xed, @@ -4671,7 +4672,7 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0x89, 0x2e, 0xc2, 0x30, 0x5b, 0x37, 0xa4, 0x4a, 0xf8, 0xea, 0xef, 0x4b, 0x95, 0xe0, 0xb2, 0x2c, 0xc0, 0x29, 0x8e, 0xa6, 0x65, 0xf0, 0x05, 0xdf, 0x45, 0xcb, 0x40, 0xcf, 0x42, 0xb1, 0x55, 0xf7, 0x62, 0x19, 0xe2, 0xee, 0x4a, 0xa9, 0xbd, 0x4e, 0x81, 0x4c, 0x34, 0x69, 0xdf, 0x92, 0x01, 0x31, - 0xaf, 0xe0, 0xfe, 0x4b, 0x80, 0xc1, 0xf9, 0x99, 0xc5, 0x0d, 0x2f, 0xde, 0xee, 0xe1, 0x0c, 0x44, + 0xaf, 0xe0, 0xfe, 0x2b, 0x80, 0xc1, 0xf9, 0x99, 0xc5, 0x0d, 0x2f, 0xde, 0xee, 0xe1, 0x0c, 0x44, 0x97, 0xa1, 0x50, 0x56, 0xb3, 0x82, 0x54, 0x2a, 0xb1, 0x58, 0x61, 0xa0, 0x00, 0x06, 0xfc, 0x80, 0x4a, 0x9e, 0xd2, 0xb8, 0x2d, 0x37, 0x84, 0x3a, 0xcf, 0x31, 0x3b, 0xd1, 0x12, 0xa3, 0x8e, 0x05, 0x17, 0xf4, 0x3a, 0x0c, 0x7b, 0xf2, 0x86, 0x91, 0xd8, 0xff, 0x97, 0x6d, 0xd8, 0xd7, 0x05, 0x49, @@ -4689,7 +4690,7 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0x0b, 0x3a, 0xb5, 0x90, 0x38, 0xd3, 0x57, 0x28, 0x59, 0xf3, 0xf6, 0x59, 0x91, 0xc1, 0xee, 0xec, 0x4d, 0x8d, 0xaf, 0xf8, 0x5b, 0xa4, 0xb2, 0x5b, 0x69, 0x10, 0x06, 0x79, 0xf3, 0x6d, 0x0d, 0x72, 0x79, 0x87, 0x04, 0x09, 0xe6, 0xad, 0x9a, 0xfc, 0xbc, 0x03, 0x90, 0x12, 0xca, 0xf1, 0xa1, 0x12, - 0x33, 0xea, 0xc0, 0xc2, 0x81, 0xda, 0x68, 0x9a, 0xee, 0x94, 0xfd, 0xd7, 0x0e, 0x8c, 0xd0, 0xce, + 0x33, 0xea, 0xc0, 0xc2, 0x81, 0xda, 0x68, 0x9a, 0xee, 0x94, 0xfd, 0x37, 0x0e, 0x8c, 0xd0, 0xce, 0x49, 0x11, 0xf8, 0x38, 0x0c, 0x24, 0x5e, 0x54, 0x23, 0xd2, 0x8f, 0xa0, 0x3e, 0xc7, 0x06, 0x83, 0x62, 0x51, 0x8a, 0x02, 0x28, 0x26, 0x5e, 0xbc, 0x2d, 0xd5, 0xf8, 0x25, 0x6b, 0x43, 0x9c, 0x6a, 0xf0, 0xf4, 0x57, 0x8c, 0x39, 0x1b, 0xf4, 0x04, 0x0c, 0xd1, 0xad, 0x63, 0xc1, 0x8b, 0x65, 0x68, @@ -4728,7 +4729,7 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0x4e, 0x61, 0x58, 0xe3, 0xd9, 0xc3, 0xdd, 0xf7, 0x47, 0xa1, 0xb8, 0x15, 0x52, 0x9d, 0xa6, 0xcf, 0xb4, 0xf5, 0x2f, 0x50, 0x20, 0xe6, 0x65, 0xee, 0x7f, 0x73, 0xe0, 0x6c, 0xfe, 0x45, 0x80, 0x9f, 0x85, 0x4e, 0x5e, 0x02, 0xa0, 0x5d, 0x31, 0xf6, 0x05, 0x2d, 0xfb, 0x85, 0x2c, 0xc1, 0x1a, 0x56, - 0x6f, 0xdd, 0xfe, 0x57, 0x05, 0xd0, 0x78, 0xa2, 0x2f, 0x38, 0x30, 0x46, 0xd9, 0x2e, 0x47, 0x9b, + 0x6f, 0xdd, 0xfe, 0xd7, 0x05, 0xd0, 0x78, 0xa2, 0x2f, 0x38, 0x30, 0x46, 0xd9, 0x2e, 0x47, 0x9b, 0x46, 0x6f, 0xd7, 0xec, 0xf4, 0x56, 0x91, 0x4d, 0x5d, 0x1a, 0x06, 0x18, 0x9b, 0xcc, 0xd1, 0x3b, 0x61, 0xd8, 0xab, 0x56, 0x23, 0x12, 0xc7, 0xca, 0x39, 0xc8, 0x0c, 0x5e, 0x33, 0x12, 0x88, 0xd3, 0x72, 0x2a, 0x87, 0xeb, 0xd5, 0xad, 0x98, 0x8a, 0x36, 0x21, 0xfb, 0x95, 0x1c, 0xa6, 0x4c, 0x28, @@ -4856,7 +4857,7 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0x5b, 0x11, 0xbb, 0x23, 0x92, 0xf0, 0xc9, 0x30, 0xb3, 0x95, 0x90, 0x68, 0xde, 0xdb, 0xe5, 0xee, 0xcf, 0xa2, 0x7a, 0x82, 0xea, 0xe1, 0xd5, 0xfd, 0x90, 0xf1, 0xfe, 0xb4, 0x50, 0x19, 0xce, 0x50, 0x04, 0x96, 0x36, 0xcf, 0x0f, 0x83, 0x94, 0x49, 0x81, 0x31, 0x51, 0x71, 0x8a, 0xab, 0x79, 0x48, - 0x38, 0xbf, 0xae, 0x7b, 0x19, 0x06, 0xf8, 0x95, 0xbd, 0x7b, 0xf2, 0x1f, 0xb9, 0xff, 0xb6, 0x00, + 0x38, 0xbf, 0xae, 0x7b, 0x19, 0x06, 0xf8, 0x95, 0xbd, 0x7b, 0xf2, 0x1f, 0xb9, 0xff, 0xae, 0x00, 0x52, 0x31, 0xfc, 0xab, 0xed, 0x8e, 0xa3, 0x9b, 0x68, 0xc4, 0x4c, 0x4a, 0xc2, 0xda, 0xc1, 0x36, 0x51, 0x91, 0xa0, 0x52, 0x94, 0x50, 0x8d, 0x99, 0xdc, 0xf2, 0x93, 0xb9, 0xb0, 0x2a, 0x6d, 0x1c, 0x4c, 0x63, 0xbe, 0x2c, 0x60, 0x58, 0x95, 0xba, 0x9f, 0x72, 0x60, 0x8c, 0xf6, 0xb2, 0xd1, 0x20, @@ -4948,7 +4949,7 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0x33, 0x55, 0xa2, 0xf5, 0x1c, 0x1c, 0x9c, 0x5b, 0x93, 0x9e, 0x89, 0x5a, 0x02, 0xc8, 0xe2, 0xcb, 0x8a, 0x5c, 0xa9, 0x93, 0x88, 0x58, 0x95, 0xba, 0xa7, 0xe0, 0x64, 0xb9, 0xdd, 0x6a, 0x35, 0x7c, 0x52, 0x55, 0x8e, 0x0d, 0xf7, 0x03, 0x70, 0x42, 0xa4, 0x9e, 0x55, 0x0a, 0xc8, 0x81, 0x12, 0xa5, - 0xbb, 0xff, 0xb1, 0x0f, 0x4e, 0x64, 0xa2, 0x79, 0xd0, 0x6b, 0x59, 0xb5, 0xc1, 0x4e, 0x4a, 0x54, + 0xbb, 0xff, 0xa9, 0x0f, 0x4e, 0x64, 0xa2, 0x79, 0xd0, 0x6b, 0x59, 0xb5, 0xc1, 0x4e, 0x4a, 0x54, 0x4d, 0x61, 0x10, 0xf9, 0x4d, 0xf3, 0x54, 0x90, 0xba, 0x0c, 0xc5, 0xb7, 0x76, 0x05, 0x86, 0x05, 0xac, 0xf3, 0x5d, 0xc5, 0x88, 0xe7, 0xff, 0x18, 0x80, 0x62, 0x2b, 0x6f, 0xdd, 0xdb, 0xee, 0x27, 0x5b, 0xbf, 0x0a, 0x12, 0x63, 0x8d, 0x23, 0x0a, 0x60, 0x90, 0x35, 0x84, 0xc8, 0x7b, 0x97, 0xd6, @@ -4960,194 +4961,195 @@ var fileDescriptor_724696e352c3df5f = []byte{ 0x92, 0xb2, 0xf6, 0x06, 0x65, 0x51, 0x64, 0x38, 0xea, 0x2c, 0xc6, 0x79, 0x75, 0xb2, 0xa4, 0x84, 0x41, 0x99, 0x6d, 0xcf, 0x39, 0xa4, 0x44, 0x31, 0xce, 0xab, 0xe3, 0xae, 0xc1, 0xc8, 0x86, 0x17, 0xa9, 0x8e, 0x7f, 0x10, 0x26, 0x2a, 0x61, 0x53, 0xaa, 0x2b, 0x2b, 0x64, 0x87, 0x34, 0x44, 0x97, - 0xf9, 0xcb, 0x2e, 0x99, 0x32, 0xdc, 0x81, 0xed, 0xfe, 0xd7, 0x0b, 0xa0, 0xae, 0x68, 0xf6, 0xb0, - 0xa3, 0xb6, 0x54, 0x30, 0x6d, 0xd1, 0x72, 0x30, 0xad, 0xda, 0x5b, 0x32, 0x01, 0xb5, 0x49, 0x1a, - 0x50, 0x3b, 0x60, 0x3b, 0xa0, 0x56, 0x29, 0xd9, 0x1d, 0x41, 0xb5, 0x5f, 0x75, 0x60, 0x34, 0x08, - 0xab, 0x44, 0x79, 0x40, 0x07, 0xd9, 0x0a, 0x7f, 0xc9, 0xde, 0xdd, 0x04, 0x1e, 0x1c, 0x2a, 0xc8, - 0xf3, 0x40, 0x6f, 0xb5, 0x25, 0xeb, 0x45, 0xd8, 0x68, 0x07, 0x5a, 0xd0, 0x4c, 0xcb, 0xdc, 0x83, - 0xf3, 0x50, 0xde, 0x11, 0xed, 0xae, 0x76, 0xe2, 0x5b, 0x9a, 0x9e, 0x38, 0x6c, 0xcb, 0x64, 0x2a, - 0xef, 0xdd, 0x69, 0x8e, 0x28, 0x99, 0xb8, 0x3b, 0xd5, 0x1f, 0x5d, 0x18, 0xe0, 0x11, 0xe1, 0x22, - 0x97, 0x16, 0xf3, 0x8f, 0xf2, 0x68, 0x71, 0x2c, 0x4a, 0x50, 0x22, 0xa3, 0x2c, 0x46, 0x6c, 0x3d, - 0xc3, 0x61, 0x44, 0x71, 0xe4, 0x87, 0x59, 0xa0, 0xe7, 0xf4, 0xa3, 0xff, 0x68, 0x2f, 0x47, 0xff, - 0xb1, 0xae, 0xc7, 0xfe, 0x2f, 0x38, 0x30, 0x5a, 0xd1, 0x9e, 0xc5, 0x28, 0x3d, 0x61, 0xeb, 0x75, - 0xf0, 0xbc, 0xd7, 0x4b, 0xb8, 0xdb, 0xcd, 0x78, 0x86, 0xc3, 0xe0, 0xce, 0x12, 0x88, 0x32, 0x3b, - 0x07, 0x53, 0x75, 0xac, 0x24, 0xe6, 0x30, 0xed, 0x26, 0x32, 0x5a, 0x95, 0xc2, 0xb0, 0xe0, 0x85, - 0x5e, 0x87, 0x21, 0x79, 0xa9, 0x40, 0x04, 0xdf, 0x63, 0x1b, 0x7e, 0x10, 0xd3, 0xd9, 0x2a, 0xb3, - 0x0e, 0x72, 0x28, 0x56, 0x1c, 0x51, 0x1d, 0xfa, 0xaa, 0x5e, 0x4d, 0x84, 0xe1, 0xaf, 0xda, 0xc9, - 0xea, 0x2a, 0x79, 0xb2, 0x23, 0xe9, 0xfc, 0xcc, 0x22, 0xa6, 0x2c, 0xd0, 0xad, 0xf4, 0x5d, 0x81, - 0x09, 0x6b, 0xbb, 0xaf, 0xa9, 0x16, 0x72, 0x9d, 0xa0, 0xe3, 0x99, 0x82, 0xaa, 0xf0, 0x4f, 0xff, - 0x7f, 0x8c, 0xed, 0x82, 0x9d, 0xb4, 0xb0, 0x3c, 0xd1, 0x4b, 0xea, 0xe3, 0xa6, 0x5c, 0xea, 0x49, - 0xd2, 0x2a, 0xfd, 0xbc, 0x2d, 0x2e, 0x2c, 0x5d, 0x09, 0x7f, 0xc8, 0x7d, 0x63, 0x63, 0x1d, 0x33, - 0xea, 0xa8, 0x01, 0x03, 0x2d, 0x16, 0x3a, 0x53, 0x7a, 0xa7, 0xad, 0xbd, 0x85, 0x87, 0xe2, 0xf0, - 0xb9, 0xc9, 0xff, 0xc7, 0x82, 0x07, 0xba, 0x0c, 0x83, 0xfc, 0x79, 0x1c, 0x7e, 0x0d, 0x62, 0xe4, - 0xd2, 0x64, 0xf7, 0x47, 0x76, 0xd2, 0x8d, 0x82, 0xff, 0x8e, 0xb1, 0xac, 0x8b, 0xbe, 0xe8, 0xc0, - 0x38, 0x95, 0xa8, 0xe9, 0x7b, 0x3e, 0x25, 0x64, 0x4b, 0x66, 0x5d, 0x8b, 0xa9, 0x46, 0x22, 0x65, - 0x8d, 0x3a, 0x16, 0x2e, 0x19, 0xec, 0x70, 0x86, 0x3d, 0x7a, 0x03, 0x86, 0x62, 0xbf, 0x4a, 0x2a, - 0x5e, 0x14, 0x97, 0x4e, 0x1d, 0x4d, 0x53, 0x52, 0x8f, 0x98, 0x60, 0x84, 0x15, 0x4b, 0xf4, 0x6b, - 0xec, 0xbd, 0xd5, 0x4a, 0xdd, 0xdf, 0x21, 0x2b, 0x61, 0x85, 0x1f, 0x63, 0x4e, 0xdb, 0x5a, 0xfb, - 0xd2, 0xf7, 0x27, 0x29, 0x0b, 0x47, 0x91, 0xc9, 0x0e, 0x67, 0xf9, 0xa3, 0xbf, 0xe5, 0xc0, 0x19, - 0xfe, 0xf0, 0x41, 0xf6, 0x2d, 0x8f, 0x33, 0x87, 0x34, 0x49, 0xb1, 0xfb, 0x1b, 0x33, 0x79, 0x24, - 0x71, 0x3e, 0x27, 0x96, 0xa6, 0xd8, 0x7c, 0x7e, 0xe9, 0xac, 0x55, 0xcf, 0x70, 0xef, 0x4f, 0x2e, - 0xa1, 0xa7, 0x61, 0xa4, 0x25, 0xb6, 0x43, 0x3f, 0x6e, 0xb2, 0xdb, 0x38, 0x7d, 0xfc, 0x9e, 0xe4, - 0x7a, 0x0a, 0xc6, 0x3a, 0x8e, 0x91, 0xb3, 0xfa, 0xc9, 0xfd, 0x72, 0x56, 0xa3, 0x6b, 0x30, 0x92, - 0x84, 0x0d, 0x91, 0xb6, 0x35, 0x2e, 0x95, 0xd8, 0x0c, 0xbc, 0x90, 0xb7, 0xb6, 0x36, 0x14, 0x5a, - 0x7a, 0x72, 0x4f, 0x61, 0x31, 0xd6, 0xe9, 0xb0, 0x08, 0x68, 0xf1, 0xa0, 0x44, 0xc4, 0x8e, 0xec, - 0x0f, 0x66, 0x22, 0xa0, 0xf5, 0x42, 0x6c, 0xe2, 0xa2, 0x45, 0x38, 0xd9, 0xea, 0x38, 0xf3, 0xf3, - 0x5b, 0x80, 0x2a, 0xe8, 0xa4, 0xf3, 0xc0, 0xdf, 0x59, 0xc7, 0x38, 0xed, 0x9f, 0xdf, 0xef, 0xb4, - 0xdf, 0x25, 0x83, 0xf3, 0x43, 0x87, 0xc9, 0xe0, 0x8c, 0xaa, 0xf0, 0x90, 0xd7, 0x4e, 0x42, 0x96, - 0x92, 0xc7, 0xac, 0xc2, 0x83, 0xc1, 0x1f, 0xe1, 0xf1, 0xe5, 0xb7, 0xf7, 0xa6, 0x1e, 0x9a, 0xd9, - 0x07, 0x0f, 0xef, 0x4b, 0x05, 0xbd, 0x0a, 0x43, 0x44, 0x64, 0xa1, 0x2e, 0xfd, 0x9c, 0x2d, 0x25, - 0xc1, 0xcc, 0x6b, 0x2d, 0xe3, 0x6c, 0x39, 0x0c, 0x2b, 0x7e, 0x68, 0x03, 0x46, 0xea, 0x61, 0x9c, - 0xcc, 0x34, 0x7c, 0x2f, 0x26, 0x71, 0xe9, 0x61, 0x36, 0x69, 0x72, 0x75, 0xaf, 0x2b, 0x12, 0x2d, - 0x9d, 0x33, 0x57, 0xd2, 0x9a, 0x58, 0x27, 0x83, 0x08, 0xf3, 0x0f, 0xb3, 0x48, 0x78, 0xe9, 0xfb, - 0xba, 0xc0, 0x3a, 0xf6, 0x78, 0x1e, 0xe5, 0xf5, 0xb0, 0x5a, 0x36, 0xb1, 0x95, 0x83, 0x58, 0x07, - 0xe2, 0x2c, 0x4d, 0xf4, 0x2c, 0x8c, 0xb6, 0xc2, 0x6a, 0xb9, 0x45, 0x2a, 0xeb, 0x5e, 0x52, 0xa9, - 0x97, 0xa6, 0x4c, 0x2b, 0xe3, 0xba, 0x56, 0x86, 0x0d, 0x4c, 0xd4, 0x82, 0xc1, 0x26, 0xcf, 0xd5, - 0x50, 0x7a, 0xd4, 0xd6, 0xd9, 0x46, 0x24, 0x7f, 0x10, 0x36, 0x04, 0xfe, 0x03, 0x4b, 0x36, 0xe8, - 0x1f, 0x3a, 0x70, 0x22, 0x73, 0xbf, 0xac, 0xf4, 0x0e, 0x6b, 0x2a, 0x8b, 0x49, 0x78, 0xf6, 0x71, - 0x36, 0x7c, 0x26, 0xf0, 0x4e, 0x27, 0x08, 0x67, 0x5b, 0xc4, 0xc7, 0x85, 0x25, 0x5c, 0x29, 0x3d, - 0x66, 0x6f, 0x5c, 0x18, 0x41, 0x39, 0x2e, 0xec, 0x07, 0x96, 0x6c, 0xd0, 0x93, 0x30, 0x28, 0x72, - 0x23, 0x96, 0x1e, 0x37, 0xbd, 0xee, 0x22, 0x85, 0x22, 0x96, 0xe5, 0x93, 0x1f, 0x80, 0x93, 0x1d, - 0x47, 0xb7, 0x03, 0x65, 0xfd, 0xf8, 0x0d, 0x07, 0xf4, 0x0b, 0xe9, 0xd6, 0x9f, 0x7e, 0x79, 0x16, - 0x46, 0x2b, 0xfc, 0x25, 0x4e, 0x7e, 0xa5, 0xbd, 0xdf, 0xb4, 0xf7, 0xce, 0x69, 0x65, 0xd8, 0xc0, - 0x74, 0xaf, 0x00, 0xea, 0xcc, 0xcb, 0x7f, 0x28, 0xc7, 0xc9, 0x3f, 0x76, 0x60, 0xcc, 0xd0, 0x19, - 0xac, 0xfb, 0x55, 0x17, 0x00, 0x35, 0xfd, 0x28, 0x0a, 0x23, 0xfd, 0xc9, 0x43, 0x91, 0x76, 0x82, - 0xc5, 0x5b, 0xac, 0x76, 0x94, 0xe2, 0x9c, 0x1a, 0xee, 0x3f, 0xed, 0x87, 0x34, 0xd0, 0x5c, 0x65, - 0x2d, 0x76, 0xba, 0x66, 0x2d, 0x7e, 0x0a, 0x86, 0x5e, 0x8e, 0xc3, 0x60, 0x3d, 0xcd, 0x6d, 0xac, - 0xbe, 0xc5, 0x73, 0xe5, 0xb5, 0xab, 0x0c, 0x53, 0x61, 0x30, 0xec, 0x57, 0x16, 0xfc, 0x46, 0xd2, - 0x99, 0xfc, 0xf6, 0xb9, 0xe7, 0x39, 0x1c, 0x2b, 0x0c, 0xf6, 0xfa, 0xe1, 0x0e, 0x51, 0x8e, 0x80, - 0xf4, 0xf5, 0x43, 0xfe, 0xe4, 0x06, 0x2b, 0x43, 0x17, 0x61, 0x58, 0x39, 0x11, 0x84, 0x67, 0x42, - 0x8d, 0x94, 0xf2, 0x34, 0xe0, 0x14, 0x87, 0x29, 0x84, 0xc2, 0xf0, 0x2c, 0x4c, 0x28, 0x65, 0x1b, - 0xc7, 0x93, 0x8c, 0x29, 0x9b, 0xcb, 0x76, 0x09, 0xc6, 0x8a, 0x65, 0x9e, 0x6f, 0x79, 0xf8, 0x48, - 0x7c, 0xcb, 0xda, 0xad, 0x87, 0x62, 0xaf, 0xb7, 0x1e, 0xcc, 0xb9, 0x3d, 0xd4, 0xd3, 0xdc, 0xfe, - 0x74, 0x1f, 0x0c, 0x5e, 0x27, 0x11, 0x4b, 0x1b, 0xff, 0x24, 0x0c, 0xee, 0xf0, 0x7f, 0xb3, 0x57, - 0x66, 0x05, 0x06, 0x96, 0xe5, 0xf4, 0xbb, 0x6d, 0xb6, 0xfd, 0x46, 0x75, 0x3e, 0x5d, 0xc5, 0x69, - 0x5a, 0x47, 0x59, 0x80, 0x53, 0x1c, 0x5a, 0xa1, 0x46, 0x35, 0xfb, 0x66, 0xd3, 0xef, 0x78, 0xf7, - 0x7f, 0x51, 0x16, 0xe0, 0x14, 0x07, 0x3d, 0x0e, 0x03, 0x35, 0x3f, 0xd9, 0xf0, 0x6a, 0x59, 0xcf, - 0xe8, 0x22, 0x83, 0x62, 0x51, 0xca, 0xdc, 0x62, 0x7e, 0xb2, 0x11, 0x11, 0x66, 0xd9, 0xed, 0xc8, - 0xd8, 0xb1, 0xa8, 0x95, 0x61, 0x03, 0x93, 0x35, 0x29, 0x14, 0x3d, 0x13, 0x71, 0xb2, 0x69, 0x93, - 0x64, 0x01, 0x4e, 0x71, 0xe8, 0xfc, 0xaf, 0x84, 0xcd, 0x96, 0xdf, 0x10, 0x11, 0xdc, 0xda, 0xfc, - 0x9f, 0x13, 0x70, 0xac, 0x30, 0x28, 0x36, 0x15, 0x61, 0x54, 0xfc, 0x64, 0x5f, 0x9a, 0x5b, 0x17, - 0x70, 0xac, 0x30, 0xdc, 0xeb, 0x30, 0xc6, 0x57, 0xf2, 0x5c, 0xc3, 0xf3, 0x9b, 0x8b, 0x73, 0xe8, - 0x72, 0xc7, 0xad, 0x87, 0x27, 0x73, 0x6e, 0x3d, 0x9c, 0x31, 0x2a, 0x75, 0xde, 0x7e, 0x70, 0x7f, - 0x58, 0x80, 0xa1, 0x63, 0x7c, 0xac, 0xf3, 0xd8, 0xdf, 0x9d, 0x46, 0xb7, 0x32, 0x0f, 0x75, 0xae, - 0xdb, 0xbc, 0xc4, 0xb4, 0xef, 0x23, 0x9d, 0xff, 0xa9, 0x00, 0x67, 0x25, 0xaa, 0x3c, 0xcb, 0x2d, - 0xce, 0xb1, 0x07, 0xd0, 0x8e, 0x7e, 0xa0, 0x23, 0x63, 0xa0, 0xd7, 0xed, 0x9d, 0x46, 0x17, 0xe7, - 0xba, 0x0e, 0xf5, 0xab, 0x99, 0xa1, 0xc6, 0x56, 0xb9, 0xee, 0x3f, 0xd8, 0x7f, 0xe1, 0xc0, 0x64, - 0xfe, 0x60, 0x1f, 0xc3, 0xdb, 0xa8, 0x6f, 0x98, 0x6f, 0xa3, 0xfe, 0xa2, 0xbd, 0x29, 0x66, 0x76, - 0xa5, 0xcb, 0x2b, 0xa9, 0xff, 0xdd, 0x81, 0xd3, 0xb2, 0x02, 0xdb, 0x3d, 0x67, 0xfd, 0x80, 0x05, - 0xef, 0x1c, 0xfd, 0x34, 0x7b, 0xdd, 0x98, 0x66, 0x2f, 0xda, 0xeb, 0xb8, 0xde, 0x8f, 0xae, 0x6f, - 0xca, 0xff, 0xb9, 0x03, 0xa5, 0xbc, 0x0a, 0xc7, 0xf0, 0xc9, 0x5f, 0x33, 0x3f, 0xf9, 0xf5, 0xa3, - 0xe9, 0x79, 0xf7, 0x0f, 0x5e, 0xea, 0x36, 0x50, 0xa8, 0x21, 0xf5, 0x2a, 0xc7, 0x96, 0x4f, 0x9a, - 0xb3, 0xc8, 0x57, 0xd0, 0x1a, 0x30, 0x10, 0xb3, 0x28, 0x15, 0x31, 0x05, 0xae, 0xd8, 0xd0, 0xb6, - 0x28, 0x3d, 0x61, 0x63, 0x67, 0xff, 0x63, 0xc1, 0xc3, 0xfd, 0xcd, 0x02, 0x9c, 0x53, 0x6f, 0x1e, - 0x93, 0x1d, 0xd2, 0x48, 0xd7, 0x07, 0x7b, 0x21, 0xc3, 0x53, 0x3f, 0xed, 0xbd, 0x90, 0x91, 0xb2, - 0x48, 0xd7, 0x42, 0x0a, 0xc3, 0x1a, 0x4f, 0x54, 0x86, 0x33, 0xec, 0x45, 0x8b, 0x05, 0x3f, 0xf0, - 0x1a, 0xfe, 0xab, 0x24, 0xc2, 0xa4, 0x19, 0xee, 0x78, 0x0d, 0xa1, 0xa9, 0xab, 0x5b, 0xd3, 0x0b, - 0x79, 0x48, 0x38, 0xbf, 0x6e, 0xc7, 0x89, 0xbb, 0xaf, 0xd7, 0x13, 0xb7, 0xfb, 0x27, 0x0e, 0x8c, - 0x1e, 0xe3, 0x0b, 0xd1, 0xa1, 0xb9, 0x24, 0x9e, 0xb3, 0xb7, 0x24, 0xba, 0x2c, 0x83, 0xbd, 0x22, - 0x74, 0x3c, 0x9a, 0x8b, 0x3e, 0xe3, 0xa8, 0x38, 0x1e, 0x1e, 0x2f, 0xf9, 0x61, 0x7b, 0xed, 0x38, - 0x48, 0xaa, 0x4d, 0xf4, 0x8d, 0x4c, 0xfe, 0xd1, 0x82, 0xad, 0x24, 0x5a, 0x1d, 0xad, 0x39, 0x44, - 0x1e, 0xd2, 0xaf, 0x3a, 0x00, 0xbc, 0x9d, 0x22, 0x7d, 0x39, 0x6d, 0xdb, 0xe6, 0x91, 0x8d, 0x14, - 0x65, 0xc2, 0x9b, 0xa6, 0x96, 0x50, 0x5a, 0x80, 0xb5, 0x96, 0xdc, 0x43, 0x82, 0xd1, 0x7b, 0xce, - 0x6d, 0xfa, 0x45, 0x07, 0x4e, 0x64, 0x9a, 0x9b, 0x53, 0x7f, 0xcb, 0x7c, 0xe3, 0xd1, 0x82, 0x66, - 0x65, 0x26, 0xb5, 0xd6, 0x8d, 0x27, 0xff, 0xc5, 0x05, 0xe3, 0xb5, 0x71, 0xf4, 0x1a, 0x0c, 0x4b, - 0xcb, 0x87, 0x9c, 0xde, 0x36, 0xdf, 0xba, 0x55, 0xc7, 0x1b, 0x09, 0x89, 0x71, 0xca, 0x2f, 0x13, - 0x26, 0x58, 0xe8, 0x29, 0x4c, 0xf0, 0xfe, 0xbe, 0x94, 0x9b, 0x6f, 0x97, 0xee, 0x3f, 0x12, 0xbb, - 0xf4, 0x43, 0xd6, 0xed, 0xd2, 0x0f, 0x1f, 0xb3, 0x5d, 0x5a, 0x73, 0x12, 0x16, 0xef, 0xc1, 0x49, - 0xf8, 0x1a, 0x9c, 0xde, 0x49, 0x0f, 0x9d, 0x6a, 0x26, 0x89, 0xd4, 0x4d, 0x4f, 0xe6, 0x5a, 0xa3, - 0xe9, 0x01, 0x3a, 0x4e, 0x48, 0x90, 0x68, 0xc7, 0xd5, 0x34, 0x42, 0xf1, 0x7a, 0x0e, 0x39, 0x9c, - 0xcb, 0x24, 0xeb, 0xed, 0x19, 0xec, 0xc1, 0xdb, 0xf3, 0x96, 0x03, 0x67, 0xbc, 0x8e, 0x6b, 0x76, - 0x98, 0x6c, 0x89, 0x90, 0x93, 0x1b, 0xf6, 0x54, 0x08, 0x83, 0xbc, 0x70, 0xab, 0xe5, 0x15, 0xe1, - 0xfc, 0x06, 0xa1, 0xc7, 0x52, 0xd7, 0x3b, 0x8f, 0x6b, 0xcd, 0xf7, 0x93, 0x7f, 0x23, 0x1b, 0xcf, - 0x03, 0x6c, 0xe8, 0x3f, 0x6a, 0xf7, 0xb4, 0x6d, 0x21, 0xa6, 0x67, 0xe4, 0x1e, 0x62, 0x7a, 0x32, - 0xae, 0xb7, 0x51, 0x4b, 0xae, 0xb7, 0x00, 0x26, 0xfc, 0xa6, 0x57, 0x23, 0xeb, 0xed, 0x46, 0x83, - 0xdf, 0x9b, 0x91, 0xaf, 0x11, 0xe7, 0x5a, 0xf0, 0x56, 0xc2, 0x8a, 0xd7, 0xc8, 0xbe, 0x43, 0xaf, - 0xee, 0x07, 0x2d, 0x65, 0x28, 0xe1, 0x0e, 0xda, 0x74, 0xc2, 0xb2, 0x1c, 0x82, 0x24, 0xa1, 0xa3, - 0xcd, 0x02, 0x47, 0x86, 0xf8, 0x84, 0xbd, 0x92, 0x82, 0xb1, 0x8e, 0x83, 0x96, 0x61, 0xb8, 0x1a, - 0xc4, 0xe2, 0xc6, 0xf0, 0x09, 0x26, 0xcc, 0xde, 0x45, 0x45, 0xe0, 0xfc, 0xd5, 0xb2, 0xba, 0x2b, - 0xfc, 0x50, 0x4e, 0x52, 0x4c, 0x55, 0x8e, 0xd3, 0xfa, 0x68, 0x95, 0x11, 0x13, 0xef, 0xac, 0xf1, - 0x78, 0x8e, 0x47, 0xba, 0x38, 0x8c, 0xe6, 0xaf, 0xca, 0x97, 0xe2, 0xc6, 0x04, 0x3b, 0xf1, 0x60, - 0x5a, 0x4a, 0x41, 0x7b, 0x15, 0xfa, 0xe4, 0xbe, 0xaf, 0x42, 0xb3, 0x6c, 0xb8, 0x49, 0x43, 0xb9, - 0x87, 0x2f, 0x58, 0xcb, 0x86, 0x9b, 0x46, 0x4a, 0x8a, 0x6c, 0xb8, 0x29, 0x00, 0xeb, 0x2c, 0xd1, - 0x5a, 0x37, 0x37, 0xf9, 0x29, 0x26, 0x34, 0x0e, 0xee, 0xf4, 0xd6, 0xfd, 0xa5, 0xa7, 0xf7, 0xf5, - 0x97, 0x76, 0xf8, 0x77, 0xcf, 0x1c, 0xc0, 0xbf, 0x5b, 0x67, 0x79, 0x4a, 0x17, 0xe7, 0x84, 0x4b, - 0xdd, 0xc2, 0xf9, 0x8e, 0x65, 0x46, 0xe1, 0x91, 0xa7, 0xec, 0x5f, 0xcc, 0x19, 0x74, 0x0d, 0x20, - 0x3f, 0x77, 0xe8, 0x00, 0x72, 0x2a, 0x9e, 0x53, 0x38, 0x4b, 0x78, 0x5b, 0x14, 0xe2, 0x39, 0x05, - 0x63, 0x1d, 0x27, 0xeb, 0x2d, 0x7d, 0xf0, 0xc8, 0xbc, 0xa5, 0x93, 0xc7, 0xe0, 0x2d, 0x3d, 0xdf, - 0xb3, 0xb7, 0xf4, 0x16, 0x9c, 0x6a, 0x85, 0xd5, 0x79, 0x3f, 0x8e, 0xda, 0xec, 0x22, 0xe1, 0x6c, - 0xbb, 0x5a, 0x23, 0x09, 0x73, 0xb7, 0x8e, 0x5c, 0x7a, 0x97, 0xde, 0xc8, 0x16, 0x5b, 0xc8, 0x72, - 0x8d, 0x66, 0x2a, 0x30, 0xd3, 0x09, 0x8b, 0xba, 0xcd, 0x29, 0xc4, 0x79, 0x2c, 0x74, 0x3f, 0xed, - 0x23, 0xc7, 0xe3, 0xa7, 0xfd, 0x20, 0x0c, 0xc5, 0xf5, 0x76, 0x52, 0x0d, 0x6f, 0x06, 0xcc, 0x19, - 0x3f, 0x3c, 0xfb, 0x0e, 0x65, 0xca, 0x16, 0xf0, 0x3b, 0x7b, 0x53, 0x13, 0xf2, 0x7f, 0xcd, 0x8a, - 0x2d, 0x20, 0xe8, 0x9b, 0x5d, 0xee, 0x2b, 0xb9, 0x47, 0x79, 0x5f, 0xe9, 0xdc, 0x81, 0xee, 0x2a, - 0xe5, 0x39, 0xa3, 0x1f, 0xfd, 0x99, 0x73, 0x46, 0x7f, 0xdd, 0x81, 0xb1, 0x1d, 0xdd, 0x65, 0x20, - 0x1c, 0xe6, 0x16, 0x02, 0x77, 0x0c, 0x4f, 0xc4, 0xac, 0x4b, 0xe5, 0x9c, 0x01, 0xba, 0x93, 0x05, - 0x60, 0xb3, 0x25, 0x39, 0x41, 0x45, 0x8f, 0xdd, 0xaf, 0xa0, 0xa2, 0x37, 0x98, 0x1c, 0x93, 0x87, - 0x5c, 0xe6, 0x45, 0xb7, 0x1b, 0x53, 0x2c, 0x65, 0xa2, 0x0a, 0x29, 0xd6, 0xf9, 0xa1, 0x2f, 0x38, - 0x30, 0x21, 0xcf, 0x65, 0xc2, 0xe5, 0x17, 0x8b, 0xa8, 0x48, 0x9b, 0xc7, 0x41, 0x16, 0x56, 0xbf, - 0x91, 0xe1, 0x83, 0x3b, 0x38, 0x53, 0xa9, 0xae, 0x82, 0xd0, 0x6a, 0x31, 0x0b, 0xfe, 0x15, 0x3a, - 0xcc, 0x4c, 0x0a, 0xc6, 0x3a, 0x0e, 0xfa, 0x96, 0x03, 0xc5, 0x7a, 0x18, 0x6e, 0xc7, 0xa5, 0x27, - 0x99, 0x40, 0x7f, 0xc1, 0xb2, 0x6e, 0x7a, 0x85, 0xd2, 0xe6, 0x4a, 0xe9, 0xd3, 0xd2, 0x76, 0xc4, - 0x60, 0x77, 0xf6, 0xa6, 0xc6, 0x8d, 0xe7, 0x9c, 0xe2, 0x37, 0xdf, 0xd6, 0x20, 0xc2, 0xb6, 0xc9, - 0x9a, 0x86, 0xbe, 0xec, 0xc0, 0xc4, 0xcd, 0x8c, 0x41, 0x43, 0x84, 0x85, 0x62, 0xfb, 0xa6, 0x12, - 0x3e, 0xdc, 0x59, 0x28, 0xee, 0x68, 0x01, 0xfa, 0xbc, 0x69, 0xe8, 0xe4, 0xf1, 0xa3, 0x16, 0x07, - 0x30, 0x63, 0x58, 0xe5, 0xd7, 0x82, 0xf2, 0x2d, 0x9e, 0xf7, 0x1c, 0x1f, 0x32, 0x49, 0x3b, 0x93, - 0x7e, 0xac, 0x9c, 0xaa, 0xc4, 0xb4, 0xb7, 0x58, 0x58, 0xec, 0xc6, 0xe7, 0xd7, 0xcd, 0x2d, 0x5f, - 0x3e, 0x0b, 0xe3, 0xa6, 0x6f, 0x0f, 0xbd, 0xdb, 0x7c, 0xaa, 0xe3, 0x42, 0xf6, 0xd5, 0x83, 0x31, - 0x89, 0x6f, 0xbc, 0x7c, 0x60, 0x3c, 0x4d, 0x50, 0x38, 0xd2, 0xa7, 0x09, 0xfa, 0x8e, 0xe7, 0x69, - 0x82, 0x89, 0xa3, 0x78, 0x9a, 0xe0, 0xe4, 0x81, 0x9e, 0x26, 0xd0, 0x9e, 0x86, 0xe8, 0xbf, 0xcb, - 0xd3, 0x10, 0x33, 0x70, 0x42, 0xde, 0xfd, 0x21, 0x22, 0xfb, 0x3b, 0x77, 0xfb, 0xab, 0x57, 0xc6, - 0xe7, 0xcc, 0x62, 0x9c, 0xc5, 0xa7, 0x8b, 0xac, 0x18, 0xb0, 0x9a, 0x03, 0xb6, 0xde, 0x8d, 0x32, - 0xa7, 0x16, 0x3b, 0x3e, 0x0b, 0x11, 0x25, 0xa3, 0x9d, 0x8b, 0x0c, 0x76, 0x47, 0xfe, 0x83, 0x79, - 0x0b, 0xd0, 0x4b, 0x50, 0x0a, 0xb7, 0xb6, 0x1a, 0xa1, 0x57, 0x4d, 0xdf, 0x4f, 0x90, 0x71, 0x09, - 0xfc, 0xae, 0xaa, 0x4a, 0xb7, 0xbb, 0xd6, 0x05, 0x0f, 0x77, 0xa5, 0x80, 0xde, 0xa2, 0x8a, 0x49, - 0x12, 0x46, 0xa4, 0x9a, 0xda, 0x6a, 0x86, 0x59, 0x9f, 0x89, 0xf5, 0x3e, 0x97, 0x4d, 0x3e, 0xbc, - 0xf7, 0xea, 0xa3, 0x64, 0x4a, 0x71, 0xb6, 0x59, 0x28, 0x82, 0xb3, 0xad, 0x3c, 0x53, 0x51, 0x2c, - 0x6e, 0x2c, 0xed, 0x67, 0xb0, 0x52, 0x6f, 0x69, 0xe7, 0x1a, 0x9b, 0x62, 0xdc, 0x85, 0xb2, 0xfe, - 0xc6, 0xc1, 0xd0, 0xf1, 0xbc, 0x71, 0xf0, 0x71, 0x80, 0x8a, 0xcc, 0xb6, 0x26, 0x8d, 0x0f, 0xcb, - 0x56, 0xae, 0xd2, 0x70, 0x9a, 0xda, 0xb3, 0xb2, 0x8a, 0x0d, 0xd6, 0x58, 0xa2, 0xff, 0x9d, 0xfb, - 0x08, 0x08, 0xb7, 0xb0, 0xd4, 0xac, 0xcf, 0x89, 0x9f, 0xb9, 0x87, 0x40, 0xfe, 0x91, 0x03, 0x93, - 0x7c, 0xe6, 0x65, 0x95, 0x7b, 0xaa, 0x5a, 0x88, 0xbb, 0x3d, 0xb6, 0x43, 0x57, 0x78, 0xd6, 0x24, - 0x83, 0x2b, 0x73, 0x74, 0xef, 0xd3, 0x12, 0xf4, 0xd5, 0x9c, 0x23, 0xc5, 0x09, 0x5b, 0x36, 0xcb, - 0xfc, 0xa7, 0x1c, 0x4e, 0xdd, 0xee, 0xe5, 0x14, 0xf1, 0x4f, 0xba, 0x9a, 0x54, 0x11, 0x6b, 0xde, - 0x2f, 0x1d, 0x91, 0x49, 0x55, 0x7f, 0x6f, 0xe2, 0x40, 0x86, 0xd5, 0x2f, 0x3a, 0x30, 0xe1, 0x65, - 0x42, 0x4d, 0x98, 0x1d, 0xc8, 0x8a, 0x4d, 0x6a, 0x26, 0x4a, 0xe3, 0x57, 0x98, 0x92, 0x97, 0x8d, - 0x6a, 0xc1, 0x1d, 0xcc, 0xd1, 0x0f, 0x1d, 0x38, 0x9f, 0x78, 0xf1, 0x36, 0xcf, 0xe6, 0x1c, 0xa7, - 0x77, 0x75, 0x45, 0xe3, 0x4e, 0xb3, 0xd5, 0xf8, 0x8a, 0xf5, 0xd5, 0xb8, 0xd1, 0x9d, 0x27, 0x5f, - 0x97, 0x8f, 0x8a, 0x75, 0x79, 0x7e, 0x1f, 0x4c, 0xbc, 0x5f, 0xd3, 0x27, 0x3f, 0xe3, 0xf0, 0x57, - 0xbf, 0xba, 0xaa, 0x7c, 0x9b, 0xa6, 0xca, 0xb7, 0x62, 0xf3, 0xdd, 0x21, 0x5d, 0xf7, 0xfc, 0x55, - 0x07, 0x4e, 0xe7, 0xed, 0x48, 0x39, 0x4d, 0xfa, 0xa8, 0xd9, 0x24, 0x8b, 0xa7, 0x2c, 0xbd, 0x41, - 0x56, 0x9e, 0x3d, 0x99, 0xbc, 0x0a, 0x8f, 0xdc, 0xed, 0x2b, 0xde, 0x8d, 0xde, 0x90, 0xae, 0x16, - 0xff, 0xf9, 0xb0, 0xe6, 0x85, 0x4c, 0x48, 0xcb, 0x7a, 0x0c, 0x77, 0x00, 0x03, 0x7e, 0xd0, 0xf0, - 0x03, 0x22, 0xee, 0x6b, 0xda, 0x3c, 0xc3, 0x8a, 0x67, 0x8b, 0x28, 0x75, 0x2c, 0xb8, 0xdc, 0x67, - 0xa7, 0x64, 0xf6, 0x21, 0xb8, 0xfe, 0xe3, 0x7f, 0x08, 0xee, 0x26, 0x0c, 0xdf, 0xf4, 0x93, 0x3a, - 0x0b, 0xa6, 0x10, 0xbe, 0x3e, 0x0b, 0xf7, 0x1c, 0x29, 0xb9, 0xb4, 0xef, 0x37, 0x24, 0x03, 0x9c, - 0xf2, 0x42, 0x17, 0x39, 0x63, 0x16, 0xb9, 0x9d, 0x0d, 0xa9, 0xbd, 0x21, 0x0b, 0x70, 0x8a, 0x43, - 0x07, 0x6b, 0x94, 0xfe, 0x92, 0x39, 0xa0, 0x44, 0x66, 0x64, 0x1b, 0x19, 0x2f, 0x05, 0x45, 0x7e, - 0x9b, 0xf8, 0x86, 0xc6, 0x03, 0x1b, 0x1c, 0x55, 0x72, 0xea, 0xa1, 0xae, 0xc9, 0xa9, 0x5f, 0x67, - 0x0a, 0x5b, 0xe2, 0x07, 0x6d, 0xb2, 0x16, 0x88, 0x78, 0xef, 0x15, 0x3b, 0x77, 0x9f, 0x39, 0x4d, - 0x7e, 0x04, 0x4f, 0x7f, 0x63, 0x8d, 0x9f, 0xe6, 0x72, 0x19, 0xd9, 0xd7, 0xe5, 0x92, 0x9a, 0x5c, - 0x46, 0xad, 0x9b, 0x5c, 0x12, 0xd2, 0xb2, 0x62, 0x72, 0xf9, 0x99, 0x32, 0x07, 0xfc, 0x85, 0x03, - 0x48, 0xe9, 0x5d, 0x4a, 0xa0, 0x1e, 0x43, 0x50, 0xe5, 0x27, 0x1c, 0x80, 0x40, 0x3d, 0x17, 0x6a, - 0x77, 0x17, 0xe4, 0x34, 0xd3, 0x06, 0xa4, 0x30, 0xac, 0xf1, 0x74, 0xff, 0xcc, 0x49, 0x63, 0x97, - 0xd3, 0xbe, 0x1f, 0x43, 0x10, 0xd9, 0xae, 0x19, 0x44, 0xb6, 0x61, 0xd1, 0x74, 0xaf, 0xba, 0xd1, - 0x25, 0x9c, 0xec, 0x27, 0x05, 0x38, 0xa1, 0x23, 0x97, 0xc9, 0x71, 0x7c, 0xec, 0x9b, 0x46, 0x04, - 0xed, 0x35, 0xbb, 0xfd, 0x2d, 0x0b, 0x0f, 0x50, 0x5e, 0xb4, 0xf6, 0xc7, 0x33, 0xd1, 0xda, 0x37, - 0xec, 0xb3, 0xde, 0x3f, 0x64, 0xfb, 0x3f, 0x3b, 0x70, 0x2a, 0x53, 0xe3, 0x18, 0x26, 0xd8, 0x8e, - 0x39, 0xc1, 0x9e, 0xb7, 0xde, 0xeb, 0x2e, 0xb3, 0xeb, 0xdb, 0x85, 0x8e, 0xde, 0xb2, 0x43, 0xdc, - 0xa7, 0x1d, 0x28, 0x52, 0x6d, 0x59, 0xc6, 0x73, 0x7d, 0xf4, 0x48, 0x66, 0x00, 0xd3, 0xeb, 0x85, - 0x74, 0x56, 0xed, 0x63, 0x30, 0xcc, 0xb9, 0x4f, 0x7e, 0xca, 0x01, 0x48, 0x91, 0xee, 0x97, 0x0a, - 0xec, 0x7e, 0xb7, 0x00, 0x67, 0x72, 0xa7, 0x11, 0xfa, 0xac, 0xb2, 0xc8, 0x39, 0xb6, 0xa3, 0x15, - 0x0d, 0x46, 0xba, 0x61, 0x6e, 0xcc, 0x30, 0xcc, 0x09, 0x7b, 0xdc, 0xfd, 0x3a, 0xc0, 0x08, 0x31, - 0xad, 0x0d, 0xd6, 0x8f, 0x9d, 0x34, 0x00, 0x56, 0xe5, 0x35, 0xfa, 0x4b, 0x78, 0x89, 0xc7, 0xfd, - 0x89, 0x76, 0xc3, 0x41, 0x76, 0xf4, 0x18, 0x64, 0xc5, 0x4d, 0x53, 0x56, 0x60, 0xfb, 0x7e, 0xe4, - 0x2e, 0xc2, 0xe2, 0x15, 0xc8, 0x73, 0x2c, 0xf7, 0x96, 0x04, 0xd2, 0xb8, 0x0e, 0x5b, 0xe8, 0xf9, - 0x3a, 0xec, 0x18, 0x8c, 0xbc, 0xe8, 0xab, 0x04, 0xa2, 0xb3, 0xd3, 0xdf, 0xfb, 0xd1, 0x85, 0x07, - 0xbe, 0xff, 0xa3, 0x0b, 0x0f, 0xfc, 0xf0, 0x47, 0x17, 0x1e, 0xf8, 0xc4, 0xed, 0x0b, 0xce, 0xf7, - 0x6e, 0x5f, 0x70, 0xbe, 0x7f, 0xfb, 0x82, 0xf3, 0xc3, 0xdb, 0x17, 0x9c, 0x7f, 0x77, 0xfb, 0x82, - 0xf3, 0x77, 0xfe, 0xf4, 0xc2, 0x03, 0x2f, 0x0e, 0xc9, 0x8e, 0xfd, 0xbf, 0x00, 0x00, 0x00, 0xff, - 0xff, 0xc2, 0xab, 0xa3, 0xd2, 0xf4, 0xd8, 0x00, 0x00, + 0xf9, 0xcb, 0x2e, 0x99, 0x32, 0xdc, 0x81, 0xed, 0xfe, 0x8b, 0x47, 0x40, 0x5d, 0xd1, 0xec, 0x61, + 0x47, 0x6d, 0xa9, 0x60, 0xda, 0xa2, 0xe5, 0x60, 0x5a, 0xb5, 0xb7, 0x64, 0x02, 0x6a, 0x93, 0x34, + 0xa0, 0x76, 0xc0, 0x76, 0x40, 0xad, 0x52, 0xb2, 0x3b, 0x82, 0x6a, 0xbf, 0xea, 0xc0, 0x68, 0x10, + 0x56, 0x89, 0xf2, 0x80, 0x0e, 0xb2, 0x15, 0xfe, 0x92, 0xbd, 0xbb, 0x09, 0x3c, 0x38, 0x54, 0x90, + 0xe7, 0x81, 0xde, 0x6a, 0x4b, 0xd6, 0x8b, 0xb0, 0xd1, 0x0e, 0xb4, 0xa0, 0x99, 0x96, 0xb9, 0x07, + 0xe7, 0xa1, 0xbc, 0x23, 0xda, 0x5d, 0xed, 0xc4, 0xb7, 0x34, 0x3d, 0x71, 0xd8, 0x96, 0xc9, 0x54, + 0xde, 0xbb, 0xd3, 0x1c, 0x51, 0x32, 0x71, 0x77, 0xaa, 0x3f, 0xba, 0x30, 0xc0, 0x23, 0xc2, 0x45, + 0x2e, 0x2d, 0xe6, 0x1f, 0xe5, 0xd1, 0xe2, 0x58, 0x94, 0xa0, 0x44, 0x46, 0x59, 0x8c, 0xd8, 0x7a, + 0x86, 0xc3, 0x88, 0xe2, 0xc8, 0x0f, 0xb3, 0x40, 0xcf, 0xe9, 0x47, 0xff, 0xd1, 0x5e, 0x8e, 0xfe, + 0x63, 0x5d, 0x8f, 0xfd, 0x5f, 0x70, 0x60, 0xb4, 0xa2, 0x3d, 0x8b, 0x51, 0x7a, 0xc2, 0xd6, 0xeb, + 0xe0, 0x79, 0xaf, 0x97, 0x70, 0xb7, 0x9b, 0xf1, 0x0c, 0x87, 0xc1, 0x9d, 0x25, 0x10, 0x65, 0x76, + 0x0e, 0xa6, 0xea, 0x58, 0x49, 0xcc, 0x61, 0xda, 0x4d, 0x64, 0xb4, 0x2a, 0x85, 0x61, 0xc1, 0x0b, + 0xbd, 0x0e, 0x43, 0xf2, 0x52, 0x81, 0x08, 0xbe, 0xc7, 0x36, 0xfc, 0x20, 0xa6, 0xb3, 0x55, 0x66, + 0x1d, 0xe4, 0x50, 0xac, 0x38, 0xa2, 0x3a, 0xf4, 0x55, 0xbd, 0x9a, 0x08, 0xc3, 0x5f, 0xb5, 0x93, + 0xd5, 0x55, 0xf2, 0x64, 0x47, 0xd2, 0xf9, 0x99, 0x45, 0x4c, 0x59, 0xa0, 0x5b, 0xe9, 0xbb, 0x02, + 0x13, 0xd6, 0x76, 0x5f, 0x53, 0x2d, 0xe4, 0x3a, 0x41, 0xc7, 0x33, 0x05, 0x55, 0xe1, 0x9f, 0xfe, + 0xff, 0x18, 0xdb, 0x05, 0x3b, 0x69, 0x61, 0x79, 0xa2, 0x97, 0xd4, 0xc7, 0x4d, 0xb9, 0xd4, 0x93, + 0xa4, 0x55, 0xfa, 0x79, 0x5b, 0x5c, 0x58, 0xba, 0x12, 0xfe, 0x90, 0xfb, 0xc6, 0xc6, 0x3a, 0x66, + 0xd4, 0x51, 0x03, 0x06, 0x5a, 0x2c, 0x74, 0xa6, 0xf4, 0x4e, 0x5b, 0x7b, 0x0b, 0x0f, 0xc5, 0xe1, + 0x73, 0x93, 0xff, 0x8f, 0x05, 0x0f, 0x74, 0x19, 0x06, 0xf9, 0xf3, 0x38, 0xfc, 0x1a, 0xc4, 0xc8, + 0xa5, 0xc9, 0xee, 0x8f, 0xec, 0xa4, 0x1b, 0x05, 0xff, 0x1d, 0x63, 0x59, 0x17, 0x7d, 0xd1, 0x81, + 0x71, 0x2a, 0x51, 0xd3, 0xf7, 0x7c, 0x4a, 0xc8, 0x96, 0xcc, 0xba, 0x16, 0x53, 0x8d, 0x44, 0xca, + 0x1a, 0x75, 0x2c, 0x5c, 0x32, 0xd8, 0xe1, 0x0c, 0x7b, 0xf4, 0x06, 0x0c, 0xc5, 0x7e, 0x95, 0x54, + 0xbc, 0x28, 0x2e, 0x9d, 0x3a, 0x9a, 0xa6, 0xa4, 0x1e, 0x31, 0xc1, 0x08, 0x2b, 0x96, 0xe8, 0xd7, + 0xd8, 0x7b, 0xab, 0x95, 0xba, 0xbf, 0x43, 0x56, 0xc2, 0x0a, 0x3f, 0xc6, 0x9c, 0xb6, 0xb5, 0xf6, + 0xa5, 0xef, 0x4f, 0x52, 0x16, 0x8e, 0x22, 0x93, 0x1d, 0xce, 0xf2, 0x47, 0x7f, 0xcb, 0x81, 0x33, + 0xfc, 0xe1, 0x83, 0xec, 0x5b, 0x1e, 0x67, 0x0e, 0x69, 0x92, 0x62, 0xf7, 0x37, 0x66, 0xf2, 0x48, + 0xe2, 0x7c, 0x4e, 0x2c, 0x4d, 0xb1, 0xf9, 0xfc, 0xd2, 0x59, 0xab, 0x9e, 0xe1, 0xde, 0x9f, 0x5c, + 0x42, 0x4f, 0xc3, 0x48, 0x4b, 0x6c, 0x87, 0x7e, 0xdc, 0x64, 0xb7, 0x71, 0xfa, 0xf8, 0x3d, 0xc9, + 0xf5, 0x14, 0x8c, 0x75, 0x1c, 0x23, 0x67, 0xf5, 0x93, 0xfb, 0xe5, 0xac, 0x46, 0xd7, 0x60, 0x24, + 0x09, 0x1b, 0x22, 0x6d, 0x6b, 0x5c, 0x2a, 0xb1, 0x19, 0x78, 0x21, 0x6f, 0x6d, 0x6d, 0x28, 0xb4, + 0xf4, 0xe4, 0x9e, 0xc2, 0x62, 0xac, 0xd3, 0x61, 0x11, 0xd0, 0xe2, 0x41, 0x89, 0x88, 0x1d, 0xd9, + 0x1f, 0xcc, 0x44, 0x40, 0xeb, 0x85, 0xd8, 0xc4, 0x45, 0x8b, 0x70, 0xb2, 0xd5, 0x71, 0xe6, 0xe7, + 0xb7, 0x00, 0x55, 0xd0, 0x49, 0xe7, 0x81, 0xbf, 0xb3, 0x8e, 0x71, 0xda, 0x3f, 0xbf, 0xdf, 0x69, + 0xbf, 0x4b, 0x06, 0xe7, 0x87, 0x0e, 0x93, 0xc1, 0x19, 0x55, 0xe1, 0x21, 0xaf, 0x9d, 0x84, 0x2c, + 0x25, 0x8f, 0x59, 0x85, 0x07, 0x83, 0x3f, 0xc2, 0xe3, 0xcb, 0x6f, 0xef, 0x4d, 0x3d, 0x34, 0xb3, + 0x0f, 0x1e, 0xde, 0x97, 0x0a, 0x7a, 0x15, 0x86, 0x88, 0xc8, 0x42, 0x5d, 0xfa, 0x39, 0x5b, 0x4a, + 0x82, 0x99, 0xd7, 0x5a, 0xc6, 0xd9, 0x72, 0x18, 0x56, 0xfc, 0xd0, 0x06, 0x8c, 0xd4, 0xc3, 0x38, + 0x99, 0x69, 0xf8, 0x5e, 0x4c, 0xe2, 0xd2, 0xc3, 0x6c, 0xd2, 0xe4, 0xea, 0x5e, 0x57, 0x24, 0x5a, + 0x3a, 0x67, 0xae, 0xa4, 0x35, 0xb1, 0x4e, 0x06, 0x11, 0xe6, 0x1f, 0x66, 0x91, 0xf0, 0xd2, 0xf7, + 0x75, 0x81, 0x75, 0xec, 0xf1, 0x3c, 0xca, 0xeb, 0x61, 0xb5, 0x6c, 0x62, 0x2b, 0x07, 0xb1, 0x0e, + 0xc4, 0x59, 0x9a, 0xe8, 0x59, 0x18, 0x6d, 0x85, 0xd5, 0x72, 0x8b, 0x54, 0xd6, 0xbd, 0xa4, 0x52, + 0x2f, 0x4d, 0x99, 0x56, 0xc6, 0x75, 0xad, 0x0c, 0x1b, 0x98, 0xa8, 0x05, 0x83, 0x4d, 0x9e, 0xab, + 0xa1, 0xf4, 0xa8, 0xad, 0xb3, 0x8d, 0x48, 0xfe, 0x20, 0x6c, 0x08, 0xfc, 0x07, 0x96, 0x6c, 0xd0, + 0x3f, 0x74, 0xe0, 0x44, 0xe6, 0x7e, 0x59, 0xe9, 0x1d, 0xd6, 0x54, 0x16, 0x93, 0xf0, 0xec, 0xe3, + 0x6c, 0xf8, 0x4c, 0xe0, 0x9d, 0x4e, 0x10, 0xce, 0xb6, 0x88, 0x8f, 0x0b, 0x4b, 0xb8, 0x52, 0x7a, + 0xcc, 0xde, 0xb8, 0x30, 0x82, 0x72, 0x5c, 0xd8, 0x0f, 0x2c, 0xd9, 0xa0, 0x27, 0x61, 0x50, 0xe4, + 0x46, 0x2c, 0x3d, 0x6e, 0x7a, 0xdd, 0x45, 0x0a, 0x45, 0x2c, 0xcb, 0x3b, 0x92, 0xa8, 0x3c, 0x65, + 0x2b, 0x89, 0x8a, 0x3a, 0x19, 0x1e, 0x3c, 0x89, 0xca, 0xe4, 0x07, 0xe0, 0x64, 0xc7, 0x79, 0xf2, + 0x40, 0x59, 0x4c, 0xee, 0x31, 0x0b, 0x8a, 0xfb, 0x1b, 0x0e, 0xe8, 0xb7, 0xec, 0xad, 0xbf, 0x67, + 0xf3, 0x2c, 0x8c, 0x56, 0xf8, 0xf3, 0xa2, 0xfc, 0x9e, 0x7e, 0xbf, 0x69, 0xc4, 0x9e, 0xd3, 0xca, + 0xb0, 0x81, 0xe9, 0x5e, 0x01, 0xd4, 0xf9, 0xd8, 0xc0, 0xa1, 0xbc, 0x41, 0xff, 0xd8, 0x81, 0x31, + 0x43, 0x11, 0xb2, 0xee, 0x2c, 0x5e, 0x00, 0xd4, 0xf4, 0xa3, 0x28, 0x8c, 0xf4, 0x77, 0x1c, 0x45, + 0x2e, 0x0d, 0x16, 0x44, 0xb2, 0xda, 0x51, 0x8a, 0x73, 0x6a, 0xb8, 0xff, 0xb4, 0x1f, 0xd2, 0xe8, + 0x79, 0x95, 0x8a, 0xd9, 0xe9, 0x9a, 0x8a, 0xf9, 0x29, 0x18, 0x7a, 0x39, 0x0e, 0x83, 0xf5, 0x34, + 0x61, 0xb3, 0xfa, 0x16, 0xcf, 0x95, 0xd7, 0xae, 0x32, 0x4c, 0x85, 0xc1, 0xb0, 0x5f, 0x59, 0xf0, + 0x1b, 0x49, 0x67, 0x46, 0xdf, 0xe7, 0x9e, 0xe7, 0x70, 0xac, 0x30, 0xd8, 0x93, 0x8e, 0x3b, 0x44, + 0x79, 0x37, 0xd2, 0x27, 0x1d, 0xf9, 0x3b, 0x22, 0xac, 0x0c, 0x5d, 0x84, 0x61, 0xe5, 0x19, 0x11, + 0xee, 0x16, 0x35, 0x52, 0xca, 0x7d, 0x82, 0x53, 0x1c, 0xa6, 0xe5, 0x0a, 0x6b, 0xba, 0xb0, 0x0b, + 0x95, 0x6d, 0x9c, 0xb9, 0x32, 0xf6, 0x79, 0xbe, 0x61, 0x49, 0x30, 0x56, 0x2c, 0xf3, 0x1c, 0xe6, + 0xc3, 0x47, 0xe2, 0x30, 0xd7, 0xae, 0x72, 0x14, 0x7b, 0xbd, 0xca, 0x61, 0xce, 0xed, 0xa1, 0x9e, + 0xe6, 0xf6, 0xa7, 0xfb, 0x60, 0xf0, 0x3a, 0x89, 0x58, 0x2e, 0xfc, 0x27, 0x61, 0x70, 0x87, 0xff, + 0x9b, 0xbd, 0x07, 0x2c, 0x30, 0xb0, 0x2c, 0xa7, 0xdf, 0x6d, 0xb3, 0xed, 0x37, 0xaa, 0xf3, 0xe9, + 0x2a, 0x4e, 0x73, 0x55, 0xca, 0x02, 0x9c, 0xe2, 0xd0, 0x0a, 0x35, 0x7a, 0x5c, 0x69, 0x36, 0xfd, + 0x24, 0x1b, 0xff, 0xb6, 0x28, 0x0b, 0x70, 0x8a, 0x83, 0x1e, 0x87, 0x81, 0x9a, 0x9f, 0x6c, 0x78, + 0xb5, 0xac, 0xbb, 0x77, 0x91, 0x41, 0xb1, 0x28, 0x65, 0xbe, 0x3e, 0x3f, 0xd9, 0x88, 0x08, 0x33, + 0x57, 0x77, 0xa4, 0x21, 0x59, 0xd4, 0xca, 0xb0, 0x81, 0xc9, 0x9a, 0x14, 0x8a, 0x9e, 0x89, 0xe0, + 0xdf, 0xb4, 0x49, 0xb2, 0x00, 0xa7, 0x38, 0x74, 0xfe, 0x57, 0xc2, 0x66, 0xcb, 0x6f, 0x88, 0xb0, + 0x74, 0x6d, 0xfe, 0xcf, 0x09, 0x38, 0x56, 0x18, 0x14, 0x9b, 0x8a, 0x30, 0x2a, 0x7e, 0xb2, 0xcf, + 0xe7, 0xad, 0x0b, 0x38, 0x56, 0x18, 0xee, 0x75, 0x18, 0xe3, 0x2b, 0x79, 0xae, 0xe1, 0xf9, 0xcd, + 0xc5, 0x39, 0x74, 0xb9, 0xe3, 0x2a, 0xc7, 0x93, 0x39, 0x57, 0x39, 0xce, 0x18, 0x95, 0x3a, 0xaf, + 0x74, 0xb8, 0x3f, 0x2c, 0xc0, 0xd0, 0x31, 0xbe, 0x40, 0x7a, 0xec, 0x8f, 0x69, 0xa3, 0x5b, 0x99, + 0xd7, 0x47, 0xd7, 0x6d, 0xde, 0xcc, 0xda, 0xf7, 0xe5, 0xd1, 0xff, 0x5c, 0x80, 0xb3, 0x12, 0x55, + 0x1e, 0x50, 0x17, 0xe7, 0xd8, 0xab, 0x6e, 0x47, 0x3f, 0xd0, 0x91, 0x31, 0xd0, 0xeb, 0xf6, 0x8e, + 0xd8, 0x8b, 0x73, 0x5d, 0x87, 0xfa, 0xd5, 0xcc, 0x50, 0x63, 0xab, 0x5c, 0xf7, 0x1f, 0xec, 0xbf, + 0x70, 0x60, 0x32, 0x7f, 0xb0, 0x8f, 0xe1, 0xc1, 0xd7, 0x37, 0xcc, 0x07, 0x5f, 0x7f, 0xd1, 0xde, + 0x14, 0x33, 0xbb, 0xd2, 0xe5, 0xe9, 0xd7, 0xff, 0xee, 0xc0, 0x69, 0x59, 0x81, 0xed, 0x9e, 0xb3, + 0x7e, 0xc0, 0x22, 0x92, 0x8e, 0x7e, 0x9a, 0xbd, 0x6e, 0x4c, 0xb3, 0x17, 0xed, 0x75, 0x5c, 0xef, + 0x47, 0xd7, 0x87, 0xf2, 0xff, 0xdc, 0x81, 0x52, 0x5e, 0x85, 0x63, 0xf8, 0xe4, 0xaf, 0x99, 0x9f, + 0xfc, 0xfa, 0xd1, 0xf4, 0xbc, 0xfb, 0x07, 0x2f, 0x75, 0x1b, 0x28, 0xd4, 0x90, 0x7a, 0x95, 0x63, + 0xcb, 0xd1, 0xce, 0x59, 0xe4, 0x2b, 0x68, 0x0d, 0x18, 0x88, 0x59, 0xe8, 0x8d, 0x98, 0x02, 0x57, + 0x6c, 0x68, 0x5b, 0x94, 0x9e, 0x70, 0x1c, 0xb0, 0xff, 0xb1, 0xe0, 0xe1, 0xfe, 0x66, 0x01, 0xce, + 0xa9, 0x87, 0x9c, 0xc9, 0x0e, 0x69, 0xa4, 0xeb, 0x83, 0x3d, 0xfb, 0xe1, 0xa9, 0x9f, 0xf6, 0x9e, + 0xfd, 0x48, 0x59, 0xa4, 0x6b, 0x21, 0x85, 0x61, 0x8d, 0x27, 0x2a, 0xc3, 0x19, 0xf6, 0x4c, 0xc7, + 0x82, 0x1f, 0x78, 0x0d, 0xff, 0x55, 0x12, 0x61, 0xd2, 0x0c, 0x77, 0xbc, 0x86, 0xd0, 0xd4, 0xd5, + 0x55, 0xf0, 0x85, 0x3c, 0x24, 0x9c, 0x5f, 0xb7, 0xc3, 0x8c, 0xd0, 0xd7, 0xab, 0x19, 0xc1, 0xfd, + 0x13, 0x07, 0x46, 0x8f, 0xf1, 0xd9, 0xeb, 0xd0, 0x5c, 0x12, 0xcf, 0xd9, 0x5b, 0x12, 0x5d, 0x96, + 0xc1, 0x5e, 0x11, 0x3a, 0x5e, 0x02, 0x46, 0x9f, 0x71, 0x54, 0x70, 0x12, 0x0f, 0x02, 0xfd, 0xb0, + 0xbd, 0x76, 0x1c, 0x24, 0x7f, 0x28, 0xfa, 0x46, 0xc6, 0x1e, 0x50, 0xb0, 0x95, 0x19, 0xac, 0xa3, + 0x35, 0x87, 0x48, 0xae, 0xfa, 0x55, 0x07, 0x80, 0xb7, 0x53, 0xe4, 0x64, 0xa7, 0x6d, 0xdb, 0x3c, + 0xb2, 0x91, 0xa2, 0x4c, 0x78, 0xd3, 0xd4, 0x12, 0x4a, 0x0b, 0xb0, 0xd6, 0x92, 0x7b, 0xc8, 0x9a, + 0x7a, 0xcf, 0x09, 0x5b, 0xbf, 0xe8, 0xc0, 0x89, 0x4c, 0x73, 0x73, 0xea, 0x6f, 0x99, 0x0f, 0x57, + 0x5a, 0xd0, 0xac, 0xcc, 0x4c, 0xdd, 0xba, 0xf1, 0xe4, 0xbf, 0xba, 0x60, 0x3c, 0xa1, 0x8e, 0x5e, + 0x83, 0x61, 0x69, 0xf9, 0x90, 0xd3, 0xdb, 0xe6, 0x03, 0xbe, 0xea, 0x78, 0x23, 0x21, 0x31, 0x4e, + 0xf9, 0x65, 0x62, 0x1f, 0x0b, 0x3d, 0xc5, 0x3e, 0xde, 0xdf, 0xe7, 0x7f, 0xf3, 0x8d, 0xed, 0xfd, + 0x47, 0x62, 0x6c, 0x7f, 0xc8, 0xba, 0xb1, 0xfd, 0xe1, 0x63, 0x36, 0xb6, 0x6b, 0x9e, 0xcf, 0xe2, + 0x3d, 0x78, 0x3e, 0x5f, 0x83, 0xd3, 0x3b, 0xe9, 0xa1, 0x53, 0xcd, 0x24, 0x91, 0x8f, 0xea, 0xc9, + 0x5c, 0x13, 0x3b, 0x3d, 0x40, 0xc7, 0x09, 0x09, 0x12, 0xed, 0xb8, 0x9a, 0x86, 0x5d, 0x5e, 0xcf, + 0x21, 0x87, 0x73, 0x99, 0x64, 0x5d, 0x58, 0x83, 0x3d, 0xb8, 0xb0, 0xde, 0x72, 0xe0, 0x8c, 0xd7, + 0x71, 0x77, 0x10, 0x93, 0x2d, 0x11, 0x47, 0x73, 0xc3, 0x9e, 0x0a, 0x61, 0x90, 0x17, 0xbe, 0xc2, + 0xbc, 0x22, 0x9c, 0xdf, 0x20, 0xf4, 0x58, 0x1a, 0x4f, 0xc0, 0x83, 0x75, 0xf3, 0x9d, 0xff, 0xdf, + 0xc8, 0x06, 0x29, 0x01, 0x1b, 0xfa, 0x8f, 0xda, 0x3d, 0x6d, 0x5b, 0x08, 0x54, 0x1a, 0xb9, 0x87, + 0x40, 0xa5, 0x8c, 0x3f, 0x71, 0xd4, 0x92, 0x3f, 0x31, 0x80, 0x09, 0xbf, 0xe9, 0xd5, 0xc8, 0x7a, + 0xbb, 0xd1, 0xe0, 0x97, 0x81, 0xe4, 0x13, 0xcb, 0xb9, 0x16, 0xbc, 0x95, 0xb0, 0xe2, 0x35, 0xb2, + 0x8f, 0xeb, 0xab, 0x4b, 0x4f, 0x4b, 0x19, 0x4a, 0xb8, 0x83, 0x36, 0x9d, 0xb0, 0x2c, 0x31, 0x22, + 0x49, 0xe8, 0x68, 0xb3, 0x68, 0x98, 0x21, 0x3e, 0x61, 0xaf, 0xa4, 0x60, 0xac, 0xe3, 0xa0, 0x65, + 0x18, 0xae, 0x06, 0xb1, 0xb8, 0x06, 0x7d, 0x82, 0x09, 0xb3, 0x77, 0x51, 0x11, 0x38, 0x7f, 0xb5, + 0xac, 0x2e, 0x40, 0x3f, 0x94, 0x93, 0xe9, 0x53, 0x95, 0xe3, 0xb4, 0x3e, 0x5a, 0x65, 0xc4, 0xc4, + 0xe3, 0x71, 0x3c, 0x48, 0xe5, 0x91, 0x2e, 0x5e, 0xb0, 0xf9, 0xab, 0xf2, 0xf9, 0xbb, 0x31, 0xc1, + 0x4e, 0xbc, 0x02, 0x97, 0x52, 0xd0, 0x9e, 0xba, 0x3e, 0xb9, 0xef, 0x53, 0xd7, 0x2c, 0xc5, 0x6f, + 0xd2, 0x50, 0x3e, 0xef, 0x0b, 0xd6, 0x52, 0xfc, 0xa6, 0xe1, 0x9f, 0x22, 0xc5, 0x6f, 0x0a, 0xc0, + 0x3a, 0x4b, 0xb4, 0xd6, 0xcd, 0xf7, 0x7f, 0x8a, 0x09, 0x8d, 0x83, 0x7b, 0xf2, 0x75, 0x27, 0xf0, + 0xe9, 0x7d, 0x9d, 0xc0, 0x1d, 0x4e, 0xeb, 0x33, 0x07, 0x70, 0x5a, 0xd7, 0x59, 0xf2, 0xd5, 0xc5, + 0x39, 0x11, 0x27, 0x60, 0xe1, 0x7c, 0xc7, 0xd2, 0xbd, 0xf0, 0x70, 0x5a, 0xf6, 0x2f, 0xe6, 0x0c, + 0xba, 0x46, 0xc5, 0x9f, 0x3b, 0x74, 0x54, 0x3c, 0x15, 0xcf, 0x29, 0x9c, 0x65, 0xf1, 0x2d, 0x0a, + 0xf1, 0x9c, 0x82, 0xb1, 0x8e, 0x93, 0x75, 0x01, 0x3f, 0x78, 0x64, 0x2e, 0xe0, 0xc9, 0x63, 0x70, + 0x01, 0x9f, 0xef, 0xd9, 0x05, 0x7c, 0x0b, 0x4e, 0xb5, 0xc2, 0xea, 0xbc, 0x1f, 0x47, 0x6d, 0x76, + 0x3b, 0x72, 0xb6, 0x5d, 0xad, 0x91, 0x84, 0xf9, 0x90, 0x47, 0x2e, 0xbd, 0x4b, 0x6f, 0x64, 0x8b, + 0x2d, 0x64, 0xb9, 0x46, 0x33, 0x15, 0x98, 0xe9, 0x84, 0x85, 0x12, 0xe7, 0x14, 0xe2, 0x3c, 0x16, + 0xba, 0xf3, 0xf9, 0x91, 0xe3, 0x71, 0x3e, 0x7f, 0x10, 0x86, 0xe2, 0x7a, 0x3b, 0xa9, 0x86, 0x37, + 0x03, 0x16, 0x61, 0x30, 0x3c, 0xfb, 0x0e, 0x65, 0xca, 0x16, 0xf0, 0x3b, 0x7b, 0x53, 0x13, 0xf2, + 0x7f, 0xcd, 0x8a, 0x2d, 0x20, 0xe8, 0x9b, 0x5d, 0x2e, 0x61, 0xb9, 0x47, 0x79, 0x09, 0xeb, 0xdc, + 0x81, 0x2e, 0x60, 0xe5, 0x79, 0xd8, 0x1f, 0xfd, 0x99, 0xf3, 0xb0, 0x7f, 0xdd, 0x81, 0xb1, 0x1d, + 0xdd, 0x65, 0x20, 0xa2, 0x00, 0x2c, 0x44, 0x23, 0x19, 0x9e, 0x88, 0x59, 0x97, 0xca, 0x39, 0x03, + 0x74, 0x27, 0x0b, 0xc0, 0x66, 0x4b, 0x72, 0x22, 0xa5, 0x1e, 0xbb, 0x5f, 0x91, 0x52, 0x6f, 0x30, + 0x39, 0x26, 0x0f, 0xb9, 0x2c, 0x34, 0xc0, 0x6e, 0xa0, 0xb4, 0x94, 0x89, 0x2a, 0x4e, 0x5a, 0xe7, + 0x87, 0xbe, 0xe0, 0xc0, 0x84, 0x3c, 0x97, 0x09, 0x97, 0x5f, 0x2c, 0x42, 0x3d, 0x6d, 0x1e, 0x07, + 0xd9, 0x5d, 0x81, 0x8d, 0x0c, 0x1f, 0xdc, 0xc1, 0x99, 0x4a, 0x75, 0x15, 0x59, 0x57, 0x8b, 0x59, + 0x44, 0xb3, 0xd0, 0x61, 0x66, 0x52, 0x30, 0xd6, 0x71, 0xd0, 0xb7, 0x1c, 0x28, 0xd6, 0xc3, 0x70, + 0x3b, 0x2e, 0x3d, 0xc9, 0x04, 0xfa, 0x0b, 0x96, 0x75, 0xd3, 0x2b, 0x94, 0x36, 0x57, 0x4a, 0x9f, + 0x96, 0xb6, 0x23, 0x06, 0xbb, 0xb3, 0x37, 0x35, 0x6e, 0xbc, 0x51, 0x15, 0xbf, 0xf9, 0xb6, 0x06, + 0x11, 0xb6, 0x4d, 0xd6, 0x34, 0xf4, 0x65, 0x07, 0x26, 0x6e, 0x66, 0x0c, 0x1a, 0x22, 0xd6, 0x15, + 0xdb, 0x37, 0x95, 0xf0, 0xe1, 0xce, 0x42, 0x71, 0x47, 0x0b, 0xd0, 0xe7, 0x4d, 0x43, 0x27, 0x0f, + 0x8a, 0xb5, 0x38, 0x80, 0x19, 0xc3, 0x2a, 0xbf, 0xeb, 0x94, 0x6f, 0xf1, 0xbc, 0xf7, 0xf8, 0x12, + 0xda, 0x99, 0xf4, 0x63, 0xe5, 0x54, 0x25, 0xa6, 0xbd, 0xc5, 0xc2, 0x62, 0x37, 0x3e, 0xbf, 0x6e, + 0x6e, 0xf9, 0xf2, 0x59, 0x18, 0x37, 0x7d, 0x7b, 0xe8, 0xdd, 0xe6, 0xfb, 0x23, 0x17, 0xb2, 0x4f, + 0x39, 0x8c, 0x49, 0x7c, 0xe3, 0x39, 0x07, 0xe3, 0xbd, 0x85, 0xc2, 0x91, 0xbe, 0xb7, 0xd0, 0x77, + 0x3c, 0xef, 0x2d, 0x4c, 0x1c, 0xc5, 0x7b, 0x0b, 0x27, 0x0f, 0xf4, 0xde, 0x82, 0xf6, 0xde, 0x45, + 0xff, 0x5d, 0xde, 0xbb, 0x98, 0x81, 0x13, 0xf2, 0x42, 0x13, 0x11, 0x29, 0xed, 0xb9, 0xdb, 0x5f, + 0x3d, 0x9d, 0x3e, 0x67, 0x16, 0xe3, 0x2c, 0x3e, 0x5d, 0x64, 0xc5, 0x80, 0xd5, 0x1c, 0xb0, 0x15, + 0xc7, 0x65, 0x4e, 0x2d, 0x76, 0x7c, 0x16, 0x22, 0x4a, 0x86, 0x70, 0x17, 0x19, 0xec, 0x8e, 0xfc, + 0x07, 0xf3, 0x16, 0xa0, 0x97, 0xa0, 0x14, 0x6e, 0x6d, 0x35, 0x42, 0xaf, 0x9a, 0x3e, 0x0a, 0x21, + 0xe3, 0x12, 0xf8, 0x05, 0x5c, 0x95, 0x43, 0x78, 0xad, 0x0b, 0x1e, 0xee, 0x4a, 0x01, 0xbd, 0x45, + 0x15, 0x93, 0x24, 0x8c, 0x48, 0x35, 0xb5, 0xd5, 0x0c, 0xb3, 0x3e, 0x13, 0xeb, 0x7d, 0x2e, 0x9b, + 0x7c, 0x78, 0xef, 0xd5, 0x47, 0xc9, 0x94, 0xe2, 0x6c, 0xb3, 0x50, 0x04, 0x67, 0x5b, 0x79, 0xa6, + 0xa2, 0x58, 0x5c, 0xc3, 0xda, 0xcf, 0x60, 0xa5, 0x1e, 0x08, 0xcf, 0x35, 0x36, 0xc5, 0xb8, 0x0b, + 0x65, 0xfd, 0xe1, 0x86, 0xa1, 0xe3, 0x79, 0xb8, 0xe1, 0xe3, 0x00, 0x15, 0x99, 0x42, 0x4e, 0x1a, + 0x1f, 0x96, 0xad, 0xdc, 0x0f, 0xe2, 0x34, 0xb5, 0xb7, 0x72, 0x15, 0x1b, 0xac, 0xb1, 0x44, 0xff, + 0x3b, 0xf7, 0x65, 0x13, 0x6e, 0x61, 0xa9, 0x59, 0x9f, 0x13, 0x3f, 0x73, 0xaf, 0x9b, 0xfc, 0x23, + 0x07, 0x26, 0xf9, 0xcc, 0xcb, 0x2a, 0xf7, 0x54, 0xb5, 0x10, 0x17, 0x96, 0x6c, 0x87, 0xae, 0xf0, + 0x54, 0x50, 0x06, 0x57, 0xe6, 0xe8, 0xde, 0xa7, 0x25, 0xe8, 0xab, 0x39, 0x47, 0x8a, 0x13, 0xb6, + 0x6c, 0x96, 0xf9, 0xef, 0x53, 0x9c, 0xba, 0xdd, 0xcb, 0x29, 0xe2, 0x9f, 0x74, 0x35, 0xa9, 0x22, + 0xd6, 0xbc, 0x5f, 0x3a, 0x22, 0x93, 0xaa, 0xfe, 0x88, 0xc6, 0x81, 0x0c, 0xab, 0x5f, 0x74, 0x60, + 0xc2, 0xcb, 0x84, 0x9a, 0x30, 0x3b, 0x90, 0x15, 0x9b, 0xd4, 0x4c, 0x94, 0xc6, 0xaf, 0x30, 0x25, + 0x2f, 0x1b, 0xd5, 0x82, 0x3b, 0x98, 0xa3, 0x1f, 0x3a, 0x70, 0x3e, 0xf1, 0xe2, 0x6d, 0x9e, 0xa2, + 0x3a, 0x4e, 0x2f, 0x20, 0x8b, 0xc6, 0x9d, 0x66, 0xab, 0xf1, 0x15, 0xeb, 0xab, 0x71, 0xa3, 0x3b, + 0x4f, 0xbe, 0x2e, 0x1f, 0x15, 0xeb, 0xf2, 0xfc, 0x3e, 0x98, 0x78, 0xbf, 0xa6, 0x4f, 0x7e, 0xc6, + 0xe1, 0x4f, 0x99, 0x75, 0x55, 0xf9, 0x36, 0x4d, 0x95, 0x6f, 0xc5, 0xe6, 0x63, 0x4a, 0xba, 0xee, + 0xf9, 0xab, 0x0e, 0x9c, 0xce, 0xdb, 0x91, 0x72, 0x9a, 0xf4, 0x51, 0xb3, 0x49, 0x16, 0x4f, 0x59, + 0x7a, 0x83, 0xac, 0xbc, 0xe5, 0x32, 0x79, 0x15, 0x1e, 0xb9, 0xdb, 0x57, 0xbc, 0x1b, 0xbd, 0x21, + 0x5d, 0x2d, 0xfe, 0xf3, 0x61, 0xcd, 0x0b, 0x99, 0x90, 0x96, 0xf5, 0x18, 0xee, 0x00, 0x06, 0xfc, + 0xa0, 0xe1, 0x07, 0x44, 0x5c, 0x42, 0xb5, 0x79, 0x86, 0x15, 0x6f, 0x31, 0x51, 0xea, 0x58, 0x70, + 0xb9, 0xcf, 0x4e, 0xc9, 0xec, 0xeb, 0x76, 0xfd, 0xc7, 0xff, 0xba, 0xdd, 0x4d, 0x18, 0xbe, 0xe9, + 0x27, 0x75, 0x16, 0x4c, 0x21, 0x7c, 0x7d, 0x16, 0x2e, 0x6f, 0x52, 0x72, 0x69, 0xdf, 0x6f, 0x48, + 0x06, 0x38, 0xe5, 0x85, 0x2e, 0x72, 0xc6, 0x2c, 0x72, 0x3b, 0x1b, 0x52, 0x7b, 0x43, 0x16, 0xe0, + 0x14, 0x87, 0x0e, 0xd6, 0x28, 0xfd, 0x25, 0x13, 0x5b, 0x89, 0x74, 0xcf, 0x36, 0xd2, 0x78, 0x0a, + 0x8a, 0xfc, 0x8a, 0xf4, 0x0d, 0x8d, 0x07, 0x36, 0x38, 0xaa, 0x8c, 0xdb, 0x43, 0x5d, 0x33, 0x6e, + 0xbf, 0xce, 0x14, 0xb6, 0xc4, 0x0f, 0xda, 0x64, 0x2d, 0x10, 0xf1, 0xde, 0x2b, 0x76, 0x2e, 0x74, + 0x73, 0x9a, 0xfc, 0x08, 0x9e, 0xfe, 0xc6, 0x1a, 0x3f, 0xcd, 0xe5, 0x32, 0xb2, 0xaf, 0xcb, 0x25, + 0x35, 0xb9, 0x8c, 0x5a, 0x37, 0xb9, 0x24, 0xa4, 0x65, 0xc5, 0xe4, 0xf2, 0x33, 0x65, 0x0e, 0xf8, + 0x0b, 0x07, 0x90, 0xd2, 0xbb, 0x94, 0x40, 0x3d, 0x86, 0xa0, 0xca, 0x4f, 0x38, 0x00, 0x81, 0x7a, + 0x03, 0xd5, 0xee, 0x2e, 0xc8, 0x69, 0xa6, 0x0d, 0x48, 0x61, 0x58, 0xe3, 0xe9, 0xfe, 0x99, 0x93, + 0xc6, 0x2e, 0xa7, 0x7d, 0x3f, 0x86, 0x20, 0xb2, 0x5d, 0x33, 0x88, 0x6c, 0xc3, 0xa2, 0xe9, 0x5e, + 0x75, 0xa3, 0x4b, 0x38, 0xd9, 0x4f, 0x0a, 0x70, 0x42, 0x47, 0x2e, 0x93, 0xe3, 0xf8, 0xd8, 0x37, + 0x8d, 0x08, 0xda, 0x6b, 0x76, 0xfb, 0x5b, 0x16, 0x1e, 0xa0, 0xbc, 0x68, 0xed, 0x8f, 0x67, 0xa2, + 0xb5, 0x6f, 0xd8, 0x67, 0xbd, 0x7f, 0xc8, 0xf6, 0x7f, 0x71, 0xe0, 0x54, 0xa6, 0xc6, 0x31, 0x4c, + 0xb0, 0x1d, 0x73, 0x82, 0x3d, 0x6f, 0xbd, 0xd7, 0x5d, 0x66, 0xd7, 0xb7, 0x0b, 0x1d, 0xbd, 0x65, + 0x87, 0xb8, 0x4f, 0x3b, 0x50, 0xa4, 0xda, 0xb2, 0x8c, 0xe7, 0xfa, 0xe8, 0x91, 0xcc, 0x00, 0xa6, + 0xd7, 0x0b, 0xe9, 0xac, 0xda, 0xc7, 0x60, 0x98, 0x73, 0x9f, 0xfc, 0x94, 0x03, 0x90, 0x22, 0xdd, + 0x2f, 0x15, 0xd8, 0xfd, 0x6e, 0x01, 0xce, 0xe4, 0x4e, 0x23, 0xf4, 0x59, 0x65, 0x91, 0x73, 0x6c, + 0x47, 0x2b, 0x1a, 0x8c, 0x74, 0xc3, 0xdc, 0x98, 0x61, 0x98, 0x13, 0xf6, 0xb8, 0xfb, 0x75, 0x80, + 0x11, 0x62, 0x5a, 0x1b, 0xac, 0x1f, 0x3b, 0x69, 0x00, 0xac, 0x4a, 0xd6, 0xf4, 0x97, 0xf0, 0x12, + 0x8f, 0xfb, 0x13, 0xed, 0x86, 0x83, 0xec, 0xe8, 0x31, 0xc8, 0x8a, 0x9b, 0xa6, 0xac, 0xc0, 0xf6, + 0xfd, 0xc8, 0x5d, 0x84, 0xc5, 0x2b, 0x90, 0xe7, 0x58, 0xee, 0x2d, 0xb3, 0xa5, 0x71, 0x1d, 0xb6, + 0xd0, 0xf3, 0x75, 0xd8, 0x31, 0x18, 0x79, 0xd1, 0x57, 0x59, 0x51, 0x67, 0xa7, 0xbf, 0xf7, 0xa3, + 0x0b, 0x0f, 0x7c, 0xff, 0x47, 0x17, 0x1e, 0xf8, 0xe1, 0x8f, 0x2e, 0x3c, 0xf0, 0x89, 0xdb, 0x17, + 0x9c, 0xef, 0xdd, 0xbe, 0xe0, 0x7c, 0xff, 0xf6, 0x05, 0xe7, 0x87, 0xb7, 0x2f, 0x38, 0xff, 0xfe, + 0xf6, 0x05, 0xe7, 0xef, 0xfc, 0xe9, 0x85, 0x07, 0x5e, 0x1c, 0x92, 0x1d, 0xfb, 0x7f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x5c, 0x5a, 0x7b, 0x47, 0xc9, 0xd9, 0x00, 0x00, } func (m *Amount) Marshal() (dAtA []byte, err error) { @@ -11546,6 +11548,32 @@ func (m *Template) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Annotations) > 0 { + keysForAnnotations := make([]string, 0, len(m.Annotations)) + for k := range m.Annotations { + keysForAnnotations = append(keysForAnnotations, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForAnnotations) + for iNdEx := len(keysForAnnotations) - 1; iNdEx >= 0; iNdEx-- { + v := m.Annotations[string(keysForAnnotations[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForAnnotations[iNdEx]) + copy(dAtA[i:], keysForAnnotations[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForAnnotations[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0xe2 + } + } if m.Plugin != nil { { size, err := m.Plugin.MarshalToSizedBuffer(dAtA[:i]) @@ -16693,6 +16721,14 @@ func (m *Template) Size() (n int) { l = m.Plugin.Size() n += 2 + l + sovGenerated(uint64(l)) } + if len(m.Annotations) > 0 { + for k, v := range m.Annotations { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) + n += mapEntrySize + 2 + sovGenerated(uint64(mapEntrySize)) + } + } return n } @@ -19198,6 +19234,16 @@ func (this *Template) String() string { mapStringForNodeSelector += fmt.Sprintf("%v: %v,", k, this.NodeSelector[k]) } mapStringForNodeSelector += "}" + keysForAnnotations := make([]string, 0, len(this.Annotations)) + for k := range this.Annotations { + keysForAnnotations = append(keysForAnnotations, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForAnnotations) + mapStringForAnnotations := "map[string]string{" + for _, k := range keysForAnnotations { + mapStringForAnnotations += fmt.Sprintf("%v: %v,", k, this.Annotations[k]) + } + mapStringForAnnotations += "}" s := strings.Join([]string{`&Template{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Inputs:` + strings.Replace(strings.Replace(this.Inputs.String(), "Inputs", "Inputs", 1), `&`, ``, 1) + `,`, @@ -19238,6 +19284,7 @@ func (this *Template) String() string { `FailFast:` + valueToStringGenerated(this.FailFast) + `,`, `HTTP:` + strings.Replace(this.HTTP.String(), "HTTP", "HTTP", 1) + `,`, `Plugin:` + strings.Replace(this.Plugin.String(), "Plugin", "Plugin", 1) + `,`, + `Annotations:` + mapStringForAnnotations + `,`, `}`, }, "") return s @@ -41039,6 +41086,133 @@ func (m *Template) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 44: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", 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.Annotations == nil { + m.Annotations = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Annotations[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/pkg/apis/workflow/v1alpha1/generated.proto b/pkg/apis/workflow/v1alpha1/generated.proto index 77270ec6ae8d..61cbfa869e20 100644 --- a/pkg/apis/workflow/v1alpha1/generated.proto +++ b/pkg/apis/workflow/v1alpha1/generated.proto @@ -1776,6 +1776,9 @@ message Template { // Timeout allows to set the total node execution timeout duration counting from the node's start time. // This duration also includes time in which the node spends in Pending state. This duration may not be applied to Step or DAG templates. optional string timeout = 38; + + // Annotations is a list of annotations to add to the template at runtime + map annotations = 44; } // TemplateRef is a reference of template resource. diff --git a/pkg/apis/workflow/v1alpha1/openapi_generated.go b/pkg/apis/workflow/v1alpha1/openapi_generated.go index 7a61234a8a9c..ea7d881d19b2 100644 --- a/pkg/apis/workflow/v1alpha1/openapi_generated.go +++ b/pkg/apis/workflow/v1alpha1/openapi_generated.go @@ -6653,6 +6653,22 @@ func schema_pkg_apis_workflow_v1alpha1_Template(ref common.ReferenceCallback) co Format: "", }, }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations is a list of annotations to add to the template at runtime", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, }, }, diff --git a/pkg/apis/workflow/v1alpha1/workflow_types.go b/pkg/apis/workflow/v1alpha1/workflow_types.go index 8dd014512b69..102b7f4ea16d 100644 --- a/pkg/apis/workflow/v1alpha1/workflow_types.go +++ b/pkg/apis/workflow/v1alpha1/workflow_types.go @@ -763,6 +763,9 @@ type Template struct { // Timeout allows to set the total node execution timeout duration counting from the node's start time. // This duration also includes time in which the node spends in Pending state. This duration may not be applied to Step or DAG templates. Timeout string `json:"timeout,omitempty" protobuf:"bytes,38,opt,name=timeout"` + + // Annotations is a list of annotations to add to the template at runtime + Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,44,opt,name=annotations"` } // SetType will set the template object based on template type. @@ -829,6 +832,29 @@ func (tmpl *Template) GetOutputs() *Outputs { return nil } +func (tmpl *Template) GetAnnotations() map[string]string { + if tmpl != nil { + return tmpl.Annotations + } + return map[string]string{} +} + +func (tmpl *Template) SetAnnotations(annotations map[string]string) { + tmpl.Annotations = annotations +} + +func (tmpl *Template) GetDisplayName() string { + displayName, ok := tmpl.GetAnnotations()[string(TemplateAnnotationDisplayName)] + if ok { + return displayName + } + return "" +} + +func (tmpl *Template) SetDisplayName() { + tmpl.Annotations[string(TemplateAnnotationDisplayName)] = tmpl.Name +} + type Artifacts []Artifact func (a Artifacts) GetArtifactByName(name string) *Artifact { @@ -3969,3 +3995,9 @@ type NodeFlag struct { // Retried tracks whether or not this node was retried by retryStrategy Retried bool `json:"retried,omitempty" protobuf:"varint,2,opt,name=retried"` } + +type TemplateAnnotation string + +const ( + TemplateAnnotationDisplayName TemplateAnnotation = "workflows.argoproj.io/display-name" +) diff --git a/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go index 7592ca05ced8..fb986432b78e 100644 --- a/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/workflow/v1alpha1/zz_generated.deepcopy.go @@ -3338,6 +3338,13 @@ func (in *Template) DeepCopyInto(out *Template) { *out = new(Memoize) (*in).DeepCopyInto(*out) } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } diff --git a/pkg/plugins/executor/swagger.yml b/pkg/plugins/executor/swagger.yml index b39d45469a2e..ccd22f7b6e6c 100644 --- a/pkg/plugins/executor/swagger.yml +++ b/pkg/plugins/executor/swagger.yml @@ -4332,6 +4332,11 @@ definitions: $ref: '#/definitions/IntOrString' affinity: $ref: '#/definitions/Affinity' + annotations: + additionalProperties: + type: string + description: Annotations is a list of annotations to add to the template at runtime + type: object archiveLocation: $ref: '#/definitions/ArtifactLocation' automountServiceAccountToken: diff --git a/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1Template.md b/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1Template.md index 96784bb73ddf..fa030b874cf3 100644 --- a/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1Template.md +++ b/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1Template.md @@ -10,6 +10,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **activeDeadlineSeconds** | **String** | | [optional] **affinity** | [**io.kubernetes.client.openapi.models.V1Affinity**](io.kubernetes.client.openapi.models.V1Affinity.md) | | [optional] +**annotations** | **Map<String, String>** | Annotations is a list of annotations to add to the template at runtime | [optional] **archiveLocation** | [**IoArgoprojWorkflowV1alpha1ArtifactLocation**](IoArgoprojWorkflowV1alpha1ArtifactLocation.md) | | [optional] **automountServiceAccountToken** | **Boolean** | AutomountServiceAccountToken indicates whether a service account token should be automatically mounted in pods. ServiceAccountName of ExecutorConfig must be specified if this value is false. | [optional] **container** | [**io.kubernetes.client.openapi.models.V1Container**](io.kubernetes.client.openapi.models.V1Container.md) | | [optional] diff --git a/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_template.py b/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_template.py index 13d05724dd34..8c6121b06af0 100644 --- a/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_template.py +++ b/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_template.py @@ -135,6 +135,7 @@ def openapi_types(): return { 'active_deadline_seconds': (str,), # noqa: E501 'affinity': (Affinity,), # noqa: E501 + 'annotations': ({str: (str,)},), # noqa: E501 'archive_location': (IoArgoprojWorkflowV1alpha1ArtifactLocation,), # noqa: E501 'automount_service_account_token': (bool,), # noqa: E501 'container': (Container,), # noqa: E501 @@ -182,6 +183,7 @@ def discriminator(): attribute_map = { 'active_deadline_seconds': 'activeDeadlineSeconds', # noqa: E501 'affinity': 'affinity', # noqa: E501 + 'annotations': 'annotations', # noqa: E501 'archive_location': 'archiveLocation', # noqa: E501 'automount_service_account_token': 'automountServiceAccountToken', # noqa: E501 'container': 'container', # noqa: E501 @@ -264,6 +266,7 @@ def _from_openapi_data(cls, *args, **kwargs): # noqa: E501 _visited_composed_classes = (Animal,) active_deadline_seconds (str): [optional] # noqa: E501 affinity (Affinity): [optional] # noqa: E501 + annotations ({str: (str,)}): Annotations is a list of annotations to add to the template at runtime. [optional] # noqa: E501 archive_location (IoArgoprojWorkflowV1alpha1ArtifactLocation): [optional] # noqa: E501 automount_service_account_token (bool): AutomountServiceAccountToken indicates whether a service account token should be automatically mounted in pods. ServiceAccountName of ExecutorConfig must be specified if this value is false.. [optional] # noqa: E501 container (Container): [optional] # noqa: E501 @@ -384,6 +387,7 @@ def __init__(self, *args, **kwargs): # noqa: E501 _visited_composed_classes = (Animal,) active_deadline_seconds (str): [optional] # noqa: E501 affinity (Affinity): [optional] # noqa: E501 + annotations ({str: (str,)}): Annotations is a list of annotations to add to the template at runtime. [optional] # noqa: E501 archive_location (IoArgoprojWorkflowV1alpha1ArtifactLocation): [optional] # noqa: E501 automount_service_account_token (bool): AutomountServiceAccountToken indicates whether a service account token should be automatically mounted in pods. ServiceAccountName of ExecutorConfig must be specified if this value is false.. [optional] # noqa: E501 container (Container): [optional] # noqa: E501 diff --git a/sdks/python/client/docs/ClusterWorkflowTemplateServiceApi.md b/sdks/python/client/docs/ClusterWorkflowTemplateServiceApi.md index 9629a9636698..79145b9f359c 100644 --- a/sdks/python/client/docs/ClusterWorkflowTemplateServiceApi.md +++ b/sdks/python/client/docs/ClusterWorkflowTemplateServiceApi.md @@ -1359,6 +1359,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -6181,6 +6184,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -12894,6 +12900,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -17716,6 +17725,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -24335,6 +24347,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -29157,6 +29172,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( diff --git a/sdks/python/client/docs/CronWorkflowServiceApi.md b/sdks/python/client/docs/CronWorkflowServiceApi.md index c0c6694b52b7..b66bcad81dfa 100644 --- a/sdks/python/client/docs/CronWorkflowServiceApi.md +++ b/sdks/python/client/docs/CronWorkflowServiceApi.md @@ -1419,6 +1419,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -6241,6 +6244,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -13036,6 +13042,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -17858,6 +17867,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -24740,6 +24752,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -29562,6 +29577,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( diff --git a/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1Template.md b/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1Template.md index edcf27bef471..3dd196d063a9 100644 --- a/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1Template.md +++ b/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1Template.md @@ -7,6 +7,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **active_deadline_seconds** | **str** | | [optional] **affinity** | [**Affinity**](Affinity.md) | | [optional] +**annotations** | **{str: (str,)}** | Annotations is a list of annotations to add to the template at runtime | [optional] **archive_location** | [**IoArgoprojWorkflowV1alpha1ArtifactLocation**](IoArgoprojWorkflowV1alpha1ArtifactLocation.md) | | [optional] **automount_service_account_token** | **bool** | AutomountServiceAccountToken indicates whether a service account token should be automatically mounted in pods. ServiceAccountName of ExecutorConfig must be specified if this value is false. | [optional] **container** | [**Container**](Container.md) | | [optional] diff --git a/sdks/python/client/docs/WorkflowServiceApi.md b/sdks/python/client/docs/WorkflowServiceApi.md index a344eeaf39c7..34ab357d1dcd 100644 --- a/sdks/python/client/docs/WorkflowServiceApi.md +++ b/sdks/python/client/docs/WorkflowServiceApi.md @@ -1374,6 +1374,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -6196,6 +6199,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -13016,6 +13022,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -18861,6 +18870,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -23683,6 +23695,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -30438,6 +30453,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -35260,6 +35278,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -42080,6 +42101,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -47925,6 +47949,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -52747,6 +52774,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( diff --git a/sdks/python/client/docs/WorkflowTemplateServiceApi.md b/sdks/python/client/docs/WorkflowTemplateServiceApi.md index 3d4ca135dbd7..5d734a20d3de 100644 --- a/sdks/python/client/docs/WorkflowTemplateServiceApi.md +++ b/sdks/python/client/docs/WorkflowTemplateServiceApi.md @@ -1361,6 +1361,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -6183,6 +6186,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -12903,6 +12909,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -17725,6 +17734,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -24358,6 +24370,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( @@ -29180,6 +29195,9 @@ with argo_workflows.ApiClient(configuration) as api_client: ], ), ), + annotations={ + "key": "key_example", + }, archive_location=IoArgoprojWorkflowV1alpha1ArtifactLocation( archive_logs=True, artifactory=IoArgoprojWorkflowV1alpha1ArtifactoryArtifact( diff --git a/workflow/controller/operator.go b/workflow/controller/operator.go index 46e8c4388e5e..1f1f2265818e 100644 --- a/workflow/controller/operator.go +++ b/workflow/controller/operator.go @@ -1961,6 +1961,17 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } + // Update displayName from processedTmpl + if displayName := processedTmpl.GetDisplayName(); node != nil && displayName != "" { + if !displayNameRegex.MatchString(displayName) { + err = fmt.Errorf("displayName must match the regex %s", displayNameRegex.String()) + return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + } + + woc.log.Debugf("Updating node %s display name to %s", node.DisplayName, displayName) + woc.setNodeDisplayName(node, displayName) + } + // Check if this is a fulfilled node for synchronization. // If so, release synchronization and return this node. No more logic will be executed. if node != nil { @@ -2507,6 +2518,7 @@ func (woc *wfOperationCtx) markWorkflowError(ctx context.Context, err error) { // stepsOrDagSeparator identifies if a node name starts with our naming convention separator from // DAG or steps templates. Will match stings with prefix like: [0]. or . var stepsOrDagSeparator = regexp.MustCompile(`^(\[\d+\])?\.`) +var displayNameRegex = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-.]{0,61}[a-zA-Z0-9]$`) // initializeExecutableNode initializes a node and stores the template. func (woc *wfOperationCtx) initializeExecutableNode(nodeName string, nodeType wfv1.NodeType, templateScope string, executeTmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, phase wfv1.NodePhase, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { @@ -3226,8 +3238,8 @@ func parseLoopIndex(s string) int { } func (n loopNodes) Less(i, j int) bool { - left := parseLoopIndex(n[i].DisplayName) - right := parseLoopIndex(n[j].DisplayName) + left := parseLoopIndex(n[i].Name) + right := parseLoopIndex(n[j].Name) return left < right } @@ -4215,3 +4227,10 @@ func getChildNodeIdsRetried(node *wfv1.NodeStatus, nodes wfv1.Nodes) []string { } return childrenIds } + +func (woc *wfOperationCtx) setNodeDisplayName(node *wfv1.NodeStatus, displayName string) { + nodeID := node.ID + newNode := node.DeepCopy() + newNode.DisplayName = displayName + woc.wf.Status.Nodes.Set(nodeID, *newNode) +} From 20105fc745bae917106ba00270d725a6e1755ea2 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 27 Jan 2025 10:33:38 -0800 Subject: [PATCH 11/11] update tasks.yaml Signed-off-by: Alex Collins --- tasks.yaml | 115 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 24 deletions(-) diff --git a/tasks.yaml b/tasks.yaml index 294085fc884b..6c59bca5add2 100644 --- a/tasks.yaml +++ b/tasks.yaml @@ -1,17 +1,56 @@ tasks: build: - dependencies: build-controller build-cli build-executor + dependencies: + - build-controller + - build-cli + - build-executor build-cli: - command: make ./dist/argo + command: + - make + - ./dist/argo env: STATIC_FILES: "false" mutex: build + watch: + - cmd/argo + - config + - errors + - persist + - pkg + - util + - server + - workflow build-controller: - command: make ./dist/workflow-controller + command: + - make + - ./dist/workflow-controller mutex: build + watch: + - cmd/workflow-controller + - config + - errors + - persist + - pkg + - util + - workflow + build-executor: + command: + - make + - argoexec-image + mutex: docker + watch: + - cmd/argoexec + - config + - errors + - pkg + - util + - workflow controller: - command: ./dist/workflow-controller - dependencies: build-controller port-forward + command: + - ./dist/workflow-controller + dependencies: + - build-controller + - port-forward env: ALWAYS_OFFLOAD_NODE_STATUS: "false" ARCHIVED_WORKFLOW_GC_PERIOD: 30s @@ -27,26 +66,38 @@ tasks: OFFLOAD_NODE_STATUS_TTL: 30s UPPERIO_DB_DEBUG: "1" WORKFLOW_GC_PERIOD: 30s - ports: "9090" + ports: + - "9090" example: - command: kubectl create -f examples/hello-world.yaml - dependencies: install - mutex: docker - build-executor: - command: make argoexec-image + command: + - kubectl + - create + - -f + - examples/hello-world.yaml + dependencies: + - install mutex: docker install: - sh: make install PROFILE=$PROFILE env: PROFILE: minimal mutex: docker + sh: make install PROFILE=$PROFILE + watch: + - manifests port-forward: - command: ./hack/port-forward.sh - dependencies: install - ports: "9000" + command: + - ./hack/port-forward.sh + dependencies: + - install + ports: + - "9000" server: - command: ./dist/argo server - dependencies: build-cli port-forward + command: + - ./dist/argo + - server + dependencies: + - build-cli + - port-forward env: ARGO_AUTH_MODE: hybrid ARGO_LOGLEVEL: info @@ -56,15 +107,31 @@ tasks: ARGO_SECURE: "false" ARGO_X_FRAME_OPTIONS: SAMEORIGIN UPPERIO_DB_DEBUG: "1" - ports: "2746" + ports: + - "2746" ui: - command: yarn start - dependencies: ui-deps - ports: "8080" + command: + - yarn + - start + dependencies: + - ui-deps + ports: + - "8080" + watch: + - package.json workingDir: ui ui-deps: - command: yarn install + command: + - yarn + - install workingDir: ui up: - command: sleep 999999 - dependencies: example controller server ui build-executor + command: + - sleep + - "999999" + dependencies: + - example + - controller + - server + - ui + - build-executor