From a67bdf98e7c7cedc0c2f9976b9d1542df213cf68 Mon Sep 17 00:00:00 2001 From: Isitha Subasinghe Date: Wed, 31 Jan 2024 02:05:48 +1100 Subject: [PATCH] fix: upgrade expr-lang. Fixes #12037 (#12573) Signed-off-by: isubasinghe Signed-off-by: Isitha Subasinghe --- api/jsonschema/schema.json | 4 +- api/openapi-spec/swagger.json | 4 +- docs/argo-server-sso.md | 2 +- docs/data-sourcing-and-transformation.md | 2 +- docs/events.md | 2 +- docs/executor_swagger.md | 2 +- docs/fields.md | 2 +- docs/retries.md | 2 +- docs/variables.md | 2 +- go.mod | 8 +-- go.sum | 16 ++--- pkg/apis/workflow/v1alpha1/event_types.go | 2 +- pkg/apis/workflow/v1alpha1/generated.proto | 4 +- .../workflow/v1alpha1/openapi_generated.go | 4 +- pkg/apis/workflow/v1alpha1/workflow_types.go | 2 +- pkg/plugins/executor/swagger.yml | 2 +- .../docs/IoArgoprojWorkflowV1alpha1Event.md | 2 +- .../IoArgoprojWorkflowV1alpha1ValueFrom.md | 2 +- .../io_argoproj_workflow_v1alpha1_event.py | 4 +- ...o_argoproj_workflow_v1alpha1_value_from.py | 4 +- .../docs/IoArgoprojWorkflowV1alpha1Event.md | 2 +- .../IoArgoprojWorkflowV1alpha1ValueFrom.md | 2 +- server/event/dispatch/operation.go | 15 +++- server/event/dispatch/operation_test.go | 6 +- test/e2e/argo_server_test.go | 2 +- test/e2e/expr_lang.go | 70 +++++++++++++++++++ util/expr/argoexpr/eval.go | 8 ++- util/template/expression_template.go | 17 +++-- util/template/replace_test.go | 2 +- util/template/resolve_var.go | 8 ++- util/template/resolve_var_test.go | 4 +- workflow/controller/operator.go | 8 ++- workflow/controller/scope.go | 18 ++++- workflow/data/data.go | 9 ++- 34 files changed, 179 insertions(+), 64 deletions(-) create mode 100644 test/e2e/expr_lang.go diff --git a/api/jsonschema/schema.json b/api/jsonschema/schema.json index f6ee970bdded..78b12ef6bd8e 100644 --- a/api/jsonschema/schema.json +++ b/api/jsonschema/schema.json @@ -4715,7 +4715,7 @@ "io.argoproj.workflow.v1alpha1.Event": { "properties": { "selector": { - "description": "Selector (https://github.com/antonmedv/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"`", + "description": "Selector (https://github.com/expr-lang/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"`", "type": "string" } }, @@ -6876,7 +6876,7 @@ "type": "string" }, "event": { - "description": "Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`", + "description": "Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`", "type": "string" }, "expression": { diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 662be1068370..8af50541c412 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -8649,7 +8649,7 @@ ], "properties": { "selector": { - "description": "Selector (https://github.com/antonmedv/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"`", + "description": "Selector (https://github.com/expr-lang/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"`", "type": "string" } } @@ -10807,7 +10807,7 @@ "type": "string" }, "event": { - "description": "Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`", + "description": "Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`", "type": "string" }, "expression": { diff --git a/docs/argo-server-sso.md b/docs/argo-server-sso.md index 8d47a1afbce6..c98ef55b50dd 100644 --- a/docs/argo-server-sso.md +++ b/docs/argo-server-sso.md @@ -83,7 +83,7 @@ metadata: # Must evaluate to a boolean. # If you want an account to be the default to use, this rule can be "true". # Details of the expression language are available in - # https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md. + # https://github.com/expr-lang/expr/blob/master/docs/language-definition.md. workflows.argoproj.io/rbac-rule: "'admin' in groups" # The precedence is used to determine which service account to use whe # Precedence is an integer. It may be negative. If omitted, it defaults to "0". diff --git a/docs/data-sourcing-and-transformation.md b/docs/data-sourcing-and-transformation.md index 8eacded5f6b6..0c5f78505835 100644 --- a/docs/data-sourcing-and-transformation.md +++ b/docs/data-sourcing-and-transformation.md @@ -53,6 +53,6 @@ A `data` template must always contain a `source`. Current available sources: A `data` template may contain any number of transformations (or zero). The transformations will be applied serially in order. Current available transformations: -* `expression`: an [`expr`](https://github.com/antonmedv/expr) expression. See language definition [here](https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md). When defining `expr` expressions Argo will pass the available data to the environment as a variable called `data` (see example above). +* `expression`: an [`expr`](https://github.com/expr-lang/expr) expression. See language definition [here](https://github.com/expr-lang/expr/blob/master/docs/Language-Definition.md). When defining `expr` expressions Argo will pass the available data to the environment as a variable called `data` (see example above). We understand that the `expression` transformation is limited. We intend to greatly expand the functionality of this template with our community's feedback. Please see the link at the top of this document to submit ideas or use cases for this feature. diff --git a/docs/events.md b/docs/events.md index edb0fd833d64..d490e022911b 100644 --- a/docs/events.md +++ b/docs/events.md @@ -156,7 +156,7 @@ requirements](https://kubernetes.io/docs/concepts/overview/working-with-objects/ Because the endpoint accepts any JSON data, it is the user's responsibility to write a suitable expression to correctly filter the events they are interested in. Therefore, DO NOT assume the existence of any fields, and guard against them using a nil check. -[Learn more about expression syntax](https://github.com/antonmedv/expr). +[Learn more about expression syntax](https://github.com/expr-lang/expr). ### Expression Environment diff --git a/docs/executor_swagger.md b/docs/executor_swagger.md index 61201056eb21..b9457cff9dc6 100644 --- a/docs/executor_swagger.md +++ b/docs/executor_swagger.md @@ -4820,7 +4820,7 @@ Cannot be updated. |------|------|---------|:--------:| ------- |-------------|---------| | configMapKeyRef | [ConfigMapKeySelector](#config-map-key-selector)| `ConfigMapKeySelector` | | | | | | default | [AnyString](#any-string)| `AnyString` | | | | | -| event | string| `string` | | | Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` | | +| event | string| `string` | | | Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` | | | expression | string| `string` | | | Expression, if defined, is evaluated to specify the value for the parameter | | | jqFilter | string| `string` | | | JQFilter expression against the resource object in resource templates | | | jsonPath | string| `string` | | | JSONPath of a resource to retrieve an output parameter value from in resource templates | | diff --git a/docs/fields.md b/docs/fields.md index 20baffac8b05..1dd2f4ea40ce 100644 --- a/docs/fields.md +++ b/docs/fields.md @@ -3496,7 +3496,7 @@ ValueFrom describes a location in which to obtain the value to a parameter |:----------:|:----------:|---------------| |`configMapKeyRef`|[`ConfigMapKeySelector`](#configmapkeyselector)|ConfigMapKeyRef is configmap selector for input parameter configuration| |`default`|`string`|Default specifies a value to be used if retrieving the value from the specified source fails| -|`event`|`string`|Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`| +|`event`|`string`|Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`| |`expression`|`string`|Expression, if defined, is evaluated to specify the value for the parameter| |`jqFilter`|`string`|JQFilter expression against the resource object in resource templates| |`jsonPath`|`string`|JSONPath of a resource to retrieve an output parameter value from in resource templates| diff --git a/docs/retries.md b/docs/retries.md index 9d6aa1ff7a8e..3c7ff3de7550 100644 --- a/docs/retries.md +++ b/docs/retries.md @@ -57,7 +57,7 @@ spec: > v3.2 and after You can also use `expression` to control retries. The `expression` field -accepts an [expr](https://github.com/antonmedv/expr) expression and has +accepts an [expr](https://github.com/expr-lang/expr) expression and has access to the following variables: - `lastRetry.exitCode`: The exit code of the last retry, or "-1" if not available diff --git a/docs/variables.md b/docs/variables.md index bf7a2f334848..cf0167c3f0f9 100644 --- a/docs/variables.md +++ b/docs/variables.md @@ -56,7 +56,7 @@ The tag is substituted with the result of evaluating the tag as an expression. Note that any hyphenated parameter names or step names will cause a parsing error. You can reference them by indexing into the parameter or step map, e.g. `inputs.parameters['my-param']` or `steps['my-step'].outputs.result`. -[Learn about the expression syntax](https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md). +[Learn about the expression syntax](https://github.com/expr-lang/expr/blob/master/docs/language-definition.md). #### Examples diff --git a/go.mod b/go.mod index 2ce2c5748f5e..b5cc85f11b3a 100644 --- a/go.mod +++ b/go.mod @@ -8,16 +8,16 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible github.com/Masterminds/sprig/v3 v3.2.3 - github.com/TwiN/go-color v1.4.0 - github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible - github.com/antonmedv/expr v1.12.6 + github.com/TwiN/go-color v1.4.1 + github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/argoproj/argo-events v1.7.3 github.com/argoproj/pkg v0.13.6 github.com/blushft/go-diagrams v0.0.0-20201006005127-c78c821223d9 github.com/colinmarc/hdfs/v2 v2.4.0 github.com/coreos/go-oidc/v3 v3.5.0 github.com/doublerebel/bellows v0.0.0-20160303004610-f177d92a03d3 - github.com/evanphx/json-patch v5.6.0+incompatible + github.com/evanphx/json-patch v5.8.0+incompatible + github.com/expr-lang/expr v1.16.0 github.com/gavv/httpexpect/v2 v2.10.0 github.com/go-git/go-git/v5 v5.11.0 github.com/go-jose/go-jose/v3 v3.0.1 diff --git a/go.sum b/go.sum index 4b3db752d93a..46eaaa694ae0 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,8 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/TwiN/go-color v1.4.0 h1:fNbOwOrvup5oj934UragnW0B1WKaAkkB85q19Y7h4ng= -github.com/TwiN/go-color v1.4.0/go.mod h1:0QTVEPlu+AoCyTrho7bXbVkrCkVpdQr7YF7PYWEtSxM= +github.com/TwiN/go-color v1.4.1 h1:mqG0P/KBgHKVqmtL5ye7K0/Gr4l6hTksPgTgMk3mUzc= +github.com/TwiN/go-color v1.4.1/go.mod h1:WcPf/jtiW95WBIsEeY1Lc/b8aaWoiqQpu5cf8WFxu+s= github.com/UnnoTed/fileb0x v1.1.4/go.mod h1:X59xXT18tdNk/D6j+KZySratBsuKJauMtVuJ9cgOiZs= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= @@ -131,16 +131,14 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible h1:KpbJFXwhVeuxNtBJ74MCGbIoaBok2uZvkD7QXp2+Wis= -github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antonmedv/expr v1.12.6 h1:qtgMHOFissxhePwokx0xB9eqS6PUy0SbhDRPD67PInA= -github.com/antonmedv/expr v1.12.6/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU= github.com/argoproj/argo-events v1.7.3 h1:XiGnKCzRRQCI7sFCKw3RoeFUOR6IupfAJI9uUK7pnG8= github.com/argoproj/argo-events v1.7.3/go.mod h1:YxDOXrveW52SDAeeTI93Wagkr4jt5DK0dA0juIdWDRw= github.com/argoproj/pkg v0.13.6 h1:36WPD9MNYECHcO1/R1pj6teYspiK7uMQLCgLGft2abM= @@ -290,10 +288,12 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.8.0+incompatible h1:1Av9pn2FyxPdvrWNQszj1g6D6YthSmvCfcN6SYclTJg= +github.com/evanphx/json-patch v5.8.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/expr-lang/expr v1.16.0 h1:BQabx+PbjsL2PEQwkJ4GIn3CcuUh8flduHhJ0lHjWwE= +github.com/expr-lang/expr v1.16.0/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ= github.com/fasthttp/websocket v1.4.3-rc.6 h1:omHqsl8j+KXpmzRjF8bmzOSYJ8GnS0E3efi1wYT+niY= github.com/fasthttp/websocket v1.4.3-rc.6/go.mod h1:43W9OM2T8FeXpCWMsBd9Cb7nE2CACNqNvCqQCoty/Lc= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= diff --git a/pkg/apis/workflow/v1alpha1/event_types.go b/pkg/apis/workflow/v1alpha1/event_types.go index bdfab46d3aa7..d6f49b0d8648 100644 --- a/pkg/apis/workflow/v1alpha1/event_types.go +++ b/pkg/apis/workflow/v1alpha1/event_types.go @@ -32,7 +32,7 @@ type WorkflowEventBindingSpec struct { } type Event struct { - // Selector (https://github.com/antonmedv/expr) that we must must match the event. E.g. `payload.message == "test"` + // Selector (https://github.com/expr-lang/expr) that we must must match the event. E.g. `payload.message == "test"` Selector string `json:"selector" protobuf:"bytes,1,opt,name=selector"` } diff --git a/pkg/apis/workflow/v1alpha1/generated.proto b/pkg/apis/workflow/v1alpha1/generated.proto index 9f8b59a78198..d2afedf68697 100644 --- a/pkg/apis/workflow/v1alpha1/generated.proto +++ b/pkg/apis/workflow/v1alpha1/generated.proto @@ -593,7 +593,7 @@ message DataSource { } message Event { - // Selector (https://github.com/antonmedv/expr) that we must must match the event. E.g. `payload.message == "test"` + // Selector (https://github.com/expr-lang/expr) that we must must match the event. E.g. `payload.message == "test"` optional string selector = 1; } @@ -1712,7 +1712,7 @@ message ValueFrom { // JQFilter expression against the resource object in resource templates optional string jqFilter = 3; - // Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` + // Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` optional string event = 7; // Parameter reference to a step or dag task in which to retrieve an output parameter value from diff --git a/pkg/apis/workflow/v1alpha1/openapi_generated.go b/pkg/apis/workflow/v1alpha1/openapi_generated.go index 569faad6f886..5410de865bcf 100644 --- a/pkg/apis/workflow/v1alpha1/openapi_generated.go +++ b/pkg/apis/workflow/v1alpha1/openapi_generated.go @@ -2534,7 +2534,7 @@ func schema_pkg_apis_workflow_v1alpha1_Event(ref common.ReferenceCallback) commo Properties: map[string]spec.Schema{ "selector": { SchemaProps: spec.SchemaProps{ - Description: "Selector (https://github.com/antonmedv/expr) that we must must match the event. E.g. `payload.message == \"test\"`", + Description: "Selector (https://github.com/expr-lang/expr) that we must must match the event. E.g. `payload.message == \"test\"`", Default: "", Type: []string{"string"}, Format: "", @@ -6631,7 +6631,7 @@ func schema_pkg_apis_workflow_v1alpha1_ValueFrom(ref common.ReferenceCallback) c }, "event": { SchemaProps: spec.SchemaProps{ - Description: "Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`", + Description: "Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`", Type: []string{"string"}, Format: "", }, diff --git a/pkg/apis/workflow/v1alpha1/workflow_types.go b/pkg/apis/workflow/v1alpha1/workflow_types.go index 9d50c5275488..33fcaf09f249 100644 --- a/pkg/apis/workflow/v1alpha1/workflow_types.go +++ b/pkg/apis/workflow/v1alpha1/workflow_types.go @@ -886,7 +886,7 @@ type ValueFrom struct { // JQFilter expression against the resource object in resource templates JQFilter string `json:"jqFilter,omitempty" protobuf:"bytes,3,opt,name=jqFilter"` - // Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` + // Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` Event string `json:"event,omitempty" protobuf:"bytes,7,opt,name=event"` // Parameter reference to a step or dag task in which to retrieve an output parameter value from diff --git a/pkg/plugins/executor/swagger.yml b/pkg/plugins/executor/swagger.yml index 56a198fb2471..e63e754e9e89 100644 --- a/pkg/plugins/executor/swagger.yml +++ b/pkg/plugins/executor/swagger.yml @@ -4394,7 +4394,7 @@ definitions: default: $ref: '#/definitions/AnyString' event: - description: Selector (https://github.com/antonmedv/expr) that is evaluated + description: Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` type: string expression: diff --git a/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1Event.md b/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1Event.md index a9564452ca03..3963165b5b4e 100644 --- a/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1Event.md +++ b/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1Event.md @@ -7,7 +7,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**selector** | **String** | Selector (https://github.com/antonmedv/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"` | +**selector** | **String** | Selector (https://github.com/expr-lang/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"` | diff --git a/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1ValueFrom.md b/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1ValueFrom.md index 9cc6cd901a91..046d505babd1 100644 --- a/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1ValueFrom.md +++ b/sdks/java/client/docs/IoArgoprojWorkflowV1alpha1ValueFrom.md @@ -10,7 +10,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **configMapKeyRef** | [**io.kubernetes.client.openapi.models.V1ConfigMapKeySelector**](io.kubernetes.client.openapi.models.V1ConfigMapKeySelector.md) | | [optional] **_default** | **String** | Default specifies a value to be used if retrieving the value from the specified source fails | [optional] -**event** | **String** | Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` | [optional] +**event** | **String** | Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` | [optional] **expression** | **String** | Expression, if defined, is evaluated to specify the value for the parameter | [optional] **jqFilter** | **String** | JQFilter expression against the resource object in resource templates | [optional] **jsonPath** | **String** | JSONPath of a resource to retrieve an output parameter value from in resource templates | [optional] diff --git a/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_event.py b/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_event.py index acb1dc122b24..8b9eb71f0df0 100644 --- a/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_event.py +++ b/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_event.py @@ -104,7 +104,7 @@ def _from_openapi_data(cls, selector, *args, **kwargs): # noqa: E501 """IoArgoprojWorkflowV1alpha1Event - a model defined in OpenAPI Args: - selector (str): Selector (https://github.com/antonmedv/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"` + selector (str): Selector (https://github.com/expr-lang/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"` Keyword Args: _check_type (bool): if True, values for parameters in openapi_types @@ -189,7 +189,7 @@ def __init__(self, selector, *args, **kwargs): # noqa: E501 """IoArgoprojWorkflowV1alpha1Event - a model defined in OpenAPI Args: - selector (str): Selector (https://github.com/antonmedv/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"` + selector (str): Selector (https://github.com/expr-lang/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"` Keyword Args: _check_type (bool): if True, values for parameters in openapi_types diff --git a/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_value_from.py b/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_value_from.py index b6b185e830da..7f9945dd7bf8 100644 --- a/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_value_from.py +++ b/sdks/python/client/argo_workflows/model/io_argoproj_workflow_v1alpha1_value_from.py @@ -158,7 +158,7 @@ def _from_openapi_data(cls, *args, **kwargs): # noqa: E501 _visited_composed_classes = (Animal,) config_map_key_ref (ConfigMapKeySelector): [optional] # noqa: E501 default (str): Default specifies a value to be used if retrieving the value from the specified source fails. [optional] # noqa: E501 - event (str): Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`. [optional] # noqa: E501 + event (str): Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`. [optional] # noqa: E501 expression (str): Expression, if defined, is evaluated to specify the value for the parameter. [optional] # noqa: E501 jq_filter (str): JQFilter expression against the resource object in resource templates. [optional] # noqa: E501 json_path (str): JSONPath of a resource to retrieve an output parameter value from in resource templates. [optional] # noqa: E501 @@ -248,7 +248,7 @@ def __init__(self, *args, **kwargs): # noqa: E501 _visited_composed_classes = (Animal,) config_map_key_ref (ConfigMapKeySelector): [optional] # noqa: E501 default (str): Default specifies a value to be used if retrieving the value from the specified source fails. [optional] # noqa: E501 - event (str): Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`. [optional] # noqa: E501 + event (str): Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message`. [optional] # noqa: E501 expression (str): Expression, if defined, is evaluated to specify the value for the parameter. [optional] # noqa: E501 jq_filter (str): JQFilter expression against the resource object in resource templates. [optional] # noqa: E501 json_path (str): JSONPath of a resource to retrieve an output parameter value from in resource templates. [optional] # noqa: E501 diff --git a/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1Event.md b/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1Event.md index 2822aa9998fc..8fe08221b8d7 100644 --- a/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1Event.md +++ b/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1Event.md @@ -4,7 +4,7 @@ ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**selector** | **str** | Selector (https://github.com/antonmedv/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"` | +**selector** | **str** | Selector (https://github.com/expr-lang/expr) that we must must match the io.argoproj.workflow.v1alpha1. E.g. `payload.message == \"test\"` | **any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1ValueFrom.md b/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1ValueFrom.md index 93d2c9f2ec1d..d603c7bddb90 100644 --- a/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1ValueFrom.md +++ b/sdks/python/client/docs/IoArgoprojWorkflowV1alpha1ValueFrom.md @@ -7,7 +7,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **config_map_key_ref** | [**ConfigMapKeySelector**](ConfigMapKeySelector.md) | | [optional] **default** | **str** | Default specifies a value to be used if retrieving the value from the specified source fails | [optional] -**event** | **str** | Selector (https://github.com/antonmedv/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` | [optional] +**event** | **str** | Selector (https://github.com/expr-lang/expr) that is evaluated against the event to get the value of the parameter. E.g. `payload.message` | [optional] **expression** | **str** | Expression, if defined, is evaluated to specify the value for the parameter | [optional] **jq_filter** | **str** | JQFilter expression against the resource object in resource templates | [optional] **json_path** | **str** | JSONPath of a resource to retrieve an output parameter value from in resource templates | [optional] diff --git a/server/event/dispatch/operation.go b/server/event/dispatch/operation.go index de9f13cf83c6..c24a95a4e79f 100644 --- a/server/event/dispatch/operation.go +++ b/server/event/dispatch/operation.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/antonmedv/expr" + "github.com/expr-lang/expr" log "github.com/sirupsen/logrus" "google.golang.org/grpc/metadata" corev1 "k8s.io/api/core/v1" @@ -121,7 +121,11 @@ func (o *Operation) dispatch(ctx context.Context, wfeb wfv1.WorkflowEventBinding if p.ValueFrom == nil { return nil, fmt.Errorf("malformed workflow template parameter \"%s\": valueFrom is nil", p.Name) } - result, err := expr.Eval(p.ValueFrom.Event, o.env) + program, err := expr.Compile(p.ValueFrom.Event, expr.Env(o.env)) + if err != nil { + return nil, fmt.Errorf("failed to compile workflow template parameter %s expression: %w", p.Name, err) + } + result, err := expr.Run(program, o.env) if err != nil { return nil, fmt.Errorf("failed to evaluate workflow template parameter \"%s\" expression: %w", p.Name, err) } @@ -183,7 +187,12 @@ func (o *Operation) populateWorkflowMetadata(wf *wfv1.Workflow, metadata *metav1 } func (o *Operation) evaluateStringExpression(statement string, errorInfo string) (string, error) { - result, err := expr.Eval(statement, exprenv.GetFuncMap(o.env)) + env := exprenv.GetFuncMap(o.env) + program, err := expr.Compile(statement, expr.Env(env)) + if err != nil { + return "", fmt.Errorf("failed to evaluate workflow %s expression: %w", errorInfo, err) + } + result, err := expr.Run(program, env) if err != nil { return "", fmt.Errorf("failed to evaluate workflow %s expression: %w", errorInfo, err) } diff --git a/server/event/dispatch/operation_test.go b/server/event/dispatch/operation_test.go index abdbe6cab5b9..36d49fa725f1 100644 --- a/server/event/dispatch/operation_test.go +++ b/server/event/dispatch/operation_test.go @@ -187,12 +187,12 @@ func TestNewOperation(t *testing.T) { sort.Strings(paramValues) assert.Equal(t, expectedParamValues, paramValues) } - assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to evaluate workflow template expression: unable to evaluate expression '': unexpected token EOF (1:1)", <-recorder.Events) + assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to evaluate workflow template expression: unexpected token EOF (1:1)", <-recorder.Events) assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to get workflow template: workflowtemplates.argoproj.io \"not-found\" not found", <-recorder.Events) assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to validate workflow template instanceid: 'my-wft-3' is not managed by the current Argo Server", <-recorder.Events) - assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to evaluate workflow template expression: unable to evaluate expression 'garbage!!!!!!': unexpected token Operator(\"!\") (1:8)\n | garbage!!!!!!\n | .......^", <-recorder.Events) + assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to evaluate workflow template expression: unexpected token Operator(\"!\") (1:8)\n | garbage!!!!!!\n | .......^", <-recorder.Events) assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to evaluate workflow template expression: unable to cast expression result 'garbage' to bool", <-recorder.Events) - assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to evaluate workflow template parameter \"my-param\" expression: unexpected token Operator(\"!\") (1:8)\n | rubbish!!!\n | .......^", <-recorder.Events) + assert.Equal(t, "Warning WorkflowEventBindingError failed to dispatch event: failed to compile workflow template parameter my-param expression: unexpected token Operator(\"!\") (1:8)\n | rubbish!!!\n | .......^", <-recorder.Events) } func Test_populateWorkflowMetadata(t *testing.T) { diff --git a/test/e2e/argo_server_test.go b/test/e2e/argo_server_test.go index 5f328d47e00f..42a8fff7fbb1 100644 --- a/test/e2e/argo_server_test.go +++ b/test/e2e/argo_server_test.go @@ -313,7 +313,7 @@ metadata: func(t *testing.T, e []corev1.Event) { assert.Equal(t, "argo", e[0].InvolvedObject.Namespace) assert.Equal(t, "WorkflowEventBindingError", e[0].Reason) - assert.Equal(t, "failed to dispatch event: failed to evaluate workflow template expression: unable to evaluate expression '': unexpected token EOF (1:1)", e[0].Message) + assert.Equal(t, "failed to dispatch event: failed to evaluate workflow template expression: unexpected token EOF (1:1)", e[0].Message) }, ) } diff --git a/test/e2e/expr_lang.go b/test/e2e/expr_lang.go new file mode 100644 index 000000000000..3c0511d53c8d --- /dev/null +++ b/test/e2e/expr_lang.go @@ -0,0 +1,70 @@ +//go:build functional +// +build functional + +package e2e + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + apiv1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "github.com/argoproj/argo-workflows/v3/test/e2e/fixtures" +) + +type ExprSuite struct { + fixtures.E2ESuite +} + +func (s *ExprSuite) TestRegression12037() { + s.Given(). + Workflow(`apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: broken- +spec: + entrypoint: main + templates: + - name: main + dag: + tasks: + - name: split + template: foo + - name: map + template: foo + depends: split + + - name: foo + container: + image: alpine + command: + - sh + - -c + - | + echo "foo" +`).When(). + SubmitWorkflow(). + WaitForWorkflow(fixtures.ToBeSucceeded). + Then(). + ExpectWorkflow(func(t *testing.T, metadata *v1.ObjectMeta, status *v1alpha1.WorkflowStatus) { + assert.Equal(t, status.Phase, v1alpha1.WorkflowSucceeded) + }). + ExpectWorkflowNode(func(status v1alpha1.NodeStatus) bool { + return strings.Contains(status.Name, ".split") + }, func(t *testing.T, status *v1alpha1.NodeStatus, pod *apiv1.Pod) { + assert.Equal(t, v1alpha1.NodeSucceeded, status.Phase) + }). + ExpectWorkflowNode(func(status v1alpha1.NodeStatus) bool { + return strings.Contains(status.Name, ".map") + }, func(t *testing.T, status *v1alpha1.NodeStatus, pod *apiv1.Pod) { + assert.Equal(t, v1alpha1.NodeSucceeded, status.Phase) + }) +} + +func TestExprLangSSuite(t *testing.T) { + suite.Run(t, new(ExprSuite)) +} diff --git a/util/expr/argoexpr/eval.go b/util/expr/argoexpr/eval.go index 62394e91eb57..41ce14003189 100644 --- a/util/expr/argoexpr/eval.go +++ b/util/expr/argoexpr/eval.go @@ -3,11 +3,15 @@ package argoexpr import ( "fmt" - "github.com/antonmedv/expr" + "github.com/expr-lang/expr" ) func EvalBool(input string, env interface{}) (bool, error) { - result, err := expr.Eval(input, env) + program, err := expr.Compile(input, expr.Env(env)) + if err != nil { + return false, err + } + result, err := expr.Run(program, env) if err != nil { return false, fmt.Errorf("unable to evaluate expression '%s': %s", input, err) } diff --git a/util/template/expression_template.go b/util/template/expression_template.go index f80b6dcb97c9..94ab2b8b5621 100644 --- a/util/template/expression_template.go +++ b/util/template/expression_template.go @@ -7,10 +7,10 @@ import ( "os" "strings" - "github.com/antonmedv/expr" - "github.com/antonmedv/expr/file" - "github.com/antonmedv/expr/parser/lexer" "github.com/doublerebel/bellows" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/file" + "github.com/expr-lang/expr/parser/lexer" log "github.com/sirupsen/logrus" ) @@ -41,7 +41,7 @@ func expressionReplace(w io.Writer, expression string, env map[string]interface{ // This is to make sure expressions which contains `workflow.status` and `work.failures` don't get resolved to nil // when `workflow.status` and `workflow.failures` don't exist in the env. - // See https://github.com/argoproj/argo-workflows/issues/10393, https://github.com/antonmedv/expr/issues/330 + // See https://github.com/argoproj/argo-workflows/issues/10393, https://github.com/expr-lang/expr/issues/330 // This issue doesn't happen to other template parameters since `workflow.status` and `workflow.failures` only exist in the env // when the exit handlers complete. if ((hasWorkflowStatus(unmarshalledExpression) && !hasVarInEnv(env, "workflow.status")) || @@ -50,7 +50,14 @@ func expressionReplace(w io.Writer, expression string, env map[string]interface{ return w.Write([]byte(fmt.Sprintf("{{%s%s}}", kindExpression, expression))) } - result, err := expr.Eval(unmarshalledExpression, env) + program, err := expr.Compile(unmarshalledExpression, expr.Env(env)) + // This allowUnresolved check is not great + // it allows for errors that are obviously + // not failed reference checks to also pass + if err != nil && !allowUnresolved { + return 0, fmt.Errorf("failed to evaluate expression: %w", err) + } + result, err := expr.Run(program, env) if (err != nil || result == nil) && allowUnresolved { // result is also un-resolved, and any error can be unresolved log.WithError(err).Debug("Result and error are unresolved") diff --git a/util/template/replace_test.go b/util/template/replace_test.go index 0ab0c8ae6a60..9f9f02697325 100644 --- a/util/template/replace_test.go +++ b/util/template/replace_test.go @@ -78,7 +78,7 @@ func Test_Replace(t *testing.T) { }) t.Run("Disallowed", func(t *testing.T) { _, err := Replace(toJsonString("{{=foo}}"), nil, false) - assert.EqualError(t, err, "failed to evaluate expression \"foo\"") + assert.EqualError(t, err, "failed to evaluate expression: unknown name foo (1:1)\n | foo\n | ^") }) t.Run("DisallowedWorkflowStatus", func(t *testing.T) { _, err := Replace(toJsonString(`{{=workflow.status == "Succeeded" ? "SUCCESSFUL" : "FAILED"}}`), nil, false) diff --git a/util/template/resolve_var.go b/util/template/resolve_var.go index 954faa8dc20c..6d4b3b210864 100644 --- a/util/template/resolve_var.go +++ b/util/template/resolve_var.go @@ -3,7 +3,7 @@ package template import ( "strings" - "github.com/antonmedv/expr" + "github.com/expr-lang/expr" "github.com/argoproj/argo-workflows/v3/errors" ) @@ -13,7 +13,11 @@ func ResolveVar(s string, m map[string]interface{}) (interface{}, error) { kind, expression := parseTag(tag) switch kind { case kindExpression: - result, err := expr.Eval(expression, m) + program, err := expr.Compile(expression, expr.Env(m)) + if err != nil { + return nil, errors.Errorf(errors.CodeBadRequest, "Unable to compile: %q", expression) + } + result, err := expr.Run(program, m) if err != nil { return nil, errors.Errorf(errors.CodeBadRequest, "Invalid expression: %q", expression) } diff --git a/util/template/resolve_var_test.go b/util/template/resolve_var_test.go index 6ca9219b11ba..f1c82e6d9dc2 100644 --- a/util/template/resolve_var_test.go +++ b/util/template/resolve_var_test.go @@ -31,11 +31,11 @@ func Test_ResolveVar(t *testing.T) { }) t.Run("Unresolved", func(t *testing.T) { _, err := ResolveVar("{{=foo}}", nil) - assert.EqualError(t, err, "Unable to resolve: \"=foo\"") + assert.EqualError(t, err, "Unable to compile: \"foo\"") }) t.Run("Error", func(t *testing.T) { _, err := ResolveVar("{{=!}}", nil) - assert.EqualError(t, err, "Invalid expression: \"!\"") + assert.EqualError(t, err, "Unable to compile: \"!\"") }) }) } diff --git a/workflow/controller/operator.go b/workflow/controller/operator.go index 012e2d623a51..22649fc75da5 100644 --- a/workflow/controller/operator.go +++ b/workflow/controller/operator.go @@ -17,11 +17,11 @@ import ( "github.com/argoproj/argo-workflows/v3/util/secrets" - "github.com/antonmedv/expr" "github.com/argoproj/pkg/humanize" argokubeerr "github.com/argoproj/pkg/kube/errors" "github.com/argoproj/pkg/strftime" jsonpatch "github.com/evanphx/json-patch" + "github.com/expr-lang/expr" log "github.com/sirupsen/logrus" apiv1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" @@ -540,7 +540,11 @@ func (woc *wfOperationCtx) updateWorkflowMetadata() error { env := env.GetFuncMap(template.EnvMap(woc.globalParams)) for n, f := range md.LabelsFrom { - r, err := expr.Eval(f.Expression, env) + program, err := expr.Compile(f.Expression, expr.Env(env)) + if err != nil { + return fmt.Errorf("Failed to compile function for expression %q: %w", f.Expression, err) + } + r, err := expr.Run(program, env) if err != nil { return fmt.Errorf("failed to evaluate label %q expression %q: %w", n, f.Expression, err) } diff --git a/workflow/controller/scope.go b/workflow/controller/scope.go index 42ce5356c7cd..30eb60a54230 100644 --- a/workflow/controller/scope.go +++ b/workflow/controller/scope.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/antonmedv/expr" + "github.com/expr-lang/expr" "github.com/argoproj/argo-workflows/v3/errors" wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" @@ -77,7 +77,11 @@ func (s *wfScope) resolveParameter(p *wfv1.ValueFrom) (interface{}, error) { } if p.Expression != "" { env := env.GetFuncMap(s.scope) - return expr.Eval(p.Expression, env) + program, err := expr.Compile(p.Expression, expr.Env(env)) + if err != nil { + return nil, err + } + return expr.Run(program, env) } else { return s.resolveVar(p.Parameter) } @@ -93,7 +97,15 @@ func (s *wfScope) resolveArtifact(art *wfv1.Artifact) (*wfv1.Artifact, error) { if art.FromExpression != "" { env := env.GetFuncMap(s.scope) - val, err = expr.Eval(art.FromExpression, env) + program, err := expr.Compile(art.FromExpression, expr.Env(env)) + if err != nil { + return nil, err + } + val, err = expr.Run(program, env) + if err != nil { + return nil, err + } + } else { val, err = s.resolveVar(art.From) } diff --git a/workflow/data/data.go b/workflow/data/data.go index 9ffd7add77df..942fcc62f941 100644 --- a/workflow/data/data.go +++ b/workflow/data/data.go @@ -3,7 +3,7 @@ package data import ( "fmt" - "github.com/antonmedv/expr" + "github.com/expr-lang/expr" wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" ) @@ -56,5 +56,10 @@ func processTransformation(data interface{}, transformation *wfv1.Transformation } func processExpression(expression string, data interface{}) (interface{}, error) { - return expr.Eval(expression, map[string]interface{}{"data": data}) + env := map[string]interface{}{"data": data} + program, err := expr.Compile(expression, expr.Env(env)) + if err != nil { + return nil, err + } + return expr.Run(program, env) }