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 }