github.com/yasker/longhorn-engine@v0.0.0-20160621014712-6ed6cfca0729/sync/agent/process.go (about)

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"os"
     9  	"os/exec"
    10  	"strconv"
    11  	"sync"
    12  	"syscall"
    13  
    14  	"github.com/Sirupsen/logrus"
    15  	"github.com/docker/docker/pkg/reexec"
    16  	"github.com/gorilla/mux"
    17  	"github.com/rancher/go-rancher/api"
    18  	"github.com/rancher/go-rancher/client"
    19  )
    20  
    21  type Server struct {
    22  	sync.Mutex
    23  
    24  	processCounter     int
    25  	currentPort        int
    26  	startPort, endPort int
    27  	nextProcess        int
    28  	processes          map[string]*Process
    29  	processesByPort    map[int]*Process
    30  }
    31  
    32  func NewServer(start, end int) *Server {
    33  	return &Server{
    34  		currentPort:     start,
    35  		startPort:       start,
    36  		endPort:         end,
    37  		processes:       map[string]*Process{},
    38  		processesByPort: map[int]*Process{},
    39  	}
    40  }
    41  
    42  func (s *Server) ListProcesses(rw http.ResponseWriter, req *http.Request) error {
    43  	apiContext := api.GetApiContext(req)
    44  	resp := ProcessCollection{
    45  		Collection: client.Collection{
    46  			ResourceType: "process",
    47  		},
    48  	}
    49  
    50  	s.Lock()
    51  	for _, p := range s.processes {
    52  		resp.Data = append(resp.Data, *p)
    53  	}
    54  	s.Unlock()
    55  
    56  	apiContext.Write(&resp)
    57  	return nil
    58  }
    59  
    60  func (s *Server) GetProcess(rw http.ResponseWriter, req *http.Request) error {
    61  	apiContext := api.GetApiContext(req)
    62  	id := mux.Vars(req)["id"]
    63  
    64  	s.Lock()
    65  	p, ok := s.processes[id]
    66  	s.Unlock()
    67  
    68  	if ok {
    69  		apiContext.Write(&p)
    70  	} else {
    71  		rw.WriteHeader(http.StatusNotFound)
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  func (s *Server) CreateProcess(rw http.ResponseWriter, req *http.Request) error {
    78  	var p Process
    79  	apiContext := api.GetApiContext(req)
    80  	if err := apiContext.Read(&p); err != nil {
    81  		return err
    82  	}
    83  
    84  	s.Lock()
    85  
    86  	if p.SrcFile == "" {
    87  		var err error
    88  		p.Port, err = s.nextPort()
    89  		if err != nil {
    90  			s.Unlock()
    91  			return err
    92  		}
    93  	}
    94  
    95  	s.processCounter++
    96  	id := strconv.Itoa(s.processCounter)
    97  	p.Id = id
    98  	p.Type = "process"
    99  	s.processes[p.Id] = &p
   100  	s.processesByPort[p.Port] = &p
   101  
   102  	s.Unlock()
   103  
   104  	p.ExitCode = -2
   105  	go func() {
   106  		if err := s.launch(&p); err != nil {
   107  			logrus.Errorf("Failed to launch %#v: %v", p, err)
   108  		}
   109  		s.Lock()
   110  		delete(s.processesByPort, p.Port)
   111  		s.Unlock()
   112  	}()
   113  
   114  	apiContext.Write(&p)
   115  	return nil
   116  }
   117  
   118  func (s *Server) launch(p *Process) error {
   119  	switch p.ProcessType {
   120  	case "sync":
   121  		return s.launchSync(p)
   122  	case "fold":
   123  		return s.launchFold(p)
   124  	case "backup":
   125  		return s.launchBackup(p)
   126  	case "rmbackup":
   127  		return s.launchRmBackup(p)
   128  	case "restore":
   129  		return s.launchRestore(p)
   130  	case "inspectbackup":
   131  		return s.launchInspectBackup(p)
   132  	case "hardlink":
   133  		return s.launchHardLink(p)
   134  	}
   135  	return fmt.Errorf("Unknown process type %s", p.ProcessType)
   136  }
   137  
   138  func (s *Server) launchFold(p *Process) error {
   139  	cmd := reexec.Command("sfold", p.SrcFile, p.DestFile)
   140  	cmd.SysProcAttr = &syscall.SysProcAttr{
   141  		Pdeathsig: syscall.SIGKILL,
   142  	}
   143  	cmd.Stdout = os.Stdout
   144  	cmd.Stderr = os.Stderr
   145  	if err := cmd.Start(); err != nil {
   146  		return err
   147  	}
   148  
   149  	logrus.Infof("Running %s %v", cmd.Path, cmd.Args)
   150  	err := cmd.Wait()
   151  	if err != nil {
   152  		logrus.Infof("Error running %s %v: %v", "sfold", cmd.Args, err)
   153  		p.ExitCode = 1
   154  		if exitError, ok := err.(*exec.ExitError); ok {
   155  			if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
   156  				logrus.Infof("Error running %s %v: %v", "sfold", cmd.Args, waitStatus.ExitStatus())
   157  				p.ExitCode = waitStatus.ExitStatus()
   158  			}
   159  		}
   160  		return err
   161  	}
   162  
   163  	p.ExitCode = 0
   164  	logrus.Infof("Done running %s %v", "sfold", cmd.Args)
   165  	return nil
   166  }
   167  
   168  func binName() (string, error) {
   169  	if _, err := os.Stat(os.Args[0]); err == nil {
   170  		return os.Args[0], nil
   171  	}
   172  	return exec.LookPath(os.Args[0])
   173  }
   174  
   175  func (s *Server) launchSync(p *Process) error {
   176  	args := []string{"ssync"}
   177  	if p.Host != "" {
   178  		args = append(args, "-host", p.Host)
   179  	}
   180  	if p.Port != 0 {
   181  		args = append(args, "-port", strconv.Itoa(p.Port))
   182  	}
   183  	if p.SrcFile == "" {
   184  		args = append(args, "-daemon")
   185  	} else {
   186  		args = append(args, p.SrcFile)
   187  		if p.DestFile != "" {
   188  			args = append(args, p.DestFile)
   189  		}
   190  	}
   191  
   192  	cmd := reexec.Command(args...)
   193  	cmd.SysProcAttr = &syscall.SysProcAttr{
   194  		Pdeathsig: syscall.SIGKILL,
   195  	}
   196  	cmd.Stdout = os.Stdout
   197  	cmd.Stderr = os.Stderr
   198  	if err := cmd.Start(); err != nil {
   199  		return err
   200  	}
   201  
   202  	logrus.Infof("Running %s %v", "ssync", args)
   203  	err := cmd.Wait()
   204  	if err != nil {
   205  		logrus.Infof("Error running %s %v: %v", "ssync", args, err)
   206  		p.ExitCode = 1
   207  		if exitError, ok := err.(*exec.ExitError); ok {
   208  			if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
   209  				logrus.Infof("Error running %s %v: %v", "ssync", args, waitStatus.ExitStatus())
   210  				p.ExitCode = waitStatus.ExitStatus()
   211  			}
   212  		}
   213  		return err
   214  	}
   215  
   216  	p.ExitCode = 0
   217  	logrus.Infof("Done running %s %v", "ssync", args)
   218  	return nil
   219  }
   220  
   221  func (s *Server) nextPort() (int, error) {
   222  	// Must be called with s.Lock() obtained
   223  	for i := 0; i < (s.endPort - s.startPort + 1); i++ {
   224  		port := s.currentPort
   225  		s.currentPort++
   226  		if s.currentPort > s.endPort {
   227  			s.currentPort = s.startPort
   228  		}
   229  
   230  		if _, ok := s.processesByPort[port]; ok {
   231  			continue
   232  		}
   233  
   234  		return port, nil
   235  	}
   236  
   237  	return 0, errors.New("Out of ports")
   238  }
   239  
   240  func (s *Server) launchBackup(p *Process) error {
   241  	buf := new(bytes.Buffer)
   242  
   243  	cmd := reexec.Command("sbackup", "create", p.SrcFile, "--dest", p.DestFile,
   244  		"--volume", p.Host)
   245  	cmd.SysProcAttr = &syscall.SysProcAttr{
   246  		Pdeathsig: syscall.SIGKILL,
   247  	}
   248  	cmd.Stdout = buf
   249  	cmd.Stderr = os.Stdout
   250  	if err := cmd.Start(); err != nil {
   251  		return err
   252  	}
   253  
   254  	logrus.Infof("Running %s %v", cmd.Path, cmd.Args)
   255  	err := cmd.Wait()
   256  
   257  	p.Output = buf.String()
   258  	fmt.Fprintf(os.Stdout, p.Output)
   259  	if err != nil {
   260  		logrus.Infof("Error running %s %v: %v", "sbackup", cmd.Args, err)
   261  		p.ExitCode = 1
   262  		if exitError, ok := err.(*exec.ExitError); ok {
   263  			if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
   264  				logrus.Infof("Error running %s %v: %v", "sbackup", cmd.Args, waitStatus.ExitStatus())
   265  				p.ExitCode = waitStatus.ExitStatus()
   266  			}
   267  		}
   268  		return err
   269  	}
   270  
   271  	p.ExitCode = 0
   272  	logrus.Infof("Done running %s %v, returns %v", "sbackup", cmd.Args, p.Output)
   273  	return nil
   274  }
   275  
   276  func (s *Server) launchRmBackup(p *Process) error {
   277  	buf := new(bytes.Buffer)
   278  
   279  	cmd := reexec.Command("sbackup", "delete", p.SrcFile)
   280  	cmd.SysProcAttr = &syscall.SysProcAttr{
   281  		Pdeathsig: syscall.SIGKILL,
   282  	}
   283  	cmd.Stdout = buf
   284  	cmd.Stderr = os.Stdout
   285  	if err := cmd.Start(); err != nil {
   286  		return err
   287  	}
   288  
   289  	logrus.Infof("Running %s %v", cmd.Path, cmd.Args)
   290  	err := cmd.Wait()
   291  
   292  	p.Output = buf.String()
   293  	fmt.Fprintf(os.Stdout, p.Output)
   294  	if err != nil {
   295  		logrus.Infof("Error running %s %v: %v", "sbackup", cmd.Args, err)
   296  		p.ExitCode = 1
   297  		if exitError, ok := err.(*exec.ExitError); ok {
   298  			if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
   299  				logrus.Infof("Error running %s %v: %v", "sbackup", cmd.Args, waitStatus.ExitStatus())
   300  				p.ExitCode = waitStatus.ExitStatus()
   301  			}
   302  		}
   303  		return err
   304  	}
   305  
   306  	p.ExitCode = 0
   307  	logrus.Infof("Done running %s %v", "sbackup", cmd.Args)
   308  	return nil
   309  }
   310  
   311  func (s *Server) launchRestore(p *Process) error {
   312  	buf := new(bytes.Buffer)
   313  
   314  	cmd := reexec.Command("sbackup", "restore", p.SrcFile, "--to", p.DestFile)
   315  	cmd.SysProcAttr = &syscall.SysProcAttr{
   316  		Pdeathsig: syscall.SIGKILL,
   317  	}
   318  	cmd.Stdout = buf
   319  	cmd.Stderr = os.Stderr
   320  	if err := cmd.Start(); err != nil {
   321  		return err
   322  	}
   323  
   324  	logrus.Infof("Running %s %v", cmd.Path, cmd.Args)
   325  	err := cmd.Wait()
   326  
   327  	p.Output = buf.String()
   328  	fmt.Fprintf(os.Stdout, p.Output)
   329  	if err != nil {
   330  		logrus.Infof("Error running %s %v: %v", "sbackup", cmd.Args, err)
   331  		p.ExitCode = 1
   332  		if exitError, ok := err.(*exec.ExitError); ok {
   333  			if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
   334  				logrus.Infof("Error running %s %v: %v", "sbackup", cmd.Args, waitStatus.ExitStatus())
   335  				p.ExitCode = waitStatus.ExitStatus()
   336  			}
   337  		}
   338  		return err
   339  	}
   340  
   341  	p.ExitCode = 0
   342  	logrus.Infof("Done running %s %v", "sbackup", cmd.Args)
   343  	return nil
   344  }
   345  
   346  func (s *Server) launchHardLink(p *Process) error {
   347  	oldName := p.SrcFile
   348  	newName := p.DestFile
   349  
   350  	if oldName == "" {
   351  		p.ExitCode = 0
   352  		return nil
   353  	}
   354  
   355  	if _, err := os.Stat(newName); err == nil {
   356  		logrus.Infof("Old file %s exists, deleting", newName)
   357  		if err := os.Remove(newName); err != nil {
   358  			p.ExitCode = 1
   359  			return err
   360  		}
   361  	}
   362  
   363  	if err := os.Link(oldName, newName); err != nil {
   364  		p.ExitCode = 1
   365  		return err
   366  	}
   367  
   368  	p.ExitCode = 0
   369  	logrus.Infof("Done running %s %v %v", "hardlink", oldName, newName)
   370  	return nil
   371  }
   372  
   373  func (s *Server) launchInspectBackup(p *Process) error {
   374  	buf := new(bytes.Buffer)
   375  
   376  	cmd := reexec.Command("sbackup", "inspect", p.SrcFile)
   377  	cmd.SysProcAttr = &syscall.SysProcAttr{
   378  		Pdeathsig: syscall.SIGKILL,
   379  	}
   380  	cmd.Stdout = buf
   381  	cmd.Stderr = os.Stderr
   382  	if err := cmd.Start(); err != nil {
   383  		return err
   384  	}
   385  
   386  	logrus.Infof("Running %s %v", cmd.Path, cmd.Args)
   387  	err := cmd.Wait()
   388  
   389  	p.Output = buf.String()
   390  	fmt.Fprintf(os.Stdout, p.Output)
   391  	if err != nil {
   392  		logrus.Infof("Error running %s %v: %v", "sbackup", cmd.Args, err)
   393  		p.ExitCode = 1
   394  		if exitError, ok := err.(*exec.ExitError); ok {
   395  			if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
   396  				logrus.Infof("Error running %s %v: %v", "sbackup", cmd.Args, waitStatus.ExitStatus())
   397  				p.ExitCode = waitStatus.ExitStatus()
   398  			}
   399  		}
   400  		return err
   401  	}
   402  
   403  	p.ExitCode = 0
   404  	logrus.Infof("Done running %s %v", "sbackup", cmd.Args)
   405  	return nil
   406  }