mirror of
https://github.com/Quiq/docker-registry-ui.git
synced 2025-07-19 16:46:58 +00:00
Support V2 Manifest Lists
This commit is contained in:
parent
ee38e35ba6
commit
fa5ad6ac60
19
main.go
19
main.go
@ -243,6 +243,22 @@ func (a *apiClient) viewTagInfo(c echo.Context) error {
|
|||||||
layersCount = len(gjson.Get(infoV1, "fsLayers").Array())
|
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 := jet.VarMap{}
|
||||||
data.Set("namespace", namespace)
|
data.Set("namespace", namespace)
|
||||||
data.Set("repo", repo)
|
data.Set("repo", repo)
|
||||||
@ -254,6 +270,9 @@ func (a *apiClient) viewTagInfo(c echo.Context) error {
|
|||||||
data.Set("layersCount", layersCount)
|
data.Set("layersCount", layersCount)
|
||||||
data.Set("layersV2", layersV2)
|
data.Set("layersV2", layersV2)
|
||||||
data.Set("layersV1", layersV1)
|
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)
|
return c.Render(http.StatusOK, "tag_info.html", data)
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,12 @@ func (c *Client) getToken(scope string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// callRegistry make an HTTP request to Docker registry.
|
// callRegistry make an HTTP request to Docker registry.
|
||||||
func (c *Client) callRegistry(uri, scope string, manifest uint, delete bool) (string, gorequest.Response) {
|
func (c *Client) callRegistry(uri, scope string, manifest uint, delete bool, list bool) (string, gorequest.Response) {
|
||||||
acceptHeader := fmt.Sprintf("application/vnd.docker.distribution.manifest.v%d+json", manifest)
|
endpoint := "manifest"
|
||||||
|
if list {
|
||||||
|
endpoint = "manifest.list"
|
||||||
|
}
|
||||||
|
acceptHeader := fmt.Sprintf("application/vnd.docker.distribution.%s.v%d+json", endpoint, manifest)
|
||||||
authHeader := ""
|
authHeader := ""
|
||||||
if c.authURL != "" {
|
if c.authURL != "" {
|
||||||
authHeader = fmt.Sprintf("Bearer %s", c.getToken(scope))
|
authHeader = fmt.Sprintf("Bearer %s", c.getToken(scope))
|
||||||
@ -179,7 +183,7 @@ func (c *Client) Repositories(useCache bool) map[string][]string {
|
|||||||
uri := "/v2/_catalog"
|
uri := "/v2/_catalog"
|
||||||
c.repos = map[string][]string{}
|
c.repos = map[string][]string{}
|
||||||
for {
|
for {
|
||||||
data, resp := c.callRegistry(uri, scope, 2, false)
|
data, resp := c.callRegistry(uri, scope, 2, false, false)
|
||||||
if data == "" {
|
if data == "" {
|
||||||
return c.repos
|
return c.repos
|
||||||
}
|
}
|
||||||
@ -212,7 +216,7 @@ func (c *Client) Repositories(useCache bool) map[string][]string {
|
|||||||
// Tags get tags for the repo.
|
// Tags get tags for the repo.
|
||||||
func (c *Client) Tags(repo string) []string {
|
func (c *Client) Tags(repo string) []string {
|
||||||
scope := fmt.Sprintf("repository:%s:*", repo)
|
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
|
var tags []string
|
||||||
for _, t := range gjson.Get(data, "tags").Array() {
|
for _, t := range gjson.Get(data, "tags").Array() {
|
||||||
tags = append(tags, t.String())
|
tags = append(tags, t.String())
|
||||||
@ -220,10 +224,17 @@ func (c *Client) Tags(repo string) []string {
|
|||||||
return tags
|
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.
|
// TagInfo get image info for the repo tag.
|
||||||
func (c *Client) TagInfo(repo, tag string, v1only bool) (rsha256, rinfoV1, rinfoV2 string) {
|
func (c *Client) TagInfo(repo, tag string, v1only bool) (rsha256, rinfoV1, rinfoV2 string) {
|
||||||
scope := fmt.Sprintf("repository:%s:*", repo)
|
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 == "" {
|
if infoV1 == "" {
|
||||||
return "", "", ""
|
return "", "", ""
|
||||||
}
|
}
|
||||||
@ -232,7 +243,7 @@ func (c *Client) TagInfo(repo, tag string, v1only bool) (rsha256, rinfoV1, rinfo
|
|||||||
return "", infoV1, ""
|
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")
|
digest := resp.Header.Get("Docker-Content-Digest")
|
||||||
if infoV2 == "" || digest == "" {
|
if infoV2 == "" || digest == "" {
|
||||||
return "", "", ""
|
return "", "", ""
|
||||||
@ -269,5 +280,5 @@ func (c *Client) CountTags(interval uint8) {
|
|||||||
// DeleteTag delete image tag.
|
// DeleteTag delete image tag.
|
||||||
func (c *Client) DeleteTag(repo, tag string) {
|
func (c *Client) DeleteTag(repo, tag string) {
|
||||||
scope := fmt.Sprintf("repository:%s:*", repo)
|
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)
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,19 @@
|
|||||||
<th colspan="2">Image Details</th>
|
<th colspan="2">Image Details</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
{{if not isDigest}}
|
||||||
<tr>
|
<tr>
|
||||||
<td width="20%">Image</td><td>{{ registryHost }}/{{ repoPath }}:{{ tag }}</td>
|
<td width="20%">Image</td><td>{{ registryHost }}/{{ repoPath }}:{{ tag }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{{end}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>sha256</td><td>{{ sha256 }}</td>
|
<td>sha256</td><td>{{ sha256 }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{{if not isDigest}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>Created On</td><td>{{ created|pretty_time }}</td>
|
<td>Created On</td><td>{{ created|pretty_time }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{{end}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>Image Size</td><td>{{ imageSize|pretty_size }}</td>
|
<td>Image Size</td><td>{{ imageSize|pretty_size }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -34,6 +38,29 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
{{if len(digests) != 0}}
|
||||||
|
<h4>Manifest List v2</h4>
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead bgcolor="#ddd">
|
||||||
|
<tr>
|
||||||
|
<th>Manifest #</th>
|
||||||
|
<th>Digest</th>
|
||||||
|
<th>Size</th>
|
||||||
|
<th>Architecture</th>
|
||||||
|
<th>OS</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{{range index, manifest := digests}}
|
||||||
|
<tr>
|
||||||
|
<td>{{ index+1 }}</td>
|
||||||
|
<td><a href='{{ basePath }}/{{ namespace }}/{{ repo }}/{{ manifest["digest"] }}'>{{ manifest["digest"] }}</a></td>
|
||||||
|
<td>{{ digestSizes[index]|pretty_size }}</td>
|
||||||
|
<td>{{ manifest["architecture"] }}</td>
|
||||||
|
<td>{{ manifest["os"] }}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
{{if layersV2}}
|
{{if layersV2}}
|
||||||
<h4>Manifest v2</h4>
|
<h4>Manifest v2</h4>
|
||||||
<table class="table table-striped table-bordered">
|
<table class="table table-striped table-bordered">
|
||||||
@ -53,7 +80,9 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</table>
|
</table>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if not isDigest}}
|
||||||
<h4>Manifest v1</h4>
|
<h4>Manifest v1</h4>
|
||||||
{{range index, layer := layersV1}}
|
{{range index, layer := layersV1}}
|
||||||
<table class="table table-striped table-bordered">
|
<table class="table table-striped table-bordered">
|
||||||
@ -81,5 +110,6 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</table>
|
</table>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
Loading…
Reference in New Issue
Block a user