github.com/reds/docker@v1.11.2-rc1/libcontainerd/container_linux.go (about)

     1  package libcontainerd
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	containerd "github.com/docker/containerd/api/grpc/types"
    14  	"github.com/docker/docker/restartmanager"
    15  	"github.com/opencontainers/specs/specs-go"
    16  	"golang.org/x/net/context"
    17  )
    18  
    19  type container struct {
    20  	containerCommon
    21  
    22  	// Platform specific fields are below here.
    23  	pauseMonitor
    24  	oom bool
    25  }
    26  
    27  func (ctr *container) clean() error {
    28  	if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" {
    29  		return nil
    30  	}
    31  	if _, err := os.Lstat(ctr.dir); err != nil {
    32  		if os.IsNotExist(err) {
    33  			return nil
    34  		}
    35  		return err
    36  	}
    37  
    38  	if err := os.RemoveAll(ctr.dir); err != nil {
    39  		return err
    40  	}
    41  	return nil
    42  }
    43  
    44  // cleanProcess removes the fifos used by an additional process.
    45  // Caller needs to lock container ID before calling this method.
    46  func (ctr *container) cleanProcess(id string) {
    47  	if p, ok := ctr.processes[id]; ok {
    48  		for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} {
    49  			if err := os.Remove(p.fifo(i)); err != nil {
    50  				logrus.Warnf("failed to remove %v for process %v: %v", p.fifo(i), id, err)
    51  			}
    52  		}
    53  	}
    54  	delete(ctr.processes, id)
    55  }
    56  
    57  func (ctr *container) spec() (*specs.Spec, error) {
    58  	var spec specs.Spec
    59  	dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename))
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	if err := json.Unmarshal(dt, &spec); err != nil {
    64  		return nil, err
    65  	}
    66  	return &spec, nil
    67  }
    68  
    69  func (ctr *container) start() error {
    70  	spec, err := ctr.spec()
    71  	if err != nil {
    72  		return nil
    73  	}
    74  	iopipe, err := ctr.openFifos(spec.Process.Terminal)
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	r := &containerd.CreateContainerRequest{
    80  		Id:         ctr.containerID,
    81  		BundlePath: ctr.dir,
    82  		Stdin:      ctr.fifo(syscall.Stdin),
    83  		Stdout:     ctr.fifo(syscall.Stdout),
    84  		Stderr:     ctr.fifo(syscall.Stderr),
    85  		// check to see if we are running in ramdisk to disable pivot root
    86  		NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
    87  	}
    88  	ctr.client.appendContainer(ctr)
    89  
    90  	resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
    91  	if err != nil {
    92  		ctr.closeFifos(iopipe)
    93  		return err
    94  	}
    95  	ctr.startedAt = time.Now()
    96  
    97  	if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
    98  		return err
    99  	}
   100  	ctr.systemPid = systemPid(resp.Container)
   101  
   102  	return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{
   103  		State: StateStart,
   104  		Pid:   ctr.systemPid,
   105  	})
   106  }
   107  
   108  func (ctr *container) newProcess(friendlyName string) *process {
   109  	return &process{
   110  		dir: ctr.dir,
   111  		processCommon: processCommon{
   112  			containerID:  ctr.containerID,
   113  			friendlyName: friendlyName,
   114  			client:       ctr.client,
   115  		},
   116  	}
   117  }
   118  
   119  func (ctr *container) handleEvent(e *containerd.Event) error {
   120  	ctr.client.lock(ctr.containerID)
   121  	defer ctr.client.unlock(ctr.containerID)
   122  	switch e.Type {
   123  	case StateExit, StatePause, StateResume, StateOOM:
   124  		st := StateInfo{
   125  			State:     e.Type,
   126  			ExitCode:  e.Status,
   127  			OOMKilled: e.Type == StateExit && ctr.oom,
   128  		}
   129  		if e.Type == StateOOM {
   130  			ctr.oom = true
   131  		}
   132  		if e.Type == StateExit && e.Pid != InitFriendlyName {
   133  			st.ProcessID = e.Pid
   134  			st.State = StateExitProcess
   135  		}
   136  		if st.State == StateExit && ctr.restartManager != nil {
   137  			restart, wait, err := ctr.restartManager.ShouldRestart(e.Status, false, time.Since(ctr.startedAt))
   138  			if err != nil {
   139  				logrus.Warnf("container %s %v", ctr.containerID, err)
   140  			} else if restart {
   141  				st.State = StateRestart
   142  				ctr.restarting = true
   143  				ctr.client.deleteContainer(e.Id)
   144  				go func() {
   145  					err := <-wait
   146  					ctr.client.lock(ctr.containerID)
   147  					defer ctr.client.unlock(ctr.containerID)
   148  					ctr.restarting = false
   149  					if err != nil {
   150  						st.State = StateExit
   151  						ctr.clean()
   152  						ctr.client.q.append(e.Id, func() {
   153  							if err := ctr.client.backend.StateChanged(e.Id, st); err != nil {
   154  								logrus.Error(err)
   155  							}
   156  						})
   157  						if err != restartmanager.ErrRestartCanceled {
   158  							logrus.Error(err)
   159  						}
   160  					} else {
   161  						ctr.start()
   162  					}
   163  				}()
   164  			}
   165  		}
   166  
   167  		// Remove process from list if we have exited
   168  		// We need to do so here in case the Message Handler decides to restart it.
   169  		switch st.State {
   170  		case StateExit:
   171  			ctr.clean()
   172  			ctr.client.deleteContainer(e.Id)
   173  		case StateExitProcess:
   174  			ctr.cleanProcess(st.ProcessID)
   175  		}
   176  		ctr.client.q.append(e.Id, func() {
   177  			if err := ctr.client.backend.StateChanged(e.Id, st); err != nil {
   178  				logrus.Error(err)
   179  			}
   180  			if e.Type == StatePause || e.Type == StateResume {
   181  				ctr.pauseMonitor.handle(e.Type)
   182  			}
   183  			if e.Type == StateExit {
   184  				if en := ctr.client.getExitNotifier(e.Id); en != nil {
   185  					en.close()
   186  				}
   187  			}
   188  		})
   189  
   190  	default:
   191  		logrus.Debugf("event unhandled: %+v", e)
   192  	}
   193  	return nil
   194  }
   195  
   196  // discardFifos attempts to fully read the container fifos to unblock processes
   197  // that may be blocked on the writer side.
   198  func (ctr *container) discardFifos() {
   199  	for _, i := range []int{syscall.Stdout, syscall.Stderr} {
   200  		f := ctr.fifo(i)
   201  		c := make(chan struct{})
   202  		go func() {
   203  			close(c) // this channel is used to not close the writer too early, before readonly open has been called.
   204  			io.Copy(ioutil.Discard, openReaderFromFifo(f))
   205  		}()
   206  		<-c
   207  		closeReaderFifo(f) // avoid blocking permanently on open if there is no writer side
   208  	}
   209  }