@spinnaker/amazon 0.11.1 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/dist/domain/IAmazonLaunchTemplate.d.ts +1 -1
- package/dist/domain/IAmazonServerGroup.d.ts +10 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/instance/awsInstanceType.service.d.ts +10 -2
- package/dist/instance/awsInstanceTypes.d.ts +10 -0
- package/dist/instance/details/CostFactor.d.ts +6 -0
- package/dist/reactShims/aws.react.injector.d.ts +2 -1
- package/dist/search/searchResultFormatter.d.ts +2 -2
- package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +17 -2
- package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +39 -0
- package/dist/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.d.ts +3 -4
- package/dist/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.d.ts +9 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.d.ts +13 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.d.ts +9 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.d.ts +11 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.d.ts +14 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.d.ts +12 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.d.ts +15 -0
- package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.d.ts +8 -0
- package/dist/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.d.ts +8 -0
- package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +9 -6
- package/dist/serverGroup/serverGroup.transformer.d.ts +2 -1
- package/package.json +5 -5
- package/src/domain/IAmazonLaunchTemplate.ts +1 -1
- package/src/domain/IAmazonServerGroup.ts +11 -0
- package/src/function/details/FunctionActions.spec.tsx +10 -9
- package/src/help/amazon.help.ts +9 -6
- package/src/image/image.reader.spec.ts +75 -0
- package/src/instance/awsInstanceType.service.spec.js +37 -71
- package/src/instance/awsInstanceType.service.ts +191 -0
- package/src/instance/awsInstanceTypes.ts +311 -0
- package/src/instance/details/CostFactor.tsx +29 -0
- package/src/instance/details/InstanceInformation.spec.tsx +2 -1
- package/src/instance/details/instance.details.controller.js +471 -473
- package/src/loadBalancer/details/loadBalancerDetails.controller.spec.ts +5 -3
- package/src/reactShims/aws.react.injector.ts +2 -1
- package/src/search/{searchResultFormatter.js → searchResultFormatter.ts} +5 -4
- package/src/serverGroup/configure/AmazonImageSelectInput.spec.tsx +10 -7
- package/src/serverGroup/configure/serverGroupCommandBuilder.aws.service.spec.js +302 -1
- package/src/serverGroup/configure/{serverGroupCommandBuilder.service.js → serverGroupCommandBuilder.service.ts} +182 -73
- package/src/serverGroup/configure/serverGroupCommandBuilder.spec.js +25 -8
- package/src/serverGroup/configure/serverGroupConfiguration.service.spec.ts +6 -13
- package/src/serverGroup/configure/serverGroupConfiguration.service.ts +53 -0
- package/src/serverGroup/configure/wizard/AmazonCloneServerGroupModal.tsx +1 -1
- package/src/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.tsx +31 -31
- package/src/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.tsx +133 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.tsx +99 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.tsx +36 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.tsx +144 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.tsx +138 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.tsx +135 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.tsx +137 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.less +5 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.tsx +108 -0
- package/src/serverGroup/configure/wizard/instanceType/advancedMode/advancedMode.less +110 -0
- package/src/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.tsx +62 -0
- package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +107 -62
- package/src/serverGroup/configure/wizard/pages/advancedSettings/ServerGroupAdvancedSettingsCommon.tsx +1 -1
- package/src/serverGroup/details/scalingPolicy/scalingPolicySummary.component.ts +6 -2
- package/src/serverGroup/details/scalingProcesses/AutoScalingProcessService.spec.ts +1 -2
- package/src/serverGroup/details/sections/InstancesDistributionDetailsSection.spec.tsx +8 -5
- package/src/serverGroup/details/sections/LaunchTemplateDetailsSection.spec.tsx +8 -5
- package/src/serverGroup/serverGroup.transformer.spec.ts +5 -3
- package/src/serverGroup/serverGroup.transformer.ts +24 -22
- package/src/subnet/SubnetSelectInput.spec.tsx +7 -4
- package/src/vpc/{VpcReader.spec.js → VpcReader.spec.ts} +2 -10
- package/src/vpc/VpcTag.spec.tsx +37 -0
- package/src/vpc/vpc.module.ts +6 -2
- package/dist/vpc/vpcTag.directive.d.ts +0 -2
- package/src/image/image.reader.spec.js +0 -123
- package/src/instance/awsInstanceType.service.js +0 -437
- package/src/vpc/vpcTag.directive.js +0 -34
- package/src/vpc/vpcTag.directive.spec.js +0 -60
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { FormikErrors, FormikProps } from 'formik';
|
|
2
|
+
import { some } from 'lodash';
|
|
2
3
|
import React from 'react';
|
|
4
|
+
import { Subject } from 'rxjs';
|
|
3
5
|
|
|
4
6
|
import type { IWizardPageComponent } from '@spinnaker/core';
|
|
5
|
-
import { NgReact } from '@spinnaker/core';
|
|
6
|
-
import { AWSProviderSettings } from '../../../../aws.settings';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import type { IAmazonInstanceTypeCategory } from '../../../../instance/awsInstanceType.service';
|
|
9
|
+
import { InstanceTypeSelector } from '../instanceType/InstanceTypeSelector';
|
|
10
|
+
import { AwsReactInjector } from '../../../../reactShims';
|
|
9
11
|
import type { IAmazonServerGroupCommand } from '../../serverGroupConfiguration.service';
|
|
10
12
|
|
|
11
13
|
export interface IServerGroupInstanceTypeProps {
|
|
@@ -13,88 +15,131 @@ export interface IServerGroupInstanceTypeProps {
|
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export interface IServerGroupInstanceTypeState {
|
|
16
|
-
|
|
17
|
-
newProfileType?: string;
|
|
18
|
+
instanceTypeDetails: IAmazonInstanceTypeCategory[];
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export class ServerGroupInstanceType
|
|
21
22
|
extends React.Component<IServerGroupInstanceTypeProps, IServerGroupInstanceTypeState>
|
|
22
23
|
implements IWizardPageComponent<IAmazonServerGroupCommand> {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
}
|
|
24
|
+
public state: IServerGroupInstanceTypeState = {
|
|
25
|
+
instanceTypeDetails: [],
|
|
26
|
+
};
|
|
27
|
+
private props$ = new Subject<IServerGroupInstanceTypeProps>();
|
|
28
|
+
private destroy$ = new Subject<void>();
|
|
30
29
|
|
|
31
|
-
public validate(values: IAmazonServerGroupCommand) {
|
|
30
|
+
public validate(values: IAmazonServerGroupCommand): FormikErrors<IAmazonServerGroupCommand> {
|
|
32
31
|
const errors: FormikErrors<IAmazonServerGroupCommand> = {};
|
|
33
32
|
|
|
34
|
-
if (
|
|
35
|
-
|
|
33
|
+
if (values.viewState.useSimpleInstanceTypeSelector) {
|
|
34
|
+
if (!values.instanceType) {
|
|
35
|
+
errors.instanceType = 'Instance Type required.';
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
const advancedModeErrors = this.validateAdvancedModeFields(values, errors);
|
|
39
|
+
Object.assign(errors, { ...advancedModeErrors });
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
return errors;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
private validateAdvancedModeFields(
|
|
46
|
+
values: IAmazonServerGroupCommand,
|
|
47
|
+
errors: FormikErrors<IAmazonServerGroupCommand>,
|
|
48
|
+
): FormikErrors<IAmazonServerGroupCommand> {
|
|
49
|
+
if (!values.launchTemplateOverridesForInstanceType.length) {
|
|
50
|
+
errors.instanceType = 'At least one instance type required.';
|
|
51
|
+
}
|
|
52
|
+
if (values.launchTemplateOverridesForInstanceType.length > 40) {
|
|
53
|
+
errors.instanceType = 'Maximum of 40 instance types are allowed.';
|
|
54
|
+
}
|
|
55
|
+
const weightsSpecified = values.launchTemplateOverridesForInstanceType.filter(
|
|
56
|
+
(it) => it.weightedCapacity !== undefined,
|
|
57
|
+
);
|
|
58
|
+
if (
|
|
59
|
+
!(
|
|
60
|
+
weightsSpecified.length === values.launchTemplateOverridesForInstanceType.length ||
|
|
61
|
+
weightsSpecified.length === 0
|
|
62
|
+
)
|
|
63
|
+
) {
|
|
64
|
+
errors.instanceType = 'Weighted capacity must be specified for all instance types selected or none.';
|
|
65
|
+
}
|
|
66
|
+
if (
|
|
67
|
+
some(weightsSpecified, function (instanceType) {
|
|
68
|
+
const weightedCapacity = Number(instanceType.weightedCapacity);
|
|
69
|
+
return weightedCapacity < 1 || weightedCapacity > 999;
|
|
70
|
+
})
|
|
71
|
+
) {
|
|
72
|
+
errors.instanceType = 'Weighted capacity must be a number between 1 and 999.';
|
|
73
|
+
}
|
|
47
74
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
this.props.formik.setFieldValue('instanceType', type);
|
|
52
|
-
this.setState({ newInstanceType: type, newProfileType: undefined });
|
|
53
|
-
};
|
|
75
|
+
if (values.onDemandBaseCapacity < 0) {
|
|
76
|
+
errors.onDemandBaseCapacity = 'On-Demand base capacity must be non-negative.';
|
|
77
|
+
}
|
|
54
78
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
79
|
+
if (values.onDemandBaseCapacity > values.capacity.max) {
|
|
80
|
+
errors.onDemandBaseCapacity = 'On-Demand base capacity must be less than or equal to max capacity.';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (values.onDemandPercentageAboveBaseCapacity < 0 || values.onDemandPercentageAboveBaseCapacity > 100) {
|
|
84
|
+
errors.onDemandPercentageAboveBaseCapacity =
|
|
85
|
+
'On-Demand percentage above base capacity must be a number between 0 and 100.';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (values.spotPrice) {
|
|
89
|
+
const spotPriceNum = Number(values.spotPrice);
|
|
90
|
+
if (Number.isNaN(spotPriceNum)) {
|
|
91
|
+
errors.spotPrice = 'Spot Max Price must be a number or empty string, used to unset previous max price.';
|
|
92
|
+
}
|
|
93
|
+
if (spotPriceNum <= 0) {
|
|
94
|
+
errors.spotPrice = 'Spot Max Price must be greater than 0, if specified.';
|
|
95
|
+
}
|
|
59
96
|
}
|
|
60
|
-
|
|
97
|
+
|
|
98
|
+
if (values.spotInstancePools && values.spotAllocationStrategy !== 'lowest-price') {
|
|
99
|
+
errors.spotInstancePools = "Spot Instance Pools is only supported for 'lowest-price' Spot Allocation Strategy.";
|
|
100
|
+
}
|
|
101
|
+
if (values.spotInstancePools <= 0 || values.spotInstancePools > 20) {
|
|
102
|
+
errors.spotInstancePools = 'Spot Instance Pools count must be a number between 1 and 20, when applicable.';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return errors;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public componentDidMount(): void {
|
|
109
|
+
Promise.resolve(AwsReactInjector.awsInstanceTypeService.getCategories()).then(
|
|
110
|
+
(categories: IAmazonInstanceTypeCategory[]) => {
|
|
111
|
+
this.setState({ instanceTypeDetails: categories });
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public componentDidUpdate() {
|
|
117
|
+
this.props$.next(this.props);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public componentWillUnmount() {
|
|
121
|
+
this.destroy$.next();
|
|
122
|
+
}
|
|
61
123
|
|
|
62
124
|
public render() {
|
|
63
125
|
const { values } = this.props.formik;
|
|
64
|
-
|
|
65
|
-
const { InstanceArchetypeSelector, InstanceTypeSelector } = NgReact;
|
|
66
126
|
const showTypeSelector = !!(values.viewState.disableImageSelection || values.amiName);
|
|
67
127
|
|
|
68
|
-
|
|
69
|
-
const
|
|
128
|
+
// mark unavailable instance types for all profiles
|
|
129
|
+
const availableInstanceTypesForConfig: string[] = values.backingData?.filtered?.instanceTypes ?? [];
|
|
130
|
+
const markedInstanceTypeDetails: IAmazonInstanceTypeCategory[] = Array.from(this.state.instanceTypeDetails);
|
|
131
|
+
if (!values.viewState.disableImageSelection && availableInstanceTypesForConfig.length) {
|
|
132
|
+
markedInstanceTypeDetails.forEach((profile) => {
|
|
133
|
+
profile.families.forEach((family) => {
|
|
134
|
+
family.instanceTypes.forEach((instanceType) => {
|
|
135
|
+
instanceType.unavailable = !availableInstanceTypesForConfig.includes(instanceType.name);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
70
140
|
|
|
71
141
|
if (showTypeSelector && values) {
|
|
72
|
-
return
|
|
73
|
-
<div className="container-fluid form-horizontal">
|
|
74
|
-
<div className="row">
|
|
75
|
-
<InstanceArchetypeSelector
|
|
76
|
-
command={values}
|
|
77
|
-
onTypeChanged={this.instanceTypeChanged}
|
|
78
|
-
onProfileChanged={this.instanceProfileChanged}
|
|
79
|
-
/>
|
|
80
|
-
<div style={{ padding: '0 15px' }}>
|
|
81
|
-
{values.viewState.instanceProfile && values.viewState.instanceProfile !== 'custom' && (
|
|
82
|
-
<InstanceTypeSelector command={values} onTypeChanged={this.instanceTypeChanged} />
|
|
83
|
-
)}
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
{isLaunchTemplatesEnabled && isCpuCreditsEnabled && (
|
|
87
|
-
<div className="row">
|
|
88
|
-
<CpuCreditsToggle
|
|
89
|
-
command={values}
|
|
90
|
-
newInstanceType={this.state.newInstanceType}
|
|
91
|
-
newProfileType={this.state.newProfileType}
|
|
92
|
-
setUnlimitedCpuCredits={this.setUnlimitedCpuCredits}
|
|
93
|
-
/>
|
|
94
|
-
</div>
|
|
95
|
-
)}
|
|
96
|
-
</div>
|
|
97
|
-
);
|
|
142
|
+
return <InstanceTypeSelector formik={this.props.formik} instanceTypeDetails={markedInstanceTypeDetails} />;
|
|
98
143
|
}
|
|
99
144
|
|
|
100
145
|
return <h5 className="text-center">Please select an image.</h5>;
|
|
@@ -222,7 +222,7 @@ export class ServerGroupAdvancedSettingsCommon extends React.Component<IServerGr
|
|
|
222
222
|
</div>
|
|
223
223
|
</div>
|
|
224
224
|
)}
|
|
225
|
-
{!AWSProviderSettings.disableSpotPricing && (
|
|
225
|
+
{!AWSProviderSettings.disableSpotPricing && values.viewState.useSimpleInstanceTypeSelector && (
|
|
226
226
|
<div className="form-group">
|
|
227
227
|
<div className="col-md-5 sm-label-right">
|
|
228
228
|
<b>Spot Instances Price (optional)</b> <HelpField id="aws.serverGroup.spotMaxPrice" />
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { module } from 'angular';
|
|
2
2
|
import { react2angular } from 'react2angular';
|
|
3
3
|
import { withErrorBoundary } from '@spinnaker/core';
|
|
4
|
-
import {
|
|
4
|
+
import { ScalingPolicySummary } from './ScalingPolicySummary';
|
|
5
5
|
|
|
6
6
|
export const SCALING_POLICY_SUMMARY_COMPONENT = 'spinnaker.amazon.scalingPolicy.scalingPolicySummary.component';
|
|
7
7
|
module(SCALING_POLICY_SUMMARY_COMPONENT, []).component(
|
|
8
8
|
'scalingPolicySummary',
|
|
9
|
-
react2angular(withErrorBoundary(
|
|
9
|
+
react2angular(withErrorBoundary(ScalingPolicySummary, 'scalingPolicySummary'), [
|
|
10
|
+
'application',
|
|
11
|
+
'policy',
|
|
12
|
+
'serverGroup',
|
|
13
|
+
]),
|
|
10
14
|
);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { IAmazonAsg, IAmazonServerGroup } from '../../../domain';
|
|
2
|
-
|
|
3
1
|
import { AutoScalingProcessService } from './AutoScalingProcessService';
|
|
2
|
+
import type { IAmazonAsg, IAmazonServerGroup } from '../../../domain';
|
|
4
3
|
|
|
5
4
|
describe('AutoScalingProcessService', () => {
|
|
6
5
|
describe('normalizeScalingProcesses', () => {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { shallow } from 'enzyme';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { Application } from '@spinnaker/core';
|
|
5
|
+
import { ApplicationModelBuilder } from '@spinnaker/core';
|
|
3
6
|
import { mockLaunchTemplate, mockServerGroup } from '@spinnaker/mocks';
|
|
4
|
-
|
|
7
|
+
|
|
8
|
+
import type { IAmazonMixedInstancesPolicy, IAmazonServerGroupView, IScalingPolicy } from '../../../domain';
|
|
5
9
|
import { InstancesDistributionDetailsSection } from '../../../index';
|
|
6
|
-
import { Application, ApplicationModelBuilder } from '@spinnaker/core';
|
|
7
10
|
|
|
8
11
|
describe('InstancesDistribution', () => {
|
|
9
12
|
let app: Application;
|
|
@@ -50,7 +53,7 @@ describe('InstancesDistribution', () => {
|
|
|
50
53
|
]);
|
|
51
54
|
let index = 0;
|
|
52
55
|
expectedLabels.forEach((value, key) => {
|
|
53
|
-
|
|
56
|
+
const labeledValue = actualLabeledValues.at(index++);
|
|
54
57
|
expect(labeledValue.prop('label')).toEqual(key);
|
|
55
58
|
expect(labeledValue.prop('value')).toEqual(value);
|
|
56
59
|
});
|
|
@@ -77,7 +80,7 @@ describe('InstancesDistribution', () => {
|
|
|
77
80
|
]);
|
|
78
81
|
let index = 0;
|
|
79
82
|
expectedLabels.forEach((value, key) => {
|
|
80
|
-
|
|
83
|
+
const labeledValue = actualLabeledValues.at(index++);
|
|
81
84
|
expect(labeledValue.prop('label')).toEqual(key);
|
|
82
85
|
expect(labeledValue.prop('value')).toEqual(value);
|
|
83
86
|
});
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { shallow } from 'enzyme';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { Application } from '@spinnaker/core';
|
|
5
|
+
import { ApplicationModelBuilder } from '@spinnaker/core';
|
|
3
6
|
import {
|
|
4
7
|
createCustomMockLaunchTemplate,
|
|
5
8
|
mockLaunchTemplate,
|
|
6
9
|
mockLaunchTemplateData,
|
|
7
10
|
mockServerGroup,
|
|
8
11
|
} from '@spinnaker/mocks';
|
|
9
|
-
|
|
10
|
-
import { IAmazonServerGroupView, IAmazonMixedInstancesPolicy, IScalingPolicy } from '../../../domain';
|
|
12
|
+
|
|
11
13
|
import { LaunchTemplateDetailsSection } from './LaunchTemplateDetailsSection';
|
|
12
14
|
import { MultipleInstanceTypesSubSection } from './MultipleInstanceTypesSubSection';
|
|
15
|
+
import type { IAmazonMixedInstancesPolicy, IAmazonServerGroupView, IScalingPolicy } from '../../../domain';
|
|
13
16
|
|
|
14
17
|
describe('Launch template details', () => {
|
|
15
18
|
let app: Application;
|
|
@@ -144,7 +147,7 @@ describe('Launch template details', () => {
|
|
|
144
147
|
]);
|
|
145
148
|
let index = 0;
|
|
146
149
|
expectedLabels.forEach((value, key) => {
|
|
147
|
-
|
|
150
|
+
const labeledValue = actualLabeledValues.at(index++);
|
|
148
151
|
expect(labeledValue.prop('label')).toEqual(key);
|
|
149
152
|
value != '' && expect(labeledValue.prop('value')).toEqual(value);
|
|
150
153
|
});
|
|
@@ -239,7 +242,7 @@ describe('Launch template details', () => {
|
|
|
239
242
|
});
|
|
240
243
|
|
|
241
244
|
it('should not render multiple instance types subsection when overrides are not specified', () => {
|
|
242
|
-
|
|
245
|
+
const testServerGroup = baseServerGroupWithMipOverrides;
|
|
243
246
|
testServerGroup.mixedInstancesPolicy.launchTemplateOverridesForInstanceType = null;
|
|
244
247
|
|
|
245
248
|
const multipleInstanceTypes = shallow(
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { IQService, IRootScopeService, IScope } from 'angular';
|
|
2
|
+
import { mock } from 'angular';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import type { IAmazonServerGroup, IScalingPolicyAlarmView, IStepAdjustment } from '../domain';
|
|
5
|
+
import type { AwsServerGroupTransformer } from './serverGroup.transformer';
|
|
6
|
+
import { AWS_SERVER_GROUP_TRANSFORMER } from './serverGroup.transformer';
|
|
5
7
|
import { VpcReader } from '../vpc/VpcReader';
|
|
6
8
|
|
|
7
9
|
describe('awsServerGroupTransformer', () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { module } from 'angular';
|
|
2
|
-
import { defaults } from 'lodash';
|
|
3
2
|
|
|
4
3
|
import type { IVpc } from '@spinnaker/core';
|
|
5
4
|
|
|
5
|
+
import type { IAmazonServerGroupCommand, IAmazonServerGroupDeployConfiguration } from './configure';
|
|
6
6
|
import type {
|
|
7
7
|
IAmazonServerGroup,
|
|
8
8
|
IAmazonServerGroupView,
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
IStepAdjustmentView,
|
|
14
14
|
ITargetTrackingPolicy,
|
|
15
15
|
} from '../domain';
|
|
16
|
+
|
|
16
17
|
import { VpcReader } from '../vpc/VpcReader';
|
|
17
18
|
|
|
18
19
|
export class AwsServerGroupTransformer {
|
|
@@ -84,31 +85,32 @@ export class AwsServerGroupTransformer {
|
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
public convertServerGroupCommandToDeployConfiguration(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
public convertServerGroupCommandToDeployConfiguration(
|
|
89
|
+
base: IAmazonServerGroupCommand,
|
|
90
|
+
): IAmazonServerGroupDeployConfiguration {
|
|
91
|
+
const deployConfig = ({ ...base } as any) as IAmazonServerGroupDeployConfiguration;
|
|
92
|
+
|
|
93
|
+
deployConfig.cloudProvider = 'aws';
|
|
94
|
+
deployConfig.availabilityZones = { [deployConfig.region]: base.availabilityZones };
|
|
95
|
+
deployConfig.loadBalancers = (base.loadBalancers || []).concat(base.vpcLoadBalancers || []);
|
|
96
|
+
deployConfig.targetGroups = base.targetGroups || [];
|
|
97
|
+
deployConfig.account = deployConfig.credentials;
|
|
98
|
+
deployConfig.subnetType = deployConfig.subnetType || '';
|
|
97
99
|
|
|
98
100
|
if (base.viewState.mode !== 'clone') {
|
|
99
|
-
delete
|
|
101
|
+
delete deployConfig.source;
|
|
100
102
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
|
|
104
|
+
if (!deployConfig.ramdiskId) {
|
|
105
|
+
delete deployConfig.ramdiskId; // TODO: clean up in kato? - should ignore if empty string
|
|
103
106
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return command;
|
|
107
|
+
|
|
108
|
+
const deleteFields = ['region', 'viewState', 'backingData', 'selectedProvider', 'instanceProfile', 'vpcId'];
|
|
109
|
+
deleteFields.forEach((key: keyof typeof deployConfig) => {
|
|
110
|
+
delete deployConfig[key];
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return deployConfig;
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
public constructNewStepScalingPolicyTemplate(serverGroup: IAmazonServerGroup): IScalingPolicy {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { shallow, render } from 'enzyme';
|
|
3
1
|
import { mock } from 'angular';
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
2
|
+
import { render, shallow } from 'enzyme';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import type { Application } from '@spinnaker/core';
|
|
6
|
+
import { ApplicationModelBuilder } from '@spinnaker/core';
|
|
6
7
|
import { mockServerGroupDataSourceConfig, mockSubnet } from '@spinnaker/mocks';
|
|
7
8
|
|
|
9
|
+
import { SubnetSelectInput } from './SubnetSelectInput';
|
|
10
|
+
|
|
8
11
|
describe('SubnetSelectInput', () => {
|
|
9
12
|
let application: Application;
|
|
10
13
|
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import { mockHttpClient } from 'core/api/mock/jasmine';
|
|
1
|
+
// eslint-disable-next-line @spinnaker/import-from-npm-not-relative
|
|
2
|
+
import { mockHttpClient } from '../../../core/src/api/mock/jasmine';
|
|
3
3
|
import { VpcReader } from '../vpc/VpcReader';
|
|
4
4
|
|
|
5
5
|
describe('VpcReader', function () {
|
|
6
|
-
var $scope;
|
|
7
|
-
|
|
8
|
-
beforeEach(
|
|
9
|
-
window.inject(function ($rootScope) {
|
|
10
|
-
$scope = $rootScope.$new();
|
|
11
|
-
}),
|
|
12
|
-
);
|
|
13
|
-
|
|
14
6
|
afterEach(function () {
|
|
15
7
|
VpcReader.resetCache();
|
|
16
8
|
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { mount } from 'enzyme';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { VpcTag } from './VpcTag';
|
|
5
|
+
import { VpcReader } from '../vpc/VpcReader';
|
|
6
|
+
|
|
7
|
+
const tick = () => new Promise((resolve) => setTimeout(resolve));
|
|
8
|
+
|
|
9
|
+
describe('VpcTag', function () {
|
|
10
|
+
describe('vpc tag rendering - no VPC provided', function () {
|
|
11
|
+
it('displays default message when no vpcId supplied', function () {
|
|
12
|
+
const component = mount(<VpcTag vpcId={undefined} />);
|
|
13
|
+
expect(component.text()).toBe('None (EC2 Classic)');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('displays default message when null vpcId supplied', function () {
|
|
17
|
+
const component = mount(<VpcTag vpcId={null} />);
|
|
18
|
+
expect(component.text()).toBe('None (EC2 Classic)');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('vpc tag rendering - VPC provided', function () {
|
|
23
|
+
it('displays vpc name when found', async function () {
|
|
24
|
+
spyOn(VpcReader, 'getVpcName').and.returnValue(Promise.resolve('Main VPC'));
|
|
25
|
+
const component = mount(<VpcTag vpcId="vpc-1" />);
|
|
26
|
+
await tick();
|
|
27
|
+
expect(component.text()).toBe('Main VPC (vpc-1)');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('displays vpc id when not found', async function () {
|
|
31
|
+
spyOn(VpcReader, 'getVpcName').and.returnValue(Promise.resolve(null));
|
|
32
|
+
const component = mount(<VpcTag vpcId="vpc-2" />);
|
|
33
|
+
await tick();
|
|
34
|
+
expect(component.text()).toBe('(vpc-2)');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
package/src/vpc/vpc.module.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { module } from 'angular';
|
|
2
|
-
import {
|
|
2
|
+
import { react2angular } from 'react2angular';
|
|
3
|
+
|
|
4
|
+
import { withErrorBoundary } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
import { VpcTag } from './VpcTag';
|
|
3
7
|
|
|
4
8
|
export const VPC_MODULE = 'spinnaker.amazon.vpc';
|
|
5
|
-
module(VPC_MODULE, [
|
|
9
|
+
module(VPC_MODULE, []).component('vpcTag', react2angular(withErrorBoundary(VpcTag, 'vpcTag'), ['vpcId']));
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
import { mockHttpClient } from 'core/api/mock/jasmine';
|
|
3
|
-
import { AwsImageReader } from './image.reader';
|
|
4
|
-
|
|
5
|
-
describe('Service: aws Image Reader', function () {
|
|
6
|
-
var service, scope;
|
|
7
|
-
|
|
8
|
-
beforeEach(
|
|
9
|
-
window.inject(function ($rootScope) {
|
|
10
|
-
service = new AwsImageReader();
|
|
11
|
-
scope = $rootScope.$new();
|
|
12
|
-
}),
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
describe('findImages', function () {
|
|
16
|
-
var query = 'abc',
|
|
17
|
-
region = 'us-west-1';
|
|
18
|
-
|
|
19
|
-
const buildQueryString = () => `/images/find?provider=aws&q=${query}®ion=${region}`;
|
|
20
|
-
|
|
21
|
-
it('queries gate when 3 characters are supplied', async function () {
|
|
22
|
-
const http = mockHttpClient();
|
|
23
|
-
var result = null;
|
|
24
|
-
|
|
25
|
-
http.expectGET(buildQueryString()).respond(200, [{ success: true }]);
|
|
26
|
-
|
|
27
|
-
service.findImages({ provider: 'aws', q: query, region: region }).then(function (results) {
|
|
28
|
-
result = results;
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
await http.flush();
|
|
32
|
-
|
|
33
|
-
expect(result.length).toBe(1);
|
|
34
|
-
expect(result[0].success).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('queries gate when more than 3 characters are supplied', async function () {
|
|
38
|
-
const http = mockHttpClient();
|
|
39
|
-
var result = null;
|
|
40
|
-
|
|
41
|
-
query = 'abcd';
|
|
42
|
-
|
|
43
|
-
http.expectGET(buildQueryString()).respond(200, [{ success: true }]);
|
|
44
|
-
|
|
45
|
-
var promise = service.findImages({ provider: 'aws', q: query, region: region });
|
|
46
|
-
|
|
47
|
-
promise.then(function (results) {
|
|
48
|
-
result = results;
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
await http.flush();
|
|
52
|
-
|
|
53
|
-
expect(result.length).toBe(1);
|
|
54
|
-
expect(result[0].success).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('returns a message prompting user to enter more characters when less than 3 are supplied', function () {
|
|
58
|
-
query = 'ab';
|
|
59
|
-
|
|
60
|
-
var result = null;
|
|
61
|
-
|
|
62
|
-
service.findImages({ provider: 'aws', q: query, region: region }).then(function (results) {
|
|
63
|
-
result = results;
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
scope.$digest();
|
|
67
|
-
|
|
68
|
-
expect(result.length).toBe(1);
|
|
69
|
-
expect(result[0].message).toBe('Please enter at least 3 characters...');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('returns an empty array when server errors', async function () {
|
|
73
|
-
const http = mockHttpClient();
|
|
74
|
-
query = 'abc';
|
|
75
|
-
var result = null;
|
|
76
|
-
|
|
77
|
-
http.expectGET(buildQueryString()).respond(404, {});
|
|
78
|
-
|
|
79
|
-
service.findImages({ provider: 'aws', q: query, region: region }).then(function (results) {
|
|
80
|
-
result = results;
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
await http.flush();
|
|
84
|
-
|
|
85
|
-
expect(result.length).toBe(0);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
describe('getImage', function () {
|
|
90
|
-
var imageName = 'abc',
|
|
91
|
-
region = 'us-west-1',
|
|
92
|
-
credentials = 'test';
|
|
93
|
-
|
|
94
|
-
const buildQueryString = () => `/images/${credentials}/${region}/${imageName}?provider=aws`;
|
|
95
|
-
|
|
96
|
-
it('returns null if server returns 404 or an empty list', async function () {
|
|
97
|
-
const http = mockHttpClient();
|
|
98
|
-
var result = 'not null';
|
|
99
|
-
|
|
100
|
-
http.expectGET(buildQueryString()).respond(404, {});
|
|
101
|
-
|
|
102
|
-
service.getImage(imageName, region, credentials).then(function (results) {
|
|
103
|
-
result = results;
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
await http.flush();
|
|
107
|
-
|
|
108
|
-
expect(result).toBe(null);
|
|
109
|
-
|
|
110
|
-
result = 'not null';
|
|
111
|
-
|
|
112
|
-
http.expectGET(buildQueryString()).respond(200, []);
|
|
113
|
-
|
|
114
|
-
service.getImage(imageName, region, credentials).then(function (results) {
|
|
115
|
-
result = results;
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
await http.flush();
|
|
119
|
-
|
|
120
|
-
expect(result).toBe(null);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
});
|