@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.
Files changed (30) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/image/image.reader.d.ts +1 -0
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/instance/awsInstanceType.service.d.ts +41 -1
  6. package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +4 -0
  7. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AmazonInstanceTypeInfoRenderer.d.ts +5 -0
  8. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.d.ts +2 -0
  9. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeSelect.d.ts +7 -0
  10. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.d.ts +2 -1
  11. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.d.ts +2 -0
  12. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.d.ts +3 -1
  13. package/package.json +5 -5
  14. package/src/help/amazon.help.ts +19 -1
  15. package/src/image/image.reader.ts +1 -0
  16. package/src/instance/awsInstanceType.service.spec.js +225 -32
  17. package/src/instance/awsInstanceType.service.ts +77 -21
  18. package/src/serverGroup/configure/AmazonImageSelectInput.tsx +1 -1
  19. package/src/serverGroup/configure/serverGroupConfiguration.service.ts +21 -10
  20. package/src/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.tsx +2 -2
  21. package/src/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.tsx +1 -1
  22. package/src/serverGroup/configure/wizard/instanceType/advancedMode/AmazonInstanceTypeInfoRenderer.tsx +59 -0
  23. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.tsx +15 -2
  24. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeSelect.tsx +78 -0
  25. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.tsx +6 -2
  26. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.tsx +10 -1
  27. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.tsx +16 -24
  28. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.tsx +4 -1
  29. package/src/serverGroup/configure/wizard/instanceType/advancedMode/advancedMode.less +13 -1
  30. 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<IInstanceTypesByRegion> {
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: IInstanceType) {
87
+ .map(function (type: IAmazonInstanceType) {
40
88
  return {
41
- region: type.region,
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(availableInstanceTypes: IInstanceTypesByRegion, selectedRegions: string[]) {
144
+ function getAvailableTypesForRegions(
145
+ availableInstanceTypes: IAmazonInstanceTypesByRegion,
146
+ selectedRegions: string[],
147
+ ): IAmazonInstanceType[] {
99
148
  selectedRegions = selectedRegions || [];
100
- let availableTypes: string[] = [];
149
+ let availableTypes: IAmazonInstanceType[] = [];
101
150
 
102
151
  // prime the list of available types
103
152
  if (selectedRegions && selectedRegions.length) {
104
- availableTypes = _.map(availableInstanceTypes[selectedRegions[0]], 'name');
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 = _.intersection(availableTypes, _.map(availableInstanceTypes[selectedRegion], 'name'));
159
+ availableTypes = _.intersectionBy(availableTypes, availableInstanceTypes[selectedRegion], 'name');
111
160
  }
112
161
  });
113
162
 
114
- return availableTypes.sort(sortTypesByFamilyAndSize);
163
+ return availableTypes?.sort((a, b) => sortTypesByFamilyAndSize(a.name, b.name));
115
164
  }
116
165
 
117
166
  const families: { [key: string]: string[] } = {
118
- paravirtual: ['c1', 'c3', 'hi1', 'hs1', 'm1', 'm2', 'm3', 't1'],
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(instanceTypes: string[], virtualizationType: string, vpcOnly: boolean): string[] {
126
- return instanceTypes.filter((instanceType: string) => {
127
- if (virtualizationType === '*') {
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
- const [family] = instanceType.split('.');
132
- if (!vpcOnly && families.vpcOnly.includes(family)) {
183
+
184
+ if (!vpcConfigured && !families.ec2ClassicSupported.includes(i.name.split('.')[0])) {
133
185
  return false;
134
186
  }
135
- if (!families.paravirtual.includes(family) && virtualizationType === 'hvm') {
136
- return true;
187
+ if (virtualizationType && !i.supportedVirtualizationTypes.includes(virtualizationType)) {
188
+ return false;
189
+ }
190
+ if (architecture && !i.supportedArchitectures.includes(architecture)) {
191
+ return false;
137
192
  }
138
- return families[virtualizationType].includes(family);
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
- instanceTypes,
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
- instanceTypes,
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 filtered = this.awsInstanceTypeService.getAvailableTypesForRegions(command.backingData.instanceTypes, [
377
- command.region,
378
- ]);
379
- if (command.virtualizationType) {
380
- filtered = this.awsInstanceTypeService.filterInstanceTypes(
381
- filtered,
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
- if (command.instanceType && !filtered.includes(command.instanceType)) {
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 = filtered;
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
- To configure a single instance type, use
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 with multiple instance types,</span>
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.instanceTypes) || []
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
- import { TextInput, Tooltip } from '@spinnaker/core';
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>{instanceType}</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: string[];
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
  />
@@ -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 { IAmazonPreferredInstanceType } from '../../../../../instance/awsInstanceType.service';
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}
@@ -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: string[];
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
- };
@@ -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 {...inputProps} />
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: 80%;
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