diff --git a/internal/temporalcli/commands.taskqueue.go b/internal/temporalcli/commands.taskqueue.go index ee5feacbf..769523869 100644 --- a/internal/temporalcli/commands.taskqueue.go +++ b/internal/temporalcli/commands.taskqueue.go @@ -6,7 +6,7 @@ import ( "github.com/fatih/color" "github.com/temporalio/cli/internal/printer" - commonpb "go.temporal.io/api/common/v1" + deploymentpb "go.temporal.io/api/deployment/v1" "go.temporal.io/api/enums/v1" "go.temporal.io/api/taskqueue/v1" "go.temporal.io/api/workflowservice/v1" @@ -332,12 +332,13 @@ func (c *TemporalTaskQueueDescribeCommand) runLegacy(cctx *CommandContext, args Partition int `json:"partition"` taskqueue.PollerInfo // copy this out to display nicer in table or card, but not json - Versioning *commonpb.WorkerVersionCapabilities `json:"-"` + Versioning *deploymentpb.WorkerDeploymentOptions `json:"-"` } var statuses []*statusWithPartition var pollers []*pollerWithPartition var config *taskqueue.TaskQueueConfig + var versioningInfo *taskqueue.TaskQueueVersioningInfo // TODO: remove this when the server does partition fan-out for p := 0; p < partitions; p++ { @@ -362,13 +363,17 @@ func (c *TemporalTaskQueueDescribeCommand) runLegacy(cctx *CommandContext, args pollers = append(pollers, &pollerWithPartition{ Partition: p, PollerInfo: *pi, - Versioning: pi.WorkerVersionCapabilities, + Versioning: pi.GetDeploymentOptions(), }) } - // Capture config from the first partition (they should all be the same) + // Capture config from the root partition if p == 0 && resp.Config != nil { config = resp.Config } + // Capture versioning info from the root partition + if p == 0 && resp.VersioningInfo != nil { + versioningInfo = resp.VersioningInfo + } } // For JSON, we'll just dump the proto @@ -383,6 +388,11 @@ func (c *TemporalTaskQueueDescribeCommand) runLegacy(cctx *CommandContext, args output["config"] = config } + // Include versioning info if not nil + if versioningInfo != nil { + output["versioning_info"] = versioningInfo + } + return cctx.Printer.PrintStructured(output, printer.StructuredOptions{}) } @@ -409,6 +419,12 @@ func (c *TemporalTaskQueueDescribeCommand) runLegacy(cctx *CommandContext, args return printTaskQueueConfig(cctx, config) } + // Display versioning info if not nil + if versioningInfo != nil { + cctx.Printer.Println(color.MagentaString("\nVersioning Info:")) + return printTaskQueueVersioningInfo(cctx, versioningInfo) + } + return nil } diff --git a/internal/temporalcli/commands.taskqueue.helper.go b/internal/temporalcli/commands.taskqueue.helper.go index f63e8b22b..ee641846b 100644 --- a/internal/temporalcli/commands.taskqueue.helper.go +++ b/internal/temporalcli/commands.taskqueue.helper.go @@ -98,3 +98,48 @@ func printTaskQueueConfig(cctx *CommandContext, config *taskqueue.TaskQueueConfi return nil } + +func printTaskQueueVersioningInfo(cctx *CommandContext, versioningInfo *taskqueue.TaskQueueVersioningInfo) error { + if versioningInfo == nil { + return nil + } + + // For JSON, we'll just dump the proto + if cctx.JSONOutput { + return cctx.Printer.PrintStructured(versioningInfo, printer.StructuredOptions{}) + } + + // For text, we will use a structured display + var currentVersionDeploymentName, currentVersionBuildID string + currentVersionDeploymentName = versioningInfo.GetCurrentDeploymentVersion().GetDeploymentName() + currentVersionBuildID = versioningInfo.GetCurrentDeploymentVersion().GetBuildId() + + var rampingVersionDeploymentName, rampingVersionBuildID string + if versioningInfo.RampingDeploymentVersion != nil { + rampingVersionDeploymentName = versioningInfo.RampingDeploymentVersion.DeploymentName + rampingVersionBuildID = versioningInfo.RampingDeploymentVersion.BuildId + } + + var updateTime time.Time + if versioningInfo.UpdateTime != nil { + updateTime = versioningInfo.UpdateTime.AsTime() + } + + printMe := struct { + CurrentVersionDeploymentName string `cli:",cardOmitEmpty"` + CurrentVersionBuildID string `cli:",cardOmitEmpty"` + RampingVersionDeploymentName string `cli:",cardOmitEmpty"` + RampingVersionBuildID string `cli:",cardOmitEmpty"` + RampingVersionPercentage float32 `cli:",cardOmitEmpty"` + UpdateTime time.Time `cli:",cardOmitEmpty"` + }{ + CurrentVersionDeploymentName: currentVersionDeploymentName, + CurrentVersionBuildID: currentVersionBuildID, + RampingVersionDeploymentName: rampingVersionDeploymentName, + RampingVersionBuildID: rampingVersionBuildID, + RampingVersionPercentage: versioningInfo.RampingVersionPercentage, + UpdateTime: updateTime, + } + + return cctx.Printer.PrintStructured(printMe, printer.StructuredOptions{}) +} diff --git a/internal/temporalcli/commands.taskqueue_test.go b/internal/temporalcli/commands.taskqueue_test.go index b695758b2..6c5e3ba1a 100644 --- a/internal/temporalcli/commands.taskqueue_test.go +++ b/internal/temporalcli/commands.taskqueue_test.go @@ -10,6 +10,7 @@ import ( "github.com/temporalio/cli/internal/temporalcli" "go.temporal.io/api/enums/v1" "go.temporal.io/api/workflowservice/v1" + "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" ) @@ -386,6 +387,99 @@ func (s *SharedServerSuite) TestTaskQueue_Describe_Simple_Legacy() { s.GreaterOrEqual(10, len(jsonOut.TaskQueues)) } +func (s *SharedServerSuite) TestTaskQueue_Describe_VersioningInfo_Legacy() { + deploymentName := uuid.NewString() + buildId := uuid.NewString() + version := worker.WorkerDeploymentVersion{ + DeploymentName: deploymentName, + BuildID: buildId, + } + taskQueue := uuid.NewString() + + // Start a versioned worker polling on task queue + w := s.DevServer.StartDevWorker(s.Suite.T(), DevWorkerOptions{ + TaskQueue: taskQueue, + Worker: worker.Options{ + DeploymentOptions: worker.DeploymentOptions{ + UseVersioning: true, + Version: version, + DefaultVersioningBehavior: workflow.VersioningBehaviorPinned, + }, + }, + }) + defer w.Stop() + + // Wait for deployment to appear in list + s.EventuallyWithT(func(t *assert.CollectT) { + res := s.Execute( + "worker", "deployment", "list", + "--address", s.Address(), + ) + assert.NoError(t, res.Err) + assert.Contains(t, res.Stdout.String(), deploymentName) + }, 30*time.Second, 100*time.Millisecond) + + // Wait for version to be describable + s.EventuallyWithT(func(t *assert.CollectT) { + res := s.Execute( + "worker", "deployment", "describe-version", + "--address", s.Address(), + "--deployment-name", version.DeploymentName, "--build-id", version.BuildID, + ) + assert.NoError(t, res.Err) + }, 30*time.Second, 100*time.Millisecond) + + // Set this version as current + res := s.Execute( + "worker", "deployment", "set-current-version", + "--address", s.Address(), + "--deployment-name", version.DeploymentName, "--build-id", version.BuildID, + "--yes", + ) + s.NoError(res.Err) + + // Text: describe task queue until the versioning info shows the expected current version + s.EventuallyWithT(func(t *assert.CollectT) { + res = s.Execute( + "task-queue", "describe", + "--legacy-mode", + "--address", s.Address(), + "--task-queue", taskQueue, + ) + assert.NoError(t, res.Err) + assert.Contains(t, res.Stdout.String(), "Versioning Info:") + assert.Contains(t, res.Stdout.String(), deploymentName) + assert.Contains(t, res.Stdout.String(), buildId) + }, 30*time.Second, 100*time.Millisecond) + // Confirm that the deployment name and build id are on the same line as their respective labels + s.ContainsOnSameLine(res.Stdout.String(), "CurrentVersionDeploymentName", deploymentName) + s.ContainsOnSameLine(res.Stdout.String(), "CurrentVersionBuildID", buildId) + + // JSON output verification + res = s.Execute( + "task-queue", "describe", + "-o", "json", + "--legacy-mode", + "--address", s.Address(), + "--task-queue", taskQueue, + ) + s.NoError(res.Err) + + var jsonOut struct { + Pollers []map[string]any `json:"pollers"` + TaskQueues []map[string]any `json:"taskQueues"` + VersioningInfo struct { + CurrentDeploymentVersion struct { + DeploymentName string `json:"deployment_name"` + BuildId string `json:"build_id"` + } `json:"current_deployment_version"` + } `json:"versioning_info"` + } + s.NoError(json.Unmarshal(res.Stdout.Bytes(), &jsonOut)) + s.Equal(deploymentName, jsonOut.VersioningInfo.CurrentDeploymentVersion.DeploymentName) + s.Equal(buildId, jsonOut.VersioningInfo.CurrentDeploymentVersion.BuildId) +} + func (s *SharedServerSuite) TestTaskQueue_ListPartition() { testTaskQueue := uuid.NewString() res := s.Execute(