grim/convey

Parents 52fc7651ae43
Children 93df1b650a9f
Add initial draft of extends implementation for config files.
--- a/loaders/convey/convey.go Thu Sep 14 18:32:15 2017 -0500
+++ b/loaders/convey/convey.go Thu Sep 14 19:08:14 2017 -0500
@@ -26,6 +26,7 @@
"bitbucket.org/rw_grim/convey/environment"
"bitbucket.org/rw_grim/convey/plans"
"bitbucket.org/rw_grim/convey/state"
+ "bitbucket.org/rw_grim/convey/tasks"
cYaml "bitbucket.org/rw_grim/convey/yaml"
)
@@ -35,6 +36,7 @@
}
type config struct {
+ Extends string `yaml:"extends"`
Tasks map[string]yaml.MapSlice `yaml:"tasks"`
Plans map[string]plans.Plan `yaml:"plans"`
MetaPlans map[string]plans.MetaPlan `yaml:"meta-plans"`
@@ -59,12 +61,29 @@
return nil, err
}
- if len(cfg.Tasks) == 0 {
- return nil, ErrNoTasks
- }
+ // see if we're extending something - partially load a base
+ // config object if so that we'll modify; otherwise use an
+ // empty base config object that we'll set in a similar way
+
+ var baseConfig *cConfig.Config
+
+ if cfg.Extends != "" {
+ baseConfig, err = cConfig.LoadFile(cfg.Extends, c)
- if len(cfg.Plans) == 0 {
- return nil, ErrNoPlans
+ // We can safely ignore no plans and no tasks errors here
+ // as we're ensured to also get a valid base config back.
+ // This is a bit of a strange idiom, but is still used in
+ // places like io.Reader (return non-zero n on error).
+ if err != nil && err != ErrNoPlans || err != ErrNoTasks {
+ return nil, err
+ }
+ } else {
+ baseConfig = &cConfig.Config{
+ Tasks: map[string]tasks.Task{},
+ Plans: map[string]plans.Plan{},
+ MetaPlans: map[string]plans.MetaPlan{},
+ SSHIdentities: []string{},
+ }
}
// turn the raw tasks into real tasks
@@ -76,7 +95,7 @@
// iterate through each plan and do any cleanup we need to
for _, plan := range cfg.Plans {
// set stage names for any that are missing them
- for idx, _ := range plan.Stages {
+ for idx := range plan.Stages {
if plan.Stages[idx].Name == "" {
plan.Stages[idx].Name = fmt.Sprintf("stage-%d", idx)
}
@@ -86,16 +105,51 @@
// store the default plan in the loader
c.defaultPlan = cfg.Options.DefaultPlan
- // create the real config
- realConfig := &cConfig.Config{
- Environment: cfg.Environment,
- Plans: cfg.Plans,
- MetaPlans: cfg.MetaPlans,
- SSHIdentities: cfg.Options.SSHIdentities,
- Tasks: realTasks,
+ // tasks, plans, and metaplans are all name => * maps, and
+ // if we're extending something we want to inherit all of these
+ // EXCEPT for things that we are explicitly overriding by using
+ // the same name. Do a shallow merge here
+
+ for name, task := range realTasks {
+ baseConfig.Tasks[name] = task
+ }
+
+ for name, plan := range cfg.Plans {
+ baseConfig.Plans[name] = plan
+ }
+
+ for name, metaPlan := range cfg.MetaPlans {
+ baseConfig.MetaPlans[name] = metaPlan
}
- return realConfig, nil
+ baseConfig.Environment = environment.Merge(baseConfig.Environment, cfg.Environment)
+
+ // Check if the default plan is being overridden
+ if cfg.Options.DefaultPlan != "" {
+ c.defaultPlan = cfg.Options.DefaultPlan
+ }
+
+ // don't clobber ssh-identities with an empty list
+ if len(cfg.Options.SSHIdentities) > 0 {
+ baseConfig.SSHIdentities = cfg.Options.SSHIdentities
+ }
+
+ // Return the base config at this point, but maybe possibly return an
+ // error that we're missing something in order to proceed. This gets
+ // kind of weird: if we're calling Load recursively through LoadFile,
+ // as is the case with extending a config file, we're going to ignore
+ // these errors but need the actual config (we may define tasks and
+ // plans in an extending config).
+
+ if len(baseConfig.Tasks) == 0 {
+ return baseConfig, ErrNoTasks
+ }
+
+ if len(baseConfig.Plans) == 0 {
+ return baseConfig, ErrNoPlans
+ }
+
+ return baseConfig, nil
}
func (c *Loader) LoadOverride(path, base string, data []byte, config *cConfig.Config) {
--- a/loaders/convey/convey_test.go Thu Sep 14 18:32:15 2017 -0500
+++ b/loaders/convey/convey_test.go Thu Sep 14 19:08:14 2017 -0500
@@ -75,9 +75,8 @@
l := Loader{}
- cfg, err := l.Load(".", ".", []byte(data))
+ _, err := l.Load(".", ".", []byte(data))
Expect(err).To(MatchError(ErrNoTasks))
- Expect(cfg).To(BeNil())
}
func (s *conveySuite) TestPlansRequired(t sweet.T) {
@@ -88,7 +87,6 @@
l := Loader{}
- cfg, err := l.Load(".", ".", []byte(data))
+ _, err := l.Load(".", ".", []byte(data))
Expect(err).To(MatchError(ErrNoPlans))
- Expect(cfg).To(BeNil())
}
--- a/loaders/convey/environment_test.go Thu Sep 14 18:32:15 2017 -0500
+++ b/loaders/convey/environment_test.go Thu Sep 14 19:08:14 2017 -0500
@@ -67,7 +67,7 @@
cfg, err := loader.Load(".", ".", []byte(baseData))
Expect(err).To(BeNil())
- Expect(cfg.Environment).To(BeNil())
+ Expect(cfg.Environment).To(BeEmpty())
overrideData := `
environment:
@@ -84,7 +84,7 @@
cfg, err := loader.Load(".", ".", []byte(baseData))
Expect(err).To(BeNil())
- Expect(cfg.Environment).To(BeNil())
+ Expect(cfg.Environment).To(BeEmpty())
overrideData := `
environment: foo=bar