@spinnaker/amazon 0.11.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/domain/IAmazonLaunchTemplate.d.ts +1 -1
  3. package/dist/domain/IAmazonServerGroup.d.ts +10 -0
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/instance/awsInstanceType.service.d.ts +10 -2
  7. package/dist/instance/awsInstanceTypes.d.ts +10 -0
  8. package/dist/instance/details/CostFactor.d.ts +6 -0
  9. package/dist/reactShims/aws.react.injector.d.ts +2 -1
  10. package/dist/search/searchResultFormatter.d.ts +2 -2
  11. package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +17 -2
  12. package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +39 -0
  13. package/dist/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.d.ts +3 -4
  14. package/dist/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.d.ts +9 -0
  15. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.d.ts +13 -0
  16. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.d.ts +9 -0
  17. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.d.ts +11 -0
  18. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.d.ts +14 -0
  19. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.d.ts +12 -0
  20. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.d.ts +15 -0
  21. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.d.ts +8 -0
  22. package/dist/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.d.ts +8 -0
  23. package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +9 -6
  24. package/dist/serverGroup/serverGroup.transformer.d.ts +2 -1
  25. package/package.json +5 -5
  26. package/src/domain/IAmazonLaunchTemplate.ts +1 -1
  27. package/src/domain/IAmazonServerGroup.ts +11 -0
  28. package/src/function/details/FunctionActions.spec.tsx +10 -9
  29. package/src/help/amazon.help.ts +9 -6
  30. package/src/image/image.reader.spec.ts +75 -0
  31. package/src/instance/awsInstanceType.service.spec.js +37 -71
  32. package/src/instance/awsInstanceType.service.ts +191 -0
  33. package/src/instance/awsInstanceTypes.ts +311 -0
  34. package/src/instance/details/CostFactor.tsx +29 -0
  35. package/src/instance/details/InstanceInformation.spec.tsx +2 -1
  36. package/src/instance/details/instance.details.controller.js +471 -473
  37. package/src/loadBalancer/details/loadBalancerDetails.controller.spec.ts +5 -3
  38. package/src/reactShims/aws.react.injector.ts +2 -1
  39. package/src/search/{searchResultFormatter.js → searchResultFormatter.ts} +5 -4
  40. package/src/serverGroup/configure/AmazonImageSelectInput.spec.tsx +10 -7
  41. package/src/serverGroup/configure/serverGroupCommandBuilder.aws.service.spec.js +302 -1
  42. package/src/serverGroup/configure/{serverGroupCommandBuilder.service.js → serverGroupCommandBuilder.service.ts} +182 -73
  43. package/src/serverGroup/configure/serverGroupCommandBuilder.spec.js +25 -8
  44. package/src/serverGroup/configure/serverGroupConfiguration.service.spec.ts +6 -13
  45. package/src/serverGroup/configure/serverGroupConfiguration.service.ts +53 -0
  46. package/src/serverGroup/configure/wizard/AmazonCloneServerGroupModal.tsx +1 -1
  47. package/src/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.tsx +31 -31
  48. package/src/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.tsx +133 -0
  49. package/src/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.tsx +99 -0
  50. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.tsx +36 -0
  51. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.tsx +144 -0
  52. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.tsx +138 -0
  53. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.tsx +135 -0
  54. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.tsx +137 -0
  55. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.less +5 -0
  56. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.tsx +108 -0
  57. package/src/serverGroup/configure/wizard/instanceType/advancedMode/advancedMode.less +110 -0
  58. package/src/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.tsx +62 -0
  59. package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +107 -62
  60. package/src/serverGroup/configure/wizard/pages/advancedSettings/ServerGroupAdvancedSettingsCommon.tsx +1 -1
  61. package/src/serverGroup/details/scalingPolicy/scalingPolicySummary.component.ts +6 -2
  62. package/src/serverGroup/details/scalingProcesses/AutoScalingProcessService.spec.ts +1 -2
  63. package/src/serverGroup/details/sections/InstancesDistributionDetailsSection.spec.tsx +8 -5
  64. package/src/serverGroup/details/sections/LaunchTemplateDetailsSection.spec.tsx +8 -5
  65. package/src/serverGroup/serverGroup.transformer.spec.ts +5 -3
  66. package/src/serverGroup/serverGroup.transformer.ts +24 -22
  67. package/src/subnet/SubnetSelectInput.spec.tsx +7 -4
  68. package/src/vpc/{VpcReader.spec.js → VpcReader.spec.ts} +2 -10
  69. package/src/vpc/VpcTag.spec.tsx +37 -0
  70. package/src/vpc/vpc.module.ts +6 -2
  71. package/dist/vpc/vpcTag.directive.d.ts +0 -2
  72. package/src/image/image.reader.spec.js +0 -123
  73. package/src/instance/awsInstanceType.service.js +0 -437
  74. package/src/vpc/vpcTag.directive.js +0 -34
  75. package/src/vpc/vpcTag.directive.spec.js +0 -60
@@ -1,49 +1,49 @@
1
- import React from 'react';
2
-
1
+ import React, { useEffect, useState } from 'react';
3
2
  import { ToggleButtonGroup, ToggleSize } from '@spinnaker/core';
4
3
  import { AwsReactInjector } from '../../../../reactShims';
5
4
 
6
- import type { IAmazonServerGroupCommand } from '../../serverGroupConfiguration.service';
7
-
8
5
  export interface ICpuCreditsToggleProps {
9
- command: IAmazonServerGroupCommand;
10
- newInstanceType?: string;
11
- newProfileType?: string;
6
+ unlimitedCpuCredits?: boolean;
7
+ selectedInstanceTypes: string[];
8
+ currentProfile: string;
12
9
  setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
13
10
  }
14
11
 
15
12
  export function CpuCreditsToggle(props: ICpuCreditsToggleProps) {
16
- const [showToggle, setShowToggle] = React.useState(false);
17
- React.useEffect(() => {
18
- if (props.newInstanceType) {
19
- const isBurstingSupportedForNewInstance = AwsReactInjector.awsInstanceTypeService.isBurstingSupported(
20
- props.newInstanceType,
21
- );
22
- if (!isBurstingSupportedForNewInstance) {
13
+ const { selectedInstanceTypes, currentProfile } = props;
14
+ const isBurstingSupportedForAllTypes = AwsReactInjector.awsInstanceTypeService.isBurstingSupportedForAllTypes(
15
+ selectedInstanceTypes,
16
+ );
17
+ const isAtleastOneTypeInProfile = AwsReactInjector.awsInstanceTypeService.getInstanceTypesInCategory(
18
+ selectedInstanceTypes,
19
+ currentProfile,
20
+ ).length
21
+ ? true
22
+ : false;
23
+
24
+ const [showToggle, setShowToggle] = useState(false);
25
+ useEffect(() => {
26
+ if (selectedInstanceTypes && selectedInstanceTypes.length) {
27
+ if (!isBurstingSupportedForAllTypes) {
23
28
  props.setUnlimitedCpuCredits(undefined);
24
29
  }
25
- setShowToggle(isBurstingSupportedForNewInstance);
30
+ setShowToggle(isBurstingSupportedForAllTypes);
26
31
  }
27
32
 
28
- if (props.newProfileType) {
29
- const { instanceType } = props.command;
30
- const isTypeInProfile = AwsReactInjector.awsInstanceTypeService.isInstanceTypeInCategory(
31
- instanceType,
32
- props.newProfileType,
33
+ if (currentProfile) {
34
+ setShowToggle(
35
+ selectedInstanceTypes &&
36
+ selectedInstanceTypes.length > 0 &&
37
+ isBurstingSupportedForAllTypes &&
38
+ isAtleastOneTypeInProfile,
33
39
  );
34
- const isBurstingSupportedForInstance = AwsReactInjector.awsInstanceTypeService.isBurstingSupported(instanceType);
35
- setShowToggle(instanceType && isTypeInProfile && isBurstingSupportedForInstance);
36
40
  }
37
- }, [props.newProfileType, props.newInstanceType]);
38
-
39
- const handleToggleChange = (state: boolean) => {
40
- props.setUnlimitedCpuCredits(state);
41
- };
41
+ }, [currentProfile, selectedInstanceTypes]);
42
42
 
43
43
  return (
44
- <div>
44
+ <div className={'row'} style={{ fontSize: '110%' }}>
45
45
  {showToggle && (
46
- <div className="row">
46
+ <div>
47
47
  <ToggleButtonGroup
48
48
  toggleSize={ToggleSize.XSMALL}
49
49
  propLabel={'Unlimited CPU credits '}
@@ -52,8 +52,8 @@ export function CpuCreditsToggle(props: ICpuCreditsToggleProps) {
52
52
  displayTextPropOffBtn={'Off'}
53
53
  tooltipPropOnBtn={'Toggle to turn ON unlimited CPU credits'}
54
54
  displayTextPropOnBtn={'On'}
55
- onClick={handleToggleChange}
56
- isPropertyActive={props.command.unlimitedCpuCredits}
55
+ onClick={(b) => props.setUnlimitedCpuCredits(b)}
56
+ isPropertyActive={props.unlimitedCpuCredits}
57
57
  />
58
58
  </div>
59
59
  )}
@@ -0,0 +1,133 @@
1
+ import type { FormikProps } from 'formik/dist/types';
2
+ import React, { useEffect, useState } from 'react';
3
+
4
+ import type { IInstanceTypeCategory } from '@spinnaker/core';
5
+ import { HelpField } from '@spinnaker/core';
6
+
7
+ import { AdvancedModeSelector } from './advancedMode/AdvancedModeSelector';
8
+ import { AWSProviderSettings } from '../../../../aws.settings';
9
+ import type { IAmazonInstanceTypeOverride, IAmazonServerGroupCommand } from '../../serverGroupConfiguration.service';
10
+ import { SimpleModeSelector } from './simpleMode/SimpleModeSelector';
11
+
12
+ export interface IInstanceTypeSelectorProps {
13
+ formik: FormikProps<IAmazonServerGroupCommand>;
14
+ instanceTypeDetails: IInstanceTypeCategory[];
15
+ }
16
+
17
+ export function InstanceTypeSelector(props: IInstanceTypeSelectorProps) {
18
+ const { instanceTypeDetails } = props;
19
+ const { values: command, setFieldValue } = props.formik;
20
+ const isLaunchTemplatesEnabled = AWSProviderSettings.serverGroups?.enableLaunchTemplates;
21
+
22
+ const useSimpleMode = command.viewState.useSimpleInstanceTypeSelector;
23
+ const [unlimitedCpuCredits, setUnlimitedCpuCredits] = useState(command.unlimitedCpuCredits);
24
+
25
+ useEffect(() => {
26
+ if (command.unlimitedCpuCredits !== unlimitedCpuCredits) {
27
+ setFieldValue('unlimitedCpuCredits', unlimitedCpuCredits);
28
+ }
29
+ }, [unlimitedCpuCredits]);
30
+
31
+ const handleModeChange = (useSimpleModeNew: boolean) => {
32
+ if (useSimpleMode !== useSimpleModeNew) {
33
+ setFieldValue('viewState', {
34
+ ...command.viewState,
35
+ useSimpleInstanceTypeSelector: useSimpleModeNew,
36
+ });
37
+
38
+ // update selected instance type(s) if mode changed.
39
+ // Simple mode uses command.instanceType to track selected type. Advanced mode uses command.launchTemplateOverridesForInstanceType to track selected types.
40
+ const multipleInstanceTypesInProps = command.launchTemplateOverridesForInstanceType;
41
+ const singleInstanceTypeInProps = command.instanceType;
42
+
43
+ const toSimple = useSimpleModeNew && multipleInstanceTypesInProps?.length;
44
+ const toAdvanced = !useSimpleModeNew && singleInstanceTypeInProps;
45
+ if (toSimple) {
46
+ const highestPriorityNum = Math.min(...multipleInstanceTypesInProps.map((it) => it.priority));
47
+ const instanceTypeWithHighestPriority = multipleInstanceTypesInProps.find(
48
+ (it) => it.priority === highestPriorityNum,
49
+ ).instanceType;
50
+
51
+ setFieldValue('instanceType', instanceTypeWithHighestPriority);
52
+ setFieldValue('launchTemplateOverridesForInstanceType', []);
53
+ command.instanceTypeChanged(command);
54
+ } else if (toAdvanced) {
55
+ const instanceTypes: IAmazonInstanceTypeOverride[] = [
56
+ {
57
+ instanceType: singleInstanceTypeInProps,
58
+ priority: 1,
59
+ },
60
+ ];
61
+ setFieldValue('instanceType', undefined);
62
+ setFieldValue('launchTemplateOverridesForInstanceType', instanceTypes);
63
+ command.launchTemplateOverridesChanged(command);
64
+ }
65
+ }
66
+ };
67
+
68
+ const showAdvancedMode = isLaunchTemplatesEnabled && !useSimpleMode;
69
+ if (showAdvancedMode) {
70
+ return (
71
+ <div className="container-fluid form-horizontal" style={{ padding: '0 15px' }}>
72
+ <div>
73
+ <p>
74
+ To configure a single instance type, use
75
+ <a className="clickable" onClick={() => handleModeChange(true)}>
76
+ <span> Simple Mode</span>
77
+ </a>
78
+ .
79
+ </p>
80
+ <i>
81
+ <b>Note:</b> If multiple instance types are already selected in advanced mode, the instance type with
82
+ highest priority will be preserved in simple mode.
83
+ </i>
84
+ </div>
85
+ <AdvancedModeSelector
86
+ formik={props.formik}
87
+ instanceTypeDetails={instanceTypeDetails}
88
+ setUnlimitedCpuCredits={setUnlimitedCpuCredits}
89
+ />
90
+ </div>
91
+ );
92
+ }
93
+
94
+ return (
95
+ <div className="container-fluid form-horizontal" style={{ padding: '0 15px' }}>
96
+ <div>
97
+ <span>To configure mixed server groups with multiple instance types,</span>
98
+ {!isLaunchTemplatesEnabled && (
99
+ <span>
100
+ <a
101
+ href={
102
+ 'https://spinnaker.io/docs/setup/other_config/server-group-launch-settings/aws-ec2/launch-templates-setup/'
103
+ }
104
+ >
105
+ <span> enable launch templates</span>
106
+ </a>
107
+ <span> and</span>
108
+ </span>
109
+ )}
110
+ <span>
111
+ <a
112
+ className={isLaunchTemplatesEnabled ? 'clickable' : 'disabled'}
113
+ onClick={isLaunchTemplatesEnabled ? () => handleModeChange(false) : () => {}}
114
+ >
115
+ <span> use Advanced Mode </span>
116
+ </a>
117
+ <HelpField id={'aws.serverGroup.advancedMode'} />.
118
+ </span>
119
+ <p></p>
120
+ {isLaunchTemplatesEnabled && (
121
+ <i>
122
+ <b>Note:</b> If an instance type is already selected in simple mode, it will be preserved in advanced mode.
123
+ </i>
124
+ )}
125
+ </div>
126
+ <SimpleModeSelector
127
+ command={command}
128
+ setUnlimitedCpuCredits={setUnlimitedCpuCredits}
129
+ setFieldValue={setFieldValue}
130
+ />
131
+ </div>
132
+ );
133
+ }
@@ -0,0 +1,99 @@
1
+ import type { FormikProps } from 'formik/dist/types';
2
+ import { keyBy } from 'lodash';
3
+ import React, { useEffect, useState } from 'react';
4
+
5
+ import { usePrevious } from '@spinnaker/core';
6
+
7
+ import { InstanceProfileSelector } from './InstanceProfileSelector';
8
+ import { InstanceTypeTable } from './InstanceTypeTable';
9
+ import { InstancesDistribution } from './InstancesDistribution';
10
+ import type { IAmazonInstanceTypeCategory } from '../../../../../instance/awsInstanceType.service';
11
+ import { AwsReactInjector } from '../../../../../reactShims';
12
+ import type { IAmazonInstanceTypeOverride, IAmazonServerGroupCommand } from '../../../serverGroupConfiguration.service';
13
+
14
+ export interface IAdvancedModeSelectorProps {
15
+ formik: FormikProps<IAmazonServerGroupCommand>;
16
+ instanceTypeDetails: IAmazonInstanceTypeCategory[];
17
+ setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
18
+ }
19
+
20
+ /**
21
+ * Note: Launch templates support is expected to be enabled if this component is rendered.
22
+ */
23
+ export function AdvancedModeSelector(props: IAdvancedModeSelectorProps) {
24
+ const { instanceTypeDetails, setUnlimitedCpuCredits } = props;
25
+ const { values: command, setFieldValue } = props.formik;
26
+
27
+ // for the case of MixedInstancesPolicy without overrides, copy command.instanceType to command.launchTemplateOverridesForInstanceType
28
+ const { instanceType, launchTemplateOverridesForInstanceType } = command;
29
+ useEffect(() => {
30
+ if (!launchTemplateOverridesForInstanceType && instanceType) {
31
+ props.formik.setFieldValue('launchTemplateOverridesForInstanceType', [
32
+ { instanceType: command.instanceType, priority: 1 },
33
+ ]);
34
+ }
35
+ });
36
+
37
+ const instanceTypesInProps: IAmazonInstanceTypeOverride[] = command.launchTemplateOverridesForInstanceType
38
+ ? command.launchTemplateOverridesForInstanceType
39
+ : undefined;
40
+
41
+ const selectedInstanceTypesMap = new Map<string, IAmazonInstanceTypeOverride>(
42
+ Object.entries(keyBy(instanceTypesInProps, 'instanceType')),
43
+ );
44
+
45
+ const [instanceProfile, setInstanceProfile] = useState(command.viewState.instanceProfile || 'custom');
46
+ const prevInstanceProfile = usePrevious(instanceProfile);
47
+
48
+ const handleProfileChange = (newProfile: string) => {
49
+ setInstanceProfile(newProfile);
50
+ setFieldValue('viewState', {
51
+ ...command.viewState,
52
+ instanceProfile: newProfile,
53
+ });
54
+
55
+ // update instance types on profile change
56
+ const hasProfileChanged = prevInstanceProfile && newProfile && prevInstanceProfile !== newProfile;
57
+ const isInstanceTypesUpdateNeeded = newProfile !== 'custom' && instanceTypesInProps && instanceTypesInProps.length;
58
+ if (hasProfileChanged && isInstanceTypesUpdateNeeded) {
59
+ const instanceTypesInProfile: string[] = AwsReactInjector.awsInstanceTypeService.getInstanceTypesInCategory(
60
+ instanceTypesInProps.map((it) => it.instanceType),
61
+ newProfile,
62
+ );
63
+ const newMultipleTypes = instanceTypesInProps.filter((o) => instanceTypesInProfile.includes(o.instanceType));
64
+ setFieldValue('launchTemplateOverridesForInstanceType', newMultipleTypes);
65
+ command.launchTemplateOverridesChanged(command);
66
+ }
67
+ };
68
+
69
+ const handleInstanceTypesChange = (types: IAmazonInstanceTypeOverride[]): void => {
70
+ setFieldValue('launchTemplateOverridesForInstanceType', types);
71
+ command.launchTemplateOverridesChanged(command);
72
+ };
73
+
74
+ if (!(instanceTypeDetails && instanceTypeDetails.length > 0)) {
75
+ return null;
76
+ }
77
+
78
+ return (
79
+ <div className={'advanced-mode-selector'}>
80
+ <InstanceProfileSelector
81
+ currentProfile={instanceProfile}
82
+ handleProfileChange={handleProfileChange}
83
+ instanceProfileList={instanceTypeDetails}
84
+ />
85
+ <InstancesDistribution formik={props.formik} />
86
+ <InstanceTypeTable
87
+ currentProfile={instanceProfile}
88
+ selectedInstanceTypesMap={selectedInstanceTypesMap}
89
+ unlimitedCpuCreditsInCmd={command.unlimitedCpuCredits}
90
+ profileDetails={instanceTypeDetails.find((p) => p.type === instanceProfile)}
91
+ availableInstanceTypesList={
92
+ (command.backingData && command.backingData.filtered && command.backingData.filtered.instanceTypes) || []
93
+ }
94
+ handleInstanceTypesChange={handleInstanceTypesChange}
95
+ setUnlimitedCpuCredits={setUnlimitedCpuCredits}
96
+ />
97
+ </div>
98
+ );
99
+ }
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+
3
+ import type { IAmazonInstanceTypeCategory } from '../../../../../instance/awsInstanceType.service';
4
+
5
+ import './advancedMode.less';
6
+
7
+ export interface IInstanceProfileSelectorProps {
8
+ currentProfile: string;
9
+ handleProfileChange: (profile: string) => void;
10
+ instanceProfileList: IAmazonInstanceTypeCategory[];
11
+ }
12
+
13
+ export function InstanceProfileSelector(props: IInstanceProfileSelectorProps) {
14
+ return (
15
+ <div>
16
+ <h4 style={{ marginTop: '10px' }}>This application is</h4>
17
+ {props.instanceProfileList.map((profile) => (
18
+ <div key={profile.type} className={`instance-profile-header profile-button`}>
19
+ <button
20
+ type="button"
21
+ onClick={() => props.handleProfileChange(profile.type)}
22
+ className={props.currentProfile === profile.type ? 'instance-profile active' : 'instance-profile'}
23
+ >
24
+ {props.currentProfile === profile.type && <span className="far fa-check-circle selected-indicator" />}
25
+ <div className="panel-heading">
26
+ <h4>
27
+ <span className={`glyphicon glyphicon-${profile.icon}`} />
28
+ <div>{profile.label}</div>
29
+ </h4>
30
+ </div>
31
+ </button>
32
+ </div>
33
+ ))}
34
+ </div>
35
+ );
36
+ }
@@ -0,0 +1,144 @@
1
+ import React, { useState } from 'react';
2
+ import { Checkbox } from 'react-bootstrap';
3
+ import { SortableHandle } from 'react-sortable-hoc';
4
+ import { TextInput, Tooltip } from '@spinnaker/core';
5
+ import type { IAmazonPreferredInstanceType } from '../../../../../instance/awsInstanceType.service';
6
+ import { CostFactor } from '../../../../../instance/details/CostFactor';
7
+ import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
8
+
9
+ export interface IRowProps {
10
+ isCustom: boolean;
11
+ selectedType?: IAmazonInstanceTypeOverride;
12
+ instanceTypeDetails?: IAmazonPreferredInstanceType;
13
+ removeInstanceType?: (typeToRemove: string) => void;
14
+ addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
15
+ }
16
+
17
+ export function InstanceTypeRow(props: IRowProps) {
18
+ const isRowSelected = props.selectedType ? true : false;
19
+ const [newWeightedCap, setNewWeightedCap] = useState('');
20
+ const instanceType = isRowSelected ? props.selectedType.instanceType : props.instanceTypeDetails.name;
21
+ const disableRow = isRowSelected ? false : props.instanceTypeDetails.unavailable;
22
+
23
+ const selectOrUpdateRow = (instanceType: string, weight: string) => {
24
+ if (!disableRow) {
25
+ props.addOrUpdateInstanceType(instanceType, weight);
26
+ }
27
+ setNewWeightedCap('');
28
+ };
29
+
30
+ const handleWeightChange = (e: React.ChangeEvent<any>): void => {
31
+ setNewWeightedCap(e.target.value);
32
+ if (isRowSelected) {
33
+ selectOrUpdateRow(instanceType, e.target.value);
34
+ } else {
35
+ document.getElementById(`selectInstanceType-${instanceType}`).focus();
36
+ }
37
+ };
38
+
39
+ let row;
40
+ if (props.isCustom) {
41
+ row = (
42
+ <tr key={instanceType} className={'sortable clickable'}>
43
+ <td>
44
+ <DragHandle />
45
+ </td>
46
+ <td>{instanceType}</td>
47
+ <td title={'Enter optional weight (allowed values: 1 to 999).'}>
48
+ <TextInput
49
+ className={'form-control input input-sm'}
50
+ pattern="[0-9]*"
51
+ placeholder="Enter optional weight (allowed values: 1 to 999)."
52
+ value={props.selectedType?.weightedCapacity || ''}
53
+ onChange={(e) => selectOrUpdateRow(instanceType, e.target.value)}
54
+ />
55
+ </td>
56
+ <td>
57
+ <div>
58
+ <span>
59
+ <a
60
+ className="btn btn-sm btn-link clickable"
61
+ onClick={() => props.removeInstanceType(instanceType)}
62
+ style={{ padding: '5px' }}
63
+ >
64
+ <Tooltip value={'Remove instance type'}>
65
+ <span className="glyphicon glyphicon-trash" />
66
+ </Tooltip>
67
+ </a>
68
+ </span>
69
+ </div>
70
+ </td>
71
+ </tr>
72
+ );
73
+ } else {
74
+ const { cpu, memory, cpuCreditsPerHour, storage, costFactor } = props.instanceTypeDetails;
75
+ row = (
76
+ <tr
77
+ key={instanceType}
78
+ className={isRowSelected ? 'sortable clickable' : `non-sortable ${disableRow ? 'unavailable' : 'clickable'}`}
79
+ title={
80
+ isRowSelected
81
+ ? 'Click to unselect instance type'
82
+ : disableRow
83
+ ? 'This instance type is not available for the selected configuration'
84
+ : 'Click to select instance type'
85
+ }
86
+ onClick={(e) => {
87
+ if (!$(e.target).is('input')) {
88
+ isRowSelected ? props.removeInstanceType(instanceType) : selectOrUpdateRow(instanceType, newWeightedCap);
89
+ }
90
+ }}
91
+ >
92
+ {isRowSelected ? (
93
+ <td>
94
+ <DragHandle />
95
+ </td>
96
+ ) : (
97
+ <td></td>
98
+ )}
99
+ <td>
100
+ <Checkbox
101
+ id={`selectInstanceType-${instanceType}`}
102
+ checked={isRowSelected ? true : false}
103
+ disabled={disableRow}
104
+ onChange={() => {
105
+ isRowSelected ? props.removeInstanceType(instanceType) : selectOrUpdateRow(instanceType, newWeightedCap);
106
+ }}
107
+ />
108
+ </td>
109
+ <td>{instanceType}</td>
110
+ <td>{cpu}</td>
111
+ <td>{memory}</td>
112
+ {cpuCreditsPerHour ? (
113
+ <td>{cpuCreditsPerHour}</td>
114
+ ) : (
115
+ <td title={'Cpu credits not applicable to instance type.'}>-</td>
116
+ )}
117
+ {storage.type === 'EBS' && <td>EBS Only</td>}
118
+ {storage.type === 'SSD' && <td>{storage.count + 'x' + storage.size}</td>}
119
+ <td>
120
+ <CostFactor min={costFactor} />
121
+ </td>
122
+ <td title={!disableRow ? 'Enter optional weight (allowed values: 1 to 999).' : ''}>
123
+ <TextInput
124
+ inputClassName={'form-control input input-sm'}
125
+ id={`weightedCapacity-${instanceType}`}
126
+ pattern="[0-9]*"
127
+ placeholder="Enter optional weight (allowed values: 1 to 999)."
128
+ disabled={disableRow}
129
+ value={props.selectedType?.weightedCapacity || newWeightedCap || ''}
130
+ onChange={handleWeightChange}
131
+ />
132
+ </td>
133
+ </tr>
134
+ );
135
+ }
136
+
137
+ return row;
138
+ }
139
+
140
+ const DragHandle = SortableHandle(() => (
141
+ <Tooltip value={'Drag to change priority'}>
142
+ <span className="instance-type-drag-handle glyphicon glyphicon-resize-vertical" />
143
+ </Tooltip>
144
+ ));
@@ -0,0 +1,138 @@
1
+ import React from 'react';
2
+ import type { SortEnd } from 'react-sortable-hoc';
3
+ import { arrayMove } from 'react-sortable-hoc';
4
+
5
+ import { CpuCreditsToggle } from '../CpuCreditsToggle';
6
+ import { InstanceTypeTableBody } from './InstanceTypeTableBody';
7
+ import { Header, Heading } from './InstanceTypeTableParts';
8
+ import { Footer } from './InstanceTypeTableParts';
9
+ import { AWSProviderSettings } from '../../../../../aws.settings';
10
+ import type { IAmazonInstanceTypeCategory } from '../../../../../instance/awsInstanceType.service';
11
+ import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
12
+
13
+ import './advancedMode.less';
14
+
15
+ export interface IInstanceTypeTableProps {
16
+ currentProfile: string;
17
+ selectedInstanceTypesMap: Map<string, IAmazonInstanceTypeOverride>;
18
+ unlimitedCpuCreditsInCmd: boolean;
19
+ profileDetails: IAmazonInstanceTypeCategory;
20
+ availableInstanceTypesList: string[];
21
+ handleInstanceTypesChange: (instanceTypes: IAmazonInstanceTypeOverride[]) => void;
22
+ setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
23
+ }
24
+
25
+ export function InstanceTypeTable(props: IInstanceTypeTableProps) {
26
+ const { currentProfile, selectedInstanceTypesMap } = props;
27
+
28
+ const handleSortEnd = (sortEnd: SortEnd): void => {
29
+ const sortedInstanceTypes: string[] = Array.from(selectedInstanceTypesMap.values())
30
+ .sort((i1, i2) => i1.priority - i2.priority)
31
+ .map((it) => it.instanceType);
32
+ const instanceTypesInNewOrder = arrayMove(sortedInstanceTypes, sortEnd.oldIndex, sortEnd.newIndex);
33
+
34
+ updatePriorityForSelectedTypes(instanceTypesInNewOrder);
35
+ };
36
+
37
+ const updatePriorityForSelectedTypes = (instanceTypesInNewOrder: string[]): void => {
38
+ selectedInstanceTypesMap.forEach((value, key) => {
39
+ const newPriority = 1 + instanceTypesInNewOrder.indexOf(key);
40
+ if (value.priority !== newPriority) {
41
+ value.priority = newPriority;
42
+ }
43
+ });
44
+ props.handleInstanceTypesChange(Array.from(selectedInstanceTypesMap.values()));
45
+ };
46
+
47
+ const removeInstanceType = (instanceType: string): void => {
48
+ const selectedInstanceTypesMapNew = new Map(selectedInstanceTypesMap);
49
+ selectedInstanceTypesMapNew.delete(instanceType);
50
+ props.handleInstanceTypesChange(Array.from(selectedInstanceTypesMapNew.values()));
51
+ };
52
+
53
+ const addOrUpdateInstanceType = (type: string, weight: string) => {
54
+ const weightNum = Number(weight);
55
+ const weightedCapacity = isNaN(weightNum) || weightNum === 0 ? undefined : weightNum.toString();
56
+ const itemToUpdate = selectedInstanceTypesMap.has(type)
57
+ ? {
58
+ ...selectedInstanceTypesMap.get(type), // update existing item
59
+ weightedCapacity,
60
+ }
61
+ : {
62
+ instanceType: type, // new item
63
+ weightedCapacity,
64
+ priority:
65
+ 1 +
66
+ Array.from(selectedInstanceTypesMap.values()).reduce(
67
+ (max, it) => (it.priority > max ? it.priority : max),
68
+ 0,
69
+ ),
70
+ };
71
+ selectedInstanceTypesMap.set(type, itemToUpdate);
72
+ props.handleInstanceTypesChange(Array.from(selectedInstanceTypesMap.values()));
73
+ };
74
+
75
+ const isCpuCreditsEnabled: boolean = AWSProviderSettings.serverGroups?.enableCpuCredits;
76
+ const selectedInstanceTypeNames = Array.from(selectedInstanceTypesMap.keys());
77
+ const cpuCreditsToggle = (
78
+ <div>
79
+ <CpuCreditsToggle
80
+ unlimitedCpuCredits={props.unlimitedCpuCreditsInCmd}
81
+ currentProfile={currentProfile}
82
+ selectedInstanceTypes={selectedInstanceTypeNames}
83
+ setUnlimitedCpuCredits={props.setUnlimitedCpuCredits}
84
+ />
85
+ </div>
86
+ );
87
+
88
+ if (currentProfile && currentProfile !== 'custom') {
89
+ const { label, descriptionListOverride, families, showCpuCredits } = props.profileDetails;
90
+ const isCustom = false;
91
+ return (
92
+ <div className={'row sub-section'}>
93
+ <Heading
94
+ isCustom={isCustom}
95
+ profileLabel={label}
96
+ profileDescriptionArr={descriptionListOverride ? descriptionListOverride : families.map((f) => f.description)}
97
+ />
98
+ {isCpuCreditsEnabled && cpuCreditsToggle}
99
+ <table className="table table-hover">
100
+ <Header isCustom={isCustom} showCpuCredits={showCpuCredits} />
101
+ <InstanceTypeTableBody
102
+ isCustom={isCustom}
103
+ profileFamiliesDetails={families}
104
+ selectedInstanceTypesMap={selectedInstanceTypesMap}
105
+ addOrUpdateInstanceType={addOrUpdateInstanceType}
106
+ removeInstanceType={removeInstanceType}
107
+ handleSortEnd={handleSortEnd}
108
+ />
109
+ </table>
110
+ </div>
111
+ );
112
+ } else {
113
+ const isCustom = true;
114
+ return (
115
+ <div className={'row sub-section'}>
116
+ <Heading isCustom={isCustom} />
117
+ {isCpuCreditsEnabled && cpuCreditsToggle}
118
+ <table className="table table-hover">
119
+ <Header isCustom={isCustom} />
120
+ <InstanceTypeTableBody
121
+ isCustom={isCustom}
122
+ selectedInstanceTypesMap={selectedInstanceTypesMap}
123
+ addOrUpdateInstanceType={addOrUpdateInstanceType}
124
+ removeInstanceType={removeInstanceType}
125
+ handleSortEnd={handleSortEnd}
126
+ />
127
+ <Footer
128
+ isCustom={isCustom}
129
+ availableInstanceTypesList={props.availableInstanceTypesList.filter(
130
+ (it) => !selectedInstanceTypeNames.includes(it),
131
+ )}
132
+ addOrUpdateInstanceType={addOrUpdateInstanceType}
133
+ />
134
+ </table>
135
+ </div>
136
+ );
137
+ }
138
+ }