#!/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()