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 }