diff --git a/tests/apitests/python/library/docker_api.py b/tests/apitests/python/library/docker_api.py index 47ea25207..18d4276a9 100644 --- a/tests/apitests/python/library/docker_api.py +++ b/tests/apitests/python/library/docker_api.py @@ -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)) diff --git a/tests/apitests/python/library/repository.py b/tests/apitests/python/library/repository.py index 3ffdf7dd2..1857d946a 100644 --- a/tests/apitests/python/library/repository.py +++ b/tests/apitests/python/library/repository.py @@ -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) ) diff --git a/tests/resources/Harbor-Pages/Configuration.robot b/tests/resources/Harbor-Pages/Configuration.robot index 879d940b6..bb809e558 100644 --- a/tests/resources/Harbor-Pages/Configuration.robot +++ b/tests/resources/Harbor-Pages/Configuration.robot @@ -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} diff --git a/tests/resources/Harbor-Pages/Project_Elements.robot b/tests/resources/Harbor-Pages/Project_Elements.robot index 9c8e670a9..a643ae045 100644 --- a/tests/resources/Harbor-Pages/Project_Elements.robot +++ b/tests/resources/Harbor-Pages/Project_Elements.robot @@ -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')] diff --git a/tests/resources/Harbor-Pages/Robot_Account.robot b/tests/resources/Harbor-Pages/Robot_Account.robot new file mode 100644 index 000000000..fcab60084 --- /dev/null +++ b/tests/resources/Harbor-Pages/Robot_Account.robot @@ -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')] + diff --git a/tests/resources/Harbor-Pages/Robot_Account_Elements.robot b/tests/resources/Harbor-Pages/Robot_Account_Elements.robot new file mode 100644 index 000000000..7fdca6fb6 --- /dev/null +++ b/tests/resources/Harbor-Pages/Robot_Account_Elements.robot @@ -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 + diff --git a/tests/resources/Util.robot b/tests/resources/Util.robot index 61ac44723..fb5dfa058 100644 --- a/tests/resources/Util.robot +++ b/tests/resources/Util.robot @@ -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 diff --git a/tests/robot-cases/Group1-Nightly/Common.robot b/tests/robot-cases/Group1-Nightly/Common.robot index dd28042e6..9af34fcba 100644 --- a/tests/robot-cases/Group1-Nightly/Common.robot +++ b/tests/robot-cases/Group1-Nightly/Common.robot @@ -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