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 }