Skip to content

Commit 2e6a099

Browse files
committed
Implement controller network via name
1 parent 69cf7a0 commit 2e6a099

File tree

23 files changed

+645
-210
lines changed

23 files changed

+645
-210
lines changed

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,15 @@ Each caddy docker proxy instance can be executed in one of the following modes.
449449

450450
Acts as a proxy to your Docker resources. The server starts without any configuration, and will not serve anything until it is configured by a "controller".
451451

452-
In order to make a server discoverable and configurable by controllers, you need to mark it with label `caddy_controlled_server` and define the controller network via CLI option `controller-network` or environment variable `CADDY_CONTROLLER_NETWORK`.
452+
In order to make a server discoverable and configurable by controllers, you need to mark it with label `caddy_controlled_server`.
453453

454454
Server instances doesn't need access to Docker host socket and you can run it in manager or worker nodes.
455455

456-
[Configuration example](examples/distributed.yaml#L5)
456+
[Configuration example](examples/distributed-single-network.yaml#L5)
457+
458+
When using a separate controller network, you must also configure the controller url via CLI option `controller-url` or environment variable `CADDY_CONTROLLER_URL`, that allows caddy server to fetch the controller networks configured in the controller.
459+
460+
[Configuration example](examples/distributed-controller-network.yaml#L5)
457461

458462
### Controller
459463

@@ -482,29 +486,42 @@ Run `caddy help docker-proxy` to see all available flags.
482486
```
483487
Usage of docker-proxy:
484488
--caddyfile-path string
485-
Path to a base Caddyfile that will be extended with Docker sites
489+
Path to a base Caddyfile that will be extended with Docker sites.
490+
Applicable to modes: controller, standalone
486491
--controller-network string
487-
Network allowed to configure Caddy server in CIDR notation. Ex: 10.200.200.0/24
492+
Controller network name. Ex: caddy_controller.
493+
Applicable to modes: controller
494+
--controller-url string
495+
Controller url, used by servers to fetch controller subnets. Ex: http://caddy-controller
496+
Applicable to modes: server
488497
--ingress-networks string
489498
Comma separated name of ingress networks connecting Caddy servers to containers.
490499
When not defined, networks attached to controller container are considered ingress networks
500+
Applicable to modes: controller, standalone
491501
--docker-sockets
492502
Comma separated docker sockets
493503
When not defined, DOCKER_HOST (or default docker socket if DOCKER_HOST not defined)
504+
Applicable to modes: controller, standalone
494505
--docker-certs-path
495506
Comma separated cert path, you could use empty value when no cert path for the concern index docker socket like cert_path0,,cert_path2
507+
Applicable to modes: controller, standalone
496508
--docker-apis-version
497509
Comma separated apis version, you could use empty value when no api version for the concern index docker socket like cert_path0,,cert_path2
510+
Applicable to modes: controller, standalone
498511
--label-prefix string
499512
Prefix for Docker labels (default "caddy")
513+
Applicable to modes: controller, standalone
500514
--mode
501515
Which mode this instance should run: standalone | controller | server
502516
--polling-interval duration
503517
Interval Caddy should manually check Docker for a new Caddyfile (default 30s)
518+
Applicable to modes: controller, standalone
504519
--process-caddyfile
505520
Process Caddyfile before loading it, removing invalid servers (default true)
521+
Applicable to modes: controller, standalone
506522
--proxy-service-tasks
507523
Proxy to service tasks instead of service load balancer (default true)
524+
Applicable to modes: controller, standalone
508525
```
509526
510527
Those flags can also be set via environment variables:

cmd.go

Lines changed: 111 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package caddydockerproxy
22

33
import (
4+
"encoding/json"
45
"flag"
6+
"io/ioutil"
57
"net"
8+
"net/http"
9+
"net/url"
610
"os"
711
"regexp"
812
"strings"
@@ -31,35 +35,50 @@ func init() {
3135
"Which mode this instance should run: standalone | controller | server")
3236

3337
fs.String("docker-sockets", "",
34-
"Docker sockets comma separate")
38+
"Docker sockets comma separate.\n"+
39+
"Applicable to modes: controller, standalone")
3540

3641
fs.String("docker-certs-path", "",
37-
"Docker socket certs path comma separate")
42+
"Docker socket certs path comma separate.\n"+
43+
"Applicable to modes: controller, standalone")
3844

3945
fs.String("docker-apis-version", "",
40-
"Docker socket apis version comma separate")
46+
"Docker socket apis version comma separate.\n"+
47+
"Applicable to modes: controller, standalone")
4148

4249
fs.String("controller-network", "",
43-
"Network allowed to configure caddy server in CIDR notation. Ex: 10.200.200.0/24")
50+
"Controller network name. Ex: caddy_controller.\n"+
51+
"When not defined, all networks attached to controller container are considered controller networks\n"+
52+
"Applicable to modes: controller, standalone")
53+
54+
fs.String("controller-url", "",
55+
"Controller url, used by servers to fetch controller subnets. Ex: http://caddy-controller\n"+
56+
"Applicable to modes: server")
4457

4558
fs.String("ingress-networks", "",
4659
"Comma separated name of ingress networks connecting caddy servers to containers.\n"+
47-
"When not defined, networks attached to controller container are considered ingress networks")
60+
"When not defined, all networks attached to controller container are considered ingress networks\n"+
61+
"Applicable to modes: controller, standalone")
4862

4963
fs.String("caddyfile-path", "",
50-
"Path to a base Caddyfile that will be extended with docker sites")
64+
"Path to a base Caddyfile that will be extended with docker sites.\n"+
65+
"Applicable to modes: controller, standalone")
5166

5267
fs.String("label-prefix", generator.DefaultLabelPrefix,
53-
"Prefix for Docker labels")
68+
"Prefix for Docker labels.\n"+
69+
"Applicable to modes: controller, standalone")
5470

5571
fs.Bool("proxy-service-tasks", true,
56-
"Proxy to service tasks instead of service load balancer")
72+
"Proxy to service tasks instead of service load balancer.\n"+
73+
"Applicable to modes: controller, standalone")
5774

5875
fs.Bool("process-caddyfile", true,
59-
"Process Caddyfile before loading it, removing invalid servers")
76+
"Process Caddyfile before loading it, removing invalid servers.\n"+
77+
"Applicable to modes: controller, standalone")
6078

6179
fs.Duration("polling-interval", 30*time.Second,
62-
"Interval caddy should manually check docker for a new caddyfile")
80+
"Interval caddy should manually check docker for a new caddyfile.\n"+
81+
"Applicable to modes: controller, standalone")
6382

6483
return fs
6584
}(),
@@ -75,9 +94,14 @@ func cmdFunc(flags caddycmd.Flags) (int, error) {
7594
if options.Mode&config.Server == config.Server {
7695
log.Info("Running caddy proxy server")
7796

78-
err := caddy.Run(&caddy.Config{
97+
bindAddress, err := getAdminListen(options)
98+
if err != nil {
99+
return 1, err
100+
}
101+
102+
err = caddy.Run(&caddy.Config{
79103
Admin: &caddy.AdminConfig{
80-
Listen: getAdminListen(options),
104+
Listen: bindAddress,
81105
},
82106
})
83107
if err != nil {
@@ -87,8 +111,8 @@ func cmdFunc(flags caddycmd.Flags) (int, error) {
87111

88112
if options.Mode&config.Controller == config.Controller {
89113
log.Info("Running caddy proxy controller")
90-
loader := CreateDockerLoader(options)
91-
if err := loader.Start(); err != nil {
114+
controller := CreateCaddyController(options)
115+
if err := controller.Start(); err != nil {
92116
if err := caddy.Stop(); err != nil {
93117
return 1, err
94118
}
@@ -100,37 +124,72 @@ func cmdFunc(flags caddycmd.Flags) (int, error) {
100124
select {}
101125
}
102126

103-
func getAdminListen(options *config.Options) string {
104-
if options.ControllerNetwork != nil {
105-
ifaces, err := net.Interfaces()
106-
log := logger()
127+
func getAdminListen(options *config.Options) (string, error) {
128+
if options.Mode&config.Controller == config.Controller {
129+
return "tcp/localhost:2019", nil
130+
}
131+
132+
log := logger()
133+
134+
var controllerNetworks []string
135+
136+
if options.ControllerNetwork != "" {
137+
controllerNetworks = append(controllerNetworks, options.ControllerNetwork)
138+
}
107139

140+
if options.ControllerUrl != nil {
141+
url := strings.TrimRight(options.ControllerUrl.String(), "/") + "/controller-subnets"
142+
log.Info("Fetching controller networks from url", zap.String("url", url))
143+
resp, err := http.Get(url)
108144
if err != nil {
109-
log.Error("Failed to get network interfaces", zap.Error(err))
145+
log.Error("Failed to fetch controller networks from contoller", zap.String("url", url), zap.Error(err))
146+
return "", err
110147
}
111-
for _, i := range ifaces {
112-
addrs, err := i.Addrs()
113-
if err != nil {
114-
log.Error("Failed to get network interface addresses", zap.Error(err))
115-
continue
116-
}
117-
for _, a := range addrs {
118-
switch v := a.(type) {
148+
body, err := ioutil.ReadAll(resp.Body)
149+
if err != nil {
150+
return "", err
151+
}
152+
json.Unmarshal(body, &controllerNetworks)
153+
}
154+
155+
var ipNets []*net.IPNet
156+
for _, controllerNetwork := range controllerNetworks {
157+
_, ipNet, err := net.ParseCIDR(controllerNetwork)
158+
if err != nil {
159+
log.Error("Failed to parse controller network", zap.String("ControllerNetwork", controllerNetwork), zap.Error(err))
160+
return "", err
161+
}
162+
ipNets = append(ipNets, ipNet)
163+
}
164+
165+
ifaces, err := net.Interfaces()
166+
if err != nil {
167+
log.Error("Failed to get network interfaces", zap.Error(err))
168+
return "", err
169+
}
170+
for _, iface := range ifaces {
171+
addrs, err := iface.Addrs()
172+
if err != nil {
173+
log.Error("Failed to get network interface addresses", zap.Error(err))
174+
return "", err
175+
}
176+
for _, addr := range addrs {
177+
for _, ipNet := range ipNets {
178+
switch v := addr.(type) {
119179
case *net.IPAddr:
120-
if options.ControllerNetwork.Contains(v.IP) {
121-
return "tcp/" + v.IP.String() + ":2019"
180+
if ipNet.Contains(v.IP) {
181+
return "tcp/" + v.IP.String() + ":2019", nil
122182
}
123-
break
124183
case *net.IPNet:
125-
if options.ControllerNetwork.Contains(v.IP) {
126-
return "tcp/" + v.IP.String() + ":2019"
184+
if ipNet.Contains(v.IP) {
185+
return "tcp/" + v.IP.String() + ":2019", nil
127186
}
128-
break
129187
}
130188
}
131189
}
132190
}
133-
return "tcp/localhost:2019"
191+
192+
return "tcp/0.0.0.0:2019", nil
134193
}
135194

136195
func createOptions(flags caddycmd.Flags) *config.Options {
@@ -140,7 +199,8 @@ func createOptions(flags caddycmd.Flags) *config.Options {
140199
processCaddyfileFlag := flags.Bool("process-caddyfile")
141200
pollingIntervalFlag := flags.Duration("polling-interval")
142201
modeFlag := flags.String("mode")
143-
controllerSubnetFlag := flags.String("controller-network")
202+
controllerNetwork := flags.String("controller-network")
203+
controllerUrl := flags.String("controller-url")
144204
dockerSocketsFlag := flags.String("docker-sockets")
145205
dockerCertsPathFlag := flags.String("docker-certs-path")
146206
dockerAPIsVersionFlag := flags.String("docker-apis-version")
@@ -186,19 +246,23 @@ func createOptions(flags caddycmd.Flags) *config.Options {
186246
options.DockerAPIsVersion = strings.Split(dockerAPIsVersionFlag, ",")
187247
}
188248

189-
if controllerIPRangeEnv := os.Getenv("CADDY_CONTROLLER_NETWORK"); controllerIPRangeEnv != "" {
190-
_, ipNet, err := net.ParseCIDR(controllerIPRangeEnv)
191-
if err != nil {
192-
log.Error("Failed to parse CADDY_CONTROLLER_NETWORK", zap.String("CADDY_CONTROLLER_NETWORK", controllerIPRangeEnv), zap.Error(err))
193-
} else if ipNet != nil {
194-
options.ControllerNetwork = ipNet
249+
if controllerNetworkEnv := os.Getenv("CADDY_CONTROLLER_NETWORK"); controllerNetworkEnv != "" {
250+
options.ControllerNetwork = controllerNetworkEnv
251+
} else {
252+
options.ControllerNetwork = controllerNetwork
253+
}
254+
255+
if controllerUrlEnv := os.Getenv("CADDY_CONTROLLER_URL"); controllerUrlEnv != "" {
256+
if url, err := url.Parse(controllerUrlEnv); err != nil {
257+
log.Error("Failed to parse CADDY_CONTROLLER_URL", zap.String("value", controllerUrlEnv), zap.Error(err))
258+
} else {
259+
options.ControllerUrl = url
195260
}
196-
} else if controllerSubnetFlag != "" {
197-
_, ipNet, err := net.ParseCIDR(controllerSubnetFlag)
198-
if err != nil {
199-
log.Error("Failed to parse controller-network", zap.String("controller-network", controllerSubnetFlag), zap.Error(err))
200-
} else if ipNet != nil {
201-
options.ControllerNetwork = ipNet
261+
} else if controllerUrl != "" {
262+
if url, err := url.Parse(controllerUrl); err != nil {
263+
log.Error("Failed to parse controller-url", zap.String("value", controllerUrl), zap.Error(err))
264+
} else {
265+
options.ControllerUrl = url
202266
}
203267
}
204268

config/options.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
package config
22

33
import (
4-
"net"
4+
"net/url"
55
"time"
66
)
77

88
// Options are the options for generator
99
type Options struct {
1010
CaddyfilePath string
1111
DockerSockets []string
12-
DockerCertsPath []string
13-
DockerAPIsVersion []string
12+
DockerCertsPath []string
13+
DockerAPIsVersion []string
1414
LabelPrefix string
1515
ControlledServersLabel string
1616
ProxyServiceTasks bool
1717
ProcessCaddyfile bool
1818
PollingInterval time.Duration
1919
Mode Mode
2020
Secret string
21-
ControllerNetwork *net.IPNet
21+
ControllerNetwork string
22+
ControllerUrl *url.URL
2223
IngressNetworks []string
2324
}
2425

0 commit comments

Comments
 (0)