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