@spinnaker/amazon 0.13.3 → 0.13.4
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 +13 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/instanceType/InstanceTypeWarning.d.ts +6 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.d.ts +3 -0
- package/dist/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.d.ts +1 -0
- package/package.json +3 -3
- package/src/pipeline/stages/rollbackCluster/rollbackClusterStage.html +11 -0
- package/src/serverGroup/AvailabilityZoneSelector.tsx +6 -7
- package/src/serverGroup/configure/serverGroupConfiguration.service.ts +23 -1
- package/src/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.tsx +64 -30
- package/src/serverGroup/configure/wizard/instanceType/InstanceTypeWarning.tsx +44 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.tsx +3 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/AmazonInstanceTypeInfoRenderer.tsx +14 -14
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.tsx +6 -0
- package/src/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.tsx +3 -0
- package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.tsx +3 -0
- package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +13 -3
- package/src/serverGroup/details/sections/LaunchConfigDetailsSection.tsx +6 -2
- package/src/serverGroup/serverGroup.transformer.ts +4 -0
|
@@ -4,6 +4,7 @@ import type { IAmazonInstanceType } from '../../instance/awsInstanceType.service
|
|
|
4
4
|
export declare type IBlockDeviceMappingSource = 'source' | 'ami' | 'default';
|
|
5
5
|
export interface IAmazonServerGroupCommandDirty extends IServerGroupCommandDirty {
|
|
6
6
|
targetGroups?: string[];
|
|
7
|
+
launchTemplateOverridesForInstanceType?: IAmazonInstanceTypeOverride[];
|
|
7
8
|
}
|
|
8
9
|
export interface IAmazonServerGroupCommandResult extends IServerGroupCommandResult {
|
|
9
10
|
dirty: IAmazonServerGroupCommandDirty;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { IAmazonServerGroupCommandDirty } from '../../serverGroupConfiguration.service';
|
|
3
|
+
export declare function InstanceTypeWarning(props: {
|
|
4
|
+
dirty: IAmazonServerGroupCommandDirty;
|
|
5
|
+
clearWarnings: () => void;
|
|
6
|
+
}): JSX.Element;
|
package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface IAdvancedModeSelectorProps {
|
|
|
6
6
|
formik: FormikProps<IAmazonServerGroupCommand>;
|
|
7
7
|
instanceTypeDetails: IAmazonInstanceTypeCategory[];
|
|
8
8
|
setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
|
|
9
|
+
clearWarnings: () => void;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Note: Launch templates support is expected to be enabled if this component is rendered.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { IAmazonInstanceTypeCategory } from '../../../../../instance/awsInstanceType.service';
|
|
3
3
|
import type { IAmazonInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
4
4
|
import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
|
|
5
|
+
import type { IAmazonServerGroupCommandViewState } from '../../../serverGroupConfiguration.service';
|
|
5
6
|
import './advancedMode.less';
|
|
6
7
|
export interface IInstanceTypeTableProps {
|
|
7
8
|
currentProfile: string;
|
|
@@ -11,5 +12,7 @@ export interface IInstanceTypeTableProps {
|
|
|
11
12
|
availableInstanceTypesList: IAmazonInstanceType[];
|
|
12
13
|
handleInstanceTypesChange: (instanceTypes: IAmazonInstanceTypeOverride[]) => void;
|
|
13
14
|
setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
|
|
15
|
+
viewState: IAmazonServerGroupCommandViewState;
|
|
16
|
+
clearWarnings: () => void;
|
|
14
17
|
}
|
|
15
18
|
export declare function InstanceTypeTable(props: IInstanceTypeTableProps): JSX.Element;
|
|
@@ -4,5 +4,6 @@ export interface ISimpleModeSelectorProps {
|
|
|
4
4
|
command: IAmazonServerGroupCommand;
|
|
5
5
|
setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
|
|
6
6
|
setFieldValue: (field: keyof IAmazonServerGroupCommand, value: any, shouldValidate?: boolean) => void;
|
|
7
|
+
clearWarnings: () => void;
|
|
7
8
|
}
|
|
8
9
|
export declare function SimpleModeSelector(props: ISimpleModeSelectorProps): JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spinnaker/amazon",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.4",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"lib": "npm run build"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@spinnaker/core": "^0.
|
|
16
|
+
"@spinnaker/core": "^0.23.0",
|
|
17
17
|
"@uirouter/angularjs": "1.0.26",
|
|
18
18
|
"@uirouter/core": "6.0.8",
|
|
19
19
|
"@uirouter/react": "1.0.7",
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"shx": "0.3.3",
|
|
56
56
|
"typescript": "4.3.5"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "4d61e53884040e8cae45d29b398c88e3814c6aca"
|
|
59
59
|
}
|
|
@@ -28,5 +28,16 @@
|
|
|
28
28
|
/>
|
|
29
29
|
percent of instances are healthy.
|
|
30
30
|
</div>
|
|
31
|
+
<div class="col-sm-10 col-sm-offset-2">
|
|
32
|
+
Rollback Timeout is
|
|
33
|
+
<input
|
|
34
|
+
type="number"
|
|
35
|
+
min="0"
|
|
36
|
+
max="100"
|
|
37
|
+
ng-model="stage.rollbackTimeout"
|
|
38
|
+
class="form-control input-sm inline-number"
|
|
39
|
+
/>
|
|
40
|
+
minutes.
|
|
41
|
+
</div>
|
|
31
42
|
</div>
|
|
32
43
|
</div>
|
|
@@ -27,23 +27,22 @@ export class AvailabilityZoneSelector extends React.Component<
|
|
|
27
27
|
usePreferredZones: props.usePreferredZones || !props.selectedZones || props.selectedZones.length === 0,
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
this.setDefaultZones(props);
|
|
30
|
+
this.setDefaultZones(this.state.usePreferredZones, props);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
public componentWillReceiveProps(nextProps: IAvailabilityZoneSelectorProps): void {
|
|
34
34
|
if (nextProps.region !== this.props.region || nextProps.credentials !== this.props.credentials) {
|
|
35
|
-
this.setDefaultZones(nextProps);
|
|
35
|
+
this.setDefaultZones(this.state.usePreferredZones, nextProps);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
private setDefaultZones(props: IAvailabilityZoneSelectorProps) {
|
|
40
|
-
const { credentials,
|
|
41
|
-
const { usePreferredZones } = this.state;
|
|
39
|
+
private setDefaultZones(usePreferredZones: boolean, props: IAvailabilityZoneSelectorProps) {
|
|
40
|
+
const { credentials, region } = props;
|
|
42
41
|
|
|
43
42
|
AccountService.getAvailabilityZonesForAccountAndRegion('aws', credentials, region).then((preferredZones) => {
|
|
44
43
|
this.setState({ defaultZones: preferredZones });
|
|
45
44
|
if (usePreferredZones && preferredZones) {
|
|
46
|
-
onChange(preferredZones.slice());
|
|
45
|
+
props.onChange(preferredZones.slice());
|
|
47
46
|
}
|
|
48
47
|
});
|
|
49
48
|
}
|
|
@@ -53,7 +52,7 @@ export class AvailabilityZoneSelector extends React.Component<
|
|
|
53
52
|
this.setState({ usePreferredZones });
|
|
54
53
|
|
|
55
54
|
if (usePreferredZones) {
|
|
56
|
-
this.setDefaultZones(this.props);
|
|
55
|
+
this.setDefaultZones(usePreferredZones, this.props);
|
|
57
56
|
}
|
|
58
57
|
};
|
|
59
58
|
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
chain,
|
|
4
4
|
clone,
|
|
5
5
|
cloneDeep,
|
|
6
|
+
difference,
|
|
6
7
|
extend,
|
|
7
8
|
find,
|
|
8
9
|
flatten,
|
|
@@ -61,6 +62,7 @@ export type IBlockDeviceMappingSource = 'source' | 'ami' | 'default';
|
|
|
61
62
|
|
|
62
63
|
export interface IAmazonServerGroupCommandDirty extends IServerGroupCommandDirty {
|
|
63
64
|
targetGroups?: string[];
|
|
65
|
+
launchTemplateOverridesForInstanceType?: IAmazonInstanceTypeOverride[];
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
export interface IAmazonServerGroupCommandResult extends IServerGroupCommandResult {
|
|
@@ -376,11 +378,13 @@ export class AwsServerGroupConfigurationService {
|
|
|
376
378
|
|
|
377
379
|
public configureInstanceTypes(command: IAmazonServerGroupCommand): IServerGroupCommandResult {
|
|
378
380
|
const result: IAmazonServerGroupCommandResult = { dirty: {} };
|
|
381
|
+
|
|
379
382
|
if (command.region && (command.virtualizationType || command.viewState.disableImageSelection)) {
|
|
380
383
|
let filteredTypesInfo: IAmazonInstanceType[] = this.awsInstanceTypeService.getAvailableTypesForRegions(
|
|
381
384
|
command.backingData.instanceTypesInfo,
|
|
382
385
|
[command.region],
|
|
383
386
|
);
|
|
387
|
+
|
|
384
388
|
if (command.virtualizationType || command.amiArchitecture) {
|
|
385
389
|
filteredTypesInfo = this.awsInstanceTypeService.filterInstanceTypes(
|
|
386
390
|
filteredTypesInfo,
|
|
@@ -389,18 +393,35 @@ export class AwsServerGroupConfigurationService {
|
|
|
389
393
|
command.amiArchitecture,
|
|
390
394
|
);
|
|
391
395
|
}
|
|
392
|
-
|
|
393
396
|
const filteredTypes: string[] = map(filteredTypesInfo, 'name');
|
|
397
|
+
|
|
398
|
+
// handle incompatibility for single instance type case
|
|
394
399
|
if (command.instanceType && !filteredTypes.includes(command.instanceType)) {
|
|
395
400
|
result.dirty.instanceType = command.instanceType;
|
|
396
401
|
command.instanceType = null;
|
|
397
402
|
}
|
|
403
|
+
|
|
404
|
+
// handle incompatibility for multiple instance types case
|
|
405
|
+
const multipleInstanceTypes: string[] = map(command.launchTemplateOverridesForInstanceType, 'instanceType');
|
|
406
|
+
const validInstanceTypes: string[] = intersection(multipleInstanceTypes, filteredTypes);
|
|
407
|
+
const invalidInstanceTypes: string[] = difference(multipleInstanceTypes, validInstanceTypes);
|
|
408
|
+
|
|
409
|
+
if (command.launchTemplateOverridesForInstanceType && invalidInstanceTypes.length > 0) {
|
|
410
|
+
result.dirty.launchTemplateOverridesForInstanceType = command.launchTemplateOverridesForInstanceType.filter(
|
|
411
|
+
(it) => invalidInstanceTypes.includes(it.instanceType),
|
|
412
|
+
);
|
|
413
|
+
command.launchTemplateOverridesForInstanceType = command.launchTemplateOverridesForInstanceType.filter((it) =>
|
|
414
|
+
validInstanceTypes.includes(it.instanceType),
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
398
418
|
command.backingData.filtered.instanceTypes = filteredTypes;
|
|
399
419
|
command.backingData.filtered.instanceTypesInfo = filteredTypesInfo;
|
|
400
420
|
} else {
|
|
401
421
|
command.backingData.filtered.instanceTypes = [];
|
|
402
422
|
command.backingData.filtered.instanceTypesInfo = [];
|
|
403
423
|
}
|
|
424
|
+
|
|
404
425
|
extend(command.viewState.dirty, result.dirty);
|
|
405
426
|
return result;
|
|
406
427
|
}
|
|
@@ -613,6 +634,7 @@ export class AwsServerGroupConfigurationService {
|
|
|
613
634
|
});
|
|
614
635
|
command.vpcId = subnet ? subnet.vpcId : null;
|
|
615
636
|
}
|
|
637
|
+
|
|
616
638
|
extend(result.dirty, this.configureInstanceTypes(command).dirty);
|
|
617
639
|
return result;
|
|
618
640
|
}
|
|
@@ -16,51 +16,83 @@ export interface IInstanceTypeSelectorProps {
|
|
|
16
16
|
|
|
17
17
|
export function InstanceTypeSelector(props: IInstanceTypeSelectorProps) {
|
|
18
18
|
const { instanceTypeDetails } = props;
|
|
19
|
-
const { values
|
|
19
|
+
const { values, setFieldValue } = props.formik;
|
|
20
20
|
const isLaunchTemplatesEnabled = AWSProviderSettings.serverGroups?.enableLaunchTemplates;
|
|
21
21
|
|
|
22
|
-
const useSimpleMode =
|
|
23
|
-
const [unlimitedCpuCredits, setUnlimitedCpuCredits] = useState(
|
|
22
|
+
const useSimpleMode = values.viewState.useSimpleInstanceTypeSelector;
|
|
23
|
+
const [unlimitedCpuCredits, setUnlimitedCpuCredits] = useState(values.unlimitedCpuCredits);
|
|
24
24
|
|
|
25
25
|
useEffect(() => {
|
|
26
|
-
if (
|
|
26
|
+
if (values.unlimitedCpuCredits !== unlimitedCpuCredits) {
|
|
27
27
|
setFieldValue('unlimitedCpuCredits', unlimitedCpuCredits);
|
|
28
28
|
}
|
|
29
29
|
}, [unlimitedCpuCredits]);
|
|
30
30
|
|
|
31
|
+
const clearWarnings = () => {
|
|
32
|
+
const { formik } = props;
|
|
33
|
+
|
|
34
|
+
// clear for both keys to support consistency between simple and advanced modes
|
|
35
|
+
formik.values.viewState.dirty['instanceType'] = null;
|
|
36
|
+
formik.values.viewState.dirty['launchTemplateOverridesForInstanceType'] = null;
|
|
37
|
+
|
|
38
|
+
formik.validateForm();
|
|
39
|
+
};
|
|
40
|
+
|
|
31
41
|
const handleModeChange = (useSimpleModeNew: boolean) => {
|
|
32
42
|
if (useSimpleMode !== useSimpleModeNew) {
|
|
33
43
|
setFieldValue('viewState', {
|
|
34
|
-
...
|
|
44
|
+
...values.viewState,
|
|
35
45
|
useSimpleInstanceTypeSelector: useSimpleModeNew,
|
|
36
46
|
});
|
|
37
47
|
|
|
38
48
|
// update selected instance type(s) if mode changed.
|
|
39
49
|
// Simple mode uses command.instanceType to track selected type. Advanced mode uses command.launchTemplateOverridesForInstanceType to track selected types.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
if (useSimpleModeNew) {
|
|
51
|
+
const multipleInstanceTypesInProps = values.launchTemplateOverridesForInstanceType;
|
|
52
|
+
const dirtyMultipleInstanceTypesInProps = values.viewState.dirty.launchTemplateOverridesForInstanceType;
|
|
53
|
+
|
|
54
|
+
if (multipleInstanceTypesInProps?.length) {
|
|
55
|
+
const instanceTypeWithHighestPriority = multipleInstanceTypesInProps.reduce((prev, current) => {
|
|
56
|
+
return prev.priority < current.priority ? prev : current;
|
|
57
|
+
}).instanceType;
|
|
58
|
+
setFieldValue('instanceType', instanceTypeWithHighestPriority);
|
|
59
|
+
setFieldValue('launchTemplateOverridesForInstanceType', []);
|
|
60
|
+
values.instanceTypeChanged(values);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (dirtyMultipleInstanceTypesInProps?.length) {
|
|
64
|
+
const instanceTypeWithHighestPriorityDirty = dirtyMultipleInstanceTypesInProps.reduce((prev, current) => {
|
|
65
|
+
return prev.priority < current.priority ? prev : current;
|
|
66
|
+
}).instanceType;
|
|
67
|
+
setFieldValue('viewState.dirty.instanceType', instanceTypeWithHighestPriorityDirty);
|
|
68
|
+
setFieldValue('viewState.dirty.launchTemplateOverridesForInstanceType', []);
|
|
69
|
+
}
|
|
70
|
+
} else if (!useSimpleModeNew) {
|
|
71
|
+
const singleInstanceTypeInProps = values.instanceType;
|
|
72
|
+
const dirtySingleInstanceTypeInProps = values.viewState.dirty.instanceType;
|
|
73
|
+
|
|
74
|
+
if (singleInstanceTypeInProps) {
|
|
75
|
+
const instanceTypes: IAmazonInstanceTypeOverride[] = [
|
|
76
|
+
{
|
|
77
|
+
instanceType: singleInstanceTypeInProps,
|
|
78
|
+
priority: 1,
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
setFieldValue('instanceType', undefined);
|
|
82
|
+
setFieldValue('launchTemplateOverridesForInstanceType', instanceTypes);
|
|
83
|
+
values.launchTemplateOverridesChanged(values);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (dirtySingleInstanceTypeInProps) {
|
|
87
|
+
const dirtyInstanceTypes: IAmazonInstanceTypeOverride[] = [
|
|
88
|
+
{
|
|
89
|
+
instanceType: dirtySingleInstanceTypeInProps,
|
|
90
|
+
priority: 1,
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
setFieldValue('viewState.dirty.instanceType', undefined);
|
|
94
|
+
setFieldValue('viewState.dirty.launchTemplateOverridesForInstanceType', dirtyInstanceTypes);
|
|
95
|
+
}
|
|
64
96
|
}
|
|
65
97
|
}
|
|
66
98
|
};
|
|
@@ -86,6 +118,7 @@ export function InstanceTypeSelector(props: IInstanceTypeSelectorProps) {
|
|
|
86
118
|
formik={props.formik}
|
|
87
119
|
instanceTypeDetails={instanceTypeDetails}
|
|
88
120
|
setUnlimitedCpuCredits={setUnlimitedCpuCredits}
|
|
121
|
+
clearWarnings={clearWarnings}
|
|
89
122
|
/>
|
|
90
123
|
</div>
|
|
91
124
|
);
|
|
@@ -124,9 +157,10 @@ export function InstanceTypeSelector(props: IInstanceTypeSelectorProps) {
|
|
|
124
157
|
)}
|
|
125
158
|
</div>
|
|
126
159
|
<SimpleModeSelector
|
|
127
|
-
command={
|
|
160
|
+
command={values}
|
|
128
161
|
setUnlimitedCpuCredits={setUnlimitedCpuCredits}
|
|
129
162
|
setFieldValue={setFieldValue}
|
|
163
|
+
clearWarnings={clearWarnings}
|
|
130
164
|
/>
|
|
131
165
|
</div>
|
|
132
166
|
);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { IAmazonServerGroupCommandDirty } from '../../serverGroupConfiguration.service';
|
|
4
|
+
|
|
5
|
+
export function InstanceTypeWarning(props: { dirty: IAmazonServerGroupCommandDirty; clearWarnings: () => void }) {
|
|
6
|
+
if (
|
|
7
|
+
props.dirty.instanceType ||
|
|
8
|
+
(props.dirty.launchTemplateOverridesForInstanceType &&
|
|
9
|
+
props.dirty.launchTemplateOverridesForInstanceType.length > 0)
|
|
10
|
+
) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="col-md-12">
|
|
13
|
+
<div className="alert alert-warning">
|
|
14
|
+
<p>
|
|
15
|
+
<i className="fa fa-exclamation-triangle" />
|
|
16
|
+
{props.dirty.instanceType &&
|
|
17
|
+
'The following instance type was found incompatible with the selected image/region and was removed:'}
|
|
18
|
+
{props.dirty.launchTemplateOverridesForInstanceType &&
|
|
19
|
+
props.dirty.launchTemplateOverridesForInstanceType.length > 0 &&
|
|
20
|
+
'The following instance type(s) were found incompatible with the selected image/region and were removed:'}
|
|
21
|
+
</p>
|
|
22
|
+
<ul>
|
|
23
|
+
{props.dirty.instanceType && <li key={props.dirty.instanceType}>{props.dirty.instanceType}</li>}
|
|
24
|
+
{props.dirty.launchTemplateOverridesForInstanceType &&
|
|
25
|
+
props.dirty.launchTemplateOverridesForInstanceType.length > 0 &&
|
|
26
|
+
props.dirty.launchTemplateOverridesForInstanceType.map((it) => (
|
|
27
|
+
<li key={it.instanceType}>
|
|
28
|
+
{it.instanceType}
|
|
29
|
+
{it.weightedCapacity ? ' with weight ' + it.weightedCapacity : ''}
|
|
30
|
+
</li>
|
|
31
|
+
))}
|
|
32
|
+
</ul>
|
|
33
|
+
<p className="text-right">
|
|
34
|
+
<a className="btn btn-sm btn-default dirty-flag-dismiss clickable" onClick={() => props.clearWarnings()}>
|
|
35
|
+
Okay
|
|
36
|
+
</a>
|
|
37
|
+
</p>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
} else {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -15,6 +15,7 @@ export interface IAdvancedModeSelectorProps {
|
|
|
15
15
|
formik: FormikProps<IAmazonServerGroupCommand>;
|
|
16
16
|
instanceTypeDetails: IAmazonInstanceTypeCategory[];
|
|
17
17
|
setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
|
|
18
|
+
clearWarnings: () => void;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -93,6 +94,8 @@ export function AdvancedModeSelector(props: IAdvancedModeSelectorProps) {
|
|
|
93
94
|
}
|
|
94
95
|
handleInstanceTypesChange={handleInstanceTypesChange}
|
|
95
96
|
setUnlimitedCpuCredits={setUnlimitedCpuCredits}
|
|
97
|
+
viewState={command.viewState}
|
|
98
|
+
clearWarnings={props.clearWarnings}
|
|
96
99
|
/>
|
|
97
100
|
</div>
|
|
98
101
|
);
|
|
@@ -4,50 +4,50 @@ import React from 'react';
|
|
|
4
4
|
import type { IAmazonInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
5
5
|
|
|
6
6
|
export function AmazonInstanceTypeInfoRenderer(props: { instanceType: IAmazonInstanceType }) {
|
|
7
|
-
const spotSupport = props.instanceType
|
|
8
|
-
const CpuMem = `${props.instanceType
|
|
9
|
-
const instanceStorageInfo = props.instanceType
|
|
7
|
+
const spotSupport = props.instanceType?.supportedUsageClasses?.includes('spot') ? '| SPOT supported' : '';
|
|
8
|
+
const CpuMem = `${props.instanceType?.defaultVCpus} vCPU | ${props.instanceType?.memoryInGiB} Gib Memory ${spotSupport}`;
|
|
9
|
+
const instanceStorageInfo = props.instanceType?.instanceStorageSupported && (
|
|
10
10
|
<span>
|
|
11
11
|
<br />
|
|
12
12
|
<span className={`select-option-label-attributes`}>
|
|
13
|
-
{`Instance Storage: ${_.toUpper(props.instanceType
|
|
14
|
-
props.instanceType
|
|
13
|
+
{`Instance Storage: ${_.toUpper(props.instanceType?.instanceStorageInfo?.storageTypes)} | ${
|
|
14
|
+
props.instanceType?.instanceStorageInfo?.totalSizeInGB
|
|
15
15
|
} Gib total size`}
|
|
16
16
|
</span>
|
|
17
17
|
</span>
|
|
18
18
|
);
|
|
19
|
-
const ebsInfo = props.instanceType
|
|
19
|
+
const ebsInfo = props.instanceType?.ebsInfo && (
|
|
20
20
|
<span>
|
|
21
21
|
<br />
|
|
22
22
|
<span className={`select-option-label-attributes`}>
|
|
23
|
-
{`EBS: optimization ${props.instanceType
|
|
23
|
+
{`EBS: optimization ${props.instanceType?.ebsInfo?.ebsOptimizedSupport} | NVMe ${props.instanceType?.ebsInfo?.nvmeSupport} | Encryption ${props.instanceType?.ebsInfo?.encryptionSupport}`}
|
|
24
24
|
</span>
|
|
25
25
|
</span>
|
|
26
26
|
);
|
|
27
|
-
const gpuInfo = props.instanceType
|
|
27
|
+
const gpuInfo = props.instanceType?.gpuInfo && (
|
|
28
28
|
<span>
|
|
29
29
|
<br />
|
|
30
30
|
<span className={`select-option-label-attributes`}>
|
|
31
|
-
{`GPU: ${props.instanceType
|
|
32
|
-
{props.instanceType
|
|
31
|
+
{`GPU: ${props.instanceType?.gpuInfo?.totalGpuMemoryInMiB} MiB total memory`}
|
|
32
|
+
{props.instanceType?.gpuInfo?.gpus.map(
|
|
33
33
|
(g) => ` | ${g.count} ${g.manufacturer} ${g.name} GPUs, size: ${g.gpuSizeInMiB} MiB`,
|
|
34
34
|
)}
|
|
35
35
|
</span>
|
|
36
36
|
</span>
|
|
37
37
|
);
|
|
38
|
-
const generationInfo = typeof props.instanceType
|
|
39
|
-
props.instanceType
|
|
38
|
+
const generationInfo = typeof props.instanceType?.currentGeneration !== 'undefined' &&
|
|
39
|
+
props.instanceType?.currentGeneration !== null && (
|
|
40
40
|
<span>
|
|
41
41
|
<br />
|
|
42
42
|
<span className={`select-option-label-attributes`}>
|
|
43
|
-
{props.instanceType
|
|
43
|
+
{props.instanceType?.currentGeneration ? 'Current Generation' : 'Previous Generation'}
|
|
44
44
|
</span>
|
|
45
45
|
</span>
|
|
46
46
|
);
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
49
|
<span>
|
|
50
|
-
<span className={`select-option-label`}>{props.instanceType
|
|
50
|
+
<span className={`select-option-label`}>{props.instanceType?.name}</span>
|
|
51
51
|
<br />
|
|
52
52
|
<span className={`select-option-label-attributes`}>{CpuMem}</span>
|
|
53
53
|
{instanceStorageInfo}
|
|
@@ -6,10 +6,12 @@ import { CpuCreditsToggle } from '../CpuCreditsToggle';
|
|
|
6
6
|
import { InstanceTypeTableBody } from './InstanceTypeTableBody';
|
|
7
7
|
import { Header, Heading } from './InstanceTypeTableParts';
|
|
8
8
|
import { Footer } from './InstanceTypeTableParts';
|
|
9
|
+
import { InstanceTypeWarning } from '../InstanceTypeWarning';
|
|
9
10
|
import { AWSProviderSettings } from '../../../../../aws.settings';
|
|
10
11
|
import type { IAmazonInstanceTypeCategory } from '../../../../../instance/awsInstanceType.service';
|
|
11
12
|
import type { IAmazonInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
12
13
|
import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
|
|
14
|
+
import type { IAmazonServerGroupCommandViewState } from '../../../serverGroupConfiguration.service';
|
|
13
15
|
|
|
14
16
|
import './advancedMode.less';
|
|
15
17
|
|
|
@@ -21,6 +23,8 @@ export interface IInstanceTypeTableProps {
|
|
|
21
23
|
availableInstanceTypesList: IAmazonInstanceType[];
|
|
22
24
|
handleInstanceTypesChange: (instanceTypes: IAmazonInstanceTypeOverride[]) => void;
|
|
23
25
|
setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
|
|
26
|
+
viewState: IAmazonServerGroupCommandViewState;
|
|
27
|
+
clearWarnings: () => void;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export function InstanceTypeTable(props: IInstanceTypeTableProps) {
|
|
@@ -96,6 +100,7 @@ export function InstanceTypeTable(props: IInstanceTypeTableProps) {
|
|
|
96
100
|
profileLabel={label}
|
|
97
101
|
profileDescriptionArr={descriptionListOverride ? descriptionListOverride : families.map((f) => f.description)}
|
|
98
102
|
/>
|
|
103
|
+
<InstanceTypeWarning dirty={props.viewState.dirty} clearWarnings={props.clearWarnings} />
|
|
99
104
|
{isCpuCreditsEnabled && cpuCreditsToggle}
|
|
100
105
|
<table className="table table-hover">
|
|
101
106
|
<Header isCustom={isCustom} showCpuCredits={showCpuCredits} />
|
|
@@ -115,6 +120,7 @@ export function InstanceTypeTable(props: IInstanceTypeTableProps) {
|
|
|
115
120
|
return (
|
|
116
121
|
<div className={'row sub-section'}>
|
|
117
122
|
<Heading isCustom={isCustom} />
|
|
123
|
+
<InstanceTypeWarning dirty={props.viewState.dirty} clearWarnings={props.clearWarnings} />
|
|
118
124
|
{isCpuCreditsEnabled && cpuCreditsToggle}
|
|
119
125
|
<table className="table table-hover">
|
|
120
126
|
<Header isCustom={isCustom} />
|
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { NgReact } from '@spinnaker/core';
|
|
4
4
|
|
|
5
5
|
import { CpuCreditsToggle } from '../CpuCreditsToggle';
|
|
6
|
+
import { InstanceTypeWarning } from '../InstanceTypeWarning';
|
|
6
7
|
import { AWSProviderSettings } from '../../../../../aws.settings';
|
|
7
8
|
import type { IAmazonServerGroupCommand } from '../../../serverGroupConfiguration.service';
|
|
8
9
|
|
|
@@ -10,6 +11,7 @@ export interface ISimpleModeSelectorProps {
|
|
|
10
11
|
command: IAmazonServerGroupCommand;
|
|
11
12
|
setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
|
|
12
13
|
setFieldValue: (field: keyof IAmazonServerGroupCommand, value: any, shouldValidate?: boolean) => void;
|
|
14
|
+
clearWarnings: () => void;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export function SimpleModeSelector(props: ISimpleModeSelectorProps) {
|
|
@@ -41,6 +43,7 @@ export function SimpleModeSelector(props: ISimpleModeSelectorProps) {
|
|
|
41
43
|
onTypeChanged={instanceTypeChanged}
|
|
42
44
|
onProfileChanged={instanceProfileChanged}
|
|
43
45
|
/>
|
|
46
|
+
<InstanceTypeWarning dirty={command.viewState.dirty} clearWarnings={props.clearWarnings} />
|
|
44
47
|
<div style={{ padding: '0 15px' }}>
|
|
45
48
|
{command.viewState.instanceProfile && command.viewState.instanceProfile !== 'custom' && (
|
|
46
49
|
<InstanceTypeSelector command={command} onTypeChanged={instanceTypeChanged} />
|
|
@@ -98,6 +98,9 @@ export class ServerGroupBasicSettings
|
|
|
98
98
|
setFieldValue('associateIPv6Address', false);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
// an image change might clear the single or multiple instance types previously selected with the image, validate the form to surface related errors.
|
|
103
|
+
this.props.formik.validateForm();
|
|
101
104
|
};
|
|
102
105
|
|
|
103
106
|
private accountUpdated = (account: string): void => {
|
|
@@ -31,12 +31,22 @@ export class ServerGroupInstanceType
|
|
|
31
31
|
const errors: FormikErrors<IAmazonServerGroupCommand> = {};
|
|
32
32
|
|
|
33
33
|
if (values.viewState.useSimpleInstanceTypeSelector) {
|
|
34
|
-
|
|
34
|
+
// For the clone scenario, when values.instanceType is cleared for various reasons (see serverGroupConfiguration.service.ts),
|
|
35
|
+
// the previously selected instance type might be cleared/ unselected to fail-fast and this might look like unexpected behavior. But, there is always a reason when this happens.
|
|
36
|
+
// The error handling below adds transparency around what changed and why.
|
|
37
|
+
if (values.viewState.dirty.instanceType) {
|
|
38
|
+
errors.instanceType = 'You must confirm removed instance type and pick a compatible instance type.';
|
|
39
|
+
} else if (!values.instanceType) {
|
|
35
40
|
errors.instanceType = 'Instance Type required.';
|
|
36
41
|
}
|
|
37
42
|
} else {
|
|
38
|
-
const
|
|
39
|
-
|
|
43
|
+
const prevInstanceTypes = values.viewState.dirty.launchTemplateOverridesForInstanceType;
|
|
44
|
+
if (prevInstanceTypes && prevInstanceTypes.length > 0) {
|
|
45
|
+
errors.instanceType = 'You must confirm removed instance types and/or pick more instance types.';
|
|
46
|
+
} else {
|
|
47
|
+
const advancedModeErrors = this.validateAdvancedModeFields(values, errors);
|
|
48
|
+
Object.assign(errors, { ...advancedModeErrors });
|
|
49
|
+
}
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
return errors;
|
|
@@ -58,8 +58,12 @@ export class LaunchConfigDetailsSection extends React.Component<
|
|
|
58
58
|
<dt>IAM Profile</dt>
|
|
59
59
|
<dd>{launchConfig.iamInstanceProfile}</dd>
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
{launchConfig.instanceMonitoring && (
|
|
62
|
+
<>
|
|
63
|
+
<dt>Instance Monitoring</dt>
|
|
64
|
+
<dd>{launchConfig.instanceMonitoring.enabled ? 'enabled' : 'disabled'}</dd>
|
|
65
|
+
</>
|
|
66
|
+
)}
|
|
63
67
|
|
|
64
68
|
{launchConfig.spotPrice && <dt>Spot Price</dt>}
|
|
65
69
|
{launchConfig.spotPrice && <dd>{launchConfig.spotPrice}</dd>}
|
|
@@ -96,6 +96,10 @@ export class AwsServerGroupTransformer {
|
|
|
96
96
|
deployConfig.targetGroups = base.targetGroups || [];
|
|
97
97
|
deployConfig.account = deployConfig.credentials;
|
|
98
98
|
deployConfig.subnetType = deployConfig.subnetType || '';
|
|
99
|
+
deployConfig.instanceType =
|
|
100
|
+
deployConfig.instanceType ||
|
|
101
|
+
(deployConfig.launchTemplateOverridesForInstanceType &&
|
|
102
|
+
deployConfig.launchTemplateOverridesForInstanceType[0].instanceType); // populate instanceType for backwards compatibility
|
|
99
103
|
|
|
100
104
|
if (base.viewState.mode !== 'clone') {
|
|
101
105
|
delete deployConfig.source;
|