Skip to content

Commit

Permalink
Implement update/get worker versioning commands (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sushisource committed Jun 22, 2023
1 parent 6d4ab61 commit 20854b8
Show file tree
Hide file tree
Showing 16 changed files with 575 additions and 62 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Expand Up @@ -82,6 +82,7 @@ linters-settings:
- name: unhandled-error
arguments:
- "fmt.Printf"
- "fmt.Println"
issues:
# Exclude cyclomatic and cognitive complexity rules for functional tests in the `tests` root directory.
exclude-rules:
Expand Down
3 changes: 2 additions & 1 deletion app/app_test.go
Expand Up @@ -305,7 +305,7 @@ func newServerAndClientOpts(port int, customArgs ...string) ([]string, sdkclient
}
}

func assertServerHealth(ctx context.Context, t *testing.T, opts sdkclient.Options) {
func assertServerHealth(ctx context.Context, t *testing.T, opts sdkclient.Options) sdkclient.Client {
var (
c sdkclient.Client
clientErr error
Expand Down Expand Up @@ -339,6 +339,7 @@ func assertServerHealth(ctx context.Context, t *testing.T, opts sdkclient.Option
}
time.Sleep(time.Millisecond * 100)
}
return c
}

func TestCreateDataDirectory_MissingDirectory(t *testing.T) {
Expand Down
125 changes: 125 additions & 0 deletions app/build_id_compat_cli_test.go
@@ -0,0 +1,125 @@
package app_test

import (
"context"
"fmt"
"testing"

"github.com/temporalio/cli/common"

"github.com/stretchr/testify/suite"
"github.com/temporalio/cli/app"
sconfig "github.com/temporalio/cli/server/config"
"github.com/urfave/cli/v2"
"go.temporal.io/sdk/client"
)

type buildIdCompatSuite struct {
suite.Suite
app *cli.App
stopServerCancel context.CancelFunc
client client.Client
port int
writer *common.MemWriter
}

func TestBuildIdCompatSuite(t *testing.T) {
suite.Run(t, new(buildIdCompatSuite))
}

func (s *buildIdCompatSuite) SetupSuite() {
s.app = app.BuildApp()
mw := &common.MemWriter{}
s.app.Writer = mw
s.writer = mw
// Don't call os.Exit
s.app.ExitErrHandler = func(_ *cli.Context, _ error) {}
portProvider := sconfig.NewPortProvider()
port := portProvider.MustGetFreePort()
s.port = port
portProvider.Close()
ctx, cancel := context.WithCancel(context.Background())
s.stopServerCancel = cancel

args, clientOpts := newServerAndClientOpts(port)
args = append(args,
"--dynamic-config-value",
"frontend.workerVersioningDataAPIs=true",
"--dynamic-config-value",
"frontend.workerVersioningWorkflowAPIs=true",
)

go func() {
if err := s.app.RunContext(ctx, args); err != nil {
fmt.Println("Server closed with error:", err)
}
}()

s.client = assertServerHealth(ctx, s.T(), clientOpts)
}

func (s *buildIdCompatSuite) TearDownSuite() {
s.stopServerCancel()
}

func (s *buildIdCompatSuite) testTqName() string {
return "build-id-tq-" + s.T().Name()
}

func (s *buildIdCompatSuite) makeArgs(args ...string) []string {
allArgs := []string{""}
allArgs = append(allArgs, args...)
return append(allArgs,
"--address", fmt.Sprintf("localhost:%d", s.port),
"--task-queue", s.testTqName(), "--namespace", "default")
}

func (s *buildIdCompatSuite) TestAddNewDefaultBuildIdAndGet() {
err := s.app.Run(s.makeArgs(
"task-queue", "update-build-ids", "add-new-default", "--build-id", "foo"))
s.Nil(err)
err = s.app.Run(s.makeArgs("task-queue", "get-build-ids"))
s.Nil(err)
}

func (s *buildIdCompatSuite) TestAddNewCompatBuildId() {
err := s.app.Run(s.makeArgs(
"task-queue", "update-build-ids", "add-new-default", "--build-id", "foo"))
s.Nil(err)
err = s.app.Run(s.makeArgs(
"task-queue", "update-build-ids", "add-new-compatible",
"--build-id", "bar", "--existing-compatible-build-id", "foo"))
s.Nil(err)
}

func (s *buildIdCompatSuite) TestPromoteBuildIdSet() {
err := s.app.Run(s.makeArgs(
"task-queue", "update-build-ids", "add-new-default", "--build-id", "foo"))
s.Nil(err)
err = s.app.Run(s.makeArgs(
"task-queue", "update-build-ids", "promote-set",
"--build-id", "foo"))
s.Nil(err)
}

func (s *buildIdCompatSuite) TestPromoteBuildIdInSet() {
err := s.app.Run(s.makeArgs(
"task-queue", "update-build-ids", "add-new-default", "--build-id", "foo"))
s.Nil(err)
err = s.app.Run(s.makeArgs(
"task-queue", "update-build-ids", "promote-id-in-set",
"--build-id", "foo"))
s.Nil(err)
}

func (s *buildIdCompatSuite) TestReachability() {
err := s.app.Run(s.makeArgs(
"task-queue", "update-build-ids", "add-new-default", "--build-id", "foo"))
s.Nil(err)
err = s.app.Run(s.makeArgs("task-queue", "get-build-id-reachability", "--build-id", "foo"))
s.Nil(err)
writtenContent := s.writer.GetContent()
println(writtenContent)
s.Contains(writtenContent, "foo")
s.Contains(writtenContent, "[NewWorkflows]")
}
27 changes: 23 additions & 4 deletions common/defs-cmds.go
Expand Up @@ -34,6 +34,9 @@ const (
// Task Queue subcommand definitions
DescribeTaskQueueDefinition = "Provides information for Workers that have recently polled on this Task Queue."
ListPartitionTaskQueueDefinition = "Lists the Task Queue's partitions and the matching nodes they are assigned to."
UpdateBuildIDsDefinition = "Operations to update the sets of worker Build ID versions on the Task Queue"
GetBuildIDsDefinition = "Fetch the sets of worker Build ID versions on the Task Queue"
GetBuildIDReachabilityDefinition = "Retrieves information about the reachability of Build IDs on one or more Task Queues"

// Batch subcommand definitions
DescribeBatchJobDefinition = "Provide information about a Batch operation job."
Expand Down Expand Up @@ -81,6 +84,16 @@ const (
ScheduleDescribeDefinition = "Get Schedule configuration and current state."
ScheduleDeleteDefinition = "Deletes a Schedule."
ScheduleListDefinition = "Lists Schedules."

// Update build id subcommand definitions
AddNewDefaultBuildIDDefinition = "Add a new default (incompatible) build ID to the Task Queue version sets."
AddNewDefaultBuildIDDefinitionUsage = "Creates a new build id set which will become the new overall default for the queue with the provided build id as its only member. This new set is incompatible with all previous sets/versions."
AddNewCompatibleBuildIDDefinition = "Add a new build ID compatible with an existing ID to the Task Queue version sets."
AddNewCompatibleBuildIDDefinitionUsage = "The new build ID will become the default for the set containing the existing ID. See per-flag help for more."
PromoteSetDefinition = "Promote an existing build ID set to become the default for the Task Queue."
PromoteSetDefinitionUsage = "If the set is already the default, this command has no effect."
PromoteIDInSetDefinition = "Promote an existing build ID to become the default for its containing set."
PromoteIDInSetDefinitionUsage = "New tasks compatible with the the set will be dispatched to the default id."
)

const BatchUsageText = `Batch commands change multiple [Workflow Executions](/concepts/what-is-a-workflow-execution)
Expand Down Expand Up @@ -113,7 +126,7 @@ Batch Jobs can be returned for an entire Cluster or a single Namespace.
Use the command options below to change the information returned by this command.`

const TerminateBatchUsageText = `The ` + "`" + `temporal batch terminate` + "`" + ` command terminates a Batch job with the provided Job ID.
const TerminateBatchUsageText = `The ` + "`" + `temporal batch terminate` + "`" + ` command terminates a Batch job with the provided Job ID.
For future reference, provide a reason for terminating the Batch Job.
` + "`" + `temporal batch terminate --job-id=MyJobId --reason=JobReason` + "`" + `
Expand Down Expand Up @@ -334,7 +347,7 @@ Print a single property:
tls-key-path /home/my-user/certs/cluster.key`

const EnvSetUsageText = "`" + `temporal env set [environment.property name] [property value]` + "`" + `
const EnvSetUsageText = "`" + `temporal env set [environment.property name] [property value]` + "`" + `
Property names match CLI option names, for example '--address' and '--tls-cert-path':
Expand Down Expand Up @@ -458,8 +471,8 @@ The Overlap Policy of the Schedule can be overridden as well.
Use the options provided below to change this command's behavior.`

const ScheduleBackfillUsageText = `The ` + "`" + `temporal schedule backfill` + "`" + ` command executes Actions ahead of their specified time range.
Backfilling can fill in [Workflow Runs](/concepts/what-is-a-run-id) from a time period when the Schedule was paused, or from before the Schedule was created.
const ScheduleBackfillUsageText = `The ` + "`" + `temporal schedule backfill` + "`" + ` command executes Actions ahead of their specified time range.
Backfilling can fill in [Workflow Runs](/concepts/what-is-a-run-id) from a time period when the Schedule was paused, or from before the Schedule was created.
Schedule backfills require a valid Schedule ID, along with the time in which to run the Schedule and a change to the overlap policy.
` + "`" + `` + "`" + `` + "`" + `
Expand Down Expand Up @@ -564,6 +577,12 @@ const StartDevUsageText = `The ` + "`" + `temporal server start-dev` + "`" + ` c
The results of any command run on the Server can be viewed at http://localhost:7233.
`

const UpdateBuildIDsDefinitionText = "Provides various commands for adding or changing the sets of compatible build IDs associated with a Task Queue. See the help of each sub-command for more."
const GetBuildIDsDefinitionText = "Fetch the sets of compatible build IDs associated with a Task Queue and associated information."
const GetBuildIDReachabilityDefinitionText = "This command can tell you whether or not Build IDs may be used for for new, existing, or closed workflows. " +
"Both the --build-id and --task-queue flags may be specified multiple times. " +
"If you do not provide a task queue, reachability for the provided Build IDs will be checked against all task queues."

const CustomTemplateHelpCLI = `NAME:
{{template "helpNameTemplate" .}}{{if .Description}}
Expand Down
11 changes: 11 additions & 0 deletions common/defs-flags.go
Expand Up @@ -135,4 +135,15 @@ const (
// Task Queue flags
FlagTaskQueueName = "Name of the Task Queue."
FlagTaskQueueTypeDefinition = "Task Queue type [workflow|activity]"

// Build id based versioning flags
FlagNewBuildIDUsage = "The new build id to be added."
FlagExistingCompatibleBuildIDUsage = "A build id which must already exist in the version sets known by the task queue. The new id will be stored in the set containing this id, marking it as compatible with the versions within."
FlagSetBuildIDAsDefaultUsage = "When set, establishes the compatible set being targeted as the overall default for the queue. If a different set was the current default, the targeted set will replace it as the new default."
FlagPromoteSetBuildIDUsage = "An existing build id whose containing set will be promoted."
FlagPromoteBuildIDUsage = "An existing build id which will be promoted to be the default inside its containing set."
FlagMaxBuildIDSetsUsage = "Limits how many compatible sets will be returned. Specify 1 to only return the current default major version set. 0 returns all sets."
FlagBuildIDReachabilityUsage = "Which Build ID to get reachability information for. May be specified multiple times."
FlagTaskQueueForReachabilityUsage = "Which Task Queue(s) to constrain the reachability search to. May be specified multiple times."
FlagReachabilityTypeUsage = "Specify how you'd like to filter the reachability of Build IDs. Valid choices are `open` (reachable by one or more open workflows), `closed` (reachable by one or more closed workflows), or `existing` (reachable by either). If a Build ID is reachable by new workflows, that is always reported."
)
5 changes: 5 additions & 0 deletions common/flags.go
Expand Up @@ -135,6 +135,11 @@ var (
FlagWorkflowType = "workflow-type"
FlagYes = "yes"
FlagYesAlias = []string{"y"}
FlagBuildID = "build-id"
FlagExistingCompatibleBuildID = "existing-compatible-build-id"
FlagSetBuildIDAsDefault = "set-as-default"
FlagMaxBuildIDSets = "max-sets"
FlagReachabilityType = "reachability-type"
)

var SharedFlags = []cli.Flag{
Expand Down
14 changes: 14 additions & 0 deletions common/util.go
Expand Up @@ -2,6 +2,7 @@ package common

import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
Expand Down Expand Up @@ -553,3 +554,16 @@ func AddBeforeHandler(cmd *cli.Command, h func(*cli.Context) error) {
AddBeforeHandler(subcmd, h)
}
}

// MemWriter is an io.Writer implementation that stores the written content.
type MemWriter struct {
content bytes.Buffer
}

func (mw *MemWriter) Write(p []byte) (n int, err error) {
return mw.content.Write(p)
}

func (mw *MemWriter) GetContent() string {
return mw.content.String()
}
17 changes: 17 additions & 0 deletions common/util_test.go
@@ -1,6 +1,8 @@
package common

import (
"fmt"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -239,3 +241,18 @@ func (s *utilSuite) TestGetCliIdentity() {
identity := GetCliIdentity()
s.Contains(identity, "temporal-cli")
}

func TestMemWriter(t *testing.T) {
mw := &MemWriter{}
_, err := fmt.Fprintln(mw, "This message is written to the MemWriter.")
if err != nil {
t.Fatal(err)
}

expected := "This message is written to the MemWriter."
content := mw.GetContent()

if !strings.Contains(content, expected) {
t.Errorf("Expected log content to contain '%s', but it doesn't. Content: '%s'", expected, content)
}
}
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -19,7 +19,7 @@ require (
go.temporal.io/api v1.23.0
go.temporal.io/sdk v1.23.1
go.temporal.io/sdk/contrib/tools/workflowcheck v0.0.0-20230328164709-88a40de39c33
go.temporal.io/server v1.20.1-0.20230616203625-3392a7ab579a
go.temporal.io/server v1.20.1-0.20230622163242-9405cf84817e
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
google.golang.org/grpc v1.56.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -1153,8 +1153,8 @@ go.temporal.io/sdk v1.23.1 h1:HzOaw5+f6QgDW/HH1jzwgupII7nVz+fzxFPjmFJqKiQ=
go.temporal.io/sdk v1.23.1/go.mod h1:S7vWxU01lGcCny0sWx03bkkYw4VtVrpzeqBTn2A6y+E=
go.temporal.io/sdk/contrib/tools/workflowcheck v0.0.0-20230328164709-88a40de39c33 h1:tpDvC3HKzoPGmYZT7LBkYtBWrbZa8GNiLR2LG5iG5sw=
go.temporal.io/sdk/contrib/tools/workflowcheck v0.0.0-20230328164709-88a40de39c33/go.mod h1:CW0zVy7oLeWxBo3wG5bMU2dy4xaprM2net3/DkBzruw=
go.temporal.io/server v1.20.1-0.20230616203625-3392a7ab579a h1:ckFeiF8XdIVde74T9+TNIzLmKuIZDlk0KTKQ4XuG8NA=
go.temporal.io/server v1.20.1-0.20230616203625-3392a7ab579a/go.mod h1:Wtl0Io+CzQ56261Na3UnFcT6oXFsmkdOQ7OddEknnq4=
go.temporal.io/server v1.20.1-0.20230622163242-9405cf84817e h1:lprYw79A30E97M7nR+/IbTc6FGFEwE4M/jjY3zdmaQA=
go.temporal.io/server v1.20.1-0.20230622163242-9405cf84817e/go.mod h1:Wtl0Io+CzQ56261Na3UnFcT6oXFsmkdOQ7OddEknnq4=
go.temporal.io/version v0.3.0 h1:dMrei9l9NyHt8nG6EB8vAwDLLTwx2SvRyucCSumAiig=
go.temporal.io/version v0.3.0/go.mod h1:UA9S8/1LaKYae6TyD9NaPMJTZb911JcbqghI2CBSP78=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
Expand Down
6 changes: 4 additions & 2 deletions headers/headers.go
Expand Up @@ -15,8 +15,10 @@ const (
SupportedFeaturesHeaderDelim = ","
)

const DEV_VERSION = "0.0.0-DEV"

// Set by GoReleaser using ldflags
var Version = "0.0.0-DEV"
var Version = DEV_VERSION

const (
ClientNameCLI = "temporal-cli"
Expand All @@ -36,7 +38,7 @@ var (
)

func Init() {
if Version == "0.0.0-DEV" {
if Version == DEV_VERSION {
if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "(devel)" {
Version = info.Main.Version
}
Expand Down

0 comments on commit 20854b8

Please sign in to comment.