From 843b05c2d3fe5728638422c3d4f2d4d2ee9518bc Mon Sep 17 00:00:00 2001 From: danfengliu Date: Sat, 7 Mar 2020 11:15:19 +0800 Subject: [PATCH] Add script of push cnab bunlde API test Signed-off-by: danfengliu --- .github/workflows/CI.yml | 10 +- .../python/bundle_data/bundle.json.tmpl | 1 + tests/apitests/python/library/cnab.py | 51 ++++++++++ tests/apitests/python/library/docker_api.py | 16 ++-- tests/apitests/python/library/repository.py | 12 ++- .../test_copy_artifact_outside_project.py | 2 +- tests/apitests/python/test_del_repo.py | 4 +- .../python/test_garbage_collection.py | 2 +- .../apitests/python/test_push_cnab_bundle.py | 95 +++++++++++++++++++ tests/apitests/python/test_retention.py | 6 +- tests/robot-cases/Group0-BAT/API_DB.robot | 2 + 11 files changed, 182 insertions(+), 19 deletions(-) create mode 100644 tests/apitests/python/bundle_data/bundle.json.tmpl create mode 100644 tests/apitests/python/library/cnab.py create mode 100644 tests/apitests/python/test_push_cnab_bundle.py diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b0b029b2f..6f8644bd5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -109,6 +109,7 @@ jobs: cd src/github.com/goharbor/harbor pwd go env + echo "::set-env name=CNAB_PATH::$(go env GOPATH)/src/github.com/docker" echo "::set-env name=GOPATH::$(go env GOPATH):$GITHUB_WORKSPACE" echo "::add-path::$(go env GOPATH)/bin" echo "::set-env name=TOKEN_PRIVATE_KEY_PATH::${GITHUB_WORKSPACE}/src/github.com/goharbor/harbor/tests/private_key.pem" @@ -130,9 +131,14 @@ jobs: sudo cp ./tests/harbor_ca.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates sudo service docker restart - wget https://get.helm.sh/helm-v3.1.1-linux-386.tar.gz && tar zxvf helm-v3.1.1-linux-386.tar.gz && \ - sudo mv linux-386/helm /usr/local/bin/helm3 && \ + wget https://get.helm.sh/helm-v3.1.1-linux-386.tar.gz && tar zxvf helm-v3.1.1-linux-386.tar.gz + sudo mv linux-386/helm /usr/local/bin/helm3 helm3 plugin install https://github.com/chartmuseum/helm-push + mkdir -p $CNAB_PATH && cd $CNAB_PATH && git clone https://github.com/cnabio/cnab-to-oci.git + cd cnab-to-oci && git checkout v0.3.0-beta4 + go list + make build + sudo mv bin/cnab-to-oci /usr/local/bin - name: install run: | cd src/github.com/goharbor/harbor diff --git a/tests/apitests/python/bundle_data/bundle.json.tmpl b/tests/apitests/python/bundle_data/bundle.json.tmpl new file mode 100644 index 000000000..fab3c48a8 --- /dev/null +++ b/tests/apitests/python/bundle_data/bundle.json.tmpl @@ -0,0 +1 @@ +{"actions":{"io.cnab.status":{}},"definitions":{"port":{"default":"8080","type":"string"},"text":{"default":"Hello, World!","type":"string"}},"description":"Hello, World!","images":{"hello":{"contentDigest":"sha256:61d5cb94d7e546518a7bbd5bee06bfad0ecea8f56a75b084522a43dccbbcd845","description":"hello","image":"{{ .ServiceImage }}","imageType":"docker","mediaType":"application/vnd.docker.distribution.manifest.v2+json","size":528}},"invocationImages":[{"contentDigest":"sha256:61d5cb94d7e546518a7bbd5bee06bfad0ecea8f56a75b084522a43dccbbcd845","image":"{{ .InvocationImage }}","imageType":"docker","mediaType":"application/vnd.docker.distribution.manifest.v2+json","size":941}],"maintainers":[{"email":"user@email.com","name":"user"}],"name":"hello-world","parameters":{"fields":{"definition":"","destination":null}},"schemaVersion":"v1.0.0","version":"0.1.0"} \ No newline at end of file diff --git a/tests/apitests/python/library/cnab.py b/tests/apitests/python/library/cnab.py new file mode 100644 index 000000000..85f60f62c --- /dev/null +++ b/tests/apitests/python/library/cnab.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import base +import json +import docker_api + +def load_bundle(service_image, invocation_image): + bundle_file = "./tests/apitests/python/bundle_data/bundle.json" + bundle_tmpl_file = "./tests/apitests/python/bundle_data/bundle.json.tmpl" + with open(bundle_tmpl_file,'r') as load_f: + load_dict = json.load(load_f) + print "load_dict:", load_dict + print "load_dict-invocationImages:", load_dict["invocationImages"][0]["contentDigest"] + load_dict["images"]["hello"]["image"] = service_image + load_dict["invocationImages"][0]["image"] = invocation_image + bundle_str = json.dumps(load_dict) + print "bundle_str:", bundle_str + with open(bundle_file,'w') as dump_f: + dump_f.write(bundle_str) + dump_f.close() + return bundle_file + +def cnab_fixup_bundle(bundle_file, target, auto_update_bundle = True): + fixed_bundle_file = "./tests/apitests/python/bundle_data/fixed-bundle.json" + command = ["sudo", "cnab-to-oci", "--log-level", "debug", "fixup", bundle_file, "--target", target, "--bundle", fixed_bundle_file] + if auto_update_bundle == True: + command.append("--auto-update-bundle") + #fixed_bundle_file = bundle_file + print "Command: ", command + ret = base.run_command(command) + print "Command return: ", ret + return fixed_bundle_file + +def cnab_push_bundle(bundle_file, target): + command = ["cnab-to-oci", "push", bundle_file, "--target", target, "--auto-update-bundle"] + print "Command: ", command + ret = base.run_command(command) + print "Command return: ", ret + for line in ret.split("\n"): + line = line.replace('\"', '') + if line.find('sha256') >= 0: + return line[-71:] + raise Exception(r"Fail to get sha256 in returned data: {}".format(ret)) + +def push_cnab_bundle(harbor_server, user, password, service_image, invocation_image, target, auto_update_bundle = True): + docker_api.docker_login(harbor_server, user, password, enable_manifest = False) + bundle_file = load_bundle(service_image, invocation_image) + fixed_bundle_file = cnab_fixup_bundle(bundle_file, target, auto_update_bundle = auto_update_bundle) + sha256 = cnab_push_bundle(fixed_bundle_file, target) + print "sha256:", sha256 + return sha256 diff --git a/tests/apitests/python/library/docker_api.py b/tests/apitests/python/library/docker_api.py index c5c259ae5..7ebf5265f 100644 --- a/tests/apitests/python/library/docker_api.py +++ b/tests/apitests/python/library/docker_api.py @@ -10,14 +10,15 @@ except ImportError: pip.main(['install', 'docker']) import docker -def docker_login(harbor_host, user, password): +def docker_login(harbor_host, user, password, enable_manifest = True): command = ["sudo", "docker", "login", harbor_host, "-u", user, "-p", password] print "Docker Login Command: ", command base.run_command(command) - try: - ret = subprocess.check_output(["./tests/apitests/python/update_docker_cfg.sh"], shell=False) - except subprocess.CalledProcessError, exc: - raise Exception("Failed to update docker config, error is {} {}.".format(exc.returncode, exc.output)) + if enable_manifest == True: + try: + subprocess.check_output(["./tests/apitests/python/update_docker_cfg.sh"], shell=False) + except subprocess.CalledProcessError, exc: + raise Exception("Failed to update docker config, error is {} {}.".format(exc.returncode, exc.output)) def docker_manifest_create(index, manifests): command = ["sudo", "docker","manifest","create",index] @@ -149,6 +150,9 @@ class DockerAPI(object): print("build image %s with size %d" % (repo, size)) self.DCLIENT.remove_image(repo) self.DCLIENT.remove_container(c) + self.DCLIENT.pull(repo) + image = self.DCLIENT2.images.get(repo) + return repo, image.id except Exception, err: caught_err = True if expected_error_message is not None: @@ -165,5 +169,3 @@ class DockerAPI(object): 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)) - - diff --git a/tests/apitests/python/library/repository.py b/tests/apitests/python/library/repository.py index d7c9d795a..47c6f96c6 100644 --- a/tests/apitests/python/library/repository.py +++ b/tests/apitests/python/library/repository.py @@ -37,7 +37,7 @@ def push_special_image_to_project(project_name, registry, username, password, im time.sleep(2) if expected_login_error_message != None: return - _docker_api.docker_image_build(r'{}/{}/{}'.format(registry, project_name, image), tags = tags, size=size, expected_error_message=expected_error_message) + return _docker_api.docker_image_build(r'{}/{}/{}'.format(registry, project_name, image), tags = tags, size=size, expected_error_message=expected_error_message) def is_repo_exist_in_project(repositories, repo_name): result = False @@ -78,12 +78,18 @@ class Repository(base.Base): _, status_code, _ = client.delete_repository_with_http_info(project_name, repo_name) base._assert_status_code(200, status_code) - def get_repository(self, project_name, **kwargs): + def list_repositories(self, project_name, **kwargs): client = self._get_client(**kwargs) data, status_code, _ = client.list_repositories_with_http_info(project_name) base._assert_status_code(200, status_code) return data + def get_repository(self, project_name, repo_name, **kwargs): + client = self._get_client(**kwargs) + data, status_code, _ = client.get_repository_with_http_info(project_name, repo_name) + base._assert_status_code(200, status_code) + return data + def add_label_to_tag(self, repo_name, tag, label_id, expect_status_code = 200, **kwargs): client = self._get_client(**kwargs) label = swagger_client.Label(id=label_id) @@ -122,7 +128,7 @@ class Repository(base.Base): return data def repository_should_exist(self, project_id, repo_name, **kwargs): - repositories = self.get_repository(project_id, **kwargs) + repositories = self.list_repositories(project_id, **kwargs) if is_repo_exist_in_project(repositories, repo_name) == False: raise Exception("Repository {} is not exist.".format(repo_name)) diff --git a/tests/apitests/python/test_copy_artifact_outside_project.py b/tests/apitests/python/test_copy_artifact_outside_project.py index 8e10bba85..5229f0382 100644 --- a/tests/apitests/python/test_copy_artifact_outside_project.py +++ b/tests/apitests/python/test_copy_artifact_outside_project.py @@ -91,7 +91,7 @@ class TestProjects(unittest.TestCase): TestProjects.src_repo_name, tag_name = push_image_to_project(TestProjects.project_src_repo_name, harbor_server, 'admin', 'Harbor12345', "hello-world", pull_tag_name) #6. Get repository in project(PA), there should be one repository which was created by user(UA); - src_repo_data = self.repo.get_repository(TestProjects.project_src_repo_name, **TestProjects.USER_RETAG_CLIENT) + src_repo_data = self.repo.list_repositories(TestProjects.project_src_repo_name, **TestProjects.USER_RETAG_CLIENT) _assert_status_code(TestProjects.src_repo_name, src_repo_data[0].name) #7. Get repository(RA)'s image tag detail information; diff --git a/tests/apitests/python/test_del_repo.py b/tests/apitests/python/test_del_repo.py index 0676339e5..221ed080e 100644 --- a/tests/apitests/python/test_del_repo.py +++ b/tests/apitests/python/test_del_repo.py @@ -62,14 +62,14 @@ class TestProjects(unittest.TestCase): repo_name, _ = push_image_to_project(TestProjects.project_del_repo_name, harbor_server, 'admin', 'Harbor12345', "hello-world", "latest") #4. Get repository in project(PA), there should be one repository which was created by user(UA); - repo_data = self.repo.get_repository(TestProjects.project_del_repo_name, **TestProjects.USER_del_repo_CLIENT) + repo_data = self.repo.list_repositories(TestProjects.project_del_repo_name, **TestProjects.USER_del_repo_CLIENT) _assert_status_code(repo_name, repo_data[0].name) #5. Delete repository(RA) by user(UA); self.repo.delete_repoitory(TestProjects.project_del_repo_name, repo_name.split('/')[1], **TestProjects.USER_del_repo_CLIENT) #6. Get repository by user(UA), it should get nothing; - repo_data = self.repo.get_repository(TestProjects.project_del_repo_name, **TestProjects.USER_del_repo_CLIENT) + repo_data = self.repo.list_repositories(TestProjects.project_del_repo_name, **TestProjects.USER_del_repo_CLIENT) _assert_status_code(len(repo_data), 0) if __name__ == '__main__': diff --git a/tests/apitests/python/test_garbage_collection.py b/tests/apitests/python/test_garbage_collection.py index 1df2b4c55..3ef3fd649 100644 --- a/tests/apitests/python/test_garbage_collection.py +++ b/tests/apitests/python/test_garbage_collection.py @@ -69,7 +69,7 @@ class TestProjects(unittest.TestCase): self.repo.delete_repoitory(TestProjects.project_gc_name, repo_name.split('/')[1], **TestProjects.USER_GC_CLIENT) #5. Get repository by user(UA), it should get nothing; - repo_data = self.repo.get_repository(TestProjects.project_gc_name, **TestProjects.USER_GC_CLIENT) + repo_data = self.repo.list_repositories(TestProjects.project_gc_name, **TestProjects.USER_GC_CLIENT) _assert_status_code(len(repo_data), 0) #6. Tigger garbage collection operation; diff --git a/tests/apitests/python/test_push_cnab_bundle.py b/tests/apitests/python/test_push_cnab_bundle.py new file mode 100644 index 000000000..ba442efdf --- /dev/null +++ b/tests/apitests/python/test_push_cnab_bundle.py @@ -0,0 +1,95 @@ +from __future__ import absolute_import + + +import unittest + +import library.repository +import library.cnab +from testutils import ADMIN_CLIENT +from testutils import harbor_server + +from testutils import TEARDOWN +from library.project import Project +from library.user import User +from library.repository import Repository +from library.artifact import Artifact +from library.docker_api import DockerAPI + +class TestProjects(unittest.TestCase): + @classmethod + def setUpClass(self): + self.project= Project() + self.user= User() + self.artifact = Artifact(api_type='artifact') + self.repo= Repository(api_type='repository') + self.url = ADMIN_CLIENT["endpoint"] + self.user_push_chart_password = "Aa123456" + self.cnab_repo_name = "test_cnab" + + @classmethod + def tearDownClass(self): + print "Case completed" + + @unittest.skipIf(TEARDOWN == False, "Test data won't be erased.") + def test_ClearData(self): + #1. Delete repository(RA) by user(UA); + self.repo.delete_repoitory(TestProjects.project_push_bundle_name, self.cnab_repo_name, **TestProjects.USER_CLIENT) + + #2. Delete project(PA); + self.project.delete_project(TestProjects.project_push_bundle_id, **TestProjects.USER_CLIENT) + + #3. Delete user(UA). + self.user.delete_user(TestProjects.user_id, **ADMIN_CLIENT) + + def testPushBundleByCnab(self): + """ + Test case: + Push Bundle By Cnab + Test step and expected result: + 1. Create a new user(UA); + 2. Create a new project(PA) by user(UA); + 3. Pull images for bundle; + 4. Push bundle to harbor as repository(RA); + 5. Get repository from Harbor successfully; + 6. Verfiy bundle name; + 7. Get artifact by sha256; + 8. Verify artifact information. + Tear down: + 1. Delete repository(RA) by user(UA); + 2. Delete project(PA); + 3. Delete user(UA). + """ + #1. Create a new user(UA); + TestProjects.user_id, user_name = self.user.create_user(user_password = self.user_push_chart_password, **ADMIN_CLIENT) + TestProjects.USER_CLIENT=dict(endpoint = self.url, username = user_name, password = self.user_push_chart_password) + + + #2. Create a new project(PA) by user(UA); + TestProjects.project_push_bundle_id, TestProjects.project_push_bundle_name = self.project.create_project(metadata = {"public": "false"}, **TestProjects.USER_CLIENT) + + #3. Pull images for bundle; + _docker_api = DockerAPI() + _docker_api.docker_image_pull("hello-world", tag = "latest") + _docker_api.docker_image_pull("busybox", tag = "latest") + + #4. Push bundle to harbor as repository(RA); + target = harbor_server + "/" + TestProjects.project_push_bundle_name + "/" + self.cnab_repo_name + reference_sha256 = library.cnab.push_cnab_bundle(harbor_server, user_name, self.user_push_chart_password, "hello-world:latest", "busybox:latest", target) + + #5. Get repository from Harbor successfully; + index_data = self.repo.get_repository(TestProjects.project_push_bundle_name, self.cnab_repo_name, **TestProjects.USER_CLIENT) + print "index_data:", index_data + + #6. Verfiy bundle name; + self.assertEqual(index_data.name, TestProjects.project_push_bundle_name + "/" + self.cnab_repo_name) + + #7. Get artifact by sha256; + artifact = self.artifact.get_reference_info(TestProjects.project_push_bundle_name, self.cnab_repo_name, reference_sha256, **TestProjects.USER_CLIENT) + + #8. Verify artifact information; + self.assertEqual(artifact[0].type, 'CNAB') + self.assertEqual(artifact[0].digest, reference_sha256) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/apitests/python/test_retention.py b/tests/apitests/python/test_retention.py index d67f139c0..e639cb630 100644 --- a/tests/apitests/python/test_retention.py +++ b/tests/apitests/python/test_retention.py @@ -59,7 +59,7 @@ class TestProjects(unittest.TestCase): push_special_image_to_project(TestProjects.project_src_repo_name, harbor_server, user_ra_name, user_ra_password, "test3", ['1.0']) push_special_image_to_project(TestProjects.project_src_repo_name, harbor_server, user_ra_name, user_ra_password, "test4", ['1.0']) - resp=self.repo.get_repository(TestProjects.project_src_repo_name, **TestProjects.USER_RA_CLIENT) + resp=self.repo.list_repositories(TestProjects.project_src_repo_name, **TestProjects.USER_RA_CLIENT) self.assertEqual(len(resp), 4) # Create Retention Policy @@ -91,7 +91,7 @@ class TestProjects(unittest.TestCase): print(resp) # TODO As the repository isn't deleted when no tags left anymore # TODO we should check the artifact/tag count here - # resp=self.repo.get_repository(TestProjects.project_src_repo_id, **TestProjects.USER_RA_CLIENT) + # resp=self.repo.list_repositories(TestProjects.project_src_repo_id, **TestProjects.USER_RA_CLIENT) # self.assertEqual(len(resp), 3) @@ -102,7 +102,7 @@ class TestProjects(unittest.TestCase): # TODO delete_repoitory will fail when no tags left anymore # @unittest.skipIf(TEARDOWN == False, "Test data won't be erased.") # def test_ClearData(self): - # resp=self.repo.get_repository(TestProjects.project_src_repo_id, **TestProjects.USER_RA_CLIENT) + # resp=self.repo.list_repositories(TestProjects.project_src_repo_id, **TestProjects.USER_RA_CLIENT) # for repo in resp: # self.repo.delete_repoitory(repo.name, **TestProjects.USER_RA_CLIENT) # self.project.delete_project(TestProjects.project_src_repo_id, **TestProjects.USER_RA_CLIENT) diff --git a/tests/robot-cases/Group0-BAT/API_DB.robot b/tests/robot-cases/Group0-BAT/API_DB.robot index 74b35a2b2..8af75ffba 100644 --- a/tests/robot-cases/Group0-BAT/API_DB.robot +++ b/tests/robot-cases/Group0-BAT/API_DB.robot @@ -71,4 +71,6 @@ Test Case - Push Index By Docker Manifest Harbor API Test ./tests/apitests/python/test_push_index_by_docker_manifest.py Test Case - Push Index By Docker Manifest Harbor API Test ./tests/apitests/python/test_push_chart_by_helm3_chart_cli.py +Test Case - Push Cnab Bundle + Harbor API Test ./tests/apitests/python/test_push_cnab_bundle.py