github.com/geofffranks/garden-linux@v0.0.0-20160715111146-26c893169cfa/resource_pool/resource_pool.go (about)

     1  package resource_pool
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net"
    10  	"net/url"
    11  	"os"
    12  	"os/exec"
    13  	"path"
    14  	"path/filepath"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/blang/semver"
    20  	"code.cloudfoundry.org/garden"
    21  	"github.com/cloudfoundry/gunk/command_runner"
    22  	"code.cloudfoundry.org/lager"
    23  
    24  	"code.cloudfoundry.org/garden-linux/linux_backend"
    25  	"code.cloudfoundry.org/garden-linux/linux_container"
    26  	"code.cloudfoundry.org/garden-linux/logging"
    27  	"code.cloudfoundry.org/garden-linux/network"
    28  	"code.cloudfoundry.org/garden-linux/network/bridgemgr"
    29  	"code.cloudfoundry.org/garden-linux/network/iptables"
    30  	"code.cloudfoundry.org/garden-linux/network/subnets"
    31  	"code.cloudfoundry.org/garden-linux/process"
    32  	"code.cloudfoundry.org/garden-linux/sysconfig"
    33  	"code.cloudfoundry.org/garden-shed/layercake"
    34  	"code.cloudfoundry.org/garden-shed/rootfs_provider"
    35  )
    36  
    37  var (
    38  	ErrUnknownRootFSProvider = errors.New("unknown rootfs provider")
    39  )
    40  
    41  //go:generate counterfeiter -o fake_filter_provider/FakeFilterProvider.go . FilterProvider
    42  type FilterProvider interface {
    43  	ProvideFilter(containerId string) network.Filter
    44  }
    45  
    46  //go:generate counterfeiter -o fake_subnet_pool/FakeSubnetPool.go . SubnetPool
    47  type SubnetPool interface {
    48  	Acquire(subnet subnets.SubnetSelector, ip subnets.IPSelector, logger lager.Logger) (*linux_backend.Network, error)
    49  	Release(network *linux_backend.Network, logger lager.Logger) error
    50  	Remove(network *linux_backend.Network, logger lager.Logger) error
    51  	Capacity() int
    52  }
    53  
    54  //go:generate counterfeiter -o fake_rootfs_provider/FakeRootFSProvider.go . RootFSProvider
    55  type RootFSProvider interface {
    56  	Create(log lager.Logger, id string, spec rootfs_provider.Spec) (mountpoint string, envvar []string, err error)
    57  	Destroy(log lager.Logger, id string) error
    58  	GC(log lager.Logger) error
    59  }
    60  
    61  //go:generate counterfeiter -o fake_rootfs_cleaner/FakeRootFSCleaner.go . RootFSCleaner
    62  type RootFSCleaner interface {
    63  	Clean(log lager.Logger, path string) error
    64  }
    65  
    66  type Remover interface {
    67  	Remove(id layercake.ID) error
    68  }
    69  
    70  //go:generate counterfeiter -o fake_mkdir_chowner/FakeMkdirChowner.go . MkdirChowner
    71  type MkdirChowner interface {
    72  	MkdirChown(path string, uid, gid uint32, mode os.FileMode) error
    73  }
    74  
    75  type LinuxResourcePool struct {
    76  	logger lager.Logger
    77  
    78  	binPath   string
    79  	depotPath string
    80  
    81  	sysconfig sysconfig.Config
    82  
    83  	denyNetworks  []string
    84  	allowNetworks []string
    85  
    86  	rootFSProvider RootFSProvider
    87  	rootFSCleaner  RootFSCleaner
    88  	mappingList    rootfs_provider.MappingList
    89  
    90  	subnetPool SubnetPool
    91  
    92  	externalIP net.IP
    93  	mtu        int
    94  
    95  	portPool linux_container.PortPool
    96  
    97  	bridges     bridgemgr.BridgeManager
    98  	iptablesMgr linux_container.IPTablesManager
    99  
   100  	filterProvider FilterProvider
   101  	defaultChain   iptables.Chain
   102  
   103  	runner command_runner.CommandRunner
   104  
   105  	quotaManager linux_container.QuotaManager
   106  
   107  	containerIDs chan string
   108  
   109  	currentContainerVersion semver.Version
   110  
   111  	mkdirChowner MkdirChowner
   112  }
   113  
   114  func New(
   115  	logger lager.Logger,
   116  	binPath, depotPath string,
   117  	sysconfig sysconfig.Config,
   118  	rootFSProvider RootFSProvider,
   119  	rootFSCleaner RootFSCleaner,
   120  	mappingList rootfs_provider.MappingList,
   121  	externalIP net.IP,
   122  	mtu int,
   123  	subnetPool SubnetPool,
   124  	bridges bridgemgr.BridgeManager,
   125  	iptablesMgr linux_container.IPTablesManager,
   126  	filterProvider FilterProvider,
   127  	defaultChain iptables.Chain,
   128  	portPool linux_container.PortPool,
   129  	denyNetworks, allowNetworks []string,
   130  	runner command_runner.CommandRunner,
   131  	quotaManager linux_container.QuotaManager,
   132  	currentContainerVersion semver.Version,
   133  	mkdirChowner MkdirChowner,
   134  ) *LinuxResourcePool {
   135  	pool := &LinuxResourcePool{
   136  		logger: logger.Session("pool"),
   137  
   138  		binPath:   binPath,
   139  		depotPath: depotPath,
   140  
   141  		sysconfig: sysconfig,
   142  
   143  		rootFSProvider: rootFSProvider,
   144  		rootFSCleaner:  rootFSCleaner,
   145  		mappingList:    mappingList,
   146  
   147  		allowNetworks: allowNetworks,
   148  		denyNetworks:  denyNetworks,
   149  
   150  		externalIP: externalIP,
   151  		mtu:        mtu,
   152  
   153  		subnetPool: subnetPool,
   154  
   155  		bridges:     bridges,
   156  		iptablesMgr: iptablesMgr,
   157  
   158  		filterProvider: filterProvider,
   159  		defaultChain:   defaultChain,
   160  
   161  		portPool: portPool,
   162  
   163  		runner: runner,
   164  
   165  		quotaManager: quotaManager,
   166  
   167  		containerIDs:            make(chan string),
   168  		currentContainerVersion: currentContainerVersion,
   169  
   170  		mkdirChowner: mkdirChowner,
   171  	}
   172  
   173  	go pool.generateContainerIDs()
   174  
   175  	return pool
   176  }
   177  
   178  func (p *LinuxResourcePool) MaxContainers() int {
   179  	return p.subnetPool.Capacity()
   180  }
   181  
   182  func (p *LinuxResourcePool) Setup() error {
   183  	setup := exec.Command(path.Join(p.binPath, "setup.sh"))
   184  	setup.Env = []string{
   185  		"CONTAINER_DEPOT_PATH=" + p.depotPath,
   186  		"PATH=" + os.Getenv("PATH"),
   187  	}
   188  
   189  	err := p.runner.Run(setup)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	if err := p.quotaManager.Setup(); err != nil {
   195  		return fmt.Errorf("resource_pool: enable disk quotas: %s", err)
   196  	}
   197  
   198  	return p.setupIPTables()
   199  }
   200  
   201  func (p *LinuxResourcePool) setupIPTables() error {
   202  	for _, n := range p.allowNetworks {
   203  		if n == "" {
   204  			continue
   205  		}
   206  
   207  		if err := p.defaultChain.AppendRule("", n, iptables.Return); err != nil {
   208  			return fmt.Errorf("resource_pool: setting up allow rules in iptables: %v", err)
   209  		}
   210  	}
   211  
   212  	for _, n := range p.denyNetworks {
   213  		if n == "" {
   214  			continue
   215  		}
   216  
   217  		if err := p.defaultChain.AppendRule("", n, iptables.Reject); err != nil {
   218  			return fmt.Errorf("resource_pool: setting up deny rules in iptables: %v", err)
   219  		}
   220  	}
   221  
   222  	return nil
   223  }
   224  
   225  func (p *LinuxResourcePool) Prune(keep map[string]bool) error {
   226  	entries, err := ioutil.ReadDir(p.depotPath)
   227  	if err != nil {
   228  		p.logger.Error("prune-container-pool-path-error", err, lager.Data{"depotPath": p.depotPath})
   229  		return fmt.Errorf("Cannot read path %q: %s", p.depotPath, err)
   230  	}
   231  
   232  	for _, entry := range entries {
   233  		id := entry.Name()
   234  		if id == "tmp" { // ignore temporary directory in depotPath
   235  			continue
   236  		}
   237  
   238  		_, found := keep[id]
   239  		if found {
   240  			continue
   241  		}
   242  
   243  		p.pruneEntry(id)
   244  	}
   245  
   246  	if err := p.bridges.Prune(); err != nil {
   247  		p.logger.Error("prune-bridges", err)
   248  	}
   249  
   250  	return nil
   251  }
   252  
   253  // pruneEntry does not report errors, only log them
   254  func (p *LinuxResourcePool) pruneEntry(id string) {
   255  	pLog := p.logger.Session("prune", lager.Data{"id": id})
   256  
   257  	pLog.Info("prune")
   258  
   259  	err := p.releaseSystemResources(pLog, id)
   260  	if err != nil {
   261  		pLog.Error("release-system-resources-error", err)
   262  	}
   263  
   264  	pLog.Info("end of prune")
   265  }
   266  
   267  func (p *LinuxResourcePool) Acquire(spec garden.ContainerSpec) (linux_backend.LinuxContainerSpec, error) {
   268  	id := <-p.containerIDs
   269  	containerPath := path.Join(p.depotPath, id)
   270  	handle := getHandle(spec.Handle, id)
   271  	pLog := p.logger.Session("acquire", lager.Data{"handle": handle, "id": id})
   272  
   273  	iptablesCh := make(chan error, 1)
   274  
   275  	go func(iptablesCh chan error) {
   276  		pLog.Debug("setup-iptables-starting")
   277  		if err := p.filterProvider.ProvideFilter(id).Setup(handle); err != nil {
   278  			pLog.Error("setup-iptables-failed", err)
   279  			iptablesCh <- fmt.Errorf("resource_pool: set up filter: %v", err)
   280  		} else {
   281  			pLog.Debug("setup-iptables-ended")
   282  			iptablesCh <- nil
   283  		}
   284  	}(iptablesCh)
   285  
   286  	pLog.Info("creating")
   287  
   288  	resources, err := p.acquirePoolResources(spec, id, pLog)
   289  	if err != nil {
   290  		return linux_backend.LinuxContainerSpec{}, err
   291  	}
   292  	defer cleanup(&err, func() {
   293  		p.releasePoolResources(resources, pLog)
   294  	})
   295  
   296  	pLog.Info("acquired-pool-resources")
   297  
   298  	pLog.Info("running-graph-cleanup")
   299  	if err := p.rootFSProvider.GC(pLog); err != nil {
   300  		pLog.Error("graph-cleanup-failed", err)
   301  	}
   302  
   303  	containerRootFSPath, rootFSEnv, err := p.acquireSystemResources(
   304  		spec, id, resources, pLog,
   305  	)
   306  	if err != nil {
   307  		return linux_backend.LinuxContainerSpec{}, err
   308  	}
   309  
   310  	err = <-iptablesCh
   311  	if err != nil {
   312  		p.tryReleaseSystemResources(p.logger, id)
   313  		return linux_backend.LinuxContainerSpec{}, err
   314  	}
   315  
   316  	pLog.Info("created")
   317  
   318  	specEnv, err := process.NewEnv(spec.Env)
   319  	if err != nil {
   320  		p.tryReleaseSystemResources(p.logger, id)
   321  		return linux_backend.LinuxContainerSpec{}, err
   322  	}
   323  
   324  	spec.Env = rootFSEnv.Merge(specEnv).Array()
   325  	spec.Handle = handle
   326  
   327  	return linux_backend.LinuxContainerSpec{
   328  		ID:                  id,
   329  		ContainerPath:       containerPath,
   330  		ContainerRootFSPath: containerRootFSPath,
   331  		Resources:           resources,
   332  		Events:              []string{},
   333  		Version:             p.currentContainerVersion,
   334  		State:               linux_backend.StateBorn,
   335  
   336  		ContainerSpec: spec,
   337  	}, nil
   338  }
   339  
   340  func (p *LinuxResourcePool) Restore(snapshot io.Reader) (linux_backend.LinuxContainerSpec, error) {
   341  	var containerSnapshot linux_container.ContainerSnapshot
   342  
   343  	err := json.NewDecoder(snapshot).Decode(&containerSnapshot)
   344  	if err != nil {
   345  		return linux_backend.LinuxContainerSpec{}, err
   346  	}
   347  
   348  	id := containerSnapshot.ID
   349  	rLog := p.logger.Session("restore", lager.Data{
   350  		"handle": containerSnapshot.Handle,
   351  		"id":     id,
   352  	})
   353  
   354  	rLog.Debug("restoring")
   355  
   356  	resources := containerSnapshot.Resources
   357  	subnetLogger := rLog.Session("subnet-pool")
   358  
   359  	if err = p.subnetPool.Remove(resources.Network, subnetLogger); err != nil {
   360  		return linux_backend.LinuxContainerSpec{}, err
   361  	}
   362  
   363  	if err = p.bridges.Rereserve(resources.Bridge, resources.Network.Subnet, id); err != nil {
   364  		p.subnetPool.Release(resources.Network, subnetLogger)
   365  		return linux_backend.LinuxContainerSpec{}, err
   366  	}
   367  
   368  	for _, port := range resources.Ports {
   369  		err = p.portPool.Remove(port)
   370  		if err != nil {
   371  			p.subnetPool.Release(resources.Network, subnetLogger)
   372  
   373  			for _, port := range resources.Ports {
   374  				p.portPool.Release(port)
   375  			}
   376  
   377  			return linux_backend.LinuxContainerSpec{}, err
   378  		}
   379  	}
   380  
   381  	version, err := p.restoreContainerVersion(id)
   382  	if err != nil {
   383  		return linux_backend.LinuxContainerSpec{}, err
   384  	}
   385  
   386  	spec := linux_backend.LinuxContainerSpec{
   387  		ID:                  id,
   388  		ContainerPath:       path.Join(p.depotPath, id),
   389  		ContainerRootFSPath: containerSnapshot.RootFSPath,
   390  
   391  		State:  linux_backend.State(containerSnapshot.State),
   392  		Events: containerSnapshot.Events,
   393  		ContainerSpec: garden.ContainerSpec{
   394  			Handle:     containerSnapshot.Handle,
   395  			GraceTime:  containerSnapshot.GraceTime,
   396  			Properties: containerSnapshot.Properties,
   397  		},
   398  
   399  		Resources: linux_backend.NewResources(
   400  			resources.RootUID,
   401  			resources.Network,
   402  			resources.Bridge,
   403  			resources.Ports,
   404  			p.externalIP,
   405  		),
   406  
   407  		Limits:    containerSnapshot.Limits,
   408  		NetIns:    containerSnapshot.NetIns,
   409  		NetOuts:   containerSnapshot.NetOuts,
   410  		Processes: containerSnapshot.Processes,
   411  		Version:   version,
   412  	}
   413  
   414  	return spec, nil
   415  }
   416  
   417  func (p *LinuxResourcePool) Release(container linux_backend.LinuxContainerSpec) error {
   418  	pLog := p.logger.Session("release", lager.Data{
   419  		"handle": container.Handle,
   420  		"id":     container.ID,
   421  	})
   422  
   423  	pLog.Info("releasing")
   424  
   425  	err := p.releaseSystemResources(pLog, container.ID)
   426  	if err != nil {
   427  		pLog.Error("release-system-resources", err)
   428  		return err
   429  	}
   430  
   431  	p.releasePoolResources(container.Resources, pLog)
   432  
   433  	pLog.Info("released")
   434  
   435  	return nil
   436  }
   437  
   438  func (p *LinuxResourcePool) generateContainerIDs() {
   439  	for containerNum := time.Now().UnixNano(); ; containerNum++ {
   440  		containerID := []byte{}
   441  
   442  		var i uint
   443  		for i = 0; i < 11; i++ {
   444  			containerID = strconv.AppendInt(
   445  				containerID,
   446  				(containerNum>>(55-(i+1)*5))&31,
   447  				32,
   448  			)
   449  		}
   450  
   451  		p.containerIDs <- string(containerID)
   452  	}
   453  }
   454  
   455  func (p *LinuxResourcePool) writeBindMounts(containerPath string,
   456  	rootFSPath string,
   457  	bindMounts []garden.BindMount,
   458  	mkdirUID int) error {
   459  	hook := path.Join(containerPath, "lib", "hook-parent-before-clone.sh")
   460  
   461  	for _, bm := range bindMounts {
   462  		dstMount := path.Join(rootFSPath, bm.DstPath)
   463  		srcPath := bm.SrcPath
   464  
   465  		if bm.Origin == garden.BindMountOriginContainer {
   466  			srcPath = path.Join(rootFSPath, srcPath)
   467  		}
   468  
   469  		mode := "ro"
   470  		if bm.Mode == garden.BindMountModeRW {
   471  			mode = "rw"
   472  		}
   473  
   474  		linebreak := exec.Command("bash", "-c", "echo >> "+hook)
   475  		if err := p.runner.Run(linebreak); err != nil {
   476  			return err
   477  		}
   478  
   479  		if err := p.mkdirChowner.MkdirChown(dstMount, uint32(mkdirUID), uint32(mkdirUID), 0755); err != nil {
   480  			return err
   481  		}
   482  
   483  		mount := exec.Command("bash", "-c", "echo mount -n --bind "+srcPath+" "+dstMount+" >> "+hook)
   484  		if err := p.runner.Run(mount); err != nil {
   485  			return err
   486  		}
   487  
   488  		remount := exec.Command("bash", "-c", "echo mount -n --bind -o remount,"+mode+" "+srcPath+" "+dstMount+" >> "+hook)
   489  		if err := p.runner.Run(remount); err != nil {
   490  			return err
   491  		}
   492  	}
   493  
   494  	return nil
   495  }
   496  
   497  func (p *LinuxResourcePool) saveBridgeName(id string, bridgeName string) error {
   498  	bridgeNameFile := path.Join(p.depotPath, id, "bridge-name")
   499  	return ioutil.WriteFile(bridgeNameFile, []byte(bridgeName), 0644)
   500  }
   501  
   502  func (p *LinuxResourcePool) saveRootFSProvider(id string, provider string) error {
   503  	providerFile := path.Join(p.depotPath, id, "rootfs-provider")
   504  	return ioutil.WriteFile(providerFile, []byte(provider), 0644)
   505  }
   506  
   507  func (p *LinuxResourcePool) saveContainerVersion(id string) error {
   508  	versionFile := path.Join(p.depotPath, id, "version")
   509  	return ioutil.WriteFile(versionFile, []byte(p.currentContainerVersion.String()), 0644)
   510  }
   511  
   512  func (p *LinuxResourcePool) restoreContainerVersion(id string) (semver.Version, error) {
   513  	content, err := ioutil.ReadFile(filepath.Join(p.depotPath, id, "version"))
   514  	if err != nil {
   515  		if os.IsNotExist(err) {
   516  			return linux_container.MissingVersion, nil
   517  		}
   518  		return semver.Version{}, err
   519  	}
   520  
   521  	return semver.Make(string(content))
   522  }
   523  
   524  func (p *LinuxResourcePool) acquirePoolResources(spec garden.ContainerSpec, id string, logger lager.Logger) (*linux_backend.Resources, error) {
   525  	resources := linux_backend.NewResources(0, nil, "", nil, p.externalIP)
   526  
   527  	subnet, ip, err := parseNetworkSpec(spec.Network)
   528  	if err != nil {
   529  		return nil, fmt.Errorf("create container: invalid network spec: %v", err)
   530  	}
   531  
   532  	if err := p.acquireUID(resources, spec.Privileged); err != nil {
   533  		return nil, err
   534  	}
   535  
   536  	if resources.Network, err = p.subnetPool.Acquire(subnet, ip, logger.Session("subnet-pool")); err != nil {
   537  		p.releasePoolResources(resources, logger)
   538  		return nil, err
   539  	}
   540  
   541  	return resources, nil
   542  }
   543  
   544  func (p *LinuxResourcePool) acquireUID(resources *linux_backend.Resources, privileged bool) error {
   545  	if !privileged {
   546  		resources.RootUID = p.mappingList.Map(0)
   547  		return nil
   548  	}
   549  
   550  	resources.RootUID = 0
   551  	return nil
   552  }
   553  
   554  func (p *LinuxResourcePool) releasePoolResources(resources *linux_backend.Resources, logger lager.Logger) {
   555  	for _, port := range resources.Ports {
   556  		p.portPool.Release(port)
   557  	}
   558  
   559  	if resources.Network != nil {
   560  		p.subnetPool.Release(resources.Network, logger.Session("subnet-pool"))
   561  	}
   562  }
   563  
   564  func (p *LinuxResourcePool) acquireSystemResources(spec garden.ContainerSpec, id string, resources *linux_backend.Resources, pLog lager.Logger) (string, process.Env, error) {
   565  	containerPath := path.Join(p.depotPath, id)
   566  	if err := os.MkdirAll(containerPath, 0755); err != nil {
   567  		return "", nil, fmt.Errorf("resource_pool: creating container directory: %v", err)
   568  	}
   569  
   570  	rootFSPath, rootFSEnvVars, err := p.setupContainerDirectories(spec, id, resources, pLog)
   571  	if err != nil {
   572  		os.RemoveAll(containerPath)
   573  		return "", nil, err
   574  	}
   575  
   576  	createCmd := path.Join(p.binPath, "create.sh")
   577  	create := exec.Command(createCmd, containerPath)
   578  	suff, _ := resources.Network.Subnet.Mask.Size()
   579  	env := process.Env{
   580  		"id":                   id,
   581  		"rootfs_path":          rootFSPath,
   582  		"network_host_ip":      subnets.GatewayIP(resources.Network.Subnet).String(),
   583  		"network_container_ip": resources.Network.IP.String(),
   584  		"network_cidr_suffix":  strconv.Itoa(suff),
   585  		"network_cidr":         resources.Network.Subnet.String(),
   586  		"external_ip":          p.externalIP.String(),
   587  		"container_iface_mtu":  fmt.Sprintf("%d", p.mtu),
   588  		"bridge_iface":         resources.Bridge,
   589  		"root_uid":             strconv.FormatUint(uint64(resources.RootUID), 10),
   590  		"PATH":                 os.Getenv("PATH"),
   591  	}
   592  	create.Env = env.Array()
   593  
   594  	pRunner := logging.Runner{
   595  		CommandRunner: p.runner,
   596  		Logger:        pLog.Session("create-script"),
   597  	}
   598  
   599  	err = pRunner.Run(create)
   600  	defer cleanup(&err, func() {
   601  		p.tryReleaseSystemResources(pLog, id)
   602  	})
   603  
   604  	if err != nil {
   605  		pLog.Error("create-command-failed", err, lager.Data{
   606  			"CreateCmd": createCmd,
   607  			"Env":       create.Env,
   608  		})
   609  		return "", nil, err
   610  	}
   611  
   612  	err = p.saveRootFSProvider(id, "docker-composite")
   613  	if err != nil {
   614  		pLog.Error("save-rootfs-provider-failed", err, lager.Data{
   615  			"Id":     id,
   616  			"rootfs": spec.RootFSPath,
   617  		})
   618  		return "", nil, err
   619  	}
   620  
   621  	err = p.saveContainerVersion(id)
   622  	if err != nil {
   623  		pLog.Error("save-container-version-failed", err, lager.Data{
   624  			"Id":            id,
   625  			"ContainerPath": containerPath,
   626  		})
   627  		return "", nil, err
   628  	}
   629  
   630  	err = p.writeBindMounts(containerPath, rootFSPath, spec.BindMounts, resources.RootUID)
   631  	if err != nil {
   632  		pLog.Error("bind-mounts-failed", err)
   633  		return "", nil, err
   634  	}
   635  
   636  	return rootFSPath, rootFSEnvVars, nil
   637  }
   638  
   639  func (p *LinuxResourcePool) setupRootfs(spec garden.ContainerSpec, id string, resources *linux_backend.Resources, pLog lager.Logger) (string, process.Env, error) {
   640  	rootFSURL, err := url.Parse(spec.RootFSPath)
   641  	if err != nil {
   642  		pLog.Error("parse-rootfs-path-failed", err, lager.Data{
   643  			"RootFSPath": spec.RootFSPath,
   644  		})
   645  
   646  		return "", nil, err
   647  	}
   648  
   649  	rootFSSpec := rootfs_provider.Spec{
   650  		RootFS:     rootFSURL,
   651  		Namespaced: resources.RootUID != 0,
   652  		QuotaSize:  int64(spec.Limits.Disk.ByteHard),
   653  		QuotaScope: spec.Limits.Disk.Scope,
   654  	}
   655  
   656  	pLog.Debug("provide-rootfs-starting")
   657  	rootFSPath, rootFSEnvVars, err := p.rootFSProvider.Create(pLog, id, rootFSSpec)
   658  	if err != nil {
   659  		pLog.Error("provide-rootfs-failed", err)
   660  
   661  		return "", nil, err
   662  	}
   663  	pLog.Debug("provide-rootfs-ended")
   664  
   665  	pLog.Debug("clean-rootfs-starting")
   666  	if err := p.rootFSCleaner.Clean(pLog, rootFSPath); err != nil {
   667  		return "", nil, err
   668  	}
   669  	pLog.Debug("clean-rootfs-ended")
   670  
   671  	rootFSProcessEnv, err := process.NewEnv(rootFSEnvVars)
   672  	if err != nil {
   673  		pLog.Error("rootfs-env-malformed", err)
   674  
   675  		return "", nil, err
   676  	}
   677  
   678  	return rootFSPath, rootFSProcessEnv, nil
   679  }
   680  
   681  func (p *LinuxResourcePool) setupContainerDirectories(spec garden.ContainerSpec, id string, resources *linux_backend.Resources, pLog lager.Logger) (string, process.Env, error) {
   682  	rootFSPath, rootFSEnvVars, err := p.setupRootfs(spec, id, resources, pLog)
   683  	if err != nil {
   684  		return "", nil, err
   685  	}
   686  
   687  	pLog.Debug("setup-bridge-starting")
   688  	if err := p.setupBridge(pLog, id, resources); err != nil {
   689  		p.rootFSProvider.Destroy(pLog, id)
   690  		return "", nil, err
   691  	}
   692  	pLog.Debug("setup-bridge-ended")
   693  
   694  	return rootFSPath, rootFSEnvVars, nil
   695  }
   696  
   697  func (p *LinuxResourcePool) setupBridge(pLog lager.Logger, id string, resources *linux_backend.Resources) error {
   698  	var err error
   699  	if resources.Bridge, err = p.bridges.Reserve(resources.Network.Subnet, id); err != nil {
   700  		pLog.Error("reserve-bridge-failed", err, lager.Data{
   701  			"Id":     id,
   702  			"Subnet": resources.Network.Subnet,
   703  			"Bridge": resources.Bridge,
   704  		})
   705  
   706  		return err
   707  	}
   708  
   709  	if err = p.saveBridgeName(id, resources.Bridge); err != nil {
   710  		pLog.Error("save-bridge-name-failed", err, lager.Data{
   711  			"Id":     id,
   712  			"Bridge": resources.Bridge,
   713  		})
   714  
   715  		return err
   716  	}
   717  
   718  	return nil
   719  }
   720  
   721  func (p *LinuxResourcePool) tryReleaseSystemResources(logger lager.Logger, id string) {
   722  	err := p.releaseSystemResources(logger, id)
   723  	if err != nil {
   724  		logger.Error("failed-to-undo-failed-create", err)
   725  	}
   726  }
   727  
   728  func (p *LinuxResourcePool) releaseSystemResources(logger lager.Logger, id string) error {
   729  	pRunner := logging.Runner{
   730  		CommandRunner: p.runner,
   731  		Logger:        logger,
   732  	}
   733  
   734  	bridgeName, err := ioutil.ReadFile(path.Join(p.depotPath, id, "bridge-name"))
   735  	if err == nil {
   736  		if err := p.bridges.Release(string(bridgeName), id); err != nil {
   737  			return fmt.Errorf("containerpool: release bridge %s: %v", bridgeName, err)
   738  		}
   739  	}
   740  
   741  	rootFSProvider, err := ioutil.ReadFile(path.Join(p.depotPath, id, "rootfs-provider"))
   742  	if err != nil {
   743  		rootFSProvider = []byte("invalid-rootfs-provider")
   744  	}
   745  
   746  	if err = p.iptablesMgr.ContainerTeardown(id); err != nil {
   747  		return err
   748  	}
   749  
   750  	destroy := exec.Command(path.Join(p.binPath, "destroy.sh"), path.Join(p.depotPath, id))
   751  	err = pRunner.Run(destroy)
   752  	if err != nil {
   753  		return err
   754  	}
   755  
   756  	if shouldCleanRootfs(string(rootFSProvider)) {
   757  		if err = p.rootFSProvider.Destroy(logger, id); err != nil {
   758  			return err
   759  		}
   760  	}
   761  
   762  	p.filterProvider.ProvideFilter(id).TearDown()
   763  	return nil
   764  }
   765  
   766  func shouldCleanRootfs(rootFSProvider string) bool {
   767  	// invalid-rootfs-provider indicates that this is probably a recent container that failed on create.
   768  	// we should try to clean it up
   769  
   770  	providers := []string{
   771  		"docker-local-aufs",
   772  		"docker-local-vfs",
   773  		"docker-remote-aufs",
   774  		"docker-remote-vfs",
   775  		"docker-composite",
   776  		"invalid-rootfs-provider",
   777  	}
   778  
   779  	for _, provider := range providers {
   780  		if provider == rootFSProvider {
   781  			return true
   782  		}
   783  	}
   784  
   785  	return false
   786  }
   787  
   788  func getHandle(handle, id string) string {
   789  	if handle != "" {
   790  		return handle
   791  	}
   792  	return id
   793  }
   794  
   795  func cleanup(err *error, undo func()) {
   796  	if *err != nil {
   797  		undo()
   798  	}
   799  }
   800  
   801  func parseNetworkSpec(spec string) (subnets.SubnetSelector, subnets.IPSelector, error) {
   802  	var ipSelector subnets.IPSelector = subnets.DynamicIPSelector
   803  	var subnetSelector subnets.SubnetSelector = subnets.DynamicSubnetSelector
   804  
   805  	if spec != "" {
   806  		specifiedIP, ipn, err := net.ParseCIDR(suffixIfNeeded(spec))
   807  		if err != nil {
   808  			return nil, nil, err
   809  		}
   810  
   811  		subnetSelector = subnets.StaticSubnetSelector{ipn}
   812  
   813  		if !specifiedIP.Equal(subnets.NetworkIP(ipn)) {
   814  			ipSelector = subnets.StaticIPSelector{specifiedIP}
   815  		}
   816  	}
   817  
   818  	return subnetSelector, ipSelector, nil
   819  }
   820  
   821  func suffixIfNeeded(spec string) string {
   822  	if !strings.Contains(spec, "/") {
   823  		spec = spec + "/30"
   824  	}
   825  
   826  	return spec
   827  }