13 "github.com/hashicorp/consul/api"
16 var consul = &consulBooter{}
18 type consulBooter struct {
22 func (cb *consulBooter) Boot(ctx context.Context) error {
26 if cb.check(ctx) == nil {
30 bin := cfg.UsrDir + "/bin/consul"
32 URL: "https://releases.hashicorp.com/consul/0.7.2/consul_0.7.2_linux_amd64.zip",
40 dataDir := path.Join(cfg.DataDir, "consul")
41 if err := os.MkdirAll(dataDir, 0700); err != nil {
44 args := []string{"agent"}
46 cf := path.Join(cfg.DataDir, "consul-encrypt.json")
47 if _, err := os.Stat(cf); err != nil && !os.IsNotExist(err) {
49 } else if err != nil {
50 key, err := exec.Command(bin, "keygen").CombinedOutput()
54 if err = atomicWriteJSON(cf, map[string]interface{}{
55 "encrypt": strings.TrimSpace(string(key)),
56 }, 0400); err != nil {
60 args = append(args, "-config-file="+cf)
63 cf := path.Join(cfg.DataDir, "consul-ports.json")
64 err = atomicWriteJSON(cf, map[string]interface{}{
65 "client_addr": "0.0.0.0",
66 "bootstrap_expect": len(cfg.ControlHosts),
68 "datacenter": cfg.SiteID,
71 "ports": map[string]int{
72 "dns": cfg.Ports.ConsulDNS,
73 "http": cfg.Ports.ConsulHTTP,
74 "https": cfg.Ports.ConsulHTTPS,
75 "rpc": cfg.Ports.ConsulRPC,
76 "serf_lan": cfg.Ports.ConsulSerfLAN,
77 "serf_wan": cfg.Ports.ConsulSerfWAN,
78 "server": cfg.Ports.ConsulServer,
84 args = append(args, "-config-file="+cf)
86 supervisor := newSupervisor(ctx, "arvados-consul", bin, args...)
87 running, err := supervisor.Running(ctx)
92 defer feedbackf(ctx, "starting consul service")()
93 err = supervisor.Start(ctx)
95 return fmt.Errorf("starting consul: %s", err)
97 if len(cfg.ControlHosts) > 1 {
98 cmd := exec.Command(bin, append([]string{"join"}, cfg.ControlHosts...)...)
99 cmd.Stdout = os.Stderr
100 cmd.Stderr = os.Stderr
103 return fmt.Errorf("consul join: %s", err)
107 return waitCheck(ctx, 30*time.Second, cb.check)
110 var consulCfg = api.DefaultConfig()
112 func (cb *consulBooter) check(ctx context.Context) error {
114 consulCfg.Address = fmt.Sprintf("127.0.0.1:%d", cfg.Ports.ConsulHTTP)
115 consulCfg.Datacenter = cfg.SiteID
116 consul, err := api.NewClient(consulCfg)
120 _, err = consul.Catalog().Datacenters()
127 // OnlyNode returns true if this is the only consul node.
128 func (cb *consulBooter) OnlyNode() (bool, error) {
129 c, err := api.NewClient(consulCfg)
133 nodes, _, err := c.Catalog().Nodes(nil)
134 return len(nodes) == 1, err