Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| // Copyright 2015 Canonical Ltd. | |
| // Licensed under the AGPLv3, see LICENCE file for details. | |
| package service | |
| import ( | |
| "fmt" | |
| "strings" | |
| "github.com/juju/errors" | |
| "github.com/juju/utils/featureflag" | |
| "github.com/juju/utils/os" | |
| "github.com/juju/utils/series" | |
| "github.com/juju/utils/shell" | |
| "github.com/juju/juju/feature" | |
| "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) { | |
| hostSeries := series.MustHostSeries() | |
| initName, err := discoverInitSystem(hostSeries) | |
| if err != nil { | |
| return nil, errors.Trace(err) | |
| } | |
| service, err := newService(name, conf, initName, hostSeries) | |
| if err != nil { | |
| return nil, errors.Trace(err) | |
| } | |
| return service, nil | |
| } | |
| func discoverInitSystem(hostSeries string) (string, error) { | |
| initName, err := discoverLocalInitSystem() | |
| if errors.IsNotFound(err) { | |
| // Fall back to checking the juju version. | |
| versionInitName, err2 := VersionInitSystem(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) | |
| } |