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 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: // Its behavior is as follows:
// 1. If no arguments are specified, it returns a runner for the only resource in the bundle. // 1. Try to find a resource with <key> identical to the argument.
// 2. If multiple arguments are specified, for each argument: // 2. Try to find a resource with <type>.<key> identical to the 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.
// //
// If an argument resolves to multiple resources, it returns an error. // 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) keyOnly, keyWithType := ResourceKeys(b)
if len(keyWithType) == 0 { if len(keyWithType) == 0 {
return nil, fmt.Errorf("bundle defines no resources") return nil, fmt.Errorf("bundle defines no resources")
} }
var out []Runner runners, ok := keyOnly[arg]
if !ok {
// If the bundle contains only a single resource, we know what to run. runners, ok = keyWithType[arg]
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]
if !ok { if !ok {
runners, ok = keyWithType[arg] return nil, fmt.Errorf("no such resource: %s", arg)
if !ok {
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" "github.com/stretchr/testify/assert"
) )
func TestCollectNoResources(t *testing.T) { func TestFindNoResources(t *testing.T) {
b := &bundle.Bundle{ b := &bundle.Bundle{
Config: config.Root{ Config: config.Root{
Resources: config.Resources{}, Resources: config.Resources{},
}, },
} }
_, err := Collect(b, []string{"foo"}) _, err := Find(b, "foo")
assert.ErrorContains(t, err, "bundle defines no resources") assert.ErrorContains(t, err, "bundle defines no resources")
} }
func TestCollectNoArg(t *testing.T) { func TestFindSingleArg(t *testing.T) {
b := &bundle.Bundle{ b := &bundle.Bundle{
Config: config.Root{ Config: config.Root{
Resources: config.Resources{ 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.NoError(t, err)
assert.Len(t, out, 1)
} }
func TestCollectNoArgMultipleResources(t *testing.T) { func TestFindSingleArgNotFound(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) {
b := &bundle.Bundle{ b := &bundle.Bundle{
Config: config.Root{ Config: config.Root{
Resources: config.Resources{ Resources: config.Resources{
@ -63,27 +46,11 @@ func TestCollectSingleArg(t *testing.T) {
}, },
} }
out, err := Collect(b, []string{"foo"}) _, err := Find(b, "bar")
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"})
assert.ErrorContains(t, err, "no such resource: bar") assert.ErrorContains(t, err, "no such resource: bar")
} }
func TestCollectSingleArgAmbiguous(t *testing.T) { func TestFindSingleArgAmbiguous(t *testing.T) {
b := &bundle.Bundle{ b := &bundle.Bundle{
Config: config.Root{ Config: config.Root{
Resources: config.Resources{ 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: ") assert.ErrorContains(t, err, "ambiguous: ")
} }
func TestCollectSingleArgWithType(t *testing.T) { func TestFindSingleArgWithType(t *testing.T) {
b := &bundle.Bundle{ b := &bundle.Bundle{
Config: config.Root{ Config: config.Root{
Resources: config.Resources{ 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.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{ var runCmd = &cobra.Command{
Use: "run [flags] KEY...", Use: "run [flags] KEY",
Short: "Run a workload (e.g. a job or a pipeline)", Short: "Run a workload (e.g. a job or a pipeline)",
Args: cobra.ExactArgs(1),
PreRunE: ConfigureBundle, PreRunE: ConfigureBundle,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
b := bundle.Get(cmd.Context()) b := bundle.Get(cmd.Context())
@ -24,16 +25,14 @@ var runCmd = &cobra.Command{
return err return err
} }
runners, err := run.Collect(b, args) runner, err := run.Find(b, args[0])
if err != nil { if err != nil {
return err return err
} }
for _, runner := range runners { err = runner.Run(cmd.Context())
err = runner.Run(cmd.Context()) if err != nil {
if err != nil { return err
return err
}
} }
return nil return nil