@spinnaker/amazon 0.12.12 → 0.13.1
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 +27 -0
- package/dist/image/image.reader.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/instance/awsInstanceType.service.d.ts +41 -1
- package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +4 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AmazonInstanceTypeInfoRenderer.d.ts +5 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeSelect.d.ts +7 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.d.ts +2 -1
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.d.ts +3 -1
- package/package.json +5 -5
- package/src/help/amazon.help.ts +19 -1
- package/src/image/image.reader.ts +1 -0
- package/src/instance/awsInstanceType.service.spec.js +225 -32
- package/src/instance/awsInstanceType.service.ts +77 -21
- package/src/serverGroup/configure/AmazonImageSelectInput.tsx +1 -1
- package/src/serverGroup/configure/serverGroupConfiguration.service.ts +21 -10
- package/src/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.tsx +2 -2
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.tsx +1 -1
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/AmazonInstanceTypeInfoRenderer.tsx +59 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.tsx +15 -2
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeSelect.tsx +78 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.tsx +6 -2
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.tsx +10 -1
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.tsx +16 -24
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.tsx +4 -1
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/advancedMode.less +13 -1
- package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.tsx +3 -0
|
@@ -21,6 +21,54 @@ export interface IAmazonInstanceTypeCategory extends IInstanceTypeCategory {
|
|
|
21
21
|
descriptionListOverride?: string[];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export interface IAmazonInstanceType extends IInstanceType {
|
|
25
|
+
defaultVCpus: number;
|
|
26
|
+
memoryInGiB: number;
|
|
27
|
+
hypervisor: string;
|
|
28
|
+
instanceStorageInfo?: IAmazonInstanceTypeStorageInfo;
|
|
29
|
+
ebsInfo?: IAmazonInstanceTypeEbsInfo;
|
|
30
|
+
gpuInfo?: IAmazonInstanceGpuInfo;
|
|
31
|
+
|
|
32
|
+
instanceStorageSupported: boolean;
|
|
33
|
+
currentGeneration: boolean;
|
|
34
|
+
bareMetal: boolean;
|
|
35
|
+
ipv6Supported?: boolean;
|
|
36
|
+
burstablePerformanceSupported: boolean;
|
|
37
|
+
|
|
38
|
+
supportedArchitectures: string[];
|
|
39
|
+
supportedUsageClasses: string[];
|
|
40
|
+
supportedRootDeviceTypes: string[];
|
|
41
|
+
supportedVirtualizationTypes: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface IAmazonInstanceTypeStorageInfo {
|
|
45
|
+
storageTypes: string;
|
|
46
|
+
totalSizeInGB?: number;
|
|
47
|
+
nvmeSupport?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface IAmazonInstanceTypeEbsInfo {
|
|
51
|
+
ebsOptimizedSupport: string;
|
|
52
|
+
nvmeSupport?: string;
|
|
53
|
+
encryptionSupport: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface IAmazonInstanceGpuInfo {
|
|
57
|
+
totalGpuMemoryInMiB: number;
|
|
58
|
+
gpus: IAmazonInstanceGpuDeviceInfo[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface IAmazonInstanceGpuDeviceInfo {
|
|
62
|
+
name: string;
|
|
63
|
+
manufacturer: string;
|
|
64
|
+
count: number;
|
|
65
|
+
gpuSizeInMiB: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface IAmazonInstanceTypesByRegion extends IInstanceTypesByRegion {
|
|
69
|
+
[region: string]: IAmazonInstanceType[];
|
|
70
|
+
}
|
|
71
|
+
|
|
24
72
|
export const AMAZON_INSTANCE_AWSINSTANCETYPE_SERVICE = 'spinnaker.amazon.instanceType.service';
|
|
25
73
|
export const name = AMAZON_INSTANCE_AWSINSTANCETYPE_SERVICE; // for backwards compatibility
|
|
26
74
|
|
|
@@ -31,16 +79,14 @@ module(AMAZON_INSTANCE_AWSINSTANCETYPE_SERVICE, []).factory('awsInstanceTypeServ
|
|
|
31
79
|
return $q.when(categories);
|
|
32
80
|
}
|
|
33
81
|
|
|
34
|
-
const getAllTypesByRegion = function getAllTypesByRegion(): PromiseLike<
|
|
82
|
+
const getAllTypesByRegion = function getAllTypesByRegion(): PromiseLike<IAmazonInstanceTypesByRegion> {
|
|
35
83
|
return REST('/instanceTypes')
|
|
36
84
|
.get()
|
|
37
85
|
.then(function (types) {
|
|
38
86
|
return _.chain(types)
|
|
39
|
-
.map(function (type:
|
|
87
|
+
.map(function (type: IAmazonInstanceType) {
|
|
40
88
|
return {
|
|
41
|
-
|
|
42
|
-
account: type.account,
|
|
43
|
-
name: type.name,
|
|
89
|
+
...type,
|
|
44
90
|
key: [type.region, type.account, type.name].join(':'),
|
|
45
91
|
};
|
|
46
92
|
})
|
|
@@ -95,47 +141,57 @@ module(AMAZON_INSTANCE_AWSINSTANCETYPE_SERVICE, []).factory('awsInstanceTypeServ
|
|
|
95
141
|
return 0;
|
|
96
142
|
}
|
|
97
143
|
|
|
98
|
-
function getAvailableTypesForRegions(
|
|
144
|
+
function getAvailableTypesForRegions(
|
|
145
|
+
availableInstanceTypes: IAmazonInstanceTypesByRegion,
|
|
146
|
+
selectedRegions: string[],
|
|
147
|
+
): IAmazonInstanceType[] {
|
|
99
148
|
selectedRegions = selectedRegions || [];
|
|
100
|
-
let availableTypes:
|
|
149
|
+
let availableTypes: IAmazonInstanceType[] = [];
|
|
101
150
|
|
|
102
151
|
// prime the list of available types
|
|
103
152
|
if (selectedRegions && selectedRegions.length) {
|
|
104
|
-
availableTypes =
|
|
153
|
+
availableTypes = availableInstanceTypes[selectedRegions[0]] || [];
|
|
105
154
|
}
|
|
106
155
|
|
|
107
156
|
// this will perform an unnecessary intersection with the first region, which is fine
|
|
108
157
|
selectedRegions.forEach(function (selectedRegion) {
|
|
109
158
|
if (availableInstanceTypes[selectedRegion]) {
|
|
110
|
-
availableTypes = _.
|
|
159
|
+
availableTypes = _.intersectionBy(availableTypes, availableInstanceTypes[selectedRegion], 'name');
|
|
111
160
|
}
|
|
112
161
|
});
|
|
113
162
|
|
|
114
|
-
return availableTypes
|
|
163
|
+
return availableTypes?.sort((a, b) => sortTypesByFamilyAndSize(a.name, b.name));
|
|
115
164
|
}
|
|
116
165
|
|
|
117
166
|
const families: { [key: string]: string[] } = {
|
|
118
|
-
|
|
119
|
-
hvm: ['c3', 'c4', 'd2', 'i2', 'g2', 'm3', 'm4', 'm5', 'p2', 'r3', 'r4', 'r5', 't2', 'x1'],
|
|
120
|
-
vpcOnly: ['c4', 'm4', 'm5', 'r4', 'r5', 't2', 'x1'],
|
|
167
|
+
ec2ClassicSupported: ['m1', 'm3', 't1', 'c1', 'c3', 'cc2', 'cr1', 'm2', 'r3', 'd2', 'hs1', 'i2', 'g2'], // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-classic-platform.html#ec2-classic-instance-types
|
|
121
168
|
ebsOptimized: ['c4', 'd2', 'f1', 'g3', 'i3', 'm4', 'm5', 'p2', 'r4', 'r5', 'x1'],
|
|
122
169
|
burstablePerf: ['t2', 't3', 't3a', 't4g'],
|
|
123
170
|
};
|
|
124
171
|
|
|
125
|
-
function filterInstanceTypes(
|
|
126
|
-
|
|
127
|
-
|
|
172
|
+
function filterInstanceTypes(
|
|
173
|
+
instanceTypes: IAmazonInstanceType[],
|
|
174
|
+
virtualizationType: string,
|
|
175
|
+
vpcConfigured: boolean,
|
|
176
|
+
architecture: string,
|
|
177
|
+
): IAmazonInstanceType[] {
|
|
178
|
+
return _.filter(instanceTypes, function (i) {
|
|
179
|
+
if (virtualizationType === '*' && architecture === '*') {
|
|
128
180
|
// show all instance types
|
|
129
181
|
return true;
|
|
130
182
|
}
|
|
131
|
-
|
|
132
|
-
if (!
|
|
183
|
+
|
|
184
|
+
if (!vpcConfigured && !families.ec2ClassicSupported.includes(i.name.split('.')[0])) {
|
|
133
185
|
return false;
|
|
134
186
|
}
|
|
135
|
-
if (!
|
|
136
|
-
return
|
|
187
|
+
if (virtualizationType && !i.supportedVirtualizationTypes.includes(virtualizationType)) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (architecture && !i.supportedArchitectures.includes(architecture)) {
|
|
191
|
+
return false;
|
|
137
192
|
}
|
|
138
|
-
|
|
193
|
+
|
|
194
|
+
return true;
|
|
139
195
|
});
|
|
140
196
|
}
|
|
141
197
|
|
|
@@ -59,7 +59,7 @@ export class AmazonImageSelectInput extends React.Component<IAmazonImageSelector
|
|
|
59
59
|
|
|
60
60
|
// assume that the specific image exists in the selected region
|
|
61
61
|
const amis = { [region]: [imageId] };
|
|
62
|
-
const attributes = { virtualizationType: '*', creationDate: new Date().toISOString() };
|
|
62
|
+
const attributes = { virtualizationType: '*', architecture: '*', creationDate: new Date().toISOString() };
|
|
63
63
|
|
|
64
64
|
return { imageName, amis, attributes } as IAmazonImage;
|
|
65
65
|
}
|
|
@@ -54,6 +54,7 @@ import type {
|
|
|
54
54
|
IScalingProcess,
|
|
55
55
|
} from '../../domain';
|
|
56
56
|
import { AMAZON_INSTANCE_AWSINSTANCETYPE_SERVICE } from '../../instance/awsInstanceType.service';
|
|
57
|
+
import type { IAmazonInstanceType } from '../../instance/awsInstanceType.service';
|
|
57
58
|
import { KeyPairsReader } from '../../keyPairs';
|
|
58
59
|
|
|
59
60
|
export type IBlockDeviceMappingSource = 'source' | 'ami' | 'default';
|
|
@@ -69,6 +70,7 @@ export interface IAmazonServerGroupCommandResult extends IServerGroupCommandResu
|
|
|
69
70
|
export interface IAmazonServerGroupCommandBackingDataFiltered extends IServerGroupCommandBackingDataFiltered {
|
|
70
71
|
keyPairs: string[];
|
|
71
72
|
targetGroups: string[];
|
|
73
|
+
instanceTypesInfo: IAmazonInstanceType[];
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export interface IAmazonServerGroupCommandBackingData extends IServerGroupCommandBackingData {
|
|
@@ -77,6 +79,7 @@ export interface IAmazonServerGroupCommandBackingData extends IServerGroupComman
|
|
|
77
79
|
keyPairs: IKeyPair[];
|
|
78
80
|
targetGroups: string[];
|
|
79
81
|
scalingProcesses: IScalingProcess[];
|
|
82
|
+
instanceTypesInfo: IAmazonInstanceType[];
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
export interface IAmazonServerGroupCommandViewState extends IServerGroupCommandViewState {
|
|
@@ -145,6 +148,7 @@ export interface IAmazonServerGroupCommand extends IServerGroupCommand {
|
|
|
145
148
|
spotAllocationStrategy?: string;
|
|
146
149
|
spotInstancePools?: number;
|
|
147
150
|
launchTemplateOverridesForInstanceType?: IAmazonInstanceTypeOverride[];
|
|
151
|
+
amiArchitecture: string;
|
|
148
152
|
|
|
149
153
|
getBlockDeviceMappingsSource: (command: IServerGroupCommand) => IBlockDeviceMappingSource;
|
|
150
154
|
selectBlockDeviceMappingsSource: (command: IServerGroupCommand, selection: string) => void;
|
|
@@ -274,7 +278,7 @@ export class AwsServerGroupConfigurationService {
|
|
|
274
278
|
subnets,
|
|
275
279
|
preferredZones,
|
|
276
280
|
keyPairs,
|
|
277
|
-
|
|
281
|
+
instanceTypesInfo,
|
|
278
282
|
enabledMetrics,
|
|
279
283
|
healthCheckTypes,
|
|
280
284
|
terminationPolicies,
|
|
@@ -285,7 +289,7 @@ export class AwsServerGroupConfigurationService {
|
|
|
285
289
|
subnets,
|
|
286
290
|
preferredZones,
|
|
287
291
|
keyPairs,
|
|
288
|
-
|
|
292
|
+
instanceTypesInfo,
|
|
289
293
|
enabledMetrics,
|
|
290
294
|
healthCheckTypes,
|
|
291
295
|
terminationPolicies,
|
|
@@ -373,23 +377,29 @@ export class AwsServerGroupConfigurationService {
|
|
|
373
377
|
public configureInstanceTypes(command: IAmazonServerGroupCommand): IServerGroupCommandResult {
|
|
374
378
|
const result: IAmazonServerGroupCommandResult = { dirty: {} };
|
|
375
379
|
if (command.region && (command.virtualizationType || command.viewState.disableImageSelection)) {
|
|
376
|
-
let
|
|
377
|
-
command.
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
380
|
+
let filteredTypesInfo: IAmazonInstanceType[] = this.awsInstanceTypeService.getAvailableTypesForRegions(
|
|
381
|
+
command.backingData.instanceTypesInfo,
|
|
382
|
+
[command.region],
|
|
383
|
+
);
|
|
384
|
+
if (command.virtualizationType || command.amiArchitecture) {
|
|
385
|
+
filteredTypesInfo = this.awsInstanceTypeService.filterInstanceTypes(
|
|
386
|
+
filteredTypesInfo,
|
|
382
387
|
command.virtualizationType,
|
|
383
388
|
!!command.vpcId,
|
|
389
|
+
command.amiArchitecture,
|
|
384
390
|
);
|
|
385
391
|
}
|
|
386
|
-
|
|
392
|
+
|
|
393
|
+
const filteredTypes: string[] = map(filteredTypesInfo, 'name');
|
|
394
|
+
if (command.instanceType && !filteredTypes.includes(command.instanceType)) {
|
|
387
395
|
result.dirty.instanceType = command.instanceType;
|
|
388
396
|
command.instanceType = null;
|
|
389
397
|
}
|
|
390
|
-
command.backingData.filtered.instanceTypes =
|
|
398
|
+
command.backingData.filtered.instanceTypes = filteredTypes;
|
|
399
|
+
command.backingData.filtered.instanceTypesInfo = filteredTypesInfo;
|
|
391
400
|
} else {
|
|
392
401
|
command.backingData.filtered.instanceTypes = [];
|
|
402
|
+
command.backingData.filtered.instanceTypesInfo = [];
|
|
393
403
|
}
|
|
394
404
|
extend(command.viewState.dirty, result.dirty);
|
|
395
405
|
return result;
|
|
@@ -399,6 +409,7 @@ export class AwsServerGroupConfigurationService {
|
|
|
399
409
|
const result: IAmazonServerGroupCommandResult = { dirty: {} };
|
|
400
410
|
if (!command.amiName) {
|
|
401
411
|
command.virtualizationType = null;
|
|
412
|
+
command.amiArchitecture = null;
|
|
402
413
|
}
|
|
403
414
|
if (command.viewState.disableImageSelection) {
|
|
404
415
|
return result;
|
|
@@ -71,7 +71,7 @@ export function InstanceTypeSelector(props: IInstanceTypeSelectorProps) {
|
|
|
71
71
|
<div className="container-fluid form-horizontal" style={{ padding: '0 15px' }}>
|
|
72
72
|
<div>
|
|
73
73
|
<p>
|
|
74
|
-
|
|
74
|
+
Switch to
|
|
75
75
|
<a className="clickable" onClick={() => handleModeChange(true)}>
|
|
76
76
|
<span> Simple Mode</span>
|
|
77
77
|
</a>
|
|
@@ -94,7 +94,7 @@ export function InstanceTypeSelector(props: IInstanceTypeSelectorProps) {
|
|
|
94
94
|
return (
|
|
95
95
|
<div className="container-fluid form-horizontal" style={{ padding: '0 15px' }}>
|
|
96
96
|
<div>
|
|
97
|
-
<span>To configure mixed server groups
|
|
97
|
+
<span>To configure mixed server groups and/or multiple instance types,</span>
|
|
98
98
|
{!isLaunchTemplatesEnabled && (
|
|
99
99
|
<span>
|
|
100
100
|
<a
|
|
@@ -89,7 +89,7 @@ export function AdvancedModeSelector(props: IAdvancedModeSelectorProps) {
|
|
|
89
89
|
unlimitedCpuCreditsInCmd={command.unlimitedCpuCredits}
|
|
90
90
|
profileDetails={instanceTypeDetails.find((p) => p.type === instanceProfile)}
|
|
91
91
|
availableInstanceTypesList={
|
|
92
|
-
(command.backingData && command.backingData.filtered && command.backingData.filtered.
|
|
92
|
+
(command.backingData && command.backingData.filtered && command.backingData.filtered.instanceTypesInfo) || []
|
|
93
93
|
}
|
|
94
94
|
handleInstanceTypesChange={handleInstanceTypesChange}
|
|
95
95
|
setUnlimitedCpuCredits={setUnlimitedCpuCredits}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { IAmazonInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
5
|
+
|
|
6
|
+
export function AmazonInstanceTypeInfoRenderer(props: { instanceType: IAmazonInstanceType }) {
|
|
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
|
+
<span>
|
|
11
|
+
<br />
|
|
12
|
+
<span className={`select-option-label-attributes`}>
|
|
13
|
+
{`Instance Storage: ${_.toUpper(props.instanceType.instanceStorageInfo.storageTypes)} | ${
|
|
14
|
+
props.instanceType.instanceStorageInfo.totalSizeInGB
|
|
15
|
+
} Gib total size`}
|
|
16
|
+
</span>
|
|
17
|
+
</span>
|
|
18
|
+
);
|
|
19
|
+
const ebsInfo = props.instanceType.ebsInfo && (
|
|
20
|
+
<span>
|
|
21
|
+
<br />
|
|
22
|
+
<span className={`select-option-label-attributes`}>
|
|
23
|
+
{`EBS: optimization ${props.instanceType.ebsInfo.ebsOptimizedSupport} | NVMe ${props.instanceType.ebsInfo.nvmeSupport} | Encryption ${props.instanceType.ebsInfo.encryptionSupport}`}
|
|
24
|
+
</span>
|
|
25
|
+
</span>
|
|
26
|
+
);
|
|
27
|
+
const gpuInfo = props.instanceType.gpuInfo && (
|
|
28
|
+
<span>
|
|
29
|
+
<br />
|
|
30
|
+
<span className={`select-option-label-attributes`}>
|
|
31
|
+
{`GPU: ${props.instanceType.gpuInfo.totalGpuMemoryInMiB} MiB total memory`}
|
|
32
|
+
{props.instanceType.gpuInfo.gpus.map(
|
|
33
|
+
(g) => ` | ${g.count} ${g.manufacturer} ${g.name} GPUs, size: ${g.gpuSizeInMiB} MiB`,
|
|
34
|
+
)}
|
|
35
|
+
</span>
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
38
|
+
const generationInfo = typeof props.instanceType.currentGeneration !== 'undefined' &&
|
|
39
|
+
props.instanceType.currentGeneration !== null && (
|
|
40
|
+
<span>
|
|
41
|
+
<br />
|
|
42
|
+
<span className={`select-option-label-attributes`}>
|
|
43
|
+
{props.instanceType.currentGeneration ? 'Current Generation' : 'Previous Generation'}
|
|
44
|
+
</span>
|
|
45
|
+
</span>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<span>
|
|
50
|
+
<span className={`select-option-label`}>{props.instanceType.name}</span>
|
|
51
|
+
<br />
|
|
52
|
+
<span className={`select-option-label-attributes`}>{CpuMem}</span>
|
|
53
|
+
{instanceStorageInfo}
|
|
54
|
+
{ebsInfo}
|
|
55
|
+
{gpuInfo}
|
|
56
|
+
{generationInfo}
|
|
57
|
+
</span>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { Checkbox } from 'react-bootstrap';
|
|
3
3
|
import { SortableHandle } from 'react-sortable-hoc';
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import { HoverablePopover, TextInput, Tooltip } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import { AmazonInstanceTypeInfoRenderer } from './AmazonInstanceTypeInfoRenderer';
|
|
5
8
|
import type { IAmazonPreferredInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
9
|
+
import type { IAmazonInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
6
10
|
import { CostFactor } from '../../../../../instance/details/CostFactor';
|
|
7
11
|
import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
|
|
8
12
|
|
|
9
13
|
export interface IRowProps {
|
|
10
14
|
isCustom: boolean;
|
|
11
15
|
selectedType?: IAmazonInstanceTypeOverride;
|
|
16
|
+
selectedTypeInfo?: IAmazonInstanceType;
|
|
12
17
|
instanceTypeDetails?: IAmazonPreferredInstanceType;
|
|
13
18
|
removeInstanceType?: (typeToRemove: string) => void;
|
|
14
19
|
addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
|
|
@@ -38,12 +43,20 @@ export function InstanceTypeRow(props: IRowProps) {
|
|
|
38
43
|
|
|
39
44
|
let row;
|
|
40
45
|
if (props.isCustom) {
|
|
46
|
+
const instanceTypeInfo = <AmazonInstanceTypeInfoRenderer instanceType={props.selectedTypeInfo} />;
|
|
41
47
|
row = (
|
|
42
48
|
<tr key={instanceType} className={'sortable clickable'}>
|
|
43
49
|
<td>
|
|
44
50
|
<DragHandle />
|
|
45
51
|
</td>
|
|
46
|
-
<td>
|
|
52
|
+
<td>
|
|
53
|
+
{`${instanceType} `}
|
|
54
|
+
<HoverablePopover placement={'right'} template={instanceTypeInfo} className={'custom-profile'}>
|
|
55
|
+
<span className="clickable help-field">
|
|
56
|
+
<i className="small glyphicon glyphicon-info-sign" />
|
|
57
|
+
</span>
|
|
58
|
+
</HoverablePopover>
|
|
59
|
+
</td>
|
|
47
60
|
<td title={'Enter optional weight (allowed values: 1 to 999).'}>
|
|
48
61
|
<TextInput
|
|
49
62
|
className={'form-control input input-sm'}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { Option } from 'react-select';
|
|
4
|
+
import Select from 'react-select';
|
|
5
|
+
|
|
6
|
+
import { HelpField } from '@spinnaker/core';
|
|
7
|
+
|
|
8
|
+
import { AmazonInstanceTypeInfoRenderer } from './AmazonInstanceTypeInfoRenderer';
|
|
9
|
+
import type { IAmazonInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
10
|
+
|
|
11
|
+
export interface InstanceTypeSelectProps {
|
|
12
|
+
availableInstanceTypesList: IAmazonInstanceType[];
|
|
13
|
+
addOrUpdateInstanceType: (type: string, weight: string) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function InstanceTypeSelect(props: InstanceTypeSelectProps) {
|
|
17
|
+
/**
|
|
18
|
+
* Filters implemented:
|
|
19
|
+
* - instance family / size / type e.g. c3/ large/ c3.large
|
|
20
|
+
* - min vcpu e.g. 16vcpu
|
|
21
|
+
* - min memory e.g 32gib
|
|
22
|
+
* - instance storage type e.g. ssd, hdd
|
|
23
|
+
* - spot support
|
|
24
|
+
* - ebs support
|
|
25
|
+
* - gpu support
|
|
26
|
+
* - current generation v/s old generation like 'currentGen' / 'oldGen'
|
|
27
|
+
*/
|
|
28
|
+
const filterOption = (option: IAmazonInstanceType, inputValue: string) => {
|
|
29
|
+
const {
|
|
30
|
+
name,
|
|
31
|
+
defaultVCpus,
|
|
32
|
+
memoryInGiB,
|
|
33
|
+
instanceStorageInfo,
|
|
34
|
+
ebsInfo,
|
|
35
|
+
gpuInfo,
|
|
36
|
+
supportedUsageClasses,
|
|
37
|
+
currentGeneration,
|
|
38
|
+
} = option;
|
|
39
|
+
|
|
40
|
+
return inputValue.split(',').every((inputVal: string) => {
|
|
41
|
+
const searchVal = _.toLower(inputVal.trim());
|
|
42
|
+
return (
|
|
43
|
+
(searchVal.match(/\d+\s*vcpu/) && defaultVCpus >= _.toNumber(searchVal.split('vcpu')[0].trim())) ||
|
|
44
|
+
(searchVal.match(/\d+\s*gib/) && memoryInGiB >= _.toNumber(searchVal.split('gib')[0].trim())) ||
|
|
45
|
+
(searchVal.match(/\b(ssd|hdd)\b/) && instanceStorageInfo?.storageTypes.includes(searchVal)) ||
|
|
46
|
+
(searchVal === 'spot' && supportedUsageClasses?.includes(searchVal)) ||
|
|
47
|
+
(searchVal === 'ebs' && ebsInfo) ||
|
|
48
|
+
(searchVal === 'gpu' && gpuInfo) ||
|
|
49
|
+
(searchVal === 'currentgen' && currentGeneration) ||
|
|
50
|
+
(searchVal === 'oldgen' && !currentGeneration) ||
|
|
51
|
+
name.match(searchVal)
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const optionRenderer = (option: IAmazonInstanceType) => {
|
|
57
|
+
return <AmazonInstanceTypeInfoRenderer instanceType={option} />;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className={'custom-profile'}>
|
|
62
|
+
<Select
|
|
63
|
+
className={`select`}
|
|
64
|
+
clearable={false}
|
|
65
|
+
multi={false}
|
|
66
|
+
placeholder={'Filter like 16vcpu, 32gib, spot, oldGen or Select an instance type to add'}
|
|
67
|
+
removeSelected={true}
|
|
68
|
+
searchable={true}
|
|
69
|
+
options={props.availableInstanceTypesList}
|
|
70
|
+
optionRenderer={optionRenderer}
|
|
71
|
+
filterOption={filterOption}
|
|
72
|
+
valueRenderer={(o) => <>{o.name}</>}
|
|
73
|
+
onChange={(o: Option<IAmazonInstanceType>) => props.addOrUpdateInstanceType(o.name, undefined)}
|
|
74
|
+
/>{' '}
|
|
75
|
+
<HelpField id="aws.serverGroup.instanceTypesSelect" />
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -8,6 +8,7 @@ import { Header, Heading } from './InstanceTypeTableParts';
|
|
|
8
8
|
import { Footer } from './InstanceTypeTableParts';
|
|
9
9
|
import { AWSProviderSettings } from '../../../../../aws.settings';
|
|
10
10
|
import type { IAmazonInstanceTypeCategory } from '../../../../../instance/awsInstanceType.service';
|
|
11
|
+
import type { IAmazonInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
11
12
|
import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
|
|
12
13
|
|
|
13
14
|
import './advancedMode.less';
|
|
@@ -17,7 +18,7 @@ export interface IInstanceTypeTableProps {
|
|
|
17
18
|
selectedInstanceTypesMap: Map<string, IAmazonInstanceTypeOverride>;
|
|
18
19
|
unlimitedCpuCreditsInCmd: boolean;
|
|
19
20
|
profileDetails: IAmazonInstanceTypeCategory;
|
|
20
|
-
availableInstanceTypesList:
|
|
21
|
+
availableInstanceTypesList: IAmazonInstanceType[];
|
|
21
22
|
handleInstanceTypesChange: (instanceTypes: IAmazonInstanceTypeOverride[]) => void;
|
|
22
23
|
setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
|
|
23
24
|
}
|
|
@@ -120,6 +121,9 @@ export function InstanceTypeTable(props: IInstanceTypeTableProps) {
|
|
|
120
121
|
<InstanceTypeTableBody
|
|
121
122
|
isCustom={isCustom}
|
|
122
123
|
selectedInstanceTypesMap={selectedInstanceTypesMap}
|
|
124
|
+
selectedInstanceTypesInfo={props.availableInstanceTypesList.filter((it) =>
|
|
125
|
+
selectedInstanceTypeNames.includes(it.name),
|
|
126
|
+
)}
|
|
123
127
|
addOrUpdateInstanceType={addOrUpdateInstanceType}
|
|
124
128
|
removeInstanceType={removeInstanceType}
|
|
125
129
|
handleSortEnd={handleSortEnd}
|
|
@@ -127,7 +131,7 @@ export function InstanceTypeTable(props: IInstanceTypeTableProps) {
|
|
|
127
131
|
<Footer
|
|
128
132
|
isCustom={isCustom}
|
|
129
133
|
availableInstanceTypesList={props.availableInstanceTypesList.filter(
|
|
130
|
-
(it) => !selectedInstanceTypeNames.includes(it),
|
|
134
|
+
(it) => !selectedInstanceTypeNames.includes(it.name),
|
|
131
135
|
)}
|
|
132
136
|
addOrUpdateInstanceType={addOrUpdateInstanceType}
|
|
133
137
|
/>
|
package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.tsx
CHANGED
|
@@ -6,13 +6,17 @@ import { SortableContainer, SortableElement } from 'react-sortable-hoc';
|
|
|
6
6
|
import type { IInstanceTypeFamily } from '@spinnaker/core';
|
|
7
7
|
|
|
8
8
|
import { InstanceTypeRow } from './InstanceTypeRow';
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
IAmazonInstanceType,
|
|
11
|
+
IAmazonPreferredInstanceType,
|
|
12
|
+
} from '../../../../../instance/awsInstanceType.service';
|
|
10
13
|
import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
|
|
11
14
|
|
|
12
15
|
export function InstanceTypeTableBody(props: {
|
|
13
16
|
isCustom: boolean;
|
|
14
17
|
profileFamiliesDetails?: IInstanceTypeFamily[];
|
|
15
18
|
selectedInstanceTypesMap: Map<string, IAmazonInstanceTypeOverride>;
|
|
19
|
+
selectedInstanceTypesInfo?: IAmazonInstanceType[];
|
|
16
20
|
addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
|
|
17
21
|
removeInstanceType: (instanceType: string) => void;
|
|
18
22
|
handleSortEnd: (sortEnd: SortEnd) => void;
|
|
@@ -21,6 +25,7 @@ export function InstanceTypeTableBody(props: {
|
|
|
21
25
|
<TableRows
|
|
22
26
|
isCustom={props.isCustom}
|
|
23
27
|
selectedInstanceTypesMap={props.selectedInstanceTypesMap}
|
|
28
|
+
selectedInstanceTypesInfo={props.selectedInstanceTypesInfo}
|
|
24
29
|
removeInstanceType={props.removeInstanceType}
|
|
25
30
|
addOrUpdateInstanceType={props.addOrUpdateInstanceType}
|
|
26
31
|
instanceTypeDetails={
|
|
@@ -43,6 +48,7 @@ const TableRows = SortableContainer(
|
|
|
43
48
|
isCustom: boolean;
|
|
44
49
|
instanceTypeDetails?: Map<string, IAmazonPreferredInstanceType>;
|
|
45
50
|
selectedInstanceTypesMap: Map<string, IAmazonInstanceTypeOverride>;
|
|
51
|
+
selectedInstanceTypesInfo?: IAmazonInstanceType[];
|
|
46
52
|
removeInstanceType: (typeToRemove: string) => void;
|
|
47
53
|
addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
|
|
48
54
|
}) => {
|
|
@@ -60,6 +66,7 @@ const TableRows = SortableContainer(
|
|
|
60
66
|
index={index}
|
|
61
67
|
isCustom={true}
|
|
62
68
|
selectedType={selectedType}
|
|
69
|
+
selectedTypeInfo={props.selectedInstanceTypesInfo.find((it) => it.name === selectedType.instanceType)}
|
|
63
70
|
removeInstanceType={props.removeInstanceType}
|
|
64
71
|
addOrUpdateInstanceType={props.addOrUpdateInstanceType}
|
|
65
72
|
/>
|
|
@@ -119,6 +126,7 @@ const SortableRow = SortableElement(
|
|
|
119
126
|
(props: {
|
|
120
127
|
isCustom: boolean;
|
|
121
128
|
selectedType: IAmazonInstanceTypeOverride;
|
|
129
|
+
selectedTypeInfo?: IAmazonInstanceType;
|
|
122
130
|
instanceTypeDetails?: Map<string, IAmazonPreferredInstanceType>;
|
|
123
131
|
removeInstanceType: (typeToRemove: string) => void;
|
|
124
132
|
addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
|
|
@@ -127,6 +135,7 @@ const SortableRow = SortableElement(
|
|
|
127
135
|
key={props.selectedType.instanceType}
|
|
128
136
|
isCustom={props.isCustom}
|
|
129
137
|
selectedType={props.selectedType}
|
|
138
|
+
selectedTypeInfo={props.selectedTypeInfo}
|
|
130
139
|
instanceTypeDetails={!props.isCustom ? props.instanceTypeDetails.get(props.selectedType.instanceType) : null}
|
|
131
140
|
removeInstanceType={props.removeInstanceType}
|
|
132
141
|
addOrUpdateInstanceType={props.addOrUpdateInstanceType}
|
package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.tsx
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { Option } from 'react-select';
|
|
3
|
-
import Select from 'react-select';
|
|
4
2
|
|
|
5
3
|
import { HelpField } from '@spinnaker/core';
|
|
6
4
|
|
|
5
|
+
import { InstanceTypeSelect } from './InstanceTypeSelect';
|
|
6
|
+
import type { IAmazonInstanceType } from '../../../../../instance/awsInstanceType.service';
|
|
7
|
+
|
|
8
|
+
import './advancedMode.less';
|
|
9
|
+
|
|
7
10
|
export function Heading(props: { isCustom: boolean; profileLabel?: string; profileDescriptionArr?: string[] }) {
|
|
8
11
|
let description;
|
|
9
12
|
if (props.isCustom) {
|
|
@@ -28,6 +31,16 @@ export function Heading(props: { isCustom: boolean; profileLabel?: string; profi
|
|
|
28
31
|
<h4>Instance Types</h4>
|
|
29
32
|
<div className={'description'}>
|
|
30
33
|
{description}
|
|
34
|
+
<p>
|
|
35
|
+
Learn about AWS recommended best practices in{' '}
|
|
36
|
+
<a
|
|
37
|
+
target="_blank"
|
|
38
|
+
href="https://spinnaker.io/docs/setup/other_config/server-group-launch-settings/aws-ec2/launch-templates/#create-a-server-group-with-aws-recommended-best-practices-for-ec2-spot"
|
|
39
|
+
>
|
|
40
|
+
examples and docs
|
|
41
|
+
</a>
|
|
42
|
+
.
|
|
43
|
+
</p>
|
|
31
44
|
<i>
|
|
32
45
|
<b>Note:</b>
|
|
33
46
|
<ul>
|
|
@@ -94,7 +107,7 @@ export function Header(props: { isCustom: boolean; showCpuCredits?: boolean }) {
|
|
|
94
107
|
|
|
95
108
|
export function Footer(props: {
|
|
96
109
|
isCustom: boolean;
|
|
97
|
-
availableInstanceTypesList:
|
|
110
|
+
availableInstanceTypesList: IAmazonInstanceType[];
|
|
98
111
|
addOrUpdateInstanceType: (type: string, weight: string) => void;
|
|
99
112
|
}) {
|
|
100
113
|
return props.isCustom ? (
|
|
@@ -114,24 +127,3 @@ export function Footer(props: {
|
|
|
114
127
|
</tfoot>
|
|
115
128
|
) : null;
|
|
116
129
|
}
|
|
117
|
-
|
|
118
|
-
const InstanceTypeSelect = (props: {
|
|
119
|
-
availableInstanceTypesList: string[];
|
|
120
|
-
addOrUpdateInstanceType: (type: string, weight: string) => void;
|
|
121
|
-
}): JSX.Element => {
|
|
122
|
-
const instanceTypeListOptions = props.availableInstanceTypesList.map((instanceType) => {
|
|
123
|
-
return { label: instanceType, value: instanceType };
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<Select
|
|
128
|
-
clearable={false}
|
|
129
|
-
multi={false}
|
|
130
|
-
placeholder={'Select an instance type to add...'}
|
|
131
|
-
removeSelected={true}
|
|
132
|
-
searchable={true}
|
|
133
|
-
options={instanceTypeListOptions}
|
|
134
|
-
onChange={(o: Option<string>) => props.addOrUpdateInstanceType(o.value, undefined)}
|
|
135
|
-
/>
|
|
136
|
-
);
|
|
137
|
-
};
|
package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.tsx
CHANGED
|
@@ -99,7 +99,10 @@ export function InstancesDistribution(props: IInstancesDistributionProps) {
|
|
|
99
99
|
help={<HelpField id={'aws.serverGroup.spotMaxPrice'} />}
|
|
100
100
|
input={(inputProps) => (
|
|
101
101
|
<Tooltip value={'Recommended to leave empty and use AWS default i.e. On-Demand price'}>
|
|
102
|
-
<TextInput
|
|
102
|
+
<TextInput
|
|
103
|
+
{...inputProps}
|
|
104
|
+
placeholder={'Recommended to leave empty and use AWS default i.e. On-Demand price'}
|
|
105
|
+
/>
|
|
103
106
|
</Tooltip>
|
|
104
107
|
)}
|
|
105
108
|
/>
|
|
@@ -47,7 +47,19 @@
|
|
|
47
47
|
|
|
48
48
|
.custom-profile {
|
|
49
49
|
.select {
|
|
50
|
-
width:
|
|
50
|
+
width: 97%;
|
|
51
|
+
display: inline-flex;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.select-option-label {
|
|
55
|
+
font-size: 100%;
|
|
56
|
+
font-weight: bold;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.select-option-label-attributes {
|
|
60
|
+
font-size: 90%;
|
|
61
|
+
font-weight: normal;
|
|
62
|
+
font-style: italic;
|
|
51
63
|
}
|
|
52
64
|
}
|
|
53
65
|
|