* Copyright 2016-2017 Gary Kramlich <grim@reaperworld.com> * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. "github.com/aphistic/gomol" "bitbucket.org/rw_grim/convey/environment" "bitbucket.org/rw_grim/convey/logging" "bitbucket.org/rw_grim/convey/state" "bitbucket.org/rw_grim/convey/tasks" "bitbucket.org/rw_grim/convey/yaml" Name string `yaml:"name"` Enabled bool `yaml:"enabled"` Always bool `yaml:"always"` Concurrent bool `yaml:"concurrent"` Environment yaml.StringOrSlice `yaml:"environment"` Tasks yaml.StringOrSlice `yaml:"tasks"` func (s *Stage) UnmarshalYAML(unmarshal func(interface{}) error) error { raw := rawStage{Enabled: true} if err := unmarshal(&raw); err != nil { // validate the run attribute // If the always attribute is set, convert it to run syntax s.Run = strings.ToLower(s.Run) // now validate the run attribute return fmt.Errorf("Invalid value '%s' for run attribute", s.Run) // All good, return success func (s *Stage) Execute(path string, logger *gomol.LogAdapter, taskMap map[string]tasks.Task, env []string, st *state.State) error { stageEnv := environment.Merge(env, s.Environment) if s.Concurrent && !st.ForceSequential { taskRes := make(chan error) stageRes := make(chan error) // process all of the task results go func(taskRes, stageRes chan error) { for err := range taskRes { if err != nil && stageErr == nil { // now close the stageRes channel // run all of the tasks in go routines for _, taskName := range s.Tasks { go func(path, name string, res chan error) { res <- s.runTask(path, name, stageEnv, taskMap, st) }(path, taskName, taskRes) // block until the task wait group is done, then close the taskRes channel // now return the result from the stageRes channel for _, taskName := range s.Tasks { err := s.runTask(path, taskName, stageEnv, taskMap, st) func (s *Stage) runTask(path, name string, stageEnv []string, taskMap map[string]tasks.Task, st *state.State) error { absTaskName := fmt.Sprintf("%s/%s", path, name) taskLogger := logging.NewAdapter(fmt.Sprintf("%s/%s", path, name)) taskLogger.Info("starting") task, found := taskMap[name] taskLogger.Fatal("failed, task not found") return fmt.Errorf("task %s not found", absTaskName) err := task.Execute(absTaskName, taskLogger, stageEnv, st) taskLogger.Fatalf("failed, %s", err) taskLogger.Info("finished") // ShouldRun will return True if a stage should be run based on the passed in // error (from the plan) and whether or not the stage is enabled. func (s *Stage) ShouldRun(err error) bool { return s.Run == "on-failure" return s.Run == "on-success"