pot/modules/runtime/package.go
Lunny Xiao e5129b72c5
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Add for support
2020-11-27 21:41:07 +08:00

167 lines
3.4 KiB
Go

package runtime
import (
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"sync"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
// Package represents a package
type Package struct {
Version string `yaml:"version"`
Name string `yaml:"name"`
Imports []string `yaml:"imports"`
Tasks []Task `yaml:"tasks"`
importTasks []Task `yaml:"-"`
Main struct {
Hosts []string `yaml:"hosts"`
Tasks []string `yaml:"tasks"`
} `yaml:"main"`
}
// NewPackageFromFile load a package from file
func NewPackageFromFile(fPath string) (*Package, error) {
bs, err := ioutil.ReadFile(fPath)
if err != nil {
return nil, err
}
var pkg Package
if err := yaml.Unmarshal(bs, &pkg); err != nil {
return nil, err
}
for _, imp := range pkg.Imports {
fields := strings.Fields(imp)
if len(fields) == 1 {
subPkgPath := filepath.Join(filepath.Dir(fPath), fields[0]+".yml")
subPkg, err := NewPackageFromFile(subPkgPath)
if err != nil {
return nil, err
}
pkg.importTasks = append(pkg.importTasks, subPkg.Tasks...)
} else if len(fields) == 3 && fields[1] == "from" {
subPkgPath := filepath.Join(filepath.Dir(fPath), fields[2]+".yml")
subPkg, err := NewPackageFromFile(subPkgPath)
if err != nil {
return nil, err
}
var found bool
for _, t := range subPkg.Tasks {
if t.Name == fields[0] {
pkg.importTasks = append(pkg.importTasks, t)
found = true
break
}
}
if !found {
return nil, fmt.Errorf("load %s failed", imp)
}
}
}
return &pkg, nil
}
type ErrTaskNotExist struct {
TaskName string
}
func (err ErrTaskNotExist) Error() string {
return fmt.Sprintf("%s is not exist", err.TaskName)
}
func IsErrTaskNotExist(err error) bool {
_, ok := err.(ErrTaskNotExist)
return ok
}
// FindTask find a task from the package
func (pkg *Package) FindTask(taskname string) (*Task, error) {
for _, t := range pkg.Tasks {
if t.Name == taskname {
return &t, nil
}
}
for _, t := range pkg.importTasks {
if t.Name == taskname {
return &t, nil
}
}
return nil, ErrTaskNotExist{taskname}
}
// Run the package with special hosts and tasks
func (pkg *Package) Run(hosts []string, tasks []string) error {
if len(hosts) == 0 {
hosts = pkg.Main.Hosts
}
if len(hosts) == 0 {
return errors.New("A runnable package must have a main section")
}
if len(tasks) == 0 {
tasks = pkg.Main.Tasks
}
if len(tasks) == 0 {
return errors.New("No tasks to run")
}
log.Infof("Run package %s with %v", pkg.Name, hosts)
var hostTasks = make(map[string][]*Work)
for _, taskName := range tasks {
t, args, err := splitTaskCmd(taskName)
if err != nil {
return err
}
task, err := pkg.FindTask(t)
if err != nil {
if IsErrTaskNotExist(err) {
log.Warnf("No task named %s", taskName)
continue
}
return err
}
task.Pkg = pkg
for _, host := range hosts {
hostTasks[host] = append(hostTasks[host], &Work{
host: host,
task: task,
args: args,
})
}
}
var wg sync.WaitGroup
for _, works := range hostTasks {
wg.Add(1)
go func(works []*Work) {
for _, work := range works {
if err := work.Run(); err != nil {
log.Errorf("[%s] run task %s failed: %v", work.host,
work.task.Name, err)
break
} else {
log.Infof("[%s] run task %s successfully.", work.host,
work.task.Name)
}
}
wg.Done()
}(works)
}
wg.Wait()
return nil
}