deprecate resource label (#19349)

There is no api is using the DAO, remove it from the source code.

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
Wang Yan 2023-09-14 01:25:52 +08:00 committed by GitHub
parent 97b285168a
commit ed370a496b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1 additions and 775 deletions

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS harbor_resource_label;

View File

@ -1,130 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dao
import (
"time"
"github.com/beego/beego/v2/client/orm"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/label/model"
)
// AddResourceLabel add a label to a resource
func AddResourceLabel(rl *models.ResourceLabel) (int64, error) {
now := time.Now()
rl.CreationTime = now
rl.UpdateTime = now
return GetOrmer().Insert(rl)
}
// GetResourceLabel specified by resource ID or name
// Get the ResourceLabel by ResourceID if rIDOrName is int
// Get the ResourceLabel by ResourceName if rIDOrName is string
func GetResourceLabel(rType string, rIDOrName interface{}, labelID int64) (*models.ResourceLabel, error) {
rl := &models.ResourceLabel{
ResourceType: rType,
LabelID: labelID,
}
var err error
id, ok := rIDOrName.(int64)
if ok {
rl.ResourceID = id
err = GetOrmer().Read(rl, "ResourceType", "ResourceID", "LabelID")
} else {
rl.ResourceName = rIDOrName.(string)
err = GetOrmer().Read(rl, "ResourceType", "ResourceName", "LabelID")
}
if err != nil {
if err == orm.ErrNoRows {
return nil, nil
}
return nil, err
}
return rl, nil
}
// GetLabelsOfResource returns the label list of the resource
// Get the labels by ResourceID if rIDOrName is int, or get the labels by ResourceName
func GetLabelsOfResource(rType string, rIDOrName interface{}) ([]*model.Label, error) {
sql := `select l.id, l.name, l.description, l.color, l.scope, l.project_id, l.creation_time, l.update_time
from harbor_resource_label rl
join harbor_label l on rl.label_id=l.id
where rl.resource_type = ? and`
if _, ok := rIDOrName.(int64); ok {
sql += ` rl.resource_id = ?`
} else {
sql += ` rl.resource_name = ?`
}
labels := []*model.Label{}
_, err := GetOrmer().Raw(sql, rType, rIDOrName).QueryRows(&labels)
return labels, err
}
// DeleteResourceLabel ...
func DeleteResourceLabel(id int64) error {
_, err := GetOrmer().Delete(&models.ResourceLabel{
ID: id,
})
return err
}
// DeleteLabelsOfResource removes all labels of the resource
func DeleteLabelsOfResource(rType string, rIDOrName interface{}) error {
qs := GetOrmer().QueryTable(&models.ResourceLabel{}).
Filter("ResourceType", rType)
if _, ok := rIDOrName.(int64); ok {
qs = qs.Filter("ResourceID", rIDOrName)
} else {
qs = qs.Filter("ResourceName", rIDOrName)
}
_, err := qs.Delete()
return err
}
// ListResourceLabels lists ResourceLabel according to the query conditions
func ListResourceLabels(query ...*models.ResourceLabelQuery) ([]*models.ResourceLabel, error) {
qs := GetOrmer().QueryTable(&models.ResourceLabel{})
if len(query) > 0 {
q := query[0]
if q.LabelID > 0 {
qs = qs.Filter("LabelID", q.LabelID)
}
if len(q.ResourceType) > 0 {
qs = qs.Filter("ResourceType", q.ResourceType)
}
if q.ResourceID > 0 {
qs = qs.Filter("ResourceID", q.ResourceID)
}
if len(q.ResourceName) > 0 {
qs = qs.Filter("ResourceName", q.ResourceName)
}
}
rls := []*models.ResourceLabel{}
_, err := qs.All(&rls)
return rls, err
}
// DeleteResourceLabelByLabel delete the mapping relationship by label ID
func DeleteResourceLabelByLabel(id int64) error {
_, err := GetOrmer().QueryTable(&models.ResourceLabel{}).Filter("LabelID", id).Delete()
return err
}

View File

@ -1,100 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dao
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/label/dao"
"github.com/goharbor/harbor/src/pkg/label/model"
)
func TestMethodsOfResourceLabel(t *testing.T) {
labelDao := dao.New()
labelID, err := labelDao.Create(orm.Context(), &model.Label{
Name: "test_label",
Level: common.LabelLevelUser,
Scope: common.LabelScopeGlobal,
})
require.Nil(t, err)
defer labelDao.Delete(orm.Context(), labelID)
var resourceID int64 = 1
resourceType := common.ResourceTypeRepository
// add
rl := &models.ResourceLabel{
LabelID: labelID,
ResourceType: resourceType,
ResourceID: resourceID,
}
id, err := AddResourceLabel(rl)
require.Nil(t, err)
// get
r, err := GetResourceLabel(resourceType, resourceID, labelID)
require.Nil(t, err)
assert.Equal(t, id, r.ID)
// get by resource
labels, err := GetLabelsOfResource(resourceType, resourceID)
require.Nil(t, err)
require.Equal(t, 1, len(labels))
assert.Equal(t, id, r.ID)
// list
rls, err := ListResourceLabels(&models.ResourceLabelQuery{
LabelID: labelID,
ResourceType: resourceType,
ResourceID: resourceID,
})
require.Nil(t, err)
require.Equal(t, 1, len(rls))
assert.Equal(t, id, rls[0].ID)
// delete
err = DeleteResourceLabel(id)
require.Nil(t, err)
labels, err = GetLabelsOfResource(resourceType, resourceID)
require.Nil(t, err)
require.Equal(t, 0, len(labels))
// delete by resource
id, err = AddResourceLabel(rl)
require.Nil(t, err)
err = DeleteLabelsOfResource(resourceType, resourceID)
require.Nil(t, err)
labels, err = GetLabelsOfResource(resourceType, resourceID)
require.Nil(t, err)
require.Equal(t, 0, len(labels))
// delete by label ID
id, err = AddResourceLabel(rl)
require.Nil(t, err)
err = DeleteResourceLabelByLabel(labelID)
require.Nil(t, err)
rls, err = ListResourceLabels(&models.ResourceLabelQuery{
LabelID: labelID,
})
require.Nil(t, err)
require.Equal(t, 0, len(rls))
}

View File

@ -21,7 +21,6 @@ import (
func init() { func init() {
orm.RegisterModel( orm.RegisterModel(
new(Role), new(Role),
new(ResourceLabel),
new(OIDCUser), new(OIDCUser),
) )
} }

View File

@ -1,43 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"time"
)
// ResourceLabel records the relationship between resource and label
type ResourceLabel struct {
ID int64 `orm:"pk;auto;column(id)"`
LabelID int64 `orm:"column(label_id)"`
ResourceID int64 `orm:"column(resource_id)"`
ResourceName string `orm:"column(resource_name)"`
ResourceType string `orm:"column(resource_type)"`
CreationTime time.Time `orm:"column(creation_time);auto_now_add"`
UpdateTime time.Time `orm:"column(update_time);auto_now"`
}
// TableName ...
func (r *ResourceLabel) TableName() string {
return "harbor_resource_label"
}
// ResourceLabelQuery : query parameters for the mapping relationships of resource and label
type ResourceLabelQuery struct {
LabelID int64
ResourceID int64
ResourceName string
ResourceType string
}

View File

@ -1,90 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/goharbor/harbor/src/pkg/label/model"
)
func TestValidOfLabel(t *testing.T) {
cases := []struct {
label *model.Label
hasError bool
}{
{
label: &model.Label{
Name: "",
},
hasError: true,
},
{
label: &model.Label{
Name: "test",
Scope: "",
},
hasError: true,
},
{
label: &model.Label{
Name: "test",
Scope: "invalid_scope",
},
hasError: true,
},
{
label: &model.Label{
Name: "test",
Scope: "g",
},
hasError: false,
},
{
label: &model.Label{
Name: "test",
Scope: "p",
},
hasError: true,
},
{
label: &model.Label{
Name: "test",
Scope: "p",
ProjectID: -1,
},
hasError: true,
},
{
label: &model.Label{
Name: "test",
Scope: "p",
ProjectID: 1,
},
hasError: false,
},
}
for _, c := range cases {
err := c.label.Valid()
if c.hasError {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
}
}
}

View File

@ -1,110 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api
import (
"net/http"
"strconv"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/core/label"
"github.com/goharbor/harbor/src/lib/log"
pkg_label "github.com/goharbor/harbor/src/pkg/label"
"github.com/goharbor/harbor/src/pkg/label/model"
)
// LabelResourceAPI provides the related basic functions to handle marking labels to resources
type LabelResourceAPI struct {
BaseController
labelManager label.Manager
}
// Prepare resources for follow-up actions.
func (lra *LabelResourceAPI) Prepare() {
lra.BaseController.Prepare()
// Create label manager
lra.labelManager = &label.BaseManager{
LabelMgr: pkg_label.Mgr,
}
}
func (lra *LabelResourceAPI) getLabelsOfResource(rType string, rIDOrName interface{}) {
labels, err := lra.labelManager.GetLabelsOfResource(rType, rIDOrName)
if err != nil {
lra.handleErrors(err)
return
}
lra.Data["json"] = labels
if err := lra.ServeJSON(); err != nil {
log.Errorf("failed to serve json, %v", err)
lra.handleErrors(err)
return
}
}
func (lra *LabelResourceAPI) markLabelToResource(rl *models.ResourceLabel) {
labelID, err := lra.labelManager.MarkLabelToResource(rl)
if err != nil {
lra.handleErrors(err)
return
}
// return the ID of label and return status code 200 rather than 201 as the label is not created
lra.Redirect(http.StatusOK, strconv.FormatInt(labelID, 10))
}
func (lra *LabelResourceAPI) removeLabelFromResource(rType string, rIDOrName interface{}, labelID int64) {
if err := lra.labelManager.RemoveLabelFromResource(rType, rIDOrName, labelID); err != nil {
lra.handleErrors(err)
return
}
}
// eat the error of validate method of label manager
func (lra *LabelResourceAPI) validate(labelID, projectID int64) (*model.Label, bool) {
label, err := lra.labelManager.Validate(labelID, projectID)
if err != nil {
lra.handleErrors(err)
return nil, false
}
return label, true
}
// eat the error of exists method of label manager
func (lra *LabelResourceAPI) exists(labelID int64) (*model.Label, bool) {
label, err := lra.labelManager.Exists(labelID)
if err != nil {
return nil, false
}
return label, true
}
// Handle different kinds of errors.
func (lra *LabelResourceAPI) handleErrors(err error) {
switch err.(type) {
case *label.ErrLabelBadRequest:
lra.SendBadRequestError(err)
case *label.ErrLabelConflict:
lra.SendConflictError(err)
case *label.ErrLabelNotFound:
lra.SendNotFoundError(err)
default:
lra.SendInternalServerError(err)
}
}

View File

@ -1,90 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package label
import (
"fmt"
)
// ErrLabelBase contains the basic required info for building the final errors.
type ErrLabelBase struct {
LabelID int64
ResourceType string
ResourceIDOrName interface{}
}
// ErrLabelNotFound defines the error of not found label on the resource
// or the specified label is not found.
type ErrLabelNotFound struct {
ErrLabelBase
}
// ErrLabelConflict defines the error of label conflicts on the resource.
type ErrLabelConflict struct {
ErrLabelBase
}
// ErrLabelBadRequest defines the error of bad request to the resource.
type ErrLabelBadRequest struct {
Message string
}
// NewErrLabelNotFound builds an error with ErrLabelNotFound type
func NewErrLabelNotFound(labelID int64, resourceType string, resourceIDOrName interface{}) *ErrLabelNotFound {
return &ErrLabelNotFound{
ErrLabelBase{
LabelID: labelID,
ResourceType: resourceType,
ResourceIDOrName: resourceIDOrName,
},
}
}
// Error returns the error message of ErrLabelNotFound.
func (nf *ErrLabelNotFound) Error() string {
if len(nf.ResourceType) > 0 && nf.ResourceIDOrName != nil {
return fmt.Sprintf("not found: label '%d' on %s '%v'", nf.LabelID, nf.ResourceType, nf.ResourceIDOrName)
}
return fmt.Sprintf("not found: label '%d'", nf.LabelID)
}
// NewErrLabelConflict builds an error with NewErrLabelConflict type.
func NewErrLabelConflict(labelID int64, resourceType string, resourceIDOrName interface{}) *ErrLabelConflict {
return &ErrLabelConflict{
ErrLabelBase{
LabelID: labelID,
ResourceType: resourceType,
ResourceIDOrName: resourceIDOrName,
},
}
}
// Error returns the error message of ErrLabelConflict.
func (cl *ErrLabelConflict) Error() string {
return fmt.Sprintf("conflict: %s '%v' is already marked with label '%d'", cl.ResourceType, cl.ResourceIDOrName, cl.LabelID)
}
// NewErrLabelBadRequest builds an error with ErrLabelBadRequest type.
func NewErrLabelBadRequest(message string) *ErrLabelBadRequest {
return &ErrLabelBadRequest{
Message: message,
}
}
// Error returns the error message of ErrLabelBadRequest.
func (br *ErrLabelBadRequest) Error() string {
return br.Message
}

View File

@ -1,33 +0,0 @@
package label
import (
"fmt"
"testing"
)
// Test cases for kinds of error definitions.
func TestErrorFormats(t *testing.T) {
br := NewErrLabelBadRequest("bad requests")
if !checkErrorFormat(br, "bad requests") {
t.Fatalf("expect an error with ErrLabelBadRequest kind but got incorrect format '%v'", br)
}
cf := NewErrLabelConflict(1, "c", "repo1/mychart:1.0.0")
if !checkErrorFormat(cf, fmt.Sprintf("conflict: %s '%v' is already marked with label '%d'", "c", "repo1/mychart:1.0.0", 1)) {
t.Fatalf("expect an error with ErrLabelConflict kind but got incorrect format '%v'", cf)
}
nf := NewErrLabelNotFound(1, "c", "repo1/mychart:1.0.0")
if !checkErrorFormat(nf, fmt.Sprintf("not found: label '%d' on %s '%v'", 1, "c", "repo1/mychart:1.0.0")) {
t.Fatalf("expect an error with ErrLabelNotFound kind but got incorrect format '%v'", nf)
}
nf2 := NewErrLabelNotFound(1, "", "")
if !checkErrorFormat(nf2, fmt.Sprintf("not found: label '%d'", 1)) {
t.Fatalf("expect an error with ErrLabelNotFound kind but got incorrect format %v", nf2)
}
}
func checkErrorFormat(err error, msg string) bool {
return err.Error() == msg
}

View File

@ -1,162 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package label
import (
"fmt"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/label"
"github.com/goharbor/harbor/src/pkg/label/model"
)
// Manager defines the related operations for label management
type Manager interface {
// Mark label to the resource.
//
// If succeed, the relationship ID will be returned.
// Otherwise, an non-nil error will be returned.
MarkLabelToResource(label *models.ResourceLabel) (int64, error)
// Remove the label from the resource.
// Resource type and ID(/name) should be provided to identify the relationship.
//
// An non-nil error will be got if meet any issues or nil error returned.
RemoveLabelFromResource(resourceType string, resourceIDOrName interface{}, labelID int64) error
// Get labels for the specified resource.
// Resource is identified by the resource type and ID(/name).
//
// If succeed, a label list is returned.
// Otherwise, a non-nil error will be returned.
GetLabelsOfResource(resourceType string, resourceIDOrName interface{}) ([]*model.Label, error)
// Check the existence of the specified label.
//
// If label existing, a non-nil label object is returned and nil error is set.
// A non-nil error will be set if any issues met while checking or label is not found.
Exists(labelID int64) (*model.Label, error)
// Validate if the scope of the input label is correct.
// If the scope is project level, the projectID is required then.
//
// If everything is ok, an validated label reference will be returned.
// Otherwise, a non-nil error is returned.
Validate(labelID int64, projectID int64) (*model.Label, error)
}
// BaseManager is the default implementation of the Manager interface.
type BaseManager struct {
LabelMgr label.Manager
}
// MarkLabelToResource is the implementation of same method in Manager interface.
func (bm *BaseManager) MarkLabelToResource(label *models.ResourceLabel) (int64, error) {
if label == nil {
return -1, errors.New("nil label object")
}
// Use ID or name of resource. ID first.
var rIDOrName interface{}
if label.ResourceID != 0 {
rIDOrName = label.ResourceID
} else {
rIDOrName = label.ResourceName
}
rlabel, err := dao.GetResourceLabel(label.ResourceType, rIDOrName, label.LabelID)
if err != nil {
return -1, fmt.Errorf("failed to check the existence of label %d for resource %s %v: %v", label.LabelID, label.ResourceType, rIDOrName, err)
}
if rlabel != nil {
return -1, NewErrLabelConflict(label.LabelID, label.ResourceType, rIDOrName)
}
if _, err := dao.AddResourceLabel(label); err != nil {
return -1, fmt.Errorf("failed to add label %d to resource %s %v: %v", label.LabelID, label.ResourceType, rIDOrName, err)
}
// return the ID of label
return label.LabelID, nil
}
// RemoveLabelFromResource is the implementation of same method in Manager interface.
func (bm *BaseManager) RemoveLabelFromResource(resourceType string, resourceIDOrName interface{}, labelID int64) error {
rl, err := dao.GetResourceLabel(resourceType, resourceIDOrName, labelID)
if err != nil {
return fmt.Errorf("failed to check the existence of label %d for resource %s %v: %v", labelID, resourceType, resourceIDOrName, err)
}
if rl == nil {
return NewErrLabelNotFound(labelID, resourceType, resourceIDOrName)
}
if err = dao.DeleteResourceLabel(rl.ID); err != nil {
return fmt.Errorf("failed to delete resource label record %d: %v", rl.ID, err)
}
return nil
}
// GetLabelsOfResource is the implementation of same method in Manager interface.
func (bm *BaseManager) GetLabelsOfResource(resourceType string, resourceIDOrName interface{}) ([]*model.Label, error) {
labels, err := dao.GetLabelsOfResource(resourceType, resourceIDOrName)
if err != nil {
return nil, fmt.Errorf("failed to get labels of resource %s %v: %v", resourceType, resourceIDOrName, err)
}
return labels, nil
}
// Exists is the implementation of same method in Manager interface.
func (bm *BaseManager) Exists(labelID int64) (*model.Label, error) {
label, err := bm.LabelMgr.Get(orm.Context(), labelID)
if err != nil {
if errors.IsErr(err, errors.NotFoundCode) {
return nil, NewErrLabelNotFound(labelID, "", nil)
}
return nil, fmt.Errorf("failed to get label %d: %v", labelID, err)
}
return label, nil
}
// Validate is the implementation of same method in Manager interface.
func (bm *BaseManager) Validate(labelID int64, projectID int64) (*model.Label, error) {
label, err := bm.LabelMgr.Get(orm.Context(), labelID)
if err != nil {
if errors.IsErr(err, errors.NotFoundCode) {
return nil, NewErrLabelNotFound(labelID, "", nil)
}
return nil, fmt.Errorf("failed to get label %d: %v", labelID, err)
}
if label.Level != common.LabelLevelUser {
return nil, NewErrLabelBadRequest("only user level labels can be used")
}
if label.Scope == common.LabelScopeProject {
if projectID != label.ProjectID {
return nil, NewErrLabelBadRequest("can not add labels which don't belong to the project to the resources under the project")
}
}
return label, nil
}

View File

@ -77,19 +77,3 @@ type Reference struct {
func (r *Reference) TableName() string { func (r *Reference) TableName() string {
return "label_reference" return "label_reference"
} }
// ResourceLabel records the relationship between resource and label
type ResourceLabel struct {
ID int64 `orm:"pk;auto;column(id)"`
LabelID int64 `orm:"column(label_id)"`
ResourceID int64 `orm:"column(resource_id)"`
ResourceName string `orm:"column(resource_name)"`
ResourceType string `orm:"column(resource_type)"`
CreationTime time.Time `orm:"column(creation_time);auto_now_add"`
UpdateTime time.Time `orm:"column(update_time);auto_now"`
}
// TableName ...
func (r *ResourceLabel) TableName() string {
return "harbor_resource_label"
}