func localhost() *henchman.Machine { tc := make(henchman.TransportConfig) local, _ := henchman.NewLocal(&tc) localhost := henchman.Machine{} localhost.Hostname = "127.0.0.1" localhost.Transport = local return &localhost }
func main() { username := flag.String("user", currentUsername().Username, "User to run as") usePassword := flag.Bool("password", false, "Use password authentication") keyfile := flag.String("private-keyfile", defaultKeyFile(), "Path to the keyfile") extraArgs := flag.String("args", "", "Extra arguments for the plan") hostsFile := flag.String("i", "", "Specify hosts file name") modulesDir, err := validateModulesPath() if err != nil { log.Fatalf("Couldn't stat modules path '%s'\n", modulesDir) } flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage: %s [args] <plan>\n\n", os.Args[0]) flag.PrintDefaults() } flag.Parse() planFile := flag.Arg(0) if planFile == "" { flag.Usage() os.Exit(1) } if *username == "" { fmt.Fprintf(os.Stderr, "Missing username") os.Exit(1) } // We support two SSH authentications methods for now // password and client key bases. Both are mutually exclusive and password takes // higher precedence tc := make(henchman.TransportConfig) tc["username"] = *username if *usePassword { var password string if password, err = gopass.GetPass("Password:"******"Couldn't get password: "******"password"] = password } else { tc["keyfile"] = *keyfile } planBuf, err := ioutil.ReadFile(planFile) if err != nil { log.Fatalf("Error reading plan - %s\n", planFile) } var hostsFileBuf []byte = nil if *hostsFile != "" { hostsFileBuf, err = ioutil.ReadFile(*hostsFile) if err != nil { log.Fatalf("Error reading hosts file - %s\n", *hostsFile) } } var plan *henchman.Plan parsedArgs := parseExtraArgs(*extraArgs) plan, err = henchman.NewPlanFromYAML(planBuf, hostsFileBuf, parsedArgs) if err != nil { log.Fatalf("Couldn't read the plan: %s", err) os.Exit(1) } fmt.Println(plan.Tasks) local := localhost() // Execute the same plan concurrently across all the machines. // Note the tasks themselves in plan are executed sequentially. wg := new(sync.WaitGroup) for _, hostname := range plan.Hosts { machine := henchman.Machine{} machine.Hostname = hostname machine.Transport = sshTransport(&tc, hostname) // initializes a map for "register" values for each host registerMap := make(map[string]interface{}) //renders all tasks in the plan file tasks, err := henchman.PrepareTasks(plan.Tasks, plan.Vars, machine) fmt.Println("\n" + "INITIAL TASKS") fmt.Println(tasks) fmt.Println() if err != nil { fmt.Println(err) } // for each host use the task list of the plan and run each task individually wg.Add(1) go func(machine *henchman.Machine) { defer wg.Done() // makes a temporary tasks to temper with for ndx := 0; ndx < len(tasks); ndx++ { var status *henchman.TaskStatus var err error task := tasks[ndx] // if there is a valid include field within task // update task list // else // do standard task run procedure whenVal, err := task.ProcessWhen(registerMap) if err != nil { log.Println("Error at When Eval at task: " + task.Name) log.Println("Error: " + err.Error()) } if whenVal == true { if task.Include != "" { tasks, err = henchman.UpdateTasks(tasks, task.Vars, ndx, *machine) fmt.Println("\n" + "NESTED TASKS at " + machine.Hostname) fmt.Println(tasks) fmt.Println() if err != nil { log.Println("Error at Include Eval at task: " + task.Name) log.Println("Error: " + err.Error()) } } else { if tasks[ndx].LocalAction { log.Printf("Local action detected\n") status, err = task.Run(local, registerMap) } else { status, err = task.Run(machine, registerMap) } plan.SaveStatus(task, status.Status) if err != nil { log.Printf("Error when executing task: %s\n", err.Error()) } if status.Status == "failure" { log.Printf("Task was unsuccessful: %s\n", task.Id) break } } } } }(&machine) } wg.Wait() plan.PrintReport() }