mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-18 21:51:24 +01:00
add api test case for garbage collection (#6366)
Add API test case for garbage collection, and add swagger.yaml, GC and chart feature were updated in swagger.yaml. Signed-off-by: danfengliu <danfengl@vmware.com>
This commit is contained in:
parent
b406d7ccc0
commit
c4bf65162c
@ -2575,7 +2575,9 @@ paths:
|
|||||||
- Products
|
- Products
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Get job log successfully.
|
description: Get successfully.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
'400':
|
'400':
|
||||||
description: Illegal format of provided ID value.
|
description: Illegal format of provided ID value.
|
||||||
'401':
|
'401':
|
||||||
@ -2776,7 +2778,11 @@ paths:
|
|||||||
description: The project name
|
description: The project name
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
$ref: '#/definitions/ChartInfoList'
|
description: Searched for charts of project in Harbor successfully.
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/ChartInfoEntry'
|
||||||
'401':
|
'401':
|
||||||
$ref: '#/definitions/UnauthorizedChartAPIError'
|
$ref: '#/definitions/UnauthorizedChartAPIError'
|
||||||
'403':
|
'403':
|
||||||
@ -4279,9 +4285,15 @@ definitions:
|
|||||||
total_versions:
|
total_versions:
|
||||||
type: integer
|
type: integer
|
||||||
description: Total count of chart versions
|
description: Total count of chart versions
|
||||||
|
latest_version:
|
||||||
|
type: string
|
||||||
|
description: latest version of chart
|
||||||
created:
|
created:
|
||||||
type: string
|
type: string
|
||||||
description: The created time of chart
|
description: The created time of chart
|
||||||
|
updated:
|
||||||
|
type: string
|
||||||
|
description: The created time of chart
|
||||||
icon:
|
icon:
|
||||||
type: string
|
type: string
|
||||||
description: The icon path of chart
|
description: The icon path of chart
|
||||||
@ -4430,19 +4442,35 @@ definitions:
|
|||||||
GCResult:
|
GCResult:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
status:
|
id:
|
||||||
|
type: integer
|
||||||
|
description: the id of gc job.
|
||||||
|
job_name:
|
||||||
|
type: string
|
||||||
|
description: the job name of gc job.
|
||||||
|
job_kind:
|
||||||
|
type: string
|
||||||
|
description: the job kind of gc job.
|
||||||
|
schedule:
|
||||||
|
$ref: '#/definitions/GCScheduleSchedule'
|
||||||
|
job_status:
|
||||||
|
type: string
|
||||||
|
description: the status of gc job.
|
||||||
|
deleted:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: the result of gc job.
|
description: if gc job was deleted.
|
||||||
msg:
|
creation_time:
|
||||||
type: string
|
type: string
|
||||||
description: the details of gc job.
|
description: the creation time of gc job.
|
||||||
starttime:
|
update_time:
|
||||||
type: string
|
type: string
|
||||||
description: the start time of gc job.
|
description: the update time of gc job.
|
||||||
endtime:
|
|
||||||
type: string
|
|
||||||
description: the end time of gc job.
|
|
||||||
GCSchedule:
|
GCSchedule:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
schedule:
|
||||||
|
$ref: '#/definitions/GCScheduleSchedule'
|
||||||
|
GCScheduleSchedule:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
type:
|
type:
|
||||||
|
@ -25,7 +25,7 @@ def push_image_to_project(project_name, registry, username, password, image, tag
|
|||||||
_docker_api.docker_image_pull(image, tag = tag)
|
_docker_api.docker_image_pull(image, tag = tag)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
new_harbor_registry, new_tag = _docker_api.docker_image_tag(image, r'{}/{}/{}'.format(registry, project_name, image))
|
new_harbor_registry, new_tag = _docker_api.docker_image_tag(r'{}:{}'.format(image, tag), r'{}/{}/{}'.format(registry, project_name, image))
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
_docker_api.docker_image_push(new_harbor_registry, new_tag)
|
_docker_api.docker_image_push(new_harbor_registry, new_tag)
|
||||||
|
149
tests/apitests/python/library/system.py
Normal file
149
tests/apitests/python/library/system.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import base
|
||||||
|
import swagger_client
|
||||||
|
from swagger_client.rest import ApiException
|
||||||
|
|
||||||
|
class System(base.Base):
|
||||||
|
def get_gc_history(self, expect_status_code = 200, expect_response_body = None, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data, status_code, _ = client.system_gc_get_with_http_info()
|
||||||
|
except ApiException as e:
|
||||||
|
if e.status == expect_status_code:
|
||||||
|
if expect_response_body is not None and e.body.strip() != expect_response_body.strip():
|
||||||
|
raise Exception(r"Get configuration response body is not as expected {} actual status is {}.".format(expect_response_body.strip(), e.body.strip()))
|
||||||
|
else:
|
||||||
|
return e.reason, e.body
|
||||||
|
else:
|
||||||
|
raise Exception(r"Get configuration result is not as expected {} actual status is {}.".format(expect_status_code, e.status))
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_gc_status_by_id(self, job_id, expect_status_code = 200, expect_response_body = None, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data, status_code, _ = client.system_gc_id_get_with_http_info(job_id)
|
||||||
|
except ApiException as e:
|
||||||
|
if e.status == expect_status_code:
|
||||||
|
if expect_response_body is not None and e.body.strip() != expect_response_body.strip():
|
||||||
|
raise Exception(r"Get configuration response body is not as expected {} actual status is {}.".format(expect_response_body.strip(), e.body.strip()))
|
||||||
|
else:
|
||||||
|
return e.reason, e.body
|
||||||
|
else:
|
||||||
|
raise Exception(r"Get configuration result is not as expected {} actual status is {}.".format(expect_status_code, e.status))
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_gc_log_by_id(self, job_id, expect_status_code = 200, expect_response_body = None, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data, status_code, _ = client.system_gc_id_log_get_with_http_info(job_id)
|
||||||
|
except ApiException as e:
|
||||||
|
if e.status == expect_status_code:
|
||||||
|
if expect_response_body is not None and e.body.strip() != expect_response_body.strip():
|
||||||
|
raise Exception(r"Get configuration response body is not as expected {} actual status is {}.".format(expect_response_body.strip(), e.body.strip()))
|
||||||
|
else:
|
||||||
|
return e.reason, e.body
|
||||||
|
else:
|
||||||
|
raise Exception(r"Get configuration result is not as expected {} actual status is {}.".format(expect_status_code, e.status))
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_gc_schedule(self, expect_status_code = 200, expect_response_body = None, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data, status_code, _ = client.system_gc_schedule_get_with_http_info()
|
||||||
|
except ApiException as e:
|
||||||
|
if e.status == expect_status_code:
|
||||||
|
if expect_response_body is not None and e.body.strip() != expect_response_body.strip():
|
||||||
|
raise Exception(r"Get configuration response body is not as expected {} actual status is {}.".format(expect_response_body.strip(), e.body.strip()))
|
||||||
|
else:
|
||||||
|
return e.reason, e.body
|
||||||
|
else:
|
||||||
|
raise Exception(r"Get configuration result is not as expected {} actual status is {}.".format(expect_status_code, e.status))
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def set_gc_schedule(self, schedule_type = 'None', offtime = None, weekday = None, expect_status_code = 200, expect_response_body = None, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
gc_schedule = swagger_client.GCSchedule()
|
||||||
|
gc_schedule.type = schedule_type
|
||||||
|
if offtime is not None:
|
||||||
|
gc_schedule.offtime = offtime
|
||||||
|
if weekday is not None:
|
||||||
|
gc_schedule.weekday = weekday
|
||||||
|
try:
|
||||||
|
data, status_code, _ = client.system_gc_schedule_put_with_http_info(gc_schedule)
|
||||||
|
except ApiException as e:
|
||||||
|
if e.status == expect_status_code:
|
||||||
|
if expect_response_body is not None and e.body.strip() != expect_response_body.strip():
|
||||||
|
raise Exception(r"Get configuration response body is not as expected {} actual status is {}.".format(expect_response_body.strip(), e.body.strip()))
|
||||||
|
else:
|
||||||
|
return e.reason, e.body
|
||||||
|
else:
|
||||||
|
raise Exception(r"Get configuration result is not as expected {} actual status is {}.".format(expect_status_code, e.status))
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def create_gc_schedule(self, schedule_type, offtime = None, weekday = None, expect_status_code = 201, expect_response_body = None, **kwargs):
|
||||||
|
client = self._get_client(**kwargs)
|
||||||
|
gcscheduleschedule = swagger_client.GCScheduleSchedule()
|
||||||
|
gcscheduleschedule.type = schedule_type
|
||||||
|
if offtime is not None:
|
||||||
|
gcscheduleschedule.offtime = offtime
|
||||||
|
if weekday is not None:
|
||||||
|
gcscheduleschedule.weekday = weekday
|
||||||
|
|
||||||
|
gc_schedule = swagger_client.GCSchedule(gcscheduleschedule)
|
||||||
|
try:
|
||||||
|
_, status_code, header = client.system_gc_schedule_post_with_http_info(gc_schedule)
|
||||||
|
except ApiException as e:
|
||||||
|
if e.status == expect_status_code:
|
||||||
|
if expect_response_body is not None and e.body.strip() != expect_response_body.strip():
|
||||||
|
raise Exception(r"Create GC schedule response body is not as expected {} actual status is {}.".format(expect_response_body.strip(), e.body.strip()))
|
||||||
|
else:
|
||||||
|
return e.reason, e.body
|
||||||
|
else:
|
||||||
|
raise Exception(r"Create GC schedule result is not as expected {} actual status is {}.".format(expect_status_code, e.status))
|
||||||
|
base._assert_status_code(expect_status_code, status_code)
|
||||||
|
return base._get_id_from_header(header)
|
||||||
|
|
||||||
|
def gc_now(self, **kwargs):
|
||||||
|
gc_id = self.create_gc_schedule('Manual', **kwargs)
|
||||||
|
return gc_id
|
||||||
|
|
||||||
|
def validate_gc_job_status(self, gc_id, expected_gc_status, **kwargs):
|
||||||
|
get_gc_status_finish = False
|
||||||
|
timeout_count = 20
|
||||||
|
while not (get_gc_status_finish):
|
||||||
|
time.sleep(5)
|
||||||
|
status = self.get_gc_status_by_id(gc_id, **kwargs)
|
||||||
|
if len(status) is not 1:
|
||||||
|
raise Exception(r"Get GC status count expected 1 actual count is {}.".format(len(status)))
|
||||||
|
if status[0].job_status == expected_gc_status:
|
||||||
|
get_gc_status_finish = True
|
||||||
|
timeout_count = timeout_count - 1
|
||||||
|
|
||||||
|
if not (get_gc_status_finish):
|
||||||
|
raise Exception("Scan image result is not as expected {} actual scan status is {}".format(expected_scan_status, actual_scan_status))
|
||||||
|
|
||||||
|
def validate_deletion_success(self, gc_id, **kwargs):
|
||||||
|
log_content = self.get_gc_log_by_id(gc_id, **kwargs)
|
||||||
|
key_message = "blobs eligible for deletion"
|
||||||
|
key_message_pos = log_content.find(key_message)
|
||||||
|
full_message = log_content[key_message_pos-30 : key_message_pos + len(key_message)]
|
||||||
|
deleted_files_count_list = re.findall(r'\s+(\d+)\s+blobs eligible for deletion', full_message)
|
||||||
|
|
||||||
|
if len(deleted_files_count_list) != 1:
|
||||||
|
raise Exception(r"Fail to get blobs eligible for deletion in log file, failure is {}.".format(len(deleted_files_count_list)))
|
||||||
|
deleted_files_count = int(deleted_files_count_list[0])
|
||||||
|
if deleted_files_count == 0:
|
||||||
|
raise Exception(r"Get blobs eligible for deletion count is {}, while we expect more than 1.".format(deleted_files_count))
|
||||||
|
|
92
tests/apitests/python/test_garbage_collection.py
Normal file
92
tests/apitests/python/test_garbage_collection.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from testutils import ADMIN_CLIENT
|
||||||
|
from testutils import TEARDOWN
|
||||||
|
from library.user import User
|
||||||
|
from library.system import System
|
||||||
|
from library.project import Project
|
||||||
|
from library.repository import Repository
|
||||||
|
from library.repository import push_image_to_project
|
||||||
|
from testutils import harbor_server
|
||||||
|
from library.base import _assert_status_code
|
||||||
|
|
||||||
|
class TestProjects(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUp(self):
|
||||||
|
system = System()
|
||||||
|
self.system= system
|
||||||
|
|
||||||
|
project = Project()
|
||||||
|
self.project= project
|
||||||
|
|
||||||
|
user = User()
|
||||||
|
self.user= user
|
||||||
|
|
||||||
|
repo = Repository()
|
||||||
|
self.repo= repo
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDown(self):
|
||||||
|
print "Case completed"
|
||||||
|
|
||||||
|
@unittest.skipIf(TEARDOWN == False, "Test data won't be erased.")
|
||||||
|
def test_ClearData(self):
|
||||||
|
#2. Delete project(PA);
|
||||||
|
self.project.delete_project(TestProjects.project_gc_id, **TestProjects.USER_GC_CLIENT)
|
||||||
|
|
||||||
|
#3. Delete user(UA);
|
||||||
|
self.user.delete_user(TestProjects.user_gc_id, **ADMIN_CLIENT)
|
||||||
|
|
||||||
|
def testGarbageCollection(self):
|
||||||
|
"""
|
||||||
|
Test case:
|
||||||
|
Garbage Collection
|
||||||
|
Test step and expected result:
|
||||||
|
1. Create a new user(UA);
|
||||||
|
2. Create a new project(PA) by user(UA);
|
||||||
|
3. Push a new image(IA) in project(PA) by admin;
|
||||||
|
4. Delete repository(RA) by user(UA);
|
||||||
|
5. Get repository by user(UA), it should get nothing;
|
||||||
|
6. Tigger garbage collection operation;
|
||||||
|
7. Check garbage collection job was finished;
|
||||||
|
8. Get garbage collection log, check there is number of files was deleted.
|
||||||
|
Tear down:
|
||||||
|
1. Delete project(PA);
|
||||||
|
2. Delete user(UA).
|
||||||
|
"""
|
||||||
|
url = ADMIN_CLIENT["endpoint"]
|
||||||
|
admin_name = ADMIN_CLIENT["username"]
|
||||||
|
admin_password = ADMIN_CLIENT["password"]
|
||||||
|
user_gc_password = "Aa123456"
|
||||||
|
|
||||||
|
#1. Create a new user(UA);
|
||||||
|
TestProjects.user_gc_id, user_gc_name = self.user.create_user_success(user_password = user_gc_password, **ADMIN_CLIENT)
|
||||||
|
|
||||||
|
TestProjects.USER_GC_CLIENT=dict(endpoint = url, username = user_gc_name, password = user_gc_password)
|
||||||
|
|
||||||
|
#2. Create a new project(PA) by user(UA);
|
||||||
|
TestProjects.project_gc_id, project_gc_name = self.project.create_project(metadata = {"public": "false"}, **TestProjects.USER_GC_CLIENT)
|
||||||
|
|
||||||
|
#3. Push a new image(IA) in project(PA) by admin;
|
||||||
|
repo_name, _ = push_image_to_project(project_gc_name, harbor_server, admin_name, admin_password, "tomcat", "latest")
|
||||||
|
|
||||||
|
#4. Delete repository(RA) by user(UA);
|
||||||
|
self.repo.delete_repoitory(repo_name, **TestProjects.USER_GC_CLIENT)
|
||||||
|
|
||||||
|
#5. Get repository by user(UA), it should get nothing;
|
||||||
|
repo_data = self.repo.get_repository(TestProjects.project_gc_id, **TestProjects.USER_GC_CLIENT)
|
||||||
|
_assert_status_code(len(repo_data), 0)
|
||||||
|
|
||||||
|
#6. Tigger garbage collection operation;
|
||||||
|
gc_id = self.system.gc_now(**ADMIN_CLIENT)
|
||||||
|
|
||||||
|
#7. Check garbage collection job was finished;
|
||||||
|
self.system.validate_gc_job_status(gc_id, "finished", **ADMIN_CLIENT)
|
||||||
|
|
||||||
|
#8. Get garbage collection log, check there is number of files was deleted.
|
||||||
|
self.system.validate_deletion_success(gc_id, **ADMIN_CLIENT)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -33,3 +33,5 @@ Test Case - Manage Project Member
|
|||||||
Harbor API Test ./tests/apitests/python/test_manage_project_member.py
|
Harbor API Test ./tests/apitests/python/test_manage_project_member.py
|
||||||
Test Case - Project Level Policy Content Trust
|
Test Case - Project Level Policy Content Trust
|
||||||
Harbor API Test ./tests/apitests/python/test_project_level_policy_content_trust.py
|
Harbor API Test ./tests/apitests/python/test_project_level_policy_content_trust.py
|
||||||
|
Test Case - Garbage Collection
|
||||||
|
Harbor API Test ./tests/apitests/python/test_garbage_collection.py
|
Loading…
Reference in New Issue
Block a user