From 510038b9729780df0068e7d9062d5586a1441702 Mon Sep 17 00:00:00 2001 From: jkozol Date: Tue, 24 Jan 2023 17:09:58 +0100 Subject: [PATCH] ImageWizard: add gcp upload GCE images can now be created and uploaded to gcp. This adds new upload fields for the Storage region, Bucket, Object key, and Credentials. The credentials should be a file upload. This is all only on RHEL. --- src/components/Wizard/CreateImageWizard.js | 17 +++ src/constants.js | 1 + src/forms/steps/gcp.js | 144 +++++++++++++++++++++ src/forms/steps/imageOutput.js | 9 ++ src/forms/steps/imageOutputStepMapper.js | 2 + src/forms/steps/index.js | 1 + test/verify/check-imageWizard | 26 ++++ translations/en.json | 13 ++ 8 files changed, 213 insertions(+) create mode 100644 src/forms/steps/gcp.js diff --git a/src/components/Wizard/CreateImageWizard.js b/src/components/Wizard/CreateImageWizard.js index 6d9776179..66a09e683 100644 --- a/src/components/Wizard/CreateImageWizard.js +++ b/src/components/Wizard/CreateImageWizard.js @@ -14,6 +14,7 @@ import { vmwareDest, ociAuth, ociDest, + gcp, ostreeSettings, review, } from "../../forms/steps"; @@ -33,6 +34,7 @@ import Packages from "../../forms/components/Packages"; import Review from "../../forms/components/Review"; import TextFieldCustom from "../../forms/components/TextFieldCustom"; import UploadOCIFile from "../../forms/components/UploadOCIFile"; +import UploadFile from "../../forms/components/UploadFile"; import BlueprintSelect from "../../forms/components/BlueprintSelect"; import { FormSpy, useFormApi } from "@data-driven-forms/react-form-renderer"; @@ -142,6 +144,19 @@ const CreateImageWizard = (props) => { tenancy: formValues.image.upload.settings.tenancy, }, }; + } else if (formValues?.image?.type === "gce") { + const credentialsEncoded = window.btoa( + formValues.image.upload.settings.credentials + ); + uploadSettings = { + image_name: formValues.image.upload.image_name, + provider: "gcp", + settings: { + region: formValues.image.upload.settings.region, + bucket: formValues.image.upload.settings.bucket, + credentials: credentialsEncoded, + }, + }; } } @@ -208,6 +223,7 @@ const CreateImageWizard = (props) => { awsDest(intl), azureAuth(intl), azureDest(intl), + gcp(intl), ociAuth(intl), ociDest(intl), vmwareAuth(intl), @@ -244,6 +260,7 @@ const CreateImageWizard = (props) => { blueprintNames: blueprintNames, }, "blueprint-listener": BlueprintListenerWrapper, + "upload-file": UploadFile, }} onCancel={handleClose} /> diff --git a/src/constants.js b/src/constants.js index 6d414d96f..214215423 100644 --- a/src/constants.js +++ b/src/constants.js @@ -21,6 +21,7 @@ export const ImageTypeLabels = { "edge-simplified-installer": "RHEL for Edge Simplified Installer (.iso)", vhd: "Microsoft Azure (.vhd)", vmdk: "VMWare VSphere (.vmdk)", + gce: "Google Cloud Platform (.tar.gz)", }; export const UNIT_KIB = 1024 ** 1; diff --git a/src/forms/steps/gcp.js b/src/forms/steps/gcp.js new file mode 100644 index 000000000..3f0f57172 --- /dev/null +++ b/src/forms/steps/gcp.js @@ -0,0 +1,144 @@ +import React from "react"; +import { defineMessages, FormattedMessage } from "react-intl"; +import validatorTypes from "@data-driven-forms/react-form-renderer/validator-types"; +import { Popover, Button } from "@patternfly/react-core"; +import { HelpIcon } from "@patternfly/react-icons"; + +const messages = defineMessages({ + imageNamePopoverBody: { + defaultMessage: + "Provide a file name to be used for the image file that will be uploaded.", + }, + imageNamePopoverAria: { + defaultMessage: "Image name help", + }, + bucketPopoverBody: { + defaultMessage: + "Provide the name of the bucket where the image will be uploaded. This bucket must already exist.", + }, + bucketPopoverAria: { + defaultMessage: "Bucket help", + }, + regionPopoverBody: { + defaultMessage: + "Provide the region where the bucket is located. This region can be a regular Google storage region, but also a dual or multi region.", + }, + regionPopoverAria: { + defaultMessage: "Region help", + }, + credentialsPopoverBody: { + defaultMessage: + "The credentials file is a JSON file downloaded from GCP. The credentials are used to determine the GCP project to upload the image to.", + }, + credentialsPopoverAria: { + defaultMessage: "Credentials help", + }, +}); + +const gcp = (intl) => { + return { + title: , + name: "gcp", + nextStep: "review", + fields: [ + { + component: "text-field-custom", + name: "image.upload.image_name", + className: "pf-u-w-50", + type: "text", + label: , + labelIcon: ( + + + + ), + isRequired: true, + autoFocus: true, + validate: [ + { + type: validatorTypes.REQUIRED, + }, + ], + }, + { + component: "text-field-custom", + name: "image.upload.settings.region", + className: "pf-u-w-50", + type: "text", + label: , + labelIcon: ( + + + + ), + isRequired: true, + validate: [ + { + type: validatorTypes.REQUIRED, + }, + ], + }, + { + component: "text-field-custom", + name: "image.upload.settings.bucket", + className: "pf-u-w-50", + type: "text", + label: , + labelIcon: ( + + + + ), + isRequired: true, + validate: [ + { + type: validatorTypes.REQUIRED, + }, + ], + }, + { + component: "upload-file", + name: "image.upload.settings.credentials", + className: "pf-u-w-50", + type: "text", + label: , + labelIcon: ( + + + + ), + isRequired: true, + validate: [ + { + type: validatorTypes.REQUIRED, + }, + ], + }, + ], + }; +}; + +export default gcp; diff --git a/src/forms/steps/imageOutput.js b/src/forms/steps/imageOutput.js index b9e3e43c5..8607ef1fb 100644 --- a/src/forms/steps/imageOutput.js +++ b/src/forms/steps/imageOutput.js @@ -222,6 +222,15 @@ const imageOutput = (intl) => { is: "vhd", }, }, + { + name: "image.isUpload", + component: componentTypes.CHECKBOX, + label: , + condition: { + when: "image.type", + is: "gce", + }, + }, { name: "image.isUpload", component: componentTypes.CHECKBOX, diff --git a/src/forms/steps/imageOutputStepMapper.js b/src/forms/steps/imageOutputStepMapper.js index aa742415a..a8213ada9 100644 --- a/src/forms/steps/imageOutputStepMapper.js +++ b/src/forms/steps/imageOutputStepMapper.js @@ -9,6 +9,8 @@ export default (props) => { return "azure-auth"; case "vmdk": return "vmware-auth"; + case "gce": + return "gcp"; default: return "review"; } diff --git a/src/forms/steps/index.js b/src/forms/steps/index.js index b09997807..46a30bfa0 100644 --- a/src/forms/steps/index.js +++ b/src/forms/steps/index.js @@ -24,3 +24,4 @@ export { default as locale } from "./locale"; export { default as other } from "./other"; export { default as openscap } from "./openscap"; export { default as ignition } from "./ignition"; +export { default as gcp } from "./gcp"; diff --git a/test/verify/check-imageWizard b/test/verify/check-imageWizard index 965e51b3c..e31ffa3d8 100755 --- a/test/verify/check-imageWizard +++ b/test/verify/check-imageWizard @@ -3,6 +3,8 @@ import time import composerlib import testlib +import unittest +import os @testlib.nondestructive @@ -33,6 +35,30 @@ class TestImageWizard(composerlib.ComposerCase): # Create image b.click("footer button:contains('Create')") + @unittest.skipIf(os.environ.get("TEST_OS").split('-')[0] != "rhel", "Skipping test for non RHEL") + def testUploadGCPFields(self): + b = self.browser + + self.login_and_go("/composer", superuser=True) + b.wait_visible("#main") + + # Create blueprint + b.click("tr[data-testid=httpd-server] button[aria-label='Create image']") + b.wait_in_text(".pf-c-wizard__main", "httpd-server") + time.sleep(1) + # select qcow2 image type and keep default size + b.select_PF4("#image-output-select-toggle", "Google Cloud Platform (.gce)") + b.click("input[id='image.isUpload']") + b.click("button:contains('Next')") + + b.set_input_text("input[id='image.upload.image_name']", "testImageName") + b.set_input_text("input[id='image.upload.settings.region']", "testStorageName") + b.set_input_text("input[id='image.upload.settings.bucket']", "testBucket") + b.wait_in_text(".pf-c-wizard__main-body", "Credentials") + + # Cancel upload + b.click("footer button:contains('Cancel')") + if __name__ == '__main__': testlib.test_main() diff --git a/translations/en.json b/translations/en.json index cf1d2c6bc..bb0b7847f 100644 --- a/translations/en.json +++ b/translations/en.json @@ -3,6 +3,7 @@ "+YJ/Sw": "Append", "+ubJEs": "Manufacturing server URL", "/9I/o9": "Add partition", + "/Ktg9J": "GCP", "/VnDMl": "Other", "/f2U0m": "{blueprint} Image creation is {queue}.", "/nwCxv": "File system configurations", @@ -34,6 +35,7 @@ "9+Ddtu": "Next", "96NKhh": "Add zone", "9OVLWd": "Filesystem info", + "9RX5kq": "Provide a file name to be used for the image file that will be uploaded.", "9XUYQt": "Import", "9fE5XX": "No root partition configured.", "9llaed": "Download logs", @@ -58,6 +60,7 @@ "GsBRWL": "Languages", "HAlOn1": "Name", "HIQslE": "Image output", + "HLZ9CC": "Credentials", "I3b4hn": "Version", "IHusTg": "Firstboot URL", "IMpl6/": "Secret access key", @@ -78,6 +81,7 @@ "M8FAMU": "Manually configure partitions", "MKV/jm": "Image Builder may extend this size based on requirements, selected packages, and configurations.", "N26JOi": "{blueprint} Image creation has been added to the {queue}.", + "N9tOr0": "Bucket help", "NNkgEU": "Source names cannot contain spaces.", "NOOrvy": "Zone sources", "ORGv1Q": "Created", @@ -96,6 +100,7 @@ "RcxFT1": "Locale", "Ry6SwO": "Disabled Services", "SNzHd8": "Remove key", + "Sb1B69": "Storage region", "TADV5r": "Are you sure you want to stop the image build?", "TLiG9d": "NTP Servers", "TP/O/b": "Remove user", @@ -109,20 +114,24 @@ "Ucrl50": "Disabled services", "V52jNn": "Enabled", "VC1pOq": "Add locale", + "VH3Xt/": "Bucket", "VcpIj8": "No blueprints", "Vp6WM3": "Mount point", "VzWTJu": "Blueprints", "VzzYJk": "Create", "WWUaVx": "Enter kernel name.", "WyriYH": "Storage container", + "Wz9Z1Z": "Credentials help", "XZCsHB": "Upload to VMWare", "Xe56d/": "Bucket region", + "XsPqPW": "Provide the region where the bucket is located. This region can be a regular Google storage region, but also a dual or multi region.", "XyfAvU": "Mirrorlist", "YDMrKK": "Users", "YIf1rr": "Image ID", "YMk8tG": "Enter valid device node such as /dev/sda1. Only used for the simplified-installer image type.", "YeKWbP": "Authentication", "ZuzLh4": "Blueprints table", + "ZvnTwM": "Provide the name of the bucket where the image will be uploaded. This bucket must already exist.", "aQ7PKk": "Manufacturing Server URL", "agOXPD": "Size", "ax75cZ": "Minimum size", @@ -139,10 +148,12 @@ "eALudX": "Add services", "eKEL/g": "Pending", "f3funQ": "Installation device", + "f7FU1f": "Image name help", "fE1QI4": "User OCID", "fhwTpA": "Sources", "gL86bv": "Group name", "gP2Vek": "Enter values", + "gQL2CA": "Region help", "guBlw5": "NTP servers", "hpiPhh": "Enter kernel commandline arguments.", "hzmswI": "Groups", @@ -156,6 +167,7 @@ "kPQ+0U": "A source with this name already exists.", "kkxJBT": "Create image", "l8uwaa": "Configure partitions", + "lX0x2F": "The credentials file is a JSON file downloaded from GCP. The credentials are used to determine the GCP project to upload the image to.", "leet9B": "Yum repository", "m+E44N": "No packages selected", "mA1RDm": "Edit source", @@ -187,6 +199,7 @@ "tthToS": "Disabled", "tzMNF3": "Status", "uPDgth": "Hostname", + "uRdfZs": "Upload to GCP", "vAPLDS": "Toggle which package list to show", "vXCeIi": "Failed", "vr2saa": "{blueprint} Image creation failed.",