~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/service/discovery.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

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