Last active
June 26, 2016 20:43
-
-
Save martinrusev/c62f5acd38b97ccac081695eb181ad60 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| func main() { | |
| cmd := os.Args[1] // the first argument is a command we’ll execute on all servers | |
| hosts := os.Args[2:] // other arguments (starting from the second one) – the list of servers | |
| results := make(chan string, 10) // we’ll write results into the buffered channel of strings | |
| timeout := time.After(5 * time.Second) // in 5 seconds the message will come to timeout channel | |
| // initialize the structure with the configuration for ssh packat. | |
| // makeKeyring() function will be written later | |
| config := &ssh.ClientConfig{ | |
| User: os.Getenv("LOGNAME"), | |
| Auth: []ssh.ClientAuth{makeKeyring()}, | |
| } | |
| // running one goroutine (light-weight alternative of OS thread) per server, | |
| // executeCmd() function will be written later | |
| for _, hostname := range hosts { | |
| go func(hostname string) { | |
| results <- executeCmd(cmd, hostname, config) | |
| }(hostname) | |
| } | |
| // collect results from all the servers or print "Timed out", | |
| // if the total execution time has expired | |
| for i := 0; i < len(hosts); i++ { | |
| select { | |
| case res := <-results: | |
| fmt.Print(res) | |
| case <-timeout: | |
| fmt.Println("Timed out!") | |
| return | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import "fmt" | |
| import "sync" | |
| import "time" | |
| type Error struct { | |
| message string | |
| } | |
| func (e Error) Error() string { | |
| return e.message | |
| } | |
| func main() { | |
| var wg sync.WaitGroup | |
| waitGroupLength := 8 | |
| errChannel := make(chan error, 1) | |
| // Setup waitgroup to match the number of go routines we'll launch off | |
| wg.Add(waitGroupLength) | |
| finished := make(chan bool, 1) // this along with wg.Wait() are why the error handling works and doesn't deadlock | |
| for i := 0; i < waitGroupLength; i++ { | |
| go func(i int) { | |
| fmt.Printf("Go routine %d executed\n", i+1) | |
| // Sleep for the time needed for each other go routine to complete. | |
| // This helps show that the program exists with the last go routine to fail. | |
| // comment this line if you want to see it fail fast | |
| time.Sleep(time.Duration(waitGroupLength - i)) | |
| time.Sleep(0) // only here so the time import is needed | |
| // comment out the following 3 lines to see what happens without an error | |
| // Note, the channel has a length of one so the last go routine to error | |
| // will always be the last error. | |
| if i%4 == 1 { | |
| errChannel <- Error{fmt.Sprintf("Errored on routine %d", i+1)} | |
| } | |
| // Mark the wait group as Done so it does not hang | |
| wg.Done() | |
| }(i) | |
| } | |
| // Put the wait group in a go routine. | |
| // By putting the wait group in the go routine we ensure either all pass | |
| // and we close the "finished" channel or we wait forever for the wait group | |
| // to finish. | |
| // | |
| // Waiting forever is okay because of the blocking select below. | |
| go func() { | |
| wg.Wait() | |
| close(finished) | |
| }() | |
| // This select will block until one of the two channels returns a value. | |
| // This means on the first failure in the go routines above the errChannel will release a | |
| // value first. Because there is a "return" statement in the err check this function will | |
| // exit when an error occurs. | |
| // | |
| // Due to the blocking on wg.Wait() the finished channel will not get a value unless all | |
| // the go routines before were successful because not all the wg.Done() calls would have | |
| // happened. | |
| select { | |
| case <-finished: | |
| case err := <-errChannel: | |
| if err != nil { | |
| fmt.Println("error ", err) | |
| return | |
| } | |
| } | |
| fmt.Println("Successfully executed all go routines") | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment