@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.
- package/CHANGELOG.md +43 -0
- package/dist/domain/IAmazonLaunchTemplate.d.ts +1 -1
- package/dist/domain/IAmazonServerGroup.d.ts +10 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/instance/awsInstanceType.service.d.ts +10 -2
- package/dist/instance/awsInstanceTypes.d.ts +10 -0
- package/dist/instance/details/CostFactor.d.ts +6 -0
- package/dist/reactShims/aws.react.injector.d.ts +2 -1
- package/dist/search/searchResultFormatter.d.ts +2 -2
- package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +17 -2
- package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +39 -0
- package/dist/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.d.ts +3 -4
- package/dist/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.d.ts +9 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.d.ts +13 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.d.ts +9 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.d.ts +11 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.d.ts +14 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.d.ts +12 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.d.ts +15 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.d.ts +8 -0
- package/dist/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.d.ts +8 -0
- package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +9 -6
- package/dist/serverGroup/serverGroup.transformer.d.ts +2 -1
- package/package.json +5 -5
- package/src/domain/IAmazonLaunchTemplate.ts +1 -1
- package/src/domain/IAmazonServerGroup.ts +11 -0
- package/src/function/details/FunctionActions.spec.tsx +10 -9
- package/src/help/amazon.help.ts +9 -6
- package/src/image/image.reader.spec.ts +75 -0
- package/src/instance/awsInstanceType.service.spec.js +37 -71
- package/src/instance/awsInstanceType.service.ts +191 -0
- package/src/instance/awsInstanceTypes.ts +311 -0
- package/src/instance/details/CostFactor.tsx +29 -0
- package/src/instance/details/InstanceInformation.spec.tsx +2 -1
- package/src/instance/details/instance.details.controller.js +471 -473
- package/src/loadBalancer/details/loadBalancerDetails.controller.spec.ts +5 -3
- package/src/reactShims/aws.react.injector.ts +2 -1
- package/src/search/{searchResultFormatter.js → searchResultFormatter.ts} +5 -4
- package/src/serverGroup/configure/AmazonImageSelectInput.spec.tsx +10 -7
- package/src/serverGroup/configure/serverGroupCommandBuilder.aws.service.spec.js +302 -1
- package/src/serverGroup/configure/{serverGroupCommandBuilder.service.js → serverGroupCommandBuilder.service.ts} +182 -73
- package/src/serverGroup/configure/serverGroupCommandBuilder.spec.js +25 -8
- package/src/serverGroup/configure/serverGroupConfiguration.service.spec.ts +6 -13
- package/src/serverGroup/configure/serverGroupConfiguration.service.ts +53 -0
- package/src/serverGroup/configure/wizard/AmazonCloneServerGroupModal.tsx +1 -1
- package/src/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.tsx +31 -31
- package/src/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.tsx +133 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.tsx +99 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.tsx +36 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.tsx +144 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.tsx +138 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.tsx +135 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.tsx +137 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.less +5 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.tsx +108 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/advancedMode.less +110 -0
- package/src/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.tsx +62 -0
- package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +107 -62
- package/src/serverGroup/configure/wizard/pages/advancedSettings/ServerGroupAdvancedSettingsCommon.tsx +1 -1
- package/src/serverGroup/details/scalingPolicy/scalingPolicySummary.component.ts +6 -2
- package/src/serverGroup/details/scalingProcesses/AutoScalingProcessService.spec.ts +1 -2
- package/src/serverGroup/details/sections/InstancesDistributionDetailsSection.spec.tsx +8 -5
- package/src/serverGroup/details/sections/LaunchTemplateDetailsSection.spec.tsx +8 -5
- package/src/serverGroup/serverGroup.transformer.spec.ts +5 -3
- package/src/serverGroup/serverGroup.transformer.ts +24 -22
- package/src/subnet/SubnetSelectInput.spec.tsx +7 -4
- package/src/vpc/{VpcReader.spec.js → VpcReader.spec.ts} +2 -10
- package/src/vpc/VpcTag.spec.tsx +37 -0
- package/src/vpc/vpc.module.ts +6 -2
- package/dist/vpc/vpcTag.directive.d.ts +0 -2
- package/src/image/image.reader.spec.js +0 -123
- package/src/instance/awsInstanceType.service.js +0 -437
- package/src/vpc/vpcTag.directive.js +0 -34
- 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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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(
|
|
30
|
+
setShowToggle(isBurstingSupportedForAllTypes);
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
}, [
|
|
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
|
|
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={
|
|
56
|
-
isPropertyActive={props.
|
|
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
|
+
}
|
package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.tsx
ADDED
|
@@ -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
|
+
}
|