github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/linux_container/linux_container.go (about)

     1  package linux_container
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"os/exec"
     9  	"path"
    10  	"path/filepath"
    11  	"strconv"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/cloudfoundry-incubator/garden"
    17  	"github.com/cloudfoundry-incubator/garden-linux/linux_backend"
    18  	"github.com/cloudfoundry-incubator/garden-linux/network"
    19  	"github.com/cloudfoundry-incubator/garden-linux/network/subnets"
    20  	"github.com/cloudfoundry-incubator/garden-linux/old/bandwidth_manager"
    21  	"github.com/cloudfoundry-incubator/garden-linux/old/cgroups_manager"
    22  	"github.com/cloudfoundry-incubator/garden-linux/old/logging"
    23  	"github.com/cloudfoundry-incubator/garden-linux/old/quota_manager"
    24  	"github.com/cloudfoundry-incubator/garden-linux/process"
    25  	"github.com/cloudfoundry-incubator/garden-linux/process_tracker"
    26  	"github.com/cloudfoundry/gunk/command_runner"
    27  	"github.com/pivotal-golang/lager"
    28  )
    29  
    30  type UndefinedPropertyError struct {
    31  	Key string
    32  }
    33  
    34  func (err UndefinedPropertyError) Error() string {
    35  	return fmt.Sprintf("property does not exist: %s", err.Key)
    36  }
    37  
    38  type LinuxContainer struct {
    39  	logger lager.Logger
    40  
    41  	id     string
    42  	handle string
    43  	path   string
    44  
    45  	properties      garden.Properties
    46  	propertiesMutex sync.RWMutex
    47  
    48  	graceTime time.Duration
    49  
    50  	state      State
    51  	stateMutex sync.RWMutex
    52  
    53  	events      []string
    54  	eventsMutex sync.RWMutex
    55  
    56  	resources *linux_backend.Resources
    57  
    58  	portPool PortPool
    59  
    60  	runner command_runner.CommandRunner
    61  
    62  	cgroupsManager   cgroups_manager.CgroupsManager
    63  	quotaManager     quota_manager.QuotaManager
    64  	bandwidthManager bandwidth_manager.BandwidthManager
    65  
    66  	processTracker process_tracker.ProcessTracker
    67  
    68  	filter network.Filter
    69  
    70  	oomMutex    sync.RWMutex
    71  	oomNotifier *exec.Cmd
    72  
    73  	currentBandwidthLimits *garden.BandwidthLimits
    74  	bandwidthMutex         sync.RWMutex
    75  
    76  	currentDiskLimits *garden.DiskLimits
    77  	diskMutex         sync.RWMutex
    78  
    79  	currentMemoryLimits *garden.MemoryLimits
    80  	memoryMutex         sync.RWMutex
    81  
    82  	currentCPULimits *garden.CPULimits
    83  	cpuMutex         sync.RWMutex
    84  
    85  	netIns      []NetInSpec
    86  	netInsMutex sync.RWMutex
    87  
    88  	netOuts      []garden.NetOutRule
    89  	netOutsMutex sync.RWMutex
    90  
    91  	mtu uint32
    92  
    93  	env process.Env
    94  
    95  	processIDPool *ProcessIDPool
    96  }
    97  
    98  type ProcessIDPool struct {
    99  	currentProcessID uint32
   100  	mu               sync.Mutex
   101  }
   102  
   103  func (p *ProcessIDPool) Next() uint32 {
   104  	p.mu.Lock()
   105  	defer p.mu.Unlock()
   106  
   107  	p.currentProcessID = p.currentProcessID + 1
   108  	return p.currentProcessID
   109  }
   110  
   111  func (p *ProcessIDPool) Restore(id uint32) {
   112  	p.mu.Lock()
   113  	defer p.mu.Unlock()
   114  
   115  	if id >= p.currentProcessID {
   116  		p.currentProcessID = id
   117  	}
   118  }
   119  
   120  type NetInSpec struct {
   121  	HostPort      uint32
   122  	ContainerPort uint32
   123  }
   124  
   125  type PortPool interface {
   126  	Acquire() (uint32, error)
   127  	Remove(uint32) error
   128  	Release(uint32)
   129  }
   130  
   131  type State string
   132  
   133  const (
   134  	StateBorn    = State("born")
   135  	StateActive  = State("active")
   136  	StateStopped = State("stopped")
   137  )
   138  
   139  func NewLinuxContainer(
   140  	logger lager.Logger,
   141  	id, handle, path string,
   142  	properties garden.Properties,
   143  	graceTime time.Duration,
   144  	resources *linux_backend.Resources,
   145  	portPool PortPool,
   146  	runner command_runner.CommandRunner,
   147  	cgroupsManager cgroups_manager.CgroupsManager,
   148  	quotaManager quota_manager.QuotaManager,
   149  	bandwidthManager bandwidth_manager.BandwidthManager,
   150  	processTracker process_tracker.ProcessTracker,
   151  	env process.Env,
   152  	filter network.Filter,
   153  ) *LinuxContainer {
   154  	return &LinuxContainer{
   155  		logger: logger,
   156  
   157  		id:     id,
   158  		handle: handle,
   159  		path:   path,
   160  
   161  		properties: properties,
   162  
   163  		graceTime: graceTime,
   164  
   165  		state:  StateBorn,
   166  		events: []string{},
   167  
   168  		resources: resources,
   169  
   170  		portPool: portPool,
   171  
   172  		runner: runner,
   173  
   174  		cgroupsManager:   cgroupsManager,
   175  		quotaManager:     quotaManager,
   176  		bandwidthManager: bandwidthManager,
   177  
   178  		processTracker: processTracker,
   179  
   180  		filter: filter,
   181  
   182  		env:           env,
   183  		processIDPool: &ProcessIDPool{},
   184  	}
   185  }
   186  
   187  func (c *LinuxContainer) ID() string {
   188  	return c.id
   189  }
   190  
   191  func (c *LinuxContainer) Handle() string {
   192  	return c.handle
   193  }
   194  
   195  func (c *LinuxContainer) GraceTime() time.Duration {
   196  	return c.graceTime
   197  }
   198  
   199  func (c *LinuxContainer) State() State {
   200  	c.stateMutex.RLock()
   201  	defer c.stateMutex.RUnlock()
   202  
   203  	return c.state
   204  }
   205  
   206  func (c *LinuxContainer) Events() []string {
   207  	c.eventsMutex.RLock()
   208  	defer c.eventsMutex.RUnlock()
   209  
   210  	events := make([]string, len(c.events))
   211  
   212  	copy(events, c.events)
   213  
   214  	return events
   215  }
   216  
   217  func (c *LinuxContainer) Resources() *linux_backend.Resources {
   218  	return c.resources
   219  }
   220  
   221  func (c *LinuxContainer) Snapshot(out io.Writer) error {
   222  	cLog := c.logger.Session("snapshot")
   223  
   224  	cLog.Debug("saving")
   225  
   226  	c.bandwidthMutex.RLock()
   227  	defer c.bandwidthMutex.RUnlock()
   228  
   229  	c.cpuMutex.RLock()
   230  	defer c.cpuMutex.RUnlock()
   231  
   232  	c.diskMutex.RLock()
   233  	defer c.diskMutex.RUnlock()
   234  
   235  	c.memoryMutex.RLock()
   236  	defer c.memoryMutex.RUnlock()
   237  
   238  	c.netInsMutex.RLock()
   239  	defer c.netInsMutex.RUnlock()
   240  
   241  	c.netOutsMutex.RLock()
   242  	defer c.netOutsMutex.RUnlock()
   243  
   244  	processSnapshots := []ProcessSnapshot{}
   245  
   246  	for _, p := range c.processTracker.ActiveProcesses() {
   247  		processSnapshots = append(
   248  			processSnapshots,
   249  			ProcessSnapshot{
   250  				ID: p.ID(),
   251  			},
   252  		)
   253  	}
   254  
   255  	properties, _ := c.Properties()
   256  
   257  	snapshot := ContainerSnapshot{
   258  		ID:     c.id,
   259  		Handle: c.handle,
   260  
   261  		GraceTime: c.graceTime,
   262  
   263  		State:  string(c.State()),
   264  		Events: c.Events(),
   265  
   266  		Limits: LimitsSnapshot{
   267  			Bandwidth: c.currentBandwidthLimits,
   268  			CPU:       c.currentCPULimits,
   269  			Disk:      c.currentDiskLimits,
   270  			Memory:    c.currentMemoryLimits,
   271  		},
   272  
   273  		Resources: ResourcesSnapshot{
   274  			UserUID: c.resources.UserUID,
   275  			RootUID: c.resources.RootUID,
   276  			Network: c.resources.Network,
   277  			Bridge:  c.resources.Bridge,
   278  			Ports:   c.resources.Ports,
   279  		},
   280  
   281  		NetIns:  c.netIns,
   282  		NetOuts: c.netOuts,
   283  
   284  		Processes: processSnapshots,
   285  
   286  		Properties: properties,
   287  
   288  		EnvVars: c.env.Array(),
   289  	}
   290  
   291  	var err error
   292  
   293  	err = json.NewEncoder(out).Encode(snapshot)
   294  	if err != nil {
   295  		cLog.Error("failed-to-save", err, lager.Data{
   296  			"snapshot": snapshot,
   297  		})
   298  		return err
   299  	}
   300  
   301  	cLog.Info("saved", lager.Data{
   302  		"snapshot": snapshot,
   303  	})
   304  
   305  	return nil
   306  }
   307  
   308  func (c *LinuxContainer) Restore(snapshot ContainerSnapshot) error {
   309  	cLog := c.logger.Session("restore")
   310  
   311  	cLog.Debug("restoring")
   312  
   313  	cRunner := logging.Runner{
   314  		CommandRunner: c.runner,
   315  		Logger:        cLog,
   316  	}
   317  
   318  	c.setState(State(snapshot.State))
   319  
   320  	snapshotEnv, err := process.NewEnv(snapshot.EnvVars)
   321  	if err != nil {
   322  		cLog.Error("restoring-env", err, lager.Data{
   323  			"env": snapshot.EnvVars,
   324  		})
   325  		return err
   326  	}
   327  	c.env = snapshotEnv
   328  
   329  	for _, ev := range snapshot.Events {
   330  		c.registerEvent(ev)
   331  	}
   332  
   333  	if snapshot.Limits.Memory != nil {
   334  		err := c.LimitMemory(*snapshot.Limits.Memory)
   335  		if err != nil {
   336  			cLog.Error("failed-to-limit-memory", err)
   337  			return err
   338  		}
   339  	}
   340  
   341  	for _, process := range snapshot.Processes {
   342  		cLog.Info("restoring-process", lager.Data{
   343  			"process": process,
   344  		})
   345  
   346  		c.processIDPool.Restore(process.ID)
   347  
   348  		pidfile := path.Join(c.path, "processes", fmt.Sprintf("%d.pid", process.ID))
   349  
   350  		signaller := &linux_backend.NamespacedSignaller{
   351  			Runner:        c.runner,
   352  			ContainerPath: c.path,
   353  			PidFilePath:   pidfile,
   354  		}
   355  
   356  		c.processTracker.Restore(process.ID, signaller)
   357  	}
   358  
   359  	net := exec.Command(path.Join(c.path, "net.sh"), "setup")
   360  
   361  	err = cRunner.Run(net)
   362  	if err != nil {
   363  		cLog.Error("failed-to-reenforce-network-rules", err)
   364  		return err
   365  	}
   366  
   367  	for _, in := range snapshot.NetIns {
   368  		_, _, err = c.NetIn(in.HostPort, in.ContainerPort)
   369  		if err != nil {
   370  			cLog.Error("failed-to-reenforce-port-mapping", err)
   371  			return err
   372  		}
   373  	}
   374  
   375  	for _, out := range snapshot.NetOuts {
   376  		if err := c.NetOut(out); err != nil {
   377  			cLog.Error("failed-to-reenforce-net-out", err)
   378  			return err
   379  		}
   380  	}
   381  
   382  	cLog.Info("restored")
   383  
   384  	return nil
   385  }
   386  
   387  func (c *LinuxContainer) Start() error {
   388  	cLog := c.logger.Session("start")
   389  
   390  	cLog.Debug("starting")
   391  
   392  	start := exec.Command(path.Join(c.path, "start.sh"))
   393  	start.Env = []string{
   394  		"id=" + c.id,
   395  		"PATH=" + os.Getenv("PATH"),
   396  	}
   397  
   398  	cRunner := logging.Runner{
   399  		CommandRunner: c.runner,
   400  		Logger:        cLog,
   401  	}
   402  
   403  	err := cRunner.Run(start)
   404  	if err != nil {
   405  		cLog.Error("failed-to-start", err)
   406  		return fmt.Errorf("container: start: %v", err)
   407  	}
   408  
   409  	c.setState(StateActive)
   410  
   411  	cLog.Info("started")
   412  
   413  	return nil
   414  }
   415  
   416  func (c *LinuxContainer) Cleanup() {
   417  	cLog := c.logger.Session("cleanup")
   418  
   419  	cLog.Debug("stopping-oom-notifier")
   420  	c.stopOomNotifier()
   421  
   422  	cLog.Info("done")
   423  }
   424  
   425  func (c *LinuxContainer) Stop(kill bool) error {
   426  	stop := exec.Command(path.Join(c.path, "stop.sh"))
   427  
   428  	if kill {
   429  		stop.Args = append(stop.Args, "-w", "0")
   430  	}
   431  
   432  	err := c.runner.Run(stop)
   433  	if err != nil {
   434  		return err
   435  	}
   436  
   437  	c.stopOomNotifier()
   438  
   439  	c.setState(StateStopped)
   440  
   441  	return nil
   442  }
   443  
   444  func (c *LinuxContainer) Properties() (garden.Properties, error) {
   445  	c.propertiesMutex.RLock()
   446  	defer c.propertiesMutex.RUnlock()
   447  
   448  	return c.properties, nil
   449  }
   450  
   451  func (c *LinuxContainer) Property(key string) (string, error) {
   452  	c.propertiesMutex.RLock()
   453  	defer c.propertiesMutex.RUnlock()
   454  
   455  	value, found := c.properties[key]
   456  	if !found {
   457  		return "", UndefinedPropertyError{key}
   458  	}
   459  
   460  	return value, nil
   461  }
   462  
   463  func (c *LinuxContainer) SetProperty(key string, value string) error {
   464  	c.propertiesMutex.Lock()
   465  	defer c.propertiesMutex.Unlock()
   466  
   467  	props := garden.Properties{}
   468  	for k, v := range c.properties {
   469  		props[k] = v
   470  	}
   471  
   472  	props[key] = value
   473  
   474  	c.properties = props
   475  
   476  	return nil
   477  }
   478  
   479  func (c *LinuxContainer) RemoveProperty(key string) error {
   480  	c.propertiesMutex.Lock()
   481  	defer c.propertiesMutex.Unlock()
   482  
   483  	if _, found := c.properties[key]; !found {
   484  		return UndefinedPropertyError{key}
   485  	}
   486  
   487  	delete(c.properties, key)
   488  
   489  	return nil
   490  }
   491  
   492  func (c *LinuxContainer) HasProperties(properties garden.Properties) bool {
   493  	c.propertiesMutex.RLock()
   494  	defer c.propertiesMutex.RUnlock()
   495  
   496  	for k, v := range properties {
   497  		if value, ok := c.properties[k]; !ok || (ok && value != v) {
   498  			return false
   499  		}
   500  	}
   501  
   502  	return true
   503  }
   504  
   505  func (c *LinuxContainer) Info() (garden.ContainerInfo, error) {
   506  	mappedPorts := []garden.PortMapping{}
   507  
   508  	c.netInsMutex.RLock()
   509  
   510  	for _, spec := range c.netIns {
   511  		mappedPorts = append(mappedPorts, garden.PortMapping{
   512  			HostPort:      spec.HostPort,
   513  			ContainerPort: spec.ContainerPort,
   514  		})
   515  	}
   516  
   517  	c.netInsMutex.RUnlock()
   518  
   519  	processIDs := []uint32{}
   520  	for _, process := range c.processTracker.ActiveProcesses() {
   521  		processIDs = append(processIDs, process.ID())
   522  	}
   523  
   524  	properties, _ := c.Properties()
   525  
   526  	info := garden.ContainerInfo{
   527  		State:         string(c.State()),
   528  		Events:        c.Events(),
   529  		Properties:    properties,
   530  		ContainerPath: c.path,
   531  		ProcessIDs:    processIDs,
   532  		MappedPorts:   mappedPorts,
   533  	}
   534  
   535  	info.ContainerIP = c.resources.Network.IP.String()
   536  	info.HostIP = subnets.GatewayIP(c.resources.Network.Subnet).String()
   537  	info.ExternalIP = c.Resources().ExternalIP.String()
   538  
   539  	return info, nil
   540  }
   541  
   542  func (c *LinuxContainer) StreamIn(dstPath string, tarStream io.Reader) error {
   543  	nsTarPath := path.Join(c.path, "bin", "nstar")
   544  	pidPath := path.Join(c.path, "run", "wshd.pid")
   545  
   546  	pidFile, err := os.Open(pidPath)
   547  	if err != nil {
   548  		return err
   549  	}
   550  
   551  	var pid int
   552  	_, err = fmt.Fscanf(pidFile, "%d", &pid)
   553  	if err != nil {
   554  		return err
   555  	}
   556  
   557  	tar := exec.Command(
   558  		nsTarPath,
   559  		strconv.Itoa(pid),
   560  		"vcap",
   561  		dstPath,
   562  	)
   563  
   564  	tar.Stdin = tarStream
   565  
   566  	cLog := c.logger.Session("stream-in")
   567  
   568  	cRunner := logging.Runner{
   569  		CommandRunner: c.runner,
   570  		Logger:        cLog,
   571  	}
   572  
   573  	return cRunner.Run(tar)
   574  }
   575  
   576  func (c *LinuxContainer) StreamOut(srcPath string) (io.ReadCloser, error) {
   577  	workingDir := filepath.Dir(srcPath)
   578  	compressArg := filepath.Base(srcPath)
   579  	if strings.HasSuffix(srcPath, "/") {
   580  		workingDir = srcPath
   581  		compressArg = "."
   582  	}
   583  
   584  	nsTarPath := path.Join(c.path, "bin", "nstar")
   585  	pidPath := path.Join(c.path, "run", "wshd.pid")
   586  
   587  	pidFile, err := os.Open(pidPath)
   588  	if err != nil {
   589  		return nil, err
   590  	}
   591  
   592  	var pid int
   593  	_, err = fmt.Fscanf(pidFile, "%d", &pid)
   594  	if err != nil {
   595  		return nil, err
   596  	}
   597  
   598  	tar := exec.Command(
   599  		nsTarPath,
   600  		strconv.Itoa(pid),
   601  		"vcap",
   602  		workingDir,
   603  		compressArg,
   604  	)
   605  
   606  	tarRead, tarWrite, err := os.Pipe()
   607  	if err != nil {
   608  		return nil, err
   609  	}
   610  
   611  	tar.Stdout = tarWrite
   612  
   613  	err = c.runner.Background(tar)
   614  	if err != nil {
   615  		return nil, err
   616  	}
   617  
   618  	// close our end of the tar pipe
   619  	tarWrite.Close()
   620  
   621  	go c.runner.Wait(tar)
   622  
   623  	return tarRead, nil
   624  }
   625  
   626  func (c *LinuxContainer) NetIn(hostPort uint32, containerPort uint32) (uint32, uint32, error) {
   627  	if hostPort == 0 {
   628  		randomPort, err := c.portPool.Acquire()
   629  		if err != nil {
   630  			return 0, 0, err
   631  		}
   632  
   633  		c.resources.AddPort(randomPort)
   634  
   635  		hostPort = randomPort
   636  	}
   637  
   638  	if containerPort == 0 {
   639  		containerPort = hostPort
   640  	}
   641  
   642  	net := exec.Command(path.Join(c.path, "net.sh"), "in")
   643  	net.Env = []string{
   644  		fmt.Sprintf("HOST_PORT=%d", hostPort),
   645  		fmt.Sprintf("CONTAINER_PORT=%d", containerPort),
   646  		"PATH=" + os.Getenv("PATH"),
   647  	}
   648  
   649  	err := c.runner.Run(net)
   650  	if err != nil {
   651  		return 0, 0, err
   652  	}
   653  
   654  	c.netInsMutex.Lock()
   655  	defer c.netInsMutex.Unlock()
   656  
   657  	c.netIns = append(c.netIns, NetInSpec{hostPort, containerPort})
   658  
   659  	return hostPort, containerPort, nil
   660  }
   661  
   662  func (c *LinuxContainer) NetOut(r garden.NetOutRule) error {
   663  	err := c.filter.NetOut(r)
   664  	if err != nil {
   665  		return err
   666  	}
   667  
   668  	c.netOutsMutex.Lock()
   669  	defer c.netOutsMutex.Unlock()
   670  
   671  	c.netOuts = append(c.netOuts, r)
   672  
   673  	return nil
   674  }
   675  
   676  func (c *LinuxContainer) CurrentEnvVars() process.Env {
   677  	return c.env
   678  }
   679  
   680  func (c *LinuxContainer) setState(state State) {
   681  	c.stateMutex.Lock()
   682  	defer c.stateMutex.Unlock()
   683  
   684  	c.state = state
   685  }
   686  
   687  func (c *LinuxContainer) registerEvent(event string) {
   688  	c.eventsMutex.Lock()
   689  	defer c.eventsMutex.Unlock()
   690  
   691  	c.events = append(c.events, event)
   692  }