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  }