Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(deploy/blockDevices): Adds a Volume section in the Deploy wizard #8911

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b9107e4
feat(deploy/blockDevices): Add a Volume section in the Deploy Wizzard…
jorgebee65 Feb 11, 2021
d566588
Merge branch 'master' of https://github.com/spinnaker/deck into block…
jorgebee65 Feb 11, 2021
9c209f6
feat(blockDevice/tags): add other volumeTypes
jorgebee65 Mar 2, 2021
bfce1d8
Merge branch 'master' into blockDeviceTags
jorgebee65 Mar 9, 2021
74f92ce
Merge branch 'master' into blockDeviceTags
jorgebee65 Mar 15, 2021
aedb62c
feat(blockDevice/tags): rename attribute to blockDeviceTags
jorgebee65 Mar 16, 2021
e111619
feat(blockDevice/tags): fix imports in AmazonCloneServerGroupModal.tsx
jorgebee65 Mar 16, 2021
f93f476
feat(blockDevice/tags): fix imports in AmazonCloneServerGroupModal.tsx
jorgebee65 Mar 16, 2021
8e47e4b
Merge branch 'master' into blockDeviceTags
jorgebee65 Mar 19, 2021
8527537
Merge branch 'master' into blockDeviceTags
jorgebee65 Mar 29, 2021
e8239c9
Merge branch 'master' into blockDeviceTags
rvazquezglez Mar 29, 2021
fa4b76e
Merge branch 'master' into blockDeviceTags
jorgebee65 Apr 6, 2021
63c2fe9
Merge branch 'master' into blockDeviceTags
zachsmith1 May 25, 2021
c68a2ee
Merge branch 'master' into blockDeviceTags
jorgebee65 Jun 1, 2021
232664f
Merge branch 'master' into blockDeviceTags
jorgebee65 Jun 1, 2021
7cecd63
Merge branch 'master' into blockDeviceTags
jorgebee65 Jun 1, 2021
709ccb5
Update app/scripts/modules/amazon/src/serverGroup/configure/serverGro…
jorgebee65 Jun 1, 2021
e6944aa
Merge branch 'master' into blockDeviceTags
zachsmith1 Jun 14, 2021
5d3b65b
Merge branch 'master' into blockDeviceTags
zachsmith1 Jun 21, 2021
2f05a95
refactor(amazon/volumes): Refactoring the code implementing FormikFor…
jorgebee65 Jul 13, 2021
b1d5ca3
refactor(amazon/volumes): Make some fields required and other optiona…
jorgebee65 Jul 13, 2021
41d5cc3
Merge branch 'master' into blockDeviceTags
jorgebee65 Jul 13, 2021
af52e1f
Merge branch 'master' into blockDeviceTags
jorgebee65 Jul 19, 2021
89ee718
Merge branch 'master' into blockDeviceTags
jorgebee65 Jul 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/amazon/src/help/amazon.help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ const helpContents: { [key: string]: string } = {
'aws.securityGroup.name':
'<p>The {{firewall}} name is formed by combining the application name, the <b>Stack</b> field, and the <b>Detail</b> field.</p>',
'aws.securityGroup.cross.account.ingress.help': '<p>Accounts that are excluded will not show up in this list</p>',
'aws.securityGroup.volume.devicename': 'The device name (for example, /dev/sdh or xvdh).',
'aws.securityGroup.volume.size': 'Must be an integer and represents the amount in GiB',
'aws.securityGroup.volume.type':'Amazon EBS Volume Type',
'aws.securityGroup.volume.iops':'Maximum number of input/output operations per second (IOPS) that the volume should provide. You can specify IOPS only for gp3, io1, and io2 volumes.',
'aws.securityGroup.volume.encrypted':'If the instance type supports EBS encryption and you want to encrypt the volume',
'aws.securityGroup.volume.snapshot':'Type the ID of the snapshot from which you are restoring the volume',
'aws.securityGroup.volume.delete.on.termination':'Meaning the volume will automatically be deleted when you destroy the instance.',
'aws.securityGroup.volume.virtual.name':'The virtual device name (ephemeralN). The name must be in the form ephemeralX where X is a number starting from zero (0). For example, an instance type with 2 available instance store volumes can specify mappings for ephemeral0 and ephemeral1. The number of available instance store volumes depends on the instance type. After you connect to the instance, you must mount the volume.',
'aws.scalingPolicy.search.restricted': `<p>Resets dimensions to "AutoScalingGroupName: {name of the ASG}" and provides
a simpler, combined input for the namespace and metric name fields.</p>`,
'aws.scalingPolicy.search.all': `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,27 @@ export interface IAmazonServerGroupCommandViewState extends IServerGroupCommandV
spelLoadBalancers: string[];
}

export interface IBlockDevicesCommand {
blockDevices: IServerGroupCommandBlockDevice[];
}

export interface IServerGroupCommandBlockDevice {
deleteOnTermination: boolean;
deviceName: string;
size: number;
volumeType: string;
virtualName: string;
iops: number;
snapshotId: string;
encrypted: boolean;
}

export interface IVolumeListenerAction {
order?: number;
volumeActionConfig?: IServerGroupCommandBlockDevice; // writes
volumeConfig?: IServerGroupCommandBlockDevice; // reads
}

export interface IAmazonServerGroupCommand extends IServerGroupCommand {
associateIPv6Address?: boolean;
associatePublicIpAddress: boolean;
Expand All @@ -102,6 +123,8 @@ export interface IAmazonServerGroupCommand extends IServerGroupCommand {
unlimitedCpuCredits?: boolean;
capacityRebalance?: boolean;
viewState: IAmazonServerGroupCommandViewState;
blockDevices: IServerGroupCommandBlockDevice[];
blockDeviceTags: string[];

getBlockDeviceMappingsSource: (command: IServerGroupCommand) => IBlockDeviceMappingSource;
selectBlockDeviceMappingsSource: (command: IServerGroupCommand, selection: string) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
ServerGroupInstanceType,
ServerGroupLoadBalancers,
ServerGroupSecurityGroups,
ServerGroupVolumes,
ServerGroupZones,
} from './pages';
import { AwsReactInjector } from '../../../reactShims';
Expand Down Expand Up @@ -245,6 +246,12 @@ export class AmazonCloneServerGroupModal extends React.Component<
<ServerGroupAdvancedSettings ref={innerRef} formik={formik} app={application} />
)}
/>
<WizardPage
label="Volumes"
wizard={wizard}
order={nextIdx()}
render={({ innerRef }) => <ServerGroupVolumes ref={innerRef} formik={formik} app={application} />}
/>
</>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './ServerGroupLoadBalancers';
export * from './ServerGroupSecurityGroups';
export * from './advancedSettings/ServerGroupAdvancedSettings';
export * from './advancedSettings/ServerGroupAdvancedSettingsCommon';
export * from './volumes/ServerGroupVolumes';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.configure-config-modal .StandardFieldLayout .sm-label-right {
min-width: 160px;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { Form } from 'formik';
import React from 'react';
import { Modal } from 'react-bootstrap';

import {
FormikFormField,
CheckboxInput,
HelpField,
ModalClose,
noop,
NumberInput,
ReactModal,
SelectInput,
SpinFormik,
SubmitButton,
TextInput,
} from '@spinnaker/core';
import { IServerGroupCommandBlockDevice } from '../../../serverGroupConfiguration.service';

import './ConfigureConfigModal.css';

export interface IConfigureVolumeConfigModalProps {
config: IServerGroupCommandBlockDevice;
closeModal?(result?: any): void; // provided by ReactModal
dismissModal?(rejection?: any): void; // provided by ReactModal
}

export class ConfigureVolumeConfigModal extends React.Component<IConfigureVolumeConfigModalProps> {
private initialValues: IServerGroupCommandBlockDevice;
private volumeTypes = ['gp2', 'gp3', 'st1', 'io1', 'io2', 'sc1'];

public static defaultProps: Partial<IConfigureVolumeConfigModalProps> = {
closeModal: noop,
dismissModal: noop,
};

public static show(props: IConfigureVolumeConfigModalProps): Promise<void> {
return ReactModal.show(ConfigureVolumeConfigModal, props);
}

constructor(props: IConfigureVolumeConfigModalProps) {
super(props);

const config = props.config || ({} as IServerGroupCommandBlockDevice);

this.initialValues = {
deviceName: config.deviceName || '/dev/sdh',
deleteOnTermination: config.deleteOnTermination,
size: config.size || 16,
volumeType: config.volumeType || 'gp2',
virtualName: config.virtualName || '',
iops: config.iops,
snapshotId: config.snapshotId || '',
encrypted: config.encrypted,
};
}

private close = (reason?: null): void => {
this.props.dismissModal(reason);
};

private submit = (data: IServerGroupCommandBlockDevice): void => {
this.props.closeModal(data);
};

public render() {
const submitLabel = 'Save Device';

return (
<div className="configure-config-modal">
<SpinFormik<IServerGroupCommandBlockDevice>
initialValues={this.initialValues}
onSubmit={this.submit}
render={({ isValid }) => (
<Form className="form-horizontal">
<ModalClose dismiss={this.close} />
<Modal.Header>
<Modal.Title>
Configure Volume
</Modal.Title>
</Modal.Header>

<Modal.Body>
<FormikFormField
name="deleteOnTermination"
label="Delete on Termination"
help={<HelpField id="aws.securityGroup.volume.delete.on.termination" />}
input={(props) => <CheckboxInput {...props} />} />

<FormikFormField
name="encrypted"
label="Encrypted"
help={<HelpField id="aws.securityGroup.volume.encrypted" />}
input={(props) => <CheckboxInput {...props} />} />

<FormikFormField
name="deviceName"
label="Device Name"
required={true}
input={(props) => <TextInput {...props} />}
help={<HelpField id="aws.securityGroup.volume.devicename" />}
/>

<FormikFormField
name="volumeType"
label="Volune Type"
required={true}
help={<HelpField id="aws.securityGroup.volume.type" />}
input={(props) => (
<SelectInput
options={this.volumeTypes}
{...props}
/>
)}
/>

<FormikFormField
name="virtualName"
label="Virtual Name"
required={false}
input={(props) => <TextInput {...props} />}
help={<HelpField id="aws.securityGroup.volume.virtual.name" />}
/>

<FormikFormField
name="snapshotId"
label="Snapshot Id"
required={false}
input={(props) => <TextInput {...props} />}
help={<HelpField id="aws.securityGroup.volume.snapshot" />}
/>

<FormikFormField
name="size"
label="Size"
required={true}
input={(props) => <NumberInput {...props} />}
help={<HelpField id="aws.securityGroup.volume.size" />}
/>

<FormikFormField
name="iops"
label="Iops"
required={false}
input={(props) => <NumberInput {...props} />}
help={<HelpField id="aws.securityGroup.volume.iops" />}
/>
</Modal.Body>

<Modal.Footer>
<button className="btn btn-default" onClick={this.close} type="button">
Cancel
</button>
<SubmitButton isDisabled={!isValid} submitting={false} isFormSubmit={true} label={submitLabel} />
</Modal.Footer>
</Form>
)}
/>
</div>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import { FormikProps } from 'formik';
import { VolumeEditor } from './VolumeEditor';

import { Application, IServerGroup, IWizardPageComponent, MapEditor, HelpField } from '@spinnaker/core';

import { IAmazonServerGroupCommand } from '../../../serverGroupConfiguration.service';
import { isNil } from 'lodash';

export interface IServerGroupVolumesProps {
app: Application;
formik: FormikProps<IAmazonServerGroupCommand>;
}

export interface IServerGroupVolumesState {
namePreview: string;
createsNewCluster: boolean;
latestServerGroup: IServerGroup;
}

export class ServerGroupVolumes
extends React.Component<IServerGroupVolumesProps, IServerGroupVolumesState>
implements IWizardPageComponent<IAmazonServerGroupCommand> {
private duplicateKeys = false;

public validate() {
const errors = {} as any;
if (this.duplicateKeys) {
errors.tags = 'Tags have duplicate keys.';
}
return errors;
}



private tagsChanged = (tags: { [key: string]: string }, duplicateKeys: boolean) => {
this.duplicateKeys = duplicateKeys;
this.props.formik.setFieldValue('blockDeviceTags', tags);
};

constructor(props: IServerGroupVolumesProps) {
super(props);
}

public render() {
const { values } = this.props.formik;
const { app } = this.props;
const blockTags = isNil(values.blockDeviceTags) ? [] : values.blockDeviceTags;

return (
<div className="container-fluid form-horizontal">
<div className="form-group">
<div className="sm-label-left">
<b>Volumes (optional)</b>
</div>
<VolumeEditor
app={app}
formik={this.props.formik}
/>
</div>
<div className="form-group">
<div className="sm-label-left">
<b>Tags (optional)</b>
<HelpField id="aws.serverGroup.blockDevice.tags" />
</div>
<MapEditor model={blockTags as any} allowEmpty={true} onChange={this.tagsChanged} />
</div>
Comment on lines +61 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use a FormikFormField and a MapEditorInput which would also make the validate and `tagsChanged functions unnecessary

</div>
);
}
}
Loading