Skip to content

Commit ad2b712

Browse files
krancourKent Rancourt
authored andcommitted
feat(platform): support placement options for each plane and router mesh
This updates deisctl code that creates and loads system units such that platform components belonging to each of the control plane, data plane, and router mesh can OPTIONALLY be scheduled only where fleet machine metadata indicates. The means of achieving this are that a "decorator" is applied to any system unit just before scheduling if and only if this option is enabled by means of the etcd key /deis/platform/enablePlacementOptions having been set to the value true. The decorator adds the correct metadata. Nodes in existing clusters will lack the machine metadata referenced by the system unit decorators. That would make this a breaking change except for the fact that this is entirely OPT-IN. Users should be encouraged to begin adopting metadata that will permit them to isolate their planes more easily in the future. As such, user-data.example is modified such that machines configured from that cloud-config are eligible to host both planes and the router mesh. This permits NEW small clusters without any isolation to be constructed as easily as ever before and permits the platform to be installed to such a cluster without complication. The main benefit of this change is that advanced users can use custom cloud config to isolate their planes using ONLY metadata and without also needing to also manually customize their system units, as would have been required in the past.
1 parent c91fd57 commit ad2b712

44 files changed

Lines changed: 198 additions & 33 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dev-cluster: discovery-url
2727
ssh-add ~/.vagrant.d/insecure_private_key
2828
deisctl config platform set sshPrivateKey=$(HOME)/.vagrant.d/insecure_private_key
2929
deisctl config platform set domain=local3.deisapp.com
30+
deisctl config platform set enablePlacementOptions=true
3031
deisctl install platform
3132

3233
discovery-url:

contrib/coreos/user-data.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ coreos:
77
public-ip: $private_ipv4
88
# allow etcd to slow down at times
99
etcd_request_timeout: 3.0
10+
metadata: controlPlane=true,dataPlane=true,routerMesh=true
1011
units:
1112
- name: etcd.service
1213
command: start

controller/scheduler/fleet.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,10 @@ def _create_container(self, name, image, command, unit, **kwargs):
150150
f['value'] = f['value'].format(**l)
151151
# prepare tags only if one was provided
152152
tags = kwargs.get('tags', {})
153-
if tags:
154-
tagset = ' '.join(['"{}={}"'.format(k, v) for k, v in tags.viewitems()])
153+
tagset = ' '.join(['"{}={}"'.format(k, v) for k, v in tags.viewitems()])
154+
if settings.ENABLE_PLACEMENT_OPTIONS in ['true', 'True', 'TRUE', '1']:
155155
unit.append({"section": "X-Fleet", "name": "MachineMetadata",
156-
"value": tagset})
156+
"value": tagset + ' "dataPlane=true"'})
157157
# post unit to fleet
158158
self._put_unit(name, {"desiredState": "loaded", "options": unit})
159159

controller/templates/confd_settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
# platform domain must be provided
2525
DEIS_DOMAIN = '{{ getv "/deis/platform/domain" }}'
2626

27+
ENABLE_PLACEMENT_OPTIONS = """{{ if exists "/deis/platform/enablePlacementOptions" }}{{ getv "/deis/platform/enablePlacementOptions" }}{{ else }}false{{end}}"""
28+
2729
# use the private registry module
2830
REGISTRY_MODULE = 'registry.private'
2931
REGISTRY_URL = '{{ getv "/deis/registry/protocol" }}://{{ getv "/deis/registry/host" }}:{{ getv "/deis/registry/port" }}' # noqa

deisctl/backend/fleet/create.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package fleet
33
import (
44
"fmt"
55
"io"
6+
"strconv"
67
"strings"
78
"sync"
89
"time"
@@ -96,7 +97,15 @@ func (c *FleetClient) createServiceUnit(component string, num int) (name string,
9697
if err != nil {
9798
return "", nil, err
9899
}
99-
uf, err = NewUnit(component, c.templatePaths)
100+
decorateStr, err := c.configBackend.GetWithDefault("/deis/platform/enablePlacementOptions", "false")
101+
if err != nil {
102+
return "", nil, err
103+
}
104+
decorate, err := strconv.ParseBool(decorateStr)
105+
if err != nil {
106+
return "", nil, err
107+
}
108+
uf, err = NewUnit(component, c.templatePaths, decorate)
100109
if err != nil {
101110
return
102111
}

deisctl/backend/fleet/create_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66
"sync"
77
"testing"
88

9+
"github.com/deis/deis/deisctl/config/model"
10+
"github.com/deis/deis/deisctl/test/mock"
11+
912
"github.com/coreos/fleet/schema"
1013
)
1114

@@ -30,7 +33,9 @@ func TestCreate(t *testing.T) {
3033
testFleetClient := stubFleetClient{testUnits: []*schema.Unit{}, unitsMutex: &sync.Mutex{},
3134
unitStatesMutex: &sync.Mutex{}}
3235

33-
c := &FleetClient{templatePaths: []string{name}, Fleet: &testFleetClient}
36+
testConfigBackend := mock.ConfigBackend{Expected: []*model.ConfigNode{{Key: "/deis/platform/enablePlacementOptions", Value: "true"}}}
37+
38+
c := &FleetClient{templatePaths: []string{name}, Fleet: &testFleetClient, configBackend: testConfigBackend}
3439

3540
var errOutput string
3641
var wg sync.WaitGroup

deisctl/backend/fleet/fleet.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import (
66
"path"
77
"text/tabwriter"
88

9+
"github.com/deis/deis/deisctl/config"
10+
911
"github.com/coreos/fleet/client"
1012
"github.com/coreos/fleet/machine"
1113
)
1214

1315
// FleetClient used to wrap Fleet API calls
1416
type FleetClient struct {
15-
Fleet client.API
17+
Fleet client.API
18+
configBackend config.Backend
1619

1720
// used to cache MachineStates
1821
machineStates map[string]*machine.MachineState
@@ -25,7 +28,7 @@ type FleetClient struct {
2528

2629
// NewClient returns a client used to communicate with Fleet
2730
// using the Registry API
28-
func NewClient() (*FleetClient, error) {
31+
func NewClient(cb config.Backend) (*FleetClient, error) {
2932
client, err := getRegistryClient()
3033
if err != nil {
3134
return nil, err
@@ -41,6 +44,6 @@ func NewClient() (*FleetClient, error) {
4144
out := new(tabwriter.Writer)
4245
out.Init(os.Stdout, 0, 8, 1, '\t', 0)
4346

44-
return &FleetClient{Fleet: client, templatePaths: templatePaths, runner: sshCommandRunner{},
47+
return &FleetClient{Fleet: client, configBackend: cb, templatePaths: templatePaths, runner: sshCommandRunner{},
4548
out: out, errWriter: os.Stderr}, nil
4649
}

deisctl/backend/fleet/fleet_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66
"sync"
77
"testing"
88

9+
"github.com/deis/deis/deisctl/config/model"
10+
"github.com/deis/deis/deisctl/test/mock"
11+
912
"github.com/coreos/fleet/machine"
1013
"github.com/coreos/fleet/schema"
1114
)
@@ -176,8 +179,10 @@ func TestNewClient(t *testing.T) {
176179
// set required flags
177180
Flags.Endpoint = "http://127.0.0.1:4001"
178181

182+
testConfigBackend := mock.ConfigBackend{Expected: []*model.ConfigNode{}}
183+
179184
// instantiate client
180-
_, err := NewClient()
185+
_, err := NewClient(testConfigBackend)
181186
if err != nil {
182187
t.Fatal(err)
183188
}

deisctl/backend/fleet/scale_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import (
77
"sync"
88
"testing"
99

10+
"github.com/deis/deis/deisctl/config/model"
11+
"github.com/deis/deis/deisctl/test/mock"
12+
1013
"github.com/coreos/fleet/schema"
1114
)
1215

@@ -30,7 +33,9 @@ func TestScaleUp(t *testing.T) {
3033

3134
testFleetClient := stubFleetClient{testUnits: testUnits, testUnitStates: []*schema.UnitState{}, unitsMutex: &sync.Mutex{}, unitStatesMutex: &sync.Mutex{}}
3235

33-
c := &FleetClient{templatePaths: []string{name}, Fleet: &testFleetClient}
36+
testConfigBackend := mock.ConfigBackend{Expected: []*model.ConfigNode{{Key: "/deis/platform/enablePlacementOptions", Value: "true"}}}
37+
38+
c := &FleetClient{templatePaths: []string{name}, Fleet: &testFleetClient, configBackend: testConfigBackend}
3439

3540
var errOutput string
3641
var wg sync.WaitGroup

deisctl/backend/fleet/unit.go

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ import (
1111
"github.com/coreos/fleet/unit"
1212
)
1313

14+
// path hierarchy for finding systemd service templates
15+
var templatePaths = []string{
16+
os.Getenv("DEISCTL_UNITS"),
17+
path.Join(os.Getenv("HOME"), ".deis", "units"),
18+
"/var/lib/deis/units",
19+
}
20+
21+
// and the same for systemd service "decorators" for optionally isolating
22+
// control plane, data plane, and router mesh
23+
var decoratorPaths = []string{
24+
path.Join(os.Getenv("DEISCTL_UNITS"), "decorators"),
25+
path.Join(os.Getenv("HOME"), ".deis", "units", "decorators"),
26+
"/var/lib/deis/units/decorators",
27+
}
28+
1429
// Units returns a list of units filtered by target
1530
func (c *FleetClient) Units(target string) (units []string, err error) {
1631
allUnits, err := c.Fleet.Units()
@@ -56,14 +71,19 @@ func (c *FleetClient) lastUnit(component string) (num int, err error) {
5671

5772
// NewUnit takes a component type and returns a Fleet unit
5873
// that includes the relevant systemd service template
59-
func NewUnit(component string, templatePaths []string) (uf *unit.UnitFile, err error) {
74+
func NewUnit(component string, templatePaths []string, decorate bool) (uf *unit.UnitFile, err error) {
6075
template, err := readTemplate(component, templatePaths)
6176
if err != nil {
6277
return
6378
}
64-
uf, err = unit.NewUnitFile(string(template))
65-
if err != nil {
66-
return
79+
if decorate {
80+
decorator, err := readDecorator(component)
81+
if err != nil {
82+
return nil, err
83+
}
84+
uf, err = unit.NewUnitFile(string(template) + "\n" + string(decorator))
85+
} else {
86+
uf, err = unit.NewUnitFile(string(template))
6787
}
6888
return
6989
}
@@ -104,3 +124,26 @@ func readTemplate(component string, templatePaths []string) (out []byte, err err
104124
}
105125
return
106126
}
127+
128+
// readDecorator returns the contents of a file containing a snippet that can
129+
// optionally be grafted on to the end of a corresponding systemd unit to
130+
// achieve isolation of the control plane, data plane, and router mesh
131+
func readDecorator(component string) (out []byte, err error) {
132+
decoratorName := "deis-" + component + ".service.decorator"
133+
var decoratorFile string
134+
135+
// look in $DEISCTL_UNITS env var, then the local and global root paths
136+
for _, p := range decoratorPaths {
137+
filename := path.Join(p, decoratorName)
138+
if _, err := os.Stat(filename); err == nil {
139+
decoratorFile = filename
140+
break
141+
}
142+
}
143+
144+
if decoratorFile == "" {
145+
return
146+
}
147+
out, err = ioutil.ReadFile(decoratorFile)
148+
return
149+
}

0 commit comments

Comments
 (0)