From cf953f74fedfabf2f9b1b8c80789c4d3ee10bad1 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 2 Aug 2016 10:53:53 +0800 Subject: [PATCH] return more information when calling get mannifest API Conflicts: api/repository.go --- api/repository.go | 66 +++++++++++++++++++++++++++++++++-------------- models/repo.go | 62 -------------------------------------------- 2 files changed, 47 insertions(+), 81 deletions(-) delete mode 100644 models/repo.go diff --git a/api/repository.go b/api/repository.go index da00d691d..84d4ac286 100644 --- a/api/repository.go +++ b/api/repository.go @@ -16,16 +16,16 @@ package api import ( - "encoding/json" "fmt" + "io/ioutil" "net/http" "os" "sort" "strconv" "strings" - "time" "github.com/docker/distribution/manifest/schema1" + "github.com/docker/distribution/manifest/schema2" "github.com/vmware/harbor/dao" "github.com/vmware/harbor/models" "github.com/vmware/harbor/service/cache" @@ -270,6 +270,15 @@ func (ra *RepositoryAPI) GetManifests() { ra.CustomAbort(http.StatusBadRequest, "repo_name or tag is nil") } + version := ra.GetString("version") + if len(version) == 0 { + version = "v2" + } + + if version != "v1" && version != "v2" { + ra.CustomAbort(http.StatusBadRequest, "version should be v1 or v2") + } + projectName := getProjectName(repoName) project, err := dao.GetProjectByName(projectName) if err != nil { @@ -290,10 +299,20 @@ func (ra *RepositoryAPI) GetManifests() { ra.CustomAbort(http.StatusInternalServerError, "internal error") } - item := models.RepoItem{} + result := struct { + Manifest interface{} `json:"manifest"` + Config interface{} `json:"config,omitempty" ` + }{} - mediaTypes := []string{schema1.MediaTypeManifest} - _, _, payload, err := rc.PullManifest(tag, mediaTypes) + mediaTypes := []string{} + switch version { + case "v1": + mediaTypes = append(mediaTypes, schema1.MediaTypeManifest) + case "v2": + mediaTypes = append(mediaTypes, schema2.MediaTypeManifest) + } + + _, mediaType, payload, err := rc.PullManifest(tag, mediaTypes) if err != nil { if regErr, ok := err.(*registry_error.Error); ok { ra.CustomAbort(regErr.StatusCode, regErr.Detail) @@ -302,24 +321,33 @@ func (ra *RepositoryAPI) GetManifests() { log.Errorf("error occurred while getting manifest of %s:%s: %v", repoName, tag, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } - mani := models.Manifest{} - err = json.Unmarshal(payload, &mani) - if err != nil { - log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err) - ra.RenderError(http.StatusInternalServerError, "Internal Server Error") - return - } - v1Compatibility := mani.History[0].V1Compatibility - err = json.Unmarshal([]byte(v1Compatibility), &item) + manifest, _, err := registry.UnMarshal(mediaType, payload) if err != nil { - log.Errorf("Failed to decode V1 field for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err) - ra.RenderError(http.StatusInternalServerError, "Internal Server Error") - return + log.Errorf("an error occurred while parsing manifest of %s:%s: %v", repoName, tag, err) + ra.CustomAbort(http.StatusInternalServerError, "") } - item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days" - ra.Data["json"] = item + result.Manifest = manifest + + deserializedmanifest, ok := manifest.(*schema2.DeserializedManifest) + if ok { + _, data, err := rc.PullBlob(deserializedmanifest.Target().Digest.String()) + if err != nil { + log.Errorf("failed to get config of manifest %s:%s: %v", repoName, tag, err) + ra.CustomAbort(http.StatusInternalServerError, "") + } + + b, err := ioutil.ReadAll(data) + if err != nil { + log.Errorf("failed to read config of manifest %s:%s: %v", repoName, tag, err) + ra.CustomAbort(http.StatusInternalServerError, "") + } + + result.Config = string(b) + } + + ra.Data["json"] = result ra.ServeJSON() } diff --git a/models/repo.go b/models/repo.go deleted file mode 100644 index f224c902e..000000000 --- a/models/repo.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright (c) 2016 VMware, Inc. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package models - -import ( - "time" -) - -// Repo holds information about repositories. -type Repo struct { - Repositories []string `json:"repositories"` -} - -// RepoItem holds manifest of an image. -type RepoItem struct { - ID string `json:"Id"` - Parent string `json:"Parent"` - Created time.Time `json:"Created"` - DurationDays string `json:"Duration Days"` - Author string `json:"Author"` - Architecture string `json:"Architecture"` - DockerVersion string `json:"Docker Version"` - Os string `json:"OS"` - //Size int `json:"Size"` -} - -// Tag holds information about a tag. -type Tag struct { - Version string `json:"version"` - ImageID string `json:"image_id"` -} - -// Manifest ... -type Manifest struct { - SchemaVersion int `json:"schemaVersion"` - Name string `json:"name"` - Tag string `json:"tag"` - Architecture string `json:"architecture"` - FsLayers []blobSumItem `json:"fsLayers"` - History []histroyItem `json:"history"` -} - -type histroyItem struct { - V1Compatibility string `json:"v1Compatibility"` -} - -type blobSumItem struct { - BlobSum string `json:"blobSum"` -}