diff --git a/main.go b/main.go index 9e6e865..f9f2829 100644 --- a/main.go +++ b/main.go @@ -243,6 +243,22 @@ func (a *apiClient) viewTagInfo(c echo.Context) error { layersCount = len(gjson.Get(infoV1, "fsLayers").Array()) } + isDigest := strings.HasPrefix(tag, "sha256:") + var digests []map[string]gjson.Result + var digestSizes []int64 + for _, s := range a.client.Manifests(repoPath, tag) { + var r map[string]gjson.Result = s.Map() + r["architecture"] = s.Get("platform.architecture") + r["os"] = s.Get("platform.os") + _, _, dInfo := a.client.TagInfo(repoPath, s.Get("digest").String(), false) + var dSize int64 + for _, d := range gjson.Get(dInfo, "layers.#.size").Array() { + dSize = dSize + d.Int() + } + digestSizes = append(digestSizes, dSize) + digests = append(digests, r) + } + data := jet.VarMap{} data.Set("namespace", namespace) data.Set("repo", repo) @@ -254,6 +270,9 @@ func (a *apiClient) viewTagInfo(c echo.Context) error { data.Set("layersCount", layersCount) data.Set("layersV2", layersV2) data.Set("layersV1", layersV1) + data.Set("isDigest", isDigest) + data.Set("digests", digests) + data.Set("digestSizes", digestSizes) return c.Render(http.StatusOK, "tag_info.html", data) } diff --git a/registry/client.go b/registry/client.go index ff53994..184c0ef 100644 --- a/registry/client.go +++ b/registry/client.go @@ -106,8 +106,12 @@ func (c *Client) getToken(scope string) string { } // callRegistry make an HTTP request to Docker registry. -func (c *Client) callRegistry(uri, scope string, manifest uint, delete bool) (string, gorequest.Response) { - acceptHeader := fmt.Sprintf("application/vnd.docker.distribution.manifest.v%d+json", manifest) +func (c *Client) callRegistry(uri, scope string, manifest uint, delete bool, list bool) (string, gorequest.Response) { + endpoint := "manifest" + if list { + endpoint = "manifest.list" + } + acceptHeader := fmt.Sprintf("application/vnd.docker.distribution.%s.v%d+json", endpoint, manifest) authHeader := "" if c.authURL != "" { authHeader = fmt.Sprintf("Bearer %s", c.getToken(scope)) @@ -179,7 +183,7 @@ func (c *Client) Repositories(useCache bool) map[string][]string { uri := "/v2/_catalog" c.repos = map[string][]string{} for { - data, resp := c.callRegistry(uri, scope, 2, false) + data, resp := c.callRegistry(uri, scope, 2, false, false) if data == "" { return c.repos } @@ -212,7 +216,7 @@ func (c *Client) Repositories(useCache bool) map[string][]string { // Tags get tags for the repo. func (c *Client) Tags(repo string) []string { scope := fmt.Sprintf("repository:%s:*", repo) - data, _ := c.callRegistry(fmt.Sprintf("/v2/%s/tags/list", repo), scope, 2, false) + data, _ := c.callRegistry(fmt.Sprintf("/v2/%s/tags/list", repo), scope, 2, false, false) var tags []string for _, t := range gjson.Get(data, "tags").Array() { tags = append(tags, t.String()) @@ -220,10 +224,17 @@ func (c *Client) Tags(repo string) []string { return tags } +// Manifests gets manifest list entries for a tag for the repo. +func (c *Client) Manifests(repo string, tag string) []gjson.Result { + scope := fmt.Sprintf("repository:%s:*", repo) + data, _ := c.callRegistry(fmt.Sprintf("/v2/%s/manifests/%s", repo, tag), scope, 2, false, true) + return gjson.Get(data, "manifests").Array() +} + // TagInfo get image info for the repo tag. func (c *Client) TagInfo(repo, tag string, v1only bool) (rsha256, rinfoV1, rinfoV2 string) { scope := fmt.Sprintf("repository:%s:*", repo) - infoV1, _ := c.callRegistry(fmt.Sprintf("/v2/%s/manifests/%s", repo, tag), scope, 1, false) + infoV1, _ := c.callRegistry(fmt.Sprintf("/v2/%s/manifests/%s", repo, tag), scope, 1, false, false) if infoV1 == "" { return "", "", "" } @@ -232,7 +243,7 @@ func (c *Client) TagInfo(repo, tag string, v1only bool) (rsha256, rinfoV1, rinfo return "", infoV1, "" } - infoV2, resp := c.callRegistry(fmt.Sprintf("/v2/%s/manifests/%s", repo, tag), scope, 2, false) + infoV2, resp := c.callRegistry(fmt.Sprintf("/v2/%s/manifests/%s", repo, tag), scope, 2, false, false) digest := resp.Header.Get("Docker-Content-Digest") if infoV2 == "" || digest == "" { return "", "", "" @@ -269,5 +280,5 @@ func (c *Client) CountTags(interval uint8) { // DeleteTag delete image tag. func (c *Client) DeleteTag(repo, tag string) { scope := fmt.Sprintf("repository:%s:*", repo) - c.callRegistry(fmt.Sprintf("/v2/%s/manifests/%s", repo, tag), scope, 2, true) + c.callRegistry(fmt.Sprintf("/v2/%s/manifests/%s", repo, tag), scope, 2, true, false) } diff --git a/templates/tag_info.html b/templates/tag_info.html index b5ad178..ad8efc4 100644 --- a/templates/tag_info.html +++ b/templates/tag_info.html @@ -17,15 +17,19 @@ Image Details + {{if not isDigest}} Image{{ registryHost }}/{{ repoPath }}:{{ tag }} + {{end}} sha256{{ sha256 }} + {{if not isDigest}} Created On{{ created|pretty_time }} + {{end}} Image Size{{ imageSize|pretty_size }} @@ -34,6 +38,29 @@ +{{if len(digests) != 0}} +

Manifest List v2

+ + + + + + + + + + +{{range index, manifest := digests}} + + + + + + + +{{end}} +
Manifest #DigestSizeArchitectureOS
{{ index+1 }}{{ manifest["digest"] }}{{ digestSizes[index]|pretty_size }}{{ manifest["architecture"] }}{{ manifest["os"] }}
+{{else}} {{if layersV2}}

Manifest v2

@@ -53,7 +80,9 @@ {{end}}
{{end}} +{{end}} +{{if not isDigest}}

Manifest v1

{{range index, layer := layersV1}} @@ -81,5 +110,6 @@ {{end}}
{{end}} +{{end}} {{end}}