func PutRepositoryV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { username, _, _ := utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization")) namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") body, err := ctx.Req.Body().String() if err != nil { log.Error("[REGISTRY API V1] Get request body error: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Put V1 repository failed,request body is empty"}) return http.StatusBadRequest, result } r := new(models.Repository) if err := r.Put(namespace, repository, body, ctx.Req.Header.Get("User-Agent"), setting.APIVERSION_V1); err != nil { log.Error("[REGISTRY API V1] Put repository error: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": err.Error()}) return http.StatusBadRequest, result } if ctx.Req.Header.Get("X-Docker-Token") == "true" { token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v", utils.MD5(username), namespace, repository, "write") ctx.Resp.Header().Set("X-Docker-Token", token) ctx.Resp.Header().Set("WWW-Authenticate", token) } result, _ := json.Marshal(map[string]string{}) return http.StatusOK, result }
func GetTagsListV2Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") r := new(models.Repository) if has, _, err := r.Has(namespace, repository); err != nil || has == false { log.Error("[REGISTRY API V2] Repository not found: %v", repository) result, _ := json.Marshal(map[string]string{"message": "Repository not found"}) return http.StatusNotFound, result } data := map[string]interface{}{} tags := []string{} data["name"] = fmt.Sprintf("%s/%s", namespace, repository) for _, value := range r.Tags { t := new(models.Tag) if err := t.GetByKey(value); err != nil { log.Error("[REGISTRY API V2] Tag not found: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Tag not found"}) return http.StatusNotFound, result } tags = append(tags, t.Name) } data["tags"] = tags result, _ := json.Marshal(data) return http.StatusOK, result }
func PutRepositoryImagesV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") r := new(models.Repository) if err := r.PutImages(namespace, repository); err != nil { log.Error("[REGISTRY API V1] Put images error: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Put V1 images error"}) return http.StatusBadRequest, result } if ctx.Req.Header.Get("X-Docker-Token") == "true" { username, _, _ := utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization")) token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v", utils.MD5(username), namespace, repository, "write") ctx.Resp.Header().Set("X-Docker-Token", token) ctx.Resp.Header().Set("WWW-Authenticate", token) } result, _ := json.Marshal(map[string]string{}) return http.StatusNoContent, result }
func GetTagV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") r := new(models.Repository) if exists, err := r.Get(namespace, repository); err != nil { log.Error("[REGISTRY API V1] Failed to get repository %v/%v context: %v", namespace, repository, err.Error()) result, _ := json.Marshal(map[string]string{"message": "Failed to get repository context"}) return http.StatusBadRequest, result } else if exists == false { log.Error("[REGISTRY API V1] Not found repository %v/%v", namespace, repository) result, _ := json.Marshal(map[string]string{"message": "Not found repository"}) return http.StatusNotFound, result } tags := map[string]string{} tagslist := r.GetTagslist() for _, tagname := range tagslist { t := new(models.Tag) if exists, err := t.Get(namespace, repository, tagname); err != nil || !exists { log.Error(fmt.Sprintf("[REGISTRY API V1] %s/%s %v is not exist", namespace, repository, tagname)) result, _ := json.Marshal(map[string]string{"message": "Tag is not exist"}) return http.StatusNotFound, result } tags[tagname] = t.ImageId } result, _ := json.Marshal(tags) return http.StatusOK, result }
func GetTagV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") repo := new(models.Repository) if has, _, err := repo.Has(namespace, repository); err != nil { log.Error("[REGISTRY API V1] Read repository json error: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Get V1 tag failed,wrong name or repository"}) return http.StatusBadRequest, result } else if has == false { log.Error("[REGISTRY API V1] Read repository no found. %v/%v", namespace, repository) result, _ := json.Marshal(map[string]string{"message": "Get V1 tag failed,read repository no found"}) return http.StatusNotFound, result } tag := map[string]string{} for _, value := range repo.Tags { t := new(models.Tag) if err := db.Get(t, value); err != nil { log.Error(fmt.Sprintf("[REGISTRY API V1] %s/%s Tags is not exist", namespace, repository)) result, _ := json.Marshal(map[string]string{"message": fmt.Sprintf("%s/%s Tags is not exist", namespace, repository)}) return http.StatusNotFound, result } tag[t.Name] = t.ImageId } result, _ := json.Marshal(tag) return http.StatusOK, result }
func GetTagsListV2Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") r := new(models.Repository) if _, err := r.Get(namespace, repository); err != nil { log.Error("[REGISTRY API V2] Failed to get repository %v/%v: %v", namespace, repository, err.Error()) result, _ := json.Marshal(map[string]string{"message": "Failed to get repository"}) return http.StatusBadRequest, result } data := map[string]interface{}{} data["name"] = fmt.Sprintf("%s/%s", namespace, repository) tagslist := r.GetTagslist() if len(tagslist) <= 0 { log.Error("[REGISTRY API V2] Repository %v/%v tags list is empty", namespace, repository) result, _ := json.Marshal(map[string]string{"message": "Tags list is empty"}) return http.StatusNotFound, result } data["tags"] = tagslist result, _ := json.Marshal(data) return http.StatusOK, result }
func PutManifestsV2Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { //TODO: to consider parallel situation manifest := ManifestCtx defer func() { ManifestCtx = []byte{} }() namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") agent := ctx.Req.Header.Get("User-Agent") tag := ctx.Params(":tag") if len(manifest) == 0 { manifest, _ = ctx.Req.Body().Bytes() } digest, err := utils.DigestManifest(manifest) if err != nil { log.Error("[REGISTRY API V2] Failed to get manifest digest: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Failed to get manifest digest"}) return http.StatusBadRequest, result } r := new(models.Repository) if err := r.Put(namespace, repository, "", agent, setting.APIVERSION_V2); err != nil { log.Error("[REGISTRY API V2] Failed to save repository %v/%v: %v", namespace, repository, err.Error()) result, _ := json.Marshal(map[string]string{"message": "Failed to save repository"}) return http.StatusInternalServerError, result } err, schema := module.ParseManifest(manifest, namespace, repository, tag) if err != nil { log.Error("[REGISTRY API V2] Failed to decode manifest: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Failed to decode manifest"}) return http.StatusBadRequest, result } random := fmt.Sprintf("%s://%s/v2/%s/%s/manifests/%s", setting.ListenMode, setting.Domains, namespace, repository, digest) ctx.Resp.Header().Set("Docker-Content-Digest", digest) ctx.Resp.Header().Set("Location", random) var status = []int{http.StatusBadRequest, http.StatusAccepted, http.StatusCreated} result, _ := json.Marshal(map[string]string{}) return status[schema], result }
func PutManifestsV2Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { //TODO: to consider parallel situation manifest := ManifestCtx defer func() { ManifestCtx = []byte{} }() namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") agent := ctx.Req.Header.Get("User-Agent") repo := new(models.Repository) if err := repo.Put(namespace, repository, "", agent, setting.APIVERSION_V2); err != nil { log.Error("[REGISTRY API V2] Save repository failed: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": err.Error()}) return http.StatusBadRequest, result } if len(manifest) == 0 { manifest, _ = ctx.Req.Body().Bytes() } if err := module.ParseManifest(manifest); err != nil { log.Error("[REGISTRY API V2] Decode Manifest Error: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Manifest converted failed"}) return http.StatusBadRequest, result } digest, err := utils.DigestManifest(manifest) if err != nil { log.Error("[REGISTRY API V2] Get manifest digest failed: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Get manifest digest failed"}) return http.StatusBadRequest, result } random := fmt.Sprintf("%s://%s/v2/%s/%s/manifests/%s", setting.ListenMode, setting.Domains, namespace, repository, digest) ctx.Resp.Header().Set("Docker-Content-Digest", digest) ctx.Resp.Header().Set("Location", random) result, _ := json.Marshal(map[string]string{}) return http.StatusAccepted, result }
func PutManifestsV2Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") agent := ctx.Req.Header.Get("User-Agent") repo := new(models.Repository) if err := repo.Put(namespace, repository, "", agent, setting.APIVERSION_V2); err != nil { log.Error("[REGISTRY API V2] Save repository failed: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": err.Error()}) return http.StatusBadRequest, result } manifest, _ := ioutil.ReadAll(ctx.Req.Request.Body) if err := modules.ParseManifest(manifest); err != nil { log.Error("[REGISTRY API V2] Decode Manifest Error: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Manifest converted failed"}) return http.StatusBadRequest, result } digest, err := utils.DigestManifest(manifest) if err != nil { log.Error("[REGISTRY API V2] Get manifest digest failed: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Get manifest digest failed"}) return http.StatusBadRequest, result } random := fmt.Sprintf("https://%v/v2/%v/%v/manifests/%v", setting.Domains, namespace, repository, digest) ctx.Resp.Header().Set("Docker-Content-Digest", digest) ctx.Resp.Header().Set("Location", random) result, _ := json.Marshal(map[string]string{}) return http.StatusAccepted, result }
func PutTagV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") tag := ctx.Params(":tag") bodystr, _ := ctx.Req.Body().String() rege, _ := regexp.Compile(`"([[:alnum:]]+)"`) imageIds := rege.FindStringSubmatch(bodystr) r := new(models.Repository) if err := r.PutTag(imageIds[1], namespace, repository, tag); err != nil { log.Error("[REGISTRY API V1] Failed to save repository %v/%v tag: %v", namespace, repository, err.Error()) result, _ := json.Marshal(map[string]string{"message": "Failed to save repository tag"}) return http.StatusBadRequest, result } result, _ := json.Marshal(map[string]string{}) return http.StatusOK, result }
func ParseManifest(data []byte) error { var manifest map[string]interface{} if err := json.Unmarshal(data, &manifest); err != nil { return err } tag := manifest["tag"] namespace, repository := strings.Split(manifest["name"].(string), "/")[0], strings.Split(manifest["name"].(string), "/")[1] for k := len(manifest["history"].([]interface{})) - 1; k >= 0; k-- { v := manifest["history"].([]interface{})[k] compatibility := v.(map[string]interface{})["v1Compatibility"].(string) var image map[string]interface{} if err := json.Unmarshal([]byte(compatibility), &image); err != nil { return err } i := map[string]string{} r := new(models.Repository) if k == 0 { i["Tag"] = tag.(string) } i["id"] = image["id"].(string) if err := r.PutJSONFromManifests(i, namespace, repository); err != nil { return err } if k == 0 { if err := r.PutTagFromManifests(image["id"].(string), namespace, repository, tag.(string), string(data)); err != nil { return err } } } return nil }
func ParseManifest(data []byte, namespace, repository, tag string) (error, int64) { var manifest map[string]interface{} if err := json.Unmarshal(data, &manifest); err != nil { return err, 0 } schemaVersion := int64(manifest["schemaVersion"].(float64)) if schemaVersion == 1 { for k := len(manifest["history"].([]interface{})) - 1; k >= 0; k-- { v := manifest["history"].([]interface{})[k] compatibility := v.(map[string]interface{})["v1Compatibility"].(string) var image map[string]interface{} if err := json.Unmarshal([]byte(compatibility), &image); err != nil { return err, 0 } i := map[string]string{} r := new(models.Repository) if k == 0 { i["Tag"] = tag } i["id"] = image["id"].(string) if err := r.PutJSONFromManifests(i, namespace, repository); err != nil { return err, 0 } if k == 0 { if err := r.PutTagFromManifests(image["id"].(string), namespace, repository, tag, string(data), schemaVersion); err != nil { return err, 0 } } } } else if schemaVersion == 2 { r := new(models.Repository) if err := r.PutTagFromManifests("schemaV2", namespace, repository, tag, string(data), schemaVersion); err != nil { return err, 0 } } else { return fmt.Errorf("invalid schema version"), 0 } return nil, schemaVersion }
func GetRepositoryImagesV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") repo := new(models.Repository) if has, _, err := repo.Has(namespace, repository); err != nil { log.Error("[REGISTRY API V1] Read repository json error: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Get V1 repository images failed,wrong name or repository"}) return http.StatusBadRequest, result } else if has == false { log.Error("[REGISTRY API V1] Read repository no found, %v/%v", namespace, repository) result, _ := json.Marshal(map[string]string{"message": "Get V1 repository images failed,repository no found"}) return http.StatusNotFound, result } repo.Download += 1 if err := repo.Save(); err != nil { log.Error("[REGISTRY API V1] Update download count error: %v", err.Error()) result, _ := json.Marshal(map[string]string{"message": "Save V1 repository failed"}) return http.StatusBadRequest, result } username, _, _ := utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization")) token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v", utils.MD5(username), namespace, repository, "read") ctx.Resp.Header().Set("X-Docker-Token", token) ctx.Resp.Header().Set("WWW-Authenticate", token) ctx.Resp.Header().Set("Content-Length", fmt.Sprint(len(repo.JSON))) return http.StatusOK, []byte(repo.JSON) }
func GetRepositoryImagesV1Handler(ctx *macaron.Context, log *logs.BeeLogger) (int, []byte) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") r := new(models.Repository) if exists, err := r.Get(namespace, repository); err != nil { log.Error("[REGISTRY API V1] Failed to get repository %v/%v context: %v", namespace, repository, err.Error()) result, _ := json.Marshal(map[string]string{"message": "Failed to get repository context"}) return http.StatusBadRequest, result } else if exists == false { log.Error("[REGISTRY API V1] Not found repository %v/%v", namespace, repository) result, _ := json.Marshal(map[string]string{"message": "Not found repository"}) return http.StatusNotFound, result } r.Download += 1 if err := r.Save(namespace, repository); err != nil { log.Error("[REGISTRY API V1] Failed to save repository %v/%v context: %v", namespace, repository, err.Error()) result, _ := json.Marshal(map[string]string{"message": "Failed to save repository context"}) return http.StatusBadRequest, result } username, _, _ := utils.DecodeBasicAuth(ctx.Req.Header.Get("Authorization")) token := fmt.Sprintf("Token signature=%v,repository=\"%v/%v\",access=%v", utils.MD5(username), namespace, repository, "read") ctx.Resp.Header().Set("X-Docker-Token", token) ctx.Resp.Header().Set("WWW-Authenticate", token) ctx.Resp.Header().Set("Content-Length", fmt.Sprint(len(r.JSON))) return http.StatusOK, []byte(r.JSON) }
func (n *notification) Handler(ctx *macaron.Context) { namespace := ctx.Params(":namespace") repository := ctx.Params(":repository") //Notification function just supports DockerV2 now r := new(models.Repository) if exists, err := r.Get(namespace, repository); err != nil || exists == false { return } if r.Version != setting.APIVERSION_V2 { return } actor := ActorRecord{Name: namespace} repo := fmt.Sprintf("%v/%v", namespace, repository) switch ctx.Req.Method { case "HEAD": digest := ctx.Params(":digest") tarsum := strings.Split(digest, ":")[1] i := new(models.Image) if exists, _ := i.Get(tarsum); exists == false { return } desc := Descriptor{ MediaType: DefaultMedisType, Size: i.Size, Digest: digest, } req := newReqRecord(utils.EncodeBasicAuth(namespace, "headblobsv2"), ctx.Req.Request) requrl, err := module.NewURLBuilderFromRequest(ctx.Req.Request).BuildBlobURL(repo, desc.Digest) if err != nil { fmt.Errorf("[REGISTRY API V2] Head blobs and get request URL failed, error:: %v", err.Error()) } b := newBridge(requrl, actor, req, notice) Err := b.createBlobEventAndWrite(EventActionPull, repo, desc) if Err.Err != nil { ctx.Resp.WriteHeader(http.StatusForbidden) return } else if Err.StatusCode >= 300 { ctx.Resp.WriteHeader(Err.StatusCode) return } case "GET": if flag := utils.Compare(ctx.Req.RequestURI, "/v2/"); flag == 0 { return } if flag := strings.Contains(ctx.Req.RequestURI, "/blobs/"); flag == true { digest := ctx.Params(":digest") tarsum := strings.Split(digest, ":")[1] i := new(models.Image) if exists, _ := i.Get(tarsum); exists == false { return } desc := Descriptor{ MediaType: BlobMediaType, Size: i.Size, Digest: digest, } req := newReqRecord(utils.EncodeBasicAuth(namespace, "getblobsv2"), ctx.Req.Request) requrl, err := module.NewURLBuilderFromRequest(ctx.Req.Request).BuildBlobURL(repo, desc.Digest) if err != nil { fmt.Errorf("[REGISTRY API V2] Get blobs and get request URL failed, error:: %v", err.Error()) } b := newBridge(requrl, actor, req, notice) Err := b.createBlobEventAndWrite(EventActionPull, repo, desc) if Err.Err != nil { ctx.Resp.WriteHeader(http.StatusForbidden) return } else if Err.StatusCode >= 300 { ctx.Resp.WriteHeader(Err.StatusCode) return } return } if flag := strings.Contains(ctx.Req.RequestURI, "/manifests/"); flag == true { t := new(models.Tag) if exists, err := t.Get(namespace, repository, ctx.Params(":tag")); err != nil || !exists { return } digest, err := utils.DigestManifest([]byte(t.Manifest)) if err != nil { fmt.Errorf("[REGISTRY API V2] Get manifest digest failed: %v", err.Error()) return } var sm SignedManifest if err := json.Unmarshal([]byte(t.Manifest), &sm); err != nil { fmt.Errorf("Unmarshal manifest error") } sm.Raw = make([]byte, len(t.Manifest), len(t.Manifest)) copy(sm.Raw, []byte(t.Manifest)) req := newReqRecord(utils.EncodeBasicAuth(namespace, "getmanifestv2"), ctx.Req.Request) requrl, err := module.NewURLBuilderFromRequest(ctx.Req.Request).BuildManifestURL(sm.Name, digest) if err != nil { fmt.Errorf("[REGISTRY API V2] Get manifest and get request URL failed, error:: %v", err.Error()) } b := newBridge(requrl, actor, req, notice) Err := b.createManifestEventAndWrite(EventActionPull, repo, &sm) if Err.Err != nil { ctx.Resp.WriteHeader(http.StatusForbidden) return } else if Err.StatusCode >= 300 { ctx.Resp.WriteHeader(Err.StatusCode) return } return } case "PUT": if flag := strings.Contains(ctx.Req.RequestURI, "/blobs/uploads/"); flag == true { param := ctx.Params(":uuid") uuid := strings.Split(param, "?")[0] layer, _ := ioutil.ReadFile(fmt.Sprintf("%v/%v/layer", setting.ImagePath, uuid)) digest := ctx.Query("digest") desc := Descriptor{ MediaType: DefaultMedisType, Size: int64(len(layer)), Digest: digest, } req := newReqRecord(utils.EncodeBasicAuth(namespace, "putblobsv2"), ctx.Req.Request) requrl, err := module.NewURLBuilderFromRequest(ctx.Req.Request).BuildBlobURL(repo, desc.Digest) if err != nil { fmt.Errorf("[REGISTRY API V2] Get blobs and get request URL failed, error:: %v", err.Error()) } b := newBridge(requrl, actor, req, notice) Err := b.createBlobEventAndWrite(EventActionPush, repo, desc) if Err.Err != nil { ctx.Resp.WriteHeader(http.StatusForbidden) return } else if Err.StatusCode >= 300 { ctx.Resp.WriteHeader(Err.StatusCode) return } return } if flag := strings.Contains(ctx.Req.RequestURI, "/manifests/"); flag == true { buf, _ := ctx.Req.Body().Bytes() handler.ManifestCtx = buf var sm SignedManifest if err := json.Unmarshal(buf, &sm); err != nil { fmt.Errorf("Unmarshal manifest error") } sm.Raw = make([]byte, len(buf), len(buf)) copy(sm.Raw, buf) digest, err := utils.DigestManifest(buf) if err != nil { fmt.Errorf("[REGISTRY API V2] Get manifest digest failed: %v", err.Error()) //return http.StatusBadRequest, result } req := newReqRecord(utils.EncodeBasicAuth(namespace, "putmanifestv2"), ctx.Req.Request) requrl, err := module.NewURLBuilderFromRequest(ctx.Req.Request).BuildManifestURL(sm.Name, digest) if err != nil { fmt.Errorf("[REGISTRY API V2] Put manifest and get request URL failed, error:: %v", err.Error()) } b := newBridge(requrl, actor, req, notice) Err := b.createManifestEventAndWrite(EventActionPush, repo, &sm) if Err.Err != nil { ctx.Resp.WriteHeader(http.StatusForbidden) return } else if Err.StatusCode >= 300 { ctx.Resp.WriteHeader(Err.StatusCode) return } return } default: return } }