@spinnaker/amazon 0.11.0 → 0.12.2

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 (116) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/aws.settings.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 +10 -2
  6. package/dist/instance/awsInstanceTypes.d.ts +10 -0
  7. package/dist/instance/details/CostFactor.d.ts +6 -0
  8. package/dist/search/searchResultFormatter.d.ts +2 -2
  9. package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +14 -1
  10. package/dist/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.d.ts +3 -4
  11. package/dist/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.d.ts +9 -0
  12. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.d.ts +13 -0
  13. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.d.ts +9 -0
  14. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.d.ts +11 -0
  15. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.d.ts +14 -0
  16. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.d.ts +12 -0
  17. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.d.ts +15 -0
  18. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.d.ts +8 -0
  19. package/dist/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.d.ts +8 -0
  20. package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +9 -6
  21. package/dist/serverGroup/details/scalingPolicy/ScalingPolicySummary.d.ts +0 -1
  22. package/dist/serverGroup/details/scalingPolicy/chart/MetricAlarmChart.d.ts +0 -4
  23. package/dist/serverGroup/details/scalingPolicy/index.d.ts +0 -1
  24. package/dist/serverGroup/details/scalingPolicy/targetTracking/TargetTrackingChart.d.ts +1 -3
  25. package/package.json +5 -5
  26. package/src/aws.module.ts +3 -3
  27. package/src/aws.settings.ts +2 -0
  28. package/src/function/details/FunctionActions.spec.tsx +15 -12
  29. package/src/function/details/FunctionActions.tsx +2 -1
  30. package/src/help/amazon.help.ts +9 -6
  31. package/src/image/image.reader.spec.ts +75 -0
  32. package/src/instance/awsInstanceType.service.spec.js +37 -71
  33. package/src/instance/awsInstanceType.service.ts +191 -0
  34. package/src/instance/awsInstanceTypes.ts +311 -0
  35. package/src/instance/details/CostFactor.tsx +29 -0
  36. package/src/instance/details/InstanceInformation.spec.tsx +2 -1
  37. package/src/instance/details/instance.details.controller.js +472 -473
  38. package/src/loadBalancer/details/LoadBalancerActions.tsx +2 -1
  39. package/src/loadBalancer/details/loadBalancerDetails.controller.spec.ts +5 -3
  40. package/src/search/{searchResultFormatter.js → searchResultFormatter.ts} +5 -4
  41. package/src/securityGroup/details/securityGroupDetail.controller.js +2 -1
  42. package/src/serverGroup/configure/AmazonImageSelectInput.spec.tsx +10 -7
  43. package/src/serverGroup/configure/serverGroupCommandBuilder.aws.service.spec.js +302 -1
  44. package/src/serverGroup/configure/serverGroupCommandBuilder.service.js +96 -24
  45. package/src/serverGroup/configure/serverGroupCommandBuilder.spec.js +25 -8
  46. package/src/serverGroup/configure/serverGroupConfiguration.service.spec.ts +6 -13
  47. package/src/serverGroup/configure/serverGroupConfiguration.service.ts +26 -1
  48. package/src/serverGroup/configure/wizard/AmazonCloneServerGroupModal.tsx +1 -1
  49. package/src/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.tsx +31 -31
  50. package/src/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.tsx +133 -0
  51. package/src/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.tsx +99 -0
  52. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.tsx +36 -0
  53. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.tsx +144 -0
  54. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.tsx +137 -0
  55. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.tsx +135 -0
  56. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.tsx +137 -0
  57. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.less +5 -0
  58. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.tsx +108 -0
  59. package/src/serverGroup/configure/wizard/instanceType/advancedMode/advancedMode.less +110 -0
  60. package/src/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.tsx +62 -0
  61. package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +106 -62
  62. package/src/serverGroup/configure/wizard/pages/advancedSettings/ServerGroupAdvancedSettingsCommon.tsx +1 -1
  63. package/src/serverGroup/details/AmazonServerGroupActions.tsx +2 -1
  64. package/src/serverGroup/details/scalingPolicy/ScalingPolicySummary.tsx +7 -3
  65. package/src/serverGroup/details/scalingPolicy/StepPolicySummary.tsx +26 -28
  66. package/src/serverGroup/details/scalingPolicy/chart/MetricAlarmChart.tsx +1 -25
  67. package/src/serverGroup/details/scalingPolicy/index.ts +0 -1
  68. package/src/serverGroup/details/scalingPolicy/scalingPolicy.module.ts +1 -9
  69. package/src/serverGroup/details/scalingPolicy/scalingPolicySummary.component.ts +6 -2
  70. package/src/serverGroup/details/scalingPolicy/targetTracking/TargetTrackingChart.tsx +2 -17
  71. package/src/serverGroup/details/scalingPolicy/upsert/alarm/AlarmConfigurer.less +1 -1
  72. package/src/serverGroup/details/scalingProcesses/AutoScalingProcessService.spec.ts +1 -2
  73. package/src/serverGroup/details/sections/AdvancedSettingsDetailsSection.tsx +3 -2
  74. package/src/serverGroup/details/sections/AmazonCapacityDetailsSection.tsx +3 -8
  75. package/src/serverGroup/details/sections/InstancesDistributionDetailsSection.spec.tsx +8 -5
  76. package/src/serverGroup/details/sections/LaunchTemplateDetailsSection.spec.tsx +8 -5
  77. package/src/serverGroup/details/sections/ScalingPoliciesDetailsSection.tsx +3 -3
  78. package/src/serverGroup/details/sections/ScalingProcessesDetailsSection.tsx +3 -10
  79. package/src/serverGroup/details/sections/ScheduledActionsDetailsSection.tsx +3 -2
  80. package/src/serverGroup/details/sections/SecurityGroupsDetailsSection.tsx +3 -2
  81. package/src/serverGroup/serverGroup.transformer.spec.ts +5 -3
  82. package/src/subnet/SubnetSelectInput.spec.tsx +7 -4
  83. package/src/vpc/{VpcReader.spec.js → VpcReader.spec.ts} +2 -10
  84. package/src/vpc/VpcTag.spec.tsx +37 -0
  85. package/src/vpc/vpc.module.ts +6 -2
  86. package/dist/reactShims/aws.ngReact.d.ts +0 -11
  87. package/dist/serverGroup/details/scalingPolicy/ScalingPolicyTypeRegistry.d.ts +0 -10
  88. package/dist/serverGroup/details/scalingPolicy/alarmBasedSummary.component.d.ts +0 -2
  89. package/dist/serverGroup/details/scalingPolicy/detailsSummary.component.d.ts +0 -12
  90. package/dist/serverGroup/details/scalingPolicy/popover/scalingPolicyPopover.component.d.ts +0 -1
  91. package/dist/serverGroup/details/scalingPolicy/stepPolicySummary.component.d.ts +0 -1
  92. package/dist/serverGroup/details/scalingPolicy/targetTracking/TargetTrackingPolicy.config.d.ts +0 -1
  93. package/dist/serverGroup/details/scalingPolicy/targetTracking/targetTracking.module.d.ts +0 -2
  94. package/dist/serverGroup/details/scalingPolicy/targetTracking/targetTrackingChart.component.d.ts +0 -1
  95. package/dist/serverGroup/details/scalingPolicy/targetTracking/targetTrackingSummary.component.d.ts +0 -1
  96. package/dist/vpc/vpcTag.directive.d.ts +0 -2
  97. package/src/image/image.reader.spec.js +0 -123
  98. package/src/instance/awsInstanceType.service.js +0 -437
  99. package/src/reactShims/aws.ngReact.ts +0 -27
  100. package/src/serverGroup/details/scalingPolicy/ScalingPolicyTypeRegistry.ts +0 -18
  101. package/src/serverGroup/details/scalingPolicy/alarmBasedSummary.component.html +0 -35
  102. package/src/serverGroup/details/scalingPolicy/alarmBasedSummary.component.js +0 -60
  103. package/src/serverGroup/details/scalingPolicy/alarmBasedSummary.template.html +0 -5
  104. package/src/serverGroup/details/scalingPolicy/detailsSummary.component.ts +0 -32
  105. package/src/serverGroup/details/scalingPolicy/popover/scalingPolicyDetails.popover.html +0 -1
  106. package/src/serverGroup/details/scalingPolicy/popover/scalingPolicyPopover.component.html +0 -107
  107. package/src/serverGroup/details/scalingPolicy/popover/scalingPolicyPopover.component.ts +0 -31
  108. package/src/serverGroup/details/scalingPolicy/scalingPolicySummary.component.less +0 -15
  109. package/src/serverGroup/details/scalingPolicy/stepPolicySummary.component.ts +0 -10
  110. package/src/serverGroup/details/scalingPolicy/targetTracking/TargetTrackingPolicy.config.ts +0 -6
  111. package/src/serverGroup/details/scalingPolicy/targetTracking/targetTracking.module.ts +0 -8
  112. package/src/serverGroup/details/scalingPolicy/targetTracking/targetTrackingChart.component.ts +0 -16
  113. package/src/serverGroup/details/scalingPolicy/targetTracking/targetTrackingSummary.component.ts +0 -14
  114. package/src/serverGroup/details/scalingPolicy/targetTracking/targetTrackingSummary.html +0 -5
  115. package/src/vpc/vpcTag.directive.js +0 -34
  116. package/src/vpc/vpcTag.directive.spec.js +0 -60
@@ -0,0 +1,144 @@
1
+ import React, { useState } from 'react';
2
+ import { Checkbox } from 'react-bootstrap';
3
+ import { SortableHandle } from 'react-sortable-hoc';
4
+ import { TextInput, Tooltip } from '@spinnaker/core';
5
+ import type { IAmazonPreferredInstanceType } from '../../../../../instance/awsInstanceType.service';
6
+ import { CostFactor } from '../../../../../instance/details/CostFactor';
7
+ import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
8
+
9
+ export interface IRowProps {
10
+ isCustom: boolean;
11
+ selectedType?: IAmazonInstanceTypeOverride;
12
+ instanceTypeDetails?: IAmazonPreferredInstanceType;
13
+ removeInstanceType?: (typeToRemove: string) => void;
14
+ addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
15
+ }
16
+
17
+ export function InstanceTypeRow(props: IRowProps) {
18
+ const isRowSelected = props.selectedType ? true : false;
19
+ const [newWeightedCap, setNewWeightedCap] = useState('');
20
+ const instanceType = isRowSelected ? props.selectedType.instanceType : props.instanceTypeDetails.name;
21
+ const disableRow = isRowSelected ? false : props.instanceTypeDetails.unavailable;
22
+
23
+ const selectOrUpdateRow = (instanceType: string, weight: string) => {
24
+ if (!disableRow) {
25
+ props.addOrUpdateInstanceType(instanceType, weight);
26
+ }
27
+ setNewWeightedCap('');
28
+ };
29
+
30
+ const handleWeightChange = (e: React.ChangeEvent<any>): void => {
31
+ setNewWeightedCap(e.target.value);
32
+ if (isRowSelected) {
33
+ selectOrUpdateRow(instanceType, e.target.value);
34
+ } else {
35
+ document.getElementById(`selectInstanceType-${instanceType}`).focus();
36
+ }
37
+ };
38
+
39
+ let row;
40
+ if (props.isCustom) {
41
+ row = (
42
+ <tr key={instanceType} className={'sortable clickable'}>
43
+ <td>
44
+ <DragHandle />
45
+ </td>
46
+ <td>{instanceType}</td>
47
+ <td title={'Enter optional weight (allowed values: 1 to 999).'}>
48
+ <TextInput
49
+ className={'form-control input input-sm'}
50
+ pattern="[0-9]*"
51
+ placeholder="Enter optional weight (allowed values: 1 to 999)."
52
+ value={props.selectedType?.weightedCapacity || ''}
53
+ onChange={(e) => selectOrUpdateRow(instanceType, e.target.value)}
54
+ />
55
+ </td>
56
+ <td>
57
+ <div>
58
+ <span>
59
+ <a
60
+ className="btn btn-sm btn-link clickable"
61
+ onClick={() => props.removeInstanceType(instanceType)}
62
+ style={{ padding: '5px' }}
63
+ >
64
+ <Tooltip value={'Remove instance type'}>
65
+ <span className="glyphicon glyphicon-trash" />
66
+ </Tooltip>
67
+ </a>
68
+ </span>
69
+ </div>
70
+ </td>
71
+ </tr>
72
+ );
73
+ } else {
74
+ const { cpu, memory, cpuCreditsPerHour, storage, costFactor } = props.instanceTypeDetails;
75
+ row = (
76
+ <tr
77
+ key={instanceType}
78
+ className={isRowSelected ? 'sortable clickable' : `non-sortable ${disableRow ? 'unavailable' : 'clickable'}`}
79
+ title={
80
+ isRowSelected
81
+ ? 'Click to unselect instance type'
82
+ : disableRow
83
+ ? 'This instance type is not available for the selected configuration'
84
+ : 'Click to select instance type'
85
+ }
86
+ onClick={(e) => {
87
+ if (!$(e.target).is('input')) {
88
+ isRowSelected ? props.removeInstanceType(instanceType) : selectOrUpdateRow(instanceType, newWeightedCap);
89
+ }
90
+ }}
91
+ >
92
+ {isRowSelected ? (
93
+ <td>
94
+ <DragHandle />
95
+ </td>
96
+ ) : (
97
+ <td></td>
98
+ )}
99
+ <td>
100
+ <Checkbox
101
+ id={`selectInstanceType-${instanceType}`}
102
+ checked={isRowSelected ? true : false}
103
+ disabled={disableRow}
104
+ onChange={() => {
105
+ isRowSelected ? props.removeInstanceType(instanceType) : selectOrUpdateRow(instanceType, newWeightedCap);
106
+ }}
107
+ />
108
+ </td>
109
+ <td>{instanceType}</td>
110
+ <td>{cpu}</td>
111
+ <td>{memory}</td>
112
+ {cpuCreditsPerHour ? (
113
+ <td>{cpuCreditsPerHour}</td>
114
+ ) : (
115
+ <td title={'Cpu credits not applicable to instance type.'}>-</td>
116
+ )}
117
+ {storage.type === 'EBS' && <td>EBS Only</td>}
118
+ {storage.type === 'SSD' && <td>{storage.count + 'x' + storage.size}</td>}
119
+ <td>
120
+ <CostFactor min={costFactor} />
121
+ </td>
122
+ <td title={!disableRow ? 'Enter optional weight (allowed values: 1 to 999).' : ''}>
123
+ <TextInput
124
+ inputClassName={'form-control input input-sm'}
125
+ id={`weightedCapacity-${instanceType}`}
126
+ pattern="[0-9]*"
127
+ placeholder="Enter optional weight (allowed values: 1 to 999)."
128
+ disabled={disableRow}
129
+ value={props.selectedType?.weightedCapacity || newWeightedCap || ''}
130
+ onChange={handleWeightChange}
131
+ />
132
+ </td>
133
+ </tr>
134
+ );
135
+ }
136
+
137
+ return row;
138
+ }
139
+
140
+ const DragHandle = SortableHandle(() => (
141
+ <Tooltip value={'Drag to change priority'}>
142
+ <span className="instance-type-drag-handle glyphicon glyphicon-resize-vertical" />
143
+ </Tooltip>
144
+ ));
@@ -0,0 +1,137 @@
1
+ import React from 'react';
2
+ import type { SortEnd } from 'react-sortable-hoc';
3
+ import { arrayMove } from 'react-sortable-hoc';
4
+
5
+ import { CpuCreditsToggle } from '../CpuCreditsToggle';
6
+ import { InstanceTypeTableBody } from './InstanceTypeTableBody';
7
+ import { Header, Heading } from './InstanceTypeTableParts';
8
+ import { Footer } from './InstanceTypeTableParts';
9
+ import { AWSProviderSettings } from '../../../../../aws.settings';
10
+ import type { IAmazonInstanceTypeCategory } from '../../../../../instance/awsInstanceType.service';
11
+ import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
12
+
13
+ import './advancedMode.less';
14
+
15
+ export interface IInstanceTypeTableProps {
16
+ currentProfile: string;
17
+ selectedInstanceTypesMap: Map<string, IAmazonInstanceTypeOverride>;
18
+ unlimitedCpuCreditsInCmd: boolean;
19
+ profileDetails: IAmazonInstanceTypeCategory;
20
+ availableInstanceTypesList: string[];
21
+ handleInstanceTypesChange: (instanceTypes: IAmazonInstanceTypeOverride[]) => void;
22
+ setUnlimitedCpuCredits: (unlimitedCpuCredits: boolean | undefined) => void;
23
+ }
24
+
25
+ export function InstanceTypeTable(props: IInstanceTypeTableProps) {
26
+ const { currentProfile, selectedInstanceTypesMap } = props;
27
+
28
+ const handleSortEnd = (sortEnd: SortEnd): void => {
29
+ const sortedInstanceTypes: string[] = Array.from(selectedInstanceTypesMap.values())
30
+ .sort((i1, i2) => i1.priority - i2.priority)
31
+ .map((it) => it.instanceType);
32
+ const instanceTypesInNewOrder = arrayMove(sortedInstanceTypes, sortEnd.oldIndex, sortEnd.newIndex);
33
+
34
+ updatePriorityForSelectedTypes(instanceTypesInNewOrder);
35
+ };
36
+
37
+ const updatePriorityForSelectedTypes = (instanceTypesInNewOrder: string[]): void => {
38
+ selectedInstanceTypesMap.forEach((value, key) => {
39
+ const newPriority = 1 + instanceTypesInNewOrder.indexOf(key);
40
+ if (value.priority !== newPriority) {
41
+ value.priority = newPriority;
42
+ }
43
+ });
44
+ props.handleInstanceTypesChange(Array.from(selectedInstanceTypesMap.values()));
45
+ };
46
+
47
+ const removeInstanceType = (instanceType: string): void => {
48
+ const selectedInstanceTypesMapNew = new Map(selectedInstanceTypesMap);
49
+ selectedInstanceTypesMapNew.delete(instanceType);
50
+ props.handleInstanceTypesChange(Array.from(selectedInstanceTypesMapNew.values()));
51
+ };
52
+
53
+ const addOrUpdateInstanceType = (type: string, weight: string) => {
54
+ const weightNum = Number(weight);
55
+ const itemToUpdate = selectedInstanceTypesMap.has(type)
56
+ ? {
57
+ ...selectedInstanceTypesMap.get(type), // update existing item
58
+ weightedCapacity: !isNaN(weightNum) && weightNum !== 0 ? weightNum : undefined,
59
+ }
60
+ : {
61
+ instanceType: type, // new item
62
+ weightedCapacity: !isNaN(weightNum) && weightNum !== 0 ? weightNum : undefined,
63
+ priority:
64
+ 1 +
65
+ Array.from(selectedInstanceTypesMap.values()).reduce(
66
+ (max, it) => (it.priority > max ? it.priority : max),
67
+ 0,
68
+ ),
69
+ };
70
+ selectedInstanceTypesMap.set(type, itemToUpdate);
71
+ props.handleInstanceTypesChange(Array.from(selectedInstanceTypesMap.values()));
72
+ };
73
+
74
+ const isCpuCreditsEnabled: boolean = AWSProviderSettings.serverGroups?.enableCpuCredits;
75
+ const selectedInstanceTypeNames = Array.from(selectedInstanceTypesMap.keys());
76
+ const cpuCreditsToggle = (
77
+ <div>
78
+ <CpuCreditsToggle
79
+ unlimitedCpuCredits={props.unlimitedCpuCreditsInCmd}
80
+ currentProfile={currentProfile}
81
+ selectedInstanceTypes={selectedInstanceTypeNames}
82
+ setUnlimitedCpuCredits={props.setUnlimitedCpuCredits}
83
+ />
84
+ </div>
85
+ );
86
+
87
+ if (currentProfile && currentProfile !== 'custom') {
88
+ const { label, descriptionListOverride, families, showCpuCredits } = props.profileDetails;
89
+ const isCustom = false;
90
+ return (
91
+ <div className={'row sub-section'}>
92
+ <Heading
93
+ isCustom={isCustom}
94
+ profileLabel={label}
95
+ profileDescriptionArr={descriptionListOverride ? descriptionListOverride : families.map((f) => f.description)}
96
+ />
97
+ {isCpuCreditsEnabled && cpuCreditsToggle}
98
+ <table className="table table-hover">
99
+ <Header isCustom={isCustom} showCpuCredits={showCpuCredits} />
100
+ <InstanceTypeTableBody
101
+ isCustom={isCustom}
102
+ profileFamiliesDetails={families}
103
+ selectedInstanceTypesMap={selectedInstanceTypesMap}
104
+ addOrUpdateInstanceType={addOrUpdateInstanceType}
105
+ removeInstanceType={removeInstanceType}
106
+ handleSortEnd={handleSortEnd}
107
+ />
108
+ </table>
109
+ </div>
110
+ );
111
+ } else {
112
+ const isCustom = true;
113
+ return (
114
+ <div className={'row sub-section'}>
115
+ <Heading isCustom={isCustom} />
116
+ {isCpuCreditsEnabled && cpuCreditsToggle}
117
+ <table className="table table-hover">
118
+ <Header isCustom={isCustom} />
119
+ <InstanceTypeTableBody
120
+ isCustom={isCustom}
121
+ selectedInstanceTypesMap={selectedInstanceTypesMap}
122
+ addOrUpdateInstanceType={addOrUpdateInstanceType}
123
+ removeInstanceType={removeInstanceType}
124
+ handleSortEnd={handleSortEnd}
125
+ />
126
+ <Footer
127
+ isCustom={isCustom}
128
+ availableInstanceTypesList={props.availableInstanceTypesList.filter(
129
+ (it) => !selectedInstanceTypeNames.includes(it),
130
+ )}
131
+ addOrUpdateInstanceType={addOrUpdateInstanceType}
132
+ />
133
+ </table>
134
+ </div>
135
+ );
136
+ }
137
+ }
@@ -0,0 +1,135 @@
1
+ import { difference, flatten, keyBy } from 'lodash';
2
+ import React from 'react';
3
+ import type { SortEnd } from 'react-sortable-hoc';
4
+ import { SortableContainer, SortableElement } from 'react-sortable-hoc';
5
+
6
+ import type { IInstanceTypeFamily } from '@spinnaker/core';
7
+
8
+ import { InstanceTypeRow } from './InstanceTypeRow';
9
+ import type { IAmazonPreferredInstanceType } from '../../../../../instance/awsInstanceType.service';
10
+ import type { IAmazonInstanceTypeOverride } from '../../../serverGroupConfiguration.service';
11
+
12
+ export function InstanceTypeTableBody(props: {
13
+ isCustom: boolean;
14
+ profileFamiliesDetails?: IInstanceTypeFamily[];
15
+ selectedInstanceTypesMap: Map<string, IAmazonInstanceTypeOverride>;
16
+ addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
17
+ removeInstanceType: (instanceType: string) => void;
18
+ handleSortEnd: (sortEnd: SortEnd) => void;
19
+ }) {
20
+ return (
21
+ <TableRows
22
+ isCustom={props.isCustom}
23
+ selectedInstanceTypesMap={props.selectedInstanceTypesMap}
24
+ removeInstanceType={props.removeInstanceType}
25
+ addOrUpdateInstanceType={props.addOrUpdateInstanceType}
26
+ instanceTypeDetails={
27
+ props.isCustom
28
+ ? null
29
+ : new Map(
30
+ Object.entries(
31
+ keyBy(flatten(props.profileFamiliesDetails.map((f: IInstanceTypeFamily) => f.instanceTypes)), 'name'),
32
+ ),
33
+ )
34
+ }
35
+ onSortEnd={(sortEnd) => props.handleSortEnd(sortEnd)}
36
+ distance={1}
37
+ />
38
+ );
39
+ }
40
+
41
+ const TableRows = SortableContainer(
42
+ (props: {
43
+ isCustom: boolean;
44
+ instanceTypeDetails?: Map<string, IAmazonPreferredInstanceType>;
45
+ selectedInstanceTypesMap: Map<string, IAmazonInstanceTypeOverride>;
46
+ removeInstanceType: (typeToRemove: string) => void;
47
+ addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
48
+ }) => {
49
+ const { isCustom, selectedInstanceTypesMap } = props;
50
+
51
+ let selectedRows, unselectedRows;
52
+ if (isCustom) {
53
+ selectedRows =
54
+ selectedInstanceTypesMap.size > 0 &&
55
+ Array.from(selectedInstanceTypesMap.values())
56
+ .sort((i1, i2) => i1.priority - i2.priority)
57
+ .map((selectedType, index: number) => (
58
+ <SortableRow
59
+ key={`${selectedType.instanceType}-${index}`}
60
+ index={index}
61
+ isCustom={true}
62
+ selectedType={selectedType}
63
+ removeInstanceType={props.removeInstanceType}
64
+ addOrUpdateInstanceType={props.addOrUpdateInstanceType}
65
+ />
66
+ ));
67
+ } else {
68
+ const { instanceTypeDetails } = props;
69
+ const instanceTypesInProfile: string[] = Array.from(instanceTypeDetails?.keys());
70
+ const unselectedInstanceTypes: string[] = difference(
71
+ instanceTypesInProfile,
72
+ Array.from(selectedInstanceTypesMap.keys()),
73
+ );
74
+
75
+ const selectedRowsOrdered: IAmazonInstanceTypeOverride[] = Array.from(selectedInstanceTypesMap.values())
76
+ .filter((selectedType: IAmazonInstanceTypeOverride) =>
77
+ instanceTypesInProfile.includes(selectedType.instanceType),
78
+ )
79
+ .sort((i1, i2) => i1.priority - i2.priority);
80
+
81
+ selectedRows =
82
+ selectedRowsOrdered &&
83
+ selectedRowsOrdered.length > 0 &&
84
+ selectedRowsOrdered.map((selectedType: IAmazonInstanceTypeOverride, index: number) => (
85
+ <SortableRow
86
+ key={`${selectedType.instanceType}-${index}`}
87
+ index={index}
88
+ isCustom={false}
89
+ selectedType={selectedType}
90
+ instanceTypeDetails={instanceTypeDetails}
91
+ removeInstanceType={props.removeInstanceType}
92
+ addOrUpdateInstanceType={props.addOrUpdateInstanceType}
93
+ />
94
+ ));
95
+
96
+ unselectedRows =
97
+ unselectedInstanceTypes &&
98
+ unselectedInstanceTypes.length > 0 &&
99
+ unselectedInstanceTypes.map((instanceType) => (
100
+ <InstanceTypeRow
101
+ key={instanceType}
102
+ isCustom={false}
103
+ instanceTypeDetails={instanceTypeDetails.get(instanceType)}
104
+ addOrUpdateInstanceType={props.addOrUpdateInstanceType}
105
+ />
106
+ ));
107
+ }
108
+
109
+ return (
110
+ <tbody>
111
+ {selectedRows}
112
+ {!isCustom ? unselectedRows : null}
113
+ </tbody>
114
+ );
115
+ },
116
+ );
117
+
118
+ const SortableRow = SortableElement(
119
+ (props: {
120
+ isCustom: boolean;
121
+ selectedType: IAmazonInstanceTypeOverride;
122
+ instanceTypeDetails?: Map<string, IAmazonPreferredInstanceType>;
123
+ removeInstanceType: (typeToRemove: string) => void;
124
+ addOrUpdateInstanceType: (instanceType: string, weight: string) => void;
125
+ }) => (
126
+ <InstanceTypeRow
127
+ key={props.selectedType.instanceType}
128
+ isCustom={props.isCustom}
129
+ selectedType={props.selectedType}
130
+ instanceTypeDetails={!props.isCustom ? props.instanceTypeDetails.get(props.selectedType.instanceType) : null}
131
+ removeInstanceType={props.removeInstanceType}
132
+ addOrUpdateInstanceType={props.addOrUpdateInstanceType}
133
+ />
134
+ ),
135
+ );
@@ -0,0 +1,137 @@
1
+ import React from 'react';
2
+ import type { Option } from 'react-select';
3
+ import Select from 'react-select';
4
+
5
+ import { HelpField } from '@spinnaker/core';
6
+
7
+ export function Heading(props: { isCustom: boolean; profileLabel?: string; profileDescriptionArr?: string[] }) {
8
+ let description;
9
+ if (props.isCustom) {
10
+ description = <p>Choose the instance types that best suit the needs of your application.</p>;
11
+ } else {
12
+ description = (
13
+ <>
14
+ <p>
15
+ <b>{props.profileLabel}</b>
16
+ </p>
17
+ <ul>
18
+ {props.profileDescriptionArr.map((d, index) => (
19
+ <li key={index}>{d}</li>
20
+ ))}
21
+ </ul>
22
+ </>
23
+ );
24
+ }
25
+
26
+ return (
27
+ <div className={'row sub-section'}>
28
+ <h4>Instance Types</h4>
29
+ <div className={'description'}>
30
+ {description}
31
+ <i>
32
+ <b>Note:</b>
33
+ <ul>
34
+ <li>
35
+ The order of instance types sets their priority when On-Demand capacity is launched; instance type at the
36
+ top is prioritized the highest.
37
+ </li>
38
+ <li>Some instance types might not be available for the selected configuration.</li>
39
+ </ul>
40
+ </i>
41
+ </div>
42
+ </div>
43
+ );
44
+ }
45
+
46
+ export function Header(props: { isCustom: boolean; showCpuCredits?: boolean }) {
47
+ let emptyHeaders, instanceTypeHeader, otherHeaders, tailingEmptyHeader;
48
+ if (props.isCustom) {
49
+ emptyHeaders = <th />;
50
+ instanceTypeHeader = (
51
+ <th>
52
+ Instance Type <HelpField id="aws.serverGroup.instanceTypes" />
53
+ </th>
54
+ );
55
+ otherHeaders = null;
56
+ tailingEmptyHeader = <th />;
57
+ } else {
58
+ emptyHeaders = (
59
+ <>
60
+ <th />
61
+ <th />
62
+ </>
63
+ );
64
+ instanceTypeHeader = <th>InstanceType</th>;
65
+ otherHeaders = (
66
+ <>
67
+ <th>vCPU</th>
68
+ <th>Mem (GiB)</th>
69
+ {props.showCpuCredits && <th>CPU Credits</th>}
70
+ <th>
71
+ Storage (GB)
72
+ <HelpField id={'aws.serverGroup.storageType'} />
73
+ </th>
74
+ <th>Cost</th>
75
+ </>
76
+ );
77
+ tailingEmptyHeader = null;
78
+ }
79
+
80
+ return (
81
+ <thead>
82
+ <tr>
83
+ {emptyHeaders}
84
+ {instanceTypeHeader}
85
+ {otherHeaders}
86
+ <th>
87
+ Weight <HelpField id="aws.serverGroup.instanceTypeWeight" />
88
+ </th>
89
+ {tailingEmptyHeader}
90
+ </tr>
91
+ </thead>
92
+ );
93
+ }
94
+
95
+ export function Footer(props: {
96
+ isCustom: boolean;
97
+ availableInstanceTypesList: string[];
98
+ addOrUpdateInstanceType: (type: string, weight: string) => void;
99
+ }) {
100
+ return props.isCustom ? (
101
+ <tfoot>
102
+ <tr>
103
+ <td>
104
+ <span className={'glyphicon glyphicon-plus-sign'} style={{ paddingTop: '8px' }} />
105
+ </td>
106
+ <td colSpan={2}>
107
+ <InstanceTypeSelect
108
+ availableInstanceTypesList={props.availableInstanceTypesList}
109
+ addOrUpdateInstanceType={props.addOrUpdateInstanceType}
110
+ />
111
+ </td>
112
+ <td></td>
113
+ </tr>
114
+ </tfoot>
115
+ ) : null;
116
+ }
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
+ };
@@ -0,0 +1,5 @@
1
+ .InstancesDistribution {
2
+ .StandardFieldLayout_Label {
3
+ min-width: 300px;
4
+ }
5
+ }
@@ -0,0 +1,108 @@
1
+ import type { FormikProps } from 'formik';
2
+ import { get } from 'lodash';
3
+ import React from 'react';
4
+
5
+ import { FormikFormField, HelpField, NumberInput, ReactSelectInput, TextInput, Tooltip } from '@spinnaker/core';
6
+ import type { IAmazonServerGroupCommand } from '../../../serverGroupConfiguration.service';
7
+
8
+ import './InstancesDistribution.less';
9
+
10
+ export interface IInstancesDistributionProps {
11
+ formik: FormikProps<IAmazonServerGroupCommand>;
12
+ }
13
+
14
+ function useDefaultFormikValue(formik: FormikProps<IAmazonServerGroupCommand>, field: string, defaultValue: any) {
15
+ const value = get(formik.values, field);
16
+ React.useEffect(() => {
17
+ if (value === undefined && defaultValue !== undefined) {
18
+ formik.setFieldValue(field, defaultValue);
19
+ }
20
+ }, [field, defaultValue, value]);
21
+ }
22
+
23
+ export function InstancesDistribution(props: IInstancesDistributionProps) {
24
+ const { values: command, setFieldValue } = props.formik;
25
+
26
+ const spotAllocStrategyOptions = [
27
+ { label: 'capacity-optimized (recommended)', value: 'capacity-optimized' },
28
+ { label: 'capacity-optimized-prioritized', value: 'capacity-optimized-prioritized' },
29
+ { label: 'lowest-price', value: 'lowest-price' },
30
+ ];
31
+
32
+ // When allocation strategy toggles to/from lowest-price, update spotInstancePools
33
+ React.useEffect(() => {
34
+ if (command.spotAllocationStrategy !== 'lowest-price') {
35
+ setFieldValue('spotInstancePools', undefined);
36
+ } else if (command.spotInstancePools === undefined) {
37
+ setFieldValue('spotInstancePools', 2);
38
+ }
39
+ }, [command.spotAllocationStrategy]);
40
+
41
+ useDefaultFormikValue(props.formik, 'spotAllocationStrategy', 'capacity-optimized');
42
+
43
+ // prioritized is the only supported strategy for now
44
+ useDefaultFormikValue(props.formik, 'onDemandAllocationStrategy', 'prioritized');
45
+
46
+ // AWS defaults
47
+ useDefaultFormikValue(props.formik, 'onDemandBaseCapacity', 0);
48
+ useDefaultFormikValue(props.formik, 'onDemandPercentageAboveBaseCapacity', 100);
49
+
50
+ return (
51
+ <div className={'InstancesDistribution row sub-section form-group'}>
52
+ <h4>Instances Distribution</h4>
53
+ <div className={'description'}>
54
+ Diversify and distribute instance types across purchase options.{' '}
55
+ <HelpField id={'aws.serverGroup.instancesDistribution'} />
56
+ </div>
57
+ <br />
58
+
59
+ <FormikFormField
60
+ label={'Spot Allocation Strategy'}
61
+ name={'spotAllocationStrategy'}
62
+ help={<HelpField id={'aws.serverGroup.spotAllocationStrategy'} />}
63
+ input={(inputProps) => <ReactSelectInput {...inputProps} mode="PLAIN" options={spotAllocStrategyOptions} />}
64
+ />
65
+
66
+ {props.formik.values.spotAllocationStrategy === 'lowest-price' && (
67
+ <FormikFormField
68
+ label={'Spot Instance Pools Count'}
69
+ name={'spotInstancePools'}
70
+ help={<HelpField id={'aws.serverGroup.spotInstancePoolCount'} />}
71
+ input={(inputProps) => <NumberInput {...inputProps} />}
72
+ />
73
+ )}
74
+
75
+ <FormikFormField
76
+ label={'On-Demand Allocation Strategy'}
77
+ name={'onDemandAllocationStrategy'}
78
+ help={<HelpField id={'aws.serverGroup.odAllocationStrategy'} />}
79
+ input={(inputProps) => <TextInput {...inputProps} disabled={true} />}
80
+ />
81
+
82
+ <FormikFormField
83
+ label={'On-Demand Base Capacity'}
84
+ name={'onDemandBaseCapacity'}
85
+ help={<HelpField id={'aws.serverGroup.odBase'} />}
86
+ input={(inputProps) => <NumberInput {...inputProps} />}
87
+ />
88
+
89
+ <FormikFormField
90
+ label={'On-Demand Percentage Above Base Capacity'}
91
+ name={'onDemandPercentageAboveBaseCapacity'}
92
+ help={<HelpField id={'aws.serverGroup.odPercentAboveBase'} />}
93
+ input={(inputProps) => <NumberInput {...inputProps} />}
94
+ />
95
+
96
+ <FormikFormField
97
+ label={'Spot Max Price'}
98
+ name={'spotPrice'}
99
+ help={<HelpField id={'aws.serverGroup.spotMaxPrice'} />}
100
+ input={(inputProps) => (
101
+ <Tooltip value={'Recommended to leave empty and use AWS default i.e. On-Demand price'}>
102
+ <TextInput {...inputProps} />
103
+ </Tooltip>
104
+ )}
105
+ />
106
+ </div>
107
+ );
108
+ }