Support BuildX cache images & display extended attributes of manifests

This commit is contained in:
Christoph Honal 2020-02-16 13:51:15 +01:00
parent 0772c0258b
commit 82a72fe1b6
2 changed files with 50 additions and 32 deletions

36
main.go
View File

@ -210,9 +210,15 @@ func (a *apiClient) viewTagInfo(c echo.Context) error {
} }
sha256, infoV1, infoV2 := a.client.TagInfo(repoPath, tag, false) sha256, infoV1, infoV2 := a.client.TagInfo(repoPath, tag, false)
if infoV1 == "" || infoV2 == "" { manifests := a.client.Manifests(repoPath, tag)
if (infoV1 == "" || infoV2 == "") && len(manifests) == 0 {
return c.Redirect(http.StatusSeeOther, fmt.Sprintf("%s/%s/%s", a.config.BasePath, namespace, repo)) return c.Redirect(http.StatusSeeOther, fmt.Sprintf("%s/%s/%s", a.config.BasePath, namespace, repo))
} }
isListOnly := (infoV1 == "" && infoV2 == "")
newRepoPath := gjson.Get(infoV1, "name").String()
if newRepoPath != "" {
repoPath = newRepoPath
}
var imageSize int64 var imageSize int64
if gjson.Get(infoV2, "layers").Exists() { if gjson.Get(infoV2, "layers").Exists() {
@ -244,18 +250,20 @@ func (a *apiClient) viewTagInfo(c echo.Context) error {
} }
isDigest := strings.HasPrefix(tag, "sha256:") isDigest := strings.HasPrefix(tag, "sha256:")
var digests []map[string]gjson.Result var digests []map[string]interface{}
var digestSizes []int64 for _, s := range manifests {
for _, s := range a.client.Manifests(repoPath, tag) { r, _ := gjson.Parse(s.String()).Value().(map[string]interface{})
var r map[string]gjson.Result = s.Map() if s.Get("mediaType").String() == "application/vnd.docker.distribution.manifest.v2+json" {
r["architecture"] = s.Get("platform.architecture") _, _, dInfo := a.client.TagInfo(repoPath, s.Get("digest").String(), false)
r["os"] = s.Get("platform.os") var dSize int64
_, _, dInfo := a.client.TagInfo(repoPath, s.Get("digest").String(), false) for _, d := range gjson.Get(dInfo, "layers.#.size").Array() {
var dSize int64 dSize = dSize + d.Int()
for _, d := range gjson.Get(dInfo, "layers.#.size").Array() { }
dSize = dSize + d.Int() r["size"] = dSize
} else {
r["size"] = s.Get("size").Int()
} }
digestSizes = append(digestSizes, dSize) r["ordered_keys"] = registry.SortedMapKeys(r)
digests = append(digests, r) digests = append(digests, r)
} }
@ -265,14 +273,14 @@ func (a *apiClient) viewTagInfo(c echo.Context) error {
data.Set("sha256", sha256) data.Set("sha256", sha256)
data.Set("imageSize", imageSize) data.Set("imageSize", imageSize)
data.Set("tag", tag) data.Set("tag", tag)
data.Set("repoPath", gjson.Get(infoV1, "name").String()) data.Set("repoPath", repoPath)
data.Set("created", gjson.Get(gjson.Get(infoV1, "history.0.v1Compatibility").String(), "created").String()) data.Set("created", gjson.Get(gjson.Get(infoV1, "history.0.v1Compatibility").String(), "created").String())
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("isDigest", isDigest)
data.Set("isListOnly", isListOnly)
data.Set("digests", digests) 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)
} }

View File

@ -17,11 +17,10 @@
<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}} {{if not isListOnly}}
<tr> <tr>
<td>sha256</td><td>{{ sha256 }}</td> <td>sha256</td><td>{{ sha256 }}</td>
</tr> </tr>
@ -36,32 +35,44 @@
<tr> <tr>
<td>Layer Count</td><td>{{ layersCount }}</td> <td>Layer Count</td><td>{{ layersCount }}</td>
</tr> </tr>
{{end}}
</table> </table>
{{if len(digests) != 0}} {{if digests}}
<h4>Manifest List v2</h4> <h4>Manifest List v2</h4>
{{range index, manifest := digests}}
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead bgcolor="#ddd"> <thead bgcolor="#ddd">
<tr> <tr>
<th>Manifest #</th> <th colspan="2">Manifest #{{ index+1 }}</th>
<th>Digest</th>
<th>Size</th>
<th>Architecture</th>
<th>OS</th>
</tr> </tr>
</thead> </thead>
{{range index, manifest := digests}} {{range key := manifest["ordered_keys"]}}
<tr> <tr>
<td>{{ index+1 }}</td> <td width="20%">{{ key }}</td>
{{if key == "platform" || key == "annotations"}}
<td style="padding: 0">
<table class="table table-bordered" style="padding: 0; width: 100%; margin-bottom: 0; min-height: 37px">
<!-- Nested range does not work. Iterating via filter over the map. -->
{{ manifest[key]|parse_map|raw }}
</table>
</td>
{{else if key == "size"}}
<td>{{ manifest[key]|pretty_size }}</td>
{{else if key == "digest"}}
{{if not isListOnly}}
<td><a href='{{ basePath }}/{{ namespace }}/{{ repo }}/{{ manifest["digest"] }}'>{{ manifest["digest"] }}</a></td> <td><a href='{{ basePath }}/{{ namespace }}/{{ repo }}/{{ manifest["digest"] }}'>{{ manifest["digest"] }}</a></td>
<td>{{ digestSizes[index]|pretty_size }}</td> {{else}}
<td>{{ manifest["architecture"] }}</td> <td>{{ manifest["digest"] }}</td>
<td>{{ manifest["os"] }}</td> {{end}}
{{else}}
<td>{{ manifest[key] }}</td>
{{end}}
</tr> </tr>
{{end}} {{end}}
</table> </table>
{{else}} {{end}}
{{if layersV2}} {{else if layersV2}}
<h4>Manifest v2</h4> <h4>Manifest v2</h4>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead bgcolor="#ddd"> <thead bgcolor="#ddd">
@ -80,9 +91,8 @@
{{end}} {{end}}
</table> </table>
{{end}} {{end}}
{{end}}
{{if not isDigest}} {{if not isListOnly && 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">