Add Nightly GUI test for system robot account

1. Add GUI test for system robot account feature;
2. Fix issue of failing to catch docker pull image exception in API test.

Signed-off-by: Danfeng Liu (c) <danfengl@vmware.com>
This commit is contained in:
Danfeng Liu (c) 2020-12-25 15:10:30 +08:00
parent 06e993ff76
commit ae05e3fdb0
8 changed files with 245 additions and 55 deletions

View File

@ -86,6 +86,8 @@ class DockerAPI(object):
self.DCLIENT2 = docker.from_env()
def docker_login(self, registry, username, password, expected_error_message = None):
ret = ""
err_message = ""
if username == "" or password == "":
print("[Warnig]: No docker credential was provided.")
return
@ -93,46 +95,55 @@ class DockerAPI(object):
expected_error_message = None
if registry == "docker":
registry = None
ret = ""
try:
print("Docker login: {}:{}:{}".format(registry,username,password))
ret = self.DCLIENT.login(registry = registry, username=username, password=password)
print("Docker image login commond return:", ret)
return ret
except docker.errors.APIError as err:
except Exception as err:
print( "Docker image pull catch exception:", str(err))
err_message = str(err)
if expected_error_message is None:
raise Exception(r" Docker pull image {} failed, error is [{}]".format (image, str(err)))
else:
print("Docker image login did not catch exception and return message is:", ret)
err_message = ret
finally:
if expected_error_message is not None:
print( "docker login error:", str(err))
if str(err).lower().find(expected_error_message.lower()) < 0:
raise Exception(r"Docker login: Return message {} is not as expected {}".format(str(err), expected_error_message))
if str(err_message).lower().find(expected_error_message.lower()) < 0:
raise Exception(r" Failed to catch error [{}] when login image {}, return message: {}".format (expected_error_message, image, err_message))
else:
print(r"Docker image login got expected error message:{}".format(expected_error_message))
else:
raise Exception(r" Docker login failed, error is [{}]".format (str(err)))
if str(err_message).lower().find("error".lower()) >= 0:
raise Exception(r" It's was not suppose to catch error when login image {}, return message is [{}]".format (image, err_message))
def docker_image_pull(self, image, tag = None, expected_error_message = None):
ret = ""
err_message = ""
if tag is not None:
_tag = tag
else:
_tag = "latest"
if expected_error_message is "":
expected_error_message = None
ret = ""
try:
ret = self.DCLIENT.pull(r'{}:{}'.format(image, _tag))
print("Docker image pull commond return:", ret)
return ret
except Exception as err:
if expected_error_message is not None:
print( "docker image pull error:", str(err))
if str(err).lower().find(expected_error_message.lower()) < 0:
raise Exception(r"Pull image: Return message {} is not as expected {}".format(str(err), expected_error_message))
else:
print( "Docker image pull catch exception:", str(err))
err_message = str(err)
if expected_error_message is None:
raise Exception(r" Docker pull image {} failed, error is [{}]".format (image, str(err)))
else:
print("Docker image pull did not catch exception and return message is:", ret)
err_message = ret
finally:
if expected_error_message is not None:
if str(ret).lower().find(expected_error_message.lower()) < 0:
raise Exception(r" Failed to catch error [{}] when pull image {}, return message: {}".format (expected_error_message, image, str(ret)))
if str(err_message).lower().find(expected_error_message.lower()) < 0:
raise Exception(r" Failed to catch error [{}] when pull image {}, return message: {}".format (expected_error_message, image, err_message))
else:
print(r"Docker image pull got expected error message:{}".format(expected_error_message))
else:
if str(ret).lower().find("error".lower()) >= 0:
raise Exception(r" It's was not suppose to catch error when pull image {}, return message is [{}]".format (image, ret))
if str(err_message).lower().find("error".lower()) >= 0:
raise Exception(r" It's was not suppose to catch error when pull image {}, return message is [{}]".format (image, err_message))
def docker_image_tag(self, image, harbor_registry, tag = None):
_tag = base._random_name("tag")
@ -148,40 +159,37 @@ class DockerAPI(object):
def docker_image_push(self, harbor_registry, tag, expected_error_message = None):
ret = ""
err_message = ""
if expected_error_message is "":
expected_error_message = None
try:
ret = self.DCLIENT.push(harbor_registry, tag)
print("Docker image push commond return:", ret)
except Exception as err:
print( "docker image push catch Exception:", str(err))
if expected_error_message is not None:
print( "docker image push error:", str(err))
if str(err).lower().find(expected_error_message.lower()) < 0:
raise Exception(r"Push image: Return message {} is not as expected {}".format(str(err), expected_error_message))
else:
raise Exception(r" Docker push image {} failed, error is [{}]".format (harbor_registry, message))
print( "Docker image push catch exception:", str(err))
err_message = str(err)
if expected_error_message is None:
raise Exception(r" Docker push image {} failed, error is [{}]".format (image, str(err)))
else:
print( "docker image push does not catch Exception:", str(expected_error_message))
print("Docker image push did not catch exception and return message is:", ret)
err_message = ret
finally:
if expected_error_message is not None:
if str(ret).lower().find(expected_error_message.lower()) < 0:
raise Exception(r" Failed to catch error [{}] when push image {}, return message: {}".
format (expected_error_message, harbor_registry, str(ret)))
if str(err_message).lower().find(expected_error_message.lower()) < 0:
raise Exception(r" Failed to catch error [{}] when push image {}, return message: {}".format (expected_error_message, harbor_registry, err_message))
else:
print("docker image push action return expected error message [{}]".format(expected_error_message))
print(r"Docker image push got expected error message:{}".format(expected_error_message))
else:
if str(ret).lower().find("errorDetail".lower()) >= 0:
raise Exception(r" It's was not suppose to catch error when push image {}, return message is [{}]".
format (harbor_registry, ret))
if str(err_message).lower().find("error".lower()) >= 0:
raise Exception(r" It's was not suppose to catch error when push image {}, return message is [{}]".format (harbor_registry, err_message))
def docker_image_build(self, harbor_registry, tags=None, size=1, expected_error_message = None):
ret = ""
err_message = ""
try:
baseimage='busybox:latest'
self.DCLIENT.login(username=DOCKER_USER, password=DOCKER_PWD)
if not self.DCLIENT.images(name=baseimage):
print( "docker pull is triggered when building {}".format(harbor_registry))
print( "Docker pull is triggered when building {}".format(harbor_registry))
self.DCLIENT.pull(baseimage)
c=self.DCLIENT.create_container(image='busybox:latest',command='dd if=/dev/urandom of=test bs=1M count=%d' % size )
self.DCLIENT.start(c)
@ -203,23 +211,20 @@ class DockerAPI(object):
self.DCLIENT.remove_container(c)
#self.DCLIENT.pull(repo)
#image = self.DCLIENT2.images.get(repo)
return repo
except Exception as err:
if expected_error_message is not None:
print( "docker image build error:", str(err))
if str(err).lower().find(expected_error_message.lower()) < 0:
raise Exception(r"Push image: Return message {} is not as expected {}".format(str(err), expected_error_message))
else:
raise Exception(r" Docker build image {} failed, error is [{}]".format (harbor_registry, str(err)))
print( "Docker image build catch exception:", str(err))
err_message = str(err)
if expected_error_message is None:
raise Exception(r" Docker push image {} failed, error is [{}]".format (image, str(err)))
else:
print("docker image build does not catch Exception:", str(expected_error_message))
print("Docker build -> docker image push ret:", ret)
print("Docker image build did not catch exception and return message is:", ret)
err_message = ret
finally:
if expected_error_message is not None:
if str(ret).lower().find(expected_error_message.lower()) < 0:
raise Exception(r" Failed to catch error [{}] when build image {}, return message: {}".
format (expected_error_message, harbor_registry, str(ret)))
if str(err_message).lower().find(expected_error_message.lower()) < 0:
raise Exception(r" Failed to catch error [{}] when build image {}, return message: {}".format (expected_error_message, harbor_registry, err_message))
else:
print("docker image build return expected error message [{}]".format(expected_error_message))
print(r"Docker image build got expected error message: {}".format(expected_error_message))
else:
if str(ret).lower().find("errorDetail".lower()) >= 0:
raise Exception(r" It's was not suppose to catch error when push image {}, return message is [{}]".format (harbor_registry, ret))
if str(err_message).lower().find("error".lower()) >= 0:
raise Exception(r" It's was not suppose to catch error when build image {}, return message is [{}]".format (harbor_registry, err_message))

View File

@ -13,8 +13,7 @@ def pull_harbor_image(registry, username, password, image, tag, expected_login_e
if expected_login_error_message != None:
return
time.sleep(2)
ret = _docker_api.docker_image_pull(r'{}/{}'.format(registry, image), tag = tag, expected_error_message = expected_error_message)
print("Docker pull image return message: {}".format(ret))
_docker_api.docker_image_pull(r'{}/{}'.format(registry, image), tag = tag, expected_error_message = expected_error_message)
def push_image_to_project(project_name, registry, username, password, image, tag, expected_login_error_message = None, expected_error_message = None, profix_for_image = None, new_image=None):
print("Start to push image {}/{}/{}:{}".format(registry, project_name, image, tag) )

View File

@ -156,6 +156,11 @@ Switch To Distribution
Retry Element Click xpath=//clr-main-container//clr-vertical-nav-group//span[contains(.,'Distributions')]
Sleep 1
Switch To Robot Account
Sleep 1
Retry Element Click xpath=//clr-main-container//clr-vertical-nav-group//span[contains(.,'Robot Accounts')]
Sleep 1
Modify Token Expiration
[Arguments] ${minutes}
Input Text xpath=//*[@id='tokenExpiration'] ${minutes}

View File

@ -35,6 +35,7 @@ ${create_project_CANCEL_button_xpath} xpath=//button[contains(.,'CANCEL')]
${create_project_OK_button_xpath} xpath=//button[contains(.,'OK')]
${delete_confirm_btn} xpath=//button[contains(.,'DELETE')]
${project_statistics_private_repository_icon} xpath=//project/div/div/div[1]/div/statistics-panel/div/div[2]/div[1]/div[2]/div[2]/statistics/div/span[1]
${project_statistics_total_projects_icon} xpath=//div[contains(@class, 'statistic-column-block') and contains(., 'TOTAL')]//div[1]/statistics//span[contains(@class, 'statistic-data')]
${repo_delete_confirm_btn} xpath=//clr-modal//button[2]
${repo_retag_confirm_dlg} css=${modal-dialog}
${repo_delete_on_card_view_btn} //clr-modal//button[contains(.,'DELETE')]

View File

@ -0,0 +1,122 @@
# 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
*** Settings ***
Documentation This resource provides any keywords related to the Harbor private registry appliance
Resource ../../resources/Util.robot
*** Variables ***
*** Keywords ***
Create A Random Permission Item List
${permission_item_all_list}= Create List Push Artifact
... Pull Artifact
... Delete Artifact
... Read Helm Chart
... Create Helm Chart Version
... Delete Helm Chart Version
... Create Tag
... Delete Tag
... Create Artifact label
... Create Scan
Set Suite Variable ${permission_item_all_list}
${len}= Get Length ${permission_item_all_list}
${tmp_list}= Create List @{EMPTY}
FOR ${i} IN RANGE 0 ${${len}-1}
${r}= Evaluate random.randint(0, 1)
Run Keyword If '${r}'=='1' Append To List ${tmp_list} ${permission_item_all_list}[${i}]
END
Run Keyword If ${tmp_list}==@{EMPTY} Append To List ${tmp_list} ${permission_item_all_list}[${0}]
[Return] ${tmp_list}
Create A Random Project Permission List
[Arguments] ${project_count}
${tmp_list}= Create List @{EMPTY}
FOR ${i} IN RANGE ${project_count}
${d}= Get Current Date result_format=%m%s
${pro_name}= Set Variable project_${i}_${d}
${permission_item_list}= Create A Random Permission Item List
Log To Console '@{permission_item_list}'
Create An New Project And Go Into Project ${pro_name}
${tmp_dict} = Create Dictionary project_name=${pro_name} permission_item_list=@{permission_item_list}
Append To List ${tmp_list} ${tmp_dict}
END
Log To Console tmp_list:'@{tmp_list}'
[Return] ${tmp_list}
Filter Project In Project Permisstion List
[Arguments] ${name}
Retry Double Keywords When Error Retry Element Click ${save_sys_robot_project_filter_chb} Retry Wait Until Page Contains Element ${save_sys_robot_project_filter_input}
Retry Text Input ${save_sys_robot_project_filter_input} ${name}
Retry Double Keywords When Error Retry Element Click ${save_sys_robot_project_filter_close_btn} Retry Wait Until Page Not Contains Element ${save_sys_robot_project_filter_input}
Clear Global Permissions By JaveScript
Retry Element Click //button[contains(., 'RESET PERMISSIONS')]
FOR ${i} IN RANGE 0 10
Execute JavaScript document.getElementsByClassName('dropdown-item')[${i}].click();
END
Select Project Permission
[Arguments] ${project_name} ${permission_item_list}
FOR ${permission} IN @{permission_item_list}
Log To Console project: ${project_name}; permission: ${permission}
${item}= Set Variable //clr-dg-row[contains(., '${project_name}')]//clr-dropdown/clr-dropdown-menu//span[contains(., '${permission}')]
Execute JavaScript document.evaluate("${item}",document.body,null,9,null).singleNodeValue.click();
Capture Page Screenshot
END
Create A New System Robot Account
[Arguments] ${name}=${null} ${expiration_type}=default ${expiration_value}=${null} ${description}=${null} ${is_cover_all}=${false} ${cover_all_permission_list}=@{EMPTY} ${project_permission_list}=@{EMPTY}
${d}= Get Current Date result_format=%m%s
${name}= Set Variable If '${name}'=='${null}' robot_name${d} ${name}
Switch To Robot Account
Retry Double Keywords When Error Retry Element Click ${new_sys_robot_account_btn} Retry Wait Until Page Contains Element ${sys_robot_account_name_input}
Retry Text Input ${sys_robot_account_name_input} ${name}
Run Keyword If '${expiration_type}' != 'default' Run Keywords Retry Element Click xpath=${sys_robot_account_expiration_type_select} AND
... Retry Element Click xpath=${sys_robot_account_expiration_type_select}//option[@value='${expiration_type}']
Run Keyword If '${description}' != '${null}' Retry Text Input ${sys_robot_account_description_textarea} ${description}
Run Keyword If '${is_cover_all}' == '${true}' Retry Double Keywords When Error Retry Element Click ${sys_robot_account_coverall_chb} Retry Checkbox Should Be Selected ${sys_robot_account_coverall_chb_input}
#Clear Global Permissions
Clear Global Permissions By JaveScript
# Select project
FOR ${project} IN @{project_permission_list}
Log To Console project: ${project}
Should Be True type($project) is not dict
${tmp} = Convert To Dictionary ${project}
Should Be True type($tmp) is dict
${project_name}= Get From Dictionary ${tmp} project_name
Log To Console project_name: ${project_name}
${permission_item_list}= Get From Dictionary ${tmp} permission_item_list
Log To Console permission_item_list: ${permission_item_list}
Filter Project In Project Permisstion List ${project_name}
Retry Element Click //clr-dg-row[contains(.,'${project_name}')]//clr-checkbox-wrapper/label
Retry Element Click //clr-dg-row[contains(., '${project_name}')]//clr-dropdown/button
Select Project Permission ${project_name} ${permission_item_list}
END
# Save it
Retry Double Keywords When Error Retry Element Click ${save_sys_robot_account_btn} Retry Wait Until Page Not Contains Element ${save_sys_robot_account_btn}
Retry Double Keywords When Error Retry Element Click ${save_sys_robot_project_paste_icon} Retry Wait Until Page Not Contains Element ${save_sys_robot_project_paste_icon}
[Return] ${name}
System Robot Account Exist
[Arguments] ${name} ${project_count}
Retry Double Keywords When Error Retry Element Click ${filter_dist_btn} Wait Until Element Is Visible And Enabled ${filter_dist_input}
Retry Text Input ${filter_dist_input} ${name}
Retry Wait Until Page Contains Element //clr-dg-row[contains(.,'${name}') and contains(.,'${project_count} PROJECT')]

View File

@ -0,0 +1,32 @@
# 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
*** Settings ***
Documentation This resource provides any keywords related to the Harbor private registry appliance
*** Variables ***
${new_sys_robot_account_btn} //system-robot-accounts//button/span/span[contains(.,'NEW ROBOT ACCOUNT')]
${sys_robot_account_name_input} //*[@id='name']
${sys_robot_account_expiration_type_select} //*[@id='expiration-type']
${sys_robot_account_expiration_input} //*[@id='robotTokenExpiration']
${sys_robot_account_description_textarea} //*[@id='description']
${sys_robot_account_coverall_chb_input} xpath=//input[@id='coverAll']
${sys_robot_account_coverall_chb} //clr-checkbox-wrapper[contains(@class, 'clr-checkbox-wrapper')]/label[contains(@for, 'coverAll')]
${sys_robot_account_permission_list_btn} //form/section//clr-dropdown/button
${save_sys_robot_account_btn} //*[@id='system-robot-save']
${save_sys_robot_project_filter_chb} //clr-dg-string-filter/clr-dg-filter//clr-icon
${save_sys_robot_project_filter_input} //input[contains(@name, 'search')]
${save_sys_robot_project_filter_close_btn} //button/clr-icon[contains(@title, 'Close')]
${save_sys_robot_project_paste_icon} //hbr-copy-input//clr-icon

View File

@ -69,6 +69,8 @@ Resource Harbor-Pages/Vulnerability_Elements.robot
Resource Harbor-Pages/LDAP-Mode.robot
Resource Harbor-Pages/OIDC_Auth.robot
Resource Harbor-Pages/OIDC_Auth_Elements.robot
Resource Harbor-Pages/Robot_Account.robot
Resource Harbor-Pages/Robot_Account_Elements.robot
Resource Harbor-Pages/Verify.robot
Resource Docker-Util.robot
Resource CNAB_Util.robot

View File

@ -754,3 +754,27 @@ Test Case - P2P Preheat Policy CRUD
Delete A Distribution ${dist_name} ${endpoint}
Close Browser
Test Case - System Robot Account Cover All Projects
[Tags] sys_robot_account_cover
${d}= Get Current Date result_format=%m%s
${pro_name}= Set Variable project_${d}
Init Chrome Driver
Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD}
Create An New Project And Go Into Project ${pro_name}
${name}= Create A New System Robot Account is_cover_all=${true}
Navigate To Projects
Switch To Robot Account
System Robot Account Exist ${name} All
Close Browser
Test Case - System Robot Account
[Tags] sys_robot_account
${d}= Get Current Date result_format=%m%s
${project_count}= Evaluate random.randint(3, 5)
${pro_name}= Set Variable project_${d}
Init Chrome Driver
Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD}
${project_permission_list}= Create A Random Project Permission List ${project_count}
${name}= Create A New System Robot Account project_permission_list=${project_permission_list}
System Robot Account Exist ${name} ${project_count}
Close Browser