Skip to content

Commit

Permalink
ImageWizard: add gcp upload
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jkozol committed Jan 30, 2023
1 parent a6d9031 commit 510038b
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/components/Wizard/CreateImageWizard.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
vmwareDest,
ociAuth,
ociDest,
gcp,
ostreeSettings,
review,
} from "../../forms/steps";
Expand All @@ -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";

Expand Down Expand Up @@ -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,
},
};
}
}

Expand Down Expand Up @@ -208,6 +223,7 @@ const CreateImageWizard = (props) => {
awsDest(intl),
azureAuth(intl),
azureDest(intl),
gcp(intl),
ociAuth(intl),
ociDest(intl),
vmwareAuth(intl),
Expand Down Expand Up @@ -244,6 +260,7 @@ const CreateImageWizard = (props) => {
blueprintNames: blueprintNames,
},
"blueprint-listener": BlueprintListenerWrapper,
"upload-file": UploadFile,
}}
onCancel={handleClose}
/>
Expand Down
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
144 changes: 144 additions & 0 deletions src/forms/steps/gcp.js
Original file line number Diff line number Diff line change
@@ -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: <FormattedMessage defaultMessage="GCP" />,
name: "gcp",
nextStep: "review",
fields: [
{
component: "text-field-custom",
name: "image.upload.image_name",
className: "pf-u-w-50",
type: "text",
label: <FormattedMessage defaultMessage="Image name" />,
labelIcon: (
<Popover
bodyContent={intl.formatMessage(messages.imageNamePopoverBody)}
aria-label={intl.formatMessage(messages.imageNamePopoverAria)}
>
<Button
variant="plain"
aria-label={intl.formatMessage(messages.imageNamePopoverAria)}
>
<HelpIcon />
</Button>
</Popover>
),
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: <FormattedMessage defaultMessage="Storage region" />,
labelIcon: (
<Popover
bodyContent={intl.formatMessage(messages.regionPopoverBody)}
aria-label={intl.formatMessage(messages.regionPopoverAria)}
>
<Button variant="plain">
<HelpIcon />
</Button>
</Popover>
),
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
},
{
component: "text-field-custom",
name: "image.upload.settings.bucket",
className: "pf-u-w-50",
type: "text",
label: <FormattedMessage defaultMessage="Bucket" />,
labelIcon: (
<Popover
bodyContent={intl.formatMessage(messages.bucketPopoverBody)}
aria-label={intl.formatMessage(messages.bucketPopoverAria)}
>
<Button variant="plain">
<HelpIcon />
</Button>
</Popover>
),
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
},
{
component: "upload-file",
name: "image.upload.settings.credentials",
className: "pf-u-w-50",
type: "text",
label: <FormattedMessage defaultMessage="Credentials" />,
labelIcon: (
<Popover
bodyContent={intl.formatMessage(messages.credentialsPopoverBody)}
aria-label={intl.formatMessage(messages.credentialsPopoverAria)}
>
<Button variant="plain">
<HelpIcon />
</Button>
</Popover>
),
isRequired: true,
validate: [
{
type: validatorTypes.REQUIRED,
},
],
},
],
};
};

export default gcp;
9 changes: 9 additions & 0 deletions src/forms/steps/imageOutput.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,15 @@ const imageOutput = (intl) => {
is: "vhd",
},
},
{
name: "image.isUpload",
component: componentTypes.CHECKBOX,
label: <FormattedMessage defaultMessage="Upload to GCP" />,
condition: {
when: "image.type",
is: "gce",
},
},
{
name: "image.isUpload",
component: componentTypes.CHECKBOX,
Expand Down
2 changes: 2 additions & 0 deletions src/forms/steps/imageOutputStepMapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export default (props) => {
return "azure-auth";
case "vmdk":
return "vmware-auth";
case "gce":
return "gcp";
default:
return "review";
}
Expand Down
1 change: 1 addition & 0 deletions src/forms/steps/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
26 changes: 26 additions & 0 deletions test/verify/check-imageWizard
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import time
import composerlib
import testlib
import unittest
import os


@testlib.nondestructive
Expand Down Expand Up @@ -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()
13 changes: 13 additions & 0 deletions translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -58,6 +60,7 @@
"GsBRWL": "Languages",
"HAlOn1": "Name",
"HIQslE": "Image output",
"HLZ9CC": "Credentials",
"I3b4hn": "Version",
"IHusTg": "Firstboot URL",
"IMpl6/": "Secret access key",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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.",
Expand Down

0 comments on commit 510038b

Please sign in to comment.