mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-30 06:18:02 +02:00
Merge pull request #14300 from heww/quota-apis
refactor: generate quota APIs by go-swagger
This commit is contained in:
commit
fba6dd799e
@ -1806,120 +1806,6 @@ paths:
|
|||||||
description: User does not have permission to call this API.
|
description: User does not have permission to call this API.
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
'/quotas':
|
|
||||||
get:
|
|
||||||
summary: List quotas
|
|
||||||
description: List quotas
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
parameters:
|
|
||||||
- name: reference
|
|
||||||
in: query
|
|
||||||
description: The reference type of quota.
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
- name: reference_id
|
|
||||||
in: query
|
|
||||||
description: The reference id of quota.
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
- name: sort
|
|
||||||
in: query
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
description: |
|
|
||||||
Sort method, valid values include:
|
|
||||||
'hard.resource_name', '-hard.resource_name', 'used.resource_name', '-used.resource_name'.
|
|
||||||
Here '-' stands for descending order, resource_name should be the real resource name of the quota.
|
|
||||||
- name: page
|
|
||||||
in: query
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
required: false
|
|
||||||
description: 'The page number, default is 1.'
|
|
||||||
- name: page_size
|
|
||||||
in: query
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
required: false
|
|
||||||
description: 'The size of per page, default is 10, maximum is 100.'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successfully retrieved the quotas.
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/Quota'
|
|
||||||
headers:
|
|
||||||
X-Total-Count:
|
|
||||||
description: The total count of access logs
|
|
||||||
type: integer
|
|
||||||
Link:
|
|
||||||
description: Link refers to the previous page and next page
|
|
||||||
type: string
|
|
||||||
'401':
|
|
||||||
description: User is not authenticated.
|
|
||||||
'403':
|
|
||||||
description: User does not have permission to call this API.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
'/quotas/{id}':
|
|
||||||
get:
|
|
||||||
summary: Get the specified quota
|
|
||||||
description: Get the specified quota
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Quota
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
required: true
|
|
||||||
description: Quota ID
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successfully retrieved the quota.
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Quota'
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User does not have permission to call this API
|
|
||||||
'404':
|
|
||||||
description: Quota does not exist.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
put:
|
|
||||||
summary: Update the specified quota
|
|
||||||
description: Update hard limits of the specified quota
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Quota
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
required: true
|
|
||||||
description: Quota ID
|
|
||||||
- name: hard
|
|
||||||
in: body
|
|
||||||
required: true
|
|
||||||
description: The new hard limits for the quota
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/QuotaUpdateReq'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Updated quota hard limits successfully.
|
|
||||||
'400':
|
|
||||||
description: Illegal format of quota update request.
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User does not have permission to the quota.
|
|
||||||
'404':
|
|
||||||
description: Quota ID does not exist.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
'/projects/{project_id}/webhook/policies':
|
'/projects/{project_id}/webhook/policies':
|
||||||
get:
|
get:
|
||||||
summary: List project webhook policies.
|
summary: List project webhook policies.
|
||||||
@ -2736,30 +2622,6 @@ definitions:
|
|||||||
artifact_count:
|
artifact_count:
|
||||||
type: integer
|
type: integer
|
||||||
description: The count of artifacts in the repository
|
description: The count of artifacts in the repository
|
||||||
ProjectReq:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
project_name:
|
|
||||||
type: string
|
|
||||||
description: The name of the project.
|
|
||||||
metadata:
|
|
||||||
description: The metadata of the project.
|
|
||||||
$ref: '#/definitions/ProjectMetadata'
|
|
||||||
cve_allowlist:
|
|
||||||
description: The CVE allowlist of the project.
|
|
||||||
$ref: '#/definitions/CVEAllowlist'
|
|
||||||
count_limit:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: The count quota of the project.
|
|
||||||
storage_limit:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: The storage quota of the project.
|
|
||||||
registry_id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: The ID of referenced registry when creating the proxy cache project
|
|
||||||
Project:
|
Project:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -2836,38 +2698,6 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
description: 'Whether this project reuse the system level CVE allowlist as the allowlist of its own. The valid values are "true", "false".
|
description: 'Whether this project reuse the system level CVE allowlist as the allowlist of its own. The valid values are "true", "false".
|
||||||
If it is set to "true" the actual allowlist associate with this project, if any, will be ignored.'
|
If it is set to "true" the actual allowlist associate with this project, if any, will be ignored.'
|
||||||
ProjectSummary:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
repo_count:
|
|
||||||
type: integer
|
|
||||||
description: The number of the repositories under this project.
|
|
||||||
chart_count:
|
|
||||||
type: integer
|
|
||||||
description: The total number of charts under this project.
|
|
||||||
project_admin_count:
|
|
||||||
type: integer
|
|
||||||
description: The total number of project admin members.
|
|
||||||
maintainer_count:
|
|
||||||
type: integer
|
|
||||||
description: The total number of maintainer members.
|
|
||||||
developer_count:
|
|
||||||
type: integer
|
|
||||||
description: The total number of developer members.
|
|
||||||
guest_count:
|
|
||||||
type: integer
|
|
||||||
description: The total number of guest members.
|
|
||||||
quota:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
hard:
|
|
||||||
$ref: "#/definitions/ResourceList"
|
|
||||||
description: The hard limits of the quota
|
|
||||||
used:
|
|
||||||
$ref: "#/definitions/ResourceList"
|
|
||||||
description: The used status of the quota
|
|
||||||
registry:
|
|
||||||
$ref: "#/definitions/Registry"
|
|
||||||
User:
|
User:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -3833,42 +3663,6 @@ definitions:
|
|||||||
cve_id:
|
cve_id:
|
||||||
type: string
|
type: string
|
||||||
description: The ID of the CVE, such as "CVE-2019-10164"
|
description: The ID of the CVE, such as "CVE-2019-10164"
|
||||||
ResourceList:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
QuotaUpdateReq:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
hard:
|
|
||||||
$ref: "#/definitions/ResourceList"
|
|
||||||
description: The new hard limits for the quota
|
|
||||||
QuotaRefObject:
|
|
||||||
type: object
|
|
||||||
additionalProperties: {}
|
|
||||||
Quota:
|
|
||||||
type: object
|
|
||||||
description: The quota object
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
description: ID of the quota
|
|
||||||
ref:
|
|
||||||
$ref: "#/definitions/QuotaRefObject"
|
|
||||||
description: The reference object of the quota
|
|
||||||
hard:
|
|
||||||
$ref: "#/definitions/ResourceList"
|
|
||||||
description: The hard limits of the quota
|
|
||||||
used:
|
|
||||||
$ref: "#/definitions/ResourceList"
|
|
||||||
description: The used status of the quota
|
|
||||||
creation_time:
|
|
||||||
type: string
|
|
||||||
description: the creation time of the quota
|
|
||||||
update_time:
|
|
||||||
type: string
|
|
||||||
description: the update time of the quota
|
|
||||||
WebhookTargetObject:
|
WebhookTargetObject:
|
||||||
type: object
|
type: object
|
||||||
description: The webhook policy target object.
|
description: The webhook policy target object.
|
||||||
|
@ -1700,6 +1700,114 @@ paths:
|
|||||||
$ref: '#/responses/404'
|
$ref: '#/responses/404'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/responses/500'
|
$ref: '#/responses/500'
|
||||||
|
'/quotas':
|
||||||
|
get:
|
||||||
|
summary: List quotas
|
||||||
|
description: List quotas
|
||||||
|
tags:
|
||||||
|
- quota
|
||||||
|
operationId: listQuotas
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- $ref: '#/parameters/page'
|
||||||
|
- $ref: '#/parameters/pageSize'
|
||||||
|
- name: reference
|
||||||
|
in: query
|
||||||
|
description: The reference type of quota.
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
- name: reference_id
|
||||||
|
in: query
|
||||||
|
description: The reference id of quota.
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
- name: sort
|
||||||
|
in: query
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Sort method, valid values include:
|
||||||
|
'hard.resource_name', '-hard.resource_name', 'used.resource_name', '-used.resource_name'.
|
||||||
|
Here '-' stands for descending order, resource_name should be the real resource name of the quota.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Successfully retrieved the quotas.
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Quota'
|
||||||
|
headers:
|
||||||
|
X-Total-Count:
|
||||||
|
description: The total count of access logs
|
||||||
|
type: integer
|
||||||
|
Link:
|
||||||
|
description: Link refers to the previous page and next page
|
||||||
|
type: string
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
'/quotas/{id}':
|
||||||
|
get:
|
||||||
|
summary: Get the specified quota
|
||||||
|
description: Get the specified quota
|
||||||
|
tags:
|
||||||
|
- quota
|
||||||
|
operationId: getQuota
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
description: Quota ID
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Successfully retrieved the quota.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Quota'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'404':
|
||||||
|
$ref: '#/responses/404'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
put:
|
||||||
|
summary: Update the specified quota
|
||||||
|
description: Update hard limits of the specified quota
|
||||||
|
tags:
|
||||||
|
- quota
|
||||||
|
operationId: updateQuota
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/parameters/requestId'
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
description: Quota ID
|
||||||
|
- name: hard
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
description: The new hard limits for the quota
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/QuotaUpdateReq'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '#/responses/200'
|
||||||
|
'400':
|
||||||
|
$ref: '#/responses/400'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'404':
|
||||||
|
$ref: '#/responses/404'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
/robots/{robot_id}:
|
/robots/{robot_id}:
|
||||||
get:
|
get:
|
||||||
summary: Get a robot account
|
summary: Get a robot account
|
||||||
@ -3585,6 +3693,10 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
description: The total number of limited guest members.
|
description: The total number of limited guest members.
|
||||||
quota:
|
quota:
|
||||||
|
$ref: "#/definitions/ProjectSummaryQuota"
|
||||||
|
registry:
|
||||||
|
$ref: "#/definitions/Registry"
|
||||||
|
ProjectSummaryQuota:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
hard:
|
hard:
|
||||||
@ -3593,8 +3705,6 @@ definitions:
|
|||||||
used:
|
used:
|
||||||
$ref: "#/definitions/ResourceList"
|
$ref: "#/definitions/ResourceList"
|
||||||
description: The used status of the quota
|
description: The used status of the quota
|
||||||
registry:
|
|
||||||
$ref: "#/definitions/Registry"
|
|
||||||
CVEAllowlist:
|
CVEAllowlist:
|
||||||
type: object
|
type: object
|
||||||
description: The CVE Allowlist for system or project
|
description: The CVE Allowlist for system or project
|
||||||
@ -3678,6 +3788,10 @@ definitions:
|
|||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
|
x-go-type:
|
||||||
|
type: ResourceList
|
||||||
|
import:
|
||||||
|
package: "github.com/goharbor/harbor/src/pkg/quota/types"
|
||||||
ReplicationExecution:
|
ReplicationExecution:
|
||||||
type: object
|
type: object
|
||||||
description: The replication execution
|
description: The replication execution
|
||||||
@ -4328,3 +4442,38 @@ definitions:
|
|||||||
retained:
|
retained:
|
||||||
type: integer
|
type: integer
|
||||||
x-omitempty: false
|
x-omitempty: false
|
||||||
|
QuotaUpdateReq:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
hard:
|
||||||
|
$ref: "#/definitions/ResourceList"
|
||||||
|
description: The new hard limits for the quota
|
||||||
|
QuotaRefObject:
|
||||||
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
|
Quota:
|
||||||
|
type: object
|
||||||
|
description: The quota object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
description: ID of the quota
|
||||||
|
ref:
|
||||||
|
$ref: "#/definitions/QuotaRefObject"
|
||||||
|
description: The reference object of the quota
|
||||||
|
hard:
|
||||||
|
$ref: "#/definitions/ResourceList"
|
||||||
|
description: The hard limits of the quota
|
||||||
|
x-omitempty: false
|
||||||
|
used:
|
||||||
|
$ref: "#/definitions/ResourceList"
|
||||||
|
description: The used status of the quota
|
||||||
|
x-omitempty: false
|
||||||
|
creation_time:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: the creation time of the quota
|
||||||
|
update_time:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: the update time of the quota
|
||||||
|
@ -158,7 +158,6 @@ var (
|
|||||||
{Name: common.MetricPath, Scope: SystemScope, Group: BasicGroup, EnvKey: "METRIC_PATH", DefaultValue: "/metrics", ItemType: &StringType{}, Editable: true},
|
{Name: common.MetricPath, Scope: SystemScope, Group: BasicGroup, EnvKey: "METRIC_PATH", DefaultValue: "/metrics", ItemType: &StringType{}, Editable: true},
|
||||||
|
|
||||||
{Name: common.QuotaPerProjectEnable, Scope: UserScope, Group: QuotaGroup, EnvKey: "QUOTA_PER_PROJECT_ENABLE", DefaultValue: "true", ItemType: &BoolType{}, Editable: true},
|
{Name: common.QuotaPerProjectEnable, Scope: UserScope, Group: QuotaGroup, EnvKey: "QUOTA_PER_PROJECT_ENABLE", DefaultValue: "true", ItemType: &BoolType{}, Editable: true},
|
||||||
{Name: common.CountPerProject, Scope: UserScope, Group: QuotaGroup, EnvKey: "COUNT_PER_PROJECT", DefaultValue: "-1", ItemType: &QuotaType{}, Editable: true},
|
|
||||||
{Name: common.StoragePerProject, Scope: UserScope, Group: QuotaGroup, EnvKey: "STORAGE_PER_PROJECT", DefaultValue: "-1", ItemType: &QuotaType{}, Editable: true},
|
{Name: common.StoragePerProject, Scope: UserScope, Group: QuotaGroup, EnvKey: "STORAGE_PER_PROJECT", DefaultValue: "-1", ItemType: &QuotaType{}, Editable: true},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -156,7 +156,6 @@ const (
|
|||||||
|
|
||||||
// Quota setting items for project
|
// Quota setting items for project
|
||||||
QuotaPerProjectEnable = "quota_per_project_enable"
|
QuotaPerProjectEnable = "quota_per_project_enable"
|
||||||
CountPerProject = "count_per_project"
|
|
||||||
StoragePerProject = "storage_per_project"
|
StoragePerProject = "storage_per_project"
|
||||||
|
|
||||||
// DefaultGCTimeWindowHours is the reserve blob time window used by GC, default is 2 hours
|
// DefaultGCTimeWindowHours is the reserve blob time window used by GC, default is 2 hours
|
||||||
|
@ -22,8 +22,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego/orm"
|
"github.com/astaxie/beego/orm"
|
||||||
"github.com/goharbor/harbor/src/pkg/quota/types"
|
|
||||||
"github.com/goharbor/harbor/src/replication/model"
|
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -263,24 +261,3 @@ type ProjectQueryResult struct {
|
|||||||
func (p *Project) TableName() string {
|
func (p *Project) TableName() string {
|
||||||
return ProjectTable
|
return ProjectTable
|
||||||
}
|
}
|
||||||
|
|
||||||
// QuotaSummary ...
|
|
||||||
type QuotaSummary struct {
|
|
||||||
Hard types.ResourceList `json:"hard"`
|
|
||||||
Used types.ResourceList `json:"used"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectSummary ...
|
|
||||||
type ProjectSummary struct {
|
|
||||||
RepoCount int64 `json:"repo_count"`
|
|
||||||
ChartCount uint64 `json:"chart_count"`
|
|
||||||
|
|
||||||
ProjectAdminCount int64 `json:"project_admin_count"`
|
|
||||||
MaintainerCount int64 `json:"maintainer_count"`
|
|
||||||
DeveloperCount int64 `json:"developer_count"`
|
|
||||||
GuestCount int64 `json:"guest_count"`
|
|
||||||
LimitedGuestCount int64 `json:"limited_guest_count"`
|
|
||||||
|
|
||||||
Quota *QuotaSummary `json:"quota,omitempty"`
|
|
||||||
Registry *model.Registry `json:"registry"`
|
|
||||||
}
|
|
||||||
|
@ -157,10 +157,6 @@ func init() {
|
|||||||
beego.Router("/api/"+api.APIVersion+"/chartrepo/:repo/charts/:name/:version/labels", chartLabelAPIType, "get:GetLabels;post:MarkLabel")
|
beego.Router("/api/"+api.APIVersion+"/chartrepo/:repo/charts/:name/:version/labels", chartLabelAPIType, "get:GetLabels;post:MarkLabel")
|
||||||
beego.Router("/api/"+api.APIVersion+"/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel")
|
beego.Router("/api/"+api.APIVersion+"/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel")
|
||||||
|
|
||||||
quotaAPIType := &QuotaAPI{}
|
|
||||||
beego.Router("/api/quotas", quotaAPIType, "get:List")
|
|
||||||
beego.Router("/api/quotas/:id([0-9]+)", quotaAPIType, "get:Get;put:Put")
|
|
||||||
|
|
||||||
beego.Router("/api/internal/switchquota", &InternalAPI{}, "put:SwitchQuota")
|
beego.Router("/api/internal/switchquota", &InternalAPI{}, "put:SwitchQuota")
|
||||||
beego.Router("/api/internal/syncquota", &InternalAPI{}, "post:SyncQuota")
|
beego.Router("/api/internal/syncquota", &InternalAPI{}, "post:SyncQuota")
|
||||||
|
|
||||||
@ -918,55 +914,3 @@ func (a testapi) RegistryUpdate(authInfo usrInfo, registryID int64, req *apimode
|
|||||||
|
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// QuotasGet returns quotas
|
|
||||||
func (a testapi) QuotasGet(query *apilib.QuotaQuery, authInfo ...usrInfo) (int, []apilib.Quota, error) {
|
|
||||||
_sling := sling.New().Get(a.basePath).
|
|
||||||
Path("api/quotas").
|
|
||||||
QueryStruct(query)
|
|
||||||
|
|
||||||
var successPayload []apilib.Quota
|
|
||||||
|
|
||||||
var httpStatusCode int
|
|
||||||
var err error
|
|
||||||
var body []byte
|
|
||||||
if len(authInfo) > 0 {
|
|
||||||
httpStatusCode, body, err = request(_sling, jsonAcceptHeader, authInfo[0])
|
|
||||||
} else {
|
|
||||||
httpStatusCode, body, err = request(_sling, jsonAcceptHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && httpStatusCode == 200 {
|
|
||||||
err = json.Unmarshal(body, &successPayload)
|
|
||||||
} else {
|
|
||||||
log.Println(string(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
return httpStatusCode, successPayload, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return specific quota
|
|
||||||
func (a testapi) QuotasGetByID(authInfo usrInfo, quotaID string) (int, apilib.Quota, error) {
|
|
||||||
_sling := sling.New().Get(a.basePath)
|
|
||||||
|
|
||||||
// create api path
|
|
||||||
path := "api/quotas/" + quotaID
|
|
||||||
_sling = _sling.Path(path)
|
|
||||||
|
|
||||||
var successPayload apilib.Quota
|
|
||||||
|
|
||||||
httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo)
|
|
||||||
if err == nil && httpStatusCode == 200 {
|
|
||||||
err = json.Unmarshal(body, &successPayload)
|
|
||||||
}
|
|
||||||
return httpStatusCode, successPayload, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update spec for the quota
|
|
||||||
func (a testapi) QuotasPut(authInfo usrInfo, quotaID string, req QuotaUpdateRequest) (int, error) {
|
|
||||||
path := "/api/quotas/" + quotaID
|
|
||||||
_sling := sling.New().Put(a.basePath).Path(path).BodyJSON(req)
|
|
||||||
|
|
||||||
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo)
|
|
||||||
return httpStatusCode, err
|
|
||||||
}
|
|
||||||
|
@ -1,149 +0,0 @@
|
|||||||
// Copyright 2018 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 (
|
|
||||||
"fmt"
|
|
||||||
"github.com/goharbor/harbor/src/common/rbac"
|
|
||||||
"github.com/goharbor/harbor/src/controller/quota"
|
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/quota/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// QuotaUpdateRequest struct for the body of put quota API
|
|
||||||
type QuotaUpdateRequest struct {
|
|
||||||
Hard types.ResourceList `json:"hard"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// QuotaAPI handles request to /api/quotas/
|
|
||||||
type QuotaAPI struct {
|
|
||||||
BaseController
|
|
||||||
id int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare validates the URL and the user
|
|
||||||
func (qa *QuotaAPI) Prepare() {
|
|
||||||
qa.BaseController.Prepare()
|
|
||||||
|
|
||||||
if !qa.SecurityCtx.IsAuthenticated() {
|
|
||||||
qa.SendUnAuthorizedError(errors.New("Unauthorized"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(qa.GetStringFromPath(":id")) != 0 {
|
|
||||||
id, err := qa.GetInt64FromPath(":id")
|
|
||||||
if err != nil || id <= 0 {
|
|
||||||
text := "invalid quota ID: "
|
|
||||||
if err != nil {
|
|
||||||
text += err.Error()
|
|
||||||
} else {
|
|
||||||
text += fmt.Sprintf("%d", id)
|
|
||||||
}
|
|
||||||
qa.SendBadRequestError(errors.New(text))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
qa.id = id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns quota by id
|
|
||||||
func (qa *QuotaAPI) Get() {
|
|
||||||
if !qa.SecurityCtx.Can(orm.Context(), rbac.ActionRead, rbac.ResourceQuota) {
|
|
||||||
qa.SendForbiddenError(errors.New(qa.SecurityCtx.GetUsername()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
quota, err := quota.Ctl.Get(qa.Ctx.Request.Context(), qa.id)
|
|
||||||
if err != nil {
|
|
||||||
qa.SendError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
qa.Data["json"] = quota
|
|
||||||
qa.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put update the quota
|
|
||||||
func (qa *QuotaAPI) Put() {
|
|
||||||
if !qa.SecurityCtx.Can(orm.Context(), rbac.ActionUpdate, rbac.ResourceQuota) {
|
|
||||||
qa.SendForbiddenError(errors.New(qa.SecurityCtx.GetUsername()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var req *QuotaUpdateRequest
|
|
||||||
if err := qa.DecodeJSONReq(&req); err != nil {
|
|
||||||
qa.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := qa.Ctx.Request.Context()
|
|
||||||
q, err := quota.Ctl.Get(ctx, qa.id)
|
|
||||||
if err != nil {
|
|
||||||
qa.SendError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := quota.Validate(ctx, q.Reference, req.Hard); err != nil {
|
|
||||||
qa.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
q.SetHard(req.Hard)
|
|
||||||
|
|
||||||
if err := quota.Ctl.Update(ctx, q); err != nil {
|
|
||||||
qa.SendInternalServerError(fmt.Errorf("failed to update hard limits of the quota, error: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns quotas by query
|
|
||||||
func (qa *QuotaAPI) List() {
|
|
||||||
if !qa.SecurityCtx.Can(orm.Context(), rbac.ActionList, rbac.ResourceQuota) {
|
|
||||||
qa.SendForbiddenError(errors.New(qa.SecurityCtx.GetUsername()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
page, size, err := qa.GetPaginationParams()
|
|
||||||
if err != nil {
|
|
||||||
qa.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
query := &q.Query{
|
|
||||||
Keywords: q.KeyWords{
|
|
||||||
"reference": qa.GetString("reference"),
|
|
||||||
"reference_id": qa.GetString("reference_id"),
|
|
||||||
},
|
|
||||||
PageNumber: page,
|
|
||||||
PageSize: size,
|
|
||||||
Sorting: qa.GetString("sort"),
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := qa.Ctx.Request.Context()
|
|
||||||
|
|
||||||
total, err := quota.Ctl.Count(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
qa.SendInternalServerError(fmt.Errorf("failed to query database for total of quotas, error: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
quotas, err := quota.Ctl.List(ctx, query, quota.WithReferenceObject())
|
|
||||||
if err != nil {
|
|
||||||
qa.SendInternalServerError(fmt.Errorf("failed to query database for quotas, error: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
qa.SetPaginationHeader(total, page, size)
|
|
||||||
qa.Data["json"] = quotas
|
|
||||||
qa.ServeJSON()
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
// Copyright 2018 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 (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
o "github.com/astaxie/beego/orm"
|
|
||||||
"github.com/goharbor/harbor/src/controller/quota"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/quota/driver"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/quota/types"
|
|
||||||
"github.com/goharbor/harbor/src/testing/apitests/apilib"
|
|
||||||
"github.com/goharbor/harbor/src/testing/mock"
|
|
||||||
drivertesting "github.com/goharbor/harbor/src/testing/pkg/quota/driver"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
reference = uuid.New().String()
|
|
||||||
hardLimits = types.ResourceList{types.ResourceStorage: -1}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
mockDriver := &drivertesting.Driver{}
|
|
||||||
|
|
||||||
mockHardLimitsFn := func() types.ResourceList {
|
|
||||||
return hardLimits
|
|
||||||
}
|
|
||||||
|
|
||||||
mockLoadFn := func(ctx context.Context, key string) driver.RefObject {
|
|
||||||
return driver.RefObject{"id": key}
|
|
||||||
}
|
|
||||||
|
|
||||||
mockValidateFn := func(hardLimits types.ResourceList) error {
|
|
||||||
if len(hardLimits) == 0 {
|
|
||||||
return fmt.Errorf("no resources found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mockDriver.On("HardLimits").Return(mockHardLimitsFn)
|
|
||||||
mock.OnAnything(mockDriver, "Load").Return(mockLoadFn, nil)
|
|
||||||
mock.OnAnything(mockDriver, "Validate").Return(mockValidateFn)
|
|
||||||
|
|
||||||
driver.Register(reference, mockDriver)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQuotaAPIList(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
apiTest := newHarborAPI()
|
|
||||||
|
|
||||||
ctx := orm.NewContext(context.TODO(), o.NewOrm())
|
|
||||||
var quotaIDs []int64
|
|
||||||
defer func() {
|
|
||||||
for _, quotaID := range quotaIDs {
|
|
||||||
quota.Ctl.Delete(ctx, quotaID)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
count := 10
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
quotaID, err := quota.Ctl.Create(ctx, reference, uuid.New().String(), hardLimits)
|
|
||||||
assert.Nil(err)
|
|
||||||
quotaIDs = append(quotaIDs, quotaID)
|
|
||||||
}
|
|
||||||
|
|
||||||
code, quotas, err := apiTest.QuotasGet(&apilib.QuotaQuery{Reference: reference}, *admin)
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(int(200), code)
|
|
||||||
assert.Len(quotas, count, fmt.Sprintf("quotas len should be %d", count))
|
|
||||||
|
|
||||||
code, quotas, err = apiTest.QuotasGet(&apilib.QuotaQuery{Reference: reference, PageSize: 1}, *admin)
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(int(200), code)
|
|
||||||
assert.Len(quotas, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQuotaAPIGet(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
apiTest := newHarborAPI()
|
|
||||||
|
|
||||||
ctx := orm.NewContext(context.TODO(), o.NewOrm())
|
|
||||||
quotaID, err := quota.Ctl.Create(ctx, reference, uuid.New().String(), hardLimits)
|
|
||||||
assert.Nil(err)
|
|
||||||
defer quota.Ctl.Delete(ctx, quotaID)
|
|
||||||
|
|
||||||
code, quota, err := apiTest.QuotasGetByID(*admin, fmt.Sprintf("%d", quotaID))
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(int(200), code)
|
|
||||||
assert.Equal(map[string]int64{"storage": -1}, quota.Hard)
|
|
||||||
|
|
||||||
code, _, err = apiTest.QuotasGetByID(*admin, "100")
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(int(404), code)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQuotaPut(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
apiTest := newHarborAPI()
|
|
||||||
|
|
||||||
ctx := orm.NewContext(context.TODO(), o.NewOrm())
|
|
||||||
quotaID, err := quota.Ctl.Create(ctx, reference, uuid.New().String(), hardLimits)
|
|
||||||
assert.Nil(err)
|
|
||||||
defer quota.Ctl.Delete(ctx, quotaID)
|
|
||||||
|
|
||||||
code, quota, err := apiTest.QuotasGetByID(*admin, fmt.Sprintf("%d", quotaID))
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(int(200), code)
|
|
||||||
assert.Equal(map[string]int64{"storage": -1}, quota.Hard)
|
|
||||||
|
|
||||||
code, err = apiTest.QuotasPut(*admin, fmt.Sprintf("%d", quotaID), QuotaUpdateRequest{})
|
|
||||||
assert.Nil(err, err)
|
|
||||||
assert.Equal(int(400), code)
|
|
||||||
|
|
||||||
code, err = apiTest.QuotasPut(*admin, fmt.Sprintf("%d", quotaID), QuotaUpdateRequest{Hard: types.ResourceList{types.ResourceStorage: 100}})
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(int(200), code)
|
|
||||||
|
|
||||||
code, quota, err = apiTest.QuotasGetByID(*admin, fmt.Sprintf("%d", quotaID))
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(int(200), code)
|
|
||||||
assert.Equal(map[string]int64{"storage": 100}, quota.Hard)
|
|
||||||
}
|
|
@ -17,6 +17,8 @@ package types
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -43,6 +45,11 @@ func (resource ResourceName) FormatValue(value int64) string {
|
|||||||
// ResourceList is a set of (resource name, value) pairs.
|
// ResourceList is a set of (resource name, value) pairs.
|
||||||
type ResourceList map[ResourceName]int64
|
type ResourceList map[ResourceName]int64
|
||||||
|
|
||||||
|
// Validate validates this resource list
|
||||||
|
func (resources ResourceList) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (resources ResourceList) String() string {
|
func (resources ResourceList) String() string {
|
||||||
bytes, _ := json.Marshal(resources)
|
bytes, _ := json.Marshal(resources)
|
||||||
return string(bytes)
|
return string(bytes)
|
||||||
|
@ -45,6 +45,7 @@ func New() http.Handler {
|
|||||||
SysteminfoAPI: newSystemInfoAPI(),
|
SysteminfoAPI: newSystemInfoAPI(),
|
||||||
PingAPI: newPingAPI(),
|
PingAPI: newPingAPI(),
|
||||||
GCAPI: newGCAPI(),
|
GCAPI: newGCAPI(),
|
||||||
|
QuotaAPI: newQuotaAPI(),
|
||||||
RetentionAPI: newRetentionAPI(),
|
RetentionAPI: newRetentionAPI(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
67
src/server/v2.0/handler/model/quota.go
Normal file
67
src/server/v2.0/handler/model/quota.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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 model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/quota"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/quota/types"
|
||||||
|
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Quota model
|
||||||
|
type Quota struct {
|
||||||
|
*quota.Quota
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSwagger converts the quota to the swagger model
|
||||||
|
func (q *Quota) ToSwagger(ctx context.Context) *models.Quota {
|
||||||
|
if q.Quota == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hard, err := q.GetHard()
|
||||||
|
if err != nil {
|
||||||
|
fields := log.Fields{"quota_id": q.ID, "error": err}
|
||||||
|
log.G(ctx).WithFields(fields).Warningf("failed to get hard from quota")
|
||||||
|
|
||||||
|
hard = types.ResourceList{}
|
||||||
|
}
|
||||||
|
|
||||||
|
used, err := q.GetUsed()
|
||||||
|
if err != nil {
|
||||||
|
fields := log.Fields{"quota_id": q.ID, "error": err}
|
||||||
|
log.G(ctx).WithFields(fields).Warningf("failed to get used from quota")
|
||||||
|
|
||||||
|
used = types.ResourceList{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.Quota{
|
||||||
|
ID: q.ID,
|
||||||
|
Ref: q.Ref,
|
||||||
|
Hard: hard,
|
||||||
|
Used: used,
|
||||||
|
CreationTime: strfmt.DateTime(q.CreationTime),
|
||||||
|
UpdateTime: strfmt.DateTime(q.UpdateTime),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQuota new quota instance
|
||||||
|
func NewQuota(quota *quota.Quota) *Quota {
|
||||||
|
return &Quota{Quota: quota}
|
||||||
|
}
|
@ -1,9 +1,22 @@
|
|||||||
|
// 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 handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/controller/retention"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -19,6 +32,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/controller/project"
|
"github.com/goharbor/harbor/src/controller/project"
|
||||||
"github.com/goharbor/harbor/src/controller/quota"
|
"github.com/goharbor/harbor/src/controller/quota"
|
||||||
"github.com/goharbor/harbor/src/controller/repository"
|
"github.com/goharbor/harbor/src/controller/repository"
|
||||||
|
"github.com/goharbor/harbor/src/controller/retention"
|
||||||
"github.com/goharbor/harbor/src/core/api"
|
"github.com/goharbor/harbor/src/core/api"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/goharbor/harbor/src/lib"
|
"github.com/goharbor/harbor/src/lib"
|
||||||
@ -606,10 +620,10 @@ func getProjectQuotaSummary(ctx context.Context, p *project.Project, summary *mo
|
|||||||
|
|
||||||
summary.Quota = &models.ProjectSummaryQuota{}
|
summary.Quota = &models.ProjectSummaryQuota{}
|
||||||
if hard, err := q.GetHard(); err == nil {
|
if hard, err := q.GetHard(); err == nil {
|
||||||
lib.JSONCopy(&summary.Quota.Hard, hard)
|
summary.Quota.Hard = hard
|
||||||
}
|
}
|
||||||
if used, err := q.GetUsed(); err == nil {
|
if used, err := q.GetUsed(); err == nil {
|
||||||
lib.JSONCopy(&summary.Quota.Used, used)
|
summary.Quota.Used = used
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
116
src/server/v2.0/handler/quota.go
Normal file
116
src/server/v2.0/handler/quota.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// 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 handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-openapi/runtime/middleware"
|
||||||
|
"github.com/goharbor/harbor/src/common/rbac"
|
||||||
|
"github.com/goharbor/harbor/src/controller/quota"
|
||||||
|
"github.com/goharbor/harbor/src/lib"
|
||||||
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
|
||||||
|
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||||
|
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/quota"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newQuotaAPI() *quotaAPI {
|
||||||
|
return "aAPI{
|
||||||
|
quotaCtl: quota.Ctl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type quotaAPI struct {
|
||||||
|
BaseAPI
|
||||||
|
quotaCtl quota.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qa *quotaAPI) GetQuota(ctx context.Context, params operation.GetQuotaParams) middleware.Responder {
|
||||||
|
if err := qa.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceQuota); err != nil {
|
||||||
|
return qa.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
quota, err := qa.quotaCtl.Get(ctx, params.ID, quota.WithReferenceObject())
|
||||||
|
if err != nil {
|
||||||
|
return qa.SendError(ctx, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
return operation.NewGetQuotaOK().WithPayload(model.NewQuota(quota).ToSwagger(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qa *quotaAPI) ListQuotas(ctx context.Context, params operation.ListQuotasParams) middleware.Responder {
|
||||||
|
if err := qa.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceQuota); err != nil {
|
||||||
|
return qa.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
query := &q.Query{
|
||||||
|
Keywords: q.KeyWords{
|
||||||
|
"reference": lib.StringValue(params.Reference),
|
||||||
|
"reference_id": lib.StringValue(params.ReferenceID),
|
||||||
|
},
|
||||||
|
PageNumber: *params.Page,
|
||||||
|
PageSize: *params.PageSize,
|
||||||
|
Sorting: lib.StringValue(params.Sort),
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := qa.quotaCtl.Count(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return qa.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
quotas, err := qa.quotaCtl.List(ctx, query, quota.WithReferenceObject())
|
||||||
|
if err != nil {
|
||||||
|
return qa.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := make([]*models.Quota, len(quotas))
|
||||||
|
for i, quota := range quotas {
|
||||||
|
payload[i] = model.NewQuota(quota).ToSwagger(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return operation.NewListQuotasOK().
|
||||||
|
WithXTotalCount(total).
|
||||||
|
WithLink(qa.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
||||||
|
WithPayload(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qa *quotaAPI) UpdateQuota(ctx context.Context, params operation.UpdateQuotaParams) middleware.Responder {
|
||||||
|
if err := qa.RequireSystemAccess(ctx, rbac.ActionUpdate, rbac.ResourceQuota); err != nil {
|
||||||
|
return qa.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Hard == nil || len(params.Hard.Hard) == 0 {
|
||||||
|
return qa.SendError(ctx, errors.BadRequestError(nil).WithMessage("hard required in body"))
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := qa.quotaCtl.Get(ctx, params.ID)
|
||||||
|
if err != nil {
|
||||||
|
return qa.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quota.Validate(ctx, q.Reference, params.Hard.Hard); err != nil {
|
||||||
|
return qa.SendError(ctx, errors.BadRequestError(nil).WithMessage(err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
q.SetHard(params.Hard.Hard)
|
||||||
|
|
||||||
|
if err := qa.quotaCtl.Update(ctx, q); err != nil {
|
||||||
|
return qa.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return operation.NewUpdateQuotaOK()
|
||||||
|
}
|
283
src/server/v2.0/handler/quota_test.go
Normal file
283
src/server/v2.0/handler/quota_test.go
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
// 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 handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/quota"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/quota/types"
|
||||||
|
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||||
|
"github.com/goharbor/harbor/src/server/v2.0/restapi"
|
||||||
|
quotatesting "github.com/goharbor/harbor/src/testing/controller/quota"
|
||||||
|
"github.com/goharbor/harbor/src/testing/mock"
|
||||||
|
htesting "github.com/goharbor/harbor/src/testing/server/v2.0/handler"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QuotaTestSuite struct {
|
||||||
|
htesting.Suite
|
||||||
|
|
||||||
|
quotaCtl *quotatesting.Controller
|
||||||
|
quota *quota.Quota
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuotaTestSuite) SetupSuite() {
|
||||||
|
suite.quota = "a.Quota{
|
||||||
|
ID: 1,
|
||||||
|
Reference: "project",
|
||||||
|
ReferenceID: "1",
|
||||||
|
Hard: `{"storage": 100}`,
|
||||||
|
Used: `{"storage": 1000}`,
|
||||||
|
CreationTime: time.Now(),
|
||||||
|
UpdateTime: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.quotaCtl = "atesting.Controller{}
|
||||||
|
|
||||||
|
suite.Config = &restapi.Config{
|
||||||
|
QuotaAPI: "aAPI{
|
||||||
|
quotaCtl: suite.quotaCtl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.Suite.SetupSuite()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuotaTestSuite) TestAuthorization() {
|
||||||
|
newBody := func(body interface{}) io.Reader {
|
||||||
|
if body == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := json.Marshal(body)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
return bytes.NewBuffer(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
quota := models.QuotaUpdateReq{
|
||||||
|
Hard: types.ResourceList{"storage": 1000},
|
||||||
|
}
|
||||||
|
|
||||||
|
reqs := []struct {
|
||||||
|
method string
|
||||||
|
url string
|
||||||
|
body interface{}
|
||||||
|
}{
|
||||||
|
{http.MethodGet, "/quotas/1", nil},
|
||||||
|
{http.MethodGet, "/quotas", nil},
|
||||||
|
{http.MethodPut, "/quotas/1", quota},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, req := range reqs {
|
||||||
|
{
|
||||||
|
// authorized required
|
||||||
|
suite.Security.On("IsAuthenticated").Return(false).Once()
|
||||||
|
|
||||||
|
res, err := suite.DoReq(req.method, req.url, newBody(req.body))
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(401, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// permission required
|
||||||
|
suite.Security.On("IsAuthenticated").Return(true).Once()
|
||||||
|
suite.Security.On("GetUsername").Return("username").Once()
|
||||||
|
suite.Security.On("Can", mock.Anything, mock.Anything, mock.Anything).Return(false).Once()
|
||||||
|
|
||||||
|
res, err := suite.DoReq(req.method, req.url, newBody(req.body))
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(403, res.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuotaTestSuite) TestGetQuota() {
|
||||||
|
times := 3
|
||||||
|
suite.Security.On("IsAuthenticated").Return(true).Times(times)
|
||||||
|
suite.Security.On("Can", mock.Anything, mock.Anything, mock.Anything).Return(true).Times(times)
|
||||||
|
|
||||||
|
{
|
||||||
|
// get quota failed
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Get").Return(nil, fmt.Errorf("failed to get quota")).Once()
|
||||||
|
|
||||||
|
res, err := suite.Get("/quotas/1")
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(500, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// quota not found
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Get").Return(nil, errors.NotFoundError(nil)).Once()
|
||||||
|
|
||||||
|
var quota map[string]interface{}
|
||||||
|
res, err := suite.GetJSON("/quotas/1", "a)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(404, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// quota found
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Get").Return(suite.quota, nil).Once()
|
||||||
|
|
||||||
|
var quota map[string]interface{}
|
||||||
|
res, err := suite.GetJSON("/quotas/1", "a)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(200, res.StatusCode)
|
||||||
|
suite.Equal(float64(1), quota["id"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuotaTestSuite) TestListQuotas() {
|
||||||
|
times := 5
|
||||||
|
suite.Security.On("IsAuthenticated").Return(true).Times(times)
|
||||||
|
suite.Security.On("Can", mock.Anything, mock.Anything, mock.Anything).Return(true).Times(times)
|
||||||
|
|
||||||
|
{
|
||||||
|
// list quotas failed
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Count").Return(int64(0), fmt.Errorf("failed to count quotas")).Once()
|
||||||
|
|
||||||
|
res, err := suite.Get("/quotas")
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(500, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// list quotas failed
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Count").Return(int64(1), nil).Once()
|
||||||
|
mock.OnAnything(suite.quotaCtl, "List").Return(nil, fmt.Errorf("failed to list quotas")).Once()
|
||||||
|
|
||||||
|
res, err := suite.Get("/quotas")
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(500, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// quotas not found
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Count").Return(int64(0), nil).Once()
|
||||||
|
mock.OnAnything(suite.quotaCtl, "List").Return(nil, nil).Once()
|
||||||
|
|
||||||
|
var quotas []interface{}
|
||||||
|
res, err := suite.GetJSON("/quotas", "as)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(200, res.StatusCode)
|
||||||
|
suite.Len(quotas, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// quotas found
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Count").Return(int64(3), nil).Once()
|
||||||
|
mock.OnAnything(suite.quotaCtl, "List").Return([]*quota.Quota{suite.quota}, nil).Once()
|
||||||
|
|
||||||
|
var quotas []interface{}
|
||||||
|
res, err := suite.GetJSON("/quotas?page_size=1&page=2", "as)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(200, res.StatusCode)
|
||||||
|
suite.Len(quotas, 1)
|
||||||
|
suite.Equal("3", res.Header.Get("X-Total-Count"))
|
||||||
|
suite.Contains(res.Header, "Link")
|
||||||
|
suite.Equal(`</api/v2.0/quotas?page=1&page_size=1>; rel="prev" , </api/v2.0/quotas?page=3&page_size=1>; rel="next"`, res.Header.Get("Link"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuotaTestSuite) TestUpdateQuota() {
|
||||||
|
times := 6
|
||||||
|
suite.Security.On("IsAuthenticated").Return(true).Times(times)
|
||||||
|
suite.Security.On("Can", mock.Anything, mock.Anything, mock.Anything).Return(true).Times(times)
|
||||||
|
|
||||||
|
{
|
||||||
|
// update quota no body
|
||||||
|
res, err := suite.Put("/quotas/1", nil)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(422, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// update quota with empty hard
|
||||||
|
quota := models.QuotaUpdateReq{
|
||||||
|
Hard: types.ResourceList{},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := suite.PutJSON("/quotas/1", quota)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(400, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// quota not found
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Get").Return(nil, errors.NotFoundError(nil)).Once()
|
||||||
|
|
||||||
|
quota := models.QuotaUpdateReq{
|
||||||
|
Hard: types.ResourceList{"storage": 1000},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := suite.PutJSON("/quotas/1", quota)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(404, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// update quota
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Get").Return(suite.quota, nil).Once()
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Update").Return(nil).Once()
|
||||||
|
|
||||||
|
quota := models.QuotaUpdateReq{
|
||||||
|
Hard: types.ResourceList{"storage": 1000},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := suite.PutJSON("/quotas/1", quota)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(200, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// update quota failed
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Get").Return(suite.quota, nil).Once()
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Update").Return(fmt.Errorf("failed to update the quota")).Once()
|
||||||
|
|
||||||
|
quota := models.QuotaUpdateReq{
|
||||||
|
Hard: types.ResourceList{"storage": 1000},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := suite.PutJSON("/quotas/1", quota)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(500, res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// resource not support
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Get").Return(suite.quota, nil).Once()
|
||||||
|
mock.OnAnything(suite.quotaCtl, "Update").Return(nil).Once()
|
||||||
|
|
||||||
|
quota := models.QuotaUpdateReq{
|
||||||
|
Hard: types.ResourceList{"size": 1000},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := suite.PutJSON("/quotas/1", quota)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(400, res.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuotaTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &QuotaTestSuite{})
|
||||||
|
}
|
@ -42,9 +42,6 @@ func registerLegacyRoutes() {
|
|||||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/?:name", &api.MetadataAPI{}, "get:Get")
|
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/?:name", &api.MetadataAPI{}, "get:Get")
|
||||||
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/", &api.MetadataAPI{}, "post:Post")
|
beego.Router("/api/"+version+"/projects/:id([0-9]+)/metadatas/", &api.MetadataAPI{}, "post:Post")
|
||||||
|
|
||||||
beego.Router("/api/"+version+"/quotas", &api.QuotaAPI{}, "get:List")
|
|
||||||
beego.Router("/api/"+version+"/quotas/:id([0-9]+)", &api.QuotaAPI{}, "get:Get;put:Put")
|
|
||||||
|
|
||||||
beego.Router("/api/"+version+"/system/CVEAllowlist", &api.SysCVEAllowlistAPI{}, "get:Get;put:Put")
|
beego.Router("/api/"+version+"/system/CVEAllowlist", &api.SysCVEAllowlistAPI{}, "get:Get;put:Put")
|
||||||
beego.Router("/api/"+version+"/system/oidc/ping", &api.OIDCAPI{}, "post:Ping")
|
beego.Router("/api/"+version+"/system/oidc/ping", &api.OIDCAPI{}, "post:Ping")
|
||||||
|
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Harbor API
|
|
||||||
*
|
|
||||||
* These APIs provide services for manipulating Harbor project.
|
|
||||||
*
|
|
||||||
* OpenAPI spec version: 0.3.0
|
|
||||||
*
|
|
||||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
|
||||||
*
|
|
||||||
* 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 apilib
|
|
||||||
|
|
||||||
// QuotaQuery query for quota
|
|
||||||
type QuotaQuery struct {
|
|
||||||
Reference string `url:"reference,omitempty"`
|
|
||||||
ReferenceID string `url:"reference_id,omitempty"`
|
|
||||||
Page int64 `url:"page,omitempty"`
|
|
||||||
PageSize int64 `url:"page_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quota ...
|
|
||||||
type Quota struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
Ref map[string]interface{} `json:"ref"`
|
|
||||||
Hard map[string]int64 `json:"hard"`
|
|
||||||
Used map[string]int64 `json:"used"`
|
|
||||||
}
|
|
@ -28,7 +28,7 @@ def get_endpoint():
|
|||||||
|
|
||||||
def _create_client(server, credential, debug, api_type="products"):
|
def _create_client(server, credential, debug, api_type="products"):
|
||||||
cfg = None
|
cfg = None
|
||||||
if api_type in ('projectv2', 'artifact', 'repository', 'scan', 'scanall', 'preheat', 'replication', 'robot', 'gc', 'retention'):
|
if api_type in ('projectv2', 'artifact', 'repository', 'scan', 'scanall', 'preheat', 'quota', 'replication', 'robot', 'gc', 'retention'):
|
||||||
cfg = v2_swagger_client.Configuration()
|
cfg = v2_swagger_client.Configuration()
|
||||||
else:
|
else:
|
||||||
cfg = swagger_client.Configuration()
|
cfg = swagger_client.Configuration()
|
||||||
@ -56,6 +56,7 @@ def _create_client(server, credential, debug, api_type="products"):
|
|||||||
"projectv2": v2_swagger_client.ProjectApi(v2_swagger_client.ApiClient(cfg)),
|
"projectv2": v2_swagger_client.ProjectApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
"artifact": v2_swagger_client.ArtifactApi(v2_swagger_client.ApiClient(cfg)),
|
"artifact": v2_swagger_client.ArtifactApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
"preheat": v2_swagger_client.PreheatApi(v2_swagger_client.ApiClient(cfg)),
|
"preheat": v2_swagger_client.PreheatApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
|
"quota": v2_swagger_client.QuotaApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
"repository": v2_swagger_client.RepositoryApi(v2_swagger_client.ApiClient(cfg)),
|
"repository": v2_swagger_client.RepositoryApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
"scan": v2_swagger_client.ScanApi(v2_swagger_client.ApiClient(cfg)),
|
"scan": v2_swagger_client.ScanApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
"scanall": v2_swagger_client.ScanAllApi(v2_swagger_client.ApiClient(cfg)),
|
"scanall": v2_swagger_client.ScanAllApi(v2_swagger_client.ApiClient(cfg)),
|
||||||
|
@ -137,7 +137,7 @@ class System(base.Base):
|
|||||||
params['reference'] = reference
|
params['reference'] = reference
|
||||||
params['reference_id'] = reference_id
|
params['reference_id'] = reference_id
|
||||||
|
|
||||||
client = self._get_client(**kwargs)
|
client = self._get_client(api_type='quota', **kwargs)
|
||||||
data, status_code, _ = client.quotas_get_with_http_info(**params)
|
data, status_code, _ = client.list_quotas_with_http_info(**params)
|
||||||
base._assert_status_code(200, status_code)
|
base._assert_status_code(200, status_code)
|
||||||
return data
|
return data
|
||||||
|
Loading…
Reference in New Issue
Block a user