diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index 08a68010e..75721f0f8 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -3202,6 +3202,7 @@ func NewTemporalWorkerDeploymentCommand(cctx *CommandContext, parent *TemporalWo s.Command.AddCommand(&NewTemporalWorkerDeploymentManagerIdentityCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalWorkerDeploymentSetCurrentVersionCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalWorkerDeploymentSetRampingVersionCommand(cctx, &s).Command) + s.Command.AddCommand(&NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx, &s).Command) s.Command.AddCommand(&NewTemporalWorkerDeploymentUpdateVersionMetadataCommand(cctx, &s).Command) return &s } @@ -3556,6 +3557,41 @@ func NewTemporalWorkerDeploymentSetRampingVersionCommand(cctx *CommandContext, p return &s } +type TemporalWorkerDeploymentUpdateVersionComputeConfigCommand struct { + Parent *TemporalWorkerDeploymentCommand + Command cobra.Command + DeploymentVersionOptions + AwsLambdaFunctionArn string + AwsLambdaAssumeRoleArn string + AwsLambdaAssumeRoleExternalId string + Remove bool +} + +func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandContext, parent *TemporalWorkerDeploymentCommand) *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand { + var s TemporalWorkerDeploymentUpdateVersionComputeConfigCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "update-version-compute-config [flags]" + s.Command.Short = "Update compute configuration for a Version" + if hasHighlighting { + s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\x1b[0m\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\x1b[0m\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + } else { + s.Command.Long = "Update compute configuration associated with a Worker Deployment\nVersion.\n\nFor example, to update the AWS Lambda function ARN associated with an\nexisting Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-function-arn UpdatedLambdaFunctionARN\n```\n\nTo update the AWS IAM role ARN that is assumed by the serverless worker\nmanager associated with an existing Worker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --aws-lambda-assume-role-arn UpdatedRoleARN\n```\n\nIf --remove is specified, the compute configuration for the Worker\nDeployment Version will be removed:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --remove\n```\n\nIf a Worker Deployment Version with the supplied BuildID does not exist,\nthis command will return an error.\n\nNote: This is an experimental feature and may change in the future." + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.AwsLambdaFunctionArn, "aws-lambda-function-arn", "", "Qualified (contains version suffix) or unqualified AWS Lambda function ARN to invoke when there are no active pollers for task queue targets in the Worker Deployment.") + s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleArn, "aws-lambda-assume-role-arn", "", "AWS IAM role ARN that the Temporal server will assume when invoking the Lambda function that spawns a new Worker in this Worker Deployment Version. Required when --aws-lambda-function-arn is specified.") + s.Command.Flags().StringVar(&s.AwsLambdaAssumeRoleExternalId, "aws-lambda-assume-role-external-id", "", "Temporal server will enforce that the AWS IAM trust policy associated with the AWS IAM role specified in --aws-lambda-assume-role-arn has an aws:ExternalId condition that matches the supplied value. Required when --aws-lambda-function-arn is specified.") + s.Command.Flags().BoolVar(&s.Remove, "remove", false, "Removes any compute configuration associated with this Worker Deployment Version.") + s.DeploymentVersionOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type TemporalWorkerDeploymentUpdateVersionMetadataCommand struct { Parent *TemporalWorkerDeploymentCommand Command cobra.Command diff --git a/internal/temporalcli/commands.worker.deployment.go b/internal/temporalcli/commands.worker.deployment.go index a13d7dfc9..cb4c49130 100644 --- a/internal/temporalcli/commands.worker.deployment.go +++ b/internal/temporalcli/commands.worker.deployment.go @@ -22,6 +22,7 @@ import ( "go.temporal.io/sdk/client" "go.temporal.io/sdk/converter" "go.temporal.io/sdk/worker" + "google.golang.org/protobuf/types/known/fieldmaskpb" ) type versionSummariesRowType struct { @@ -969,6 +970,97 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, return nil } +// awsLambdaProviderDetailsPayload returns the encoded Payload representing AWS +// Lambda compute provider details. +func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) awsLambdaProviderDetailsPayload() (*commonpb.Payload, error) { + // Map keys from temporal-auto-scaled-workers: + // https://github.com/temporalio/temporal-auto-scaled-workers/blob/c4a7e69b6504365d7e5326b0b8e6cd95e3293f96/wci/workflow/compute_provider/aws_lambda.go#L16-L20 + providerDetails := map[string]any{ + "arn": c.AwsLambdaFunctionArn, + } + if c.AwsLambdaAssumeRoleArn != "" { + providerDetails["role"] = c.AwsLambdaAssumeRoleArn + } + if c.AwsLambdaAssumeRoleExternalId != "" { + providerDetails["role_external_id"] = c.AwsLambdaAssumeRoleExternalId + } + err := validateAWSLambdaProviderDetails(providerDetails) + if err != nil { + return nil, err + } + dc := converter.GetDefaultDataConverter() + return dc.ToPayload(&providerDetails) +} + +func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) run(cctx *CommandContext, args []string) error { + cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) + if err != nil { + return err + } + defer cl.Close() + + ns := c.Parent.Parent.Namespace + buildID := c.BuildId + identity := c.Parent.Parent.Identity + deploymentName := c.DeploymentName + requestID := uuid.NewString() + + request := &workflowservice.UpdateWorkerDeploymentVersionComputeConfigRequest{ + Namespace: ns, + DeploymentVersion: &deployment.WorkerDeploymentVersion{ + DeploymentName: deploymentName, + BuildId: buildID, + }, + Identity: identity, + RequestId: requestID, + } + + if c.Remove { + request.RemoveComputeConfigScalingGroups = []string{"default"} + } else { + detailsPayload, err := c.awsLambdaProviderDetailsPayload() + if err != nil { + return err + } + sg := &computepb.ComputeConfigScalingGroup{ + Provider: &computepb.ComputeProvider{ + Type: "aws-lambda", + Details: detailsPayload, + }, + Scaler: &computepb.ComputeScaler{ + // Hard-coded: no-sync is the only supported algorithm + // in temporal-auto-scaled-workers as of 2026-04-01. + Type: "no-sync", + }, + } + updatePaths := []string{ + "provider.details", + } + ccScalingGroups := map[string]*computepb.ComputeConfigScalingGroupUpdate{ + "default": &computepb.ComputeConfigScalingGroupUpdate{ + ScalingGroup: sg, + UpdateMask: &fieldmaskpb.FieldMask{ + Paths: updatePaths, + }, + }, + } + request.ComputeConfigScalingGroups = ccScalingGroups + + } + + _, err = cl.WorkflowService().UpdateWorkerDeploymentVersionComputeConfig(cctx, request) + if err != nil { + return fmt.Errorf("error updating worker deployment version compute config: %w", err) + } + + if c.Remove { + cctx.Printer.Println("Successfully removed worker deployment version compute config") + } else { + cctx.Printer.Println("Successfully updated worker deployment version compute config") + } + return nil +} + func (c *TemporalWorkerDeploymentDeleteVersionCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) if err != nil { diff --git a/internal/temporalcli/commands.worker.deployment_test.go b/internal/temporalcli/commands.worker.deployment_test.go index 8b97854e3..7c6400179 100644 --- a/internal/temporalcli/commands.worker.deployment_test.go +++ b/internal/temporalcli/commands.worker.deployment_test.go @@ -1292,6 +1292,32 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { ) s.Error(res.Err) s.ErrorContains(res.Err, "missing required AWS Lambda provider detail: role") + + // Attempting to update the compute config for a non-existent WDV + // should fail. + nonExistingBuildID := "non-existing" + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", nonExistingBuildID, + "--aws-lambda-function-arn", invokeARN, + "--aws-lambda-assume-role-arn", assumeRoleARN, + "--aws-lambda-assume-role-external-id", assumeRoleExternalID, + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "build ID 'non-existing' not found") + + // Same for attempting to remove the compute config for a non-existent WDV. + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", nonExistingBuildID, + "--remove", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "build ID 'non-existing' not found") } // TODO(jaypipes): Enable this test when we have a way of ensuring AWS resource @@ -1384,4 +1410,30 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_LambdaComputeConfi jsonOut := jsonDeploymentVersionInfoType{} s.NoError(json.Unmarshal(res.Stdout.Bytes(), &jsonOut)) s.NotNil(jsonOut.ComputeConfig, "ComputeConfig should not be nil.") + + // We should be able to update the compute config. + invokeARN2 := "arn:aws:lambda:us-east-1:123456789012:function:MyExampleFunction:2" + assumeRoleARN2 := "arn:aws:iam::123456789012:role/MyServiceRole2" + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + "--aws-lambda-function-arn", invokeARN2, + "--aws-lambda-assume-role-arn", assumeRoleARN2, + "--aws-lambda-assume-role-external-id", assumeRoleExternalID, + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Successfully updated worker deployment version compute config") + + // As well as remove the compute config. + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", computeConfigBuildID, + "--remove", + ) + s.NoError(res.Err) + s.Contains(res.Stdout.String(), "Successfully removed worker deployment version compute config") } diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 32af0d955..0496bd3c4 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -979,6 +979,7 @@ commands: - worker deployment set-ramping-version - worker deployment delete-version - worker deployment update-version-metadata + - worker deployment update-version-compute-config - worker deployment manager-identity - name: temporal worker deployment create @@ -1319,6 +1320,72 @@ commands: Keys of entries to be deleted from metadata. Can be passed multiple times. + - name: temporal worker deployment update-version-compute-config + summary: Update compute configuration for a Version + description: | + Update compute configuration associated with a Worker Deployment + Version. + + For example, to update the AWS Lambda function ARN associated with an + existing Worker Deployment Version: + + ``` + temporal worker deployment update-version-compute-config \ + --deployment-name YourDeploymentName --build-id YourBuildID \ + --aws-lambda-function-arn UpdatedLambdaFunctionARN + ``` + + To update the AWS IAM role ARN that is assumed by the serverless worker + manager associated with an existing Worker Deployment Version: + + ``` + temporal worker deployment update-version-compute-config \ + --deployment-name YourDeploymentName --build-id YourBuildID \ + --aws-lambda-assume-role-arn UpdatedRoleARN + ``` + + If --remove is specified, the compute configuration for the Worker + Deployment Version will be removed: + + ``` + temporal worker deployment update-version-compute-config \ + --deployment-name YourDeploymentName --build-id YourBuildID \ + --remove + ``` + + If a Worker Deployment Version with the supplied BuildID does not exist, + this command will return an error. + + Note: This is an experimental feature and may change in the future. + option-sets: + - deployment-version + options: + - name: aws-lambda-function-arn + type: string + description: | + Qualified (contains version suffix) or unqualified AWS Lambda + function ARN to invoke when there are no active pollers for task + queue targets in the Worker Deployment. + - name: aws-lambda-assume-role-arn + type: string + description: | + AWS IAM role ARN that the Temporal server will assume when invoking + the Lambda function that spawns a new Worker in this Worker + Deployment Version. Required when --aws-lambda-function-arn is + specified. + - name: aws-lambda-assume-role-external-id + type: string + description: | + Temporal server will enforce that the AWS IAM trust policy associated + with the AWS IAM role specified in --aws-lambda-assume-role-arn has + an aws:ExternalId condition that matches the supplied value. Required + when --aws-lambda-function-arn is specified. + - name: remove + type: bool + description: | + Removes any compute configuration associated with this Worker + Deployment Version. + - name: temporal worker deployment manager-identity summary: Manager Identity commands change the `ManagerIdentity` of a Worker Deployment description: |