Run command must always take a single argument (#156)

This commit is contained in:
Pieter Noordhuis 2022-12-22 16:19:38 +01:00 committed by GitHub
parent 61ef0ba8c6
commit 49aa858b89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 109 deletions

View File

@ -24,55 +24,34 @@ type Runner interface {
Run(ctx context.Context) error
}
// Collect collects a list of runners given a list of arguments.
// Find locates a runner matching the specified argument.
//
// Its behavior is as follows:
// 1. If no arguments are specified, it returns a runner for the only resource in the bundle.
// 2. If multiple arguments are specified, for each argument:
// 2.1. Try to find a resource with <key> identical to the argument.
// 2.2. Try to find a resource with <type>.<key> identical to the argument.
// 1. Try to find a resource with <key> identical to the argument.
// 2. Try to find a resource with <type>.<key> identical to the argument.
//
// If an argument resolves to multiple resources, it returns an error.
func Collect(b *bundle.Bundle, args []string) ([]Runner, error) {
func Find(b *bundle.Bundle, arg string) (Runner, error) {
keyOnly, keyWithType := ResourceKeys(b)
if len(keyWithType) == 0 {
return nil, fmt.Errorf("bundle defines no resources")
}
var out []Runner
// If the bundle contains only a single resource, we know what to run.
if len(args) == 0 {
if len(keyWithType) != 1 {
return nil, fmt.Errorf("bundle defines multiple resources; please specify resource to run")
}
for _, runners := range keyWithType {
if len(runners) != 1 {
// This invariant is covered by [ResourceKeys].
panic("length of []run.Runner must be 1")
}
out = append(out, runners[0])
}
return out, nil
}
for _, arg := range args {
runners, ok := keyOnly[arg]
runners, ok := keyOnly[arg]
if !ok {
runners, ok = keyWithType[arg]
if !ok {
runners, ok = keyWithType[arg]
if !ok {
return nil, fmt.Errorf("no such resource: %s", arg)
}
return nil, fmt.Errorf("no such resource: %s", arg)
}
if len(runners) != 1 {
var keys []string
for _, runner := range runners {
keys = append(keys, runner.Key())
}
return nil, fmt.Errorf("ambiguous: %s (can resolve to all of %s)", arg, strings.Join(keys, ", "))
}
out = append(out, runners[0])
}
return out, nil
if len(runners) != 1 {
var keys []string
for _, runner := range runners {
keys = append(keys, runner.Key())
}
return nil, fmt.Errorf("ambiguous: %s (can resolve to all of %s)", arg, strings.Join(keys, ", "))
}
return runners[0], nil
}

View File

@ -9,18 +9,18 @@ import (
"github.com/stretchr/testify/assert"
)
func TestCollectNoResources(t *testing.T) {
func TestFindNoResources(t *testing.T) {
b := &bundle.Bundle{
Config: config.Root{
Resources: config.Resources{},
},
}
_, err := Collect(b, []string{"foo"})
_, err := Find(b, "foo")
assert.ErrorContains(t, err, "bundle defines no resources")
}
func TestCollectNoArg(t *testing.T) {
func TestFindSingleArg(t *testing.T) {
b := &bundle.Bundle{
Config: config.Root{
Resources: config.Resources{
@ -31,28 +31,11 @@ func TestCollectNoArg(t *testing.T) {
},
}
out, err := Collect(b, []string{})
_, err := Find(b, "foo")
assert.NoError(t, err)
assert.Len(t, out, 1)
}
func TestCollectNoArgMultipleResources(t *testing.T) {
b := &bundle.Bundle{
Config: config.Root{
Resources: config.Resources{
Jobs: map[string]*resources.Job{
"foo": {},
"bar": {},
},
},
},
}
_, err := Collect(b, []string{})
assert.ErrorContains(t, err, "bundle defines multiple resources")
}
func TestCollectSingleArg(t *testing.T) {
func TestFindSingleArgNotFound(t *testing.T) {
b := &bundle.Bundle{
Config: config.Root{
Resources: config.Resources{
@ -63,27 +46,11 @@ func TestCollectSingleArg(t *testing.T) {
},
}
out, err := Collect(b, []string{"foo"})
assert.NoError(t, err)
assert.Len(t, out, 1)
}
func TestCollectSingleArgNotFound(t *testing.T) {
b := &bundle.Bundle{
Config: config.Root{
Resources: config.Resources{
Jobs: map[string]*resources.Job{
"foo": {},
},
},
},
}
_, err := Collect(b, []string{"bar"})
_, err := Find(b, "bar")
assert.ErrorContains(t, err, "no such resource: bar")
}
func TestCollectSingleArgAmbiguous(t *testing.T) {
func TestFindSingleArgAmbiguous(t *testing.T) {
b := &bundle.Bundle{
Config: config.Root{
Resources: config.Resources{
@ -97,11 +64,11 @@ func TestCollectSingleArgAmbiguous(t *testing.T) {
},
}
_, err := Collect(b, []string{"key"})
_, err := Find(b, "key")
assert.ErrorContains(t, err, "ambiguous: ")
}
func TestCollectSingleArgWithType(t *testing.T) {
func TestFindSingleArgWithType(t *testing.T) {
b := &bundle.Bundle{
Config: config.Root{
Resources: config.Resources{
@ -112,27 +79,6 @@ func TestCollectSingleArgWithType(t *testing.T) {
},
}
out, err := Collect(b, []string{"jobs.key"})
_, err := Find(b, "jobs.key")
assert.NoError(t, err)
assert.Len(t, out, 1)
}
func TestCollectMultipleArg(t *testing.T) {
b := &bundle.Bundle{
Config: config.Root{
Resources: config.Resources{
Jobs: map[string]*resources.Job{
"foo": {},
"bar": {},
},
Pipelines: map[string]*resources.Pipeline{
"qux": {},
},
},
},
}
out, err := Collect(b, []string{"foo", "bar", "qux"})
assert.NoError(t, err)
assert.Len(t, out, 3)
}

View File

@ -9,9 +9,10 @@ import (
)
var runCmd = &cobra.Command{
Use: "run [flags] KEY...",
Use: "run [flags] KEY",
Short: "Run a workload (e.g. a job or a pipeline)",
Args: cobra.ExactArgs(1),
PreRunE: ConfigureBundle,
RunE: func(cmd *cobra.Command, args []string) error {
b := bundle.Get(cmd.Context())
@ -24,16 +25,14 @@ var runCmd = &cobra.Command{
return err
}
runners, err := run.Collect(b, args)
runner, err := run.Find(b, args[0])
if err != nil {
return err
}
for _, runner := range runners {
err = runner.Run(cmd.Context())
if err != nil {
return err
}
err = runner.Run(cmd.Context())
if err != nil {
return err
}
return nil