wizard: add GCP image sharing options
Adding GCP image sharing option according to the discussion on slack(https://redhat-internal.slack.com/archives/C03AZ0264LW/p1692789579814619) and mocks(https://issues.redhat.com/browse/HMS-2352). In summary, within our GCP sources, we store the project ID. Images cannot be shared directly with a project ID, but they can be shared with a service account or a Google account. Consequently, to launch instances in GCP, users are not required to provide their Google account; instead, the images should be shared with the provisioning service account. This ensures that the provisioning team has access to the necessary images, as sharing them with individual users would not allow that. After a thorough discussion, we have collectively decided to introduce an option. This option allows users to exclusively utilize the Launch service without the need to share the image with a Google account.
This commit is contained in:
parent
50b76751e7
commit
a890dc5666
5 changed files with 103 additions and 41 deletions
|
|
@ -122,20 +122,22 @@ const onSave = (values) => {
|
|||
|
||||
if (values['target-environment']?.gcp) {
|
||||
let share = '';
|
||||
switch (values['google-account-type']) {
|
||||
case 'googleAccount':
|
||||
share = `user:${values['google-email']}`;
|
||||
break;
|
||||
case 'serviceAccount':
|
||||
share = `serviceAccount:${values['google-email']}`;
|
||||
break;
|
||||
case 'googleGroup':
|
||||
share = `group:${values['google-email']}`;
|
||||
break;
|
||||
case 'domain':
|
||||
share = `domain:${values['google-domain']}`;
|
||||
break;
|
||||
// no default
|
||||
if (values['image_sharing'] === 'gcp-account') {
|
||||
switch (values['google-account-type']) {
|
||||
case 'googleAccount':
|
||||
share = `user:${values['google-email']}`;
|
||||
break;
|
||||
case 'serviceAccount':
|
||||
share = `serviceAccount:${values['google-email']}`;
|
||||
break;
|
||||
case 'googleGroup':
|
||||
share = `group:${values['google-email']}`;
|
||||
break;
|
||||
case 'domain':
|
||||
share = `domain:${values['google-domain']}`;
|
||||
break;
|
||||
// no default
|
||||
}
|
||||
}
|
||||
|
||||
const request = {
|
||||
|
|
@ -148,15 +150,16 @@ const onSave = (values) => {
|
|||
image_type: 'gcp',
|
||||
upload_request: {
|
||||
type: 'gcp',
|
||||
options: {
|
||||
share_with_accounts: [share],
|
||||
},
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
customizations,
|
||||
};
|
||||
|
||||
if (share !== '') {
|
||||
request.options = [share];
|
||||
}
|
||||
requests.push(request);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,20 +102,48 @@ const googleCloudStep = {
|
|||
name: 'google-cloud-text-component',
|
||||
label: (
|
||||
<p>
|
||||
Your image will be uploaded to GCP and shared with the account you
|
||||
provide below.
|
||||
Select how to share your image. The image you create can be used to
|
||||
launch instances on GCP, regardless of which method you select.
|
||||
</p>
|
||||
),
|
||||
},
|
||||
{
|
||||
component: componentTypes.PLAIN_TEXT,
|
||||
name: 'google-cloud-text-component',
|
||||
label: (
|
||||
<p>
|
||||
<b>The shared image will expire within 14 days.</b> To permanently
|
||||
access the image, copy it to your Google Cloud Platform account.
|
||||
</p>
|
||||
),
|
||||
component: componentTypes.RADIO,
|
||||
label: 'Select image sharing',
|
||||
isRequired: true,
|
||||
name: 'image-sharing',
|
||||
initialValue: 'gcp-account',
|
||||
autoFocus: true,
|
||||
options: [
|
||||
{
|
||||
label: 'Share image with a Google account',
|
||||
'data-testid': 'account-sharing',
|
||||
autoFocus: true,
|
||||
description: (
|
||||
<p>
|
||||
Your image will be uploaded to GCP and shared with the account you
|
||||
provide below.
|
||||
<b>The image expires in 14 days.</b> To keep permanent access to
|
||||
your image, copy it to your GCP project.
|
||||
</p>
|
||||
),
|
||||
value: 'gcp-account',
|
||||
},
|
||||
{
|
||||
label: 'Share image with Red Hat Insights only',
|
||||
'data-testid': 'image-sharing',
|
||||
description: (
|
||||
<p>
|
||||
Your image will be uploaded to GCP and shared with Red Hat
|
||||
Insights.
|
||||
<b> The image expires in 14 days.</b> You cannot access or
|
||||
recreate this image in your GCP project.
|
||||
</p>
|
||||
),
|
||||
value: 'insights',
|
||||
autoFocus: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
component: 'radio-popover',
|
||||
|
|
@ -137,6 +165,10 @@ const googleCloudStep = {
|
|||
type: validatorTypes.REQUIRED,
|
||||
},
|
||||
],
|
||||
condition: {
|
||||
when: 'image-sharing',
|
||||
is: 'gcp-account',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: componentTypes.TEXT_FIELD,
|
||||
|
|
@ -145,11 +177,16 @@ const googleCloudStep = {
|
|||
type: 'text',
|
||||
label: 'Principal (e.g. e-mail address)',
|
||||
condition: {
|
||||
or: [
|
||||
{ when: 'google-account-type', is: 'googleAccount' },
|
||||
{ when: 'google-account-type', is: 'serviceAccount' },
|
||||
{ when: 'google-account-type', is: 'googleGroup' },
|
||||
{ when: 'google-account-type', is: null },
|
||||
and: [
|
||||
{ when: 'image-sharing', is: 'gcp-account' },
|
||||
{
|
||||
or: [
|
||||
{ when: 'google-account-type', is: 'googleAccount' },
|
||||
{ when: 'google-account-type', is: 'serviceAccount' },
|
||||
{ when: 'google-account-type', is: 'googleGroup' },
|
||||
{ when: 'google-account-type', is: null },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
isRequired: true,
|
||||
|
|
@ -170,8 +207,10 @@ const googleCloudStep = {
|
|||
type: 'text',
|
||||
label: 'Domain',
|
||||
condition: {
|
||||
when: 'google-account-type',
|
||||
is: 'domain',
|
||||
and: [
|
||||
{ when: 'image-sharing', is: 'gcp-account' },
|
||||
{ when: 'google-account-type', is: 'domain' },
|
||||
],
|
||||
},
|
||||
isRequired: true,
|
||||
validate: [
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
export const isGcpUploadRequestOptions = (
|
||||
options: UploadRequest['options']
|
||||
): options is GcpUploadRequestOptions => {
|
||||
return (options as GcpUploadRequestOptions).share_with_accounts !== undefined;
|
||||
return true;
|
||||
};
|
||||
|
||||
export const isAwsUploadRequestOptions = (
|
||||
|
|
|
|||
|
|
@ -458,7 +458,14 @@ describe('Step Upload to Google', () => {
|
|||
test('clicking Next loads Registration', async () => {
|
||||
await setUp();
|
||||
|
||||
await user.type(screen.getByTestId('input-google-email'), 'test@test.com');
|
||||
const shareRadioButton = await screen.findByRole('radio', {
|
||||
name: /share image with a google account/i,
|
||||
});
|
||||
await user.click(shareRadioButton);
|
||||
|
||||
const googleEmailInput = await screen.findByTestId('input-google-email');
|
||||
|
||||
await user.type(googleEmailInput, 'test@test.com');
|
||||
await clickNext();
|
||||
|
||||
await screen.findByRole('textbox', {
|
||||
|
|
@ -485,15 +492,22 @@ describe('Step Upload to Google', () => {
|
|||
test('the google account id field is shown and required', async () => {
|
||||
await setUp();
|
||||
|
||||
const accessKeyId = screen.getByTestId('input-google-email');
|
||||
await waitFor(() => {
|
||||
screen.getByTestId('account-sharing');
|
||||
});
|
||||
|
||||
user.click(screen.getByTestId('account-sharing'));
|
||||
const accessKeyId = await screen.findByTestId('input-google-email');
|
||||
expect(accessKeyId).toHaveValue('');
|
||||
expect(accessKeyId).toBeEnabled();
|
||||
|
||||
// expect(accessKeyId).toBeRequired(); // DDf does not support required value
|
||||
});
|
||||
|
||||
test('the google email field must be a valid email', async () => {
|
||||
await setUp();
|
||||
|
||||
await user.click(screen.getByTestId('account-sharing'));
|
||||
await user.type(screen.getByTestId('input-google-email'), 'a');
|
||||
expect(await getNextButton()).toHaveClass('pf-m-disabled');
|
||||
expect(await getNextButton()).toBeDisabled();
|
||||
|
|
@ -1033,7 +1047,11 @@ describe('Click through all steps', () => {
|
|||
await user.type(screen.getByTestId('aws-account-id'), '012345678901');
|
||||
await clickNext();
|
||||
|
||||
await user.click(screen.getByTestId('account-sharing'));
|
||||
|
||||
await user.type(screen.getByTestId('input-google-email'), 'test@test.com');
|
||||
|
||||
await user.click(await screen.findByTestId('image-sharing'));
|
||||
await clickNext();
|
||||
|
||||
await user.click(screen.getByTestId('azure-radio-manual'));
|
||||
|
|
@ -1391,10 +1409,8 @@ describe('Keyboard accessibility', () => {
|
|||
await clickNext();
|
||||
|
||||
// Target environment google
|
||||
const googleAccountRadio = screen.getByRole('radio', {
|
||||
name: /google account/i,
|
||||
});
|
||||
expect(googleAccountRadio).toHaveFocus();
|
||||
await user.click(screen.getByTestId('account-sharing'));
|
||||
expect(screen.getByTestId('account-sharing')).toHaveFocus();
|
||||
await user.type(screen.getByTestId('input-google-email'), 'test@test.com');
|
||||
await clickNext();
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ failOnConsole({
|
|||
) ||
|
||||
errorMessage.includes(
|
||||
"Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application."
|
||||
) ||
|
||||
// [2023-09] Suppresses an error that occurs on the GCP step of the Wizard.
|
||||
errorMessage.includes(
|
||||
'Warning: Cannot update a component (`ForwardRef(Field)`) while rendering a different component (`Radio`). To locate the bad setState() call inside `Radio`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render'
|
||||
))
|
||||
) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue