grim/convey

Move plans to the runtime package

2021-12-23, Gary Kramlich
178aec6c4f86
Move plans to the runtime package
package runner
import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
log "github.com/sirupsen/logrus"
"keep.imfreedom.org/grim/convey/config"
"keep.imfreedom.org/grim/convey/environment"
"keep.imfreedom.org/grim/convey/globals"
"keep.imfreedom.org/grim/convey/logging"
"keep.imfreedom.org/grim/convey/runtime"
)
type RunnerCmd struct {
ConfigFile string `kong:"flag,help='The config file to load',placeholder='FILE',short='f',default='convey.yml',name='config'"`
CpuShares string `kong:"flag,help='The amount of cpu shares given to run tasks',placeholder='CPU-SHARES',short='c'"`
ForceSequential bool `kong:"flag,help='Force concurrent stages to be ran sequentially',short='S',default='false'"`
KeepWorkspace bool `kong:"flag,help='Keep the workspace directory after running',default='false'"`
Memory string `kong:"flag,help='The amount of memory shares given to run tasks',placeholder='MEMORY',short='m'"`
Timeout time.Duration `kong:"flag,help='The maximum amount of time a plan can run. 0 to disable. Units must be specified.',placeholder='DURATION',default='15m'"`
Environment []string `kong:"flag,name='env',help='Set an environment variable',short='e',placeholder='ENV'"`
Plans []string `kong:"arg,help='The names of the plans to run',default='default'"`
}
func (c *RunnerCmd) runPlan(name string, cfg *config.Config, plan runtime.Plan) error {
configPath, _ := filepath.Split(c.ConfigFile)
if configPath == "" {
cwd, err := os.Getwd()
if err != nil {
return err
}
configPath = cwd
}
env := environment.New(c.Environment...)
rt := runtime.New(env, configPath, c.ForceSequential, c.KeepWorkspace, c.Timeout)
rt.Tasks = cfg.Tasks
rt.MetaPlans = cfg.MetaPlans
defer rt.Shutdown()
if c.CpuShares != "" {
rt.CpuShares = c.CpuShares
}
if c.Memory != "" {
rt.Memory = c.Memory
}
configEnv := environment.New(cfg.Environment...)
return plan.Execute(name, configEnv, rt)
}
func (c *RunnerCmd) runPlans(ctx context.Context, cfg *config.Config) error {
errChan := make(chan error, 1)
cancelled := false
go func() {
for _, name := range c.Plans {
plan := cfg.Plans[name]
if err := c.runPlan(name, cfg, plan); err != nil {
errChan <- err
return
}
if cancelled {
break
}
}
errChan <- nil
}()
for {
select {
case err := <-errChan:
return err
case <-ctx.Done():
cancelled = true
}
}
}
func (c *RunnerCmd) Run(g *globals.Globals) error {
logging.Setup(g.Color, g.Verbose)
cfg, err := config.LoadFile(c.ConfigFile)
if err != nil {
return err
}
err = cfg.Valid()
if err != nil {
return fmt.Errorf("config is invalid: %s", err)
}
err = cfg.HasPlans(c.Plans)
if err != nil {
return err
}
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
errChan := make(chan error, 1)
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context, cfg *config.Config) {
errChan <- c.runPlans(ctx, cfg)
}(ctx, cfg)
for {
select {
case err := <-errChan:
return err
case s := <-signalChan:
log.Infof("caught signal %s, exiting...", s)
cancel()
break
}
}
return nil
}