Skip to content
This repository was archived by the owner on Jan 21, 2020. It is now read-only.

Commit 1ddac60

Browse files
author
David Chung
authored
Swarm ingress controller fixes + documentation/ example YAML (#679)
Signed-off-by: David Chung <[email protected]>
1 parent 5e2f950 commit 1ddac60

File tree

23 files changed

+420
-196
lines changed

23 files changed

+420
-196
lines changed

docs/controller/ingress/example.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#
2+
# Example Ingress YML that will sync services in Docker Swarm to the backend.
3+
# The backend is specified by the L4Plugin property.
4+
#
5+
# To run this:
6+
#
7+
# INFRAKIT_MANAGER_BACKEND=swarm infrakit plugin start manager simulator ingress
8+
#
9+
# In another console:
10+
# infrakit ingress controller commit -y path/to/this/file
11+
#
12+
# Creating Docker services
13+
# docker network create --driver overlay --ingress ingress
14+
# docker service create --network ingress --name t2 --publish 7777:80 nginx
15+
#
16+
# Verify that the route has been added in the simulator/lb1:
17+
# infrakit simulator/lb1 routes ls
18+
#
19+
# FRONTEND PORT PROTOCOL BACKEND PORT CERT
20+
# 7777 TCP 7777 <-- from the swarm service
21+
# 80 http 8080 <-- from the static route in the config
22+
#
23+
# In Docker:
24+
# docker service ls
25+
#ID NAME MODE REPLICAS IMAGE PORTS
26+
#m4fghruao79i t2 replicated 1/1 nginx:latest *:7777->80/tcp
27+
#
28+
# Remove the service
29+
# docker service rm test1
30+
#
31+
# infrakit simulator/lb1 routes ls # should be empty
32+
#
33+
# FRONTEND PORT PROTOCOL BACKEND PORT CERT
34+
# 80 http 8080 <-- from the static route
35+
36+
kind: ingress
37+
metadata:
38+
name: test.com
39+
tags:
40+
project: testing
41+
user: chungers
42+
43+
# options block map to pkg/controller/ingress/types/Options
44+
options:
45+
# SyncInterval is how often to sync changes between the services and the LB
46+
SyncInterval: 1s # syntax is a string form of Go time.Duration
47+
48+
# properties block map to pkg/controller/ingress/types/Properties
49+
properties:
50+
- Backends:
51+
Groups:
52+
53+
# This is a group at socket(group), groupID(cattle).
54+
- group/cattle
55+
56+
# This is the plugin name of the L4 plugin. When you run `infrakit plugin start ... simulator`
57+
# the default socket file name is 'simulator' and there's a default lb2 in the RPC object.
58+
L4Plugin: simulator/lb1
59+
60+
# Here we have a static route that is always present.
61+
Routes:
62+
- LoadBalancerPort: 80
63+
LoadBalancerProtocol: https
64+
Port: 8080
65+
Protocol: http
66+
67+
# Plus all the services in Swarm that have --publish <frontend-port>:<container_port>
68+
RouteSources:
69+
swarm:
70+
Host: unix:///var/run/docker.sock
71+
72+
# This field here is required and leave it at 'default'. (TODO - make this not required).
73+
# default will match any services that don't have vhost information.
74+
# There are ways to specify the vhost for a service in the label. If that information
75+
# isn't there, the service will have to be lumped to a 'default' vhost. Otherwise,
76+
# the value of this field will be used to group and match the service which loadbalancer.
77+
Vhost: default

pkg/controller/ingress/controller.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/docker/infrakit/pkg/discovery"
1010
"github.com/docker/infrakit/pkg/manager"
1111
"github.com/docker/infrakit/pkg/types"
12+
"golang.org/x/net/context"
1213
)
1314

1415
// NewController returns a controller implementation
@@ -76,7 +77,7 @@ func (m *managed) Enforce(spec types.Spec) (*types.Object, error) {
7677
if err != nil {
7778
return nil, err
7879
}
79-
m.start()
80+
m.Start()
8081
return m.object(), nil
8182
}
8283

@@ -88,15 +89,41 @@ func (m *managed) Inspect() (*types.Object, error) {
8889
// Free implements internal/Managed
8990
func (m *managed) Free() (*types.Object, error) {
9091
if m.started() {
91-
m.stop()
92+
m.Stop()
9293
}
9394
return m.Inspect()
9495
}
9596

9697
// Terminate implements internal/Managed
9798
func (m *managed) Terminate() (*types.Object, error) {
9899
if m.started() {
99-
m.stop()
100+
m.Stop()
100101
}
101102
return m.Inspect()
102103
}
104+
105+
// Start implements internal/ControlLoop
106+
func (m *managed) Start() {
107+
m.lock.Lock()
108+
defer m.lock.Unlock()
109+
110+
if m.process != nil && m.poller != nil {
111+
go m.poller.Run(context.Background())
112+
}
113+
}
114+
115+
// Stop implements internal/ControlLoop
116+
func (m *managed) Stop() error {
117+
m.lock.Lock()
118+
defer m.lock.Unlock()
119+
120+
if m.process != nil && m.poller != nil {
121+
m.poller.Stop()
122+
}
123+
return nil
124+
}
125+
126+
// Running implements internal/ControlLoop
127+
func (m *managed) Running() bool {
128+
return m.started()
129+
}

pkg/controller/ingress/fsm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ func (c *managed) init(in types.Spec) (err error) {
170170
}
171171

172172
if c.ticker == nil {
173-
interval := c.options.SyncInterval
173+
interval := c.options.SyncInterval.Duration()
174174
if interval == 0 {
175175
interval = ingress.DefaultSyncInterval
176176
}

pkg/controller/ingress/fsm_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,22 @@ func TestControllerInitSpec(t *testing.T) {
8686

8787
managedObject := &managed{
8888
options: ingress.Options{
89-
SyncInterval: expectedInterval,
89+
SyncInterval: types.Duration(expectedInterval),
9090
},
9191
}
9292

9393
err := managedObject.init(types.Spec{})
9494
require.NoError(t, err)
9595

9696
t.Log("verify that the default value remains despite no Options in the spec")
97-
require.Equal(t, expectedInterval, managedObject.options.SyncInterval)
97+
require.Equal(t, expectedInterval, managedObject.options.SyncInterval.Duration())
9898

9999
t.Log("verify that spec's option value makes into the ingress.Options")
100100
managedObject = &managed{}
101101

102102
expectedOptions := ingress.Options{
103103
HardSync: true,
104-
SyncInterval: expectedInterval,
104+
SyncInterval: types.Duration(expectedInterval),
105105
}
106106

107107
err = managedObject.init(types.Spec{

pkg/controller/ingress/l4.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,14 @@ func configureL4(elb loadbalancer.L4, desired []loadbalancer.Route, options type
9393
log.Info("CHANGED", "name", elb.Name(), "listener", l)
9494
}
9595
for _, l := range toRemove {
96-
log.Info("REMOVE", "name", elb.Name(), "listener", l)
9796

98-
if options.RemoveListeners {
99-
_, err := elb.Unpublish(l.LoadBalancerPort)
100-
if err != nil {
101-
log.Warn("err unpublishing", "route", l, "err", err)
102-
return err
103-
}
104-
log.Info("REMOVED", "name", elb.Name(), "listener", l)
97+
log.Info("REMOVE", "name", elb.Name(), "listener", l)
98+
_, err := elb.Unpublish(l.LoadBalancerPort)
99+
if err != nil {
100+
log.Warn("err unpublishing", "route", l, "err", err)
101+
return err
105102
}
103+
log.Info("REMOVED", "name", elb.Name(), "listener", l)
106104
}
107105
return nil
108106
}

pkg/controller/ingress/managed.go

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"github.com/docker/infrakit/pkg/spi/instance"
1919
"github.com/docker/infrakit/pkg/spi/loadbalancer"
2020
"github.com/docker/infrakit/pkg/types"
21-
"golang.org/x/net/context"
2221
)
2322

2423
var log = logutil.New("module", "controller/ingress")
@@ -101,37 +100,9 @@ func (c *managed) l4Client(spec ingress.Spec) (loadbalancer.L4, error) {
101100
return loadbalancer_rpc.NewClient(spec.L4Plugin, endpoint.Address)
102101
}
103102

104-
// Run starts the controller given the spec it needs to maintain
105-
func (c *managed) Run(spec types.Spec) error {
106-
err := c.init(spec)
107-
if err != nil {
108-
return err
109-
}
110-
c.start()
111-
return nil
112-
}
113-
114103
func (c *managed) started() bool {
115104
c.lock.RLock()
116105
defer c.lock.RUnlock()
117106

118107
return c.process != nil && c.poller != nil
119108
}
120-
121-
func (c *managed) start() {
122-
c.lock.Lock()
123-
defer c.lock.Unlock()
124-
125-
if c.process != nil && c.poller != nil {
126-
go c.poller.Run(context.Background())
127-
}
128-
}
129-
130-
func (c *managed) stop() {
131-
c.lock.Lock()
132-
defer c.lock.Unlock()
133-
134-
if c.process != nil && c.poller != nil {
135-
c.poller.Stop()
136-
}
137-
}

pkg/controller/ingress/managed_test.go

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ func TestManagedStartStop(t *testing.T) {
2222
ticker := make(chan time.Time, 1)
2323
leader := make(chan bool, 1)
2424

25-
doneWork := make(chan interface{})
26-
25+
doneWork := make(chan int, 1)
2726
managedObject := &managed{
2827
Leadership: fakeLeadership(leader),
2928
ticker: ticker,
@@ -33,9 +32,7 @@ func TestManagedStartStop(t *testing.T) {
3332

3433
routes: func() (map[ingress.Vhost][]loadbalancer.Route, error) {
3534
// if this function is called then we know we've done work in the state transition
36-
// from syncing to waiting
37-
close(doneWork)
38-
35+
doneWork <- 1
3936
return nil, nil
4037
},
4138
}
@@ -56,7 +53,7 @@ func TestManagedStartStop(t *testing.T) {
5653
err := managedObject.init(spec)
5754
require.NoError(t, err)
5855

59-
managedObject.start()
56+
managedObject.Start()
6057

6158
t.Log("verify initial state machine is in the follower state")
6259
require.Equal(t, follower, managedObject.stateMachine.State())
@@ -96,20 +93,14 @@ func TestManagedStartStop(t *testing.T) {
9693
leader <- true
9794

9895
// here we change the routes function to test for another close
99-
doneWork2 := make(chan interface{})
100-
managedObject.routes = func() (map[ingress.Vhost][]loadbalancer.Route, error) {
101-
// if this function is called then we know we've done work in the state transition
102-
// from syncing to waiting
103-
close(doneWork2)
104-
return nil, nil
105-
}
10696

10797
ticker <- time.Now()
10898

109-
t.Log("verify state machine moved to the waiting state")
99+
<-doneWork // if not called, the test will hang here
110100

111-
<-doneWork2 // if not called, the test will hang here
101+
t.Log("verify state machine moved to the waiting state")
112102
require.Equal(t, waiting, managedObject.stateMachine.State())
113103

114-
managedObject.stop()
104+
managedObject.Stop()
105+
115106
}

pkg/controller/ingress/swarm/info.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package swarm
22

33
import (
4-
log "github.com/Sirupsen/logrus"
54
"github.com/docker/docker/client"
65
"golang.org/x/net/context"
76
)
@@ -10,7 +9,7 @@ import (
109
func AmISwarmLeader(ctx context.Context, client client.APIClient) (bool, error) {
1110
info, err := client.Info(ctx)
1211

13-
log.Debugln("info=", info, "err=", err)
12+
log.Debug("Swarm info", "info", info, "err", err)
1413

1514
if err != nil {
1615
return false, err
@@ -19,19 +18,19 @@ func AmISwarmLeader(ctx context.Context, client client.APIClient) (bool, error)
1918
// inspect itself to see if i am the leader
2019
node, _, err := client.NodeInspectWithRaw(ctx, info.Swarm.NodeID)
2120

22-
log.Debugln("nodeId=", info.Swarm.NodeID, "node=", node, "err=", err)
21+
log.Debug("Inspect node", "nodeID", info.Swarm.NodeID, "node", node, "err", err, "V", debugV)
2322

2423
if err != nil {
2524
return false, err
2625
}
2726

28-
log.Debugln("manager=", node.ManagerStatus)
27+
log.Debug("manager status", "status", node.ManagerStatus, "V", debugV)
2928

3029
if node.ManagerStatus == nil {
3130
return false, nil
3231
}
3332

34-
log.Debugln("leader=", node.ManagerStatus.Leader)
33+
log.Debug("leader status", "leader", node.ManagerStatus.Leader)
3534

3635
return node.ManagerStatus.Leader, nil
3736
}

pkg/controller/ingress/swarm/init.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@ import (
55

66
"github.com/docker/go-connections/tlsconfig"
77
ingress "github.com/docker/infrakit/pkg/controller/ingress/types"
8+
logutil "github.com/docker/infrakit/pkg/log"
89
"github.com/docker/infrakit/pkg/spi/loadbalancer"
910
"github.com/docker/infrakit/pkg/types"
1011
"github.com/docker/infrakit/pkg/util/docker"
1112
)
1213

14+
var log = logutil.New("module", "controller/ingress/swarm")
15+
16+
const (
17+
debugV = logutil.V(300)
18+
debugV2 = logutil.V(310)
19+
)
20+
1321
func init() {
1422

1523
// Register the swarm based ingress route finder. This will be included when the package is imported
@@ -20,11 +28,13 @@ func init() {
2028
)
2129
}
2230

31+
// Docker is alias for docker connection information
32+
type Docker docker.ConnectInfo
33+
2334
// Spec is the struct that captures the configuration of the swarm-based ingress route finder
2435
type Spec struct {
25-
2636
// Docker holds the connection params to the Docker engine for join tokens, etc.
27-
Docker docker.ConnectInfo
37+
Docker `json:",inline" yaml:",inline"`
2838
}
2939

3040
// RoutesFromSwarmServices determines the routes based on the services running in the Docker swarm
@@ -52,6 +62,7 @@ func RoutesFromSwarmServices(properties *types.Any,
5262
return nil, err
5363
}
5464

65+
log.Info("Connected to Docker", "client", dockerClient)
5566
routes, err := NewServiceRoutes(dockerClient).SetOptions(options).Build()
5667
if err != nil {
5768
return nil, err

pkg/controller/ingress/swarm/init_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ func TestParseSpec(t *testing.T) {
1919
RouteSources: map[string]*types.Any{
2020
"swarm": types.AnyValueMust(
2121
Spec{
22-
Docker: docker.ConnectInfo{
22+
Docker(docker.ConnectInfo{
2323
Host: "/var/run/docker.sock",
24-
},
24+
}),
2525
},
2626
),
2727
},

0 commit comments

Comments
 (0)