forked from juju/juju
-
Notifications
You must be signed in to change notification settings - Fork 0
/
discovery.go
167 lines (149 loc) · 4.51 KB
/
discovery.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package service
import (
"fmt"
"strings"
"github.com/juju/errors"
"github.com/juju/utils/featureflag"
"github.com/juju/utils/os"
"github.com/juju/utils/shell"
"github.com/juju/juju/feature"
"github.com/juju/juju/juju/series"
"github.com/juju/juju/service/common"
"github.com/juju/juju/service/systemd"
"github.com/juju/juju/service/upstart"
"github.com/juju/juju/service/windows"
)
// DiscoverService returns an interface to a service appropriate
// for the current system
func DiscoverService(name string, conf common.Conf) (Service, error) {
initName, err := discoverInitSystem()
if err != nil {
return nil, errors.Trace(err)
}
service, err := newService(name, conf, initName, series.HostSeries())
if err != nil {
return nil, errors.Trace(err)
}
return service, nil
}
func discoverInitSystem() (string, error) {
initName, err := discoverLocalInitSystem()
if errors.IsNotFound(err) {
// Fall back to checking the juju version.
versionInitName, err2 := VersionInitSystem(series.HostSeries())
if err2 != nil {
// The key error is the one from discoverLocalInitSystem so
// that is what we return.
return "", errors.Wrap(err2, err)
}
initName = versionInitName
} else if err != nil {
return "", errors.Trace(err)
}
return initName, nil
}
// VersionInitSystem returns an init system name based on the provided
// series. If one cannot be identified a NotFound error is returned.
func VersionInitSystem(series string) (string, error) {
initName, err := versionInitSystem(series)
if err != nil {
return "", errors.Trace(err)
}
logger.Debugf("discovered init system %q from series %q", initName, series)
return initName, nil
}
func versionInitSystem(ser string) (string, error) {
seriesos, err := series.GetOSFromSeries(ser)
if err != nil {
notFound := errors.NotFoundf("init system for series %q", ser)
return "", errors.Wrap(err, notFound)
}
switch seriesos {
case os.Windows:
return InitSystemWindows, nil
case os.Ubuntu:
switch ser {
case "precise", "quantal", "raring", "saucy", "trusty", "utopic":
return InitSystemUpstart, nil
default:
// vivid and later
if featureflag.Enabled(feature.LegacyUpstart) {
return InitSystemUpstart, nil
}
return InitSystemSystemd, nil
}
case os.CentOS:
return InitSystemSystemd, nil
}
return "", errors.NotFoundf("unknown os %q (from series %q), init system", seriesos, ser)
}
type discoveryCheck struct {
name string
isRunning func() (bool, error)
}
var discoveryFuncs = []discoveryCheck{
{InitSystemUpstart, upstart.IsRunning},
{InitSystemSystemd, systemd.IsRunning},
{InitSystemWindows, windows.IsRunning},
}
func discoverLocalInitSystem() (string, error) {
for _, check := range discoveryFuncs {
local, err := check.isRunning()
if err != nil {
logger.Debugf("failed to find init system %q: %v", check.name, err)
}
// We expect that in error cases "local" will be false.
if local {
logger.Debugf("discovered init system %q from local host", check.name)
return check.name, nil
}
}
return "", errors.NotFoundf("init system (based on local host)")
}
const discoverInitSystemScript = `
# Use guaranteed discovery mechanisms for known init systems.
if [ -d /run/systemd/system ]; then
echo -n systemd
exit 0
elif [ -f /sbin/initctl ] && /sbin/initctl --system list 2>&1 > /dev/null; then
echo -n upstart
exit 0
fi
# uh-oh
exit 1
`
// DiscoverInitSystemScript returns the shell script to use when
// discovering the local init system. The script is quite specific to
// bash, so it includes an explicit bash shbang.
func DiscoverInitSystemScript() string {
renderer := shell.BashRenderer{}
data := renderer.RenderScript([]string{discoverInitSystemScript})
return string(data)
}
// shellCase is the template for a bash case statement, for use in
// newShellSelectCommand.
const shellCase = `
case "$%s" in
%s
*)
%s
;;
esac`
// newShellSelectCommand creates a bash case statement with clause for
// each of the linux init systems. The body of each clause comes from
// calling the provided handler with the init system name. If the
// handler does not support the args then it returns a false "ok" value.
func newShellSelectCommand(envVarName, dflt string, handler func(string) (string, bool)) string {
var cases []string
for _, initSystem := range linuxInitSystems {
cmd, ok := handler(initSystem)
if !ok {
continue
}
cases = append(cases, initSystem+")", " "+cmd, " ;;")
}
if len(cases) == 0 {
return ""
}
return fmt.Sprintf(shellCase[1:], envVarName, strings.Join(cases, "\n"), dflt)
}