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  }