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

     1  package goansible
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  	"sync"
     9  )
    10  
    11  type ResultData map[string]Value
    12  
    13  func (rd ResultData) Set(key string, v interface{}) {
    14  	rd[key] = Any(v)
    15  }
    16  
    17  func (rd ResultData) Get(key string) interface{} {
    18  	if a, ok := rd[key]; !ok {
    19  		return nil
    20  	} else {
    21  		return a.Read()
    22  	}
    23  }
    24  
    25  type Result struct {
    26  	Changed bool
    27  	Failed  bool
    28  	Data    ResultData
    29  }
    30  
    31  func (r *Result) MarshalJSON() ([]byte, error) {
    32  	o := make(map[string]interface{})
    33  	m := make(map[string]interface{})
    34  
    35  	o["changed"] = r.Changed
    36  	o["failed"] = r.Failed
    37  	o["data"] = m
    38  
    39  	for k, v := range r.Data {
    40  		m[k] = v.Read()
    41  	}
    42  
    43  	return json.Marshal(o)
    44  }
    45  
    46  func (r *Result) Get(key string) (Value, bool) {
    47  	v, ok := r.Data[key]
    48  
    49  	return v, ok
    50  }
    51  
    52  func (r *Result) Add(key string, v interface{}) {
    53  	r.Data[key] = Any(v)
    54  }
    55  
    56  func WrapResult(changed bool, data ResultData) *Result {
    57  	return &Result{changed, false, data}
    58  }
    59  
    60  func NewResult(changed bool) *Result {
    61  	return &Result{changed, false, make(ResultData)}
    62  }
    63  
    64  func FailureResult(err error) *Result {
    65  	res := &Result{false, true, make(ResultData)}
    66  	res.Add("error", err.Error())
    67  
    68  	return res
    69  }
    70  
    71  type CommandEnv struct {
    72  	Env   *Environment
    73  	Paths Paths
    74  	//progress ProgressReporter
    75  }
    76  
    77  func NewCommandEnv(env *Environment, task *Task) *CommandEnv {
    78  	return &CommandEnv{
    79  		Env:   env,
    80  		Paths: task.Paths,
    81  		//progress: env.report,
    82  	}
    83  }
    84  
    85  /*
    86  func (e *CommandEnv) Progress(str string) {
    87  	if e.progress == nil {
    88  		fmt.Printf("=== %s\n", str)
    89  	} else {
    90  		e.progress.Progress(str)
    91  	}
    92  }
    93  */
    94  
    95  type Command interface {
    96  	Run(env *CommandEnv) (*Result, error)
    97  }
    98  
    99  type ArgParser interface {
   100  	ParseArgs(s Scope, args string) (Vars, error)
   101  }
   102  
   103  type Commands map[string]reflect.Type
   104  
   105  var AvailableCommands Commands
   106  
   107  var initAvailable sync.Once
   108  
   109  func RegisterCommand(name string, cmd Command) {
   110  	initAvailable.Do(func() {
   111  		AvailableCommands = make(Commands)
   112  	})
   113  
   114  	ref := reflect.ValueOf(cmd)
   115  	e := ref.Elem()
   116  
   117  	AvailableCommands[name] = e.Type()
   118  }
   119  
   120  func MakeCommand(s Scope, task *Task, args string) (Command, Vars, error) {
   121  	name := task.Command()
   122  
   123  	t, ok := AvailableCommands[name]
   124  
   125  	if !ok {
   126  		return nil, nil, fmt.Errorf("Unknown command: %s", name)
   127  	}
   128  
   129  	obj := reflect.New(t)
   130  
   131  	var sm Vars
   132  	var err error
   133  
   134  	if ap, ok := obj.Interface().(ArgParser); ok {
   135  		sm, err = ap.ParseArgs(s, args)
   136  	} else {
   137  		sm, err = ParseSimpleMap(s, args)
   138  	}
   139  
   140  	if err != nil {
   141  		return nil, nil, err
   142  	}
   143  
   144  	for ik, iv := range task.Vars {
   145  		if str, ok := iv.Read().(string); ok {
   146  			exp, err := ExpandTemplates(s, str)
   147  			if err != nil {
   148  				return nil, nil, err
   149  			}
   150  
   151  			sm[ik] = Any(exp)
   152  		} else {
   153  			sm[ik] = iv
   154  		}
   155  	}
   156  
   157  	e := obj.Elem()
   158  
   159  	for i := 0; i < t.NumField(); i++ {
   160  		f := t.Field(i)
   161  
   162  		name := strings.ToLower(f.Name)
   163  		required := false
   164  
   165  		parts := strings.Split(f.Tag.Get("goansible"), ",")
   166  
   167  		switch len(parts) {
   168  		case 0:
   169  			// nothing
   170  		case 1:
   171  			name = parts[0]
   172  		case 2:
   173  			name = parts[0]
   174  			switch parts[1] {
   175  			case "required":
   176  				required = true
   177  			default:
   178  				return nil, nil, fmt.Errorf("Unsupported tag flag: %s", parts[1])
   179  			}
   180  		}
   181  
   182  		if val, ok := sm[name]; ok {
   183  			ef := e.Field(i)
   184  
   185  			switch ef.Interface().(type) {
   186  			case bool:
   187  				ef.Set(reflect.ValueOf(val.Read()))
   188  			case int, uint, uint32, uint64:
   189  				ef.Set(reflect.ValueOf(val.Read()))
   190  			case map[string]string:
   191  				iv := val.Read()
   192  				m := make(map[string]string)
   193  
   194  				switch iv := iv.(type) {
   195  				case map[interface{}]interface{}:
   196  					for k, v := range iv {
   197  						m[fmt.Sprintf("%v", k)] = fmt.Sprintf("%v", v)
   198  					}
   199  				case map[string]interface{}:
   200  					for k, v := range iv {
   201  						m[k] = fmt.Sprintf("%v", v)
   202  					}
   203  				case map[string]string:
   204  					m = iv
   205  				}
   206  
   207  				ef.Set(reflect.ValueOf(m))
   208  			default:
   209  				val := fmt.Sprintf("%v", val.Read())
   210  				enum := f.Tag.Get("enum")
   211  				if enum != "" {
   212  					found := false
   213  
   214  					for _, p := range strings.Split(enum, ",") {
   215  						if p == val {
   216  							found = true
   217  							break
   218  						}
   219  					}
   220  
   221  					if !found {
   222  						return nil, nil, fmt.Errorf("Invalid value '%s' for variable '%s'. Possibles: %s", val, name, enum)
   223  					}
   224  				}
   225  
   226  				ef.Set(reflect.ValueOf(val))
   227  			}
   228  		} else if required {
   229  			return nil, nil, fmt.Errorf("Missing value for %s", f.Name)
   230  		}
   231  	}
   232  
   233  	return obj.Interface().(Command), sm, nil
   234  }