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

     1  package rootfs_provider
     2  
     3  import (
     4  	"errors"
     5  	"net/url"
     6  	"os"
     7  	"os/exec"
     8  	"time"
     9  
    10  	"github.com/docker/docker/daemon/graphdriver"
    11  	"github.com/pivotal-golang/clock"
    12  	"github.com/pivotal-golang/lager"
    13  
    14  	"github.com/cloudfoundry-incubator/garden-linux/old/repository_fetcher"
    15  	"github.com/cloudfoundry-incubator/garden-linux/process"
    16  )
    17  
    18  type dockerRootFSProvider struct {
    19  	graphDriver   graphdriver.Driver
    20  	volumeCreator VolumeCreator
    21  	repoFetcher   repository_fetcher.RepositoryFetcher
    22  	namespacer    Namespacer
    23  	copier        Copier
    24  	clock         clock.Clock
    25  
    26  	fallback RootFSProvider
    27  }
    28  
    29  var ErrInvalidDockerURL = errors.New("invalid docker url")
    30  
    31  //go:generate counterfeiter -o fake_graph_driver/fake_graph_driver.go . GraphDriver
    32  type GraphDriver interface {
    33  	graphdriver.Driver
    34  }
    35  
    36  //go:generate counterfeiter -o fake_copier/fake_copier.go . Copier
    37  type Copier interface {
    38  	Copy(src, dest string) error
    39  }
    40  
    41  func NewDocker(
    42  	repoFetcher repository_fetcher.RepositoryFetcher,
    43  	graphDriver GraphDriver,
    44  	volumeCreator VolumeCreator,
    45  	namespacer Namespacer,
    46  	copier Copier,
    47  	clock clock.Clock,
    48  ) (RootFSProvider, error) {
    49  	return &dockerRootFSProvider{
    50  		repoFetcher:   repoFetcher,
    51  		graphDriver:   graphDriver,
    52  		volumeCreator: volumeCreator,
    53  		namespacer:    namespacer,
    54  		copier:        copier,
    55  		clock:         clock,
    56  	}, nil
    57  }
    58  
    59  func (provider *dockerRootFSProvider) ProvideRootFS(logger lager.Logger, id string, url *url.URL, shouldNamespace bool) (string, process.Env, error) {
    60  	if len(url.Path) == 0 {
    61  		return "", nil, ErrInvalidDockerURL
    62  	}
    63  
    64  	tag := "latest"
    65  	if len(url.Fragment) > 0 {
    66  		tag = url.Fragment
    67  	}
    68  
    69  	imageID, envvars, volumes, err := provider.repoFetcher.Fetch(logger, url, tag)
    70  	if err != nil {
    71  		return "", nil, err
    72  	}
    73  
    74  	if shouldNamespace {
    75  		if imageID, err = provider.namespace(imageID); err != nil {
    76  			return "", nil, err
    77  		}
    78  	}
    79  
    80  	err = provider.graphDriver.Create(id, imageID)
    81  	if err != nil {
    82  		return "", nil, err
    83  	}
    84  
    85  	rootPath, err := provider.graphDriver.Get(id, "")
    86  	if err != nil {
    87  		return "", nil, err
    88  	}
    89  
    90  	for _, v := range volumes {
    91  		if err = provider.volumeCreator.Create(rootPath, v); err != nil {
    92  			return "", nil, err
    93  		}
    94  	}
    95  
    96  	return rootPath, envvars, nil
    97  }
    98  
    99  func (provider *dockerRootFSProvider) namespace(imageID string) (string, error) {
   100  	namespacedImageID := imageID + "@namespaced"
   101  	if !provider.graphDriver.Exists(namespacedImageID) {
   102  		if err := provider.createNamespacedLayer(namespacedImageID, imageID); err != nil {
   103  			return "", err
   104  		}
   105  	}
   106  
   107  	return namespacedImageID, nil
   108  }
   109  
   110  func (provider *dockerRootFSProvider) createNamespacedLayer(id string, parentId string) error {
   111  	var err error
   112  	var path string
   113  	if path, err = provider.createAufsWorkaroundLayer(id, parentId); err != nil {
   114  		return err
   115  	}
   116  
   117  	return provider.namespacer.Namespace(path)
   118  }
   119  
   120  // aufs directory permissions dont overlay cleanly, so we create an empty layer
   121  // and copy the parent layer in while namespacing (rather than just creating a
   122  // regular overlay layer and doing the namespacing directly inside it)
   123  func (provider *dockerRootFSProvider) createAufsWorkaroundLayer(id, parentId string) (string, error) {
   124  	errs := func(err error) (string, error) {
   125  		return "", err
   126  	}
   127  
   128  	originalRootfs, err := provider.graphDriver.Get(parentId, "")
   129  	if err != nil {
   130  		return errs(err)
   131  	}
   132  
   133  	if err := provider.graphDriver.Create(id, ""); err != nil { // empty layer
   134  		return errs(err)
   135  	}
   136  
   137  	namespacedRootfs, err := provider.graphDriver.Get(id, "") // path where empty layer is
   138  	if err != nil {
   139  		return errs(err)
   140  	}
   141  
   142  	if err := provider.copier.Copy(originalRootfs, namespacedRootfs); err != nil {
   143  		return errs(err)
   144  	}
   145  
   146  	return namespacedRootfs, nil
   147  }
   148  
   149  func (provider *dockerRootFSProvider) CleanupRootFS(logger lager.Logger, id string) error {
   150  	provider.graphDriver.Put(id)
   151  
   152  	var err error
   153  	maxAttempts := 10
   154  
   155  	for errorCount := 0; errorCount < maxAttempts; errorCount++ {
   156  		err = provider.graphDriver.Remove(id)
   157  		if err == nil {
   158  			break
   159  		}
   160  
   161  		logger.Error("cleanup-rootfs", err, lager.Data{
   162  			"current-attempts": errorCount + 1,
   163  			"max-attempts":     maxAttempts,
   164  		})
   165  
   166  		provider.clock.Sleep(200 * time.Millisecond)
   167  	}
   168  
   169  	return err
   170  }
   171  
   172  type ShellOutCp struct {
   173  	WorkDir string
   174  }
   175  
   176  func (s ShellOutCp) Copy(src, dest string) error {
   177  	if err := os.Remove(dest); err != nil {
   178  		return err
   179  	}
   180  
   181  	return exec.Command("cp", "-a", src, dest).Run()
   182  }