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  }