diff --git a/make/migrations/postgresql/0015_1.10.0_schema.up.sql b/make/migrations/postgresql/0015_1.10.0_schema.up.sql new file mode 100644 index 000000000..f119ad9df --- /dev/null +++ b/make/migrations/postgresql/0015_1.10.0_schema.up.sql @@ -0,0 +1,9 @@ +/** Add table for immutable tag **/ +CREATE TABLE immutable_tag_rule +( + id SERIAL PRIMARY KEY NOT NULL, + project_id int NOT NULL, + tag_filter text, + enabled boolean default true NOT NULL, + creation_time timestamp default CURRENT_TIMESTAMP +) \ No newline at end of file diff --git a/src/common/dao/immutable.go b/src/common/dao/immutable.go new file mode 100644 index 000000000..5b279b1fb --- /dev/null +++ b/src/common/dao/immutable.go @@ -0,0 +1,73 @@ +package dao + +import ( + "fmt" + + "github.com/astaxie/beego/orm" + "github.com/goharbor/harbor/src/common/models" +) + +// CreateImmutableRule creates the Immutable Rule +func CreateImmutableRule(ir *models.ImmutableRule) (int64, error) { + ir.Enabled = true + o := GetOrmer() + return o.Insert(ir) +} + +// UpdateImmutableRule update the immutable rules +func UpdateImmutableRule(projectID int, ir *models.ImmutableRule) (int64, error) { + o := GetOrmer() + return o.Update(ir, "TagFilter") +} + +// ToggleImmutableRule enable/disable immutable rules +func ToggleImmutableRule(id int64, enabled bool) (int64, error) { + o := GetOrmer() + ir := &models.ImmutableRule{ID: id, Enabled: enabled} + return o.Update(ir, "Enabled") +} + +// GetImmutableRule get immutable rule +func GetImmutableRule(id int64) (*models.ImmutableRule, error) { + o := GetOrmer() + ir := &models.ImmutableRule{ID: id} + err := o.Read(ir) + if err == orm.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, err + } + return ir, nil +} + +// QueryImmutableRuleByProjectID get all immutable rule by project +func QueryImmutableRuleByProjectID(projectID int64) ([]models.ImmutableRule, error) { + o := GetOrmer() + qs := o.QueryTable(&models.ImmutableRule{}).Filter("ProjectID", projectID) + var r []models.ImmutableRule + _, err := qs.All(&r) + if err != nil { + return nil, fmt.Errorf("failed to get immutable tag rule by projectID %d, error: %v", projectID, err) + } + return r, nil +} + +// QueryEnabledImmutableRuleByProjectID get all enabled immutable rule by project +func QueryEnabledImmutableRuleByProjectID(projectID int64) ([]models.ImmutableRule, error) { + o := GetOrmer() + qs := o.QueryTable(&models.ImmutableRule{}).Filter("ProjectID", projectID).Filter("Enabled", true) + var r []models.ImmutableRule + _, err := qs.All(&r) + if err != nil { + return nil, fmt.Errorf("failed to get enabled immutable tag rule for by projectID %d, error: %v", projectID, err) + } + return r, nil +} + +// DeleteImmutableRule delete the immutable rule +func DeleteImmutableRule(id int64) (int64, error) { + o := GetOrmer() + ir := &models.ImmutableRule{ID: id} + return o.Delete(ir) +} diff --git a/src/common/dao/immutable_test.go b/src/common/dao/immutable_test.go new file mode 100644 index 000000000..9ffaa813d --- /dev/null +++ b/src/common/dao/immutable_test.go @@ -0,0 +1,130 @@ +package dao + +import ( + "testing" + + "github.com/goharbor/harbor/src/common/models" +) + +func TestCreateImmutableRule(t *testing.T) { + ir := &models.ImmutableRule{TagFilter: "**", ProjectID: 1} + id, err := CreateImmutableRule(ir) + if err != nil { + t.Errorf("error: %+v", err) + } + if id <= 0 { + t.Error("Can not create immutable tag rule") + } + _, err = DeleteImmutableRule(id) + if err != nil { + t.Errorf("error: %+v", err) + } +} + +func TestUpdateImmutableRule(t *testing.T) { + ir := &models.ImmutableRule{TagFilter: "**", ProjectID: 1} + id, err := CreateImmutableRule(ir) + if err != nil { + t.Errorf("error: %+v", err) + } + if id <= 0 { + t.Error("Can not create immutable tag rule") + } + + updatedIR := &models.ImmutableRule{ID: id, TagFilter: "1.2.0", ProjectID: 1} + updatedCnt, err := UpdateImmutableRule(1, updatedIR) + if err != nil { + t.Errorf("error: %+v", err) + } + if updatedCnt <= 0 { + t.Error("Failed to update immutable id") + } + + newIr, err := GetImmutableRule(id) + + if err != nil { + t.Errorf("error: %+v", err) + } + + if newIr.TagFilter != "1.2.0" { + t.Error("Failed to update immutable tag") + } + + defer DeleteImmutableRule(id) + +} + +func TestEnableImmutableRule(t *testing.T) { + ir := &models.ImmutableRule{TagFilter: "**", ProjectID: 1} + id, err := CreateImmutableRule(ir) + if err != nil { + t.Errorf("error: %+v", err) + } + if id <= 0 { + t.Error("Can not create immutable tag rule") + } + + ToggleImmutableRule(id, false) + newIr, err := GetImmutableRule(id) + + if err != nil { + t.Errorf("error: %+v", err) + } + + if newIr.Enabled != false { + t.Error("Failed to disable the immutable rule") + } + + defer DeleteImmutableRule(id) +} + +func TestGetImmutableRuleByProject(t *testing.T) { + irs := []*models.ImmutableRule{ + {TagFilter: "version1", ProjectID: 99}, + {TagFilter: "version2", ProjectID: 99}, + {TagFilter: "version3", ProjectID: 99}, + {TagFilter: "version4", ProjectID: 99}, + } + for _, ir := range irs { + CreateImmutableRule(ir) + } + + qrs, err := QueryImmutableRuleByProjectID(99) + if err != nil { + t.Errorf("error: %+v", err) + } + + if len(qrs) != 4 { + t.Error("Failed to query 4 rows!") + } + + defer ExecuteBatchSQL([]string{"delete from immutable_tag_rule where project_id = 99 "}) + +} +func TestGetEnabledImmutableRuleByProject(t *testing.T) { + irs := []*models.ImmutableRule{ + {TagFilter: "version1", ProjectID: 99}, + {TagFilter: "version2", ProjectID: 99}, + {TagFilter: "version3", ProjectID: 99}, + {TagFilter: "version4", ProjectID: 99}, + } + for i, ir := range irs { + id, _ := CreateImmutableRule(ir) + if i == 1 { + ToggleImmutableRule(id, false) + } + + } + + qrs, err := QueryEnabledImmutableRuleByProjectID(99) + if err != nil { + t.Errorf("error: %+v", err) + } + + if len(qrs) != 3 { + t.Errorf("Failed to query 3 rows!, got %v", len(qrs)) + } + + defer ExecuteBatchSQL([]string{"delete from immutable_tag_rule where project_id = 99 "}) + +} diff --git a/src/common/models/base.go b/src/common/models/base.go index de04d0285..dcc6c57c7 100644 --- a/src/common/models/base.go +++ b/src/common/models/base.go @@ -46,5 +46,6 @@ func init() { new(CVEWhitelist), new(Quota), new(QuotaUsage), + new(ImmutableRule), ) } diff --git a/src/common/models/immutable.go b/src/common/models/immutable.go new file mode 100644 index 000000000..37953f6e2 --- /dev/null +++ b/src/common/models/immutable.go @@ -0,0 +1,14 @@ +package models + +// ImmutableRule - rule which filter image tags should be immutable. +type ImmutableRule struct { + ID int64 `orm:"pk;auto;column(id)" json:"id,omitempty"` + ProjectID int64 `orm:"column(project_id)" json:"project_id,omitempty"` + TagFilter string `orm:"column(tag_filter)" json:"tag_filter,omitempty"` + Enabled bool `orm:"column(enabled)" json:"enabled,omitempty"` +} + +// TableName ... +func (c *ImmutableRule) TableName() string { + return "immutable_tag_rule" +}