diff --git a/src/core/api/project.go b/src/core/api/project.go index 3497fb685..21b1df5e2 100644 --- a/src/core/api/project.go +++ b/src/core/api/project.go @@ -17,6 +17,7 @@ package api import ( "context" "fmt" + "github.com/goharbor/harbor/src/pkg/retention/policy" "net/http" "regexp" "strconv" @@ -56,6 +57,7 @@ type ProjectAPI struct { const projectNameMaxLen int = 255 const projectNameMinLen int = 1 const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*` +const defaultDaysToRetention = 7 // Prepare validates the URL and the user func (p *ProjectAPI) Prepare() { @@ -241,6 +243,14 @@ func (p *ProjectAPI) Post() { } } + // create a default retention policy for proxy project + if pro.RegistryID > 0 { + if err := p.addRetentionPolicyForProxy(projectID); err != nil { + p.SendInternalServerError(fmt.Errorf("failed to add tag retention policy for project: %v", err)) + return + } + } + // fire event evt.BuildAndPublish(&metadata.CreateProjectEventMetadata{ ProjectID: projectID, @@ -251,6 +261,18 @@ func (p *ProjectAPI) Post() { p.Redirect(http.StatusCreated, strconv.FormatInt(projectID, 10)) } +func (p *ProjectAPI) addRetentionPolicyForProxy(projID int64) error { + plc := policy.WithNDaysSinceLastPull(projID, defaultDaysToRetention) + retID, err := retentionController.CreateRetention(plc) + if err != nil { + return err + } + if err := p.ProjectMgr.GetMetadataManager().Add(projID, map[string]string{"retention_id": strconv.FormatInt(retID, 10)}); err != nil { + return err + } + return nil +} + // Head ... func (p *ProjectAPI) Head() { diff --git a/src/pkg/retention/policy/models.go b/src/pkg/retention/policy/models.go index e8fd86ad7..3b083b769 100644 --- a/src/pkg/retention/policy/models.go +++ b/src/pkg/retention/policy/models.go @@ -16,6 +16,7 @@ package policy import ( "github.com/astaxie/beego/validation" + "github.com/goharbor/harbor/src/lib/selector/selectors/doublestar" "github.com/goharbor/harbor/src/pkg/retention/policy/rule" "github.com/goharbor/harbor/src/pkg/retention/policy/rule/index" ) @@ -112,3 +113,47 @@ type Scope struct { // 0 for 'system', project ID for 'project' and repo ID for 'repository' Reference int64 `json:"ref" valid:"Required"` } + +// WithNDaysSinceLastPull build a retention rule to keep images n days to since last pull +func WithNDaysSinceLastPull(projID int64, n int) *Metadata { + return &Metadata{ + Algorithm: "or", + Rules: []rule.Metadata{ + { + ID: 1, + Priority: 1, + Action: "retain", + Template: "nDaysSinceLastPull", + TagSelectors: []*rule.Selector{ + { + Kind: doublestar.Kind, + Decoration: doublestar.Matches, + Pattern: "**", + }, + }, + ScopeSelectors: map[string][]*rule.Selector{ + "repository": { + { + Kind: doublestar.Kind, + Decoration: doublestar.RepoMatches, + Pattern: "**", + }, + }, + }, + Parameters: rule.Parameters{ + "nDaysSinceLastPull": n, + }, + }, + }, + Trigger: &Trigger{ + Kind: "Schedule", + Settings: map[string]interface{}{ + "cron": "0 0 0 * * *", + }, + }, + Scope: &Scope{ + Level: "project", + Reference: projID, + }, + } +}