Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

weaveworks / weave / #3760

22 Sep 2015 - 13:52 coverage increased (+0.3%) to 77.586%
#3760

Pull #1449

circleci

Eac6f57e46837c7b0d0bacac4288d0b9?size=18&default=identiconpaulbellamy
Removing confusing variable from getDNSDomain, just return "" when disabled
Pull Request #1449: Override sneaky /start HostConfig params

44 of 70 new or added lines in 3 files covered. (62.86%)

3 existing lines in 1 file now uncovered.

5317 of 6853 relevant lines covered (77.59%)

126843.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.25
/proxy/proxy.go
1
package proxy
2

3
import (
4
        "crypto/tls"
5
        "errors"
6
        "fmt"
7
        "io/ioutil"
8
        "net"
9
        "net/http"
10
        "os"
11
        "regexp"
12
        "strings"
13
        "sync"
14
        "syscall"
15

16
        docker "github.com/fsouza/go-dockerclient"
17
        "github.com/paulbellamy/weave/nameserver"
18
        "github.com/paulbellamy/weave/router"
19
        . "github.com/weaveworks/weave/common"
20
        weavedocker "github.com/weaveworks/weave/common/docker"
21
)
22

23
const (
24
        defaultCaFile   = "ca.pem"
25
        defaultKeyFile  = "key.pem"
26
        defaultCertFile = "cert.pem"
27
        dockerSock      = "/var/run/docker.sock"
28
        dockerSockUnix  = "unix://" + dockerSock
29
)
30

31
var (
32
        containerCreateRegexp  = dockerAPIEndpoint("containers/create")
33
        containerStartRegexp   = dockerAPIEndpoint("containers/[^/]*/(re)?start")
34
        containerInspectRegexp = dockerAPIEndpoint("containers/[^/]*/json")
35
        execCreateRegexp       = dockerAPIEndpoint("containers/[^/]*/exec")
36
        execInspectRegexp      = dockerAPIEndpoint("exec/[^/]*/json")
37

38
        ErrWeaveCIDRNone = errors.New("the container was created with the '-e WEAVE_CIDR=none' option")
39
        ErrNoDefaultIPAM = errors.New("the container was created without specifying an IP address with '-e WEAVE_CIDR=...' and the proxy was started with the '--no-default-ipalloc' option")
40
)
41

42
func dockerAPIEndpoint(endpoint string) *regexp.Regexp {
205×
43
        return regexp.MustCompile("^(/v[0-9\\.]*)?/" + endpoint + "$")
205×
44
}
205×
45

46
type Config struct {
47
        HostnameFromLabel   string
48
        HostnameMatch       string
49
        HostnameReplacement string
50
        ListenAddrs         []string
51
        RewriteInspect      bool
52
        NoDefaultIPAM       bool
53
        NoRewriteHosts      bool
54
        TLSConfig           TLSConfig
55
        Version             string
56
        WithDNS             bool
57
        WithoutDNS          bool
58
}
59

60
type wait struct {
61
        ident string
62
        ch    chan struct{}
63
}
64

65
type Proxy struct {
66
        sync.Mutex
67
        Config
68
        client              *docker.Client
69
        dockerBridgeIP      string
70
        hostnameMatchRegexp *regexp.Regexp
71
        weaveWaitVolume     string
72
        waiters             map[*http.Request]*wait
73
}
74

75
func NewProxy(c Config) (*Proxy, error) {
40×
76
        p := &Proxy{Config: c, waiters: make(map[*http.Request]*wait)}
40×
77

40×
78
        if err := p.TLSConfig.loadCerts(); err != nil {
!
79
                Log.Fatalf("Could not configure tls for proxy: %s", err)
!
80
        }
!
81

82
        // We pin the protocol version to 1.15 (which corresponds to
83
        // Docker 1.3.x; the earliest version supported by weave) in order
84
        // to insulate ourselves from breaking changes to the API, as
85
        // happened in 1.20 (Docker 1.8.0) when the presentation of
86
        // volumes changed in `inspect`.
87
        client, err := weavedocker.NewVersionedClient(dockerSockUnix, "1.15")
40×
88
        if err != nil {
!
89
                return nil, err
!
90
        }
!
91
        p.client = client.Client
40×
92

40×
93
        if !p.WithoutDNS {
39×
94
                dockerBridgeIP, stderr, err := callWeave("docker-bridge-ip")
39×
95
                if err != nil {
!
96
                        return nil, fmt.Errorf(string(stderr))
!
97
                }
!
98
                p.dockerBridgeIP = string(dockerBridgeIP)
39×
99
        }
100

101
        p.hostnameMatchRegexp, err = regexp.Compile(c.HostnameMatch)
40×
102
        if err != nil {
!
103
                err := fmt.Errorf("Incorrect hostname match '%s': %s", c.HostnameMatch, err.Error())
!
104
                return nil, err
!
105
        }
!
106

107
        if err = p.findWeaveWaitVolume(); err != nil {
!
108
                return nil, err
!
109
        }
!
110

111
        client.AddObserver(p)
40×
112

40×
113
        return p, nil
40×
114
}
115

116
func (proxy *Proxy) AttachExistingContainers() {
40×
117
        containers, _ := proxy.client.ListContainers(docker.ListContainersOptions{})
40×
118
        for _, cont := range containers {
115×
119
                if strings.HasPrefix(cont.Command, weaveWaitEntrypoint[0]) {
2×
120
                        proxy.ContainerStarted(cont.ID)
2×
121
                }
2×
122
        }
123
}
124

125
func (proxy *Proxy) Dial() (net.Conn, error) {
678×
126
        return net.Dial("unix", dockerSock)
678×
127
}
678×
128

129
func (proxy *Proxy) findWeaveWaitVolume() error {
40×
130
        container, err := proxy.client.InspectContainer("weaveproxy")
40×
131
        if err != nil {
!
132
                return fmt.Errorf("Could not find the weavewait volume: %s", err)
!
133
        }
!
134

135
        if container.Volumes == nil {
!
136
                return fmt.Errorf("Could not find the weavewait volume")
!
137
        }
!
138

139
        volume, ok := container.Volumes["/w"]
40×
140
        if !ok {
!
141
                return fmt.Errorf("Could not find the weavewait volume")
!
142
        }
!
143

144
        proxy.weaveWaitVolume = volume
40×
145
        return nil
40×
146
}
147

148
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
680×
149
        Log.Infof("%s %s", r.Method, r.URL)
680×
150
        path := r.URL.Path
680×
151
        var i interceptor
680×
152
        switch {
680×
153
        case containerCreateRegexp.MatchString(path):
104×
154
                i = &createContainerInterceptor{proxy}
104×
155
        case containerStartRegexp.MatchString(path):
131×
156
                i = &startContainerInterceptor{proxy}
131×
157
        case containerInspectRegexp.MatchString(path):
48×
158
                i = &inspectContainerInterceptor{proxy}
48×
159
        case execCreateRegexp.MatchString(path):
19×
160
                i = &createExecInterceptor{proxy}
19×
161
        case execInspectRegexp.MatchString(path):
14×
162
                i = &inspectExecInterceptor{proxy}
14×
163
        default:
364×
164
                i = &nullInterceptor{}
364×
165
        }
166
        proxy.Intercept(i, w, r)
680×
167
}
168

169
func (proxy *Proxy) Listen() []net.Listener {
40×
170
        listeners := []net.Listener{}
40×
171
        addrs := []string{}
40×
172
        for _, addr := range proxy.ListenAddrs {
40×
173
                listener, normalisedAddr, err := proxy.listen(addr)
40×
174
                if err != nil {
!
175
                        Log.Fatalf("Cannot listen on %s: %s", addr, err)
!
176
                }
!
177
                listeners = append(listeners, listener)
40×
178
                addrs = append(addrs, normalisedAddr)
40×
179
        }
180

181
        for _, addr := range addrs {
40×
182
                Log.Infoln("proxy listening on", addr)
40×
183
        }
40×
184
        return listeners
40×
185
}
186

187
func (proxy *Proxy) Serve(listeners []net.Listener) {
40×
188
        errs := make(chan error)
40×
189
        for _, listener := range listeners {
40×
190
                go func(listener net.Listener) {
40×
191
                        errs <- (&http.Server{Handler: proxy}).Serve(listener)
40×
192
                }(listener)
40×
193
        }
194
        for range listeners {
40×
195
                err := <-errs
40×
196
                if err != nil {
!
197
                        Log.Fatalf("Serve failed: %s", err)
!
198
                }
!
199
        }
200
}
201

202
func copyOwnerAndPermissions(from, to string) error {
1×
203
        stat, err := os.Stat(from)
1×
204
        if err != nil {
!
205
                return err
!
206
        }
!
207
        if err = os.Chmod(to, stat.Mode()); err != nil {
!
208
                return err
!
209
        }
!
210

211
        moreStat, ok := stat.Sys().(*syscall.Stat_t)
1×
212
        if !ok {
!
213
                return nil
!
214
        }
!
215

216
        if err = os.Chown(to, int(moreStat.Uid), int(moreStat.Gid)); err != nil {
!
217
                return err
!
218
        }
!
219

220
        return nil
1×
221
}
222

223
func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
40×
224
        var (
40×
225
                listener    net.Listener
40×
226
                err         error
40×
227
                proto, addr string
40×
228
        )
40×
229

40×
230
        if protoAddrParts := strings.SplitN(protoAndAddr, "://", 2); len(protoAddrParts) == 2 {
40×
231
                proto, addr = protoAddrParts[0], protoAddrParts[1]
40×
232
        } else if strings.HasPrefix(protoAndAddr, "/") {
!
233
                proto, addr = "unix", protoAndAddr
!
234
        } else {
!
235
                proto, addr = "tcp", protoAndAddr
!
236
        }
!
237

238
        switch proto {
40×
239
        case "tcp":
39×
240
                listener, err = net.Listen(proto, addr)
39×
241
                if err != nil {
!
242
                        return nil, "", err
!
243
                }
!
244
                if proxy.TLSConfig.enabled() {
1×
245
                        listener = tls.NewListener(listener, proxy.TLSConfig.Config)
1×
246
                }
1×
247

248
        case "unix":
1×
249
                // remove socket from last invocation
1×
250
                if err := os.Remove(addr); err != nil && !os.IsNotExist(err) {
!
251
                        return nil, "", err
!
252
                }
!
253
                listener, err = net.Listen(proto, addr)
1×
254
                if err != nil {
!
255
                        return nil, "", err
!
256
                }
!
257
                if err = copyOwnerAndPermissions(dockerSock, addr); err != nil {
!
258
                        return nil, "", err
!
259
                }
!
260

261
        default:
!
262
                Log.Fatalf("Invalid protocol format: %q", proto)
!
263
        }
264

265
        return listener, fmt.Sprintf("%s://%s", proto, addr), nil
40×
266
}
267

268
// weavedocker.ContainerObserver interface
269
func (proxy *Proxy) ContainerStarted(ident string) {
370×
270
        container, err := proxy.client.InspectContainer(ident)
370×
271
        if err != nil {
!
272
                Log.Warningf("Error inspecting container %s: %v", ident, err)
!
273
                return
!
274
        }
!
275
        // If this was a container we modified the entrypoint for, attach it to the network
276
        if containerShouldAttach(container) {
62×
277
                proxy.attach(container)
62×
278
        }
62×
279
        proxy.notifyWaiters(container.ID)
370×
280
}
281

282
func containerShouldAttach(container *docker.Container) bool {
501×
283
        return len(container.Config.Entrypoint) > 0 && container.Config.Entrypoint[0] == weaveWaitEntrypoint[0]
501×
284
}
501×
285

286
func (proxy *Proxy) createWait(r *http.Request, ident string) {
131×
287
        proxy.Lock()
131×
288
        ch := make(chan struct{})
131×
289
        proxy.waiters[r] = &wait{ident: ident, ch: ch}
131×
290
        proxy.Unlock()
131×
291
}
131×
292

293
func (proxy *Proxy) removeWait(r *http.Request) {
131×
294
        proxy.Lock()
131×
295
        delete(proxy.waiters, r)
131×
296
        proxy.Unlock()
131×
297
}
131×
298

299
func (proxy *Proxy) notifyWaiters(ident string) {
370×
300
        proxy.Lock()
370×
301
        for _, wait := range proxy.waiters {
130×
302
                if ident == wait.ident && wait.ch != nil {
130×
303
                        close(wait.ch)
130×
304
                        wait.ch = nil
130×
305
                }
130×
306
        }
307
        proxy.Unlock()
370×
308
}
309

310
func (proxy *Proxy) waitForStart(r *http.Request) {
130×
311
        var ch chan struct{}
130×
312
        proxy.Lock()
130×
313
        wait, found := proxy.waiters[r]
130×
314
        if found {
130×
315
                ch = wait.ch
130×
316
        }
130×
317
        proxy.Unlock()
130×
318
        if ch != nil {
74×
319
                Log.Debugf("Wait for start of container %s", wait.ident)
74×
320
                <-ch
74×
321
        }
74×
322
}
323

324
func (proxy *Proxy) ContainerDied(ident string) {
378×
325
}
378×
326

327
func (proxy *Proxy) attach(container *docker.Container) error {
62×
328
        cidrs, err := proxy.weaveCIDRsFromConfig(container.Config, container.HostConfig)
62×
329
        if err != nil {
1×
330
                Log.Infof("Leaving container %s alone because %s", container.ID, err)
1×
331
                return nil
1×
332
        }
1×
333
        Log.Infof("Attaching container %s with WEAVE_CIDR \"%s\" to weave network", container.ID, strings.Join(cidrs, " "))
61×
334
        args := []string{"attach"}
61×
335
        args = append(args, cidrs...)
61×
336
        if !proxy.NoRewriteHosts {
60×
337
                args = append(args, "--rewrite-hosts")
60×
338
        }
60×
339
        args = append(args, "--or-die", container.ID)
61×
340
        if _, stderr, err := callWeave(args...); err != nil {
!
341
                Log.Warningf("Attaching container %s to weave network failed: %s", container.ID, string(stderr))
!
342
                return errors.New(string(stderr))
!
343
        } else if len(stderr) > 0 {
!
344
                Log.Warningf("Attaching container %s to weave network: %s", container.ID, string(stderr))
!
345
        }
!
346

347
        return nil
61×
348
}
349

350
func (proxy *Proxy) weaveCIDRsFromConfig(config *docker.Config, hostConfig *docker.HostConfig) ([]string, error) {
175×
351
        netMode := ""
175×
352
        if hostConfig != nil {
125×
353
                netMode = hostConfig.NetworkMode
125×
354
        }
125×
355
        if netMode == "host" || strings.HasPrefix(netMode, "container:") {
8×
356
                return nil, fmt.Errorf("the container was created with the '--net=%s'", netMode)
8×
357
        }
8×
358
        for _, e := range config.Env {
47×
359
                if strings.HasPrefix(e, "WEAVE_CIDR=") {
43×
360
                        if e[11:] == "none" {
2×
361
                                return nil, ErrWeaveCIDRNone
2×
362
                        }
2×
363
                        return strings.Fields(e[11:]), nil
41×
364
                }
365
        }
366
        if proxy.NoDefaultIPAM {
63×
367
                return nil, ErrNoDefaultIPAM
63×
368
        }
63×
369
        return nil, nil
61×
370
}
371

372
func (proxy *Proxy) addWeaveWaitVolume(hostConfig *docker.HostConfig) {
34×
373
        var binds []string
34×
374
        for _, bind := range hostConfig.Binds {
3×
375
                s := strings.Split(bind, ":")
3×
NEW
376
                if len(s) >= 2 && s[1] == "/w" {
!
NEW
377
                        continue
!
378
                }
379
                binds = append(binds, bind)
3×
380
        }
381
        hostConfig.Binds = append(binds, fmt.Sprintf("%s:/w:ro", proxy.weaveWaitVolume))
34×
382
}
383

384
func (proxy *Proxy) setWeaveDNS(hostConfig *docker.HostConfig, hostname, dnsDomain string) error {
22×
385
        hostConfig.DNS = append(hostConfig.DNS, proxy.dockerBridgeIP)
22×
386

22×
387
        if len(hostConfig.DNSSearch) == 0 {
21×
388
                if hostname == "" {
2×
389
                        hostConfig.DNSSearch = []string{dnsDomain}
2×
390
                } else {
19×
391
                        hostConfig.DNSSearch = []string{"."}
19×
392
                }
19×
393
        }
394

395
        return nil
22×
396
}
397

398
func (proxy *Proxy) getDNSDomain() (domain string) {
32×
399
        if proxy.WithoutDNS {
2×
400
                return ""
2×
401
        }
2×
402

NEW
403
        if proxy.WithDNS {
!
NEW
404
                domain = nameserver.DefaultDomain
!
NEW
405
        }
!
406

407
        weaveContainer, err := proxy.client.InspectContainer("weave")
30×
408
        if err != nil ||
30×
409
                weaveContainer.NetworkSettings == nil ||
30×
410
                weaveContainer.NetworkSettings.IPAddress == "" {
8×
411
                return
8×
412
        }
8×
413

414
        url := fmt.Sprintf("http://%s:%d/domain", weaveContainer.NetworkSettings.IPAddress, router.HTTPPort)
22×
415
        resp, err := http.Get(url)
22×
NEW
416
        if err != nil || resp.StatusCode != http.StatusOK {
!
NEW
417
                return
!
NEW
418
        }
!
419
        defer resp.Body.Close()
22×
420

22×
421
        b, err := ioutil.ReadAll(resp.Body)
22×
NEW
422
        if err != nil {
!
NEW
423
                return
!
NEW
424
        }
!
425

426
        return string(b)
22×
427
}
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc