github.com/alloyci/alloy-runner@v1.0.1-0.20180222164613-925503ccafd6/helpers/docker/machine_command.go (about) 1 package docker_helpers 2 3 import ( 4 "bufio" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strings" 14 "time" 15 16 "github.com/Sirupsen/logrus" 17 "github.com/docker/machine/commands/mcndirs" 18 ) 19 20 type logWriter struct { 21 log func(args ...interface{}) 22 reader *bufio.Reader 23 } 24 25 func (l *logWriter) write(line string) { 26 line = strings.TrimRight(line, "\n") 27 28 if len(line) <= 0 { 29 return 30 } 31 32 l.log(line) 33 } 34 35 func (l *logWriter) watch() { 36 for { 37 line, err := l.reader.ReadString('\n') 38 if err == nil || err == io.EOF { 39 l.write(line) 40 if err == io.EOF { 41 return 42 } 43 } else { 44 if !strings.Contains(err.Error(), "bad file descriptor") { 45 logrus.WithError(err).Errorln("Problem while reading command output") 46 } 47 return 48 } 49 } 50 } 51 52 func newLogWriter(logFunction func(args ...interface{}), reader io.Reader) { 53 writer := &logWriter{ 54 log: logFunction, 55 reader: bufio.NewReader(reader), 56 } 57 58 go writer.watch() 59 } 60 61 func stdoutLogWriter(cmd *exec.Cmd, fields logrus.Fields) { 62 log := logrus.WithFields(fields) 63 reader, err := cmd.StdoutPipe() 64 65 if err == nil { 66 newLogWriter(log.Infoln, reader) 67 } 68 } 69 70 func stderrLogWriter(cmd *exec.Cmd, fields logrus.Fields) { 71 log := logrus.WithFields(fields) 72 reader, err := cmd.StderrPipe() 73 74 if err == nil { 75 newLogWriter(log.Errorln, reader) 76 } 77 } 78 79 type machineCommand struct { 80 } 81 82 func (m *machineCommand) Create(driver, name string, opts ...string) error { 83 args := []string{ 84 "create", 85 "--driver", driver, 86 } 87 for _, opt := range opts { 88 args = append(args, "--"+opt) 89 } 90 args = append(args, name) 91 92 cmd := exec.Command("docker-machine", args...) 93 cmd.Env = os.Environ() 94 95 fields := logrus.Fields{ 96 "operation": "create", 97 "driver": driver, 98 "name": name, 99 } 100 stdoutLogWriter(cmd, fields) 101 stderrLogWriter(cmd, fields) 102 103 logrus.Debugln("Executing", cmd.Path, cmd.Args) 104 return cmd.Run() 105 } 106 107 func (m *machineCommand) Provision(name string) error { 108 cmd := exec.Command("docker-machine", "provision", name) 109 cmd.Env = os.Environ() 110 111 fields := logrus.Fields{ 112 "operation": "provision", 113 "name": name, 114 } 115 stdoutLogWriter(cmd, fields) 116 stderrLogWriter(cmd, fields) 117 118 return cmd.Run() 119 } 120 121 func (m *machineCommand) Stop(name string, timeout time.Duration) error { 122 ctx, ctxCancelFn := context.WithTimeout(context.Background(), timeout) 123 defer ctxCancelFn() 124 125 cmd := exec.CommandContext(ctx, "docker-machine", "stop", name) 126 cmd.Env = os.Environ() 127 128 fields := logrus.Fields{ 129 "operation": "stop", 130 "name": name, 131 } 132 stdoutLogWriter(cmd, fields) 133 stderrLogWriter(cmd, fields) 134 135 return cmd.Run() 136 } 137 138 func (m *machineCommand) Remove(name string) error { 139 cmd := exec.Command("docker-machine", "rm", "-y", name) 140 cmd.Env = os.Environ() 141 142 fields := logrus.Fields{ 143 "operation": "remove", 144 "name": name, 145 } 146 stdoutLogWriter(cmd, fields) 147 stderrLogWriter(cmd, fields) 148 149 return cmd.Run() 150 } 151 152 func (m *machineCommand) List() (hostNames []string, err error) { 153 dir, err := ioutil.ReadDir(mcndirs.GetMachineDir()) 154 if err != nil { 155 if os.IsNotExist(err) { 156 return nil, nil 157 } 158 return nil, err 159 } 160 161 for _, file := range dir { 162 if file.IsDir() && !strings.HasPrefix(file.Name(), ".") { 163 hostNames = append(hostNames, file.Name()) 164 } 165 } 166 167 return 168 } 169 170 func (m *machineCommand) get(args ...string) (out string, err error) { 171 // Execute docker-machine to fetch IP 172 cmd := exec.Command("docker-machine", args...) 173 cmd.Env = os.Environ() 174 data, err := cmd.Output() 175 if err != nil { 176 return 177 } 178 179 // Save the IP 180 out = strings.TrimSpace(string(data)) 181 if out == "" { 182 err = fmt.Errorf("failed to get %v", args) 183 } 184 return 185 } 186 187 func (m *machineCommand) IP(name string) (string, error) { 188 return m.get("ip", name) 189 } 190 191 func (m *machineCommand) URL(name string) (string, error) { 192 return m.get("url", name) 193 } 194 195 func (m *machineCommand) CertPath(name string) (string, error) { 196 return m.get("inspect", name, "-f", "{{.HostOptions.AuthOptions.StorePath}}") 197 } 198 199 func (m *machineCommand) Status(name string) (string, error) { 200 return m.get("status", name) 201 } 202 203 func (m *machineCommand) Exist(name string) bool { 204 configPath := filepath.Join(mcndirs.GetMachineDir(), name, "config.json") 205 _, err := os.Stat(configPath) 206 if err != nil { 207 return false 208 } 209 210 cmd := exec.Command("docker-machine", "inspect", name) 211 cmd.Env = os.Environ() 212 213 fields := logrus.Fields{ 214 "operation": "exists", 215 "name": name, 216 } 217 stderrLogWriter(cmd, fields) 218 219 return cmd.Run() == nil 220 } 221 222 func (m *machineCommand) CanConnect(name string) bool { 223 // Execute docker-machine config which actively ask the machine if it is up and online 224 cmd := exec.Command("docker-machine", "config", name) 225 cmd.Env = os.Environ() 226 err := cmd.Run() 227 if err == nil { 228 return true 229 } 230 return false 231 } 232 233 func (m *machineCommand) Credentials(name string) (dc DockerCredentials, err error) { 234 if !m.CanConnect(name) { 235 err = errors.New("Can't connect") 236 return 237 } 238 239 dc.TLSVerify = true 240 dc.Host, err = m.URL(name) 241 if err == nil { 242 dc.CertPath, err = m.CertPath(name) 243 } 244 return 245 } 246 247 func NewMachineCommand() Machine { 248 return &machineCommand{} 249 }