github.com/masahide/goansible@v0.0.0-20160116054156-01eac649e9f2/runner.go (about)

     1  package goansible
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  type RunResult struct {
    10  	Task    *Task
    11  	Result  *Result
    12  	Runtime time.Duration
    13  }
    14  
    15  type Runner struct {
    16  	env       *Environment
    17  	plays     []*Play
    18  	wait      sync.WaitGroup
    19  	to_notify map[string]struct{}
    20  	//async     chan *AsyncAction
    21  	report Reporter
    22  
    23  	Results []RunResult
    24  	Start   time.Time
    25  	Runtime time.Duration
    26  }
    27  
    28  func NewRunner(env *Environment, plays []*Play) *Runner {
    29  	r := &Runner{
    30  		env:       env,
    31  		plays:     plays,
    32  		to_notify: make(map[string]struct{}),
    33  		//async:     make(chan *AsyncAction),
    34  		report: env.report,
    35  	}
    36  
    37  	//go r.handleAsync()
    38  
    39  	return r
    40  }
    41  
    42  func (r *Runner) SetReport(rep Reporter) {
    43  	r.report = rep
    44  }
    45  
    46  func (r *Runner) AddNotify(n string) {
    47  	r.to_notify[n] = struct{}{}
    48  }
    49  
    50  func (r *Runner) ShouldRunHandler(name string) bool {
    51  	_, ok := r.to_notify[name]
    52  
    53  	return ok
    54  }
    55  
    56  /*
    57  func (r *Runner) AsyncChannel() chan *AsyncAction {
    58  	return r.async
    59  }
    60  */
    61  
    62  func (r *Runner) Run(env *Environment) error {
    63  	start := time.Now()
    64  	r.Start = start
    65  	var err error
    66  
    67  	defer func() {
    68  		r.Runtime = time.Since(start)
    69  	}()
    70  
    71  	r.report.StartTasks(r)
    72  
    73  	defer r.report.EndReporter(r, err)
    74  
    75  	for _, play := range r.plays {
    76  		fs := NewFutureScope(play.Vars)
    77  
    78  		for _, task := range play.Tasks {
    79  			err = r.runTask(env, play, task, fs, fs)
    80  			if err != nil {
    81  				return err
    82  			}
    83  		}
    84  
    85  		r.Results = append(r.Results, fs.Results()...)
    86  	}
    87  
    88  	r.report.FinishTasks(r)
    89  
    90  	r.wait.Wait()
    91  
    92  	StartHandlers := false
    93  
    94  	for _, play := range r.plays {
    95  		fs := NewFutureScope(play.Vars)
    96  
    97  		for _, task := range play.Handlers {
    98  			if r.ShouldRunHandler(task.Name()) {
    99  				if !StartHandlers {
   100  					r.report.StartHandlers(r)
   101  					StartHandlers = true
   102  				}
   103  				err := r.runTask(env, play, task, fs, fs)
   104  
   105  				if err != nil {
   106  					return err
   107  				}
   108  			}
   109  		}
   110  
   111  		fs.Wait()
   112  	}
   113  
   114  	if StartHandlers {
   115  		r.report.FinishHandlers(r)
   116  	}
   117  	r.report.FinishAll(r)
   118  
   119  	return nil
   120  }
   121  
   122  func RunAdhocTask(cmd, args string) (*Result, error) {
   123  	env := NewEnv(NewNestedScope(nil), &Config{})
   124  	defer env.Cleanup()
   125  
   126  	task := AdhocTask(cmd, args)
   127  
   128  	str, err := ExpandTemplates(env.Vars, task.Args())
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	obj, _, err := MakeCommand(env.Vars, task, str)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	//ar := &AdhocProgress{out: os.Stdout, Start: time.Now()}
   139  
   140  	ce := &CommandEnv{Env: env, Paths: env.Paths /*progress: ar*/}
   141  
   142  	return obj.Run(ce)
   143  }
   144  
   145  func RunAdhocTaskVars(td TaskData) (*Result, error) {
   146  	env := NewEnv(NewNestedScope(nil), &Config{})
   147  	defer env.Cleanup()
   148  
   149  	task := &Task{data: td}
   150  	task.Init(env)
   151  
   152  	obj, _, err := MakeCommand(env.Vars, task, "")
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	//ar := &AdhocProgress{out: os.Stdout, Start: time.Now()}
   158  
   159  	ce := &CommandEnv{Env: env, Paths: env.Paths /*progress: ar*/}
   160  
   161  	return obj.Run(ce)
   162  }
   163  
   164  func RunAdhocCommand(cmd Command, debug, output bool, args string) (*Result, error) {
   165  	env := NewEnv(NewNestedScope(nil), &Config{ShowCommandOutput: output, Debug: debug})
   166  	defer env.Cleanup()
   167  
   168  	//ar := &AdhocProgress{out: os.Stdout, Start: time.Now()}
   169  
   170  	ce := &CommandEnv{Env: env, Paths: env.Paths /*progress: ar*/}
   171  
   172  	return cmd.Run(ce)
   173  }
   174  
   175  type PriorityScope struct {
   176  	task Vars
   177  	rest Scope
   178  }
   179  
   180  func (p *PriorityScope) Get(key string) (Value, bool) {
   181  	if p.task != nil {
   182  		if v, ok := p.task[key]; ok {
   183  			return Any(v), true
   184  		}
   185  	}
   186  
   187  	return p.rest.Get(key)
   188  }
   189  
   190  func (p *PriorityScope) Set(key string, val interface{}) {
   191  	p.rest.Set(key, val)
   192  }
   193  
   194  func boolify(str string) bool {
   195  	switch str {
   196  	case "", "false", "no":
   197  		return false
   198  	default:
   199  		return true
   200  	}
   201  }
   202  
   203  type ModuleRun struct {
   204  	Play        *Play
   205  	Task        *Task
   206  	Module      *Module
   207  	Runner      *Runner
   208  	Scope       Scope
   209  	FutureScope *FutureScope
   210  	Vars        Vars
   211  }
   212  
   213  func (m *ModuleRun) Run(env *CommandEnv) (*Result, error) {
   214  	for _, task := range m.Module.ModTasks {
   215  		ns := NewNestedScope(m.Scope)
   216  
   217  		for k, v := range m.Vars {
   218  			ns.Set(k, v)
   219  		}
   220  
   221  		err := m.Runner.runTask(env.Env, m.Play, task, ns, m.FutureScope)
   222  		if err != nil {
   223  			return nil, err
   224  		}
   225  	}
   226  
   227  	return NewResult(true), nil
   228  }
   229  
   230  func (r *Runner) runTaskItems(env *Environment, play *Play, task *Task, s Scope, fs *FutureScope, start time.Time) error {
   231  
   232  	for _, item := range task.Items() {
   233  		ns := NewNestedScope(s)
   234  		ns.Set("item", item)
   235  
   236  		var res *Result
   237  
   238  		name, err := ExpandTemplates(ns, task.Name())
   239  		if err != nil {
   240  			res = FailureResult(err)
   241  			r.report.StartTask(task, task.Name(), "", nil)
   242  			r.report.FinishTask(task, res)
   243  			return err
   244  		}
   245  
   246  		str, err := ExpandTemplates(ns, task.Args())
   247  		if err != nil {
   248  			res = FailureResult(err)
   249  			r.report.StartTask(task, name, "", nil)
   250  			r.report.FinishTask(task, res)
   251  			return err
   252  		}
   253  
   254  		cmd, sm, err := MakeCommand(ns, task, str)
   255  		if err != nil {
   256  			res = FailureResult(err)
   257  			r.report.StartTask(task, name, str, nil)
   258  			r.report.FinishTask(task, res)
   259  			return err
   260  		}
   261  
   262  		r.report.StartTask(task, name, str, sm)
   263  
   264  		ce := NewCommandEnv(env, task)
   265  
   266  		res, err = cmd.Run(ce)
   267  		if err != nil && res == nil {
   268  			res = FailureResult(err)
   269  		}
   270  
   271  		if name := task.Register(); name != "" {
   272  			res.Data.Set("Changed", res.Changed)
   273  			res.Data.Set("Failed", res.Failed)
   274  			fs.Set(name, res)
   275  		}
   276  
   277  		runtime := time.Since(start)
   278  
   279  		r.Results = append(r.Results, RunResult{task, res, runtime})
   280  
   281  		r.report.FinishTask(task, res)
   282  
   283  		if err == nil {
   284  			for _, x := range task.Notify() {
   285  				r.AddNotify(x)
   286  			}
   287  		} else {
   288  			return err
   289  		}
   290  	}
   291  
   292  	return nil
   293  }
   294  
   295  func (r *Runner) runTask(env *Environment, play *Play, task *Task, s Scope, fs *FutureScope) error {
   296  	ps := &PriorityScope{task.IncludeVars, s}
   297  
   298  	start := time.Now()
   299  
   300  	if when := task.When(); when != "" {
   301  		when, err := ExpandVars(ps, when)
   302  
   303  		if err != nil {
   304  			return err
   305  		}
   306  
   307  		if !boolify(when) {
   308  			return nil
   309  		}
   310  	}
   311  
   312  	env.Vars = s
   313  	if items := task.Items(); items != nil {
   314  		return r.runTaskItems(env, play, task, s, fs, start)
   315  	}
   316  
   317  	name, err := ExpandTemplates(ps, task.Name())
   318  	if err != nil {
   319  		return err
   320  	}
   321  
   322  	str, err := ExpandTemplates(ps, task.Args())
   323  	if err != nil {
   324  		return err
   325  	}
   326  
   327  	var cmd Command
   328  
   329  	var argVars Vars
   330  
   331  	if mod, ok := play.Modules[task.Command()]; ok {
   332  		sm, err := ParseSimpleMap(s, str)
   333  		if err != nil {
   334  			return err
   335  		}
   336  
   337  		for ik, iv := range task.Vars {
   338  			if str, ok := iv.Read().(string); ok {
   339  				exp, err := ExpandTemplates(s, str)
   340  				if err != nil {
   341  					return err
   342  				}
   343  
   344  				sm[ik] = Any(exp)
   345  			} else {
   346  				sm[ik] = iv
   347  			}
   348  		}
   349  
   350  		cmd = &ModuleRun{
   351  			Play:        play,
   352  			Task:        task,
   353  			Module:      mod,
   354  			Runner:      r,
   355  			Scope:       s,
   356  			FutureScope: NewFutureScope(s),
   357  			Vars:        sm,
   358  		}
   359  
   360  		argVars = sm
   361  	} else {
   362  		cmd, argVars, err = MakeCommand(ps, task, str)
   363  
   364  		if err != nil {
   365  			return err
   366  		}
   367  	}
   368  
   369  	r.report.StartTask(task, name, str, argVars)
   370  
   371  	ce := NewCommandEnv(env, task)
   372  
   373  	if name := task.Future(); name != "" {
   374  		future := NewFuture(start, task, func() (*Result, error) {
   375  			return cmd.Run(ce)
   376  		})
   377  
   378  		fs.AddFuture(name, future)
   379  
   380  		return nil
   381  	}
   382  
   383  	/*
   384  		if task.Async() {
   385  			asyncAction := &AsyncAction{Task: task}
   386  			asyncAction.Init(r)
   387  
   388  			go func() {
   389  				asyncAction.Finish(cmd.Run(ce))
   390  			}()
   391  		} else { */
   392  	res, err := cmd.Run(ce)
   393  	if err != nil && res == nil {
   394  		res = FailureResult(err)
   395  	}
   396  
   397  	if name := task.Register(); name != "" {
   398  		res.Data.Set("changed", fmt.Sprintf("%v", res.Changed))
   399  		res.Data.Set("failed", fmt.Sprintf("%v", res.Failed))
   400  		fs.Set(name, res)
   401  	}
   402  
   403  	runtime := time.Since(start)
   404  
   405  	r.Results = append(r.Results, RunResult{task, res, runtime})
   406  
   407  	r.report.FinishTask(task, res)
   408  
   409  	if err == nil && res.Changed {
   410  		for _, x := range task.Notify() {
   411  			r.AddNotify(x)
   412  		}
   413  	} else {
   414  		return err
   415  	}
   416  	//}
   417  
   418  	return err
   419  }