aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--models/migrations/migrations.go2
-rw-r--r--models/migrations/v1_20/v250.go135
-rw-r--r--modules/packages/container/metadata.go8
-rw-r--r--modules/packages/container/metadata_test.go2
-rw-r--r--routers/api/packages/container/manifest.go21
-rw-r--r--templates/package/content/container.tmpl28
-rw-r--r--templates/package/view.tmpl2
-rw-r--r--tests/integration/api_packages_container_test.go22
8 files changed, 198 insertions, 22 deletions
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index bec406f7bf..ea3619db97 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -477,6 +477,8 @@ var migrations = []Migration{
NewMigration("Add version column to action_runner table", v1_20.AddVersionToActionRunner),
// v249 -> v250
NewMigration("Improve Action table indices v3", v1_20.ImproveActionTableIndices),
+ // v250 -> v251
+ NewMigration("Change Container Metadata", v1_20.ChangeContainerMetadataMultiArch),
}
// GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go
new file mode 100644
index 0000000000..e05646e5c6
--- /dev/null
+++ b/models/migrations/v1_20/v250.go
@@ -0,0 +1,135 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_20 //nolint
+
+import (
+ "strings"
+
+ "code.gitea.io/gitea/modules/json"
+
+ "xorm.io/xorm"
+)
+
+func ChangeContainerMetadataMultiArch(x *xorm.Engine) error {
+ sess := x.NewSession()
+ defer sess.Close()
+
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ type PackageVersion struct {
+ ID int64 `xorm:"pk"`
+ MetadataJSON string `xorm:"metadata_json"`
+ }
+
+ type PackageBlob struct{}
+
+ // Get all relevant packages (manifest list images have a container.manifest.reference property)
+
+ var pvs []*PackageVersion
+ err := sess.
+ Table("package_version").
+ Select("id, metadata_json").
+ Where("id IN (SELECT DISTINCT ref_id FROM package_property WHERE ref_type = 0 AND name = 'container.manifest.reference')").
+ Find(&pvs)
+ if err != nil {
+ return err
+ }
+
+ type MetadataOld struct {
+ Type string `json:"type"`
+ IsTagged bool `json:"is_tagged"`
+ Platform string `json:"platform,omitempty"`
+ Description string `json:"description,omitempty"`
+ Authors []string `json:"authors,omitempty"`
+ Licenses string `json:"license,omitempty"`
+ ProjectURL string `json:"project_url,omitempty"`
+ RepositoryURL string `json:"repository_url,omitempty"`
+ DocumentationURL string `json:"documentation_url,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
+ ImageLayers []string `json:"layer_creation,omitempty"`
+ MultiArch map[string]string `json:"multiarch,omitempty"`
+ }
+
+ type Manifest struct {
+ Platform string `json:"platform"`
+ Digest string `json:"digest"`
+ Size int64 `json:"size"`
+ }
+
+ type MetadataNew struct {
+ Type string `json:"type"`
+ IsTagged bool `json:"is_tagged"`
+ Platform string `json:"platform,omitempty"`
+ Description string `json:"description,omitempty"`
+ Authors []string `json:"authors,omitempty"`
+ Licenses string `json:"license,omitempty"`
+ ProjectURL string `json:"project_url,omitempty"`
+ RepositoryURL string `json:"repository_url,omitempty"`
+ DocumentationURL string `json:"documentation_url,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
+ ImageLayers []string `json:"layer_creation,omitempty"`
+ Manifests []*Manifest `json:"manifests,omitempty"`
+ }
+
+ for _, pv := range pvs {
+ var old *MetadataOld
+ if err := json.Unmarshal([]byte(pv.MetadataJSON), &old); err != nil {
+ return err
+ }
+
+ // Calculate the size of every contained manifest
+
+ manifests := make([]*Manifest, 0, len(old.MultiArch))
+ for platform, digest := range old.MultiArch {
+ size, err := sess.
+ Table("package_blob").
+ Join("INNER", "package_file", "package_blob.id = package_file.blob_id").
+ Join("INNER", "package_version pv", "pv.id = package_file.version_id").
+ Join("INNER", "package_version pv2", "pv2.package_id = pv.package_id").
+ Where("pv.lower_version = ? AND pv2.id = ?", strings.ToLower(digest), pv.ID).
+ SumInt(new(PackageBlob), "size")
+ if err != nil {
+ return err
+ }
+
+ manifests = append(manifests, &Manifest{
+ Platform: platform,
+ Digest: digest,
+ Size: size,
+ })
+ }
+
+ // Convert to new metadata format
+
+ new := &MetadataNew{
+ Type: old.Type,
+ IsTagged: old.IsTagged,
+ Platform: old.Platform,
+ Description: old.Description,
+ Authors: old.Authors,
+ Licenses: old.Licenses,
+ ProjectURL: old.ProjectURL,
+ RepositoryURL: old.RepositoryURL,
+ DocumentationURL: old.DocumentationURL,
+ Labels: old.Labels,
+ ImageLayers: old.ImageLayers,
+ Manifests: manifests,
+ }
+
+ metadataJSON, err := json.Marshal(new)
+ if err != nil {
+ return err
+ }
+
+ pv.MetadataJSON = string(metadataJSON)
+
+ if _, err := sess.ID(pv.ID).Update(pv); err != nil {
+ return err
+ }
+ }
+
+ return sess.Commit()
+}
diff --git a/modules/packages/container/metadata.go b/modules/packages/container/metadata.go
index 6f62ab6a54..2a41fb9105 100644
--- a/modules/packages/container/metadata.go
+++ b/modules/packages/container/metadata.go
@@ -62,7 +62,13 @@ type Metadata struct {
DocumentationURL string `json:"documentation_url,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
ImageLayers []string `json:"layer_creation,omitempty"`
- MultiArch map[string]string `json:"multiarch,omitempty"`
+ Manifests []*Manifest `json:"manifests,omitempty"`
+}
+
+type Manifest struct {
+ Platform string `json:"platform"`
+ Digest string `json:"digest"`
+ Size int64 `json:"size"`
}
// ParseImageConfig parses the metadata of an image config
diff --git a/modules/packages/container/metadata_test.go b/modules/packages/container/metadata_test.go
index 5d8d3abfae..48809f4c99 100644
--- a/modules/packages/container/metadata_test.go
+++ b/modules/packages/container/metadata_test.go
@@ -46,7 +46,7 @@ func TestParseImageConfig(t *testing.T) {
},
metadata.Labels,
)
- assert.Empty(t, metadata.MultiArch)
+ assert.Empty(t, metadata.Manifests)
configHelm := `{"description":"` + description + `", "home": "` + projectURL + `", "sources": ["` + repositoryURL + `"], "maintainers":[{"name":"` + author + `"}]}`
diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go
index e36c6a851b..1dbd058d6b 100644
--- a/routers/api/packages/container/manifest.go
+++ b/routers/api/packages/container/manifest.go
@@ -217,7 +217,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
metadata := &container_module.Metadata{
Type: container_module.TypeOCI,
- MultiArch: make(map[string]string),
+ Manifests: make([]*container_module.Manifest, 0, len(index.Manifests)),
}
for _, manifest := range index.Manifests {
@@ -233,7 +233,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
}
}
- _, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
+ pfd, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
OwnerID: mci.Owner.ID,
Image: mci.Image,
Digest: string(manifest.Digest),
@@ -246,7 +246,18 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
return err
}
- metadata.MultiArch[platform] = string(manifest.Digest)
+ size, err := packages_model.CalculateFileSize(ctx, &packages_model.PackageFileSearchOptions{
+ VersionID: pfd.File.VersionID,
+ })
+ if err != nil {
+ return err
+ }
+
+ metadata.Manifests = append(metadata.Manifests, &container_module.Manifest{
+ Platform: platform,
+ Digest: string(manifest.Digest),
+ Size: size,
+ })
}
pv, err := createPackageAndVersion(ctx, mci, metadata)
@@ -369,8 +380,8 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
return nil, err
}
}
- for _, digest := range metadata.MultiArch {
- if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, digest); err != nil {
+ for _, manifest := range metadata.Manifests {
+ if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
log.Error("Error setting package version property: %v", err)
return nil, err
}
diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl
index 0bf749cd70..78c9434386 100644
--- a/templates/package/content/container.tmpl
+++ b/templates/package/content/container.tmpl
@@ -23,19 +23,27 @@
</div>
</div>
</div>
- {{if .PackageDescriptor.Metadata.MultiArch}}
+ {{if .PackageDescriptor.Metadata.Manifests}}
<h4 class="ui top attached header">{{.locale.Tr "packages.container.multi_arch"}}</h4>
<div class="ui attached segment">
- <div class="ui form">
- {{range $arch, $digest := .PackageDescriptor.Metadata.MultiArch}}
- <div class="field">
- <label>{{svg "octicon-terminal"}} {{$arch}}</label>
- {{if eq $.PackageDescriptor.Metadata.Type "oci"}}
- <div class="markup"><pre class="code-block"><code>docker pull {{$.RegistryHost}}/{{$.PackageDescriptor.Owner.LowerName}}/{{$.PackageDescriptor.Package.LowerName}}@{{$digest}}</code></pre></div>
+ <table class="ui very basic compact table">
+ <thead>
+ <tr>
+ <th>{{.locale.Tr "packages.container.digest"}}</th>
+ <th>{{.locale.Tr "packages.container.multi_arch"}}</th>
+ <th>{{.locale.Tr "admin.packages.size"}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range .PackageDescriptor.Metadata.Manifests}}
+ <tr>
+ <td><a href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .Digest}}">{{.Digest}}</a></td>
+ <td>{{.Platform}}</td>
+ <td>{{FileSize .Size}}</td>
+ </tr>
{{end}}
- </div>
- {{end}}
- </div>
+ </tbody>
+ </table>
</div>
{{end}}
{{if .PackageDescriptor.Metadata.Description}}
diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl
index b2a2fb1e5d..beadcf5c1e 100644
--- a/templates/package/view.tmpl
+++ b/templates/package/view.tmpl
@@ -62,7 +62,9 @@
{{template "package/metadata/rubygems" .}}
{{template "package/metadata/swift" .}}
{{template "package/metadata/vagrant" .}}
+ {{if not (and (eq .PackageDescriptor.Package.Type "container") .PackageDescriptor.Metadata.Manifests)}}
<div class="item">{{svg "octicon-database" 16 "gt-mr-3"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}</div>
+ {{end}}
</div>
{{if not (eq .PackageDescriptor.Package.Type "container")}}
<div class="ui divider"></div>
diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go
index d925fd1647..fe9208bb05 100644
--- a/tests/integration/api_packages_container_test.go
+++ b/tests/integration/api_packages_container_test.go
@@ -321,7 +321,7 @@ func TestPackageContainer(t *testing.T) {
metadata := pd.Metadata.(*container_module.Metadata)
assert.Equal(t, container_module.TypeOCI, metadata.Type)
assert.Len(t, metadata.ImageLayers, 2)
- assert.Empty(t, metadata.MultiArch)
+ assert.Empty(t, metadata.Manifests)
assert.Len(t, pd.Files, 3)
for _, pfd := range pd.Files {
@@ -462,10 +462,22 @@ func TestPackageContainer(t *testing.T) {
assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
metadata := pd.Metadata.(*container_module.Metadata)
assert.Equal(t, container_module.TypeOCI, metadata.Type)
- assert.Contains(t, metadata.MultiArch, "linux/arm/v7")
- assert.Equal(t, manifestDigest, metadata.MultiArch["linux/arm/v7"])
- assert.Contains(t, metadata.MultiArch, "linux/arm64/v8")
- assert.Equal(t, untaggedManifestDigest, metadata.MultiArch["linux/arm64/v8"])
+ assert.Len(t, metadata.Manifests, 2)
+ assert.Condition(t, func() bool {
+ for _, m := range metadata.Manifests {
+ switch m.Platform {
+ case "linux/arm/v7":
+ assert.Equal(t, manifestDigest, m.Digest)
+ assert.EqualValues(t, 1524, m.Size)
+ case "linux/arm64/v8":
+ assert.Equal(t, untaggedManifestDigest, m.Digest)
+ assert.EqualValues(t, 1514, m.Size)
+ default:
+ return false
+ }
+ }
+ return true
+ })
assert.Len(t, pd.Files, 1)
assert.True(t, pd.Files[0].File.IsLead)