From 33c7b51f998924c68bfb0d9ebc62688a007b230b Mon Sep 17 00:00:00 2001 From: Shivam Saraf Date: Fri, 19 Jun 2026 11:55:36 -0400 Subject: [PATCH 1/2] Add one-time versioning override CLI support --- go.mod | 16 +-- go.sum | 28 ++--- internal/temporalcli/commands.gen.go | 20 ++-- internal/temporalcli/commands.workflow.go | 108 +++++++++++++---- .../commands.workflow_reset_update_options.go | 42 +++---- ...ands.workflow_reset_update_options_test.go | 112 ++++++++++++++++++ .../temporalcli/commands.workflow_test.go | 107 ++++++++++++++++- internal/temporalcli/commands.yaml | 29 +++-- 8 files changed, 373 insertions(+), 89 deletions(-) diff --git a/go.mod b/go.mod index 90fe02df5..52b1a85a5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/temporalio/cli -go 1.26.3 +go 1.26.4 require ( github.com/BurntSushi/toml v1.4.0 @@ -17,13 +17,13 @@ require ( github.com/stretchr/testify v1.11.1 github.com/temporalio/cli/cliext v0.0.0 github.com/temporalio/ui-server/v2 v2.49.1 - go.temporal.io/api v1.62.13 + go.temporal.io/api v1.62.15-0.20260618002053-7c062185c563 go.temporal.io/sdk v1.44.1 go.temporal.io/sdk/contrib/envconfig v1.0.2 - go.temporal.io/server v1.32.0-157.0 + go.temporal.io/server v1.29.0-135.0.0.20260617003708-75fcd219ad4a golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f golang.org/x/mod v0.35.0 - golang.org/x/term v0.42.0 + golang.org/x/term v0.43.0 golang.org/x/tools v0.44.0 google.golang.org/grpc v1.80.0 google.golang.org/protobuf v1.36.11 @@ -201,12 +201,12 @@ require ( go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.50.0 // indirect - golang.org/x/net v0.53.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/text v0.36.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect google.golang.org/api v0.276.0 // indirect google.golang.org/genproto v0.0.0-20260420184626-e10c466a9529 // indirect diff --git a/go.sum b/go.sum index 01c1676b9..7435ba2e0 100644 --- a/go.sum +++ b/go.sum @@ -469,16 +469,16 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09 go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= -go.temporal.io/api v1.62.13 h1:xMa8Nt5oAMX+LvlCJA44wjTCc1H09i2rG9poB1/xvH4= -go.temporal.io/api v1.62.13/go.mod h1:0k75tRljEuELWGeXjEZZO7zYqBln4+1FrG6+IMOMy7Q= +go.temporal.io/api v1.62.15-0.20260618002053-7c062185c563 h1:gPketS2mBLHDxz8l2xldQxQFEmm14h+sqmaJ5QQmKi4= +go.temporal.io/api v1.62.15-0.20260618002053-7c062185c563/go.mod h1:0k75tRljEuELWGeXjEZZO7zYqBln4+1FrG6+IMOMy7Q= go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2 h1:1hKeH3GyR6YD6LKMHGCZ76t6h1Sgha0hXVQBxWi3dlQ= go.temporal.io/auto-scaled-workers v0.0.0-20260407181057-edd947d743d2/go.mod h1:T8dnzVPeO+gaUTj9eDgm/lT2lZH4+JXNvrGaQGyVi50= go.temporal.io/sdk v1.44.1 h1:Mt2OZLZpqkzDIdg9YyQzO0Rb/HqCDnnqHlIAGAJ5gqM= go.temporal.io/sdk v1.44.1/go.mod h1:vkApR12F9/Y8OR+hkxe7WyXQFuCX6clhzqnAk6rzDAM= go.temporal.io/sdk/contrib/envconfig v1.0.2 h1:MGHfsuPUtsf7X9M6WYn3zYJj/mWsuYHnA1uuiL0KEuE= go.temporal.io/sdk/contrib/envconfig v1.0.2/go.mod h1:MuMiH7hksps2uXnmKuAWaP9P6WbkSDy62kl64t1VJVg= -go.temporal.io/server v1.32.0-157.0 h1:nzFqNwx+5lXsT0/DSiFyR5vHMnDcT3PVAvmRDqCUn38= -go.temporal.io/server v1.32.0-157.0/go.mod h1:a76wf30/s28JXh+3nDQtQi8KzOfRQEddpebvmr/oQL4= +go.temporal.io/server v1.29.0-135.0.0.20260617003708-75fcd219ad4a h1:ssfAhdqUDLs8ekWvZkqIDghRUVLM0ehiU9s9KvKqph8= +go.temporal.io/server v1.29.0-135.0.0.20260617003708-75fcd219ad4a/go.mod h1:/9DA479u5Cas9fZ/c5VWJF2SjA7VTeg/tRWFshr3Lho= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -509,8 +509,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -543,8 +543,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -571,15 +571,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -587,8 +587,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 42c17e7d4..d10c44ac5 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -415,11 +415,11 @@ type WorkflowUpdateOptionsOptions struct { func (v *WorkflowUpdateOptionsOptions) BuildFlags(f *pflag.FlagSet) { v.FlagSet = f - v.VersioningOverrideBehavior = cliext.NewFlagStringEnum([]string{"pinned", "auto_upgrade"}, "") - f.Var(&v.VersioningOverrideBehavior, "versioning-override-behavior", "Override the versioning behavior of a Workflow. Accepted values: pinned, auto_upgrade. Required.") + v.VersioningOverrideBehavior = cliext.NewFlagStringEnum([]string{"pinned", "one_time", "auto_upgrade"}, "") + f.Var(&v.VersioningOverrideBehavior, "versioning-override-behavior", "Override the versioning behavior of a Workflow. Accepted values: pinned, one_time, auto_upgrade. Required.") _ = cobra.MarkFlagRequired(f, "versioning-override-behavior") - f.StringVar(&v.VersioningOverrideDeploymentName, "versioning-override-deployment-name", "", "When overriding to a `pinned` behavior, specifies the Deployment Name of the version to target.") - f.StringVar(&v.VersioningOverrideBuildId, "versioning-override-build-id", "", "When overriding to a `pinned` behavior, specifies the Build ID of the version to target.") + f.StringVar(&v.VersioningOverrideDeploymentName, "versioning-override-deployment-name", "", "When overriding to a `pinned` or `one_time` behavior, specifies the Deployment Name of the version to target.") + f.StringVar(&v.VersioningOverrideBuildId, "versioning-override-build-id", "", "When overriding to a `pinned` or `one_time` behavior, specifies the Build ID of the version to target.") } type ActivityReferenceOptions struct { @@ -5063,16 +5063,16 @@ func NewTemporalWorkflowUpdateOptionsCommand(cctx *CommandContext, parent *Tempo s.Command.Use = "update-options [flags]" s.Command.Short = "Change Workflow Execution Options" if hasHighlighting { - s.Command.Long = "Modify properties of Workflow Executions:\n\n\x1b[1mtemporal workflow update-options [options]\x1b[0m\n\nIt can override the Worker Deployment configuration of a\nWorkflow Execution, which controls Worker Versioning.\n\nFor example, to force Workers in the current Deployment execute the\nnext Workflow Task change behavior to \x1b[1mauto_upgrade\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior auto_upgrade\x1b[0m\n\nor to pin the workflow execution to a Worker Deployment, set behavior\nto \x1b[1mpinned\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior pinned \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\x1b[0m\n\nTo remove any previous overrides, set the behavior to\n\x1b[1munspecified\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior unspecified\x1b[0m\n\nTo see the current override use \x1b[1mtemporal workflow describe\x1b[0m" + s.Command.Long = "Modify properties of Workflow Executions:\n\n\x1b[1mtemporal workflow update-options [options]\x1b[0m\n\nIt can override the Worker Deployment configuration of a\nWorkflow Execution, which controls Worker Versioning.\n\nFor example, to force Workers in the current Deployment execute the\nnext Workflow Task change behavior to \x1b[1mauto_upgrade\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior auto_upgrade\x1b[0m\n\nor to pin the workflow execution to a Worker Deployment, set behavior\nto \x1b[1mpinned\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior pinned \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\x1b[0m\n\nor to move the workflow execution to a Worker Deployment Version until\nthe next Workflow Task completes there, set behavior to \x1b[1mone_time\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior one_time \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\x1b[0m\n\nTo remove any previous overrides, set the behavior to\n\x1b[1munspecified\x1b[0m:\n\n\x1b[1mtemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior unspecified\x1b[0m\n\nTo see the current override use \x1b[1mtemporal workflow describe\x1b[0m" } else { - s.Command.Long = "Modify properties of Workflow Executions:\n\n```\ntemporal workflow update-options [options]\n```\n\nIt can override the Worker Deployment configuration of a\nWorkflow Execution, which controls Worker Versioning.\n\nFor example, to force Workers in the current Deployment execute the\nnext Workflow Task change behavior to `auto_upgrade`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior auto_upgrade\n```\n\nor to pin the workflow execution to a Worker Deployment, set behavior\nto `pinned`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior pinned \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\n```\n\nTo remove any previous overrides, set the behavior to\n`unspecified`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior unspecified\n```\n\nTo see the current override use `temporal workflow describe`" + s.Command.Long = "Modify properties of Workflow Executions:\n\n```\ntemporal workflow update-options [options]\n```\n\nIt can override the Worker Deployment configuration of a\nWorkflow Execution, which controls Worker Versioning.\n\nFor example, to force Workers in the current Deployment execute the\nnext Workflow Task change behavior to `auto_upgrade`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior auto_upgrade\n```\n\nor to pin the workflow execution to a Worker Deployment, set behavior\nto `pinned`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior pinned \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\n```\n\nor to move the workflow execution to a Worker Deployment Version until\nthe next Workflow Task completes there, set behavior to `one_time`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior one_time \\\n --versioning-override-deployment-name YourDeploymentName \\\n --versioning-override-build-id YourDeploymentBuildId\n```\n\nTo remove any previous overrides, set the behavior to\n`unspecified`:\n\n```\ntemporal workflow update-options \\\n --workflow-id YourWorkflowId \\\n --versioning-override-behavior unspecified\n```\n\nTo see the current override use `temporal workflow describe`" } s.Command.Args = cobra.NoArgs - s.VersioningOverrideBehavior = cliext.NewFlagStringEnum([]string{"unspecified", "pinned", "auto_upgrade"}, "") - s.Command.Flags().Var(&s.VersioningOverrideBehavior, "versioning-override-behavior", "Override the versioning behavior of a Workflow. Accepted values: unspecified, pinned, auto_upgrade. Required.") + s.VersioningOverrideBehavior = cliext.NewFlagStringEnum([]string{"unspecified", "pinned", "one_time", "auto_upgrade"}, "") + s.Command.Flags().Var(&s.VersioningOverrideBehavior, "versioning-override-behavior", "Override the versioning behavior of a Workflow. Accepted values: unspecified, pinned, one_time, auto_upgrade. Required.") _ = cobra.MarkFlagRequired(s.Command.Flags(), "versioning-override-behavior") - s.Command.Flags().StringVar(&s.VersioningOverrideDeploymentName, "versioning-override-deployment-name", "", "When overriding to a `pinned` behavior, specifies the Deployment Name of the version to target.") - s.Command.Flags().StringVar(&s.VersioningOverrideBuildId, "versioning-override-build-id", "", "When overriding to a `pinned` behavior, specifies the Build ID of the version to target.") + s.Command.Flags().StringVar(&s.VersioningOverrideDeploymentName, "versioning-override-deployment-name", "", "When overriding to a `pinned` or `one_time` behavior, specifies the Deployment Name of the version to target.") + s.Command.Flags().StringVar(&s.VersioningOverrideBuildId, "versioning-override-build-id", "", "When overriding to a `pinned` or `one_time` behavior, specifies the Build ID of the version to target.") s.SingleWorkflowOrBatchOptions.BuildFlags(s.Command.Flags()) s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { diff --git a/internal/temporalcli/commands.workflow.go b/internal/temporalcli/commands.workflow.go index e03047714..b6736c4f3 100644 --- a/internal/temporalcli/commands.workflow.go +++ b/internal/temporalcli/commands.workflow.go @@ -137,9 +137,10 @@ func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args [] } } - if c.VersioningOverrideBehavior.Value == "pinned" { + if c.VersioningOverrideBehavior.Value == "pinned" || + c.VersioningOverrideBehavior.Value == "one_time" { if c.VersioningOverrideDeploymentName == "" || c.VersioningOverrideBuildId == "" { - return fmt.Errorf("missing deployment name and/or build id with 'pinned' behavior") + return fmt.Errorf("missing deployment name and/or build id with '%s' behavior", c.VersioningOverrideBehavior.Value) } } @@ -164,9 +165,10 @@ func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args [] overrideChange = &client.VersioningOverrideChange{ Value: &client.AutoUpgradeVersioningOverride{}, } + case "one_time": default: return fmt.Errorf( - "invalid deployment behavior: %v, valid values are: 'unspecified', 'pinned', and 'auto_upgrade'", + "invalid deployment behavior: %v, valid values are: 'unspecified', 'pinned', 'auto_upgrade', and 'one_time'", c.VersioningOverrideBehavior, ) } @@ -176,36 +178,39 @@ func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args [] return err } else if exec != nil { - _, err := cl.UpdateWorkflowExecutionOptions(cctx, client.UpdateWorkflowExecutionOptionsRequest{ - WorkflowId: exec.WorkflowId, - RunId: exec.RunId, - WorkflowExecutionOptionsChanges: client.WorkflowExecutionOptionsChanges{ - VersioningOverride: overrideChange, - }, - }) + if c.VersioningOverrideBehavior.Value == "one_time" { + err = c.updateWorkflowExecutionOptionsWithProto(cctx, cl, exec, oneTimeVersioningOverrideToProto(c.VersioningOverrideDeploymentName, c.VersioningOverrideBuildId)) + } else { + _, err = cl.UpdateWorkflowExecutionOptions(cctx, client.UpdateWorkflowExecutionOptionsRequest{ + WorkflowId: exec.WorkflowId, + RunId: exec.RunId, + WorkflowExecutionOptionsChanges: client.WorkflowExecutionOptionsChanges{ + VersioningOverride: overrideChange, + }, + }) + } if err != nil { return fmt.Errorf("failed to update workflow options: %w", err) } cctx.Printer.Println("Update workflow options succeeded") } else { // Run batch - var workflowExecutionOptions *workflowpb.WorkflowExecutionOptions - protoMask, err := fieldmaskpb.New(workflowExecutionOptions, "versioning_override") - if err != nil { - return fmt.Errorf("invalid field mask: %w", err) - } - var protoVerOverride *workflowpb.VersioningOverride - if overrideChange != nil { + if c.VersioningOverrideBehavior.Value == "one_time" { + protoVerOverride = oneTimeVersioningOverrideToProto(c.VersioningOverrideDeploymentName, c.VersioningOverrideBuildId) + } else if overrideChange != nil { protoVerOverride = versioningOverrideToProto(overrideChange.Value) } + workflowExecutionOptions, protoMask, err := workflowExecutionOptionsForVersioningOverride(protoVerOverride) + if err != nil { + return fmt.Errorf("invalid field mask: %w", err) + } + batchReq.Operation = &workflowservice.StartBatchOperationRequest_UpdateWorkflowOptionsOperation{ UpdateWorkflowOptionsOperation: &batch.BatchOperationUpdateWorkflowExecutionOptions{ - Identity: c.Parent.Identity, - WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - VersioningOverride: protoVerOverride, - }, - UpdateMask: protoMask, + Identity: c.Parent.Identity, + WorkflowExecutionOptions: workflowExecutionOptions, + UpdateMask: protoMask, }, } if err := startBatchJob(cctx, cl, batchReq); err != nil { @@ -215,6 +220,43 @@ func (c *TemporalWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args [] return nil } +func (c *TemporalWorkflowUpdateOptionsCommand) updateWorkflowExecutionOptionsWithProto( + ctx context.Context, + cl client.Client, + exec *common.WorkflowExecution, + override *workflowpb.VersioningOverride, +) error { + workflowExecutionOptions, protoMask, err := workflowExecutionOptionsForVersioningOverride(override) + if err != nil { + return fmt.Errorf("invalid field mask: %w", err) + } + + _, err = cl.WorkflowService().UpdateWorkflowExecutionOptions(ctx, &workflowservice.UpdateWorkflowExecutionOptionsRequest{ + Namespace: c.Parent.Namespace, + WorkflowExecution: &common.WorkflowExecution{ + WorkflowId: exec.GetWorkflowId(), + RunId: exec.GetRunId(), + }, + WorkflowExecutionOptions: workflowExecutionOptions, + UpdateMask: protoMask, + Identity: c.Parent.Identity, + }) + return err +} + +func workflowExecutionOptionsForVersioningOverride( + override *workflowpb.VersioningOverride, +) (*workflowpb.WorkflowExecutionOptions, *fieldmaskpb.FieldMask, error) { + workflowExecutionOptions := &workflowpb.WorkflowExecutionOptions{ + VersioningOverride: override, + } + protoMask, err := fieldmaskpb.New(workflowExecutionOptions, "versioning_override") + if err != nil { + return nil, nil, err + } + return workflowExecutionOptions, protoMask, nil +} + func (c *TemporalWorkflowMetadataCommand) run(cctx *CommandContext, _ []string) error { return queryHelper(cctx, c.Parent, PayloadInputOptions{}, metadataQueryName, nil, c.RejectCondition, c.WorkflowReferenceOptions) @@ -756,10 +798,7 @@ func versioningOverrideToProto(versioningOverride client.VersioningOverride) *wo Override: &workflowpb.VersioningOverride_Pinned{ Pinned: &workflowpb.VersioningOverride_PinnedOverride{ Behavior: workflowpb.VersioningOverride_PINNED_OVERRIDE_BEHAVIOR_PINNED, - Version: &deploymentpb.WorkerDeploymentVersion{ - DeploymentName: v.Version.DeploymentName, - BuildId: v.Version.BuildID, - }, + Version: workerDeploymentVersionToProto(v.Version.DeploymentName, v.Version.BuildID), }, }, } @@ -772,3 +811,20 @@ func versioningOverrideToProto(versioningOverride client.VersioningOverride) *wo return nil } } + +func workerDeploymentVersionToProto(deploymentName, buildID string) *deploymentpb.WorkerDeploymentVersion { + return &deploymentpb.WorkerDeploymentVersion{ + DeploymentName: deploymentName, + BuildId: buildID, + } +} + +func oneTimeVersioningOverrideToProto(deploymentName, buildID string) *workflowpb.VersioningOverride { + return &workflowpb.VersioningOverride{ + Override: &workflowpb.VersioningOverride_OneTime{ + OneTime: &workflowpb.VersioningOverride_OneTimeOverride{ + TargetDeploymentVersion: workerDeploymentVersionToProto(deploymentName, buildID), + }, + }, + } +} diff --git a/internal/temporalcli/commands.workflow_reset_update_options.go b/internal/temporalcli/commands.workflow_reset_update_options.go index ad6644604..53399578e 100644 --- a/internal/temporalcli/commands.workflow_reset_update_options.go +++ b/internal/temporalcli/commands.workflow_reset_update_options.go @@ -3,9 +3,7 @@ package temporalcli import ( "fmt" - deploymentpb "go.temporal.io/api/deployment/v1" workflowpb "go.temporal.io/api/workflow/v1" - "google.golang.org/protobuf/types/known/fieldmaskpb" ) func (c *TemporalWorkflowResetWithWorkflowUpdateOptionsCommand) run(cctx *CommandContext, args []string) error { @@ -14,12 +12,14 @@ func (c *TemporalWorkflowResetWithWorkflowUpdateOptionsCommand) run(cctx *Comman return err } - if c.VersioningOverrideBehavior.Value == "pinned" { + if c.VersioningOverrideBehavior.Value == "pinned" || + c.VersioningOverrideBehavior.Value == "one_time" { if c.VersioningOverrideDeploymentName == "" || c.VersioningOverrideBuildId == "" { - return fmt.Errorf("deployment name and build id are required with 'pinned' behavior") + return fmt.Errorf("deployment name and build id are required with '%s' behavior", c.VersioningOverrideBehavior.Value) } } - if c.VersioningOverrideBehavior.Value != "pinned" { + if c.VersioningOverrideBehavior.Value != "pinned" && + c.VersioningOverrideBehavior.Value != "one_time" { if c.VersioningOverrideDeploymentName != "" || c.VersioningOverrideBuildId != "" { return fmt.Errorf("cannot set deployment name or build id with %v behavior", c.VersioningOverrideBehavior.Value) } @@ -31,28 +31,30 @@ func (c *TemporalWorkflowResetWithWorkflowUpdateOptionsCommand) run(cctx *Comman } defer cl.Close() - VersioningOverride := &workflowpb.VersioningOverride{} + var versioningOverride *workflowpb.VersioningOverride switch c.VersioningOverrideBehavior.Value { case "pinned": - VersioningOverride.Override = &workflowpb.VersioningOverride_Pinned{ - Pinned: &workflowpb.VersioningOverride_PinnedOverride{ - Behavior: workflowpb.VersioningOverride_PINNED_OVERRIDE_BEHAVIOR_PINNED, - Version: &deploymentpb.WorkerDeploymentVersion{ - DeploymentName: c.VersioningOverrideDeploymentName, - BuildId: c.VersioningOverrideBuildId, + versioningOverride = &workflowpb.VersioningOverride{ + Override: &workflowpb.VersioningOverride_Pinned{ + Pinned: &workflowpb.VersioningOverride_PinnedOverride{ + Behavior: workflowpb.VersioningOverride_PINNED_OVERRIDE_BEHAVIOR_PINNED, + Version: workerDeploymentVersionToProto(c.VersioningOverrideDeploymentName, c.VersioningOverrideBuildId), }, }, } + case "one_time": + versioningOverride = oneTimeVersioningOverrideToProto(c.VersioningOverrideDeploymentName, c.VersioningOverrideBuildId) case "auto_upgrade": - VersioningOverride.Override = &workflowpb.VersioningOverride_AutoUpgrade{ - AutoUpgrade: true, + versioningOverride = &workflowpb.VersioningOverride{ + Override: &workflowpb.VersioningOverride_AutoUpgrade{ + AutoUpgrade: true, + }, } default: - return fmt.Errorf("invalid deployment behavior: %v, valid values are: 'pinned', and 'auto_upgrade'", c.VersioningOverrideBehavior.Value) + return fmt.Errorf("invalid deployment behavior: %v, valid values are: 'pinned', 'auto_upgrade', and 'one_time'", c.VersioningOverrideBehavior.Value) } - var workflowExecutionOptions *workflowpb.WorkflowExecutionOptions - protoMask, err := fieldmaskpb.New(workflowExecutionOptions, "versioning_override") + workflowExecutionOptions, protoMask, err := workflowExecutionOptionsForVersioningOverride(versioningOverride) if err != nil { return fmt.Errorf("invalid field mask: %w", err) } @@ -60,10 +62,8 @@ func (c *TemporalWorkflowResetWithWorkflowUpdateOptionsCommand) run(cctx *Comman postOp := &workflowpb.PostResetOperation{ Variant: &workflowpb.PostResetOperation_UpdateWorkflowOptions_{ UpdateWorkflowOptions: &workflowpb.PostResetOperation_UpdateWorkflowOptions{ - WorkflowExecutionOptions: &workflowpb.WorkflowExecutionOptions{ - VersioningOverride: VersioningOverride, - }, - UpdateMask: protoMask, + WorkflowExecutionOptions: workflowExecutionOptions, + UpdateMask: protoMask, }, }, } diff --git a/internal/temporalcli/commands.workflow_reset_update_options_test.go b/internal/temporalcli/commands.workflow_reset_update_options_test.go index 8e2bf9a19..f6148e89c 100644 --- a/internal/temporalcli/commands.workflow_reset_update_options_test.go +++ b/internal/temporalcli/commands.workflow_reset_update_options_test.go @@ -3,16 +3,19 @@ package temporalcli_test import ( "context" "fmt" + "sync" "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.temporal.io/api/enums/v1" + workflowpb "go.temporal.io/api/workflow/v1" "go.temporal.io/api/workflowservice/v1" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" + "google.golang.org/grpc" ) func (s *SharedServerSuite) TestWorkflow_ResetWithWorkflowUpdateOptions_ValidatesArguments_MissingRequiredFlag() { @@ -54,6 +57,115 @@ func (s *SharedServerSuite) TestWorkflow_ResetWithWorkflowUpdateOptions_Validate require.Contains(s.T(), res.Err.Error(), "cannot set deployment name or build id with auto_upgrade behavior") } +func (s *SharedServerSuite) TestWorkflow_ResetWithWorkflowUpdateOptions_Single_OneTimeOverrideRequest() { + workflowID := "workflow-" + uuid.NewString() + deploymentName := "deployment-" + uuid.NewString() + buildID := "build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var resetRequest *workflowservice.ResetWorkflowExecutionRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.ResetWorkflowExecutionRequest); ok { + lastRequestLock.Lock() + resetRequest = r + lastRequestLock.Unlock() + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "reset", "with-workflow-update-options", + "--address", s.Address(), + "-w", workflowID, + "-e", "3", + "--reason", "test-reset-one-time", + "--versioning-override-behavior", "one_time", + "--versioning-override-deployment-name", deploymentName, + "--versioning-override-build-id", buildID, + ) + require.NoError(s.T(), res.Err) + + lastRequestLock.Lock() + req := resetRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + require.Equal(s.T(), s.Namespace(), req.GetNamespace()) + require.Equal(s.T(), workflowID, req.GetWorkflowExecution().GetWorkflowId()) + require.Equal(s.T(), int64(3), req.GetWorkflowTaskFinishEventId()) + s.requireOneTimePostResetOverride(req.GetPostResetOperations(), deploymentName, buildID) +} + +func (s *SharedServerSuite) TestWorkflow_ResetBatchWithWorkflowUpdateOptions_OneTimeOverrideRequest() { + query := "WorkflowType = 'YourWorkflowType'" + deploymentName := "deployment-" + uuid.NewString() + buildID := "build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var startBatchRequest *workflowservice.StartBatchOperationRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.StartBatchOperationRequest); ok { + lastRequestLock.Lock() + startBatchRequest = r + lastRequestLock.Unlock() + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "reset", "with-workflow-update-options", + "--address", s.Address(), + "--query", query, + "--yes", + "-t", "FirstWorkflowTask", + "--reason", "test-batch-reset-one-time", + "--versioning-override-behavior", "one_time", + "--versioning-override-deployment-name", deploymentName, + "--versioning-override-build-id", buildID, + ) + require.NoError(s.T(), res.Err) + + lastRequestLock.Lock() + req := startBatchRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + require.Equal(s.T(), s.Namespace(), req.GetNamespace()) + require.Equal(s.T(), query, req.GetVisibilityQuery()) + resetOperation := req.GetResetOperation() + require.NotNil(s.T(), resetOperation) + s.requireOneTimePostResetOverride(resetOperation.GetPostResetOperations(), deploymentName, buildID) +} + +func (s *SharedServerSuite) requireOneTimePostResetOverride(postOps []*workflowpb.PostResetOperation, deploymentName, buildID string) { + require.Len(s.T(), postOps, 1) + updateOptions := postOps[0].GetUpdateWorkflowOptions() + require.NotNil(s.T(), updateOptions) + require.Equal(s.T(), []string{"versioning_override"}, updateOptions.GetUpdateMask().GetPaths()) + override := updateOptions.GetWorkflowExecutionOptions().GetVersioningOverride() + require.NotNil(s.T(), override) + oneTime := override.GetOneTime() + require.NotNil(s.T(), oneTime) + require.Equal(s.T(), deploymentName, oneTime.GetTargetDeploymentVersion().GetDeploymentName()) + require.Equal(s.T(), buildID, oneTime.GetTargetDeploymentVersion().GetBuildId()) +} + func (s *SharedServerSuite) TestWorkflow_ResetWithWorkflowUpdateOptions_Single_AutoUpgradeBehavior() { var wfExecutions int s.Worker().OnDevWorkflow(func(ctx workflow.Context, a any) (any, error) { diff --git a/internal/temporalcli/commands.workflow_test.go b/internal/temporalcli/commands.workflow_test.go index aedf331f7..d1d728853 100644 --- a/internal/temporalcli/commands.workflow_test.go +++ b/internal/temporalcli/commands.workflow_test.go @@ -159,7 +159,7 @@ func (s *SharedServerSuite) testSignalBatchWorkflow(json bool) *CommandResult { s.CommandHarness.Stdin.WriteString("y\n") } res := s.Execute(args...) - s.NoError(res.Err) + require.NoError(s.T(), res.Err) // Confirm that all workflows complete with the signal value for _, run := range runs { @@ -202,7 +202,7 @@ func (s *SharedServerSuite) TestWorkflow_Delete_SingleWorkflowSuccess() { "--workflow-id", run.GetID(), "--yes", ) - s.NoError(res.Err) + require.NoError(s.T(), res.Err) s.NotContains(res.Stdout.String(), "Deleting Workflow Executions in a global Namespace") s.NotContains(res.Stderr.String(), "Deleting Workflow Executions in a global Namespace") s.Contains(res.Stdout.String(), "Delete workflow succeeded") @@ -720,6 +720,59 @@ func (s *SharedServerSuite) TestWorkflow_Batch_Update_Options_Versioning_Overrid }, 10*time.Second, 100*time.Millisecond) } +func (s *SharedServerSuite) TestWorkflow_Batch_Update_Options_OneTimeOverride() { + query := "WorkflowType = 'YourWorkflowType'" + deploymentName := "deployment-" + uuid.NewString() + buildID := "build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var startBatchRequest *workflowservice.StartBatchOperationRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.StartBatchOperationRequest); ok { + lastRequestLock.Lock() + startBatchRequest = r + lastRequestLock.Unlock() + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "update-options", + "--address", s.Address(), + "--query", query, + "--yes", + "--versioning-override-behavior", "one_time", + "--versioning-override-deployment-name", deploymentName, + "--versioning-override-build-id", buildID, + ) + s.NoError(res.Err) + + lastRequestLock.Lock() + req := startBatchRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + s.Equal(s.Namespace(), req.GetNamespace()) + s.Equal(query, req.GetVisibilityQuery()) + updateOptionsOperation := req.GetUpdateWorkflowOptionsOperation() + require.NotNil(s.T(), updateOptionsOperation) + s.Equal([]string{"versioning_override"}, updateOptionsOperation.GetUpdateMask().GetPaths()) + override := updateOptionsOperation.GetWorkflowExecutionOptions().GetVersioningOverride() + require.NotNil(s.T(), override) + oneTime := override.GetOneTime() + require.NotNil(s.T(), oneTime) + s.Equal(deploymentName, oneTime.GetTargetDeploymentVersion().GetDeploymentName()) + s.Equal(buildID, oneTime.GetTargetDeploymentVersion().GetBuildId()) +} + func (s *SharedServerSuite) TestWorkflow_Update_Options_Versioning_Override() { buildId1 := "id1-" + uuid.NewString() buildId2 := "id2-" + uuid.NewString() @@ -889,6 +942,56 @@ func (s *SharedServerSuite) TestWorkflow_Update_Options_Versioning_Override() { s.Nil(versioningInfo.VersioningOverride) } +func (s *SharedServerSuite) TestWorkflow_Update_Options_OneTimeOverride() { + workflowID := "workflow-" + uuid.NewString() + deploymentName := "deployment-" + uuid.NewString() + buildID := "build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var updateOptionsRequest *workflowservice.UpdateWorkflowExecutionOptionsRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.UpdateWorkflowExecutionOptionsRequest); ok { + lastRequestLock.Lock() + updateOptionsRequest = r + lastRequestLock.Unlock() + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "update-options", + "--address", s.Address(), + "--workflow-id", workflowID, + "--versioning-override-behavior", "one_time", + "--versioning-override-deployment-name", deploymentName, + "--versioning-override-build-id", buildID, + ) + s.NoError(res.Err) + + lastRequestLock.Lock() + req := updateOptionsRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + s.Equal(s.Namespace(), req.GetNamespace()) + s.Equal(workflowID, req.GetWorkflowExecution().GetWorkflowId()) + s.Equal([]string{"versioning_override"}, req.GetUpdateMask().GetPaths()) + override := req.GetWorkflowExecutionOptions().GetVersioningOverride() + require.NotNil(s.T(), override) + oneTime := override.GetOneTime() + require.NotNil(s.T(), oneTime) + s.Equal(deploymentName, oneTime.GetTargetDeploymentVersion().GetDeploymentName()) + s.Equal(buildID, oneTime.GetTargetDeploymentVersion().GetBuildId()) +} + func (s *SharedServerSuite) TestWorkflow_Update_Execute() { workflowUpdateTest{ s: s, diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index bc655419a..9820b2c04 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -4085,6 +4085,17 @@ commands: --versioning-override-build-id YourDeploymentBuildId ``` + or to move the workflow execution to a Worker Deployment Version until + the next Workflow Task completes there, set behavior to `one_time`: + + ``` + temporal workflow update-options \ + --workflow-id YourWorkflowId \ + --versioning-override-behavior one_time \ + --versioning-override-deployment-name YourDeploymentName \ + --versioning-override-build-id YourDeploymentBuildId + ``` + To remove any previous overrides, set the behavior to `unspecified`: @@ -4107,17 +4118,18 @@ commands: enum-values: - unspecified - pinned + - one_time - auto_upgrade - name: versioning-override-deployment-name type: string description: - When overriding to a `pinned` behavior, specifies the Deployment Name of the - version to target. + When overriding to a `pinned` or `one_time` behavior, specifies the + Deployment Name of the version to target. - name: versioning-override-build-id type: string description: - When overriding to a `pinned` behavior, specifies the Build ID of the - version to target. + When overriding to a `pinned` or `one_time` behavior, specifies the + Build ID of the version to target. - name: temporal workflow query summary: Retrieve Workflow Execution state description: | @@ -5364,17 +5376,18 @@ option-sets: required: true enum-values: - pinned + - one_time - auto_upgrade - name: versioning-override-deployment-name type: string description: - When overriding to a `pinned` behavior, specifies the Deployment Name of the - version to target. + When overriding to a `pinned` or `one_time` behavior, specifies the + Deployment Name of the version to target. - name: versioning-override-build-id type: string description: - When overriding to a `pinned` behavior, specifies the Build ID of the - version to target. + When overriding to a `pinned` or `one_time` behavior, specifies the + Build ID of the version to target. - name: activity-reference options: From ef9d821211643afc44160be7579d9a1d4679290a Mon Sep 17 00:00:00 2001 From: Shivam Saraf Date: Sun, 21 Jun 2026 16:07:37 -0400 Subject: [PATCH 2/2] Show one-time override in workflow describe --- .../temporalcli/commands.workflow_view.go | 14 ++- .../commands.workflow_view_test.go | 86 +++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/internal/temporalcli/commands.workflow_view.go b/internal/temporalcli/commands.workflow_view.go index a1487d906..7becc5af3 100644 --- a/internal/temporalcli/commands.workflow_view.go +++ b/internal/temporalcli/commands.workflow_view.go @@ -210,14 +210,20 @@ func (c *TemporalWorkflowDescribeCommand) run(cctx *CommandContext, args []strin overrideBehavior := "" overridePinnedVersionDeploymentName := "" overridePinnedVersionBuildId := "" + overrideTargetVersionDeploymentName := "" + overrideTargetVersionBuildId := "" if vInfo.VersioningOverride != nil { switch vInfo.VersioningOverride.GetOverride().(type) { case *workflow.VersioningOverride_Pinned: - overridePinnedVersionDeploymentName = vInfo.GetVersioningOverride().GetPinned().Version.DeploymentName - overridePinnedVersionBuildId = vInfo.GetVersioningOverride().GetPinned().Version.BuildId + overridePinnedVersionDeploymentName = vInfo.GetVersioningOverride().GetPinned().GetVersion().GetDeploymentName() + overridePinnedVersionBuildId = vInfo.GetVersioningOverride().GetPinned().GetVersion().GetBuildId() overrideBehavior = enums.VERSIONING_BEHAVIOR_PINNED.String() case *workflow.VersioningOverride_AutoUpgrade: overrideBehavior = enums.VERSIONING_BEHAVIOR_AUTO_UPGRADE.String() + case *workflow.VersioningOverride_OneTime: + overrideTargetVersionDeploymentName = vInfo.GetVersioningOverride().GetOneTime().GetTargetDeploymentVersion().GetDeploymentName() + overrideTargetVersionBuildId = vInfo.GetVersioningOverride().GetOneTime().GetTargetDeploymentVersion().GetBuildId() + overrideBehavior = "OneTime" } } _ = cctx.Printer.PrintStructured(struct { @@ -227,6 +233,8 @@ func (c *TemporalWorkflowDescribeCommand) run(cctx *CommandContext, args []strin OverrideBehavior string `cli:",cardOmitEmpty"` OverridePinnedVersionDeploymentName string `cli:",cardOmitEmpty"` OverridePinnedVersionBuildId string `cli:",cardOmitEmpty"` + OverrideTargetVersionDeploymentName string `cli:",cardOmitEmpty"` + OverrideTargetVersionBuildId string `cli:",cardOmitEmpty"` TransitionVersionDeploymentName string `cli:",cardOmitEmpty"` TransitionVersionBuildId string `cli:",cardOmitEmpty"` }{ @@ -236,6 +244,8 @@ func (c *TemporalWorkflowDescribeCommand) run(cctx *CommandContext, args []strin OverrideBehavior: overrideBehavior, OverridePinnedVersionDeploymentName: overridePinnedVersionDeploymentName, OverridePinnedVersionBuildId: overridePinnedVersionBuildId, + OverrideTargetVersionDeploymentName: overrideTargetVersionDeploymentName, + OverrideTargetVersionBuildId: overrideTargetVersionBuildId, TransitionVersionDeploymentName: vInfo.VersionTransition.GetDeploymentVersion().GetDeploymentName(), TransitionVersionBuildId: vInfo.VersionTransition.GetDeploymentVersion().GetBuildId(), }, printer.StructuredOptions{}) diff --git a/internal/temporalcli/commands.workflow_view_test.go b/internal/temporalcli/commands.workflow_view_test.go index d3d13851e..3b8e2f988 100644 --- a/internal/temporalcli/commands.workflow_view_test.go +++ b/internal/temporalcli/commands.workflow_view_test.go @@ -7,19 +7,23 @@ import ( "fmt" "strconv" "strings" + "sync" "time" "github.com/google/uuid" "github.com/nexus-rpc/sdk-go/nexus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/temporalio/cli/internal/temporalcli" commandpb "go.temporal.io/api/command/v1" "go.temporal.io/api/common/v1" + deploymentpb "go.temporal.io/api/deployment/v1" "go.temporal.io/api/enums/v1" historypb "go.temporal.io/api/history/v1" nexuspb "go.temporal.io/api/nexus/v1" "go.temporal.io/api/operatorservice/v1" taskqueuepb "go.temporal.io/api/taskqueue/v1" + workflowpb "go.temporal.io/api/workflow/v1" "go.temporal.io/api/workflowservice/v1" "go.temporal.io/api/workflowservice/v1/workflowservicenexus" "go.temporal.io/sdk/client" @@ -28,6 +32,7 @@ import ( "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" "go.temporal.io/server/common/payloads" + "google.golang.org/grpc" ) func (s *SharedServerSuite) TestWorkflow_Describe_ActivityFailing() { @@ -736,6 +741,87 @@ func (s *SharedServerSuite) TestWorkflow_Describe_Deployment() { s.Nil(versioningInfo.VersioningOverride) } +func (s *SharedServerSuite) TestWorkflow_Describe_OneTimeOverride() { + workflowID := "workflow-" + uuid.NewString() + runID := "run-" + uuid.NewString() + taskQueue := "task-queue-" + uuid.NewString() + baseDeploymentName := "base-deployment-" + uuid.NewString() + baseBuildID := "base-build-" + uuid.NewString() + targetDeploymentName := "target-deployment-" + uuid.NewString() + targetBuildID := "target-build-" + uuid.NewString() + + var lastRequestLock sync.Mutex + var describeRequest *workflowservice.DescribeWorkflowExecutionRequest + s.CommandHarness.Options.AdditionalClientGRPCDialOptions = append( + s.CommandHarness.Options.AdditionalClientGRPCDialOptions, + grpc.WithChainUnaryInterceptor(func( + ctx context.Context, + method string, req, reply any, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, + ) error { + if r, ok := req.(*workflowservice.DescribeWorkflowExecutionRequest); ok { + lastRequestLock.Lock() + describeRequest = r + lastRequestLock.Unlock() + + resp := reply.(*workflowservice.DescribeWorkflowExecutionResponse) + resp.WorkflowExecutionInfo = &workflowpb.WorkflowExecutionInfo{ + Execution: &common.WorkflowExecution{ + WorkflowId: workflowID, + RunId: runID, + }, + Type: &common.WorkflowType{Name: "DevWorkflow"}, + Status: enums.WORKFLOW_EXECUTION_STATUS_RUNNING, + TaskQueue: taskQueue, + VersioningInfo: &workflowpb.WorkflowExecutionVersioningInfo{ + Behavior: enums.VERSIONING_BEHAVIOR_AUTO_UPGRADE, + DeploymentVersion: &deploymentpb.WorkerDeploymentVersion{ + DeploymentName: baseDeploymentName, + BuildId: baseBuildID, + }, + VersioningOverride: &workflowpb.VersioningOverride{ + Override: &workflowpb.VersioningOverride_OneTime{ + OneTime: &workflowpb.VersioningOverride_OneTimeOverride{ + TargetDeploymentVersion: &deploymentpb.WorkerDeploymentVersion{ + DeploymentName: targetDeploymentName, + BuildId: targetBuildID, + }, + }, + }, + }, + }, + } + return nil + } + return invoker(ctx, method, req, reply, cc, opts...) + }), + ) + + res := s.Execute( + "workflow", "describe", + "--address", s.Address(), + "-w", workflowID, + "-r", runID, + ) + require.NoError(s.T(), res.Err) + + lastRequestLock.Lock() + req := describeRequest + lastRequestLock.Unlock() + + require.NotNil(s.T(), req) + s.Equal(workflowID, req.GetExecution().GetWorkflowId()) + s.Equal(runID, req.GetExecution().GetRunId()) + + out := res.Stdout.String() + s.ContainsOnSameLine(out, "Behavior", "AutoUpgrade") + s.ContainsOnSameLine(out, "DeploymentName", baseDeploymentName) + s.ContainsOnSameLine(out, "BuildId", baseBuildID) + s.ContainsOnSameLine(out, "OverrideBehavior", "OneTime") + s.ContainsOnSameLine(out, "OverrideTargetVersionDeploymentName", targetDeploymentName) + s.ContainsOnSameLine(out, "OverrideTargetVersionBuildId", targetBuildID) +} + func (s *SharedServerSuite) TestWorkflow_Describe_NexusOperationAndCallback() { handlerWorkflowID := uuid.NewString() endpointName := validEndpointName(s.T())