github.com/portworx/docker@v1.12.1/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 dockerdist "github.com/docker/docker/distribution" 18 archive "github.com/docker/docker/pkg/chrootarchive" 19 "github.com/docker/docker/reference" 20 "github.com/docker/docker/registry" 21 "github.com/docker/engine-api/types" 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 // Pull downloads the plugin from Store 66 func Pull(name string, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) { 67 ref, err := reference.ParseNamed(name) 68 if err != nil { 69 logrus.Debugf("pull.go: error in ParseNamed: %v", err) 70 return nil, err 71 } 72 73 repoInfo, err := rs.ResolveRepository(ref) 74 if err != nil { 75 logrus.Debugf("pull.go: error in ResolveRepository: %v", err) 76 return nil, err 77 } 78 79 if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil { 80 logrus.Debugf("pull.go: error in ValidateRepoName: %v", err) 81 return nil, err 82 } 83 84 endpoints, err := rs.LookupPullEndpoints(repoInfo.Hostname()) 85 if err != nil { 86 logrus.Debugf("pull.go: error in LookupPullEndpoints: %v", err) 87 return nil, err 88 } 89 90 var confirmedV2 bool 91 var repository distribution.Repository 92 93 for _, endpoint := range endpoints { 94 if confirmedV2 && endpoint.Version == registry.APIVersion1 { 95 logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL) 96 continue 97 } 98 99 // TODO: reuse contexts 100 repository, confirmedV2, err = dockerdist.NewV2Repository(context.Background(), repoInfo, endpoint, metaheader, authConfig, "pull") 101 if err != nil { 102 logrus.Debugf("pull.go: error in NewV2Repository: %v", err) 103 return nil, err 104 } 105 if !confirmedV2 { 106 logrus.Debugf("pull.go: !confirmedV2") 107 return nil, ErrUnsupportedRegistry 108 } 109 logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version) 110 break 111 } 112 113 tag := DefaultTag 114 if ref, ok := ref.(reference.NamedTagged); ok { 115 tag = ref.Tag() 116 } 117 118 // tags := repository.Tags(context.Background()) 119 // desc, err := tags.Get(context.Background(), tag) 120 // if err != nil { 121 // return nil, err 122 // } 123 // 124 msv, err := repository.Manifests(context.Background()) 125 if err != nil { 126 logrus.Debugf("pull.go: error in repository.Manifests: %v", err) 127 return nil, err 128 } 129 manifest, err := msv.Get(context.Background(), "", distribution.WithTag(tag)) 130 if err != nil { 131 // TODO: change 401 to 404 132 logrus.Debugf("pull.go: error in msv.Get(): %v", err) 133 return nil, err 134 } 135 136 _, pl, err := manifest.Payload() 137 if err != nil { 138 logrus.Debugf("pull.go: error in manifest.Payload(): %v", err) 139 return nil, err 140 } 141 var m schema2.Manifest 142 if err := json.Unmarshal(pl, &m); err != nil { 143 logrus.Debugf("pull.go: error in json.Unmarshal(): %v", err) 144 return nil, err 145 } 146 if m.Config.MediaType != MediaTypeConfig { 147 return nil, ErrUnsupportedMediaType 148 } 149 150 pd := &pullData{ 151 repository: repository, 152 manifest: m, 153 } 154 155 logrus.Debugf("manifest: %s", pl) 156 return pd, nil 157 } 158 159 // WritePullData extracts manifest and rootfs to the disk. 160 func WritePullData(pd PullData, dest string, extract bool) error { 161 config, err := pd.Config() 162 if err != nil { 163 return err 164 } 165 var p types.Plugin 166 if err := json.Unmarshal(config, &p); err != nil { 167 return err 168 } 169 logrus.Debugf("%#v", p) 170 171 if err := os.MkdirAll(dest, 0700); err != nil { 172 return err 173 } 174 175 if extract { 176 if err := ioutil.WriteFile(filepath.Join(dest, "manifest.json"), config, 0600); err != nil { 177 return err 178 } 179 180 if err := os.MkdirAll(filepath.Join(dest, "rootfs"), 0700); err != nil { 181 return err 182 } 183 } 184 185 for i := 0; ; i++ { 186 l, err := pd.Layer() 187 if err == io.EOF { 188 break 189 } 190 if err != nil { 191 return err 192 } 193 194 if !extract { 195 f, err := os.Create(filepath.Join(dest, fmt.Sprintf("layer%d.tar", i))) 196 if err != nil { 197 l.Close() 198 return err 199 } 200 io.Copy(f, l) 201 l.Close() 202 f.Close() 203 continue 204 } 205 206 if _, err := archive.ApplyLayer(filepath.Join(dest, "rootfs"), l); err != nil { 207 return err 208 } 209 210 } 211 return nil 212 }