-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
96990: roachtest: support running steps in the background in `mixedversion` r=srosenberg a=renatolabs This adds a `BackgroundFunc` API to the `mixedversion` package in roachtest, allowing test writers to run tasks in the background during an upgrade test. The most common use-case for this functionality is running a workload while the cluster upgrades (other similar use-cases exist in a variety of tests); for this reason, a `Workload` convenience function is added that allows tests to add a workload to a mixed-version test with one function call. Currently, each test needs to devise their own mechanism to: spawn the background task; monitor its execution; and terminate the test on error. The current API aims to reduce copying and pasting of such logic, making for a more declarative test. In the future, the test planner itself could decide to run some steps in the background and it should be able to leverage the mechanisms introduced in this commit. Epic: [CRDB-19321](https://cockroachlabs.atlassian.net/browse/CRDB-19321) Release note: None Co-authored-by: Renato Costa <[email protected]>
- Loading branch information
Showing
10 changed files
with
860 additions
and
160 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// Copyright 2023 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package roachtestutil | ||
|
||
import ( | ||
"fmt" | ||
"sort" | ||
"strings" | ||
) | ||
|
||
// Command wraps a command to be run in a cluster. It allows users to | ||
// manipulate a command without having to perform string-based | ||
// operations. | ||
type Command struct { | ||
Binary string | ||
Arguments []string | ||
Flags map[string]*string | ||
} | ||
|
||
// NewCommand builds a command. The format parameter can take | ||
// `fmt.Print` verbs. | ||
// | ||
// Examples: | ||
// | ||
// NewCommand("./cockroach version") | ||
// NewCommand("%s version", binaryPath) | ||
func NewCommand(format string, args ...interface{}) *Command { | ||
cmd := fmt.Sprintf(format, args...) | ||
parts := strings.Fields(cmd) | ||
return &Command{ | ||
Binary: parts[0], | ||
Arguments: parts[1:], | ||
Flags: make(map[string]*string), | ||
} | ||
} | ||
|
||
func (c *Command) Arg(format string, args ...interface{}) *Command { | ||
c.Arguments = append(c.Arguments, fmt.Sprintf(format, args...)) | ||
return c | ||
} | ||
|
||
func (c *Command) HasFlag(name string) bool { | ||
_, ok := c.Flags[name] | ||
return ok | ||
} | ||
|
||
func (c *Command) Flag(name string, val interface{}) *Command { | ||
c.Flags[name] = stringP(fmt.Sprint(val)) | ||
return c | ||
} | ||
|
||
// MaybeFlag is a thin wrapper around Flag for the caller's | ||
// convenience. The flag is added only if the `condition` parameter is | ||
// true. | ||
func (c *Command) MaybeFlag(condition bool, name string, val interface{}) *Command { | ||
if condition { | ||
return c.Flag(name, val) | ||
} | ||
|
||
return c | ||
} | ||
|
||
// Option adds a flag that doesn't have an associated value | ||
func (c *Command) Option(name string) *Command { | ||
c.Flags[name] = nil | ||
return c | ||
} | ||
|
||
func (c *Command) MaybeOption(condition bool, name string) *Command { | ||
if condition { | ||
return c.Option(name) | ||
} | ||
|
||
return c | ||
} | ||
|
||
// ITEFlag (if-then-else flag) adds a flag where the value depends on | ||
// the `condition` parameter. `trueVal` is used if `condition` is | ||
// true; `falseVal` is used otherwise. | ||
func (c *Command) ITEFlag(condition bool, name string, trueVal, falseVal interface{}) *Command { | ||
if condition { | ||
return c.Flag(name, trueVal) | ||
} | ||
|
||
return c.Flag(name, falseVal) | ||
} | ||
|
||
// String returns a canonical string representation of the command | ||
// which can be passed to `cluster.Run`. | ||
func (c *Command) String() string { | ||
flags := make([]string, 0, len(c.Flags)) | ||
names := make([]string, 0, len(c.Flags)) | ||
for name := range c.Flags { | ||
names = append(names, name) | ||
} | ||
sort.Strings(names) | ||
|
||
for _, name := range names { | ||
val := c.Flags[name] | ||
prefix := "-" | ||
if len(name) > 1 { | ||
prefix = "--" | ||
} | ||
|
||
prefixedName := prefix + name | ||
parts := []string{prefixedName} | ||
if val != nil { | ||
parts = append(parts, *val) | ||
} | ||
flags = append(flags, strings.Join(parts, " ")) | ||
} | ||
|
||
cmd := append( | ||
[]string{c.Binary}, | ||
append(c.Arguments, flags...)..., | ||
) | ||
|
||
return strings.Join(cmd, " ") | ||
} | ||
|
||
func stringP(s string) *string { | ||
return &s | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2023 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package roachtestutil | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestCommand(t *testing.T) { | ||
c := NewCommand("./cockroach") | ||
require.Equal(t, "./cockroach", c.String()) | ||
|
||
c = NewCommand("./cockroach workload init") | ||
require.Equal(t, "./cockroach workload init", c.String()) | ||
|
||
c = NewCommand("./cockroach").Arg("workload").Arg("init") | ||
require.Equal(t, "./cockroach workload init", c.String()) | ||
|
||
baseCommand := NewCommand("./cockroach workload run bank").Arg("{pgurl:%d}", 1) | ||
|
||
c = clone(baseCommand) | ||
c.Flag("max-ops", 10).Flag("path", "/some/path") | ||
require.Equal(t, "./cockroach workload run bank {pgurl:1} --max-ops 10 --path /some/path", c.String()) | ||
|
||
c = clone(baseCommand) | ||
c.MaybeFlag(true, "max-ops", 10) // included | ||
c.MaybeFlag(false, "concurrency", 8) // not included | ||
require.True(t, c.HasFlag("max-ops")) | ||
require.False(t, c.HasFlag("concurrency")) | ||
require.Equal(t, "./cockroach workload run bank {pgurl:1} --max-ops 10", c.String()) | ||
|
||
c = clone(baseCommand) | ||
c.ITEFlag(true, "max-ops", 10, 20) | ||
c.ITEFlag(false, "duration", 2*time.Hour, 10*time.Minute) | ||
require.Equal(t, "./cockroach workload run bank {pgurl:1} --duration 10m0s --max-ops 10", c.String()) | ||
|
||
c = clone(baseCommand) | ||
c.Option("local") | ||
c.MaybeOption(true, "background") // included | ||
c.MaybeOption(false, "dry-run") // not included | ||
require.True(t, c.HasFlag("local")) | ||
require.True(t, c.HasFlag("background")) | ||
require.False(t, c.HasFlag("dry-run")) | ||
require.Equal(t, "./cockroach workload run bank {pgurl:1} --background --local", c.String()) | ||
|
||
c = clone(baseCommand) | ||
c.Flag("c", 10) | ||
c.MaybeFlag(true, "n", "8") // included | ||
c.MaybeFlag(false, "j", "yes") // not included | ||
c.Option("x") | ||
require.True(t, c.HasFlag("c")) | ||
require.Equal(t, "./cockroach workload run bank {pgurl:1} -c 10 -n 8 -x", c.String()) | ||
} | ||
|
||
func clone(cmd *Command) *Command { | ||
flags := make(map[string]*string) | ||
for k, v := range cmd.Flags { | ||
flags[k] = v | ||
} | ||
|
||
return &Command{ | ||
Binary: cmd.Binary, | ||
Arguments: append([]string{}, cmd.Arguments...), | ||
Flags: flags, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.