diff --git a/internal/temporalcli/commands.gen.go b/internal/temporalcli/commands.gen.go index c27b31630..32caa0538 100644 --- a/internal/temporalcli/commands.gen.go +++ b/internal/temporalcli/commands.gen.go @@ -3646,6 +3646,10 @@ type TemporalWorkerDeploymentCreateVersionCommand struct { GcpCloudRunRegion string GcpCloudRunWorkerPool string GcpCloudRunServiceAccount string + GcpCloudRunMinInstances int + GcpCloudRunMaxInstances int + GcpCloudRunInitialInstances int + GcpCloudRunUtilizationTarget float32 } func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, parent *TemporalWorkerDeploymentCommand) *TemporalWorkerDeploymentCreateVersionCommand { @@ -3655,9 +3659,9 @@ func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, paren s.Command.Use = "create-version [flags]" s.Command.Short = "Create a new Worker Deployment Version" if hasHighlighting { - s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n\x1b[1mtemporal worker deployment create-version [options]\x1b[0m\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\x1b[0m\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n\x1b[1mtemporal worker deployment create-version [options]\x1b[0m\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\x1b[0m\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n\x1b[1mtemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\x1b[0m\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." } else { - s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n```\ntemporal worker deployment create-version [options]\n```\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\n```\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com\n```\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\n\nNote: This is an experimental feature and may change in the future." + s.Command.Long = "\nCreate a new Worker Deployment Version:\n\n```\ntemporal worker deployment create-version [options]\n```\n\nConfigure a Worker Deployment Version's compute configuration as needed.\nFor example, pass compute provider information for an AWS Lambda function\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --aws-lambda-function-arn LambdaFunctionARN \\\n --aws-lambda-assume-role-arn LambdaAssumeRoleARN \\\n --aws-lambda-assume-role-external-id LambdaAssumeRoleExternalID\n```\n\nOr pass compute provider information for a GCP Cloud Run worker pool\nthat spawns a Worker in the Worker Deployment:\n\n```\ntemporal worker deployment create-version \\\n --namespace YourNamespaceName \\\n --deployment-name YourDeploymentName \\\n --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool YourWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\n```\n\nIf a Worker Deployment Version with the supplied BuildID already exists,\nthis command will return an error.\n\nReturns an error if all compute configuration fields are empty.\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.") @@ -3667,6 +3671,10 @@ func NewTemporalWorkerDeploymentCreateVersionCommand(cctx *CommandContext, paren s.Command.Flags().StringVar(&s.GcpCloudRunRegion, "gcp-cloud-run-region", "", "Region of the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunWorkerPool, "gcp-cloud-run-worker-pool", "", "GCP Cloud Run worker pool name to scale when there are no active pollers for task queue targets in the Worker Deployment.") s.Command.Flags().StringVar(&s.GcpCloudRunServiceAccount, "gcp-cloud-run-service-account", "", "Customer GCP service account the Temporal server impersonates to manage the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") + s.Command.Flags().IntVar(&s.GcpCloudRunMinInstances, "gcp-cloud-run-min-instances", 0, "Minimum number of Cloud Run worker pool instances the scaler will maintain. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunMaxInstances, "gcp-cloud-run-max-instances", 0, "Maximum number of Cloud Run worker pool instances the scaler may scale up to. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Defaults to 30 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunInitialInstances, "gcp-cloud-run-initial-instances", 0, "Number of Cloud Run worker pool instances the scaler starts with. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together, and this value must be between the min and max (inclusive). Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().Float32Var(&s.GcpCloudRunUtilizationTarget, "gcp-cloud-run-utilization-target", 0, "Target average worker utilization the scaler aims for, as a fraction in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Lower values keep more spare capacity per worker. Defaults to 0.8 when unset. Only valid with --gcp-cloud-run-worker-pool.") s.DeploymentVersionOptions.BuildFlags(s.Command.Flags()) s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { @@ -3977,6 +3985,10 @@ type TemporalWorkerDeploymentUpdateVersionComputeConfigCommand struct { GcpCloudRunRegion string GcpCloudRunWorkerPool string GcpCloudRunServiceAccount string + GcpCloudRunMinInstances int + GcpCloudRunMaxInstances int + GcpCloudRunInitialInstances int + GcpCloudRunUtilizationTarget float32 Remove bool } @@ -3987,9 +3999,9 @@ func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandC 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\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com\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." + 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\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n\x1b[1m temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\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\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com\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.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\nTo update the GCP Cloud Run worker pool associated with an existing\nWorker Deployment Version:\n\n```\n temporal worker deployment update-version-compute-config \\\n --deployment-name YourDeploymentName --build-id YourBuildID \\\n --gcp-cloud-run-project YourGCPProject \\\n --gcp-cloud-run-region us-central1 \\\n --gcp-cloud-run-worker-pool UpdatedWorkerPool \\\n --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \\\n --gcp-cloud-run-min-instances 1 \\\n --gcp-cloud-run-max-instances 3 \\\n --gcp-cloud-run-initial-instances 1 \\\n --gcp-cloud-run-utilization-target 0.75\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.") @@ -3999,6 +4011,10 @@ func NewTemporalWorkerDeploymentUpdateVersionComputeConfigCommand(cctx *CommandC s.Command.Flags().StringVar(&s.GcpCloudRunRegion, "gcp-cloud-run-region", "", "Region of the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") s.Command.Flags().StringVar(&s.GcpCloudRunWorkerPool, "gcp-cloud-run-worker-pool", "", "GCP Cloud Run worker pool name to scale when there are no active pollers for task queue targets in the Worker Deployment.") s.Command.Flags().StringVar(&s.GcpCloudRunServiceAccount, "gcp-cloud-run-service-account", "", "Customer GCP service account the Temporal server impersonates to manage the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified.") + s.Command.Flags().IntVar(&s.GcpCloudRunMinInstances, "gcp-cloud-run-min-instances", 0, "Minimum number of Cloud Run worker pool instances the scaler will maintain. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunMaxInstances, "gcp-cloud-run-max-instances", 0, "Maximum number of Cloud Run worker pool instances the scaler may scale up to. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Defaults to 30 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().IntVar(&s.GcpCloudRunInitialInstances, "gcp-cloud-run-initial-instances", 0, "Number of Cloud Run worker pool instances the scaler starts with. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together, and this value must be between the min and max (inclusive). Defaults to 0 when unset. Only valid with --gcp-cloud-run-worker-pool.") + s.Command.Flags().Float32Var(&s.GcpCloudRunUtilizationTarget, "gcp-cloud-run-utilization-target", 0, "Target average worker utilization the scaler aims for, as a fraction in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must all be set together. Lower values keep more spare capacity per worker. Defaults to 0.8 when unset. Only valid with --gcp-cloud-run-worker-pool.") 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) { diff --git a/internal/temporalcli/commands.worker.deployment.go b/internal/temporalcli/commands.worker.deployment.go index a32d749ee..7da61e7d9 100644 --- a/internal/temporalcli/commands.worker.deployment.go +++ b/internal/temporalcli/commands.worker.deployment.go @@ -136,7 +136,11 @@ type formattedComputeConfigProvider struct { } type formattedComputeConfigScaler struct { - Type string `json:"type"` + Type string `json:"type"` + MinInstances *int64 `json:"minInstances,omitempty"` + MaxInstances *int64 `json:"maxInstances,omitempty"` + InitialInstances *int64 `json:"initialInstances,omitempty"` + UtilizationTarget *float64 `json:"utilizationTarget,omitempty"` } func drainageStatusToStr(drainage client.WorkerDeploymentVersionDrainageStatus) (string, error) { @@ -380,6 +384,84 @@ func formatDrainageInfoProto(drainageInfo *deploymentpb.VersionDrainageInfo) (fo }, nil } +// Config keys for the WCI rate-based scaler, shared by the build path +// (gcpCloudRunScalerDetails) and the read path (decodeScalerSettings) so the two +// never drift. Mirrors: +// https://github.com/temporalio/temporal-auto-scaled-workers/blob/main/wci/workflow/scaling_algorithm/rate_based.go +const ( + scalerKeyMinCount = "min_count" + scalerKeyMaxCount = "max_count" + scalerKeyInitialCount = "initial_count" + scalerKeyUtilizationTarget = "utilization_target" +) + +// scalerCountFromMap reads an integer worker-count value from a decoded scaler +// details map. The default data converter round-trips JSON numbers as float64, +// so that case is handled alongside the native int types. +func scalerCountFromMap(m map[string]any, key string) (int64, bool) { + switch n := m[key].(type) { + case float64: + return int64(n), true + case int64: + return n, true + case int: + return int64(n), true + default: + return 0, false + } +} + +// scalerFloatFromMap reads a fractional value (e.g. utilization_target) from a +// decoded scaler details map. JSON numbers decode as float64. +func scalerFloatFromMap(m map[string]any, key string) (float64, bool) { + switch n := m[key].(type) { + case float64: + return n, true + case float32: + return float64(n), true + default: + return 0, false + } +} + +// scalerSettings holds the rate-based scaler settings surfaced for display. Each +// field is nil when the corresponding key is absent from the scaler details. +type scalerSettings struct { + minInstances *int64 + maxInstances *int64 + initialInstances *int64 + utilizationTarget *float64 +} + +// decodeScalerSettings extracts the rate-based scaler settings from a +// ComputeScaler's details payload for display. Best-effort: returns zero-value +// (all-nil) settings when the scaler, its details, or a key is absent, or when +// the payload cannot be decoded, so read paths never fail on an unexpected shape. +func decodeScalerSettings(s *computepb.ComputeScaler) scalerSettings { + var out scalerSettings + details := s.GetDetails() + if details == nil { + return out + } + var m map[string]any + if err := converter.GetDefaultDataConverter().FromPayload(details, &m); err != nil { + return out + } + if v, ok := scalerCountFromMap(m, scalerKeyMinCount); ok { + out.minInstances = &v + } + if v, ok := scalerCountFromMap(m, scalerKeyMaxCount); ok { + out.maxInstances = &v + } + if v, ok := scalerCountFromMap(m, scalerKeyInitialCount); ok { + out.initialInstances = &v + } + if v, ok := scalerFloatFromMap(m, scalerKeyUtilizationTarget); ok { + out.utilizationTarget = &v + } + return out +} + func formatComputeConfigProto(cc *computepb.ComputeConfig) *formattedComputeConfig { if cc == nil { return nil @@ -401,10 +483,14 @@ func formatComputeConfigProto(cc *computepb.ComputeConfig) *formattedComputeConf Type: p.GetType(), } } - if p != nil { - sg.Scaler = &formattedComputeConfigScaler{ - Type: s.GetType(), - } + if s != nil { + fs := &formattedComputeConfigScaler{Type: s.GetType()} + set := decodeScalerSettings(s) + fs.MinInstances = set.minInstances + fs.MaxInstances = set.maxInstances + fs.InitialInstances = set.initialInstances + fs.UtilizationTarget = set.utilizationTarget + sg.Scaler = fs } sgs[name] = sg } @@ -446,18 +532,37 @@ func computeConfigSummaryStr(cc *computepb.ComputeConfig) string { if cc == nil { return "" } - providers := []string{} + summaries := []string{} for _, sg := range cc.GetScalingGroups() { p := sg.GetProvider() if p == nil { continue } - pt := p.GetType() - if !slices.Contains(providers, pt) { - providers = append(providers, pt) + summary := p.GetType() + // Append whichever scaler settings are present so the one-line summary + // reflects the configured limits (ordered min, initial, max, utilization). + set := decodeScalerSettings(sg.GetScaler()) + parts := []string{} + if set.minInstances != nil { + parts = append(parts, fmt.Sprintf("min %d", *set.minInstances)) + } + if set.initialInstances != nil { + parts = append(parts, fmt.Sprintf("initial %d", *set.initialInstances)) + } + if set.maxInstances != nil { + parts = append(parts, fmt.Sprintf("max %d", *set.maxInstances)) + } + if set.utilizationTarget != nil { + parts = append(parts, fmt.Sprintf("utilization %g", *set.utilizationTarget)) + } + if len(parts) > 0 { + summary = fmt.Sprintf("%s (%s)", summary, strings.Join(parts, ", ")) + } + if !slices.Contains(summaries, summary) { + summaries = append(summaries, summary) } } - return strings.Join(providers, ",") + return strings.Join(summaries, ",") } // printWorkerDeploymentVersionInfoProto prints worker deployment version info from proto types. @@ -1015,6 +1120,99 @@ func scalerTypeForProvider(providerType string) (string, error) { return "", fmt.Errorf("no scaler mapping for compute provider %q", providerType) } +// gcpCloudRunScalerDetails builds the ComputeScaler.Details payload from the GCP +// Cloud Run scaling flags. It carries two independent groups of rate-based scaler +// settings: +// - the instance-count group (min_count/max_count/initial_count), which is +// all-or-none and must satisfy min <= initial <= max, and +// - utilization_target, a standalone fraction in (0, 1]. +// +// The *Set booleans come from cobra's Flags().Changed, so an omitted flag stays +// distinct from an explicit 0. Returns a nil payload when nothing is set, leaving +// WCI's defaults (min 0, max 30, initial 0, utilization_target 0.8) in effect. +// Every setting is GCP Cloud Run only; any use with another provider is rejected. +// Config keys mirror the WCI rate-based scaler: +// https://github.com/temporalio/temporal-auto-scaled-workers/blob/main/wci/workflow/scaling_algorithm/rate_based.go +// gcpScalerFlags holds the GCP Cloud Run scaling flag values together with +// whether each was actually set (from cobra's Flags().Changed). Pairing each +// value with its Set bool keeps an omitted flag distinct from an explicit 0 and +// removes the positional-argument risk of passing the raw values around. +type gcpScalerFlags struct { + min int + minSet bool + max int + maxSet bool + initial int + initialSet bool + utilization float32 + utilizationSet bool +} + +func (f gcpScalerFlags) anySet() bool { + return f.minSet || f.maxSet || f.initialSet || f.utilizationSet +} + +func (f gcpScalerFlags) allSet() bool { + return f.minSet && f.maxSet && f.initialSet && f.utilizationSet +} + +// gcpCloudRunScalerDetails builds the ComputeScaler.Details payload from the GCP +// Cloud Run scaling flags (min/max/initial instance counts and utilization +// target). The four form a single all-or-none group: setting any one requires +// all four. That keeps the min<=initial<=max relationship self-contained and +// avoids comparing an explicit value against WCI's default for an unset sibling. +// Returns a nil payload when nothing is set, leaving WCI's defaults (min 0, +// max 30, initial 0, utilization_target 0.8) in effect. Every setting is GCP +// Cloud Run only; any use with another provider is rejected. +func gcpCloudRunScalerDetails(providerType string, f gcpScalerFlags) (*commonpb.Payload, error) { + if !f.anySet() { + return nil, nil + } + // These are GCP Cloud Run (rate-based) knobs only. Reject on any use with + // another provider so the GCP-only nature is explicit, regardless of value. + if providerType != "gcp-cloud-run" { + return nil, fmt.Errorf("the Cloud Run scaling flags are only valid with --gcp-cloud-run-worker-pool") + } + if !f.allSet() { + return nil, fmt.Errorf("--gcp-cloud-run-min-instances, --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and --gcp-cloud-run-utilization-target must be set together") + } + if f.min < 0 { + return nil, fmt.Errorf("--gcp-cloud-run-min-instances cannot be negative") + } + if f.max < 1 { + return nil, fmt.Errorf("--gcp-cloud-run-max-instances must be at least 1") + } + if f.min > f.max { + return nil, fmt.Errorf("--gcp-cloud-run-min-instances cannot exceed --gcp-cloud-run-max-instances") + } + if f.initial < f.min || f.initial > f.max { + return nil, fmt.Errorf("--gcp-cloud-run-initial-instances must be between --gcp-cloud-run-min-instances and --gcp-cloud-run-max-instances") + } + // The (0, 1] range is intrinsic to utilization_target's meaning, not a + // default that could drift, so mirroring the check here is safe. + if f.utilization <= 0 || f.utilization > 1 { + return nil, fmt.Errorf("--gcp-cloud-run-utilization-target must be greater than 0 and at most 1") + } + details := map[string]any{ + scalerKeyMinCount: f.min, + scalerKeyMaxCount: f.max, + scalerKeyInitialCount: f.initial, + scalerKeyUtilizationTarget: f.utilization, + } + dc := converter.GetDefaultDataConverter() + return dc.ToPayload(&details) +} + +func (c *TemporalWorkerDeploymentCreateVersionCommand) gcpScalerFlags() gcpScalerFlags { + f := c.Command.Flags() + return gcpScalerFlags{ + min: c.GcpCloudRunMinInstances, minSet: f.Changed("gcp-cloud-run-min-instances"), + max: c.GcpCloudRunMaxInstances, maxSet: f.Changed("gcp-cloud-run-max-instances"), + initial: c.GcpCloudRunInitialInstances, initialSet: f.Changed("gcp-cloud-run-initial-instances"), + utilization: c.GcpCloudRunUtilizationTarget, utilizationSet: f.Changed("gcp-cloud-run-utilization-target"), + } +} + func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) if err != nil { @@ -1048,6 +1246,10 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, if err != nil { return err } + scalerDetails, err := gcpCloudRunScalerDetails(providerType, c.gcpScalerFlags()) + if err != nil { + return err + } cc := &computepb.ComputeConfig{ ScalingGroups: map[string]*computepb.ComputeConfigScalingGroup{ "default": { @@ -1056,7 +1258,8 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, Details: detailsPayload, }, Scaler: &computepb.ComputeScaler{ - Type: scalerType, + Type: scalerType, + Details: scalerDetails, }, }, }, @@ -1081,6 +1284,16 @@ func (c *TemporalWorkerDeploymentCreateVersionCommand) run(cctx *CommandContext, return nil } +func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) gcpScalerFlags() gcpScalerFlags { + f := c.Command.Flags() + return gcpScalerFlags{ + min: c.GcpCloudRunMinInstances, minSet: f.Changed("gcp-cloud-run-min-instances"), + max: c.GcpCloudRunMaxInstances, maxSet: f.Changed("gcp-cloud-run-max-instances"), + initial: c.GcpCloudRunInitialInstances, initialSet: f.Changed("gcp-cloud-run-initial-instances"), + utilization: c.GcpCloudRunUtilizationTarget, utilizationSet: f.Changed("gcp-cloud-run-utilization-target"), + } +} + func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) run(cctx *CommandContext, args []string) error { cl, err := dialClient(cctx, &c.Parent.Parent.ClientOptions) if err != nil { @@ -1130,13 +1343,18 @@ func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) run(cctx *Co if err != nil { return err } + scalerDetails, err := gcpCloudRunScalerDetails(providerType, c.gcpScalerFlags()) + if err != nil { + return err + } sg := &computepb.ComputeConfigScalingGroup{ Provider: &computepb.ComputeProvider{ Type: providerType, Details: detailsPayload, }, Scaler: &computepb.ComputeScaler{ - Type: scalerType, + Type: scalerType, + Details: scalerDetails, }, } updatePaths := []string{ @@ -1144,6 +1362,12 @@ func (c *TemporalWorkerDeploymentUpdateVersionComputeConfigCommand) run(cctx *Co "provider.details", "scaler.type", } + // Only touch scaler.details when the user supplied instance bounds; + // otherwise leave any existing scaler config untouched rather than + // clearing it. + if scalerDetails != nil { + updatePaths = append(updatePaths, "scaler.details") + } ccScalingGroups := map[string]*computepb.ComputeConfigScalingGroupUpdate{ "default": &computepb.ComputeConfigScalingGroupUpdate{ ScalingGroup: sg, diff --git a/internal/temporalcli/commands.worker.deployment.internal_test.go b/internal/temporalcli/commands.worker.deployment.internal_test.go index 9f09f9bb9..9db39ad77 100644 --- a/internal/temporalcli/commands.worker.deployment.internal_test.go +++ b/internal/temporalcli/commands.worker.deployment.internal_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/stretchr/testify/require" + computepb "go.temporal.io/api/compute/v1" + "go.temporal.io/sdk/converter" ) func TestScalerTypeForProvider(t *testing.T) { @@ -40,3 +42,113 @@ func TestScalerTypeByProviderCoversAllProviders(t *testing.T) { require.Truef(t, ok, "provider %q has no scaler mapping", providerType) } } + +func TestGCPCloudRunScalerDetails(t *testing.T) { + // Nothing set -> nil payload so WCI defaults apply (min 0, max 30, + // initial 0, utilization_target 0.8). + p, err := gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{}) + require.NoError(t, err) + require.Nil(t, p) + + // Any scaler flag alongside a non-GCP provider is rejected. Covers both an + // instance-count flag and the utilization flag. + _, err = gcpCloudRunScalerDetails("aws-lambda", gcpScalerFlags{minSet: true}) + require.ErrorContains(t, err, "only valid with --gcp-cloud-run-worker-pool") + _, err = gcpCloudRunScalerDetails("aws-lambda", gcpScalerFlags{utilization: 0.5, utilizationSet: true}) + require.ErrorContains(t, err, "only valid with --gcp-cloud-run-worker-pool") + + // All four settings are one all-or-none group: any partial set is rejected. + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 5, minSet: true}) + require.ErrorContains(t, err, "must be set together") + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{utilization: 0.5, utilizationSet: true}) // utilization alone + require.ErrorContains(t, err, "must be set together") + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{ // trio set, utilization missing + min: 1, minSet: true, max: 3, maxSet: true, initial: 2, initialSet: true, + }) + require.ErrorContains(t, err, "must be set together") + + // Value checks, with all four set so the group check passes first. + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: -1, minSet: true, max: 3, maxSet: true, initial: 0, initialSet: true, utilization: 0.5, utilizationSet: true}) + require.ErrorContains(t, err, "cannot be negative") + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 0, minSet: true, max: 0, maxSet: true, initial: 0, initialSet: true, utilization: 0.5, utilizationSet: true}) + require.ErrorContains(t, err, "--gcp-cloud-run-max-instances must be at least 1") + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 5, minSet: true, max: 3, maxSet: true, initial: 4, initialSet: true, utilization: 0.5, utilizationSet: true}) + require.ErrorContains(t, err, "cannot exceed") + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 2, minSet: true, max: 10, maxSet: true, initial: 15, initialSet: true, utilization: 0.5, utilizationSet: true}) + require.ErrorContains(t, err, "must be between") + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 0, minSet: true, max: 10, maxSet: true, initial: 5, initialSet: true, utilization: 0, utilizationSet: true}) + require.ErrorContains(t, err, "must be greater than 0 and at most 1") + _, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 0, minSet: true, max: 10, maxSet: true, initial: 5, initialSet: true, utilization: 1.5, utilizationSet: true}) + require.ErrorContains(t, err, "must be greater than 0 and at most 1") + + // All four set and valid -> payload decodes to the WCI rate-based keys. + // JSON round-trips numbers as float64; WCI handles that on read. + p, err = gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{min: 1, minSet: true, max: 10, maxSet: true, initial: 5, initialSet: true, utilization: 0.5, utilizationSet: true}) + require.NoError(t, err) + require.NotNil(t, p) + var details map[string]any + require.NoError(t, converter.GetDefaultDataConverter().FromPayload(p, &details)) + require.Equal(t, float64(1), details[scalerKeyMinCount]) + require.Equal(t, float64(10), details[scalerKeyMaxCount]) + require.Equal(t, float64(5), details[scalerKeyInitialCount]) + require.Equal(t, float64(0.5), details[scalerKeyUtilizationTarget]) +} + +func TestFormatComputeConfigProto_ScalerBounds(t *testing.T) { + // Build the scaler details the same way the run methods do. + scalerDetails, err := gcpCloudRunScalerDetails("gcp-cloud-run", gcpScalerFlags{ + min: 0, minSet: true, + max: 10, maxSet: true, + initial: 5, initialSet: true, + utilization: 0.75, utilizationSet: true, + }) + require.NoError(t, err) + require.NotNil(t, scalerDetails) + + cc := &computepb.ComputeConfig{ + ScalingGroups: map[string]*computepb.ComputeConfigScalingGroup{ + "default": { + Provider: &computepb.ComputeProvider{Type: "gcp-cloud-run"}, + Scaler: &computepb.ComputeScaler{Type: "rate-based", Details: scalerDetails}, + }, + }, + } + + // JSON/structured path surfaces min, max, initial, and utilization. + formatted := formatComputeConfigProto(cc) + require.NotNil(t, formatted) + sg, ok := formatted.ScalingGroups["default"] + require.True(t, ok) + require.NotNil(t, sg.Scaler) + require.Equal(t, "rate-based", sg.Scaler.Type) + require.NotNil(t, sg.Scaler.MinInstances) + require.NotNil(t, sg.Scaler.MaxInstances) + require.NotNil(t, sg.Scaler.InitialInstances) + require.NotNil(t, sg.Scaler.UtilizationTarget) + require.Equal(t, int64(0), *sg.Scaler.MinInstances) + require.Equal(t, int64(10), *sg.Scaler.MaxInstances) + require.Equal(t, int64(5), *sg.Scaler.InitialInstances) + require.Equal(t, float64(0.75), *sg.Scaler.UtilizationTarget) + + // Human-readable summary reflects the settings (min, initial, max, utilization). + require.Equal(t, "gcp-cloud-run (min 0, initial 5, max 10, utilization 0.75)", computeConfigSummaryStr(cc)) + + // Without scaler details, the settings are nil and the summary is just the + // provider (guards against printing zeroed-out values). + ccNoBounds := &computepb.ComputeConfig{ + ScalingGroups: map[string]*computepb.ComputeConfigScalingGroup{ + "default": { + Provider: &computepb.ComputeProvider{Type: "gcp-cloud-run"}, + Scaler: &computepb.ComputeScaler{Type: "rate-based"}, + }, + }, + } + formatted = formatComputeConfigProto(ccNoBounds) + sg = formatted.ScalingGroups["default"] + require.NotNil(t, sg.Scaler) + require.Nil(t, sg.Scaler.MinInstances) + require.Nil(t, sg.Scaler.MaxInstances) + require.Nil(t, sg.Scaler.InitialInstances) + require.Nil(t, sg.Scaler.UtilizationTarget) + require.Equal(t, "gcp-cloud-run", computeConfigSummaryStr(ccNoBounds)) +} diff --git a/internal/temporalcli/commands.worker.deployment_test.go b/internal/temporalcli/commands.worker.deployment_test.go index d7642ee0f..67f2b64dc 100644 --- a/internal/temporalcli/commands.worker.deployment_test.go +++ b/internal/temporalcli/commands.worker.deployment_test.go @@ -1405,6 +1405,155 @@ func (s *SharedServerSuite) TestCreateWorkerDeploymentVersion_Errors() { ) s.Error(res.Err) s.ErrorContains(res.Err, "--remove cannot be combined with") + + // Instance settings are GCP Cloud Run (rate-based) knobs only; setting them + // alongside a (fully specified) AWS Lambda provider is rejected client-side. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--aws-lambda-function-arn", invokeARN, + "--aws-lambda-assume-role-arn", assumeRoleARN, + "--aws-lambda-assume-role-external-id", assumeRoleExternalID, + "--gcp-cloud-run-min-instances", "1", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "only valid with --gcp-cloud-run-worker-pool") + + // A lone flag is rejected: all four scaling settings must be set together. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "32", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "must be set together") + + // The instance counts without utilization-target are also rejected: + // utilization-target is part of the same all-or-none group. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "1", + "--gcp-cloud-run-max-instances", "3", + "--gcp-cloud-run-initial-instances", "2", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "must be set together") + + // min cannot exceed max (all four set so the group check passes first). + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "5", + "--gcp-cloud-run-max-instances", "3", + "--gcp-cloud-run-initial-instances", "4", + "--gcp-cloud-run-utilization-target", "0.5", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "cannot exceed") + + // An explicit max of 0 is rejected (WCI requires at least 1). + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "0", + "--gcp-cloud-run-max-instances", "0", + "--gcp-cloud-run-initial-instances", "0", + "--gcp-cloud-run-utilization-target", "0.5", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "--gcp-cloud-run-max-instances must be at least 1") + + // initial outside [min, max] is rejected. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "2", + "--gcp-cloud-run-max-instances", "10", + "--gcp-cloud-run-initial-instances", "15", + "--gcp-cloud-run-utilization-target", "0.5", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "must be between") + + // utilization-target must be a fraction in (0, 1] (all four set so the + // group check passes first). + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--gcp-cloud-run-project", "my-gcp-project", + "--gcp-cloud-run-region", "us-central1", + "--gcp-cloud-run-worker-pool", "my-worker-pool", + "--gcp-cloud-run-service-account", "customer-sa@my-gcp-project.iam.gserviceaccount.com", + "--gcp-cloud-run-min-instances", "0", + "--gcp-cloud-run-max-instances", "10", + "--gcp-cloud-run-initial-instances", "5", + "--gcp-cloud-run-utilization-target", "1.5", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "must be greater than 0 and at most 1") + + // utilization-target is GCP Cloud Run only; rejected alongside AWS Lambda. + res = s.Execute( + "worker", "deployment", "create-version", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", uuid.NewString(), + "--aws-lambda-function-arn", invokeARN, + "--aws-lambda-assume-role-arn", assumeRoleARN, + "--aws-lambda-assume-role-external-id", assumeRoleExternalID, + "--gcp-cloud-run-utilization-target", "0.75", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "only valid with --gcp-cloud-run-worker-pool") + + // Instance settings with AWS Lambda are also rejected on update, before the RPC. + res = s.Execute( + "worker", "deployment", "update-version-compute-config", + "--address", s.Address(), + "--deployment-name", deploymentName, + "--build-id", lazyCreatedBuildID, + "--aws-lambda-function-arn", invokeARN, + "--aws-lambda-assume-role-arn", assumeRoleARN, + "--aws-lambda-assume-role-external-id", assumeRoleExternalID, + "--gcp-cloud-run-max-instances", "3", + ) + s.Error(res.Err) + s.ErrorContains(res.Err, "only valid with --gcp-cloud-run-worker-pool") } // TODO(jaypipes): Enable this test when we have a way of ensuring AWS resource diff --git a/internal/temporalcli/commands.yaml b/internal/temporalcli/commands.yaml index 9700c1547..55df76e8c 100644 --- a/internal/temporalcli/commands.yaml +++ b/internal/temporalcli/commands.yaml @@ -1104,7 +1104,11 @@ commands: --gcp-cloud-run-project YourGCPProject \ --gcp-cloud-run-region us-central1 \ --gcp-cloud-run-worker-pool YourWorkerPool \ - --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com + --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \ + --gcp-cloud-run-min-instances 1 \ + --gcp-cloud-run-max-instances 3 \ + --gcp-cloud-run-initial-instances 1 \ + --gcp-cloud-run-utilization-target 0.75 ``` If a Worker Deployment Version with the supplied BuildID already exists, @@ -1157,6 +1161,40 @@ commands: Customer GCP service account the Temporal server impersonates to manage the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified. + - name: gcp-cloud-run-min-instances + type: int + description: | + Minimum number of Cloud Run worker pool instances the scaler will + maintain. Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and + --gcp-cloud-run-utilization-target must all be set together. Defaults + to 0 when unset. Only valid with --gcp-cloud-run-worker-pool. + - name: gcp-cloud-run-max-instances + type: int + description: | + Maximum number of Cloud Run worker pool instances the scaler may + scale up to. Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and + --gcp-cloud-run-utilization-target must all be set together. Defaults + to 30 when unset. Only valid with --gcp-cloud-run-worker-pool. + - name: gcp-cloud-run-initial-instances + type: int + description: | + Number of Cloud Run worker pool instances the scaler starts with. + Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and + --gcp-cloud-run-utilization-target must all be set together, and this + value must be between the min and max (inclusive). Defaults to 0 when + unset. Only valid with --gcp-cloud-run-worker-pool. + - name: gcp-cloud-run-utilization-target + type: float + description: | + Target average worker utilization the scaler aims for, as a fraction + in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and + --gcp-cloud-run-utilization-target must all be set together. Lower + values keep more spare capacity per worker. Defaults to 0.8 when + unset. Only valid with --gcp-cloud-run-worker-pool. - name: temporal worker deployment describe-version summary: Show properties of a Worker Deployment Version @@ -1394,7 +1432,11 @@ commands: --gcp-cloud-run-project YourGCPProject \ --gcp-cloud-run-region us-central1 \ --gcp-cloud-run-worker-pool UpdatedWorkerPool \ - --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com + --gcp-cloud-run-service-account customer-sa@proj.iam.gserviceaccount.com \ + --gcp-cloud-run-min-instances 1 \ + --gcp-cloud-run-max-instances 3 \ + --gcp-cloud-run-initial-instances 1 \ + --gcp-cloud-run-utilization-target 0.75 ``` If --remove is specified, the compute configuration for the Worker @@ -1454,6 +1496,40 @@ commands: Customer GCP service account the Temporal server impersonates to manage the Cloud Run worker pool. Required when --gcp-cloud-run-worker-pool is specified. + - name: gcp-cloud-run-min-instances + type: int + description: | + Minimum number of Cloud Run worker pool instances the scaler will + maintain. Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and + --gcp-cloud-run-utilization-target must all be set together. Defaults + to 0 when unset. Only valid with --gcp-cloud-run-worker-pool. + - name: gcp-cloud-run-max-instances + type: int + description: | + Maximum number of Cloud Run worker pool instances the scaler may + scale up to. Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and + --gcp-cloud-run-utilization-target must all be set together. Defaults + to 30 when unset. Only valid with --gcp-cloud-run-worker-pool. + - name: gcp-cloud-run-initial-instances + type: int + description: | + Number of Cloud Run worker pool instances the scaler starts with. + Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and + --gcp-cloud-run-utilization-target must all be set together, and this + value must be between the min and max (inclusive). Defaults to 0 when + unset. Only valid with --gcp-cloud-run-worker-pool. + - name: gcp-cloud-run-utilization-target + type: float + description: | + Target average worker utilization the scaler aims for, as a fraction + in the range (0, 1]. Optional, but --gcp-cloud-run-min-instances, + --gcp-cloud-run-max-instances, --gcp-cloud-run-initial-instances, and + --gcp-cloud-run-utilization-target must all be set together. Lower + values keep more spare capacity per worker. Defaults to 0.8 when + unset. Only valid with --gcp-cloud-run-worker-pool. - name: remove type: bool description: |