github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/service/common/conf.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"reflect"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/utils/shell"
    13  )
    14  
    15  // Conf is responsible for defining services. Its fields
    16  // represent elements of a service configuration.
    17  type Conf struct {
    18  	// Desc is the init service's description.
    19  	Desc string
    20  
    21  	// Transient indicates whether or not the service is a one-off.
    22  	Transient bool
    23  
    24  	// AfterStopped is the name, if any, of another service. This
    25  	// service will not start until after the other stops.
    26  	AfterStopped string
    27  
    28  	// Env holds the environment variables that will be set when the
    29  	// command runs.
    30  	// Currently not used on Windows.
    31  	Env map[string]string
    32  
    33  	// TODO(ericsnow) Add a Limit type, since the possible keys are known.
    34  
    35  	// Limit holds the ulimit values that will be set when the command
    36  	// runs. Each value will be used as both the soft and hard limit.
    37  	// Currently not used on Windows.
    38  	// Valid values are integers or "infinity"
    39  	Limit map[string]string
    40  
    41  	// Timeout is how many seconds may pass before an exec call (e.g.
    42  	// ExecStart) times out. Values less than or equal to 0 (the
    43  	// default) are treated as though there is no timeout.
    44  	Timeout int
    45  
    46  	// ExecStart is the command (with arguments) that will be run. The
    47  	// path to the executable must be absolute.
    48  	// The command will be restarted if it exits with a non-zero exit code.
    49  	ExecStart string
    50  
    51  	// ExecStopPost is the command that will be run after the service stops.
    52  	// The path to the executable must be absolute.
    53  	ExecStopPost string
    54  
    55  	// Logfile, if set, indicates where the service's output should be
    56  	// written.
    57  	Logfile string
    58  
    59  	// TODO(ericsnow) Turn ExtraScript into ExecStartPre.
    60  
    61  	// ExtraScript allows to insert script before command execution.
    62  	ExtraScript string
    63  
    64  	// ServiceBinary is the actual binary without any arguments.
    65  	ServiceBinary string
    66  
    67  	// ServiceArgs is a string array of unquoted arguments
    68  	ServiceArgs []string
    69  }
    70  
    71  // IsZero determines whether or not the conf is a zero value.
    72  func (c Conf) IsZero() bool {
    73  	return reflect.DeepEqual(c, Conf{})
    74  }
    75  
    76  // Validate checks the conf's values for correctness.
    77  func (c Conf) Validate(renderer shell.Renderer) error {
    78  	if c.Desc == "" {
    79  		return errors.New("missing Desc")
    80  	}
    81  
    82  	// Check the Exec* fields.
    83  	if c.ExecStart == "" {
    84  		return errors.New("missing ExecStart")
    85  	}
    86  
    87  	for field, cmd := range map[string]string{
    88  		"ExecStart":    c.ExecStart,
    89  		"ExecStopPost": c.ExecStopPost,
    90  	} {
    91  		if cmd == "" {
    92  			continue
    93  		}
    94  		if err := c.checkExec(field, cmd, renderer); err != nil {
    95  			return errors.Trace(err)
    96  		}
    97  	}
    98  
    99  	for _, value := range c.Limit {
   100  		switch value {
   101  		case "unlimited", "infinity":
   102  		default:
   103  			_, err := strconv.Atoi(value)
   104  			if err != nil {
   105  				newErr := errors.NotValidf("limit must be \"infinity\", \"unlimited\", or an integer, %q", value)
   106  				return errors.Wrap(err, newErr)
   107  			}
   108  		}
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  func (c Conf) checkExec(name, cmd string, renderer shell.Renderer) error {
   115  	executable := strings.Fields(cmd)[0]
   116  	executable = Unquote(executable)
   117  	if !renderer.IsAbs(executable) {
   118  		return errors.NotValidf("relative path in %s (%s)", name, executable)
   119  	}
   120  	return nil
   121  }
   122  
   123  // Unquote returns the string embedded between matching quotation marks.
   124  // If there aren't any matching quotation marks then the string is
   125  // returned as-is.
   126  func Unquote(str string) string {
   127  	if len(str) < 2 {
   128  		return str
   129  	}
   130  	for _, quote := range []string{`'`, `"`} {
   131  		if str[0:1] == quote && str[len(str)-1:] == quote {
   132  			return strings.Trim(str, quote)
   133  		}
   134  	}
   135  	return str
   136  }