mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 18:25:56 +01:00
feat(retention) refactor to use go swagger api
Signed-off-by: Ziming Zhang <zziming@vmware.com>
This commit is contained in:
parent
f566748c77
commit
39fb500318
@ -2334,316 +2334,6 @@ paths:
|
|||||||
description: User have no permission to delete immutable tags of the project.
|
description: User have no permission to delete immutable tags of the project.
|
||||||
'500':
|
'500':
|
||||||
description: Internal server errors.
|
description: Internal server errors.
|
||||||
'/retentions/metadatas':
|
|
||||||
get:
|
|
||||||
summary: Get Retention Metadatas
|
|
||||||
description: Get Retention Metadatas.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Retention
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Get Retention Metadatas successfully.
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RetentionMetadata'
|
|
||||||
|
|
||||||
'/retentions':
|
|
||||||
post:
|
|
||||||
summary: Create Retention Policy
|
|
||||||
description: |
|
|
||||||
Create Retention Policy, you can reference metadatas API for the policy model.
|
|
||||||
You can check project metadatas to find whether a retention policy is already binded.
|
|
||||||
This method should only be called when no retention policy binded to project yet.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Retention
|
|
||||||
parameters:
|
|
||||||
- name: policy
|
|
||||||
in: body
|
|
||||||
description: Create Retention Policy successfully.
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RetentionPolicy'
|
|
||||||
responses:
|
|
||||||
'201':
|
|
||||||
description: Project created successfully.
|
|
||||||
headers:
|
|
||||||
Location:
|
|
||||||
type: string
|
|
||||||
description: The URL of the created resource
|
|
||||||
'400':
|
|
||||||
description: Illegal format of provided ID value.
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User have no permission.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
|
|
||||||
'/retentions/{id}':
|
|
||||||
get:
|
|
||||||
summary: Get Retention Policy
|
|
||||||
description: Get Retention Policy.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Retention
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention ID.
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Get Retention Policy successfully.
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RetentionPolicy'
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User have no permission.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
put:
|
|
||||||
summary: Update Retention Policy
|
|
||||||
description: |
|
|
||||||
Update Retention Policy, you can reference metadatas API for the policy model.
|
|
||||||
You can check project metadatas to find whether a retention policy is already binded.
|
|
||||||
This method should only be called when retention policy has already binded to project.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention ID.
|
|
||||||
- name: policy
|
|
||||||
in: body
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RetentionPolicy'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Update Retention Policy successfully.
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User have no permission.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
|
|
||||||
'/retentions/{id}/executions':
|
|
||||||
post:
|
|
||||||
summary: Trigger a Retention job
|
|
||||||
description: Trigger a Retention job, if dry_run is True, nothing would be deleted actually.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Retention
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention ID.
|
|
||||||
- name: action
|
|
||||||
in: body
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
dry_run:
|
|
||||||
type: boolean
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Trigger a Retention job successfully.
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User have no permission.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
get:
|
|
||||||
summary: Get a Retention job
|
|
||||||
description: Get a Retention job, job status may be delayed before job service schedule it up.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Retention
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention ID.
|
|
||||||
- name: page
|
|
||||||
in: query
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
required: false
|
|
||||||
description: The page number.
|
|
||||||
- name: page_size
|
|
||||||
in: query
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
required: false
|
|
||||||
description: The size of per page.
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Get a Retention job successfully.
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
$ref: '#/definitions/RetentionExecution'
|
|
||||||
headers:
|
|
||||||
X-Total-Count:
|
|
||||||
description: The total count of available items
|
|
||||||
type: integer
|
|
||||||
Link:
|
|
||||||
description: Link to previous page and next page
|
|
||||||
type: string
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User have no permission.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
|
|
||||||
'/retentions/{id}/executions/{eid}':
|
|
||||||
patch:
|
|
||||||
summary: Stop a Retention job
|
|
||||||
description: Stop a Retention job, only support "stop" action now.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Retention
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention ID.
|
|
||||||
- name: eid
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention execution ID.
|
|
||||||
- name: action
|
|
||||||
in: body
|
|
||||||
description: The action, only support "stop" now.
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
action:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Stop a Retention job successfully.
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User have no permission.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
|
|
||||||
'/retentions/{id}/executions/{eid}/tasks':
|
|
||||||
get:
|
|
||||||
summary: Get Retention job tasks
|
|
||||||
description: Get Retention job tasks, each repository as a task.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Retention
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention ID.
|
|
||||||
- name: eid
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention execution ID.
|
|
||||||
- name: page
|
|
||||||
in: query
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
required: false
|
|
||||||
description: The page number.
|
|
||||||
- name: page_size
|
|
||||||
in: query
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
required: false
|
|
||||||
description: The size of per page.
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Get Retention job tasks successfully.
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
$ref: '#/definitions/RetentionExecutionTask'
|
|
||||||
headers:
|
|
||||||
X-Total-Count:
|
|
||||||
description: The total count of available items
|
|
||||||
type: integer
|
|
||||||
Link:
|
|
||||||
description: Link to previous page and next page
|
|
||||||
type: string
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User have no permission.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
|
|
||||||
'/retentions/{id}/executions/{eid}/tasks/{tid}':
|
|
||||||
get:
|
|
||||||
summary: Get Retention job task log
|
|
||||||
description: Get Retention job task log, tags ratain or deletion detail will be shown in a table.
|
|
||||||
tags:
|
|
||||||
- Products
|
|
||||||
- Retention
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention ID.
|
|
||||||
- name: eid
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention execution ID.
|
|
||||||
- name: tid
|
|
||||||
in: path
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
required: true
|
|
||||||
description: Retention execution ID.
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Get Retention job task log successfully.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
'401':
|
|
||||||
description: User need to log in first.
|
|
||||||
'403':
|
|
||||||
description: User have no permission.
|
|
||||||
'500':
|
|
||||||
description: Unexpected internal errors.
|
|
||||||
|
|
||||||
'/scanners':
|
'/scanners':
|
||||||
get:
|
get:
|
||||||
summary: List scanner registrations
|
summary: List scanner registrations
|
||||||
@ -4279,196 +3969,6 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
description: The webhook job update time.
|
description: The webhook job update time.
|
||||||
|
|
||||||
RetentionMetadata:
|
|
||||||
type: object
|
|
||||||
description: the tag retention metadata
|
|
||||||
properties:
|
|
||||||
templates:
|
|
||||||
type: array
|
|
||||||
description: templates
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RetentionRuleMetadata'
|
|
||||||
scope_selectors:
|
|
||||||
type: array
|
|
||||||
description: supported scope selectors
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RetentionSelectorMetadata'
|
|
||||||
tag_selectors:
|
|
||||||
type: array
|
|
||||||
description: supported tag selectors
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RetentionSelectorMetadata'
|
|
||||||
|
|
||||||
RetentionRuleMetadata:
|
|
||||||
type: object
|
|
||||||
description: the tag retention rule metadata
|
|
||||||
properties:
|
|
||||||
rule_template:
|
|
||||||
type: string
|
|
||||||
description: rule id
|
|
||||||
display_text:
|
|
||||||
type: string
|
|
||||||
description: rule display text
|
|
||||||
action:
|
|
||||||
type: string
|
|
||||||
description: rule action
|
|
||||||
params:
|
|
||||||
type: array
|
|
||||||
description: rule params
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RetentionRuleParamMetadata'
|
|
||||||
|
|
||||||
RetentionRuleParamMetadata:
|
|
||||||
type: object
|
|
||||||
description: rule param
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
unit:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
|
|
||||||
RetentionSelectorMetadata:
|
|
||||||
type: object
|
|
||||||
description: retention selector
|
|
||||||
properties:
|
|
||||||
display_text:
|
|
||||||
type: string
|
|
||||||
kind:
|
|
||||||
type: string
|
|
||||||
decorations:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
RetentionPolicy:
|
|
||||||
type: object
|
|
||||||
description: retention policy
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
algorithm:
|
|
||||||
type: string
|
|
||||||
rules:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RetentionRule'
|
|
||||||
trigger:
|
|
||||||
type: object
|
|
||||||
$ref: '#/definitions/RetentionRuleTrigger'
|
|
||||||
scope:
|
|
||||||
type: object
|
|
||||||
$ref: '#/definitions/RetentionPolicyScope'
|
|
||||||
|
|
||||||
RetentionRuleTrigger:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
kind:
|
|
||||||
type: string
|
|
||||||
settings:
|
|
||||||
type: object
|
|
||||||
references:
|
|
||||||
type: object
|
|
||||||
|
|
||||||
RetentionPolicyScope:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
level:
|
|
||||||
type: string
|
|
||||||
ref:
|
|
||||||
type: integer
|
|
||||||
|
|
||||||
RetentionRule:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
priority:
|
|
||||||
type: integer
|
|
||||||
disabled:
|
|
||||||
type: boolean
|
|
||||||
action:
|
|
||||||
type: string
|
|
||||||
template:
|
|
||||||
type: string
|
|
||||||
params:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: object
|
|
||||||
tag_selectors:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RetentionSelector'
|
|
||||||
scope_selectors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RetentionSelector'
|
|
||||||
|
|
||||||
RetentionSelector:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
kind:
|
|
||||||
type: string
|
|
||||||
decoration:
|
|
||||||
type: string
|
|
||||||
pattern:
|
|
||||||
type: string
|
|
||||||
extras:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
RetentionExecution:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
policy_id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
start_time:
|
|
||||||
type: string
|
|
||||||
end_time:
|
|
||||||
type: string
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
trigger:
|
|
||||||
type: string
|
|
||||||
dry_run:
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
RetentionExecutionTask:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
execution_id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
repository:
|
|
||||||
type: string
|
|
||||||
job_id:
|
|
||||||
type: string
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
status_code:
|
|
||||||
type: integer
|
|
||||||
status_revision:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
start_time:
|
|
||||||
type: string
|
|
||||||
end_time:
|
|
||||||
type: string
|
|
||||||
total:
|
|
||||||
type: integer
|
|
||||||
retained:
|
|
||||||
type: integer
|
|
||||||
QuotaSwitcher:
|
QuotaSwitcher:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -2343,6 +2343,323 @@ paths:
|
|||||||
description: The API server is alive
|
description: The API server is alive
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
/retentions/metadatas:
|
||||||
|
get:
|
||||||
|
summary: Get Retention Metadatas
|
||||||
|
description: Get Retention Metadatas.
|
||||||
|
operationId: getRentenitionMetadata
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Get Retention Metadatas successfully.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RetentionMetadata'
|
||||||
|
|
||||||
|
/retentions:
|
||||||
|
post:
|
||||||
|
summary: Create Retention Policy
|
||||||
|
operationId: createRetention
|
||||||
|
description: >-
|
||||||
|
Create Retention Policy, you can reference metadatas API for the policy model.
|
||||||
|
You can check project metadatas to find whether a retention policy is already binded.
|
||||||
|
This method should only be called when no retention policy binded to project yet.
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
parameters:
|
||||||
|
- name: policy
|
||||||
|
in: body
|
||||||
|
description: Create Retention Policy successfully.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RetentionPolicy'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
$ref: '#/responses/201'
|
||||||
|
'400':
|
||||||
|
$ref: '#/responses/400'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
|
||||||
|
/retentions/{id}:
|
||||||
|
get:
|
||||||
|
summary: Get Retention Policy
|
||||||
|
operationId: getRetention
|
||||||
|
description: Get Retention Policy.
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention ID.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Get Retention Policy successfully.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RetentionPolicy'
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
put:
|
||||||
|
summary: Update Retention Policy
|
||||||
|
operationId: updateRetention
|
||||||
|
description: >-
|
||||||
|
Update Retention Policy, you can reference metadatas API for the policy model.
|
||||||
|
You can check project metadatas to find whether a retention policy is already binded.
|
||||||
|
This method should only be called when retention policy has already binded to project.
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention ID.
|
||||||
|
- name: policy
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RetentionPolicy'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Update Retention Policy successfully.
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
|
||||||
|
/retentions/{id}/executions:
|
||||||
|
post:
|
||||||
|
summary: Trigger a Retention job
|
||||||
|
operationId: triggerRetentionJob
|
||||||
|
description: Trigger a Retention job, if dry_run is True, nothing would be deleted actually.
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
produces:
|
||||||
|
- text/plain
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention ID.
|
||||||
|
- name: body
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
dry_run:
|
||||||
|
type: boolean
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Trigger a Retention job successfully.
|
||||||
|
'201':
|
||||||
|
description: Retention job created successfully.
|
||||||
|
headers:
|
||||||
|
Location:
|
||||||
|
type: string
|
||||||
|
description: The URL of the created resource
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
get:
|
||||||
|
summary: Get Retention jobs
|
||||||
|
operationId: listRetentionJob
|
||||||
|
description: Get Retention jobs, job status may be delayed before job service schedule it up.
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention ID.
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: false
|
||||||
|
description: The page number.
|
||||||
|
- name: page_size
|
||||||
|
in: query
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: false
|
||||||
|
description: The size of per page.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Get a Retention job successfully.
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
$ref: '#/definitions/RetentionExecution'
|
||||||
|
headers:
|
||||||
|
X-Total-Count:
|
||||||
|
description: The total count of available items
|
||||||
|
type: integer
|
||||||
|
Link:
|
||||||
|
description: Link to previous page and next page
|
||||||
|
type: string
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
|
||||||
|
/retentions/{id}/executions/{eid}:
|
||||||
|
patch:
|
||||||
|
summary: Stop a Retention job
|
||||||
|
operationId: operateRetentionJob
|
||||||
|
description: Stop a Retention job, only support "stop" action now.
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention ID.
|
||||||
|
- name: eid
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention execution ID.
|
||||||
|
- name: body
|
||||||
|
in: body
|
||||||
|
description: The action, only support "stop" now.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
action:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Stop a Retention job successfully.
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
|
||||||
|
/retentions/{id}/executions/{eid}/tasks:
|
||||||
|
get:
|
||||||
|
summary: Get Retention job tasks
|
||||||
|
operationId: listRetentionTasks
|
||||||
|
description: Get Retention job tasks, each repository as a task.
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention ID.
|
||||||
|
- name: eid
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention execution ID.
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: false
|
||||||
|
description: The page number.
|
||||||
|
- name: page_size
|
||||||
|
in: query
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: false
|
||||||
|
description: The size of per page.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Get Retention job tasks successfully.
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
$ref: '#/definitions/RetentionExecutionTask'
|
||||||
|
headers:
|
||||||
|
X-Total-Count:
|
||||||
|
description: The total count of available items
|
||||||
|
type: integer
|
||||||
|
Link:
|
||||||
|
description: Link to previous page and next page
|
||||||
|
type: string
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
|
||||||
|
/retentions/{id}/executions/{eid}/tasks/{tid}:
|
||||||
|
get:
|
||||||
|
summary: Get Retention job task log
|
||||||
|
operationId: getRetentionTaskLog
|
||||||
|
description: Get Retention job task log, tags ratain or deletion detail will be shown in a table.
|
||||||
|
tags:
|
||||||
|
- Retention
|
||||||
|
produces:
|
||||||
|
- text/plain
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention ID.
|
||||||
|
- name: eid
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention execution ID.
|
||||||
|
- name: tid
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
required: true
|
||||||
|
description: Retention execution ID.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Get Retention job task log successfully.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
'401':
|
||||||
|
$ref: '#/responses/401'
|
||||||
|
'403':
|
||||||
|
$ref: '#/responses/403'
|
||||||
|
'500':
|
||||||
|
$ref: '#/responses/500'
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
query:
|
query:
|
||||||
name: q
|
name: q
|
||||||
@ -3822,3 +4139,193 @@ definitions:
|
|||||||
- Manual
|
- Manual
|
||||||
- Schedule
|
- Schedule
|
||||||
- Event
|
- Event
|
||||||
|
RetentionMetadata:
|
||||||
|
type: object
|
||||||
|
description: the tag retention metadata
|
||||||
|
properties:
|
||||||
|
templates:
|
||||||
|
type: array
|
||||||
|
description: templates
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/RetentionRuleMetadata'
|
||||||
|
scope_selectors:
|
||||||
|
type: array
|
||||||
|
description: supported scope selectors
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/RetentionSelectorMetadata'
|
||||||
|
tag_selectors:
|
||||||
|
type: array
|
||||||
|
description: supported tag selectors
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/RetentionSelectorMetadata'
|
||||||
|
|
||||||
|
RetentionRuleMetadata:
|
||||||
|
type: object
|
||||||
|
description: the tag retention rule metadata
|
||||||
|
properties:
|
||||||
|
rule_template:
|
||||||
|
type: string
|
||||||
|
description: rule id
|
||||||
|
display_text:
|
||||||
|
type: string
|
||||||
|
description: rule display text
|
||||||
|
action:
|
||||||
|
type: string
|
||||||
|
description: rule action
|
||||||
|
params:
|
||||||
|
type: array
|
||||||
|
description: rule params
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/RetentionRuleParamMetadata'
|
||||||
|
|
||||||
|
RetentionRuleParamMetadata:
|
||||||
|
type: object
|
||||||
|
description: rule param
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
unit:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
|
||||||
|
RetentionSelectorMetadata:
|
||||||
|
type: object
|
||||||
|
description: retention selector
|
||||||
|
properties:
|
||||||
|
display_text:
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
type: string
|
||||||
|
decorations:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
RetentionPolicy:
|
||||||
|
type: object
|
||||||
|
description: retention policy
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
algorithm:
|
||||||
|
type: string
|
||||||
|
rules:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/RetentionRule'
|
||||||
|
trigger:
|
||||||
|
type: object
|
||||||
|
$ref: '#/definitions/RetentionRuleTrigger'
|
||||||
|
scope:
|
||||||
|
type: object
|
||||||
|
$ref: '#/definitions/RetentionPolicyScope'
|
||||||
|
|
||||||
|
RetentionRuleTrigger:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
kind:
|
||||||
|
type: string
|
||||||
|
settings:
|
||||||
|
type: object
|
||||||
|
references:
|
||||||
|
type: object
|
||||||
|
|
||||||
|
RetentionPolicyScope:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
level:
|
||||||
|
type: string
|
||||||
|
ref:
|
||||||
|
type: integer
|
||||||
|
|
||||||
|
RetentionRule:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
priority:
|
||||||
|
type: integer
|
||||||
|
disabled:
|
||||||
|
type: boolean
|
||||||
|
action:
|
||||||
|
type: string
|
||||||
|
template:
|
||||||
|
type: string
|
||||||
|
params:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: object
|
||||||
|
tag_selectors:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/RetentionSelector'
|
||||||
|
scope_selectors:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/RetentionSelector'
|
||||||
|
|
||||||
|
RetentionSelector:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
kind:
|
||||||
|
type: string
|
||||||
|
decoration:
|
||||||
|
type: string
|
||||||
|
pattern:
|
||||||
|
type: string
|
||||||
|
extras:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
RetentionExecution:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
policy_id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
start_time:
|
||||||
|
type: string
|
||||||
|
end_time:
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
trigger:
|
||||||
|
type: string
|
||||||
|
dry_run:
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
RetentionExecutionTask:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
execution_id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
repository:
|
||||||
|
type: string
|
||||||
|
job_id:
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
status_code:
|
||||||
|
type: integer
|
||||||
|
status_revision:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
start_time:
|
||||||
|
type: string
|
||||||
|
end_time:
|
||||||
|
type: string
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
retained:
|
||||||
|
type: integer
|
||||||
|
@ -96,7 +96,7 @@ func initDatabaseForTest(db *models.Database) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if alias != "default" {
|
if alias != "default" {
|
||||||
if err = globalOrm.Using(alias); err != nil {
|
if err = GetOrmer().Using(alias); err != nil {
|
||||||
log.Fatalf("failed to create new orm: %v", err)
|
log.Fatalf("failed to create new orm: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func init() {
|
|||||||
notifier.Subscribe(event.TopicScanningCompleted, &scan.Handler{})
|
notifier.Subscribe(event.TopicScanningCompleted, &scan.Handler{})
|
||||||
notifier.Subscribe(event.TopicDeleteArtifact, &scan.DelArtHandler{Context: orm.Context})
|
notifier.Subscribe(event.TopicDeleteArtifact, &scan.DelArtHandler{Context: orm.Context})
|
||||||
notifier.Subscribe(event.TopicReplication, &artifact.ReplicationHandler{})
|
notifier.Subscribe(event.TopicReplication, &artifact.ReplicationHandler{})
|
||||||
notifier.Subscribe(event.TopicTagRetention, &artifact.RetentionHandler{RetentionController: artifact.DefaultRetentionControllerFunc})
|
notifier.Subscribe(event.TopicTagRetention, &artifact.RetentionHandler{})
|
||||||
|
|
||||||
// replication
|
// replication
|
||||||
notifier.Subscribe(event.TopicPushArtifact, &replication.Handler{})
|
notifier.Subscribe(event.TopicPushArtifact, &replication.Handler{})
|
||||||
|
@ -2,31 +2,22 @@ package artifact
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/controller/retention"
|
||||||
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/controller/event"
|
"github.com/goharbor/harbor/src/controller/event"
|
||||||
"github.com/goharbor/harbor/src/controller/event/handler/util"
|
"github.com/goharbor/harbor/src/controller/event/handler/util"
|
||||||
evtModel "github.com/goharbor/harbor/src/controller/event/model"
|
evtModel "github.com/goharbor/harbor/src/controller/event/model"
|
||||||
"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/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/pkg/notification"
|
"github.com/goharbor/harbor/src/pkg/notification"
|
||||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||||
"github.com/goharbor/harbor/src/pkg/retention"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RetentionHandler preprocess tag retention event data
|
// RetentionHandler preprocess tag retention event data
|
||||||
type RetentionHandler struct {
|
type RetentionHandler struct {
|
||||||
RetentionController func() retention.APIController
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultRetentionControllerFunc ...
|
|
||||||
var DefaultRetentionControllerFunc = NewRetentionController
|
|
||||||
|
|
||||||
// NewRetentionController ...
|
|
||||||
func NewRetentionController() retention.APIController {
|
|
||||||
return api.GetRetentionController()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name ...
|
// Name ...
|
||||||
@ -85,7 +76,8 @@ func (r *RetentionHandler) IsStateful() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RetentionHandler) constructRetentionPayload(event *event.RetentionEvent) (*model.Payload, bool, int64, error) {
|
func (r *RetentionHandler) constructRetentionPayload(event *event.RetentionEvent) (*model.Payload, bool, int64, error) {
|
||||||
task, err := r.RetentionController().GetRetentionExecTask(event.TaskID)
|
ctx := orm.Context()
|
||||||
|
task, err := retention.Ctl.GetRetentionExecTask(ctx, event.TaskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to get retention task %d: error: %v", event.TaskID, err)
|
log.Errorf("failed to get retention task %d: error: %v", event.TaskID, err)
|
||||||
return nil, false, 0, err
|
return nil, false, 0, err
|
||||||
@ -94,7 +86,7 @@ func (r *RetentionHandler) constructRetentionPayload(event *event.RetentionEvent
|
|||||||
return nil, false, 0, fmt.Errorf("task %d not found with retention event", event.TaskID)
|
return nil, false, 0, fmt.Errorf("task %d not found with retention event", event.TaskID)
|
||||||
}
|
}
|
||||||
|
|
||||||
execution, err := r.RetentionController().GetRetentionExec(task.ExecutionID)
|
execution, err := retention.Ctl.GetRetentionExec(ctx, task.ExecutionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to get retention execution %d: error: %v", task.ExecutionID, err)
|
log.Errorf("failed to get retention execution %d: error: %v", task.ExecutionID, err)
|
||||||
return nil, false, 0, err
|
return nil, false, 0, err
|
||||||
@ -107,7 +99,7 @@ func (r *RetentionHandler) constructRetentionPayload(event *event.RetentionEvent
|
|||||||
return nil, true, 0, nil
|
return nil, true, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
md, err := r.RetentionController().GetRetention(execution.PolicyID)
|
md, err := retention.Ctl.GetRetention(ctx, execution.PolicyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to get tag retention policy %d: error: %v", execution.PolicyID, err)
|
log.Errorf("failed to get tag retention policy %d: error: %v", execution.PolicyID, err)
|
||||||
return nil, false, 0, err
|
return nil, false, 0, err
|
||||||
|
@ -1,32 +1,54 @@
|
|||||||
package artifact
|
package artifact
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
|
"github.com/goharbor/harbor/src/controller/retention"
|
||||||
|
ret "github.com/goharbor/harbor/src/pkg/retention"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/controller/event"
|
"github.com/goharbor/harbor/src/controller/event"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/goharbor/harbor/src/lib/selector"
|
"github.com/goharbor/harbor/src/lib/selector"
|
||||||
"github.com/goharbor/harbor/src/pkg/notification"
|
"github.com/goharbor/harbor/src/pkg/notification"
|
||||||
|
retentiontesting "github.com/goharbor/harbor/src/testing/controller/retention"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRetentionHandler_Handle(t *testing.T) {
|
func TestRetentionHandler_Handle(t *testing.T) {
|
||||||
config.Init()
|
config.Init()
|
||||||
handler := &RetentionHandler{RetentionController: DefaultRetentionControllerFunc}
|
handler := &RetentionHandler{}
|
||||||
|
|
||||||
policyMgr := notification.PolicyMgr
|
policyMgr := notification.PolicyMgr
|
||||||
retentionCtlFunc := handler.RetentionController
|
oldretentionCtl := retention.Ctl
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
notification.PolicyMgr = policyMgr
|
notification.PolicyMgr = policyMgr
|
||||||
handler.RetentionController = retentionCtlFunc
|
retention.Ctl = oldretentionCtl
|
||||||
}()
|
}()
|
||||||
notification.PolicyMgr = &fakedNotificationPolicyMgr{}
|
notification.PolicyMgr = &fakedNotificationPolicyMgr{}
|
||||||
handler.RetentionController = retention.FakedRetentionControllerFunc
|
retentionCtl := &retentiontesting.Controller{}
|
||||||
|
retention.Ctl = retentionCtl
|
||||||
|
retentionCtl.On("GetRetentionExecTask", mock.Anything, mock.Anything).
|
||||||
|
Return(&ret.Task{
|
||||||
|
ID: 1,
|
||||||
|
ExecutionID: 1,
|
||||||
|
Status: "Success",
|
||||||
|
StartTime: time.Now(),
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}, nil)
|
||||||
|
retentionCtl.On("GetRetentionExec", mock.Anything, mock.Anything).Return(&ret.Execution{
|
||||||
|
ID: 1,
|
||||||
|
PolicyID: 1,
|
||||||
|
Status: "Success",
|
||||||
|
Trigger: "Manual",
|
||||||
|
DryRun: true,
|
||||||
|
StartTime: time.Now(),
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}, nil)
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
data interface{}
|
data interface{}
|
||||||
@ -80,3 +102,8 @@ func TestRetentionHandler_IsStateful(t *testing.T) {
|
|||||||
handler := &RetentionHandler{}
|
handler := &RetentionHandler{}
|
||||||
assert.False(t, handler.IsStateful())
|
assert.False(t, handler.IsStateful())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
dao.PrepareTestForPostgresSQL()
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
26
src/controller/retention/callback.go
Normal file
26
src/controller/retention/callback.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package retention
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/scheduler"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
err := scheduler.RegisterCallbackFunc(SchedulerCallback, retentionCallback)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to register retention callback, %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func retentionCallback(ctx context.Context, p string) error {
|
||||||
|
param := &TriggerParam{}
|
||||||
|
if err := json.Unmarshal([]byte(p), param); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal the param: %v", err)
|
||||||
|
}
|
||||||
|
_, err := Ctl.TriggerRetentionExec(ctx, param.PolicyID, param.Trigger, false)
|
||||||
|
return err
|
||||||
|
}
|
@ -19,53 +19,58 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
"github.com/goharbor/harbor/src/jobservice/job"
|
||||||
"github.com/goharbor/harbor/src/jobservice/logger"
|
"github.com/goharbor/harbor/src/jobservice/logger"
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
"github.com/goharbor/harbor/src/pkg/repository"
|
"github.com/goharbor/harbor/src/pkg/repository"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/retention"
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||||
"github.com/goharbor/harbor/src/pkg/scheduler"
|
"github.com/goharbor/harbor/src/pkg/scheduler"
|
||||||
"github.com/goharbor/harbor/src/pkg/task"
|
"github.com/goharbor/harbor/src/pkg/task"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// go:generate mockery -name APIController -case snake
|
// go:generate mockery -name Controller -case snake
|
||||||
|
|
||||||
// APIController to handle the requests related with retention
|
// Controller to handle the requests related with retention
|
||||||
type APIController interface {
|
type Controller interface {
|
||||||
GetRetention(id int64) (*policy.Metadata, error)
|
GetRetention(ctx context.Context, id int64) (*policy.Metadata, error)
|
||||||
|
|
||||||
CreateRetention(p *policy.Metadata) (int64, error)
|
CreateRetention(ctx context.Context, p *policy.Metadata) (int64, error)
|
||||||
|
|
||||||
UpdateRetention(p *policy.Metadata) error
|
UpdateRetention(ctx context.Context, p *policy.Metadata) error
|
||||||
|
|
||||||
DeleteRetention(id int64) error
|
DeleteRetention(ctx context.Context, id int64) error
|
||||||
|
|
||||||
TriggerRetentionExec(policyID int64, trigger string, dryRun bool) (int64, error)
|
TriggerRetentionExec(ctx context.Context, policyID int64, trigger string, dryRun bool) (int64, error)
|
||||||
|
|
||||||
OperateRetentionExec(eid int64, action string) error
|
OperateRetentionExec(ctx context.Context, eid int64, action string) error
|
||||||
|
|
||||||
GetRetentionExec(eid int64) (*Execution, error)
|
GetRetentionExec(ctx context.Context, eid int64) (*retention.Execution, error)
|
||||||
|
|
||||||
ListRetentionExecs(policyID int64, query *q.Query) ([]*Execution, error)
|
ListRetentionExecs(ctx context.Context, policyID int64, query *q.Query) ([]*retention.Execution, error)
|
||||||
|
|
||||||
GetTotalOfRetentionExecs(policyID int64) (int64, error)
|
GetTotalOfRetentionExecs(ctx context.Context, policyID int64) (int64, error)
|
||||||
|
|
||||||
ListRetentionExecTasks(executionID int64, query *q.Query) ([]*Task, error)
|
ListRetentionExecTasks(ctx context.Context, executionID int64, query *q.Query) ([]*retention.Task, error)
|
||||||
|
|
||||||
GetTotalOfRetentionExecTasks(executionID int64) (int64, error)
|
GetTotalOfRetentionExecTasks(ctx context.Context, executionID int64) (int64, error)
|
||||||
|
|
||||||
GetRetentionExecTaskLog(taskID int64) ([]byte, error)
|
GetRetentionExecTaskLog(ctx context.Context, taskID int64) ([]byte, error)
|
||||||
|
|
||||||
GetRetentionExecTask(taskID int64) (*Task, error)
|
GetRetentionExecTask(ctx context.Context, taskID int64) (*retention.Task, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultAPIController ...
|
var (
|
||||||
type DefaultAPIController struct {
|
// Ctl is a global retention controller instance
|
||||||
manager Manager
|
Ctl = NewController()
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultController ...
|
||||||
|
type defaultController struct {
|
||||||
|
manager retention.Manager
|
||||||
execMgr task.ExecutionManager
|
execMgr task.ExecutionManager
|
||||||
taskMgr task.Manager
|
taskMgr task.Manager
|
||||||
launcher Launcher
|
launcher retention.Launcher
|
||||||
projectManager project.Manager
|
projectManager project.Manager
|
||||||
repositoryMgr repository.Manager
|
repositoryMgr repository.Manager
|
||||||
scheduler scheduler.Scheduler
|
scheduler scheduler.Scheduler
|
||||||
@ -84,12 +89,12 @@ type TriggerParam struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRetention Get Retention
|
// GetRetention Get Retention
|
||||||
func (r *DefaultAPIController) GetRetention(id int64) (*policy.Metadata, error) {
|
func (r *defaultController) GetRetention(ctx context.Context, id int64) (*policy.Metadata, error) {
|
||||||
return r.manager.GetPolicy(id)
|
return r.manager.GetPolicy(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRetention Create Retention
|
// CreateRetention Create Retention
|
||||||
func (r *DefaultAPIController) CreateRetention(p *policy.Metadata) (int64, error) {
|
func (r *defaultController) CreateRetention(ctx context.Context, p *policy.Metadata) (int64, error) {
|
||||||
id, err := r.manager.CreatePolicy(p)
|
id, err := r.manager.CreatePolicy(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -99,9 +104,9 @@ func (r *DefaultAPIController) CreateRetention(p *policy.Metadata) (int64, error
|
|||||||
cron, ok := p.Trigger.Settings[policy.TriggerSettingsCron]
|
cron, ok := p.Trigger.Settings[policy.TriggerSettingsCron]
|
||||||
if ok && len(cron.(string)) > 0 {
|
if ok && len(cron.(string)) > 0 {
|
||||||
extras := make(map[string]interface{})
|
extras := make(map[string]interface{})
|
||||||
if _, err = r.scheduler.Schedule(orm.Context(), schedulerVendorType, id, "", cron.(string), SchedulerCallback, TriggerParam{
|
if _, err = r.scheduler.Schedule(ctx, schedulerVendorType, id, "", cron.(string), SchedulerCallback, TriggerParam{
|
||||||
PolicyID: id,
|
PolicyID: id,
|
||||||
Trigger: ExecutionTriggerSchedule,
|
Trigger: retention.ExecutionTriggerSchedule,
|
||||||
}, extras); err != nil {
|
}, extras); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -112,7 +117,7 @@ func (r *DefaultAPIController) CreateRetention(p *policy.Metadata) (int64, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRetention Update Retention
|
// UpdateRetention Update Retention
|
||||||
func (r *DefaultAPIController) UpdateRetention(p *policy.Metadata) error {
|
func (r *defaultController) UpdateRetention(ctx context.Context, p *policy.Metadata) error {
|
||||||
p0, err := r.manager.GetPolicy(p.ID)
|
p0, err := r.manager.GetPolicy(p.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -152,16 +157,16 @@ func (r *DefaultAPIController) UpdateRetention(p *policy.Metadata) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if needUn {
|
if needUn {
|
||||||
err = r.scheduler.UnScheduleByVendor(orm.Context(), schedulerVendorType, p.ID)
|
err = r.scheduler.UnScheduleByVendor(ctx, schedulerVendorType, p.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if needSch {
|
if needSch {
|
||||||
extras := make(map[string]interface{})
|
extras := make(map[string]interface{})
|
||||||
_, err := r.scheduler.Schedule(orm.Context(), schedulerVendorType, p.ID, "", p.Trigger.Settings[policy.TriggerSettingsCron].(string), SchedulerCallback, TriggerParam{
|
_, err := r.scheduler.Schedule(ctx, schedulerVendorType, p.ID, "", p.Trigger.Settings[policy.TriggerSettingsCron].(string), SchedulerCallback, TriggerParam{
|
||||||
PolicyID: p.ID,
|
PolicyID: p.ID,
|
||||||
Trigger: ExecutionTriggerSchedule,
|
Trigger: retention.ExecutionTriggerSchedule,
|
||||||
}, extras)
|
}, extras)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -172,19 +177,18 @@ func (r *DefaultAPIController) UpdateRetention(p *policy.Metadata) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRetention Delete Retention
|
// DeleteRetention Delete Retention
|
||||||
func (r *DefaultAPIController) DeleteRetention(id int64) error {
|
func (r *defaultController) DeleteRetention(ctx context.Context, id int64) error {
|
||||||
p, err := r.manager.GetPolicy(id)
|
p, err := r.manager.GetPolicy(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if p.Trigger.Kind == policy.TriggerKindSchedule && len(p.Trigger.Settings[policy.TriggerSettingsCron].(string)) > 0 {
|
if p.Trigger.Kind == policy.TriggerKindSchedule && len(p.Trigger.Settings[policy.TriggerSettingsCron].(string)) > 0 {
|
||||||
err = r.scheduler.UnScheduleByVendor(orm.Context(), schedulerVendorType, id)
|
err = r.scheduler.UnScheduleByVendor(ctx, schedulerVendorType, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := orm.Context()
|
|
||||||
err = r.deleteExecs(ctx, id)
|
err = r.deleteExecs(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -193,7 +197,7 @@ func (r *DefaultAPIController) DeleteRetention(id int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deleteExecs delete executions
|
// deleteExecs delete executions
|
||||||
func (r *DefaultAPIController) deleteExecs(ctx context.Context, vendorID int64) error {
|
func (r *defaultController) deleteExecs(ctx context.Context, vendorID int64) error {
|
||||||
executions, err := r.execMgr.List(ctx, &q.Query{
|
executions, err := r.execMgr.List(ctx, &q.Query{
|
||||||
Keywords: map[string]interface{}{
|
Keywords: map[string]interface{}{
|
||||||
"VendorType": job.Retention,
|
"VendorType": job.Retention,
|
||||||
@ -215,19 +219,18 @@ func (r *DefaultAPIController) deleteExecs(ctx context.Context, vendorID int64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TriggerRetentionExec Trigger Retention Execution
|
// TriggerRetentionExec Trigger Retention Execution
|
||||||
func (r *DefaultAPIController) TriggerRetentionExec(policyID int64, trigger string, dryRun bool) (int64, error) {
|
func (r *defaultController) TriggerRetentionExec(ctx context.Context, policyID int64, trigger string, dryRun bool) (int64, error) {
|
||||||
p, err := r.manager.GetPolicy(policyID)
|
p, err := r.manager.GetPolicy(policyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := orm.Context()
|
|
||||||
id, err := r.execMgr.Create(ctx, job.Retention, policyID, trigger,
|
id, err := r.execMgr.Create(ctx, job.Retention, policyID, trigger,
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"dry_run": dryRun,
|
"dry_run": dryRun,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if _, err = r.launcher.Launch(p, id, dryRun); err != nil {
|
if _, err = r.launcher.Launch(ctx, p, id, dryRun); err != nil {
|
||||||
if err1 := r.execMgr.StopAndWait(ctx, id, 10*time.Second); err1 != nil {
|
if err1 := r.execMgr.StopAndWait(ctx, id, 10*time.Second); err1 != nil {
|
||||||
logger.Errorf("failed to stop the retention execution %d: %v", id, err1)
|
logger.Errorf("failed to stop the retention execution %d: %v", id, err1)
|
||||||
}
|
}
|
||||||
@ -241,8 +244,7 @@ func (r *DefaultAPIController) TriggerRetentionExec(policyID int64, trigger stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OperateRetentionExec Operate Retention Execution
|
// OperateRetentionExec Operate Retention Execution
|
||||||
func (r *DefaultAPIController) OperateRetentionExec(eid int64, action string) error {
|
func (r *defaultController) OperateRetentionExec(ctx context.Context, eid int64, action string) error {
|
||||||
ctx := orm.Context()
|
|
||||||
e, err := r.execMgr.Get(ctx, eid)
|
e, err := r.execMgr.Get(ctx, eid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -252,15 +254,14 @@ func (r *DefaultAPIController) OperateRetentionExec(eid int64, action string) er
|
|||||||
}
|
}
|
||||||
switch action {
|
switch action {
|
||||||
case "stop":
|
case "stop":
|
||||||
return r.launcher.Stop(eid)
|
return r.launcher.Stop(ctx, eid)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("not support action %s", action)
|
return fmt.Errorf("not support action %s", action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRetentionExec Get Retention Execution
|
// GetRetentionExec Get Retention Execution
|
||||||
func (r *DefaultAPIController) GetRetentionExec(executionID int64) (*Execution, error) {
|
func (r *defaultController) GetRetentionExec(ctx context.Context, executionID int64) (*retention.Execution, error) {
|
||||||
ctx := orm.Context()
|
|
||||||
e, err := r.execMgr.Get(ctx, executionID)
|
e, err := r.execMgr.Get(ctx, executionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -270,8 +271,7 @@ func (r *DefaultAPIController) GetRetentionExec(executionID int64) (*Execution,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListRetentionExecs List Retention Executions
|
// ListRetentionExecs List Retention Executions
|
||||||
func (r *DefaultAPIController) ListRetentionExecs(policyID int64, query *q.Query) ([]*Execution, error) {
|
func (r *defaultController) ListRetentionExecs(ctx context.Context, policyID int64, query *q.Query) ([]*retention.Execution, error) {
|
||||||
ctx := orm.Context()
|
|
||||||
query = q.MustClone(query)
|
query = q.MustClone(query)
|
||||||
query.Keywords["VendorType"] = job.Retention
|
query.Keywords["VendorType"] = job.Retention
|
||||||
query.Keywords["VendorID"] = policyID
|
query.Keywords["VendorID"] = policyID
|
||||||
@ -279,15 +279,15 @@ func (r *DefaultAPIController) ListRetentionExecs(policyID int64, query *q.Query
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var executions []*Execution
|
var executions []*retention.Execution
|
||||||
for _, exec := range execs {
|
for _, exec := range execs {
|
||||||
executions = append(executions, convertExecution(exec))
|
executions = append(executions, convertExecution(exec))
|
||||||
}
|
}
|
||||||
return executions, nil
|
return executions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertExecution(exec *task.Execution) *Execution {
|
func convertExecution(exec *task.Execution) *retention.Execution {
|
||||||
return &Execution{
|
return &retention.Execution{
|
||||||
ID: exec.ID,
|
ID: exec.ID,
|
||||||
PolicyID: exec.VendorID,
|
PolicyID: exec.VendorID,
|
||||||
StartTime: exec.StartTime,
|
StartTime: exec.StartTime,
|
||||||
@ -299,8 +299,7 @@ func convertExecution(exec *task.Execution) *Execution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTotalOfRetentionExecs Count Retention Executions
|
// GetTotalOfRetentionExecs Count Retention Executions
|
||||||
func (r *DefaultAPIController) GetTotalOfRetentionExecs(policyID int64) (int64, error) {
|
func (r *defaultController) GetTotalOfRetentionExecs(ctx context.Context, policyID int64) (int64, error) {
|
||||||
ctx := orm.Context()
|
|
||||||
return r.execMgr.Count(ctx, &q.Query{
|
return r.execMgr.Count(ctx, &q.Query{
|
||||||
Keywords: map[string]interface{}{
|
Keywords: map[string]interface{}{
|
||||||
"VendorType": job.Retention,
|
"VendorType": job.Retention,
|
||||||
@ -310,8 +309,7 @@ func (r *DefaultAPIController) GetTotalOfRetentionExecs(policyID int64) (int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListRetentionExecTasks List Retention Execution Histories
|
// ListRetentionExecTasks List Retention Execution Histories
|
||||||
func (r *DefaultAPIController) ListRetentionExecTasks(executionID int64, query *q.Query) ([]*Task, error) {
|
func (r *defaultController) ListRetentionExecTasks(ctx context.Context, executionID int64, query *q.Query) ([]*retention.Task, error) {
|
||||||
ctx := orm.Context()
|
|
||||||
query = q.MustClone(query)
|
query = q.MustClone(query)
|
||||||
query.Keywords["VendorType"] = job.Retention
|
query.Keywords["VendorType"] = job.Retention
|
||||||
query.Keywords["ExecutionID"] = executionID
|
query.Keywords["ExecutionID"] = executionID
|
||||||
@ -319,15 +317,15 @@ func (r *DefaultAPIController) ListRetentionExecTasks(executionID int64, query *
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var tasks []*Task
|
var tasks []*retention.Task
|
||||||
for _, tk := range tks {
|
for _, tk := range tks {
|
||||||
tasks = append(tasks, convertTask(tk))
|
tasks = append(tasks, convertTask(tk))
|
||||||
}
|
}
|
||||||
return tasks, nil
|
return tasks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertTask(t *task.Task) *Task {
|
func convertTask(t *task.Task) *retention.Task {
|
||||||
return &Task{
|
return &retention.Task{
|
||||||
ID: t.ID,
|
ID: t.ID,
|
||||||
ExecutionID: t.ExecutionID,
|
ExecutionID: t.ExecutionID,
|
||||||
Repository: t.GetStringFromExtraAttrs("repository"),
|
Repository: t.GetStringFromExtraAttrs("repository"),
|
||||||
@ -342,8 +340,7 @@ func convertTask(t *task.Task) *Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTotalOfRetentionExecTasks Count Retention Execution Histories
|
// GetTotalOfRetentionExecTasks Count Retention Execution Histories
|
||||||
func (r *DefaultAPIController) GetTotalOfRetentionExecTasks(executionID int64) (int64, error) {
|
func (r *defaultController) GetTotalOfRetentionExecTasks(ctx context.Context, executionID int64) (int64, error) {
|
||||||
ctx := orm.Context()
|
|
||||||
return r.taskMgr.Count(ctx, &q.Query{
|
return r.taskMgr.Count(ctx, &q.Query{
|
||||||
Keywords: map[string]interface{}{
|
Keywords: map[string]interface{}{
|
||||||
"VendorType": job.Retention,
|
"VendorType": job.Retention,
|
||||||
@ -353,14 +350,12 @@ func (r *DefaultAPIController) GetTotalOfRetentionExecTasks(executionID int64) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRetentionExecTaskLog Get Retention Execution Task Log
|
// GetRetentionExecTaskLog Get Retention Execution Task Log
|
||||||
func (r *DefaultAPIController) GetRetentionExecTaskLog(taskID int64) ([]byte, error) {
|
func (r *defaultController) GetRetentionExecTaskLog(ctx context.Context, taskID int64) ([]byte, error) {
|
||||||
ctx := orm.Context()
|
|
||||||
return r.taskMgr.GetLog(ctx, taskID)
|
return r.taskMgr.GetLog(ctx, taskID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRetentionExecTask Get Retention Execution Task
|
// GetRetentionExecTask Get Retention Execution Task
|
||||||
func (r *DefaultAPIController) GetRetentionExecTask(taskID int64) (*Task, error) {
|
func (r *defaultController) GetRetentionExecTask(ctx context.Context, taskID int64) (*retention.Task, error) {
|
||||||
ctx := orm.Context()
|
|
||||||
t, err := r.taskMgr.Get(ctx, taskID)
|
t, err := r.taskMgr.Get(ctx, taskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -370,8 +365,7 @@ func (r *DefaultAPIController) GetRetentionExecTask(taskID int64) (*Task, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTaskInfo Update task info
|
// UpdateTaskInfo Update task info
|
||||||
func (r *DefaultAPIController) UpdateTaskInfo(taskID int64, total int, retained int) error {
|
func (r *defaultController) UpdateTaskInfo(ctx context.Context, taskID int64, total int, retained int) error {
|
||||||
ctx := orm.Context()
|
|
||||||
t, err := r.taskMgr.Get(ctx, taskID)
|
t, err := r.taskMgr.Get(ctx, taskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -383,15 +377,17 @@ func (r *DefaultAPIController) UpdateTaskInfo(taskID int64, total int, retained
|
|||||||
return r.taskMgr.UpdateExtraAttrs(ctx, taskID, t.ExtraAttrs)
|
return r.taskMgr.UpdateExtraAttrs(ctx, taskID, t.ExtraAttrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAPIController ...
|
// NewController ...
|
||||||
func NewAPIController(retentionMgr Manager, projectManager project.Manager, repositoryMgr repository.Manager, scheduler scheduler.Scheduler, retentionLauncher Launcher, execMgr task.ExecutionManager, taskMgr task.Manager) APIController {
|
func NewController() Controller {
|
||||||
return &DefaultAPIController{
|
retentionMgr := retention.NewManager()
|
||||||
|
retentionLauncher := retention.NewLauncher(project.Mgr, repository.Mgr, retentionMgr, task.ExecMgr, task.Mgr)
|
||||||
|
return &defaultController{
|
||||||
manager: retentionMgr,
|
manager: retentionMgr,
|
||||||
execMgr: execMgr,
|
execMgr: task.ExecMgr,
|
||||||
taskMgr: taskMgr,
|
taskMgr: task.Mgr,
|
||||||
launcher: retentionLauncher,
|
launcher: retentionLauncher,
|
||||||
projectManager: projectManager,
|
projectManager: project.Mgr,
|
||||||
repositoryMgr: repositoryMgr,
|
repositoryMgr: repository.Mgr,
|
||||||
scheduler: scheduler,
|
scheduler: scheduler.Sched,
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,8 +16,15 @@ package retention
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
"github.com/goharbor/harbor/src/jobservice/job"
|
||||||
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/retention"
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/dep"
|
"github.com/goharbor/harbor/src/pkg/retention/dep"
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/policy/rule"
|
"github.com/goharbor/harbor/src/pkg/retention/policy/rule"
|
||||||
@ -28,8 +35,6 @@ import (
|
|||||||
testingTask "github.com/goharbor/harbor/src/testing/pkg/task"
|
testingTask "github.com/goharbor/harbor/src/testing/pkg/task"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ControllerTestSuite struct {
|
type ControllerTestSuite struct {
|
||||||
@ -43,6 +48,11 @@ func (s *ControllerTestSuite) SetupSuite() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
dao.PrepareTestForPostgresSQL()
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
// TestController ...
|
// TestController ...
|
||||||
func TestController(t *testing.T) {
|
func TestController(t *testing.T) {
|
||||||
suite.Run(t, new(ControllerTestSuite))
|
suite.Run(t, new(ControllerTestSuite))
|
||||||
@ -55,7 +65,7 @@ func (s *ControllerTestSuite) TestPolicy() {
|
|||||||
retentionLauncher := &fakeLauncher{}
|
retentionLauncher := &fakeLauncher{}
|
||||||
execMgr := &testingTask.ExecutionManager{}
|
execMgr := &testingTask.ExecutionManager{}
|
||||||
taskMgr := &testingTask.Manager{}
|
taskMgr := &testingTask.Manager{}
|
||||||
retentionMgr := NewManager()
|
retentionMgr := retention.NewManager()
|
||||||
execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||||
execMgr.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
execMgr.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
||||||
execMgr.On("Get", mock.Anything, mock.Anything).Return(&task.Execution{
|
execMgr.On("Get", mock.Anything, mock.Anything).Return(&task.Execution{
|
||||||
@ -83,7 +93,15 @@ func (s *ControllerTestSuite) TestPolicy() {
|
|||||||
taskMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
taskMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||||
taskMgr.On("Stop", mock.Anything, mock.Anything).Return(nil)
|
taskMgr.On("Stop", mock.Anything, mock.Anything).Return(nil)
|
||||||
|
|
||||||
c := NewAPIController(retentionMgr, projectMgr, repositoryMgr, retentionScheduler, retentionLauncher, execMgr, taskMgr)
|
c := defaultController{
|
||||||
|
manager: retentionMgr,
|
||||||
|
execMgr: execMgr,
|
||||||
|
taskMgr: taskMgr,
|
||||||
|
launcher: retentionLauncher,
|
||||||
|
projectManager: projectMgr,
|
||||||
|
repositoryMgr: repositoryMgr,
|
||||||
|
scheduler: retentionScheduler,
|
||||||
|
}
|
||||||
|
|
||||||
p1 := &policy.Metadata{
|
p1 := &policy.Metadata{
|
||||||
Algorithm: "or",
|
Algorithm: "or",
|
||||||
@ -150,26 +168,27 @@ func (s *ControllerTestSuite) TestPolicy() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := c.CreateRetention(p1)
|
ctx := orm.Context()
|
||||||
|
id, err := c.CreateRetention(ctx, p1)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().True(id > 0)
|
s.Require().True(id > 0)
|
||||||
|
|
||||||
p1, err = c.GetRetention(id)
|
p1, err = c.GetRetention(ctx, id)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().EqualValues("project", p1.Scope.Level)
|
s.Require().EqualValues("project", p1.Scope.Level)
|
||||||
s.Require().True(p1.ID > 0)
|
s.Require().True(p1.ID > 0)
|
||||||
|
|
||||||
p1.Scope.Level = "test"
|
p1.Scope.Level = "test"
|
||||||
err = c.UpdateRetention(p1)
|
err = c.UpdateRetention(ctx, p1)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
p1, err = c.GetRetention(id)
|
p1, err = c.GetRetention(ctx, id)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().EqualValues("test", p1.Scope.Level)
|
s.Require().EqualValues("test", p1.Scope.Level)
|
||||||
|
|
||||||
err = c.DeleteRetention(id)
|
err = c.DeleteRetention(ctx, id)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
|
|
||||||
p1, err = c.GetRetention(id)
|
p1, err = c.GetRetention(ctx, id)
|
||||||
s.Require().NotNil(err)
|
s.Require().NotNil(err)
|
||||||
s.Require().True(strings.Contains(err.Error(), "no such Retention policy"))
|
s.Require().True(strings.Contains(err.Error(), "no such Retention policy"))
|
||||||
s.Require().Nil(p1)
|
s.Require().Nil(p1)
|
||||||
@ -182,7 +201,7 @@ func (s *ControllerTestSuite) TestExecution() {
|
|||||||
retentionLauncher := &fakeLauncher{}
|
retentionLauncher := &fakeLauncher{}
|
||||||
execMgr := &testingTask.ExecutionManager{}
|
execMgr := &testingTask.ExecutionManager{}
|
||||||
taskMgr := &testingTask.Manager{}
|
taskMgr := &testingTask.Manager{}
|
||||||
retentionMgr := NewManager()
|
retentionMgr := retention.NewManager()
|
||||||
execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||||
execMgr.On("Get", mock.Anything, mock.Anything).Return(&task.Execution{
|
execMgr.On("Get", mock.Anything, mock.Anything).Return(&task.Execution{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
@ -209,7 +228,15 @@ func (s *ControllerTestSuite) TestExecution() {
|
|||||||
taskMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
taskMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||||
taskMgr.On("Stop", mock.Anything, mock.Anything).Return(nil)
|
taskMgr.On("Stop", mock.Anything, mock.Anything).Return(nil)
|
||||||
|
|
||||||
m := NewAPIController(retentionMgr, projectMgr, repositoryMgr, retentionScheduler, retentionLauncher, execMgr, taskMgr)
|
m := defaultController{
|
||||||
|
manager: retentionMgr,
|
||||||
|
execMgr: execMgr,
|
||||||
|
taskMgr: taskMgr,
|
||||||
|
launcher: retentionLauncher,
|
||||||
|
projectManager: projectMgr,
|
||||||
|
repositoryMgr: repositoryMgr,
|
||||||
|
scheduler: retentionScheduler,
|
||||||
|
}
|
||||||
|
|
||||||
p1 := &policy.Metadata{
|
p1 := &policy.Metadata{
|
||||||
Algorithm: "or",
|
Algorithm: "or",
|
||||||
@ -251,27 +278,28 @@ func (s *ControllerTestSuite) TestExecution() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
policyID, err := m.CreateRetention(p1)
|
ctx := orm.Context()
|
||||||
|
policyID, err := m.CreateRetention(ctx, p1)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().True(policyID > 0)
|
s.Require().True(policyID > 0)
|
||||||
|
|
||||||
id, err := m.TriggerRetentionExec(policyID, ExecutionTriggerManual, false)
|
id, err := m.TriggerRetentionExec(ctx, policyID, retention.ExecutionTriggerManual, false)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().True(id > 0)
|
s.Require().True(id > 0)
|
||||||
|
|
||||||
e1, err := m.GetRetentionExec(id)
|
e1, err := m.GetRetentionExec(ctx, id)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().NotNil(e1)
|
s.Require().NotNil(e1)
|
||||||
s.Require().EqualValues(id, e1.ID)
|
s.Require().EqualValues(id, e1.ID)
|
||||||
|
|
||||||
err = m.OperateRetentionExec(id, "stop")
|
err = m.OperateRetentionExec(ctx, id, "stop")
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
|
|
||||||
es, err := m.ListRetentionExecs(policyID, nil)
|
es, err := m.ListRetentionExecs(ctx, policyID, nil)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().EqualValues(1, len(es))
|
s.Require().EqualValues(1, len(es))
|
||||||
|
|
||||||
ts, err := m.ListRetentionExecTasks(id, nil)
|
ts, err := m.ListRetentionExecTasks(nil, id, nil)
|
||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().EqualValues(1, len(ts))
|
s.Require().EqualValues(1, len(ts))
|
||||||
|
|
||||||
@ -301,10 +329,10 @@ func (f *fakeRetentionScheduler) ListSchedules(ctx context.Context, q *q.Query)
|
|||||||
type fakeLauncher struct {
|
type fakeLauncher struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeLauncher) Stop(executionID int64) error {
|
func (f *fakeLauncher) Stop(ctx context.Context, executionID int64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeLauncher) Launch(policy *policy.Metadata, executionID int64, isDryRun bool) (int64, error) {
|
func (f *fakeLauncher) Launch(ctx context.Context, policy *policy.Metadata, executionID int64, isDryRun bool) (int64, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
@ -18,7 +18,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/goharbor/harbor/src/pkg/task"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
@ -32,9 +31,6 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/repository"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/scheduler"
|
"github.com/goharbor/harbor/src/pkg/scheduler"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,17 +39,6 @@ const (
|
|||||||
userSessionKey = "user"
|
userSessionKey = "user"
|
||||||
)
|
)
|
||||||
|
|
||||||
// the managers/controllers used globally
|
|
||||||
var (
|
|
||||||
projectMgr project.Manager
|
|
||||||
retentionController retention.APIController
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetRetentionController returns the retention API controller
|
|
||||||
func GetRetentionController() retention.APIController {
|
|
||||||
return retentionController
|
|
||||||
}
|
|
||||||
|
|
||||||
// BaseController ...
|
// BaseController ...
|
||||||
type BaseController struct {
|
type BaseController struct {
|
||||||
api.BaseAPI
|
api.BaseAPI
|
||||||
@ -182,28 +167,6 @@ func Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// init project manager
|
|
||||||
initProjectManager()
|
|
||||||
|
|
||||||
retentionMgr := retention.NewManager()
|
|
||||||
|
|
||||||
retentionLauncher := retention.NewLauncher(projectMgr, repository.Mgr, retentionMgr, task.ExecMgr, task.Mgr)
|
|
||||||
|
|
||||||
retentionController = retention.NewAPIController(retentionMgr, projectMgr, repository.Mgr, scheduler.Sched, retentionLauncher, task.ExecMgr, task.Mgr)
|
|
||||||
|
|
||||||
retentionCallbackFun := func(ctx context.Context, p string) error {
|
|
||||||
param := &retention.TriggerParam{}
|
|
||||||
if err := json.Unmarshal([]byte(p), param); err != nil {
|
|
||||||
return fmt.Errorf("failed to unmarshal the param: %v", err)
|
|
||||||
}
|
|
||||||
_, err := retentionController.TriggerRetentionExec(param.PolicyID, param.Trigger, false)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err := scheduler.RegisterCallbackFunc(retention.SchedulerCallback, retentionCallbackFun)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p2pPreheatCallbackFun := func(ctx context.Context, p string) error {
|
p2pPreheatCallbackFun := func(ctx context.Context, p string) error {
|
||||||
param := &preheat.TriggerParam{}
|
param := &preheat.TriggerParam{}
|
||||||
if err := json.Unmarshal([]byte(p), param); err != nil {
|
if err := json.Unmarshal([]byte(p), param); err != nil {
|
||||||
@ -212,7 +175,7 @@ func Init() error {
|
|||||||
_, err := preheat.Enf.EnforcePolicy(ctx, param.PolicyID)
|
_, err := preheat.Enf.EnforcePolicy(ctx, param.PolicyID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = scheduler.RegisterCallbackFunc(preheat.SchedulerCallback, p2pPreheatCallbackFun)
|
err := scheduler.RegisterCallbackFunc(preheat.SchedulerCallback, p2pPreheatCallbackFun)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -231,7 +194,3 @@ func initChartController() error {
|
|||||||
chartController = chartCtl
|
chartController = chartCtl
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initProjectManager() {
|
|
||||||
projectMgr = project.Mgr
|
|
||||||
}
|
|
||||||
|
@ -129,16 +129,6 @@ func init() {
|
|||||||
beego.Router("/api/replication/policies", &ReplicationPolicyAPI{}, "get:List;post:Create")
|
beego.Router("/api/replication/policies", &ReplicationPolicyAPI{}, "get:List;post:Create")
|
||||||
beego.Router("/api/replication/policies/:id([0-9]+)", &ReplicationPolicyAPI{}, "get:Get;put:Update;delete:Delete")
|
beego.Router("/api/replication/policies/:id([0-9]+)", &ReplicationPolicyAPI{}, "get:Get;put:Update;delete:Delete")
|
||||||
|
|
||||||
beego.Router("/api/retentions/metadatas", &RetentionAPI{}, "get:GetMetadatas")
|
|
||||||
beego.Router("/api/retentions/:id", &RetentionAPI{}, "get:GetRetention")
|
|
||||||
beego.Router("/api/retentions", &RetentionAPI{}, "post:CreateRetention")
|
|
||||||
beego.Router("/api/retentions/:id", &RetentionAPI{}, "put:UpdateRetention")
|
|
||||||
beego.Router("/api/retentions/:id/executions", &RetentionAPI{}, "post:TriggerRetentionExec")
|
|
||||||
beego.Router("/api/retentions/:id/executions/:eid", &RetentionAPI{}, "patch:OperateRetentionExec")
|
|
||||||
beego.Router("/api/retentions/:id/executions", &RetentionAPI{}, "get:ListRetentionExecs")
|
|
||||||
beego.Router("/api/retentions/:id/executions/:eid/tasks", &RetentionAPI{}, "get:ListRetentionExecTasks")
|
|
||||||
beego.Router("/api/retentions/:id/executions/:eid/tasks/:tid", &RetentionAPI{}, "get:GetRetentionExecTaskLog")
|
|
||||||
|
|
||||||
beego.Router("/api/projects/:pid([0-9]+)/webhook/policies", &NotificationPolicyAPI{}, "get:List;post:Post")
|
beego.Router("/api/projects/:pid([0-9]+)/webhook/policies", &NotificationPolicyAPI{}, "get:List;post:Post")
|
||||||
beego.Router("/api/projects/:pid([0-9]+)/webhook/policies/:id([0-9]+)", &NotificationPolicyAPI{})
|
beego.Router("/api/projects/:pid([0-9]+)/webhook/policies/:id([0-9]+)", &NotificationPolicyAPI{})
|
||||||
beego.Router("/api/projects/:pid([0-9]+)/webhook/policies/test", &NotificationPolicyAPI{}, "post:Test")
|
beego.Router("/api/projects/:pid([0-9]+)/webhook/policies/test", &NotificationPolicyAPI{}, "post:Test")
|
||||||
|
@ -1,453 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/goharbor/harbor/src/common/rbac/system"
|
|
||||||
"github.com/goharbor/harbor/src/jobservice/job"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/task"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/rbac"
|
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/project/metadata"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RetentionAPI ...
|
|
||||||
type RetentionAPI struct {
|
|
||||||
BaseController
|
|
||||||
metaMgr metadata.Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare validates the user
|
|
||||||
func (r *RetentionAPI) Prepare() {
|
|
||||||
r.BaseController.Prepare()
|
|
||||||
if !r.SecurityCtx.IsAuthenticated() {
|
|
||||||
r.SendUnAuthorizedError(errors.New("UnAuthorized"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r.metaMgr = metadata.Mgr
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMetadatas Get Metadatas
|
|
||||||
func (r *RetentionAPI) GetMetadatas() {
|
|
||||||
data := `
|
|
||||||
{
|
|
||||||
"templates": [
|
|
||||||
{
|
|
||||||
"rule_template": "latestPushedK",
|
|
||||||
"display_text": "the most recently pushed # artifacts",
|
|
||||||
"action": "retain",
|
|
||||||
"params": [
|
|
||||||
{
|
|
||||||
"type": "int",
|
|
||||||
"unit": "COUNT",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule_template": "latestPulledN",
|
|
||||||
"display_text": "the most recently pulled # artifacts",
|
|
||||||
"action": "retain",
|
|
||||||
"params": [
|
|
||||||
{
|
|
||||||
"type": "int",
|
|
||||||
"unit": "COUNT",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule_template": "nDaysSinceLastPush",
|
|
||||||
"display_text": "pushed within the last # days",
|
|
||||||
"action": "retain",
|
|
||||||
"params": [
|
|
||||||
{
|
|
||||||
"type": "int",
|
|
||||||
"unit": "DAYS",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule_template": "nDaysSinceLastPull",
|
|
||||||
"display_text": "pulled within the last # days",
|
|
||||||
"action": "retain",
|
|
||||||
"params": [
|
|
||||||
{
|
|
||||||
"type": "int",
|
|
||||||
"unit": "DAYS",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule_template": "always",
|
|
||||||
"display_text": "always",
|
|
||||||
"action": "retain",
|
|
||||||
"params": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"scope_selectors": [
|
|
||||||
{
|
|
||||||
"display_text": "Repositories",
|
|
||||||
"kind": "doublestar",
|
|
||||||
"decorations": [
|
|
||||||
"repoMatches",
|
|
||||||
"repoExcludes"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tag_selectors": [
|
|
||||||
{
|
|
||||||
"display_text": "Tags",
|
|
||||||
"kind": "doublestar",
|
|
||||||
"decorations": [
|
|
||||||
"matches",
|
|
||||||
"excludes"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
w := r.Ctx.ResponseWriter
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRetention Get Retention
|
|
||||||
func (r *RetentionAPI) GetRetention() {
|
|
||||||
id, err := r.GetIDFromURL()
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p, err := retentionController.GetRetention(id)
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.requireAccess(p, rbac.ActionRead) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.WriteJSONData(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRetention Create Retention
|
|
||||||
func (r *RetentionAPI) CreateRetention() {
|
|
||||||
p := &policy.Metadata{}
|
|
||||||
isValid, err := r.DecodeJSONReqAndValidate(p)
|
|
||||||
if !isValid {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(p.Rules) > 15 {
|
|
||||||
r.SendBadRequestError(errors.New("only 15 rules are allowed at most"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = r.checkRuleConflict(p); err != nil {
|
|
||||||
r.SendConflictError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.requireAccess(p, rbac.ActionCreate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch p.Scope.Level {
|
|
||||||
case policy.ScopeLevelProject:
|
|
||||||
if p.Scope.Reference <= 0 {
|
|
||||||
r.SendBadRequestError(fmt.Errorf("invalid Project id %d", p.Scope.Reference))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := r.ProjectCtl.Get(r.Context(), p.Scope.Reference); err != nil {
|
|
||||||
if errors.IsNotFoundErr(err) {
|
|
||||||
r.SendBadRequestError(fmt.Errorf("invalid Project id %d", p.Scope.Reference))
|
|
||||||
} else {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
r.SendBadRequestError(fmt.Errorf("scope %s is not support", p.Scope.Level))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
old, err := r.metaMgr.Get(r.Context(), p.Scope.Reference, "retention_id")
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if old != nil && len(old) > 0 {
|
|
||||||
r.SendBadRequestError(fmt.Errorf("project %v already has retention policy %v", p.Scope.Reference, old["retention_id"]))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
id, err := retentionController.CreateRetention(p)
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := r.metaMgr.Add(r.Context(), p.Scope.Reference,
|
|
||||||
map[string]string{"retention_id": strconv.FormatInt(id, 10)}); err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
}
|
|
||||||
r.Redirect(http.StatusCreated, strconv.FormatInt(id, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateRetention Update Retention
|
|
||||||
func (r *RetentionAPI) UpdateRetention() {
|
|
||||||
id, err := r.GetIDFromURL()
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p := &policy.Metadata{}
|
|
||||||
isValid, err := r.DecodeJSONReqAndValidate(p)
|
|
||||||
if !isValid {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.ID = id
|
|
||||||
if len(p.Rules) > 15 {
|
|
||||||
r.SendBadRequestError(errors.New("only 15 rules are allowed at most"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = r.checkRuleConflict(p); err != nil {
|
|
||||||
r.SendConflictError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.requireAccess(p, rbac.ActionUpdate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = retentionController.UpdateRetention(p); err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RetentionAPI) checkRuleConflict(p *policy.Metadata) error {
|
|
||||||
temp := make(map[string]int)
|
|
||||||
for n, rule := range p.Rules {
|
|
||||||
rule.ID = 0
|
|
||||||
bs, _ := json.Marshal(rule)
|
|
||||||
if old, exists := temp[string(bs)]; exists {
|
|
||||||
return fmt.Errorf("rule %d is conflict with rule %d", n, old)
|
|
||||||
}
|
|
||||||
temp[string(bs)] = n
|
|
||||||
rule.ID = n
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TriggerRetentionExec Trigger Retention Execution
|
|
||||||
func (r *RetentionAPI) TriggerRetentionExec() {
|
|
||||||
id, err := r.GetIDFromURL()
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d := &struct {
|
|
||||||
DryRun bool `json:"dry_run"`
|
|
||||||
}{
|
|
||||||
DryRun: false,
|
|
||||||
}
|
|
||||||
isValid, err := r.DecodeJSONReqAndValidate(d)
|
|
||||||
if !isValid {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p, err := retentionController.GetRetention(id)
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.requireAccess(p, rbac.ActionUpdate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
eid, err := retentionController.TriggerRetentionExec(id, task.ExecutionTriggerManual, d.DryRun)
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.Redirect(http.StatusCreated, strconv.FormatInt(eid, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// OperateRetentionExec Operate Retention Execution
|
|
||||||
func (r *RetentionAPI) OperateRetentionExec() {
|
|
||||||
id, err := r.GetIDFromURL()
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
eid, err := r.GetInt64FromPath(":eid")
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
a := &struct {
|
|
||||||
Action string `json:"action" valid:"Required;Match(stop)"`
|
|
||||||
}{}
|
|
||||||
isValid, err := r.DecodeJSONReqAndValidate(a)
|
|
||||||
if !isValid {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p, err := retentionController.GetRetention(id)
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.requireAccess(p, rbac.ActionUpdate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = retentionController.OperateRetentionExec(eid, a.Action); err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRetentionExecs List Retention Execution
|
|
||||||
func (r *RetentionAPI) ListRetentionExecs() {
|
|
||||||
id, err := r.GetIDFromURL()
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
page, size, err := r.GetPaginationParams()
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
query := &q.Query{
|
|
||||||
PageNumber: page,
|
|
||||||
PageSize: size,
|
|
||||||
}
|
|
||||||
p, err := retentionController.GetRetention(id)
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.requireAccess(p, rbac.ActionList) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
execs, err := retentionController.ListRetentionExecs(id, query)
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
total, err := retentionController.GetTotalOfRetentionExecs(id)
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.SetPaginationHeader(total, query.PageNumber, query.PageSize)
|
|
||||||
r.WriteJSONData(execs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRetentionExecTasks List Retention Execution Tasks
|
|
||||||
func (r *RetentionAPI) ListRetentionExecTasks() {
|
|
||||||
id, err := r.GetIDFromURL()
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
eid, err := r.GetInt64FromPath(":eid")
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
page, size, err := r.GetPaginationParams()
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
query := &q.Query{
|
|
||||||
PageNumber: page,
|
|
||||||
PageSize: size,
|
|
||||||
Keywords: map[string]interface{}{
|
|
||||||
"VendorID": id,
|
|
||||||
"VendorType": job.Retention,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
p, err := retentionController.GetRetention(id)
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.requireAccess(p, rbac.ActionList) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
his, err := retentionController.ListRetentionExecTasks(eid, query)
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
total, err := retentionController.GetTotalOfRetentionExecTasks(eid)
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r.SetPaginationHeader(total, query.PageNumber, query.PageSize)
|
|
||||||
r.WriteJSONData(his)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRetentionExecTaskLog Get Retention Execution Task log
|
|
||||||
func (r *RetentionAPI) GetRetentionExecTaskLog() {
|
|
||||||
id, err := r.GetIDFromURL()
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tid, err := r.GetInt64FromPath(":tid")
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p, err := retentionController.GetRetention(id)
|
|
||||||
if err != nil {
|
|
||||||
r.SendBadRequestError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !r.requireAccess(p, rbac.ActionRead) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log, err := retentionController.GetRetentionExecTaskLog(tid)
|
|
||||||
if err != nil {
|
|
||||||
r.SendInternalServerError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w := r.Ctx.ResponseWriter
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write(log)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RetentionAPI) requireAccess(p *policy.Metadata, action rbac.Action, subresources ...rbac.Resource) bool {
|
|
||||||
var hasPermission bool
|
|
||||||
|
|
||||||
switch p.Scope.Level {
|
|
||||||
case "project":
|
|
||||||
if len(subresources) == 0 {
|
|
||||||
subresources = append(subresources, rbac.ResourceTagRetention)
|
|
||||||
}
|
|
||||||
hasPermission, _ = r.HasProjectPermission(p.Scope.Reference, action, subresources...)
|
|
||||||
default:
|
|
||||||
resource := system.NewNamespace().Resource(rbac.ResourceTagRetention)
|
|
||||||
hasPermission = r.SecurityCtx.Can(r.Context(), action, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hasPermission {
|
|
||||||
if !r.SecurityCtx.IsAuthenticated() {
|
|
||||||
r.SendUnAuthorizedError(errors.New("UnAuthorized"))
|
|
||||||
} else {
|
|
||||||
r.SendForbiddenError(errors.New(r.SecurityCtx.GetUsername()))
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,468 +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 (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/dao"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/dao/models"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/mocks"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/policy/rule"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetMetadatas(t *testing.T) {
|
|
||||||
cases := []*codeCheckingCase{
|
|
||||||
// 401
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: "/api/retentions/metadatas",
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runCodeCheckingCases(t, cases...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreatePolicy(t *testing.T) {
|
|
||||||
// mock retention api controller
|
|
||||||
mockController := &mocks.APIController{}
|
|
||||||
mockController.On("CreateRetention", mock.AnythingOfType("*policy.Metadata")).Return(int64(1), nil)
|
|
||||||
|
|
||||||
controller := retentionController
|
|
||||||
retentionController = mockController
|
|
||||||
defer func() {
|
|
||||||
retentionController = controller
|
|
||||||
}()
|
|
||||||
|
|
||||||
p1 := &policy.Metadata{
|
|
||||||
Algorithm: "or",
|
|
||||||
Rules: []rule.Metadata{
|
|
||||||
{
|
|
||||||
ID: 1,
|
|
||||||
Priority: 1,
|
|
||||||
Template: "latestPushedK",
|
|
||||||
Action: "retain",
|
|
||||||
Parameters: rule.Parameters{
|
|
||||||
"latestPushedK": 10,
|
|
||||||
},
|
|
||||||
TagSelectors: []*rule.Selector{
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "release-[\\d\\.]+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ScopeSelectors: map[string][]*rule.Selector{
|
|
||||||
"repository": {
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: ".+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Trigger: &policy.Trigger{
|
|
||||||
Kind: "Schedule",
|
|
||||||
Settings: map[string]interface{}{
|
|
||||||
"cron": "* 22 11 * * *",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Scope: &policy.Scope{
|
|
||||||
Level: "project",
|
|
||||||
Reference: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cases := []*codeCheckingCase{
|
|
||||||
// 401
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/retentions",
|
|
||||||
},
|
|
||||||
code: http.StatusUnauthorized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/retentions",
|
|
||||||
bodyJSON: p1,
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusCreated,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/retentions",
|
|
||||||
bodyJSON: &policy.Metadata{
|
|
||||||
Algorithm: "NODEF",
|
|
||||||
Rules: []rule.Metadata{
|
|
||||||
{
|
|
||||||
ID: 1,
|
|
||||||
Priority: 1,
|
|
||||||
Template: "latestPushedK",
|
|
||||||
Action: "retain",
|
|
||||||
Parameters: rule.Parameters{
|
|
||||||
"latestPushedK": 10,
|
|
||||||
},
|
|
||||||
TagSelectors: []*rule.Selector{
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "release-[\\d\\.]+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ScopeSelectors: map[string][]*rule.Selector{
|
|
||||||
"repository": {
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: ".+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Trigger: &policy.Trigger{
|
|
||||||
Kind: "Schedule",
|
|
||||||
Settings: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
Scope: &policy.Scope{
|
|
||||||
Level: "project",
|
|
||||||
Reference: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusBadRequest,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: "/api/retentions",
|
|
||||||
bodyJSON: &policy.Metadata{
|
|
||||||
Algorithm: "or",
|
|
||||||
Rules: []rule.Metadata{
|
|
||||||
{
|
|
||||||
ID: 1,
|
|
||||||
Priority: 1,
|
|
||||||
Template: "latestPushedK",
|
|
||||||
Action: "retain",
|
|
||||||
Parameters: rule.Parameters{
|
|
||||||
"latestPushedK": 10,
|
|
||||||
},
|
|
||||||
TagSelectors: []*rule.Selector{
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "release-[\\d\\.]+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ScopeSelectors: map[string][]*rule.Selector{
|
|
||||||
"repository": {
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: ".+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: 2,
|
|
||||||
Priority: 1,
|
|
||||||
Template: "latestPushedK",
|
|
||||||
Action: "retain",
|
|
||||||
Parameters: rule.Parameters{
|
|
||||||
"latestPushedK": 10,
|
|
||||||
},
|
|
||||||
TagSelectors: []*rule.Selector{
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "release-[\\d\\.]+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ScopeSelectors: map[string][]*rule.Selector{
|
|
||||||
"repository": {
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: ".+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Trigger: &policy.Trigger{
|
|
||||||
Kind: "Schedule",
|
|
||||||
Settings: map[string]interface{}{
|
|
||||||
"cron": "* 22 11 * * *",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Scope: &policy.Scope{
|
|
||||||
Level: "project",
|
|
||||||
Reference: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusConflict,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runCodeCheckingCases(t, cases...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPolicy(t *testing.T) {
|
|
||||||
p := &policy.Metadata{
|
|
||||||
Algorithm: "or",
|
|
||||||
Rules: []rule.Metadata{
|
|
||||||
{
|
|
||||||
ID: 1,
|
|
||||||
Priority: 1,
|
|
||||||
Template: "latestPushedK",
|
|
||||||
Action: "retain",
|
|
||||||
Parameters: rule.Parameters{
|
|
||||||
"latestPushedK": 10,
|
|
||||||
},
|
|
||||||
TagSelectors: []*rule.Selector{
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "release-[\\d\\.]+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ScopeSelectors: map[string][]*rule.Selector{
|
|
||||||
"repository": {
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: ".+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Trigger: &policy.Trigger{
|
|
||||||
Kind: "Schedule",
|
|
||||||
Settings: map[string]interface{}{
|
|
||||||
"cron": "* 22 11 * * *",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Scope: &policy.Scope{
|
|
||||||
Level: "project",
|
|
||||||
Reference: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
p1 := &models.RetentionPolicy{
|
|
||||||
ScopeLevel: p.Scope.Level,
|
|
||||||
TriggerKind: p.Trigger.Kind,
|
|
||||||
CreateTime: time.Now(),
|
|
||||||
UpdateTime: time.Now(),
|
|
||||||
}
|
|
||||||
data, _ := json.Marshal(p)
|
|
||||||
p1.Data = string(data)
|
|
||||||
|
|
||||||
id, err := dao.CreatePolicy(p1)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.True(t, id > 0)
|
|
||||||
|
|
||||||
// mock retention api controller
|
|
||||||
mockController := &mocks.APIController{}
|
|
||||||
mockController.On("GetRetention", mock.AnythingOfType("int64")).Return(p, nil)
|
|
||||||
mockController.On("UpdateRetention", mock.AnythingOfType("*policy.Metadata")).Return(nil)
|
|
||||||
mockController.On("TriggerRetentionExec",
|
|
||||||
mock.AnythingOfType("int64"),
|
|
||||||
mock.AnythingOfType("string"),
|
|
||||||
mock.AnythingOfType("bool")).Return(int64(1), nil)
|
|
||||||
mockController.On("ListRetentionExecs", mock.AnythingOfType("int64"), mock.AnythingOfType("*q.Query")).Return(nil, nil)
|
|
||||||
mockController.On("GetTotalOfRetentionExecs", mock.AnythingOfType("int64")).Return(int64(0), nil)
|
|
||||||
|
|
||||||
controller := retentionController
|
|
||||||
retentionController = mockController
|
|
||||||
defer func() {
|
|
||||||
retentionController = controller
|
|
||||||
}()
|
|
||||||
|
|
||||||
cases := []*codeCheckingCase{
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: fmt.Sprintf("/api/retentions/%d", id),
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPut,
|
|
||||||
url: fmt.Sprintf("/api/retentions/%d", id),
|
|
||||||
bodyJSON: &policy.Metadata{
|
|
||||||
Algorithm: "or",
|
|
||||||
Rules: []rule.Metadata{
|
|
||||||
{
|
|
||||||
ID: 1,
|
|
||||||
Priority: 1,
|
|
||||||
Template: "latestPushedK",
|
|
||||||
Action: "retain",
|
|
||||||
Parameters: rule.Parameters{
|
|
||||||
"latestPushedK": 10,
|
|
||||||
},
|
|
||||||
TagSelectors: []*rule.Selector{
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "release-[\\d\\.]+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ScopeSelectors: map[string][]*rule.Selector{
|
|
||||||
"repository": {
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "b.+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Trigger: &policy.Trigger{
|
|
||||||
Kind: "Schedule",
|
|
||||||
Settings: map[string]interface{}{
|
|
||||||
"cron": "* 22 11 * * *",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Scope: &policy.Scope{
|
|
||||||
Level: "project",
|
|
||||||
Reference: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPut,
|
|
||||||
url: fmt.Sprintf("/api/retentions/%d", id),
|
|
||||||
bodyJSON: &policy.Metadata{
|
|
||||||
Algorithm: "or",
|
|
||||||
Rules: []rule.Metadata{
|
|
||||||
{
|
|
||||||
ID: 1,
|
|
||||||
Priority: 1,
|
|
||||||
Template: "latestPushedK",
|
|
||||||
Action: "retain",
|
|
||||||
Parameters: rule.Parameters{
|
|
||||||
"latestPushedK": 10,
|
|
||||||
},
|
|
||||||
TagSelectors: []*rule.Selector{
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "release-[\\d\\.]+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ScopeSelectors: map[string][]*rule.Selector{
|
|
||||||
"repository": {
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "b.+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: 2,
|
|
||||||
Priority: 1,
|
|
||||||
Template: "latestPushedK",
|
|
||||||
Action: "retain",
|
|
||||||
Parameters: rule.Parameters{
|
|
||||||
"latestPushedK": 10,
|
|
||||||
},
|
|
||||||
TagSelectors: []*rule.Selector{
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "release-[\\d\\.]+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ScopeSelectors: map[string][]*rule.Selector{
|
|
||||||
"repository": {
|
|
||||||
{
|
|
||||||
Kind: "doublestar",
|
|
||||||
Decoration: "matches",
|
|
||||||
Pattern: "b.+",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Trigger: &policy.Trigger{
|
|
||||||
Kind: "Schedule",
|
|
||||||
Settings: map[string]interface{}{
|
|
||||||
"cron": "* 22 11 * * *",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Scope: &policy.Scope{
|
|
||||||
Level: "project",
|
|
||||||
Reference: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusConflict,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodPost,
|
|
||||||
url: fmt.Sprintf("/api/retentions/%d/executions", id),
|
|
||||||
bodyJSON: &struct {
|
|
||||||
DryRun bool `json:"dry_run"`
|
|
||||||
}{
|
|
||||||
DryRun: false,
|
|
||||||
},
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusCreated,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: &testingRequest{
|
|
||||||
method: http.MethodGet,
|
|
||||||
url: fmt.Sprintf("/api/retentions/%d/executions", id),
|
|
||||||
credential: sysAdmin,
|
|
||||||
},
|
|
||||||
code: http.StatusOK,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runCodeCheckingCases(t, cases...)
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
package retention
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
|
||||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FakedRetentionController ...
|
|
||||||
type FakedRetentionController struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// FakedRetentionControllerFunc ...
|
|
||||||
var FakedRetentionControllerFunc = NewFakedRetentionController
|
|
||||||
|
|
||||||
// NewFakedRetentionController ...
|
|
||||||
func NewFakedRetentionController() APIController {
|
|
||||||
return &FakedRetentionController{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRetention ...
|
|
||||||
func (f *FakedRetentionController) GetRetention(id int64) (*policy.Metadata, error) {
|
|
||||||
return &policy.Metadata{
|
|
||||||
ID: 1,
|
|
||||||
Algorithm: "",
|
|
||||||
Rules: nil,
|
|
||||||
Trigger: nil,
|
|
||||||
Scope: nil,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRetention ...
|
|
||||||
func (f *FakedRetentionController) CreateRetention(p *policy.Metadata) (int64, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateRetention ...
|
|
||||||
func (f *FakedRetentionController) UpdateRetention(p *policy.Metadata) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRetention ...
|
|
||||||
func (f *FakedRetentionController) DeleteRetention(id int64) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TriggerRetentionExec ...
|
|
||||||
func (f *FakedRetentionController) TriggerRetentionExec(policyID int64, trigger string, dryRun bool) (int64, error) {
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OperateRetentionExec ...
|
|
||||||
func (f *FakedRetentionController) OperateRetentionExec(eid int64, action string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRetentionExec ...
|
|
||||||
func (f *FakedRetentionController) GetRetentionExec(eid int64) (*Execution, error) {
|
|
||||||
return &Execution{
|
|
||||||
DryRun: false,
|
|
||||||
PolicyID: 1,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRetentionExecs ...
|
|
||||||
func (f *FakedRetentionController) ListRetentionExecs(policyID int64, query *q.Query) ([]*Execution, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTotalOfRetentionExecs ...
|
|
||||||
func (f *FakedRetentionController) GetTotalOfRetentionExecs(policyID int64) (int64, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRetentionExecTasks ...
|
|
||||||
func (f *FakedRetentionController) ListRetentionExecTasks(executionID int64, query *q.Query) ([]*Task, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTotalOfRetentionExecTasks ...
|
|
||||||
func (f *FakedRetentionController) GetTotalOfRetentionExecTasks(executionID int64) (int64, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRetentionExecTaskLog ...
|
|
||||||
func (f *FakedRetentionController) GetRetentionExecTaskLog(taskID int64) ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRetentionExecTask ...
|
|
||||||
func (f *FakedRetentionController) GetRetentionExecTask(taskID int64) (*Task, error) {
|
|
||||||
return &Task{
|
|
||||||
ID: 1,
|
|
||||||
ExecutionID: 1,
|
|
||||||
}, nil
|
|
||||||
}
|
|
@ -15,9 +15,8 @@
|
|||||||
package retention
|
package retention
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
beegoorm "github.com/astaxie/beego/orm"
|
|
||||||
"github.com/goharbor/harbor/src/lib/orm"
|
|
||||||
"github.com/goharbor/harbor/src/lib/selector"
|
"github.com/goharbor/harbor/src/lib/selector"
|
||||||
"github.com/goharbor/harbor/src/pkg/task"
|
"github.com/goharbor/harbor/src/pkg/task"
|
||||||
|
|
||||||
@ -26,7 +25,6 @@ import (
|
|||||||
|
|
||||||
cjob "github.com/goharbor/harbor/src/common/job"
|
cjob "github.com/goharbor/harbor/src/common/job"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
|
||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
pq "github.com/goharbor/harbor/src/lib/q"
|
pq "github.com/goharbor/harbor/src/lib/q"
|
||||||
@ -58,7 +56,7 @@ type Launcher interface {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// int64 : the count of tasks
|
// int64 : the count of tasks
|
||||||
// error : common error if any errors occurred
|
// error : common error if any errors occurred
|
||||||
Launch(policy *policy.Metadata, executionID int64, isDryRun bool) (int64, error)
|
Launch(ctx context.Context, policy *policy.Metadata, executionID int64, isDryRun bool) (int64, error)
|
||||||
// Stop the jobs for one execution
|
// Stop the jobs for one execution
|
||||||
//
|
//
|
||||||
// Arguments:
|
// Arguments:
|
||||||
@ -66,21 +64,19 @@ type Launcher interface {
|
|||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// error : common error if any errors occurred
|
// error : common error if any errors occurred
|
||||||
Stop(executionID int64) error
|
Stop(ctx context.Context, executionID int64) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLauncher returns an instance of Launcher
|
// NewLauncher returns an instance of Launcher
|
||||||
func NewLauncher(projectMgr project.Manager, repositoryMgr repository.Manager,
|
func NewLauncher(projectMgr project.Manager, repositoryMgr repository.Manager,
|
||||||
retentionMgr Manager, execMgr task.ExecutionManager, taskMgr task.Manager) Launcher {
|
retentionMgr Manager, execMgr task.ExecutionManager, taskMgr task.Manager) Launcher {
|
||||||
return &launcher{
|
return &launcher{
|
||||||
projectMgr: projectMgr,
|
projectMgr: projectMgr,
|
||||||
repositoryMgr: repositoryMgr,
|
repositoryMgr: repositoryMgr,
|
||||||
retentionMgr: retentionMgr,
|
retentionMgr: retentionMgr,
|
||||||
execMgr: execMgr,
|
execMgr: execMgr,
|
||||||
taskMgr: taskMgr,
|
taskMgr: taskMgr,
|
||||||
jobserviceClient: cjob.GlobalClient,
|
jobserviceClient: cjob.GlobalClient,
|
||||||
internalCoreURL: config.InternalCoreURL(),
|
|
||||||
chartServerEnabled: config.WithChartMuseum(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,17 +88,15 @@ type jobData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type launcher struct {
|
type launcher struct {
|
||||||
retentionMgr Manager
|
retentionMgr Manager
|
||||||
taskMgr task.Manager
|
taskMgr task.Manager
|
||||||
execMgr task.ExecutionManager
|
execMgr task.ExecutionManager
|
||||||
projectMgr project.Manager
|
projectMgr project.Manager
|
||||||
repositoryMgr repository.Manager
|
repositoryMgr repository.Manager
|
||||||
jobserviceClient cjob.Client
|
jobserviceClient cjob.Client
|
||||||
internalCoreURL string
|
|
||||||
chartServerEnabled bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *launcher) Launch(ply *policy.Metadata, executionID int64, isDryRun bool) (int64, error) {
|
func (l *launcher) Launch(ctx context.Context, ply *policy.Metadata, executionID int64, isDryRun bool) (int64, error) {
|
||||||
if ply == nil {
|
if ply == nil {
|
||||||
return 0, launcherError(fmt.Errorf("the policy is nil"))
|
return 0, launcherError(fmt.Errorf("the policy is nil"))
|
||||||
}
|
}
|
||||||
@ -121,7 +115,7 @@ func (l *launcher) Launch(ply *policy.Metadata, executionID int64, isDryRun bool
|
|||||||
var err error
|
var err error
|
||||||
if level == "system" {
|
if level == "system" {
|
||||||
// get projects
|
// get projects
|
||||||
allProjects, err = getProjects(l.projectMgr)
|
allProjects, err = getProjects(ctx, l.projectMgr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, launcherError(err)
|
return 0, launcherError(err)
|
||||||
}
|
}
|
||||||
@ -156,7 +150,7 @@ func (l *launcher) Launch(ply *policy.Metadata, executionID int64, isDryRun bool
|
|||||||
var repositoryCandidates []*selector.Candidate
|
var repositoryCandidates []*selector.Candidate
|
||||||
// get repositories of projects
|
// get repositories of projects
|
||||||
for _, projectCandidate := range projectCandidates {
|
for _, projectCandidate := range projectCandidates {
|
||||||
repositories, err := getRepositories(l.projectMgr, l.repositoryMgr, projectCandidate.NamespaceID, l.chartServerEnabled)
|
repositories, err := getRepositories(ctx, l.projectMgr, l.repositoryMgr, projectCandidate.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, launcherError(err)
|
return 0, launcherError(err)
|
||||||
}
|
}
|
||||||
@ -207,7 +201,7 @@ func (l *launcher) Launch(ply *policy.Metadata, executionID int64, isDryRun bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
// submit tasks to jobservice
|
// submit tasks to jobservice
|
||||||
if err = l.submitTasks(executionID, jobDatas); err != nil {
|
if err = l.submitTasks(ctx, executionID, jobDatas); err != nil {
|
||||||
return 0, launcherError(err)
|
return 0, launcherError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,8 +235,7 @@ func createJobs(repositoryRules map[selector.Repository]*lwp.Metadata, isDryRun
|
|||||||
return jobDatas, nil
|
return jobDatas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *launcher) submitTasks(executionID int64, jobDatas []*jobData) error {
|
func (l *launcher) submitTasks(ctx context.Context, executionID int64, jobDatas []*jobData) error {
|
||||||
ctx := orm.Context()
|
|
||||||
for _, jobData := range jobDatas {
|
for _, jobData := range jobDatas {
|
||||||
_, err := l.taskMgr.Create(ctx, executionID, &task.Job{
|
_, err := l.taskMgr.Create(ctx, executionID, &task.Job{
|
||||||
Name: jobData.JobName,
|
Name: jobData.JobName,
|
||||||
@ -262,11 +255,10 @@ func (l *launcher) submitTasks(executionID int64, jobDatas []*jobData) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *launcher) Stop(executionID int64) error {
|
func (l *launcher) Stop(ctx context.Context, executionID int64) error {
|
||||||
if executionID <= 0 {
|
if executionID <= 0 {
|
||||||
return launcherError(fmt.Errorf("invalid execution ID: %d", executionID))
|
return launcherError(fmt.Errorf("invalid execution ID: %d", executionID))
|
||||||
}
|
}
|
||||||
ctx := orm.Context()
|
|
||||||
return l.execMgr.Stop(ctx, executionID)
|
return l.execMgr.Stop(ctx, executionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,8 +266,8 @@ func launcherError(err error) error {
|
|||||||
return errors.Wrap(err, "launcher")
|
return errors.Wrap(err, "launcher")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProjects(projectMgr project.Manager) ([]*selector.Candidate, error) {
|
func getProjects(ctx context.Context, projectMgr project.Manager) ([]*selector.Candidate, error) {
|
||||||
projects, err := projectMgr.List(orm.Context(), nil)
|
projects, err := projectMgr.List(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -289,8 +281,7 @@ func getProjects(projectMgr project.Manager) ([]*selector.Candidate, error) {
|
|||||||
return candidates, nil
|
return candidates, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRepositories(projectMgr project.Manager, repositoryMgr repository.Manager,
|
func getRepositories(ctx context.Context, projectMgr project.Manager, repositoryMgr repository.Manager, projectID int64) ([]*selector.Candidate, error) {
|
||||||
projectID int64, chartServerEnabled bool) ([]*selector.Candidate, error) {
|
|
||||||
var candidates []*selector.Candidate
|
var candidates []*selector.Candidate
|
||||||
/*
|
/*
|
||||||
pro, err := projectMgr.Get(projectID)
|
pro, err := projectMgr.Get(projectID)
|
||||||
@ -299,8 +290,7 @@ func getRepositories(projectMgr project.Manager, repositoryMgr repository.Manage
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// get image repositories
|
// get image repositories
|
||||||
// TODO set the context which contains the ORM
|
imageRepositories, err := repositoryMgr.List(ctx, &pq.Query{
|
||||||
imageRepositories, err := repositoryMgr.List(orm.NewContext(nil, beegoorm.NewOrm()), &pq.Query{
|
|
||||||
Keywords: map[string]interface{}{
|
Keywords: map[string]interface{}{
|
||||||
"ProjectID": projectID,
|
"ProjectID": projectID,
|
||||||
},
|
},
|
||||||
@ -317,23 +307,6 @@ func getRepositories(projectMgr project.Manager, repositoryMgr repository.Manage
|
|||||||
Kind: "image",
|
Kind: "image",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// currently, doesn't support retention for chart
|
|
||||||
/*
|
|
||||||
if chartServerEnabled {
|
|
||||||
// get chart repositories when chart server is enabled
|
|
||||||
chartRepositories, err := repositoryMgr.ListChartRepositories(projectID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, r := range chartRepositories {
|
|
||||||
candidates = append(candidates, &art.Candidate{
|
|
||||||
Namespace: pro.Name,
|
|
||||||
Repository: r.Name,
|
|
||||||
Kind: "chart",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return candidates, nil
|
return candidates, nil
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package retention
|
package retention
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/job"
|
"github.com/goharbor/harbor/src/common/job"
|
||||||
@ -136,7 +137,8 @@ func (l *launchTestSuite) SetupTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *launchTestSuite) TestGetProjects() {
|
func (l *launchTestSuite) TestGetProjects() {
|
||||||
projects, err := getProjects(l.projectMgr)
|
ctx := orm.Context()
|
||||||
|
projects, err := getProjects(ctx, l.projectMgr)
|
||||||
require.Nil(l.T(), err)
|
require.Nil(l.T(), err)
|
||||||
assert.Equal(l.T(), 2, len(projects))
|
assert.Equal(l.T(), 2, len(projects))
|
||||||
assert.Equal(l.T(), int64(1), projects[0].NamespaceID)
|
assert.Equal(l.T(), int64(1), projects[0].NamespaceID)
|
||||||
@ -151,7 +153,8 @@ func (l *launchTestSuite) TestGetRepositories() {
|
|||||||
Name: "library/image",
|
Name: "library/image",
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
repositories, err := getRepositories(l.projectMgr, l.repositoryMgr, 1, true)
|
ctx := orm.Context()
|
||||||
|
repositories, err := getRepositories(ctx, l.projectMgr, l.repositoryMgr, 1)
|
||||||
require.Nil(l.T(), err)
|
require.Nil(l.T(), err)
|
||||||
l.repositoryMgr.AssertExpectations(l.T())
|
l.repositoryMgr.AssertExpectations(l.T())
|
||||||
assert.Equal(l.T(), 1, len(repositories))
|
assert.Equal(l.T(), 1, len(repositories))
|
||||||
@ -166,23 +169,23 @@ func (l *launchTestSuite) TestLaunch() {
|
|||||||
l.taskMgr.On("Stop", mock.Anything, mock.Anything).Return(nil)
|
l.taskMgr.On("Stop", mock.Anything, mock.Anything).Return(nil)
|
||||||
|
|
||||||
launcher := &launcher{
|
launcher := &launcher{
|
||||||
projectMgr: l.projectMgr,
|
projectMgr: l.projectMgr,
|
||||||
repositoryMgr: l.repositoryMgr,
|
repositoryMgr: l.repositoryMgr,
|
||||||
retentionMgr: l.retentionMgr,
|
retentionMgr: l.retentionMgr,
|
||||||
execMgr: l.execMgr,
|
execMgr: l.execMgr,
|
||||||
taskMgr: l.taskMgr,
|
taskMgr: l.taskMgr,
|
||||||
jobserviceClient: l.jobserviceClient,
|
jobserviceClient: l.jobserviceClient,
|
||||||
chartServerEnabled: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := orm.Context()
|
||||||
var ply *policy.Metadata
|
var ply *policy.Metadata
|
||||||
// nil policy
|
// nil policy
|
||||||
n, err := launcher.Launch(ply, 1, false)
|
n, err := launcher.Launch(ctx, ply, 1, false)
|
||||||
require.NotNil(l.T(), err)
|
require.NotNil(l.T(), err)
|
||||||
|
|
||||||
// nil rules
|
// nil rules
|
||||||
ply = &policy.Metadata{}
|
ply = &policy.Metadata{}
|
||||||
n, err = launcher.Launch(ply, 1, false)
|
n, err = launcher.Launch(ctx, ply, 1, false)
|
||||||
require.Nil(l.T(), err)
|
require.Nil(l.T(), err)
|
||||||
assert.Equal(l.T(), int64(0), n)
|
assert.Equal(l.T(), int64(0), n)
|
||||||
|
|
||||||
@ -192,7 +195,7 @@ func (l *launchTestSuite) TestLaunch() {
|
|||||||
{},
|
{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err = launcher.Launch(ply, 1, false)
|
_, err = launcher.Launch(ctx, ply, 1, false)
|
||||||
require.NotNil(l.T(), err)
|
require.NotNil(l.T(), err)
|
||||||
|
|
||||||
// system scope
|
// system scope
|
||||||
@ -247,7 +250,7 @@ func (l *launchTestSuite) TestLaunch() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
n, err = launcher.Launch(ply, 1, false)
|
n, err = launcher.Launch(ctx, ply, 1, false)
|
||||||
require.Nil(l.T(), err)
|
require.Nil(l.T(), err)
|
||||||
l.repositoryMgr.AssertExpectations(l.T())
|
l.repositoryMgr.AssertExpectations(l.T())
|
||||||
assert.Equal(l.T(), int64(1), n)
|
assert.Equal(l.T(), int64(1), n)
|
||||||
@ -264,11 +267,12 @@ func (l *launchTestSuite) TestStop() {
|
|||||||
taskMgr: l.taskMgr,
|
taskMgr: l.taskMgr,
|
||||||
jobserviceClient: l.jobserviceClient,
|
jobserviceClient: l.jobserviceClient,
|
||||||
}
|
}
|
||||||
|
ctx := orm.Context()
|
||||||
// invalid execution ID
|
// invalid execution ID
|
||||||
err := launcher.Stop(0)
|
err := launcher.Stop(ctx, 0)
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
err = launcher.Stop(1)
|
err = launcher.Stop(ctx, 1)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
policy "github.com/goharbor/harbor/src/pkg/retention/policy"
|
policy "github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
@ -16,7 +17,7 @@ type APIController struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateRetention provides a mock function with given fields: p
|
// CreateRetention provides a mock function with given fields: p
|
||||||
func (_m *APIController) CreateRetention(p *policy.Metadata) (int64, error) {
|
func (_m *APIController) CreateRetention(ctx context.Context, p *policy.Metadata) (int64, error) {
|
||||||
ret := _m.Called(p)
|
ret := _m.Called(p)
|
||||||
|
|
||||||
var r0 int64
|
var r0 int64
|
||||||
@ -37,7 +38,7 @@ func (_m *APIController) CreateRetention(p *policy.Metadata) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRetention provides a mock function with given fields: id
|
// DeleteRetention provides a mock function with given fields: id
|
||||||
func (_m *APIController) DeleteRetention(id int64) error {
|
func (_m *APIController) DeleteRetention(ctx context.Context, id int64) error {
|
||||||
ret := _m.Called(id)
|
ret := _m.Called(id)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
@ -51,7 +52,7 @@ func (_m *APIController) DeleteRetention(id int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRetention provides a mock function with given fields: id
|
// GetRetention provides a mock function with given fields: id
|
||||||
func (_m *APIController) GetRetention(id int64) (*policy.Metadata, error) {
|
func (_m *APIController) GetRetention(ctx context.Context, id int64) (*policy.Metadata, error) {
|
||||||
ret := _m.Called(id)
|
ret := _m.Called(id)
|
||||||
|
|
||||||
var r0 *policy.Metadata
|
var r0 *policy.Metadata
|
||||||
@ -74,7 +75,7 @@ func (_m *APIController) GetRetention(id int64) (*policy.Metadata, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRetentionExec provides a mock function with given fields: eid
|
// GetRetentionExec provides a mock function with given fields: eid
|
||||||
func (_m *APIController) GetRetentionExec(eid int64) (*retention.Execution, error) {
|
func (_m *APIController) GetRetentionExec(ctx context.Context, eid int64) (*retention.Execution, error) {
|
||||||
ret := _m.Called(eid)
|
ret := _m.Called(eid)
|
||||||
|
|
||||||
var r0 *retention.Execution
|
var r0 *retention.Execution
|
||||||
@ -97,7 +98,7 @@ func (_m *APIController) GetRetentionExec(eid int64) (*retention.Execution, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRetentionExecTaskLog provides a mock function with given fields: taskID
|
// GetRetentionExecTaskLog provides a mock function with given fields: taskID
|
||||||
func (_m *APIController) GetRetentionExecTaskLog(taskID int64) ([]byte, error) {
|
func (_m *APIController) GetRetentionExecTaskLog(ctx context.Context, taskID int64) ([]byte, error) {
|
||||||
ret := _m.Called(taskID)
|
ret := _m.Called(taskID)
|
||||||
|
|
||||||
var r0 []byte
|
var r0 []byte
|
||||||
@ -120,7 +121,7 @@ func (_m *APIController) GetRetentionExecTaskLog(taskID int64) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRetentionExecTask provides a mock function with given fields: taskID
|
// GetRetentionExecTask provides a mock function with given fields: taskID
|
||||||
func (_m *APIController) GetRetentionExecTask(taskID int64) (*retention.Task, error) {
|
func (_m *APIController) GetRetentionExecTask(ctx context.Context, taskID int64) (*retention.Task, error) {
|
||||||
return &retention.Task{
|
return &retention.Task{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
ExecutionID: 1,
|
ExecutionID: 1,
|
||||||
@ -128,7 +129,7 @@ func (_m *APIController) GetRetentionExecTask(taskID int64) (*retention.Task, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTotalOfRetentionExecTasks provides a mock function with given fields: executionID
|
// GetTotalOfRetentionExecTasks provides a mock function with given fields: executionID
|
||||||
func (_m *APIController) GetTotalOfRetentionExecTasks(executionID int64) (int64, error) {
|
func (_m *APIController) GetTotalOfRetentionExecTasks(ctx context.Context, executionID int64) (int64, error) {
|
||||||
ret := _m.Called(executionID)
|
ret := _m.Called(executionID)
|
||||||
|
|
||||||
var r0 int64
|
var r0 int64
|
||||||
@ -149,7 +150,7 @@ func (_m *APIController) GetTotalOfRetentionExecTasks(executionID int64) (int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTotalOfRetentionExecs provides a mock function with given fields: policyID
|
// GetTotalOfRetentionExecs provides a mock function with given fields: policyID
|
||||||
func (_m *APIController) GetTotalOfRetentionExecs(policyID int64) (int64, error) {
|
func (_m *APIController) GetTotalOfRetentionExecs(ctx context.Context, policyID int64) (int64, error) {
|
||||||
ret := _m.Called(policyID)
|
ret := _m.Called(policyID)
|
||||||
|
|
||||||
var r0 int64
|
var r0 int64
|
||||||
@ -170,7 +171,7 @@ func (_m *APIController) GetTotalOfRetentionExecs(policyID int64) (int64, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListRetentionExecTasks provides a mock function with given fields: executionID, query
|
// ListRetentionExecTasks provides a mock function with given fields: executionID, query
|
||||||
func (_m *APIController) ListRetentionExecTasks(executionID int64, query *q.Query) ([]*retention.Task, error) {
|
func (_m *APIController) ListRetentionExecTasks(ctx context.Context, executionID int64, query *q.Query) ([]*retention.Task, error) {
|
||||||
ret := _m.Called(executionID, query)
|
ret := _m.Called(executionID, query)
|
||||||
|
|
||||||
var r0 []*retention.Task
|
var r0 []*retention.Task
|
||||||
@ -193,7 +194,7 @@ func (_m *APIController) ListRetentionExecTasks(executionID int64, query *q.Quer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListRetentionExecs provides a mock function with given fields: policyID, query
|
// ListRetentionExecs provides a mock function with given fields: policyID, query
|
||||||
func (_m *APIController) ListRetentionExecs(policyID int64, query *q.Query) ([]*retention.Execution, error) {
|
func (_m *APIController) ListRetentionExecs(ctx context.Context, policyID int64, query *q.Query) ([]*retention.Execution, error) {
|
||||||
ret := _m.Called(policyID, query)
|
ret := _m.Called(policyID, query)
|
||||||
|
|
||||||
var r0 []*retention.Execution
|
var r0 []*retention.Execution
|
||||||
@ -216,7 +217,7 @@ func (_m *APIController) ListRetentionExecs(policyID int64, query *q.Query) ([]*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OperateRetentionExec provides a mock function with given fields: eid, action
|
// OperateRetentionExec provides a mock function with given fields: eid, action
|
||||||
func (_m *APIController) OperateRetentionExec(eid int64, action string) error {
|
func (_m *APIController) OperateRetentionExec(ctx context.Context, eid int64, action string) error {
|
||||||
ret := _m.Called(eid, action)
|
ret := _m.Called(eid, action)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
@ -230,7 +231,7 @@ func (_m *APIController) OperateRetentionExec(eid int64, action string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TriggerRetentionExec provides a mock function with given fields: policyID, trigger, dryRun
|
// TriggerRetentionExec provides a mock function with given fields: policyID, trigger, dryRun
|
||||||
func (_m *APIController) TriggerRetentionExec(policyID int64, trigger string, dryRun bool) (int64, error) {
|
func (_m *APIController) TriggerRetentionExec(ctx context.Context, policyID int64, trigger string, dryRun bool) (int64, error) {
|
||||||
ret := _m.Called(policyID, trigger, dryRun)
|
ret := _m.Called(policyID, trigger, dryRun)
|
||||||
|
|
||||||
var r0 int64
|
var r0 int64
|
||||||
@ -251,7 +252,7 @@ func (_m *APIController) TriggerRetentionExec(policyID int64, trigger string, dr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRetention provides a mock function with given fields: p
|
// UpdateRetention provides a mock function with given fields: p
|
||||||
func (_m *APIController) UpdateRetention(p *policy.Metadata) error {
|
func (_m *APIController) UpdateRetention(ctx context.Context, p *policy.Metadata) error {
|
||||||
ret := _m.Called(p)
|
ret := _m.Called(p)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
|
@ -42,6 +42,7 @@ func New() http.Handler {
|
|||||||
SysteminfoAPI: newSystemInfoAPI(),
|
SysteminfoAPI: newSystemInfoAPI(),
|
||||||
PingAPI: newPingAPI(),
|
PingAPI: newPingAPI(),
|
||||||
GCAPI: newGCAPI(),
|
GCAPI: newGCAPI(),
|
||||||
|
RetentionAPI: newRetentionAPI(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
74
src/server/v2.0/handler/model/retention.go
Normal file
74
src/server/v2.0/handler/model/retention.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/goharbor/harbor/src/lib"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/retention"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||||
|
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RetentionPolicy ...
|
||||||
|
type RetentionPolicy struct {
|
||||||
|
*policy.Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSwagger ...
|
||||||
|
func (s *RetentionPolicy) ToSwagger() *models.RetentionPolicy {
|
||||||
|
var result models.RetentionPolicy
|
||||||
|
lib.JSONCopy(&result, s)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRetentionPolicyFromSwagger ...
|
||||||
|
func NewRetentionPolicyFromSwagger(policy *models.RetentionPolicy) *RetentionPolicy {
|
||||||
|
data, err := json.Marshal(policy)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var result RetentionPolicy
|
||||||
|
err = json.Unmarshal(data, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRetentionPolicy ...
|
||||||
|
func NewRetentionPolicy(policy *policy.Metadata) *RetentionPolicy {
|
||||||
|
return &RetentionPolicy{policy}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetentionExec ...
|
||||||
|
type RetentionExec struct {
|
||||||
|
*retention.Execution
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSwagger ...
|
||||||
|
func (e *RetentionExec) ToSwagger() *models.RetentionExecution {
|
||||||
|
var result models.RetentionExecution
|
||||||
|
lib.JSONCopy(&result, e)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRetentionExec ...
|
||||||
|
func NewRetentionExec(exec *retention.Execution) *RetentionExec {
|
||||||
|
return &RetentionExec{exec}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetentionTask ...
|
||||||
|
type RetentionTask struct {
|
||||||
|
*retention.Task
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSwagger ...
|
||||||
|
func (e *RetentionTask) ToSwagger() *models.RetentionExecutionTask {
|
||||||
|
var result models.RetentionExecutionTask
|
||||||
|
lib.JSONCopy(&result, e)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRetentionTask ...
|
||||||
|
func NewRetentionTask(task *retention.Task) *RetentionTask {
|
||||||
|
return &RetentionTask{task}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/controller/retention"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -50,6 +51,7 @@ func newProjectAPI() *projectAPI {
|
|||||||
quotaCtl: quota.Ctl,
|
quotaCtl: quota.Ctl,
|
||||||
robotMgr: robot.Mgr,
|
robotMgr: robot.Mgr,
|
||||||
preheatCtl: preheat.Ctl,
|
preheatCtl: preheat.Ctl,
|
||||||
|
retentionCtl: retention.Ctl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +65,7 @@ type projectAPI struct {
|
|||||||
quotaCtl quota.Controller
|
quotaCtl quota.Controller
|
||||||
robotMgr robot.Manager
|
robotMgr robot.Manager
|
||||||
preheatCtl preheat.Controller
|
preheatCtl preheat.Controller
|
||||||
|
retentionCtl retention.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *projectAPI) CreateProject(ctx context.Context, params operation.CreateProjectParams) middleware.Responder {
|
func (a *projectAPI) CreateProject(ctx context.Context, params operation.CreateProjectParams) middleware.Responder {
|
||||||
@ -170,9 +173,7 @@ func (a *projectAPI) CreateProject(ctx context.Context, params operation.CreateP
|
|||||||
// create a default retention policy for proxy project
|
// create a default retention policy for proxy project
|
||||||
if req.RegistryID != nil {
|
if req.RegistryID != nil {
|
||||||
plc := policy.WithNDaysSinceLastPull(projectID, defaultDaysToRetentionForProxyCacheProject)
|
plc := policy.WithNDaysSinceLastPull(projectID, defaultDaysToRetentionForProxyCacheProject)
|
||||||
// TODO: move the retention controller to `src/controller/retention` and
|
retentionID, err := a.retentionCtl.CreateRetention(ctx, plc)
|
||||||
// change to use the default retention controller in `src/controller/retention`
|
|
||||||
retentionID, err := api.GetRetentionController().CreateRetention(plc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return a.SendError(ctx, err)
|
return a.SendError(ctx, err)
|
||||||
}
|
}
|
||||||
|
356
src/server/v2.0/handler/retention.go
Normal file
356
src/server/v2.0/handler/retention.go
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-openapi/runtime/middleware"
|
||||||
|
"github.com/goharbor/harbor/src/common/rbac"
|
||||||
|
projectCtl "github.com/goharbor/harbor/src/controller/project"
|
||||||
|
retentionCtl "github.com/goharbor/harbor/src/controller/retention"
|
||||||
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/project/metadata"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/task"
|
||||||
|
"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/retention"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newRetentionAPI() *retentionAPI {
|
||||||
|
return &retentionAPI{
|
||||||
|
projectCtl: projectCtl.Ctl,
|
||||||
|
retentionCtl: retentionCtl.Ctl,
|
||||||
|
proMetaMgr: metadata.Mgr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetentionAPI ...
|
||||||
|
type retentionAPI struct {
|
||||||
|
BaseAPI
|
||||||
|
proMetaMgr metadata.Manager
|
||||||
|
retentionCtl retentionCtl.Controller
|
||||||
|
projectCtl projectCtl.Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
rentenitionMetadataPayload = &models.RetentionMetadata{
|
||||||
|
Templates: []*models.RetentionRuleMetadata{
|
||||||
|
{
|
||||||
|
Action: "retain",
|
||||||
|
DisplayText: "the most recently pushed # artifacts",
|
||||||
|
RuleTemplate: "latestPushedK",
|
||||||
|
Params: []*models.RetentionRuleParamMetadata{
|
||||||
|
{
|
||||||
|
Required: true,
|
||||||
|
Type: "int",
|
||||||
|
Unit: "COUNT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RuleTemplate: "latestPulledN",
|
||||||
|
DisplayText: "the most recently pulled # artifacts",
|
||||||
|
Action: "retain",
|
||||||
|
Params: []*models.RetentionRuleParamMetadata{
|
||||||
|
{
|
||||||
|
Type: "int",
|
||||||
|
Unit: "COUNT",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RuleTemplate: "nDaysSinceLastPush",
|
||||||
|
DisplayText: "pushed within the last # days",
|
||||||
|
Action: "retain",
|
||||||
|
Params: []*models.RetentionRuleParamMetadata{
|
||||||
|
{
|
||||||
|
Type: "int",
|
||||||
|
Unit: "DAYS",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RuleTemplate: "nDaysSinceLastPull",
|
||||||
|
DisplayText: "pulled within the last # days",
|
||||||
|
Action: "retain",
|
||||||
|
Params: []*models.RetentionRuleParamMetadata{
|
||||||
|
{
|
||||||
|
Type: "int",
|
||||||
|
Unit: "DAYS",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RuleTemplate: "always",
|
||||||
|
DisplayText: "always",
|
||||||
|
Action: "retain",
|
||||||
|
Params: []*models.RetentionRuleParamMetadata{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ScopeSelectors: []*models.RetentionSelectorMetadata{
|
||||||
|
{
|
||||||
|
DisplayText: "Repositories",
|
||||||
|
Kind: "doublestar",
|
||||||
|
Decorations: []string{
|
||||||
|
"repoMatches",
|
||||||
|
"repoExcludes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TagSelectors: []*models.RetentionSelectorMetadata{
|
||||||
|
{
|
||||||
|
DisplayText: "Tags",
|
||||||
|
Kind: "doublestar",
|
||||||
|
Decorations: []string{
|
||||||
|
"matches",
|
||||||
|
"excludes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *retentionAPI) Prepare(ctx context.Context, operation string, params interface{}) middleware.Responder {
|
||||||
|
if err := r.RequireAuthenticated(ctx); err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) GetRentenitionMetadata(ctx context.Context, params operation.GetRentenitionMetadataParams) middleware.Responder {
|
||||||
|
return operation.NewGetRentenitionMetadataOK().WithPayload(rentenitionMetadataPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) GetRetention(ctx context.Context, params operation.GetRetentionParams) middleware.Responder {
|
||||||
|
id := params.ID
|
||||||
|
p, err := r.retentionCtl.GetRetention(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
err = r.requireAccess(ctx, p, rbac.ActionRead)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewGetRetentionOK().WithPayload(model.NewRetentionPolicy(p).ToSwagger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) CreateRetention(ctx context.Context, params operation.CreateRetentionParams) middleware.Responder {
|
||||||
|
p := model.NewRetentionPolicyFromSwagger(params.Policy).Metadata
|
||||||
|
if len(p.Rules) > 15 {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(fmt.Errorf("only 15 rules are allowed at most")))
|
||||||
|
}
|
||||||
|
if err := r.checkRuleConflict(p); err != nil {
|
||||||
|
return r.SendError(ctx, errors.ConflictError(err))
|
||||||
|
}
|
||||||
|
err := r.requireAccess(ctx, p, rbac.ActionCreate)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p.Scope.Level {
|
||||||
|
case policy.ScopeLevelProject:
|
||||||
|
if p.Scope.Reference <= 0 {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(fmt.Errorf("invalid Project id %d", p.Scope.Reference)))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := r.projectCtl.Get(ctx, p.Scope.Reference); err != nil {
|
||||||
|
if errors.IsNotFoundErr(err) {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(fmt.Errorf("invalid Project id %d", p.Scope.Reference)))
|
||||||
|
}
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(err))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(fmt.Errorf("scope %s is not support", p.Scope.Level)))
|
||||||
|
}
|
||||||
|
|
||||||
|
old, err := r.proMetaMgr.Get(ctx, p.Scope.Reference, "retention_id")
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
if old != nil && len(old) > 0 {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(fmt.Errorf("project %v already has retention policy %v", p.Scope.Reference, old["retention_id"])))
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := r.retentionCtl.CreateRetention(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.proMetaMgr.Add(ctx, p.Scope.Reference,
|
||||||
|
map[string]string{"retention_id": strconv.FormatInt(id, 10)}); err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), id)
|
||||||
|
return operation.NewCreateRetentionCreated().WithLocation(location)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) UpdateRetention(ctx context.Context, params operation.UpdateRetentionParams) middleware.Responder {
|
||||||
|
p := model.NewRetentionPolicyFromSwagger(params.Policy).Metadata
|
||||||
|
p.ID = params.ID
|
||||||
|
if len(p.Rules) > 15 {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(fmt.Errorf("only 15 rules are allowed at most")))
|
||||||
|
}
|
||||||
|
if err := r.checkRuleConflict(p); err != nil {
|
||||||
|
return r.SendError(ctx, errors.ConflictError(err))
|
||||||
|
}
|
||||||
|
err := r.requireAccess(ctx, p, rbac.ActionUpdate)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.retentionCtl.UpdateRetention(ctx, p); err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewUpdateRetentionOK()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) checkRuleConflict(p *policy.Metadata) error {
|
||||||
|
temp := make(map[string]int)
|
||||||
|
for n, rule := range p.Rules {
|
||||||
|
rule.ID = 0
|
||||||
|
bs, _ := json.Marshal(rule)
|
||||||
|
if old, exists := temp[string(bs)]; exists {
|
||||||
|
return fmt.Errorf("rule %d is conflict with rule %d", n, old)
|
||||||
|
}
|
||||||
|
temp[string(bs)] = n
|
||||||
|
rule.ID = n
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) TriggerRetentionJob(ctx context.Context, params operation.TriggerRetentionJobParams) middleware.Responder {
|
||||||
|
p, err := r.retentionCtl.GetRetention(ctx, params.ID)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError((err)))
|
||||||
|
}
|
||||||
|
err = r.requireAccess(ctx, p, rbac.ActionUpdate)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
eid, err := r.retentionCtl.TriggerRetentionExec(ctx, params.ID, task.ExecutionTriggerManual, params.Body.DryRun)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), eid)
|
||||||
|
return operation.NewTriggerRetentionJobCreated().WithLocation(location)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) OperateRetentionJob(ctx context.Context, params operation.OperateRetentionJobParams) middleware.Responder {
|
||||||
|
if params.Body.Action != "stop" {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError((fmt.Errorf("action should be 'stop'"))))
|
||||||
|
}
|
||||||
|
p, err := r.retentionCtl.GetRetention(ctx, params.ID)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(err))
|
||||||
|
}
|
||||||
|
err = r.requireAccess(ctx, p, rbac.ActionUpdate)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
if err = r.retentionCtl.OperateRetentionExec(ctx, params.Eid, params.Body.Action); err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewOperateRetentionJobOK()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) ListRetentionJob(ctx context.Context, params operation.ListRetentionJobParams) middleware.Responder {
|
||||||
|
query := &q.Query{
|
||||||
|
PageNumber: *params.Page,
|
||||||
|
PageSize: *params.PageSize,
|
||||||
|
}
|
||||||
|
p, err := r.retentionCtl.GetRetention(ctx, params.ID)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(err))
|
||||||
|
}
|
||||||
|
err = r.requireAccess(ctx, p, rbac.ActionList)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
execs, err := r.retentionCtl.ListRetentionExecs(ctx, params.ID, query)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
total, err := r.retentionCtl.GetTotalOfRetentionExecs(ctx, params.ID)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
var payload []*models.RetentionExecution
|
||||||
|
for _, e := range execs {
|
||||||
|
payload = append(payload, model.NewRetentionExec(e).ToSwagger())
|
||||||
|
}
|
||||||
|
return operation.NewListRetentionJobOK().WithXTotalCount(total).
|
||||||
|
WithLink(r.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
||||||
|
WithPayload(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) ListRetentionTasks(ctx context.Context, params operation.ListRetentionTasksParams) middleware.Responder {
|
||||||
|
query := &q.Query{
|
||||||
|
PageNumber: *params.Page,
|
||||||
|
PageSize: *params.PageSize,
|
||||||
|
}
|
||||||
|
p, err := r.retentionCtl.GetRetention(ctx, params.ID)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(err))
|
||||||
|
}
|
||||||
|
err = r.requireAccess(ctx, p, rbac.ActionList)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
tasks, err := r.retentionCtl.ListRetentionExecTasks(ctx, params.Eid, query)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
total, err := r.retentionCtl.GetTotalOfRetentionExecTasks(ctx, params.Eid)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
var payload []*models.RetentionExecutionTask
|
||||||
|
for _, t := range tasks {
|
||||||
|
payload = append(payload, model.NewRetentionTask(t).ToSwagger())
|
||||||
|
}
|
||||||
|
return operation.NewListRetentionTasksOK().WithXTotalCount(total).
|
||||||
|
WithLink(r.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
||||||
|
WithPayload(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) GetRetentionTaskLog(ctx context.Context, params operation.GetRetentionTaskLogParams) middleware.Responder {
|
||||||
|
p, err := r.retentionCtl.GetRetention(ctx, params.ID)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, errors.BadRequestError(err))
|
||||||
|
}
|
||||||
|
err = r.requireAccess(ctx, p, rbac.ActionRead)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log, err := r.retentionCtl.GetRetentionExecTaskLog(ctx, params.Tid)
|
||||||
|
if err != nil {
|
||||||
|
return r.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
return operation.NewGetRetentionTaskLogOK().WithPayload(string(log))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *retentionAPI) requireAccess(ctx context.Context, p *policy.Metadata, action rbac.Action, subresources ...rbac.Resource) error {
|
||||||
|
switch p.Scope.Level {
|
||||||
|
case "project":
|
||||||
|
if len(subresources) == 0 {
|
||||||
|
subresources = append(subresources, rbac.ResourceTagRetention)
|
||||||
|
}
|
||||||
|
err := r.RequireProjectAccess(ctx, p.Scope.Reference, action, subresources...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return r.RequireSystemAccess(ctx, action, rbac.ResourceTagRetention)
|
||||||
|
}
|
@ -74,15 +74,6 @@ func registerLegacyRoutes() {
|
|||||||
beego.Router("/api/"+version+"/registries/:id/info", &api.RegistryAPI{}, "get:GetInfo")
|
beego.Router("/api/"+version+"/registries/:id/info", &api.RegistryAPI{}, "get:GetInfo")
|
||||||
beego.Router("/api/"+version+"/registries/:id/namespace", &api.RegistryAPI{}, "get:GetNamespace")
|
beego.Router("/api/"+version+"/registries/:id/namespace", &api.RegistryAPI{}, "get:GetNamespace")
|
||||||
|
|
||||||
beego.Router("/api/"+version+"/retentions/metadatas", &api.RetentionAPI{}, "get:GetMetadatas")
|
|
||||||
beego.Router("/api/"+version+"/retentions/:id", &api.RetentionAPI{}, "get:GetRetention")
|
|
||||||
beego.Router("/api/"+version+"/retentions", &api.RetentionAPI{}, "post:CreateRetention")
|
|
||||||
beego.Router("/api/"+version+"/retentions/:id", &api.RetentionAPI{}, "put:UpdateRetention")
|
|
||||||
beego.Router("/api/"+version+"/retentions/:id/executions", &api.RetentionAPI{}, "post:TriggerRetentionExec")
|
|
||||||
beego.Router("/api/"+version+"/retentions/:id/executions/:eid", &api.RetentionAPI{}, "patch:OperateRetentionExec")
|
|
||||||
beego.Router("/api/"+version+"/retentions/:id/executions", &api.RetentionAPI{}, "get:ListRetentionExecs")
|
|
||||||
beego.Router("/api/"+version+"/retentions/:id/executions/:eid/tasks", &api.RetentionAPI{}, "get:ListRetentionExecTasks")
|
|
||||||
beego.Router("/api/"+version+"/retentions/:id/executions/:eid/tasks/:tid", &api.RetentionAPI{}, "get:GetRetentionExecTaskLog")
|
|
||||||
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/immutabletagrules", &api.ImmutableTagRuleAPI{}, "get:List;post:Post")
|
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/immutabletagrules", &api.ImmutableTagRuleAPI{}, "get:List;post:Post")
|
||||||
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/immutabletagrules/:id([0-9]+)", &api.ImmutableTagRuleAPI{})
|
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/immutabletagrules/:id([0-9]+)", &api.ImmutableTagRuleAPI{})
|
||||||
|
|
||||||
|
@ -24,3 +24,4 @@ package controller
|
|||||||
//go:generate mockery --case snake --dir ../../controller/replication --name Controller --output ./replication --outpkg replication
|
//go:generate mockery --case snake --dir ../../controller/replication --name Controller --output ./replication --outpkg replication
|
||||||
//go:generate mockery --case snake --dir ../../controller/robot --name Controller --output ./robot --outpkg robot
|
//go:generate mockery --case snake --dir ../../controller/robot --name Controller --output ./robot --outpkg robot
|
||||||
//go:generate mockery --case snake --dir ../../controller/proxy --name RemoteInterface --output ./proxy --outpkg proxy
|
//go:generate mockery --case snake --dir ../../controller/proxy --name RemoteInterface --output ./proxy --outpkg proxy
|
||||||
|
//go:generate mockery --case snake --dir ../../controller/retention --name Controller --output ./retention --outpkg retention
|
||||||
|
283
src/testing/controller/retention/controller.go
Normal file
283
src/testing/controller/retention/controller.go
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||||
|
|
||||||
|
package retention
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
pkgretention "github.com/goharbor/harbor/src/pkg/retention"
|
||||||
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
policy "github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||||
|
|
||||||
|
q "github.com/goharbor/harbor/src/lib/q"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Controller is an autogenerated mock type for the Controller type
|
||||||
|
type Controller struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRetention provides a mock function with given fields: ctx, p
|
||||||
|
func (_m *Controller) CreateRetention(ctx context.Context, p *policy.Metadata) (int64, error) {
|
||||||
|
ret := _m.Called(ctx, p)
|
||||||
|
|
||||||
|
var r0 int64
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *policy.Metadata) int64); ok {
|
||||||
|
r0 = rf(ctx, p)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *policy.Metadata) error); ok {
|
||||||
|
r1 = rf(ctx, p)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRetention provides a mock function with given fields: ctx, id
|
||||||
|
func (_m *Controller) DeleteRetention(ctx context.Context, id int64) error {
|
||||||
|
ret := _m.Called(ctx, id)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||||
|
r0 = rf(ctx, id)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRetention provides a mock function with given fields: ctx, id
|
||||||
|
func (_m *Controller) GetRetention(ctx context.Context, id int64) (*policy.Metadata, error) {
|
||||||
|
ret := _m.Called(ctx, id)
|
||||||
|
|
||||||
|
var r0 *policy.Metadata
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64) *policy.Metadata); ok {
|
||||||
|
r0 = rf(ctx, id)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*policy.Metadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||||
|
r1 = rf(ctx, id)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRetentionExec provides a mock function with given fields: ctx, eid
|
||||||
|
func (_m *Controller) GetRetentionExec(ctx context.Context, eid int64) (*pkgretention.Execution, error) {
|
||||||
|
ret := _m.Called(ctx, eid)
|
||||||
|
|
||||||
|
var r0 *pkgretention.Execution
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64) *pkgretention.Execution); ok {
|
||||||
|
r0 = rf(ctx, eid)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*pkgretention.Execution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||||
|
r1 = rf(ctx, eid)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRetentionExecTask provides a mock function with given fields: ctx, taskID
|
||||||
|
func (_m *Controller) GetRetentionExecTask(ctx context.Context, taskID int64) (*pkgretention.Task, error) {
|
||||||
|
ret := _m.Called(ctx, taskID)
|
||||||
|
|
||||||
|
var r0 *pkgretention.Task
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64) *pkgretention.Task); ok {
|
||||||
|
r0 = rf(ctx, taskID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*pkgretention.Task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||||
|
r1 = rf(ctx, taskID)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRetentionExecTaskLog provides a mock function with given fields: ctx, taskID
|
||||||
|
func (_m *Controller) GetRetentionExecTaskLog(ctx context.Context, taskID int64) ([]byte, error) {
|
||||||
|
ret := _m.Called(ctx, taskID)
|
||||||
|
|
||||||
|
var r0 []byte
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64) []byte); ok {
|
||||||
|
r0 = rf(ctx, taskID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||||
|
r1 = rf(ctx, taskID)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTotalOfRetentionExecTasks provides a mock function with given fields: ctx, executionID
|
||||||
|
func (_m *Controller) GetTotalOfRetentionExecTasks(ctx context.Context, executionID int64) (int64, error) {
|
||||||
|
ret := _m.Called(ctx, executionID)
|
||||||
|
|
||||||
|
var r0 int64
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64) int64); ok {
|
||||||
|
r0 = rf(ctx, executionID)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||||
|
r1 = rf(ctx, executionID)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTotalOfRetentionExecs provides a mock function with given fields: ctx, policyID
|
||||||
|
func (_m *Controller) GetTotalOfRetentionExecs(ctx context.Context, policyID int64) (int64, error) {
|
||||||
|
ret := _m.Called(ctx, policyID)
|
||||||
|
|
||||||
|
var r0 int64
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64) int64); ok {
|
||||||
|
r0 = rf(ctx, policyID)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||||
|
r1 = rf(ctx, policyID)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRetentionExecTasks provides a mock function with given fields: ctx, executionID, query
|
||||||
|
func (_m *Controller) ListRetentionExecTasks(ctx context.Context, executionID int64, query *q.Query) ([]*pkgretention.Task, error) {
|
||||||
|
ret := _m.Called(ctx, executionID, query)
|
||||||
|
|
||||||
|
var r0 []*pkgretention.Task
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) []*pkgretention.Task); ok {
|
||||||
|
r0 = rf(ctx, executionID, query)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*pkgretention.Task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64, *q.Query) error); ok {
|
||||||
|
r1 = rf(ctx, executionID, query)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRetentionExecs provides a mock function with given fields: ctx, policyID, query
|
||||||
|
func (_m *Controller) ListRetentionExecs(ctx context.Context, policyID int64, query *q.Query) ([]*pkgretention.Execution, error) {
|
||||||
|
ret := _m.Called(ctx, policyID, query)
|
||||||
|
|
||||||
|
var r0 []*pkgretention.Execution
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) []*pkgretention.Execution); ok {
|
||||||
|
r0 = rf(ctx, policyID, query)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*pkgretention.Execution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64, *q.Query) error); ok {
|
||||||
|
r1 = rf(ctx, policyID, query)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperateRetentionExec provides a mock function with given fields: ctx, eid, action
|
||||||
|
func (_m *Controller) OperateRetentionExec(ctx context.Context, eid int64, action string) error {
|
||||||
|
ret := _m.Called(ctx, eid, action)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64, string) error); ok {
|
||||||
|
r0 = rf(ctx, eid, action)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerRetentionExec provides a mock function with given fields: ctx, policyID, trigger, dryRun
|
||||||
|
func (_m *Controller) TriggerRetentionExec(ctx context.Context, policyID int64, trigger string, dryRun bool) (int64, error) {
|
||||||
|
ret := _m.Called(ctx, policyID, trigger, dryRun)
|
||||||
|
|
||||||
|
var r0 int64
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, int64, string, bool) int64); ok {
|
||||||
|
r0 = rf(ctx, policyID, trigger, dryRun)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, int64, string, bool) error); ok {
|
||||||
|
r1 = rf(ctx, policyID, trigger, dryRun)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRetention provides a mock function with given fields: ctx, p
|
||||||
|
func (_m *Controller) UpdateRetention(ctx context.Context, p *policy.Metadata) error {
|
||||||
|
ret := _m.Called(ctx, p)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *policy.Metadata) error); ok {
|
||||||
|
r0 = rf(ctx, p)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
@ -85,10 +85,6 @@ Test Case - Project Level CVE Allowlist
|
|||||||
[Tags] pro_cve
|
[Tags] pro_cve
|
||||||
Harbor API Test ./tests/apitests/python/test_project_level_cve_allowlist.py
|
Harbor API Test ./tests/apitests/python/test_project_level_cve_allowlist.py
|
||||||
|
|
||||||
Test Case - Tag Retention
|
|
||||||
[Tags] tag_retention
|
|
||||||
Harbor API Test ./tests/apitests/python/test_retention.py
|
|
||||||
|
|
||||||
Test Case - Health Check
|
Test Case - Health Check
|
||||||
[Tags] health
|
[Tags] health
|
||||||
Harbor API Test ./tests/apitests/python/test_health_check.py
|
Harbor API Test ./tests/apitests/python/test_health_check.py
|
||||||
@ -149,10 +145,6 @@ Test Case - Proxy Cache
|
|||||||
[Tags] proxy_cache
|
[Tags] proxy_cache
|
||||||
Harbor API Test ./tests/apitests/python/test_proxy_cache.py
|
Harbor API Test ./tests/apitests/python/test_proxy_cache.py
|
||||||
|
|
||||||
Test Case - Tag Immutability
|
|
||||||
[Tags] tag_immutability
|
|
||||||
Harbor API Test ./tests/apitests/python/test_tag_immutability.py
|
|
||||||
|
|
||||||
Test Case - P2P
|
Test Case - P2P
|
||||||
[Tags] p2p
|
[Tags] p2p
|
||||||
Harbor API Test ./tests/apitests/python/test_p2p.py
|
Harbor API Test ./tests/apitests/python/test_p2p.py
|
||||||
|
Loading…
Reference in New Issue
Block a user