-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Add package registry cleanup rules #21658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
1c16095
Add package cleanup rules.
KN4CK3R 5a944f7
Do not use Sleep.
KN4CK3R 9912c73
Add tests.
KN4CK3R a28caf9
Add documentation.
KN4CK3R 8ec1717
Check if packages are enabled.
KN4CK3R b4ab39c
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R 88f99ad
lint
KN4CK3R db92e67
Provide context.
KN4CK3R 56bdd82
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R fa1916c
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R 7e9764d
Handle multiarch manifests.
KN4CK3R 5ff0a07
Split file.
KN4CK3R 8651704
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R a714417
merge main branch
lunny 03e25ac
merge main branch
lunny f2b3bb7
Use db.Insert.
KN4CK3R 2735538
Merge branch 'main' into feature-package-cleanup
KN4CK3R bc6fe0d
Merge branch 'main' into feature-package-cleanup
KN4CK3R 933ca64
Merge branch 'feature-package-cleanup' of https://github.com/KN4CK3R/…
KN4CK3R d4ea4f2
Merge branch 'main' into feature-package-cleanup
KN4CK3R c02b6fc
Merge branch 'main' into feature-package-cleanup
lunny File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| --- | ||
| date: "2022-11-01T00:00:00+00:00" | ||
| title: "Storage" | ||
| slug: "packages/storage" | ||
| draft: false | ||
| toc: false | ||
| menu: | ||
| sidebar: | ||
| parent: "packages" | ||
| name: "storage" | ||
| weight: 5 | ||
| identifier: "storage" | ||
| --- | ||
|
|
||
| # Storage | ||
|
|
||
| This document describes the storage of the package registry and how it can be managed. | ||
|
|
||
| **Table of Contents** | ||
|
|
||
| {{< toc >}} | ||
|
|
||
| ## Deduplication | ||
|
|
||
| The package registry has a build-in deduplication of uploaded blobs. | ||
| If two identical files are uploaded only one blob is saved on the filesystem. | ||
| This ensures no space is wasted for duplicated files. | ||
|
|
||
| If two packages are uploaded with identical files, both packages will display the same size but on the filesystem they require only half of the size. | ||
| Whenever a package gets deleted only the references to the underlaying blobs are removed. | ||
| The blobs get not removed at this moment, so they still require space on the filesystem. | ||
| When a new package gets uploaded the existing blobs may get referenced again. | ||
|
|
||
| These unreferenced blobs get deleted by a [clean up job]({{< relref "doc/advanced/config-cheat-sheet.en-us.md#cron---cleanup-expired-packages-croncleanup_packages" >}}). | ||
| The config setting `OLDER_THAN` configures how long unreferenced blobs are kept before they get deleted. | ||
|
|
||
| ## Cleanup Rules | ||
|
|
||
| Package registries can become large over time without cleanup. | ||
| It's recommended to delete unnecessary packages and set up cleanup rules to automatically manage the package registry usage. | ||
| Every package owner (user or organization) manages the cleanup rules which are applied to their packages. | ||
|
|
||
| |Setting|Description| | ||
| |-|-| | ||
| |Enabled|Turn the cleanup rule on or off.| | ||
| |Type|Every rule manages a specific package type.| | ||
| |Apply pattern to full package name|If enabled, the patterns below are applied to the full package name (`package/version`). Otherwise only the version (`version`) is used.| | ||
| |Keep the most recent|How many versions to *always* keep for each package.| | ||
| |Keep versions matching|The regex pattern that determines which versions to keep. An empty pattern keeps no version while `.+` keeps all versions. The container registry will always keep the `latest` version even if not configured.| | ||
| |Remove versions older than|Remove only versions older than the selected days.| | ||
| |Remove versions matching|The regex pattern that determines which versions to remove. An empty pattern or `.+` leads to the removal of every package if no other setting tells otherwise.| | ||
|
|
||
| Every cleanup rule can show a preview of the affected packages. | ||
| This can be used to check if the cleanup rules is proper configured. | ||
|
|
||
| ### Regex examples | ||
|
|
||
| Regex patterns are automatically surrounded with `\A` and `\z` anchors. | ||
| Do not include any `\A`, `\z`, `^` or `$` token in the regex patterns as they are not necessary. | ||
| The patterns are case-insensitive which matches the behaviour of the package registry in Gitea. | ||
|
|
||
| |Pattern|Description| | ||
| |-|-| | ||
| |`.*`|Match every possible version.| | ||
| |`v.+`|Match versions that start with `v`.| | ||
| |`release`|Match only the version `release`.| | ||
| |`release.*`|Match versions that are either named or start with `release`.| | ||
| |`.+-temp-.+`|Match versions that contain `-temp-`.| | ||
| |`v.+\|release`|Match versions that either start with `v` or are named `release`.| | ||
| |`package/v.+\|other/release`|Match versions of the package `package` that start with `v` or the version `release` of the package `other`. This needs the setting *Apply pattern to full package name* enabled.| | ||
|
|
||
| ### How the cleanup rules work | ||
|
|
||
| The cleanup rules are part of the [clean up job]({{< relref "doc/advanced/config-cheat-sheet.en-us.md#cron---cleanup-expired-packages-croncleanup_packages" >}}) and run periodicly. | ||
|
|
||
| The cleanup rule: | ||
|
|
||
| 1. Collects all packages of the package type for the owners registry. | ||
| 1. For every package it collects all versions. | ||
| 1. Excludes from the list the # versions based on the *Keep the most recent* value. | ||
| 1. Excludes from the list any versions matching the *Keep versions matching* value. | ||
| 1. Excludes from the list the versions more recent than the *Remove versions older than* value. | ||
| 1. Excludes from the list any versions not matching the *Remove versions matching* value. | ||
| 1. Deletes the remaining versions. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // Copyright 2022 The Gitea Authors. All rights reserved. | ||
| // Use of this source code is governed by a MIT-style | ||
| // license that can be found in the LICENSE file. | ||
|
|
||
| package v1_19 //nolint | ||
|
|
||
| import ( | ||
| "code.gitea.io/gitea/modules/timeutil" | ||
|
|
||
| "xorm.io/xorm" | ||
| ) | ||
|
|
||
| func CreatePackageCleanupRuleTable(x *xorm.Engine) error { | ||
| type PackageCleanupRule struct { | ||
| ID int64 `xorm:"pk autoincr"` | ||
| Enabled bool `xorm:"INDEX NOT NULL DEFAULT false"` | ||
| OwnerID int64 `xorm:"UNIQUE(s) INDEX NOT NULL DEFAULT 0"` | ||
| Type string `xorm:"UNIQUE(s) INDEX NOT NULL"` | ||
| KeepCount int `xorm:"NOT NULL DEFAULT 0"` | ||
| KeepPattern string `xorm:"NOT NULL DEFAULT ''"` | ||
| RemoveDays int `xorm:"NOT NULL DEFAULT 0"` | ||
| RemovePattern string `xorm:"NOT NULL DEFAULT ''"` | ||
| MatchFullName bool `xorm:"NOT NULL DEFAULT false"` | ||
| CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL DEFAULT 0"` | ||
| UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL DEFAULT 0"` | ||
| } | ||
|
|
||
| return x.Sync2(new(PackageCleanupRule)) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| // Copyright 2022 The Gitea Authors. All rights reserved. | ||
| // Use of this source code is governed by a MIT-style | ||
| // license that can be found in the LICENSE file. | ||
|
|
||
| package packages | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
| "fmt" | ||
| "regexp" | ||
|
|
||
| "code.gitea.io/gitea/models/db" | ||
| "code.gitea.io/gitea/modules/timeutil" | ||
|
|
||
| "xorm.io/builder" | ||
| ) | ||
|
|
||
| var ErrPackageCleanupRuleNotExist = errors.New("Package blob does not exist") | ||
|
|
||
| func init() { | ||
| db.RegisterModel(new(PackageCleanupRule)) | ||
| } | ||
|
|
||
| // PackageCleanupRule represents a rule which describes when to clean up package versions | ||
| type PackageCleanupRule struct { | ||
| ID int64 `xorm:"pk autoincr"` | ||
| Enabled bool `xorm:"INDEX NOT NULL DEFAULT false"` | ||
| OwnerID int64 `xorm:"UNIQUE(s) INDEX NOT NULL DEFAULT 0"` | ||
| Type Type `xorm:"UNIQUE(s) INDEX NOT NULL"` | ||
| KeepCount int `xorm:"NOT NULL DEFAULT 0"` | ||
| KeepPattern string `xorm:"NOT NULL DEFAULT ''"` | ||
| KeepPatternMatcher *regexp.Regexp `xorm:"-"` | ||
| RemoveDays int `xorm:"NOT NULL DEFAULT 0"` | ||
| RemovePattern string `xorm:"NOT NULL DEFAULT ''"` | ||
| RemovePatternMatcher *regexp.Regexp `xorm:"-"` | ||
| MatchFullName bool `xorm:"NOT NULL DEFAULT false"` | ||
| CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL DEFAULT 0"` | ||
| UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL DEFAULT 0"` | ||
| } | ||
|
|
||
| func (pcr *PackageCleanupRule) CompiledPattern() error { | ||
| if pcr.KeepPatternMatcher != nil || pcr.RemovePatternMatcher != nil { | ||
| return nil | ||
| } | ||
|
|
||
| if pcr.KeepPattern != "" { | ||
| var err error | ||
| pcr.KeepPatternMatcher, err = regexp.Compile(fmt.Sprintf(`(?i)\A%s\z`, pcr.KeepPattern)) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| if pcr.RemovePattern != "" { | ||
| var err error | ||
| pcr.RemovePatternMatcher, err = regexp.Compile(fmt.Sprintf(`(?i)\A%s\z`, pcr.RemovePattern)) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func InsertCleanupRule(ctx context.Context, pcr *PackageCleanupRule) (*PackageCleanupRule, error) { | ||
| _, err := db.GetEngine(ctx).Insert(pcr) | ||
| return pcr, err | ||
| } | ||
|
|
||
| func GetCleanupRuleByID(ctx context.Context, id int64) (*PackageCleanupRule, error) { | ||
| pcr := &PackageCleanupRule{} | ||
|
|
||
| has, err := db.GetEngine(ctx).ID(id).Get(pcr) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if !has { | ||
| return nil, ErrPackageCleanupRuleNotExist | ||
| } | ||
| return pcr, nil | ||
| } | ||
|
|
||
| func UpdateCleanupRule(ctx context.Context, pcr *PackageCleanupRule) error { | ||
| _, err := db.GetEngine(ctx).ID(pcr.ID).AllCols().Update(pcr) | ||
| return err | ||
| } | ||
|
|
||
| func GetCleanupRulesByOwner(ctx context.Context, ownerID int64) ([]*PackageCleanupRule, error) { | ||
| pcrs := make([]*PackageCleanupRule, 0, 10) | ||
| return pcrs, db.GetEngine(ctx).Where("owner_id = ?", ownerID).Find(&pcrs) | ||
| } | ||
|
|
||
| func DeleteCleanupRuleByID(ctx context.Context, ruleID int64) error { | ||
| _, err := db.GetEngine(ctx).ID(ruleID).Delete(&PackageCleanupRule{}) | ||
| return err | ||
| } | ||
|
|
||
| func HasOwnerCleanupRuleForPackageType(ctx context.Context, ownerID int64, packageType Type) (bool, error) { | ||
| return db.GetEngine(ctx). | ||
| Where("owner_id = ? AND type = ?", ownerID, packageType). | ||
| Exist(&PackageCleanupRule{}) | ||
| } | ||
|
|
||
| func IterateEnabledCleanupRules(ctx context.Context, callback func(context.Context, *PackageCleanupRule) error) error { | ||
| return db.Iterate( | ||
| ctx, | ||
| builder.Eq{"enabled": true}, | ||
| callback, | ||
| ) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| // Copyright 2022 The Gitea Authors. All rights reserved. | ||
| // Use of this source code is governed by a MIT-style | ||
| // license that can be found in the LICENSE file. | ||
|
|
||
| package org | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "net/http" | ||
|
|
||
| "code.gitea.io/gitea/modules/base" | ||
| "code.gitea.io/gitea/modules/context" | ||
| "code.gitea.io/gitea/modules/setting" | ||
| shared "code.gitea.io/gitea/routers/web/shared/packages" | ||
| ) | ||
|
|
||
| const ( | ||
| tplSettingsPackages base.TplName = "org/settings/packages" | ||
| tplSettingsPackagesRuleEdit base.TplName = "org/settings/packages_cleanup_rules_edit" | ||
| tplSettingsPackagesRulePreview base.TplName = "org/settings/packages_cleanup_rules_preview" | ||
| ) | ||
|
|
||
| func Packages(ctx *context.Context) { | ||
| ctx.Data["Title"] = ctx.Tr("packages.title") | ||
| ctx.Data["PageIsOrgSettings"] = true | ||
| ctx.Data["PageIsSettingsPackages"] = true | ||
|
|
||
| shared.SetPackagesContext(ctx, ctx.ContextUser) | ||
|
|
||
| ctx.HTML(http.StatusOK, tplSettingsPackages) | ||
| } | ||
|
|
||
| func PackagesRuleAdd(ctx *context.Context) { | ||
| ctx.Data["Title"] = ctx.Tr("packages.title") | ||
| ctx.Data["PageIsOrgSettings"] = true | ||
| ctx.Data["PageIsSettingsPackages"] = true | ||
|
|
||
| shared.SetRuleAddContext(ctx) | ||
|
|
||
| ctx.HTML(http.StatusOK, tplSettingsPackagesRuleEdit) | ||
| } | ||
|
|
||
| func PackagesRuleEdit(ctx *context.Context) { | ||
| ctx.Data["Title"] = ctx.Tr("packages.title") | ||
| ctx.Data["PageIsOrgSettings"] = true | ||
| ctx.Data["PageIsSettingsPackages"] = true | ||
|
|
||
| shared.SetRuleEditContext(ctx, ctx.ContextUser) | ||
|
|
||
| ctx.HTML(http.StatusOK, tplSettingsPackagesRuleEdit) | ||
| } | ||
|
|
||
| func PackagesRuleAddPost(ctx *context.Context) { | ||
| ctx.Data["Title"] = ctx.Tr("packages.title") | ||
| ctx.Data["PageIsOrgSettings"] = true | ||
| ctx.Data["PageIsSettingsPackages"] = true | ||
|
|
||
| shared.PerformRuleAddPost( | ||
| ctx, | ||
| ctx.ContextUser, | ||
| fmt.Sprintf("%s/org/%s/settings/packages", setting.AppSubURL, ctx.ContextUser.Name), | ||
| tplSettingsPackagesRuleEdit, | ||
| ) | ||
| } | ||
|
|
||
| func PackagesRuleEditPost(ctx *context.Context) { | ||
| ctx.Data["Title"] = ctx.Tr("packages.title") | ||
| ctx.Data["PageIsOrgSettings"] = true | ||
| ctx.Data["PageIsSettingsPackages"] = true | ||
|
|
||
| shared.PerformRuleEditPost( | ||
| ctx, | ||
| ctx.ContextUser, | ||
| fmt.Sprintf("%s/org/%s/settings/packages", setting.AppSubURL, ctx.ContextUser.Name), | ||
| tplSettingsPackagesRuleEdit, | ||
| ) | ||
| } | ||
|
|
||
| func PackagesRulePreview(ctx *context.Context) { | ||
| ctx.Data["Title"] = ctx.Tr("packages.title") | ||
| ctx.Data["PageIsOrgSettings"] = true | ||
| ctx.Data["PageIsSettingsPackages"] = true | ||
|
|
||
| shared.SetRulePreviewContext(ctx, ctx.ContextUser) | ||
|
|
||
| ctx.HTML(http.StatusOK, tplSettingsPackagesRulePreview) | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.