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

Commit e98df4c

Browse files
author
Dongsu Park
authored
Merge pull request #1655 from endocode/dongsu/install_section
fleetd: process dependencies in [Install] section
2 parents af75858 + 4a94037 commit e98df4c

File tree

4 files changed

+152
-5
lines changed

4 files changed

+152
-5
lines changed

Documentation/unit-files-and-scheduling.md

+35-5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,35 @@ To use instance units, simply create a unit file whose name matches the `<name>@
5959

6060
When working with instance units, it is strongly recommended that all units be _entirely homogenous_. This means that any unit created as, say, `[email protected]`, should be created only from the unit named `[email protected]`. This homogeneity will be enforced by the fleet API in future.
6161

62+
## Definition of the Install Section
63+
64+
Unit files which have an `[Install]` section will be automatically enabled by fleet. This means that the states of such unit files cannot be tracked by fleet. For example, assume we have loaded this `my.service` unit file:
65+
66+
```ini
67+
[Service]
68+
ExecStart=/bin/bash -c "while true; do echo my.service unit file; sleep 1; done"
69+
```
70+
71+
and then loaded an additional [sidekick][sidekick] discovery unit `my_discovery.service`:
72+
73+
```ini
74+
[Unit]
75+
BindsTo=my.service
76+
77+
[Service]
78+
ExecStart=/bin/bash -c "while true; do echo my_discovery.service unit file; sleep 1; done"
79+
80+
[Install]
81+
WantedBy=my.service
82+
83+
[X-Fleet]
84+
MachineOf=my.service
85+
```
86+
87+
fleet will load and enable the `my_discovery.service` unit above because it contains an `[Install]` section. When `my.service` is started, systemd will also start `my_discovery.service`, independent of the desired state defined for `my_discovery.service` in fleet. This can cause confusion between the output of `fleetctl list-units` and `systemctl list-units`, which will not match in this scenario. Use `fleetctl status my_discovery.service` to explicitly identify the service and get its actual unit status.
88+
89+
If systemd can not enable the unit which has `[Install]` section, fleet will interrupt load process and return an error.
90+
6291
## systemd specifiers
6392

6493
When evaluating the `[X-Fleet]` section, fleet supports a subset of systemd's [specifiers][systemd specifiers] to perform variable substitution. The following specifiers are currently supported:
@@ -90,7 +119,7 @@ For more details on the specific behavior of the engine, read more about [fleet'
90119

91120
For non-global units, several different directives are available to control the engine's scheduling decision.
92121

93-
##### Schedule unit to specific machine
122+
## Schedule unit to specific machine
94123

95124
The `MachineID` option of a unit file causes the system to schedule a unit to a machine identified by the option's value.
96125

@@ -100,7 +129,7 @@ One must use the entire ID when setting `MachineID` - the shortened ID returned
100129
fleet depends on its host to generate an identifier at `/etc/machine-id`, which is handled today by systemd.
101130
Read more about machine IDs in the [official systemd documentation][machine-id].
102131

103-
##### Schedule unit to machine with specific metadata
132+
## Schedule unit to machine with specific metadata
104133

105134
The `MachineMetadata` option of a unit file allows you to set conditional metadata required for a machine to be elegible.
106135

@@ -183,7 +212,7 @@ app.service fd1d3e94.../10.0.0.1 active running
183212
A machine is not automatically configured with metadata.
184213
A deployer may define machine metadata using the `metadata` [config option][config-option].
185214

186-
##### Schedule unit next to another unit
215+
## Schedule unit next to another unit
187216

188217
In order for a unit to be scheduled to the same machine as another unit, a unit file can define `MachineOf`.
189218
The value of this option is the exact name of another unit in the system, which we'll call the target unit.
@@ -195,13 +224,13 @@ Follower units will reschedule themselves around the cluster to ensure their `Ma
195224

196225
Note that currently `MachineOf` _cannot_ be a bidirectional dependency: i.e., if unit `foo.service` has `MachineOf=bar.service`, then `bar.service` must not have a `MachineOf=foo.service`, or fleet will be unable to schedule the units.
197226

198-
##### Schedule unit away from other unit(s)
227+
## Schedule unit away from other unit(s)
199228

200229
The value of the `Conflicts` option is a [glob pattern][glob-pattern] defining which other units next to which a given unit must not be scheduled. A unit may have multiple `Conflicts` options.
201230

202231
If a unit is scheduled to the system without an `Conflicts` option, other units' conflicts still take effect and prevent the new unit from being scheduled to machines where conflicts exist.
203232

204-
##### Dynamic requirements
233+
## Dynamic requirements
205234

206235
fleet supports several [systemd specifiers][systemd-specifiers] to allow requirements to be dynamically determined based on a Unit's name. This means that the same unit can be used for multiple Units and the requirements are dynamically substituted when the Unit is scheduled.
207236

@@ -223,4 +252,5 @@ would result in an effective `MachineOf` of `foo.socket`. Using the same unit sn
223252
[glob-pattern]: http://golang.org/pkg/path/#Match
224253
[unit-scheduling]: #unit-scheduling
225254
[example-deployment]: examples/example-deployment.md#service-files
255+
[sidekick]: examples/service-discovery.md
226256
[systemd-specifiers]: #systemd-specifiers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[Unit]
2+
BindsTo=hello.service
3+
4+
[Service]
5+
ExecStart=/bin/bash -c "while true; do echo discovery.service unit file; sleep 1; done"
6+
7+
[Install]
8+
WantedBy=hello.service
9+
10+
[X-Fleet]
11+
MachineOf=hello.service

functional/install_test.go

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2016 The fleet Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package functional
16+
17+
import (
18+
"fmt"
19+
"strings"
20+
"testing"
21+
22+
"github.com/coreos/fleet/functional/platform"
23+
"github.com/coreos/fleet/functional/util"
24+
)
25+
26+
// Load service and discovery units and test whether discovery unit adds itself as a dependency for the service.
27+
func TestInstallUnit(t *testing.T) {
28+
cluster, err := platform.NewNspawnCluster("smoke")
29+
if err != nil {
30+
t.Fatal(err)
31+
}
32+
defer cluster.Destroy(t)
33+
34+
// Start with a two-nodes cluster
35+
members, err := platform.CreateNClusterMembers(cluster, 2)
36+
if err != nil {
37+
t.Fatal(err)
38+
}
39+
m0 := members[0]
40+
_, err = cluster.WaitForNMachines(m0, 2)
41+
if err != nil {
42+
t.Fatal(err)
43+
}
44+
45+
// Load unit files
46+
stdout, stderr, err := cluster.Fleetctl(m0, "load", "fixtures/units/hello.service", "fixtures/units/discovery.service")
47+
if err != nil {
48+
t.Fatalf("Failed loading unit files: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
49+
}
50+
51+
checkState := func(match string) bool {
52+
stdout, _, err := cluster.Fleetctl(m0, "--strict-host-key-checking=false", "ssh", "discovery.service", "systemctl show --property=ActiveState discovery.service")
53+
if err != nil {
54+
t.Logf("Failed getting info using remote systemctl: %v", err)
55+
}
56+
stdout = strings.TrimSpace(stdout)
57+
return stdout == fmt.Sprintf("ActiveState=%s", match)
58+
}
59+
60+
// Verify that discovery.service unit is loaded but not started
61+
timeout, err := util.WaitForState(func() bool { return checkState("inactive") })
62+
if err != nil {
63+
t.Fatalf("discovery.service unit is not reported as inactive within %v: %v", timeout, err)
64+
}
65+
66+
// Start hello.service unit
67+
stdout, stderr, err = cluster.Fleetctl(m0, "start", "fixtures/units/hello.service")
68+
if err != nil {
69+
t.Fatalf("Failed starting unit: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
70+
}
71+
72+
// Verify that discovery.service unit was started
73+
timeout, err = util.WaitForState(func() bool { return checkState("active") })
74+
if err != nil {
75+
t.Fatalf("discovery.service unit is not reported as active within %v:\n%v", timeout, err)
76+
}
77+
78+
// Stop hello.service unit
79+
stdout, stderr, err = cluster.Fleetctl(m0, "stop", "fixtures/units/hello.service")
80+
if err != nil {
81+
t.Fatalf("Failed stopping unit: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
82+
}
83+
84+
// Verify that discovery.service unit was stopped
85+
timeout, err = util.WaitForState(func() bool { return checkState("inactive") })
86+
if err != nil {
87+
t.Fatalf("discovery.service unit is not reported as inactive within %v:\n%v", timeout, err)
88+
}
89+
}

systemd/manager.go

+17
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ func (m *systemdUnitManager) Load(name string, u unit.UnitFile) error {
108108
if err != nil {
109109
return err
110110
}
111+
if _, exists := u.Contents["Install"]; exists {
112+
log.Debugf("Detected [Install] section in the systemd unit (%s)", name)
113+
ok, err := m.enableUnit(name)
114+
if err != nil || !ok {
115+
m.removeUnit(name)
116+
return fmt.Errorf("Failed to enable systemd unit %s: %v", name, err)
117+
}
118+
}
111119
m.hashes[name] = u.Hash()
112120
return nil
113121
}
@@ -269,6 +277,15 @@ func (m *systemdUnitManager) writeUnit(name string, contents string) error {
269277
return err
270278
}
271279

280+
func (m *systemdUnitManager) enableUnit(name string) (bool, error) {
281+
log.Infof("Enabling systemd unit %s", name)
282+
283+
ufPath := m.getUnitFilePath(name)
284+
285+
ok, _, err := m.systemd.EnableUnitFiles([]string{ufPath}, true, true)
286+
return ok, err
287+
}
288+
272289
func (m *systemdUnitManager) removeUnit(name string) (err error) {
273290
log.Infof("Removing systemd unit %s", name)
274291

0 commit comments

Comments
 (0)