github.com/adxhyt/docker@v1.4.2-0.20150117221845-467b7c821390/registry/session_v2.go (about) 1 package registry 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/url" 9 "strconv" 10 11 log "github.com/Sirupsen/logrus" 12 "github.com/docker/docker/utils" 13 "github.com/gorilla/mux" 14 ) 15 16 func newV2RegistryRouter() *mux.Router { 17 router := mux.NewRouter() 18 19 v2Router := router.PathPrefix("/v2/").Subrouter() 20 21 // Version Info 22 v2Router.Path("/version").Name("version") 23 24 // Image Manifests 25 v2Router.Path("/manifest/{imagename:[a-z0-9-._/]+}/{tagname:[a-zA-Z0-9-._]+}").Name("manifests") 26 27 // List Image Tags 28 v2Router.Path("/tags/{imagename:[a-z0-9-._/]+}").Name("tags") 29 30 // Download a blob 31 v2Router.Path("/blob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9._+-]+}/{sum:[a-fA-F0-9]{4,}}").Name("downloadBlob") 32 33 // Upload a blob 34 v2Router.Path("/blob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9._+-]+}").Name("uploadBlob") 35 36 // Mounting a blob in an image 37 v2Router.Path("/mountblob/{imagename:[a-z0-9-._/]+}/{sumtype:[a-z0-9._+-]+}/{sum:[a-fA-F0-9]{4,}}").Name("mountBlob") 38 39 return router 40 } 41 42 // APIVersion2 /v2/ 43 var v2HTTPRoutes = newV2RegistryRouter() 44 45 func getV2URL(e *Endpoint, routeName string, vars map[string]string) (*url.URL, error) { 46 route := v2HTTPRoutes.Get(routeName) 47 if route == nil { 48 return nil, fmt.Errorf("unknown regisry v2 route name: %q", routeName) 49 } 50 51 varReplace := make([]string, 0, len(vars)*2) 52 for key, val := range vars { 53 varReplace = append(varReplace, key, val) 54 } 55 56 routePath, err := route.URLPath(varReplace...) 57 if err != nil { 58 return nil, fmt.Errorf("unable to make registry route %q with vars %v: %s", routeName, vars, err) 59 } 60 u, err := url.Parse(REGISTRYSERVER) 61 if err != nil { 62 return nil, fmt.Errorf("invalid registry url: %s", err) 63 } 64 65 return &url.URL{ 66 Scheme: u.Scheme, 67 Host: u.Host, 68 Path: routePath.Path, 69 }, nil 70 } 71 72 // V2 Provenance POC 73 74 func (r *Session) GetV2Version(token []string) (*RegistryInfo, error) { 75 routeURL, err := getV2URL(r.indexEndpoint, "version", nil) 76 if err != nil { 77 return nil, err 78 } 79 80 method := "GET" 81 log.Debugf("[registry] Calling %q %s", method, routeURL.String()) 82 83 req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil) 84 if err != nil { 85 return nil, err 86 } 87 setTokenAuth(req, token) 88 res, _, err := r.doRequest(req) 89 if err != nil { 90 return nil, err 91 } 92 defer res.Body.Close() 93 if res.StatusCode != 200 { 94 return nil, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d fetching Version", res.StatusCode), res) 95 } 96 97 decoder := json.NewDecoder(res.Body) 98 versionInfo := new(RegistryInfo) 99 100 err = decoder.Decode(versionInfo) 101 if err != nil { 102 return nil, fmt.Errorf("unable to decode GetV2Version JSON response: %s", err) 103 } 104 105 return versionInfo, nil 106 } 107 108 // 109 // 1) Check if TarSum of each layer exists /v2/ 110 // 1.a) if 200, continue 111 // 1.b) if 300, then push the 112 // 1.c) if anything else, err 113 // 2) PUT the created/signed manifest 114 // 115 func (r *Session) GetV2ImageManifest(imageName, tagName string, token []string) ([]byte, error) { 116 vars := map[string]string{ 117 "imagename": imageName, 118 "tagname": tagName, 119 } 120 121 routeURL, err := getV2URL(r.indexEndpoint, "manifests", vars) 122 if err != nil { 123 return nil, err 124 } 125 126 method := "GET" 127 log.Debugf("[registry] Calling %q %s", method, routeURL.String()) 128 129 req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil) 130 if err != nil { 131 return nil, err 132 } 133 setTokenAuth(req, token) 134 res, _, err := r.doRequest(req) 135 if err != nil { 136 return nil, err 137 } 138 defer res.Body.Close() 139 if res.StatusCode != 200 { 140 if res.StatusCode == 401 { 141 return nil, errLoginRequired 142 } else if res.StatusCode == 404 { 143 return nil, ErrDoesNotExist 144 } 145 return nil, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to fetch for %s:%s", res.StatusCode, imageName, tagName), res) 146 } 147 148 buf, err := ioutil.ReadAll(res.Body) 149 if err != nil { 150 return nil, fmt.Errorf("Error while reading the http response: %s", err) 151 } 152 return buf, nil 153 } 154 155 // - Succeeded to mount for this image scope 156 // - Failed with no error (So continue to Push the Blob) 157 // - Failed with error 158 func (r *Session) PostV2ImageMountBlob(imageName, sumType, sum string, token []string) (bool, error) { 159 vars := map[string]string{ 160 "imagename": imageName, 161 "sumtype": sumType, 162 "sum": sum, 163 } 164 165 routeURL, err := getV2URL(r.indexEndpoint, "mountBlob", vars) 166 if err != nil { 167 return false, err 168 } 169 170 method := "POST" 171 log.Debugf("[registry] Calling %q %s", method, routeURL.String()) 172 173 req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil) 174 if err != nil { 175 return false, err 176 } 177 setTokenAuth(req, token) 178 res, _, err := r.doRequest(req) 179 if err != nil { 180 return false, err 181 } 182 res.Body.Close() // close early, since we're not needing a body on this call .. yet? 183 switch res.StatusCode { 184 case 200: 185 // return something indicating no push needed 186 return true, nil 187 case 300: 188 // return something indicating blob push needed 189 return false, nil 190 } 191 return false, fmt.Errorf("Failed to mount %q - %s:%s : %d", imageName, sumType, sum, res.StatusCode) 192 } 193 194 func (r *Session) GetV2ImageBlob(imageName, sumType, sum string, blobWrtr io.Writer, token []string) error { 195 vars := map[string]string{ 196 "imagename": imageName, 197 "sumtype": sumType, 198 "sum": sum, 199 } 200 201 routeURL, err := getV2URL(r.indexEndpoint, "downloadBlob", vars) 202 if err != nil { 203 return err 204 } 205 206 method := "GET" 207 log.Debugf("[registry] Calling %q %s", method, routeURL.String()) 208 req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil) 209 if err != nil { 210 return err 211 } 212 setTokenAuth(req, token) 213 res, _, err := r.doRequest(req) 214 if err != nil { 215 return err 216 } 217 defer res.Body.Close() 218 if res.StatusCode != 200 { 219 if res.StatusCode == 401 { 220 return errLoginRequired 221 } 222 return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to pull %s blob", res.StatusCode, imageName), res) 223 } 224 225 _, err = io.Copy(blobWrtr, res.Body) 226 return err 227 } 228 229 func (r *Session) GetV2ImageBlobReader(imageName, sumType, sum string, token []string) (io.ReadCloser, int64, error) { 230 vars := map[string]string{ 231 "imagename": imageName, 232 "sumtype": sumType, 233 "sum": sum, 234 } 235 236 routeURL, err := getV2URL(r.indexEndpoint, "downloadBlob", vars) 237 if err != nil { 238 return nil, 0, err 239 } 240 241 method := "GET" 242 log.Debugf("[registry] Calling %q %s", method, routeURL.String()) 243 req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil) 244 if err != nil { 245 return nil, 0, err 246 } 247 setTokenAuth(req, token) 248 res, _, err := r.doRequest(req) 249 if err != nil { 250 return nil, 0, err 251 } 252 if res.StatusCode != 200 { 253 if res.StatusCode == 401 { 254 return nil, 0, errLoginRequired 255 } 256 return nil, 0, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to pull %s blob", res.StatusCode, imageName), res) 257 } 258 lenStr := res.Header.Get("Content-Length") 259 l, err := strconv.ParseInt(lenStr, 10, 64) 260 if err != nil { 261 return nil, 0, err 262 } 263 264 return res.Body, l, err 265 } 266 267 // Push the image to the server for storage. 268 // 'layer' is an uncompressed reader of the blob to be pushed. 269 // The server will generate it's own checksum calculation. 270 func (r *Session) PutV2ImageBlob(imageName, sumType string, blobRdr io.Reader, token []string) (serverChecksum string, err error) { 271 vars := map[string]string{ 272 "imagename": imageName, 273 "sumtype": sumType, 274 } 275 276 routeURL, err := getV2URL(r.indexEndpoint, "uploadBlob", vars) 277 if err != nil { 278 return "", err 279 } 280 281 method := "PUT" 282 log.Debugf("[registry] Calling %q %s", method, routeURL.String()) 283 req, err := r.reqFactory.NewRequest(method, routeURL.String(), blobRdr) 284 if err != nil { 285 return "", err 286 } 287 setTokenAuth(req, token) 288 res, _, err := r.doRequest(req) 289 if err != nil { 290 return "", err 291 } 292 defer res.Body.Close() 293 if res.StatusCode != 201 { 294 if res.StatusCode == 401 { 295 return "", errLoginRequired 296 } 297 return "", utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s blob", res.StatusCode, imageName), res) 298 } 299 300 type sumReturn struct { 301 Checksum string `json:"checksum"` 302 } 303 304 decoder := json.NewDecoder(res.Body) 305 var sumInfo sumReturn 306 307 err = decoder.Decode(&sumInfo) 308 if err != nil { 309 return "", fmt.Errorf("unable to decode PutV2ImageBlob JSON response: %s", err) 310 } 311 312 // XXX this is a json struct from the registry, with its checksum 313 return sumInfo.Checksum, nil 314 } 315 316 // Finally Push the (signed) manifest of the blobs we've just pushed 317 func (r *Session) PutV2ImageManifest(imageName, tagName string, manifestRdr io.Reader, token []string) error { 318 vars := map[string]string{ 319 "imagename": imageName, 320 "tagname": tagName, 321 } 322 323 routeURL, err := getV2URL(r.indexEndpoint, "manifests", vars) 324 if err != nil { 325 return err 326 } 327 328 method := "PUT" 329 log.Debugf("[registry] Calling %q %s", method, routeURL.String()) 330 req, err := r.reqFactory.NewRequest(method, routeURL.String(), manifestRdr) 331 if err != nil { 332 return err 333 } 334 setTokenAuth(req, token) 335 res, _, err := r.doRequest(req) 336 if err != nil { 337 return err 338 } 339 res.Body.Close() 340 if res.StatusCode != 201 { 341 if res.StatusCode == 401 { 342 return errLoginRequired 343 } 344 return utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s:%s manifest", res.StatusCode, imageName, tagName), res) 345 } 346 347 return nil 348 } 349 350 // Given a repository name, returns a json array of string tags 351 func (r *Session) GetV2RemoteTags(imageName string, token []string) ([]string, error) { 352 vars := map[string]string{ 353 "imagename": imageName, 354 } 355 356 routeURL, err := getV2URL(r.indexEndpoint, "tags", vars) 357 if err != nil { 358 return nil, err 359 } 360 361 method := "GET" 362 log.Debugf("[registry] Calling %q %s", method, routeURL.String()) 363 364 req, err := r.reqFactory.NewRequest(method, routeURL.String(), nil) 365 if err != nil { 366 return nil, err 367 } 368 setTokenAuth(req, token) 369 res, _, err := r.doRequest(req) 370 if err != nil { 371 return nil, err 372 } 373 defer res.Body.Close() 374 if res.StatusCode != 200 { 375 if res.StatusCode == 401 { 376 return nil, errLoginRequired 377 } else if res.StatusCode == 404 { 378 return nil, ErrDoesNotExist 379 } 380 return nil, utils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to fetch for %s", res.StatusCode, imageName), res) 381 } 382 383 decoder := json.NewDecoder(res.Body) 384 var tags []string 385 err = decoder.Decode(&tags) 386 if err != nil { 387 return nil, fmt.Errorf("Error while decoding the http response: %s", err) 388 } 389 return tags, nil 390 }