forked from hashicorp/nomad
-
Notifications
You must be signed in to change notification settings - Fork 0
/
connect_hook.go
119 lines (96 loc) · 2.98 KB
/
connect_hook.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package taskrunner
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/client/allocdir"
"github.com/hashicorp/nomad/client/allocrunner/interfaces"
agentconsul "github.com/hashicorp/nomad/command/agent/consul"
"github.com/hashicorp/nomad/nomad/structs"
)
var _ interfaces.TaskPrestartHook = &connectHook{}
// connectHook writes the bootstrap config for the envoy sidecar proxy
type connectHook struct {
alloc *structs.Allocation
consulHTTPAddr string
logger log.Logger
}
func newConnectHook(logger log.Logger, alloc *structs.Allocation, consulHTTPAddr string) *connectHook {
h := &connectHook{
alloc: alloc,
consulHTTPAddr: consulHTTPAddr,
}
h.logger = logger.Named(h.Name())
return h
}
func (connectHook) Name() string {
return "connect"
}
func (h *connectHook) Prestart(ctx context.Context, req *interfaces.TaskPrestartRequest, resp *interfaces.TaskPrestartResponse) error {
//TODO(schmichael) this is a pretty silly way of only running for one task
if req.Task.Name != "nomad_envoy" {
resp.Done = true
return nil
}
tg := h.alloc.Job.LookupTaskGroup(h.alloc.TaskGroup)
var service *structs.Service
for _, s := range tg.Services {
if s.Connect.HasSidecar() {
service = s
break
}
}
if service == nil {
return fmt.Errorf("envoy sidecar task exists but no services configured with a sidecar")
}
canary := false
if h.alloc.DeploymentStatus != nil {
canary = h.alloc.DeploymentStatus.Canary
}
//TODO(schmichael) Will this path work for all drivers?
grpcAddr := "unix://" + allocdir.TaskGRPCSocket
fn := filepath.Join(req.TaskDir.LocalDir, "bootstrap.json")
id := agentconsul.MakeTaskServiceID(h.alloc.ID, "group-"+tg.Name, service, canary)
h.logger.Debug("bootstrapping envoy", "sidecar_for", service.Name, "boostrap_file", fn, "sidecar_for_id", id, "grpc_addr", grpcAddr)
tries := 3
// Before running bootstrap command, ensure service has been registered
//TODO(schmichael) well this is one way to do it
RETRY:
tries--
//TODO(schmichael) run via docker container instead of host?
cmd := exec.CommandContext(ctx, "consul", "connect", "envoy",
"-grpc-addr", grpcAddr,
"-http-addr", h.consulHTTPAddr,
"-bootstrap",
"-sidecar-for", id, // must use the id not the name!
)
// Redirect output to local/bootstrap.json
fd, err := os.Create(fn)
if err != nil {
return fmt.Errorf("error creating local/bootstrap.json for envoy: %v", err)
}
cmd.Stdout = fd
buf := bytes.NewBuffer(nil)
cmd.Stderr = buf
// Generate bootstrap
err = cmd.Run()
// Close stdout/bootstrap.json
fd.Close()
// Check for error from command
if err != nil {
if tries > 0 {
time.Sleep(3 * time.Second)
goto RETRY
}
h.logger.Error("error creating bootstrap.json for envoy", "error", err, "stderr", buf.String())
return fmt.Errorf("error creating bootstrap.json for envoy: %v", err)
}
// Bootstrap written. Mark as done and move on.
resp.Done = true
return nil
}