mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-12-07 14:09:47 +00:00
fix: reduce runtime of container cleanup by relying on mass digest cleanup (#10297)
The package cleanup routine checks every container version for whether it is referenced by a multi-platform manifest, which appears to be a performance problem indicated by CPU profiling collected in #9358 on SQLite systems. This PR removes that check completely, which isn't necessary since #4698 added a much more performant mass-cleanup of these dangling platform versions. May fix #9358 completely, but it leaves fundamental scalability concerns with SQLite due to long-running transactions. The transactions will be shorter with this change. Requires end-user testing to confirm if sufficiently fixed. ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title. <!--start release-notes-assistant--> ## Release notes <!--URL:https://codeberg.org/forgejo/forgejo--> - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/10297): <!--number 10297 --><!--line 0 --><!--description cmVkdWNlIHJ1bnRpbWUgb2YgY29udGFpbmVyIGNsZWFudXAgYnkgcmVseWluZyBvbiBtYXNzIGRpZ2VzdCBjbGVhbnVw-->reduce runtime of container cleanup by relying on mass digest cleanup<!--description--> <!--end release-notes-assistant--> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10297 Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net> Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
This commit is contained in:
parent
d5fa12ded1
commit
ac487e93d9
10 changed files with 506 additions and 141 deletions
|
|
@ -7,20 +7,18 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
packages_model "forgejo.org/models/packages"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
user_model "forgejo.org/models/user"
|
||||
"forgejo.org/modules/base"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/optional"
|
||||
"forgejo.org/modules/util"
|
||||
"forgejo.org/modules/web"
|
||||
"forgejo.org/services/context"
|
||||
"forgejo.org/services/forms"
|
||||
cargo_service "forgejo.org/services/packages/cargo"
|
||||
container_service "forgejo.org/services/packages/container"
|
||||
cleanup_service "forgejo.org/services/packages/cleanup"
|
||||
)
|
||||
|
||||
func SetPackagesContext(ctx *context.Context, owner *user_model.User) {
|
||||
|
|
@ -147,66 +145,18 @@ func SetRulePreviewContext(ctx *context.Context, owner *user_model.User) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := pcr.CompiledPattern(); err != nil {
|
||||
ctx.ServerError("CompiledPattern", err)
|
||||
return
|
||||
}
|
||||
|
||||
olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
|
||||
|
||||
packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
|
||||
versionsToRemove, err := cleanup_service.GetCleanupTargets(ctx, pcr, false)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPackagesByType", err)
|
||||
ctx.ServerError("GetCleanupTargets", err)
|
||||
return
|
||||
}
|
||||
|
||||
versionsToRemove := make([]*packages_model.PackageDescriptor, 0, 10)
|
||||
|
||||
for _, p := range packages {
|
||||
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
||||
PackageID: p.ID,
|
||||
IsInternal: optional.Some(false),
|
||||
Sort: packages_model.SortCreatedDesc,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchVersions", err)
|
||||
return
|
||||
}
|
||||
keep := min(len(pvs), pcr.KeepCount)
|
||||
for _, pv := range pvs[keep:] {
|
||||
if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
|
||||
ctx.ServerError("ShouldBeSkipped", err)
|
||||
return
|
||||
} else if skip {
|
||||
continue
|
||||
}
|
||||
|
||||
toMatch := pv.LowerVersion
|
||||
if pcr.MatchFullName {
|
||||
toMatch = p.LowerName + "/" + pv.LowerVersion
|
||||
}
|
||||
|
||||
if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
|
||||
continue
|
||||
}
|
||||
if pv.CreatedUnix.AsLocalTime().After(olderThan) {
|
||||
continue
|
||||
}
|
||||
if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
|
||||
continue
|
||||
}
|
||||
|
||||
pd, err := packages_model.GetPackageDescriptor(ctx, pv)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPackageDescriptor", err)
|
||||
return
|
||||
}
|
||||
versionsToRemove = append(versionsToRemove, pd)
|
||||
}
|
||||
packageDescriptors := make([]*packages_model.PackageDescriptor, len(versionsToRemove))
|
||||
for i := range len(versionsToRemove) {
|
||||
packageDescriptors[i] = versionsToRemove[i].PackageDescriptor
|
||||
}
|
||||
|
||||
ctx.Data["CleanupRule"] = pcr
|
||||
ctx.Data["VersionsToRemove"] = versionsToRemove
|
||||
ctx.Data["VersionsToRemove"] = packageDescriptors
|
||||
}
|
||||
|
||||
func getCleanupRuleByContext(ctx *context.Context, owner *user_model.User) *packages_model.PackageCleanupRule {
|
||||
|
|
|
|||
|
|
@ -47,79 +47,32 @@ func ExecuteCleanupRules(outerCtx context.Context) error {
|
|||
default:
|
||||
}
|
||||
|
||||
if err := pcr.CompiledPattern(); err != nil {
|
||||
return fmt.Errorf("CleanupRule [%d]: CompilePattern failed: %w", pcr.ID, err)
|
||||
}
|
||||
|
||||
olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
|
||||
|
||||
packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
|
||||
versionsToRemove, err := GetCleanupTargets(ctx, pcr, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CleanupRule [%d]: GetPackagesByType failed: %w", pcr.ID, err)
|
||||
return fmt.Errorf("CleanupRule [%d]: GetCleanupTargets failed: %w", pcr.ID, err)
|
||||
}
|
||||
|
||||
anyVersionDeleted := false
|
||||
for _, p := range packages {
|
||||
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
||||
PackageID: p.ID,
|
||||
IsInternal: optional.Some(false),
|
||||
Sort: packages_model.SortCreatedDesc,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("CleanupRule [%d]: SearchVersions failed: %w", pcr.ID, err)
|
||||
}
|
||||
versionDeleted := false
|
||||
keep := min(len(pvs), pcr.KeepCount)
|
||||
for _, pv := range pvs[keep:] {
|
||||
if pcr.Type == packages_model.TypeContainer {
|
||||
if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
|
||||
return fmt.Errorf("CleanupRule [%d]: container.ShouldBeSkipped failed: %w", pcr.ID, err)
|
||||
} else if skip {
|
||||
log.Debug("Rule[%d]: keep '%s/%s' (container)", pcr.ID, p.Name, pv.Version)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
toMatch := pv.LowerVersion
|
||||
if pcr.MatchFullName {
|
||||
toMatch = p.LowerName + "/" + pv.LowerVersion
|
||||
}
|
||||
|
||||
if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
|
||||
log.Debug("Rule[%d]: keep '%s/%s' (keep pattern)", pcr.ID, p.Name, pv.Version)
|
||||
continue
|
||||
}
|
||||
if pv.CreatedUnix.AsLocalTime().After(olderThan) {
|
||||
log.Debug("Rule[%d]: keep '%s/%s' (remove days)", pcr.ID, p.Name, pv.Version)
|
||||
continue
|
||||
}
|
||||
if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
|
||||
log.Debug("Rule[%d]: keep '%s/%s' (remove pattern)", pcr.ID, p.Name, pv.Version)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("Rule[%d]: remove '%s/%s'", pcr.ID, p.Name, pv.Version)
|
||||
|
||||
if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
|
||||
packageWithVersionDeleted := make(map[int64]bool) // set of Package.ID's where at least one package version was removed
|
||||
for _, ct := range versionsToRemove {
|
||||
if err := packages_service.DeletePackageVersionAndReferences(ctx, ct.PackageVersion); err != nil {
|
||||
return fmt.Errorf("CleanupRule [%d]: DeletePackageVersionAndReferences failed: %w", pcr.ID, err)
|
||||
}
|
||||
|
||||
versionDeleted = true
|
||||
packageWithVersionDeleted[ct.Package.ID] = true
|
||||
anyVersionDeleted = true
|
||||
}
|
||||
|
||||
if versionDeleted {
|
||||
if pcr.Type == packages_model.TypeCargo {
|
||||
for packageID := range packageWithVersionDeleted {
|
||||
owner, err := user_model.GetUserByID(ctx, pcr.OwnerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUserByID failed: %w", err)
|
||||
}
|
||||
if err := cargo_service.UpdatePackageIndexIfExists(ctx, owner, owner, p.ID); err != nil {
|
||||
if err := cargo_service.UpdatePackageIndexIfExists(ctx, owner, owner, packageID); err != nil {
|
||||
return fmt.Errorf("CleanupRule [%d]: cargo.UpdatePackageIndexIfExists failed: %w", pcr.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if anyVersionDeleted {
|
||||
switch pcr.Type {
|
||||
|
|
@ -154,6 +107,83 @@ func ExecuteCleanupRules(outerCtx context.Context) error {
|
|||
return committer.Commit()
|
||||
}
|
||||
|
||||
type CleanupTarget struct {
|
||||
Package *packages_model.Package
|
||||
PackageVersion *packages_model.PackageVersion
|
||||
PackageDescriptor *packages_model.PackageDescriptor
|
||||
}
|
||||
|
||||
func GetCleanupTargets(ctx context.Context, pcr *packages_model.PackageCleanupRule, skipPackageDescriptor bool) ([]*CleanupTarget, error) {
|
||||
if err := pcr.CompiledPattern(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
|
||||
|
||||
packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure to GetPackagesByType for package cleanup rule: %w", err)
|
||||
}
|
||||
|
||||
versionsToRemove := make([]*CleanupTarget, 0, 10)
|
||||
|
||||
for _, p := range packages {
|
||||
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
||||
PackageID: p.ID,
|
||||
IsInternal: optional.Some(false),
|
||||
Sort: packages_model.SortCreatedDesc,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure to SearchVersions for package cleanup rule: %w", err)
|
||||
}
|
||||
keep := min(len(pvs), pcr.KeepCount)
|
||||
for _, pv := range pvs[keep:] {
|
||||
if pcr.Type == packages_model.TypeContainer {
|
||||
if skip := container_service.ShouldBeSkipped(pv); skip {
|
||||
log.Debug("Rule[%d]: keep '%s/%s' (container)", pcr.ID, p.Name, pv.Version)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
toMatch := pv.LowerVersion
|
||||
if pcr.MatchFullName {
|
||||
toMatch = p.LowerName + "/" + pv.LowerVersion
|
||||
}
|
||||
|
||||
if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
|
||||
log.Debug("Rule[%d]: keep '%s/%s' (keep pattern)", pcr.ID, p.Name, pv.Version)
|
||||
continue
|
||||
}
|
||||
if pv.CreatedUnix.AsLocalTime().After(olderThan) {
|
||||
log.Debug("Rule[%d]: keep '%s/%s' (remove days)", pcr.ID, p.Name, pv.Version)
|
||||
continue
|
||||
}
|
||||
if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
|
||||
log.Debug("Rule[%d]: keep '%s/%s' (remove pattern)", pcr.ID, p.Name, pv.Version)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("Rule[%d]: remove '%s/%s'", pcr.ID, p.Name, pv.Version)
|
||||
|
||||
var pd *packages_model.PackageDescriptor
|
||||
// GetPackageDescriptor is a bit expensive and can be skipped; only used for cleanup preview to display the package to the UI
|
||||
if !skipPackageDescriptor {
|
||||
pd, err = packages_model.GetPackageDescriptor(ctx, pv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure to GetPackageDescriptor for package cleanup rule: %w", err)
|
||||
}
|
||||
}
|
||||
versionsToRemove = append(versionsToRemove, &CleanupTarget{
|
||||
Package: p,
|
||||
PackageVersion: pv,
|
||||
PackageDescriptor: pd,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return versionsToRemove, nil
|
||||
}
|
||||
|
||||
func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error {
|
||||
ctx, committer, err := db.TxContext(outerCtx)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
packages_model "forgejo.org/models/packages"
|
||||
container_model "forgejo.org/models/packages/container"
|
||||
"forgejo.org/modules/optional"
|
||||
container_module "forgejo.org/modules/packages/container"
|
||||
packages_service "forgejo.org/services/packages"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
|
|
@ -82,30 +81,18 @@ func cleanupExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) e
|
|||
return nil
|
||||
}
|
||||
|
||||
func ShouldBeSkipped(ctx context.Context, pcr *packages_model.PackageCleanupRule, p *packages_model.Package, pv *packages_model.PackageVersion) (bool, error) {
|
||||
func ShouldBeSkipped(pv *packages_model.PackageVersion) bool {
|
||||
// Always skip the "latest" tag
|
||||
if pv.LowerVersion == "latest" {
|
||||
return true, nil
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the version is a digest (or untagged)
|
||||
if digest.Digest(pv.LowerVersion).Validate() == nil {
|
||||
// Check if there is another manifest referencing this version
|
||||
has, err := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{
|
||||
PackageID: p.ID,
|
||||
Properties: map[string]string{
|
||||
container_module.PropertyManifestReference: pv.LowerVersion,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
// Don't apply PackageCleanupRules to image versions that aren't tags; rely on `CleanupSHA256` to do mass
|
||||
// cleanup of them once they're dangling due to not being referenced.
|
||||
return true
|
||||
}
|
||||
|
||||
// Skip it if the version is referenced
|
||||
if has {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
42
services/packages/container/cleanup_test.go
Normal file
42
services/packages/container/cleanup_test.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packages_model "forgejo.org/models/packages"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShouldBeSkipped(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pv *packages_model.PackageVersion
|
||||
skipped bool
|
||||
}{
|
||||
{
|
||||
name: "latest: never cleaned up",
|
||||
pv: &packages_model.PackageVersion{LowerVersion: "latest"},
|
||||
skipped: true,
|
||||
},
|
||||
{
|
||||
name: "sha256:* never cleaned up",
|
||||
pv: &packages_model.PackageVersion{LowerVersion: "sha256:8a4d01effd20bcc5c65857885013efdbbdd466ac0a5a26b3fac573095533e3fc"},
|
||||
skipped: true,
|
||||
},
|
||||
{
|
||||
name: "sha256:* never cleaned up",
|
||||
pv: &packages_model.PackageVersion{LowerVersion: "v1.0.0"},
|
||||
skipped: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
skipped := ShouldBeSkipped(tt.pv)
|
||||
assert.Equal(t, tt.skipped, skipped)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPackagesContainerCleanupSHA256(t *testing.T) {
|
||||
func TestPackageContainerCleanupSHA256(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t, 1)()
|
||||
defer test.MockVariableValue(&setting.Packages.Storage.Type, setting.LocalStorageType)()
|
||||
defer test.MockVariableValue(&packages_container.SHA256BatchSize, 1)()
|
||||
|
|
|
|||
101
tests/integration/api_packages_container_cleanup_test.go
Normal file
101
tests/integration/api_packages_container_cleanup_test.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
packages_model "forgejo.org/models/packages"
|
||||
"forgejo.org/models/unittest"
|
||||
packages_service "forgejo.org/services/packages"
|
||||
packages_cleanup "forgejo.org/services/packages/cleanup"
|
||||
"forgejo.org/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPackageContainerCleanup(t *testing.T) {
|
||||
// Test fixture data contains three images; one that is a standard single-platform image (v2.0), and two that are a
|
||||
// manifest multi-platform image (v1.0).
|
||||
//
|
||||
// The goal of testing here is to ensure that package cleanup can only remove the tags, and leave the `sha256:*`
|
||||
// versions for cleanup once they're dangling by having no tags referencing them.
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
cleanupRule *packages_model.PackageCleanupRule
|
||||
expectedCleanupVersion []string
|
||||
expectedRemainingVersion []string
|
||||
}{
|
||||
{
|
||||
name: "keep single-platform",
|
||||
cleanupRule: &packages_model.PackageCleanupRule{
|
||||
KeepCount: 1,
|
||||
},
|
||||
expectedCleanupVersion: []string{"v1.0"},
|
||||
expectedRemainingVersion: []string{"v2.0"},
|
||||
},
|
||||
{
|
||||
name: "keep multi-platform",
|
||||
cleanupRule: &packages_model.PackageCleanupRule{
|
||||
RemovePattern: "v2\\.0",
|
||||
},
|
||||
expectedCleanupVersion: []string{"v2.0"},
|
||||
expectedRemainingVersion: []string{
|
||||
"v1.0",
|
||||
"sha256:4759bcf56210784f3b2c2b7438a43a5771c5eda61a11825c80f7b32143ac9c12", // linux/arm64/v8 version of v1.0
|
||||
"sha256:a972a01ff13b1cb6a9c49b3c34a283b67b75f441b8ff17ad4833a472f7b0fe08", // linux/amd64 version of v1.0
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("tests/integration/fixtures/TestPackageContainerCleanup")()
|
||||
defer tests.PrepareTestEnvWithPackageData(t)()
|
||||
|
||||
// Reduce repetition in the test case array
|
||||
tt.cleanupRule.Type = packages_model.TypeContainer
|
||||
tt.cleanupRule.OwnerID = 1
|
||||
|
||||
targets, err := packages_cleanup.GetCleanupTargets(t.Context(), tt.cleanupRule, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify we have the expected targets
|
||||
targetVersions := make([]string, len(targets))
|
||||
for i := range targets {
|
||||
targetVersions[i] = targets[i].PackageVersion.LowerVersion
|
||||
}
|
||||
assert.Len(t, targetVersions, len(tt.expectedCleanupVersion))
|
||||
for _, expected := range tt.expectedCleanupVersion {
|
||||
assert.Containsf(t, targetVersions, expected, "expected to cleanup %s, but didn't", expected)
|
||||
}
|
||||
|
||||
// Delete the target packages
|
||||
for _, ct := range targets {
|
||||
err := packages_service.DeletePackageVersionAndReferences(t.Context(), ct.PackageVersion)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Perform the sha256 cleanup routine
|
||||
require.NoError(t, packages_cleanup.CleanupExpiredData(t.Context(), -1*time.Hour))
|
||||
|
||||
// Verify the remaining versions are correct
|
||||
remainingVersions, _, err := packages_model.SearchVersions(t.Context(), &packages_model.PackageSearchOptions{
|
||||
OwnerID: tt.cleanupRule.OwnerID,
|
||||
Type: tt.cleanupRule.Type,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
remainingVersionsStr := make([]string, len(remainingVersions))
|
||||
for i := range remainingVersions {
|
||||
remainingVersionsStr[i] = remainingVersions[i].LowerVersion
|
||||
}
|
||||
assert.Len(t, remainingVersionsStr, len(tt.expectedRemainingVersion))
|
||||
for _, expected := range tt.expectedRemainingVersion {
|
||||
assert.Contains(t, remainingVersionsStr, expected, "expected to find remaining version %s, but didn't", expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
- id: 7
|
||||
owner_id: 1
|
||||
repo_id: 0
|
||||
type: container
|
||||
name: forgejo-runner-test
|
||||
lower_name: forgejo-runner-test
|
||||
semver_compatible: false
|
||||
is_internal: false
|
||||
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
- id: 4090
|
||||
version_id: 736
|
||||
blob_id: 1214
|
||||
name: sha256_efc44d65797de15fdc9787e194f9e24ea42adc2fa2821203bb65ead939908375
|
||||
lower_name: sha256_efc44d65797de15fdc9787e194f9e24ea42adc2fa2821203bb65ead939908375
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764694509
|
||||
- id: 4091
|
||||
version_id: 736
|
||||
blob_id: 222
|
||||
name: sha256_63d36825ec9a6be406f73910fa6f0d6542a7b993a750aac151f30a8e1fd58cf7
|
||||
lower_name: sha256_63d36825ec9a6be406f73910fa6f0d6542a7b993a750aac151f30a8e1fd58cf7
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764694509
|
||||
- id: 4092
|
||||
version_id: 736
|
||||
blob_id: 1212
|
||||
name: sha256_9a0d1f70c6da347f1fa0c3856bbe2642a317361a54ca8b0e44b64c362b45e01d
|
||||
lower_name: sha256_9a0d1f70c6da347f1fa0c3856bbe2642a317361a54ca8b0e44b64c362b45e01d
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764694509
|
||||
- id: 4093
|
||||
version_id: 736
|
||||
blob_id: 1213
|
||||
name: sha256_5a0f445468c4d9b40c4efb4bb292967672f5e9a2dd23e605a34fe10898e4cc4e
|
||||
lower_name: sha256_5a0f445468c4d9b40c4efb4bb292967672f5e9a2dd23e605a34fe10898e4cc4e
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764694509
|
||||
- id: 4094
|
||||
version_id: 736
|
||||
blob_id: 1215
|
||||
name: manifest.json
|
||||
lower_name: manifest.json
|
||||
composite_key: ''
|
||||
is_lead: true
|
||||
created_unix: 1764694509
|
||||
- id: 4099
|
||||
version_id: 737
|
||||
blob_id: 1219
|
||||
name: sha256_495a889816ababcd9e1af0ed66ccc55c7225c6fcf171a7eac3f24ee62fa30f74
|
||||
lower_name: sha256_495a889816ababcd9e1af0ed66ccc55c7225c6fcf171a7eac3f24ee62fa30f74
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764694509
|
||||
- id: 4100
|
||||
version_id: 737
|
||||
blob_id: 1216
|
||||
name: sha256_5096682701dd58e54ccbeb5d1fa285e9980a86568cfb0ca522fd4871892ab318
|
||||
lower_name: sha256_5096682701dd58e54ccbeb5d1fa285e9980a86568cfb0ca522fd4871892ab318
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764694509
|
||||
- id: 4101
|
||||
version_id: 737
|
||||
blob_id: 1218
|
||||
name: sha256_714573ef664bd4969f0cb9b445efbb337f0d2f52d906f3f1323063ca9bdcb6a6
|
||||
lower_name: sha256_714573ef664bd4969f0cb9b445efbb337f0d2f52d906f3f1323063ca9bdcb6a6
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764694509
|
||||
- id: 4102
|
||||
version_id: 737
|
||||
blob_id: 1217
|
||||
name: sha256_3dbb52544aa310f7819092b6c556aa1fb446e2c77711eda8e4680851445f8f18
|
||||
lower_name: sha256_3dbb52544aa310f7819092b6c556aa1fb446e2c77711eda8e4680851445f8f18
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764694509
|
||||
- id: 4103
|
||||
version_id: 737
|
||||
blob_id: 1220
|
||||
name: manifest.json
|
||||
lower_name: manifest.json
|
||||
composite_key: ''
|
||||
is_lead: true
|
||||
created_unix: 1764694509
|
||||
- id: 4104
|
||||
version_id: 738
|
||||
blob_id: 1221
|
||||
name: manifest.json
|
||||
lower_name: manifest.json
|
||||
composite_key: ''
|
||||
is_lead: true
|
||||
created_unix: 1764694509
|
||||
- id: 4115
|
||||
version_id: 740
|
||||
blob_id: 2114
|
||||
name: sha256_dd4d2c29faafea81b0b44cf8df13b8d0e01bc42059cdb69bf8f5d147b29904bf
|
||||
lower_name: sha256_dd4d2c29faafea81b0b44cf8df13b8d0e01bc42059cdb69bf8f5d147b29904bf
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764695172
|
||||
- id: 4116
|
||||
version_id: 740
|
||||
blob_id: 222
|
||||
name: sha256_63d36825ec9a6be406f73910fa6f0d6542a7b993a750aac151f30a8e1fd58cf7
|
||||
lower_name: sha256_63d36825ec9a6be406f73910fa6f0d6542a7b993a750aac151f30a8e1fd58cf7
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764695172
|
||||
- id: 4117
|
||||
version_id: 740
|
||||
blob_id: 1222
|
||||
name: sha256_9c3b61a2981c0986b91339acca08bff16acc239d1909037652d6f439d3e84578
|
||||
lower_name: sha256_9c3b61a2981c0986b91339acca08bff16acc239d1909037652d6f439d3e84578
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764695172
|
||||
- id: 4118
|
||||
version_id: 740
|
||||
blob_id: 2113
|
||||
name: sha256_bbd073df829f756775f9e7143f99bd5a0637a4be4ba95280a496c732e9ed45ea
|
||||
lower_name: sha256_bbd073df829f756775f9e7143f99bd5a0637a4be4ba95280a496c732e9ed45ea
|
||||
composite_key: ''
|
||||
is_lead: false
|
||||
created_unix: 1764695172
|
||||
- id: 4119
|
||||
version_id: 740
|
||||
blob_id: 2115
|
||||
name: manifest.json
|
||||
lower_name: manifest.json
|
||||
composite_key: ''
|
||||
is_lead: true
|
||||
created_unix: 1764695172
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
- id: 735
|
||||
package_id: 7
|
||||
creator_id: 1
|
||||
version: _upload
|
||||
lower_version: _upload
|
||||
created_unix: 1764694509
|
||||
is_internal: true
|
||||
metadata_json: 'null'
|
||||
download_count: 0
|
||||
- id: 736
|
||||
package_id: 7
|
||||
creator_id: 1
|
||||
version: 'sha256:a972a01ff13b1cb6a9c49b3c34a283b67b75f441b8ff17ad4833a472f7b0fe08'
|
||||
lower_version: 'sha256:a972a01ff13b1cb6a9c49b3c34a283b67b75f441b8ff17ad4833a472f7b0fe08'
|
||||
created_unix: 1764694509
|
||||
is_internal: false
|
||||
metadata_json: >-
|
||||
{"type":"oci","is_tagged":false,"platform":"linux/amd64","description":"A
|
||||
runner for Forgejo
|
||||
Actions.","authors":["Forgejo"],"license":"GPL-3.0-or-later","project_url":"https://forgejo.org","repository_url":"https://code.forgejo.org/forgejo/runner","documentation_url":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","labels":{"io.buildah.version":"1.42.0","maintainer":"contact@forgejo.org","org.opencontainers.image.authors":"Forgejo","org.opencontainers.image.description":"A
|
||||
runner for Forgejo
|
||||
Actions.","org.opencontainers.image.documentation":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","org.opencontainers.image.licenses":"GPL-3.0-or-later","org.opencontainers.image.source":"https://code.forgejo.org/forgejo/runner","org.opencontainers.image.title":"Forgejo
|
||||
Runner","org.opencontainers.image.url":"https://forgejo.org","org.opencontainers.image.vendor":"Forgejo","org.opencontainers.image.version":"1"},"layer_creation":["ADD
|
||||
alpine-minirootfs-3.22.2-x86_64.tar.gz / # buildkit","CMD
|
||||
[\"/bin/sh\"]","ARG RELEASE_VERSION","|1 RELEASE_VERSION=1 /bin/sh -c apk
|
||||
add --no-cache git bash","COPY
|
||||
file:abe2aa9c7d7eee672ac5953937e3ae56536980f485e40b8a70d94476ce576d39 in
|
||||
/bin/forgejo-runner","LABEL maintainer=\"contact@forgejo.org\"
|
||||
org.opencontainers.image.authors=\"Forgejo\"
|
||||
org.opencontainers.image.url=\"https://forgejo.org\"
|
||||
org.opencontainers.image.documentation=\"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner\"
|
||||
org.opencontainers.image.source=\"https://code.forgejo.org/forgejo/runner\"
|
||||
org.opencontainers.image.version=\"${RELEASE_VERSION}\"
|
||||
org.opencontainers.image.vendor=\"Forgejo\"
|
||||
org.opencontainers.image.licenses=\"GPL-3.0-or-later\"
|
||||
org.opencontainers.image.title=\"Forgejo Runner\"
|
||||
org.opencontainers.image.description=\"A runner for Forgejo Actions.\"","ENV
|
||||
HOME=/data","USER 1000:1000","WORKDIR /data","VOLUME [\"/data\"]","CMD
|
||||
[\"/bin/forgejo-runner\"]"]}
|
||||
download_count: 0
|
||||
- id: 737
|
||||
package_id: 7
|
||||
creator_id: 1
|
||||
version: 'sha256:4759bcf56210784f3b2c2b7438a43a5771c5eda61a11825c80f7b32143ac9c12'
|
||||
lower_version: 'sha256:4759bcf56210784f3b2c2b7438a43a5771c5eda61a11825c80f7b32143ac9c12'
|
||||
created_unix: 1764694509
|
||||
is_internal: false
|
||||
metadata_json: >-
|
||||
{"type":"oci","is_tagged":false,"platform":"linux/arm64/v8","description":"A
|
||||
runner for Forgejo
|
||||
Actions.","authors":["Forgejo"],"license":"GPL-3.0-or-later","project_url":"https://forgejo.org","repository_url":"https://code.forgejo.org/forgejo/runner","documentation_url":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","labels":{"io.buildah.version":"1.42.0","maintainer":"contact@forgejo.org","org.opencontainers.image.authors":"Forgejo","org.opencontainers.image.description":"A
|
||||
runner for Forgejo
|
||||
Actions.","org.opencontainers.image.documentation":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","org.opencontainers.image.licenses":"GPL-3.0-or-later","org.opencontainers.image.source":"https://code.forgejo.org/forgejo/runner","org.opencontainers.image.title":"Forgejo
|
||||
Runner","org.opencontainers.image.url":"https://forgejo.org","org.opencontainers.image.vendor":"Forgejo","org.opencontainers.image.version":"1"},"layer_creation":["ADD
|
||||
alpine-minirootfs-3.22.2-aarch64.tar.gz / # buildkit","CMD
|
||||
[\"/bin/sh\"]","ARG RELEASE_VERSION","|1 RELEASE_VERSION=1 /bin/sh -c apk
|
||||
add --no-cache git bash","COPY
|
||||
file:26caa6ee49a60c7da3a00fe966069655c32d484f1659bb0389dc18fd9616b521 in
|
||||
/bin/forgejo-runner","LABEL maintainer=\"contact@forgejo.org\"
|
||||
org.opencontainers.image.authors=\"Forgejo\"
|
||||
org.opencontainers.image.url=\"https://forgejo.org\"
|
||||
org.opencontainers.image.documentation=\"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner\"
|
||||
org.opencontainers.image.source=\"https://code.forgejo.org/forgejo/runner\"
|
||||
org.opencontainers.image.version=\"${RELEASE_VERSION}\"
|
||||
org.opencontainers.image.vendor=\"Forgejo\"
|
||||
org.opencontainers.image.licenses=\"GPL-3.0-or-later\"
|
||||
org.opencontainers.image.title=\"Forgejo Runner\"
|
||||
org.opencontainers.image.description=\"A runner for Forgejo Actions.\"","ENV
|
||||
HOME=/data","USER 1000:1000","WORKDIR /data","VOLUME [\"/data\"]","CMD
|
||||
[\"/bin/forgejo-runner\"]"]}
|
||||
download_count: 0
|
||||
- id: 738
|
||||
package_id: 7
|
||||
creator_id: 1
|
||||
version: v1.0
|
||||
lower_version: v1.0
|
||||
created_unix: 1764694509
|
||||
is_internal: false
|
||||
metadata_json: >-
|
||||
{"type":"oci","is_tagged":true,"manifests":[{"platform":"linux/amd64","digest":"sha256:a972a01ff13b1cb6a9c49b3c34a283b67b75f441b8ff17ad4833a472f7b0fe08","size":19758113},{"platform":"linux/arm64","digest":"sha256:4759bcf56210784f3b2c2b7438a43a5771c5eda61a11825c80f7b32143ac9c12","size":19297077}]}
|
||||
download_count: 0
|
||||
- id: 740
|
||||
package_id: 7
|
||||
creator_id: 1
|
||||
version: v2.0
|
||||
lower_version: v2.0
|
||||
created_unix: 1764695172
|
||||
is_internal: false
|
||||
metadata_json: >-
|
||||
{"type":"oci","is_tagged":true,"platform":"linux/amd64","description":"A
|
||||
runner for Forgejo
|
||||
Actions.","authors":["Forgejo"],"license":"GPL-3.0-or-later","project_url":"https://forgejo.org","repository_url":"https://code.forgejo.org/forgejo/runner","documentation_url":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","labels":{"io.buildah.version":"1.42.0","maintainer":"contact@forgejo.org","org.opencontainers.image.authors":"Forgejo","org.opencontainers.image.description":"A
|
||||
runner for Forgejo
|
||||
Actions.","org.opencontainers.image.documentation":"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner","org.opencontainers.image.licenses":"GPL-3.0-or-later","org.opencontainers.image.source":"https://code.forgejo.org/forgejo/runner","org.opencontainers.image.title":"Forgejo
|
||||
Runner","org.opencontainers.image.url":"https://forgejo.org","org.opencontainers.image.vendor":"Forgejo","org.opencontainers.image.version":"2"},"layer_creation":["ADD
|
||||
alpine-minirootfs-3.22.2-x86_64.tar.gz / # buildkit","CMD
|
||||
[\"/bin/sh\"]","ARG RELEASE_VERSION","|1 RELEASE_VERSION=2 /bin/sh -c apk
|
||||
add --no-cache git bash","COPY
|
||||
file:3c24d4a08c7006979c8b4397315c24dbb40df6b8fa474d8744daaf6fe17dce6f in
|
||||
/bin/forgejo-runner","LABEL maintainer=\"contact@forgejo.org\"
|
||||
org.opencontainers.image.authors=\"Forgejo\"
|
||||
org.opencontainers.image.url=\"https://forgejo.org\"
|
||||
org.opencontainers.image.documentation=\"https://forgejo.org/docs/latest/admin/actions/#forgejo-runner\"
|
||||
org.opencontainers.image.source=\"https://code.forgejo.org/forgejo/runner\"
|
||||
org.opencontainers.image.version=\"${RELEASE_VERSION}\"
|
||||
org.opencontainers.image.vendor=\"Forgejo\"
|
||||
org.opencontainers.image.licenses=\"GPL-3.0-or-later\"
|
||||
org.opencontainers.image.title=\"Forgejo Runner\"
|
||||
org.opencontainers.image.description=\"A runner for Forgejo Actions.\"","ENV
|
||||
HOME=/data","USER 1000:1000","WORKDIR /data","VOLUME [\"/data\"]","CMD
|
||||
[\"/bin/forgejo-runner\"]"]}
|
||||
download_count: 0
|
||||
|
|
@ -324,6 +324,13 @@ func PrepareCleanPackageData(t testing.TB) {
|
|||
var inTestEnv atomic.Bool
|
||||
|
||||
func PrepareTestEnv(t testing.TB, skip ...int) func() {
|
||||
deferFn := PrepareTestEnvWithPackageData(t, skip...)
|
||||
PrepareCleanPackageData(t)
|
||||
return deferFn
|
||||
}
|
||||
|
||||
// Doesn't perform the `PrepareCleanPackageData` that `PrepareTestEnv` does...
|
||||
func PrepareTestEnvWithPackageData(t testing.TB, skip ...int) func() {
|
||||
t.Helper()
|
||||
|
||||
if !inTestEnv.CompareAndSwap(false, true) {
|
||||
|
|
@ -348,7 +355,6 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() {
|
|||
// do not add more Prepare* functions here, only call necessary ones in the related test functions
|
||||
PrepareGitRepoDirectory(t)
|
||||
PrepareLFSStorage(t)
|
||||
PrepareCleanPackageData(t)
|
||||
return deferFn
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue