github.com/dustinrc/deis@v1.10.1-0.20150917223407-0894a5fb979e/builder/confd/confd.go (about) 1 // Package confd provides basic Confd support. 2 // 3 // Right now, this library is highly specific to the needs of the present 4 // builder. Because the confd library is not all public, we don't use it directly. 5 // Instead, we invoke the CLI. 6 package confd 7 8 import ( 9 "fmt" 10 "os" 11 "os/exec" 12 "strconv" 13 "time" 14 15 "github.com/Masterminds/cookoo" 16 "github.com/Masterminds/cookoo/log" 17 "github.com/Masterminds/cookoo/safely" 18 ) 19 20 // defaultEtcd is the default Etcd host. 21 const defaultEtcd = "127.0.0.1:4001" 22 23 // RunOnce runs the equivalent of `confd --onetime`. 24 // 25 // This may run the process repeatedly until either we time out (~20 minutes) or 26 // the templates are successfully built. 27 // 28 // Importantly, this blocks until the run is complete. 29 // 30 // Params: 31 // - node (string): The etcd node to use. (Only etcd is currently supported) 32 // 33 // Returns: 34 // - The []bytes from stdout and stderr when running the program. 35 // 36 func RunOnce(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) { 37 node := p.Get("node", defaultEtcd).(string) 38 39 dargs := []string{"-onetime", "-node", node, "-log-level", "error"} 40 41 log.Info(c, "Building confd templates. This may take a moment.") 42 43 limit := 1200 44 timeout := time.Second * 3 45 var lasterr error 46 start := time.Now() 47 for i := 0; i < limit; i++ { 48 if out, err := exec.Command("confd", dargs...).CombinedOutput(); err == nil { 49 log.Infof(c, "Templates generated for %s on run %d", node, i) 50 return out, nil 51 } else { 52 log.Debugf(c, "Recoverable error: %s", err) 53 log.Debugf(c, "Output: %q", out) 54 lasterr = err 55 } 56 57 time.Sleep(timeout) 58 log.Infof(c, "Re-trying template build. (Elapsed time: %d)", time.Now().Sub(start)/time.Second) 59 } 60 61 return nil, fmt.Errorf("Could not build confd templates before timeout. Last error: %s", lasterr) 62 } 63 64 // Run starts confd and runs it in the background. 65 // 66 // If the command fails immediately on startup, an error is immediately 67 // returned. But from that point, a goroutine watches the command and 68 // reports if the command dies. 69 // 70 // Params: 71 // - node (string): The etcd node to use. (Only etcd is currently supported) 72 // - interval (int, default:5): The rebuilding interval. 73 // 74 // Returns 75 // bool true if this succeeded. 76 func Run(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) { 77 node := p.Get("node", defaultEtcd).(string) 78 interval := strconv.Itoa(p.Get("interval", 5).(int)) 79 80 cmd := exec.Command("confd", "-log-level", "error", "-node", node, "-interval", interval) 81 if err := cmd.Start(); err != nil { 82 return false, err 83 } 84 85 log.Infof(c, "Watching confd.") 86 safely.Go(func() { 87 if err := cmd.Wait(); err != nil { 88 // If confd exits, builder will stop functioning as intended. So 89 // we stop builder and let the environment restart. 90 log.Errf(c, "Stopping builder. confd exited with error: %s", err) 91 os.Exit(37) 92 } 93 }) 94 95 return true, nil 96 }