#!/usr/bin/python3 # checkpoint: # 1. create supported image import os import unittest import composerlib import testlib @testlib.nondestructive @testlib.timeout(2400) @testlib.no_retry_when_changed class TestImage(composerlib.ComposerCase): def testImageStep(self): b = self.browser self.login_and_go("/composer", superuser=True) b.wait_visible("#main") # create image wizard (no upload support) b.click("li[data-blueprint=httpd-server] #create-image-button") b.wait_text("#create-image-upload-wizard #blueprint-name", "httpd-server") # check ? (Process length help) button b.click("button[aria-label='Process length help']") b.wait_text(".pf-c-popover__body", "This process can take a while. " "Images are built in the order they are started.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # check non upload image action (Create only) b.wait_js_cond('ph_select("#image-type option").length > 1') b.set_val("#image-type", "qcow2") b.wait_val("#image-type", "qcow2") # check ? (image size help) button b.click("button[aria-label='Image size help']") b.wait_text(".pf-c-popover__body", "Set the size that you want the image to be when instantiated. The total " "package size and target destination of your image should be considered when " "setting the image size.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # groups action done b.wait_text("#continue-button", "Create") # check ? (image size help) button b.click("button[aria-label='Image size help']") b.wait_text(".pf-c-popover__body", "Set the size that you want the image to be when instantiated. The total " "package size and target destination of your image should be considered when " "setting the image size.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # default size = 2GB for qcow2 image b.wait_val("#image-size-input", 2) b.focus("#image-size-input") # delete 2 and input 1 b.key_press("\b") b.key_press("1") # error if less than 2 GB b.wait_attr("#create-image-upload-wizard button:contains('Create')", "disabled", "") b.wait_attr_contains("#image-size-input-helper", "class", "pf-m-error") # delete 1 and input 2001 b.key_press("\b") b.key_press("2001") # error if greater than 2000 GB b.wait_in_text("#image-size-input-helper", "The size specified is large. We recommend that you check whether your " "target destination has any restrictions on image size.") # delete 2001 and input 2 b.key_press("\b\b\b\b") b.key_press("2") # close wizard by clicking X button b.click(".pf-c-wizard__close") b.wait_not_present("#create-image-upload-wizard") # Test cancel button b.click("li[data-blueprint=httpd-server] #create-image-button") b.wait_text("#create-image-upload-wizard #blueprint-name", "httpd-server") b.click("button:contains('Cancel')") b.wait_not_present("#create-image-upload-wizard") # collect code coverage result self.check_coverage() def testAWSStep(self): b = self.browser self.login_and_go("/composer", superuser=True) b.wait_visible("#main") # create image wizard b.click("li[data-blueprint=httpd-server] #create-image-button") b.wait_js_cond('ph_select("#image-type option").length > 1') b.set_val("#image-type", "ami") b.wait_val("#image-type", "ami") # groups action done # still keep Create if upload image not selected b.wait_text("#continue-button", "Create") # default size = 6GB for ami image b.wait_val("#image-size-input", 6) # check ? (AWS upload image help) button b.click("button[aria-label='Upload image help']") b.wait_in_text(".pf-c-popover__body", " Image Builder can upload images you create to an S3 bucket in AWS and " "then import them into EC2. When the image build is complete and the upload" " action is successful, the image file is available in the AMI section of " "EC2. Most of the values required to upload the image can be found in the " "AWS Management Console. This upload process requires that you have an " "Identity and Access Management (IAM) role named vmimport to ensure that " "the image can be imported from the S3 bucket into EC2. For more details, " "refer to the AWS Required Service Role. ") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # continue button changes to Next when upload image selected b.click("#aws-checkbox") b.click("button:contains('Next')") # Back button is click-able and back to last page b.click("button:contains('Back')") b.wait_text("#create-image-upload-wizard #blueprint-name", "httpd-server") # go to each setion by clicking link directly b.click("button:contains('Authentication')") b.click("button:contains('Destination')") b.click("button:contains('Review')") b.wait_in_text(".pf-c-alert__title", "There are one or more fields that require your attention.") b.wait_attr("#continue-button", "disabled", "") # check and enter authentication fields b.click("button:contains('Authentication')") b.wait_visible("label:contains('Access key ID')") # check access key id help button b.click("button[aria-label='Access key ID help']") b.wait_text(".pf-c-popover__body", "You can create and find existing Access key IDs on the " "Identity and Access Management (IAM) page in the AWS console.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter access key id value b.focus("input[id='access-key-id-input']") b.key_press("never") b.wait_visible("label:contains('Secret access key')") # check secret access key help button b.click("button[aria-label='Secret access key help']") b.wait_text(".pf-c-popover__body", "You can view the Secret access key only when you create a new Access key ID " "on the Identity and Access Management (IAM) page in the AWS console.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter secret access key value b.focus("input[id='secret-access-key-input']") b.key_press("gunna") # check and enter destination fields b.click("button:contains('Destination')") b.wait_visible("label:contains('Image name')") # check image name help button b.click("button[aria-label='Image name help']") b.wait_text(".pf-c-popover__body", "Provide a file name to be used for the image file that will be uploaded.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter access key id value b.focus("input[id='image-name-input']") b.key_press("give") b.wait_visible("label:contains('Amazon S3 bucket')") # check amazon s3 bucket help button b.click("button[aria-label='S3 Bucket help']") b.wait_text(".pf-c-popover__body", " Provide the S3 bucket name to which the image file will be uploaded before " "being imported into EC2. The bucket must already exist in the Region where " "you want to import your image. You can find a list of buckets on the S3 " "buckets page in the Amazon S3 storage service in the AWS console. ") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter secret access key value b.focus("input[id='bucket-input']") b.key_press("you") b.wait_visible("label:contains('AWS region')") # check image name help button b.click("button[aria-label='AWS region help']") b.wait_text(".pf-c-popover__body", "Provide the AWS Region where you want to import your image. " "This must be the same region where the S3 bucket exists.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter access key id value b.focus("input[id='region-input']") b.key_press("up") # Verify AWS Review page b.click("button:contains('Review')") # check ? (AWS upload image help) button b.click("button[aria-label='AWS help']") b.wait_in_text(".pf-c-popover__body", " Image Builder can upload images you create to an S3 bucket in AWS and " "then import them into EC2. When the image build is complete and the " "upload action is successful, the image file is available in the AMI " "section of EC2. Most of the values required to upload the image can " "be found in the AWS Management Console. This upload process requires " "that you have an Identity and Access Management (IAM) role named vmimport " "to ensure that the image can be imported from the S3 bucket into EC2. For " "more details, refer to the AWS Required Service Role. ") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # Check that the expected fields and values are present b.wait_in_text("#aws-content", "Access key ID") b.wait_in_text("#aws-content", "*****") b.wait_in_text("#aws-content", "Secret access key") b.wait_in_text("#aws-content", "*****") b.wait_in_text("#aws-content", "Image name") b.wait_in_text("#aws-content", "give") b.wait_in_text("#aws-content", "Amazon S3 bucket") b.wait_in_text("#aws-content", "you") b.wait_in_text("#aws-content", "AWS region") b.wait_in_text("#aws-content", "up") # continue button changes to Finish when upload has valid fields b.wait_visible("button:contains('Finish'):enabled") # Close wizard b.click("button:contains('Cancel')") b.wait_not_present("#create-image-upload-wizard") # collect code coverage result self.check_coverage() def testAzureStep(self): b = self.browser self.login_and_go("/composer", superuser=True) b.wait_visible("#main") # create image wizard b.click("li[data-blueprint=httpd-server] #create-image-button") b.wait_js_cond('ph_select("#image-type option").length > 1') b.set_val("#image-type", "vhd") b.wait_val("#image-type", "vhd") # groups action done # still keep Create if upload image not selected b.wait_text("#continue-button", "Create") # default size = 6GB for azure vhd images b.wait_val("#image-size-input", 2) # check ? (Azure upload image help) button b.click("button[aria-label='Upload image help']") print(b.text(".pf-c-popover__body")) b.wait_in_text(".pf-c-popover__body", " Image Builder can upload images you create to a Blob container in " "Microsoft Azure. When the image build is complete and the upload action " "is successful, the image file is available in the Storage account and Blob " "container that you specified. ") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # continue button changes to Next when upload image selected b.click("#azure-checkbox") b.click("button:contains('Next')") # check and enter authentication fields b.wait_visible("label:contains('Storage account')") # check storage account help button b.click("button[aria-label='Storage account help']") b.wait_text(".pf-c-popover__body", "Provide the name of a storage account. You can find storage accounts on the " "Storage accounts page in the Azure portal.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter storage account value b.focus("input[id='storage-account-input']") b.key_press("never") b.wait_visible("label:contains('Storage access key')") # check storage access key help button b.click("button[aria-label='Storage access key help']") b.wait_text(".pf-c-popover__body", " Provide the access key for the desired storage account. You can find the " "access key on the Access keys page of the storage account. You can find " "storage accounts on the Storage accounts page in the Azure portal. ") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter storage access key value b.focus("input[id='storage-access-key-input']") b.key_press("gunna") # check and enter destination fields b.click("button:contains('Destination')") b.wait_visible("label:contains('Image name')") # check image name help button b.click("button[aria-label='Image name help']") b.wait_text(".pf-c-popover__body", "Provide a file name to be used for the image file that will be uploaded.") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter image name id value b.focus("input[id='image-name-input']") b.key_press("give") b.wait_visible("label:contains('Storage container')") # check amazon Storage container help button b.click("button[aria-label='Storage container help']") b.text(".pf-c-popover__body") b.wait_text(".pf-c-popover__body", " Provide the Blob container to which the image file will be uploaded. You can " "find containers under the Blob service section of a storage account. You can " "find storage accounts on the Storage accounts page in the Azure portal. ") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # enter storage container value b.focus("input[id='storage-container-input']") b.key_press("you-up") # Verify Azure Review page b.click("button:contains('Review')") # check ? (Azure upload image help) button b.click("button[aria-label='Azure help']") b.wait_in_text(".pf-c-popover__body", " Image Builder can upload images you create to a Blob container in " "Microsoft Azure. When the image build is complete and the upload action " "is successful, the image file is available in the Storage account and Blob " "container that you specified. ") b.click(".pf-c-popover__content button") b.wait_not_present(".pf-c-popover__body") # Check that the expected fields and values are present b.wait_in_text("#azure-content", "Storage account") b.wait_in_text("#azure-content", "*****") b.wait_in_text("#azure-content", "Storage access key") b.wait_in_text("#azure-content", "*****") b.wait_in_text("#azure-content", "Image name") b.wait_in_text("#azure-content", "give") b.wait_in_text("#azure-content", "Storage container") b.wait_in_text("#azure-content", "you-up") # continue button changes to Finish when upload has valid fields b.wait_visible("button:contains('Finish'):enabled") # Close wizard b.click("button:contains('Cancel')") b.wait_not_present("#create-image-upload-wizard") # collect code coverage result self.check_coverage() def testOpenStack(self): b = self.browser m = self.machine self.login_and_go("/composer", superuser=True) b.wait_visible("#main") # create image wizard b.click("li[data-blueprint=httpd-server] #create-image-button") b.wait_text("#create-image-upload-wizard #blueprint-name", "httpd-server") b.wait_js_cond('ph_select("#image-type option").length > 1') b.set_val("#image-type", "openstack") b.wait_val("#image-type", "openstack") # group actions end b.focus("#image-size-input") # delete 2 and input 4 b.key_press("\b") b.key_press("4") b.click("#continue-button") b.wait_not_present("#create-image-upload-wizard") # toast notification b.wait_visible("#cmpsr-toast-imageWaiting .pficon-info") b.click("#cmpsr-toast-imageWaiting .pficon-close") b.wait_not_present("#cmpsr-toast-imageWaiting .pficon-info") # got to images tab b.click("#httpd-server-name") # correct image name and type with b.wait_timeout(300): b.click("#blueprint-tabs-tab-images") b.wait_visible("ul[data-list=images]") # get uuid as part of css selector uuid = m.execute(""" composer-cli compose list | grep httpd-server | awk '{print $1}' | head -1 """).rstrip() selector = "{}-compose-name".format(uuid) # NOTE work around a bug in osbuild-composer-11, which got the # capitalization wrong. This can be changed to `openstack` once we # depend on a newer version of osbuild-composer. image_type = b.attr("li[aria-labelledby={}] [data-image-type]".format(selector), "data-image-type") self.assertEqual(image_type.lower(), "openstack") # image building needs more time with b.wait_timeout(1800): b.wait_text("li[aria-labelledby={}] [data-status=true]".format(selector), "Image build complete") # image size should be 4GB b.wait_in_text("li[aria-labelledby={}] ".format(selector), "4 GB") # get image size from backend image_size = m.execute( "composer-cli compose info {} | head -1 | awk '{{print $6}}'".format(uuid) ).rstrip() self.assertEqual(int(image_size), 4 * 1024 * 1024 * 1024) # log should contains rpm, hostname, users stages and tar assembler b.click("button:contains('Logs')") b.wait_in_text("#{}-logs".format(uuid), "Stage org.osbuild.rpm") b.wait_in_text("#{}-logs".format(uuid), "Stage: org.osbuild.hostname") b.wait_in_text("#{}-logs".format(uuid), "Stage: org.osbuild.users") b.wait_in_text("#{}-logs".format(uuid), "Assembler org.osbuild.qemu") # close logs b.click("button:contains('Logs')") # download image b.click("#{}-actions".format(uuid)) b.click("a:contains('Download')") # delete image cancel first always b.click("#{}-actions".format(uuid)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "true") b.click("li[aria-labelledby={}] a:contains('Delete')".format(selector)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "false") b.click("#cmpsr-modal-delete button:contains('Cancel')") b.wait_not_present("#cmpsr-modal-delete") # delete here b.click("#{}-actions".format(uuid)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "true") b.click("li[aria-labelledby={}] a:contains('Delete')".format(selector)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "false") b.click("#cmpsr-modal-delete button:contains('Delete image')") b.wait_not_present("#{}".format(selector)) # collect code coverage result self.check_coverage() @unittest.skipIf(os.environ.get("TEST_OS") == "fedora-31", "Does not support ostree image") def testOSTree(self): b = self.browser m = self.machine distro = os.environ.get("TEST_OS") if (distro == "fedora-32" or distro == "fedora-33"): image_type_ostree = "fedora-iot-commit" elif (distro == "rhel-8-3" or distro == "rhel-8-4"): image_type_ostree = "rhel-edge-commit" self.login_and_go("/composer", superuser=True) b.wait_visible("#main") # create image wizard b.click("li[data-blueprint=httpd-server] #create-image-button") b.wait_text("#create-image-upload-wizard #blueprint-name", "httpd-server") b.wait_js_cond('ph_select("#image-type option").length > 1') b.set_val("#image-type", image_type_ostree) b.wait_val("#image-type", image_type_ostree) b.click("#continue-button") b.wait_not_present("#create-image-upload-wizard") # toast notification b.wait_visible("#cmpsr-toast-imageWaiting .pficon-info") b.click("#cmpsr-toast-imageWaiting .pficon-close") b.wait_not_present("#cmpsr-toast-imageWaiting .pficon-info") # got to images tab b.click("#httpd-server-name") # correct image name and type with b.wait_timeout(300): b.click("#blueprint-tabs-tab-images") b.wait_visible("ul[data-list=images]") # get uuid as part of css selector uuid = m.execute(""" composer-cli compose list | grep httpd-server | awk '{print $1}' | head -1 """).rstrip() selector = "{}-compose-name".format(uuid) image_type = b.attr("li[aria-labelledby={}] [data-image-type]".format(selector), "data-image-type") self.assertEqual(image_type, image_type_ostree) # image building needs more time with b.wait_timeout(1800): b.wait_text("li[aria-labelledby={}] [data-status=true]".format(selector), "Image build complete") # log should contains rpm, hostname, users stages and tar assembler b.click("button:contains('Logs')") b.wait_in_text("#{}-logs".format(uuid), "Stage org.osbuild.rpm") b.wait_in_text("#{}-logs".format(uuid), "Stage: org.osbuild.rpm-ostree") b.wait_in_text("#{}-logs".format(uuid), "Assembler org.osbuild.ostree.commit") # close logs b.click("button:contains('Logs')") # download image b.click("#{}-actions".format(uuid)) b.click("a:contains('Download')") # delete image cancel first always b.click("#{}-actions".format(uuid)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "true") b.click("li[aria-labelledby={}] a:contains('Delete')".format(selector)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "false") b.click("#cmpsr-modal-delete button:contains('Cancel')") b.wait_not_present("#cmpsr-modal-delete") # delete here b.click("#{}-actions".format(uuid)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "true") b.click("li[aria-labelledby={}] a:contains('Delete')".format(selector)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "false") b.click("#cmpsr-modal-delete button:contains('Delete image')") b.wait_not_present("#{}".format(selector)) self.allow_journal_messages(".*avc: denied.*", ".*audit: .*seresult=denied .*") # collect code coverage result self.check_coverage() def testCancel(self): b = self.browser m = self.machine self.login_and_go("/composer", superuser=True) b.wait_visible("#main") # create image wizard b.click("li[data-blueprint=httpd-server] #create-image-button") b.wait_text("#create-image-upload-wizard #blueprint-name", "httpd-server") b.wait_js_cond('ph_select("#image-type option").length > 1') b.set_val("#image-type", "openstack") b.wait_val("#image-type", "openstack") # group actions end b.click("#continue-button") b.wait_not_present("#create-image-upload-wizard") # toast notification b.wait_visible("#cmpsr-toast-imageWaiting .pficon-info") b.click("#cmpsr-toast-imageWaiting .pficon-close") b.wait_not_present("#cmpsr-toast-imageWaiting .pficon-info") # got to images tab b.click("#httpd-server-name") # correct image name and type with b.wait_timeout(300): b.click("#blueprint-tabs-tab-images") b.wait_visible("ul[data-list=images]") # get uuid as part of css selector uuid = m.execute(""" composer-cli compose list | grep httpd-server | awk '{print $1}' | head -1 """).rstrip() selector = "{}-compose-name".format(uuid) # stop image build b.click("#{}-actions".format(uuid)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "true") b.click("li[aria-labelledby={}] a:contains('Stop')".format(selector)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "false") b.click("#cmpsr-modal-delete button:contains('Stop build')") b.wait_not_present("#cmpsr-modal-delete") # delete canceled image build b.click("#{}-actions".format(uuid)) b.wait_attr("#{}-actions".format(uuid), "aria-expanded", "true") b.click("li[aria-labelledby={}] a:contains('Remove')".format(selector)) b.wait_not_present("#{}".format(selector)) # collect code coverage result self.check_coverage() if __name__ == '__main__': testlib.test_main()