github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/plugin/distribution/pull.go (about) 1 // +build experimental 2 3 package distribution 4 5 import ( 6 "encoding/json" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "os" 12 "path/filepath" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/distribution" 16 "github.com/docker/distribution/manifest/schema2" 17 "github.com/docker/docker/api/types" 18 dockerdist "github.com/docker/docker/distribution" 19 archive "github.com/docker/docker/pkg/chrootarchive" 20 "github.com/docker/docker/reference" 21 "github.com/docker/docker/registry" 22 "golang.org/x/net/context" 23 ) 24 25 // PullData is the plugin manifest and the rootfs 26 type PullData interface { 27 Config() ([]byte, error) 28 Layer() (io.ReadCloser, error) 29 } 30 31 type pullData struct { 32 repository distribution.Repository 33 manifest schema2.Manifest 34 index int 35 } 36 37 func (pd *pullData) Config() ([]byte, error) { 38 blobs := pd.repository.Blobs(context.Background()) 39 config, err := blobs.Get(context.Background(), pd.manifest.Config.Digest) 40 if err != nil { 41 return nil, err 42 } 43 // validate 44 var p types.Plugin 45 if err := json.Unmarshal(config, &p); err != nil { 46 return nil, err 47 } 48 return config, nil 49 } 50 51 func (pd *pullData) Layer() (io.ReadCloser, error) { 52 if pd.index >= len(pd.manifest.Layers) { 53 return nil, io.EOF 54 } 55 56 blobs := pd.repository.Blobs(context.Background()) 57 rsc, err := blobs.Open(context.Background(), pd.manifest.Layers[pd.index].Digest) 58 if err != nil { 59 return nil, err 60 } 61 pd.index++ 62 return rsc, nil 63 } 64 65 // GetRef returns the distribution reference for a given name. 66 func GetRef(name string) (reference.Named, error) { 67 ref, err := reference.ParseNamed(name) 68 if err != nil { 69 return nil, err 70 } 71 return ref, nil 72 } 73 74 // GetTag returns the tag associated with the given reference name. 75 func GetTag(ref reference.Named) string { 76 tag := DefaultTag 77 if ref, ok := ref.(reference.NamedTagged); ok { 78 tag = ref.Tag() 79 } 80 return tag 81 } 82 83 // Pull downloads the plugin from Store 84 func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) { 85 repoInfo, err := rs.ResolveRepository(ref) 86 if err != nil { 87 logrus.Debugf("pull.go: error in ResolveRepository: %v", err) 88 return nil, err 89 } 90 91 if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil { 92 logrus.Debugf("pull.go: error in ValidateRepoName: %v", err) 93 return nil, err 94 } 95 96 endpoints, err := rs.LookupPullEndpoints(repoInfo.Hostname()) 97 if err != nil { 98 logrus.Debugf("pull.go: error in LookupPullEndpoints: %v", err) 99 return nil, err 100 } 101 102 var confirmedV2 bool 103 var repository distribution.Repository 104 105 for _, endpoint := range endpoints { 106 if confirmedV2 && endpoint.Version == registry.APIVersion1 { 107 logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL) 108 continue 109 } 110 111 // TODO: reuse contexts 112 repository, confirmedV2, err = dockerdist.NewV2Repository(context.Background(), repoInfo, endpoint, metaheader, authConfig, "pull") 113 if err != nil { 114 logrus.Debugf("pull.go: error in NewV2Repository: %v", err) 115 return nil, err 116 } 117 if !confirmedV2 { 118 logrus.Debugf("pull.go: !confirmedV2") 119 return nil, ErrUnsupportedRegistry 120 } 121 logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version) 122 break 123 } 124 125 tag := DefaultTag 126 if ref, ok := ref.(reference.NamedTagged); ok { 127 tag = ref.Tag() 128 } 129 130 // tags := repository.Tags(context.Background()) 131 // desc, err := tags.Get(context.Background(), tag) 132 // if err != nil { 133 // return nil, err 134 // } 135 // 136 msv, err := repository.Manifests(context.Background()) 137 if err != nil { 138 logrus.Debugf("pull.go: error in repository.Manifests: %v", err) 139 return nil, err 140 } 141 manifest, err := msv.Get(context.Background(), "", distribution.WithTag(tag)) 142 if err != nil { 143 // TODO: change 401 to 404 144 logrus.Debugf("pull.go: error in msv.Get(): %v", err) 145 return nil, err 146 } 147 148 _, pl, err := manifest.Payload() 149 if err != nil { 150 logrus.Debugf("pull.go: error in manifest.Payload(): %v", err) 151 return nil, err 152 } 153 var m schema2.Manifest 154 if err := json.Unmarshal(pl, &m); err != nil { 155 logrus.Debugf("pull.go: error in json.Unmarshal(): %v", err) 156 return nil, err 157 } 158 if m.Config.MediaType != schema2.MediaTypePluginConfig { 159 return nil, ErrUnsupportedMediaType 160 } 161 162 pd := &pullData{ 163 repository: repository, 164 manifest: m, 165 } 166 167 logrus.Debugf("manifest: %s", pl) 168 return pd, nil 169 } 170 171 // WritePullData extracts manifest and rootfs to the disk. 172 func WritePullData(pd PullData, dest string, extract bool) error { 173 config, err := pd.Config() 174 if err != nil { 175 return err 176 } 177 var p types.Plugin 178 if err := json.Unmarshal(config, &p); err != nil { 179 return err 180 } 181 logrus.Debugf("%#v", p) 182 183 if err := os.MkdirAll(dest, 0700); err != nil { 184 return err 185 } 186 187 if extract { 188 if err := ioutil.WriteFile(filepath.Join(dest, "manifest.json"), config, 0600); err != nil { 189 return err 190 } 191 192 if err := os.MkdirAll(filepath.Join(dest, "rootfs"), 0700); err != nil { 193 return err 194 } 195 } 196 197 for i := 0; ; i++ { 198 l, err := pd.Layer() 199 if err == io.EOF { 200 break 201 } 202 if err != nil { 203 return err 204 } 205 206 if !extract { 207 f, err := os.Create(filepath.Join(dest, fmt.Sprintf("layer%d.tar", i))) 208 if err != nil { 209 l.Close() 210 return err 211 } 212 io.Copy(f, l) 213 l.Close() 214 f.Close() 215 continue 216 } 217 218 if _, err := archive.ApplyLayer(filepath.Join(dest, "rootfs"), l); err != nil { 219 return err 220 } 221 222 } 223 return nil 224 }