@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.
Files changed (75) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/domain/IAmazonLaunchTemplate.d.ts +1 -1
  3. package/dist/domain/IAmazonServerGroup.d.ts +10 -0
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/instance/awsInstanceType.service.d.ts +10 -2
  7. package/dist/instance/awsInstanceTypes.d.ts +10 -0
  8. package/dist/instance/details/CostFactor.d.ts +6 -0
  9. package/dist/reactShims/aws.react.injector.d.ts +2 -1
  10. package/dist/search/searchResultFormatter.d.ts +2 -2
  11. package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +17 -2
  12. package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +39 -0
  13. package/dist/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.d.ts +3 -4
  14. package/dist/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.d.ts +9 -0
  15. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.d.ts +13 -0
  16. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.d.ts +9 -0
  17. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.d.ts +11 -0
  18. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.d.ts +14 -0
  19. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.d.ts +12 -0
  20. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.d.ts +15 -0
  21. package/dist/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.d.ts +8 -0
  22. package/dist/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.d.ts +8 -0
  23. package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +9 -6
  24. package/dist/serverGroup/serverGroup.transformer.d.ts +2 -1
  25. package/package.json +5 -5
  26. package/src/domain/IAmazonLaunchTemplate.ts +1 -1
  27. package/src/domain/IAmazonServerGroup.ts +11 -0
  28. package/src/function/details/FunctionActions.spec.tsx +10 -9
  29. package/src/help/amazon.help.ts +9 -6
  30. package/src/image/image.reader.spec.ts +75 -0
  31. package/src/instance/awsInstanceType.service.spec.js +37 -71
  32. package/src/instance/awsInstanceType.service.ts +191 -0
  33. package/src/instance/awsInstanceTypes.ts +311 -0
  34. package/src/instance/details/CostFactor.tsx +29 -0
  35. package/src/instance/details/InstanceInformation.spec.tsx +2 -1
  36. package/src/instance/details/instance.details.controller.js +471 -473
  37. package/src/loadBalancer/details/loadBalancerDetails.controller.spec.ts +5 -3
  38. package/src/reactShims/aws.react.injector.ts +2 -1
  39. package/src/search/{searchResultFormatter.js → searchResultFormatter.ts} +5 -4
  40. package/src/serverGroup/configure/AmazonImageSelectInput.spec.tsx +10 -7
  41. package/src/serverGroup/configure/serverGroupCommandBuilder.aws.service.spec.js +302 -1
  42. package/src/serverGroup/configure/{serverGroupCommandBuilder.service.js → serverGroupCommandBuilder.service.ts} +182 -73
  43. package/src/serverGroup/configure/serverGroupCommandBuilder.spec.js +25 -8
  44. package/src/serverGroup/configure/serverGroupConfiguration.service.spec.ts +6 -13
  45. package/src/serverGroup/configure/serverGroupConfiguration.service.ts +53 -0
  46. package/src/serverGroup/configure/wizard/AmazonCloneServerGroupModal.tsx +1 -1
  47. package/src/serverGroup/configure/wizard/instanceType/CpuCreditsToggle.tsx +31 -31
  48. package/src/serverGroup/configure/wizard/instanceType/InstanceTypeSelector.tsx +133 -0
  49. package/src/serverGroup/configure/wizard/instanceType/advancedMode/AdvancedModeSelector.tsx +99 -0
  50. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceProfileSelector.tsx +36 -0
  51. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeRow.tsx +144 -0
  52. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTable.tsx +138 -0
  53. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableBody.tsx +135 -0
  54. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstanceTypeTableParts.tsx +137 -0
  55. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.less +5 -0
  56. package/src/serverGroup/configure/wizard/instanceType/advancedMode/InstancesDistribution.tsx +108 -0
  57. package/src/serverGroup/configure/wizard/instanceType/advancedMode/advancedMode.less +110 -0
  58. package/src/serverGroup/configure/wizard/instanceType/simpleMode/SimpleModeSelector.tsx +62 -0
  59. package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +107 -62
  60. package/src/serverGroup/configure/wizard/pages/advancedSettings/ServerGroupAdvancedSettingsCommon.tsx +1 -1
  61. package/src/serverGroup/details/scalingPolicy/scalingPolicySummary.component.ts +6 -2
  62. package/src/serverGroup/details/scalingProcesses/AutoScalingProcessService.spec.ts +1 -2
  63. package/src/serverGroup/details/sections/InstancesDistributionDetailsSection.spec.tsx +8 -5
  64. package/src/serverGroup/details/sections/LaunchTemplateDetailsSection.spec.tsx +8 -5
  65. package/src/serverGroup/serverGroup.transformer.spec.ts +5 -3
  66. package/src/serverGroup/serverGroup.transformer.ts +24 -22
  67. package/src/subnet/SubnetSelectInput.spec.tsx +7 -4
  68. package/src/vpc/{VpcReader.spec.js → VpcReader.spec.ts} +2 -10
  69. package/src/vpc/VpcTag.spec.tsx +37 -0
  70. package/src/vpc/vpc.module.ts +6 -2
  71. package/dist/vpc/vpcTag.directive.d.ts +0 -2
  72. package/src/image/image.reader.spec.js +0 -123
  73. package/src/instance/awsInstanceType.service.js +0 -437
  74. package/src/vpc/vpcTag.directive.js +0 -34
  75. 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 { CpuCreditsToggle } from '../instanceType/CpuCreditsToggle';
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
- newInstanceType?: string;
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
- constructor(props: IServerGroupInstanceTypeProps) {
24
- super(props);
25
- this.state = {
26
- newInstanceType: undefined,
27
- newProfileType: undefined,
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 (!values.instanceType) {
35
- errors.instanceType = 'Instance Type required.';
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 instanceProfileChanged = (_profile: string) => {
42
- // Instance profile is already set on values.viewState, so just use that value.
43
- // Once angular is gone from this component tree, we can move all the viewState stuff
44
- // into react state
45
- this.setState({ newProfileType: _profile, newInstanceType: undefined });
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
- private instanceTypeChanged = (type: string) => {
49
- const { values } = this.props.formik;
50
- values.instanceTypeChanged(values);
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
- private setUnlimitedCpuCredits = (unlimitedCpuCredits: boolean | undefined) => {
56
- if (this.props.formik.values.unlimitedCpuCredits !== unlimitedCpuCredits) {
57
- this.props.formik.setFieldValue('unlimitedCpuCredits', unlimitedCpuCredits);
58
- this.setState({});
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
- const isLaunchTemplatesEnabled = AWSProviderSettings.serverGroups?.enableLaunchTemplates;
69
- const isCpuCreditsEnabled = AWSProviderSettings.serverGroups?.enableCpuCredits;
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 { StepPolicySummary } from './StepPolicySummary';
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(StepPolicySummary, 'scalingPolicySummary'), ['application', 'policy', 'serverGroup']),
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
- import { IAmazonMixedInstancesPolicy, IAmazonServerGroupView, IScalingPolicy } from '../../../domain';
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
- let labeledValue = actualLabeledValues.at(index++);
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
- let labeledValue = actualLabeledValues.at(index++);
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
- import { Application, ApplicationModelBuilder } from '@spinnaker/core';
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
- let labeledValue = actualLabeledValues.at(index++);
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
- let testServerGroup = baseServerGroupWithMipOverrides;
245
+ const testServerGroup = baseServerGroupWithMipOverrides;
243
246
  testServerGroup.mixedInstancesPolicy.launchTemplateOverridesForInstanceType = null;
244
247
 
245
248
  const multipleInstanceTypes = shallow(
@@ -1,7 +1,9 @@
1
- import { mock, IQService, IRootScopeService, IScope } from 'angular';
1
+ import type { IQService, IRootScopeService, IScope } from 'angular';
2
+ import { mock } from 'angular';
2
3
 
3
- import { AWS_SERVER_GROUP_TRANSFORMER, AwsServerGroupTransformer } from './serverGroup.transformer';
4
- import { IScalingPolicyAlarmView, IAmazonServerGroup, IStepAdjustment } from '../domain';
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(base: any): any {
88
- // use _.defaults to avoid copying the backingData, which is huge and expensive to copy over
89
- const command = defaults({ backingData: [], viewState: [] }, base);
90
- command.cloudProvider = 'aws';
91
- command.availabilityZones = {};
92
- command.availabilityZones[command.region] = base.availabilityZones;
93
- command.loadBalancers = (base.loadBalancers || []).concat(base.vpcLoadBalancers || []);
94
- command.targetGroups = base.targetGroups || [];
95
- command.account = command.credentials;
96
- command.subnetType = command.subnetType || '';
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 command.source;
101
+ delete deployConfig.source;
100
102
  }
101
- if (!command.ramdiskId) {
102
- delete command.ramdiskId; // TODO: clean up in kato? - should ignore if empty string
103
+
104
+ if (!deployConfig.ramdiskId) {
105
+ delete deployConfig.ramdiskId; // TODO: clean up in kato? - should ignore if empty string
103
106
  }
104
- delete command.region;
105
- delete command.viewState;
106
- delete command.backingData;
107
- delete command.selectedProvider;
108
- delete command.instanceProfile;
109
- delete command.vpcId;
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 { SubnetSelectInput } from './SubnetSelectInput';
5
- import { Application, ApplicationModelBuilder } from '@spinnaker/core';
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
- 'use strict';
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
+ });
@@ -1,5 +1,9 @@
1
1
  import { module } from 'angular';
2
- import { AMAZON_VPC_VPCTAG_DIRECTIVE } from './vpcTag.directive';
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, [AMAZON_VPC_VPCTAG_DIRECTIVE]);
9
+ module(VPC_MODULE, []).component('vpcTag', react2angular(withErrorBoundary(VpcTag, 'vpcTag'), ['vpcId']));
@@ -1,2 +0,0 @@
1
- export const AMAZON_VPC_VPCTAG_DIRECTIVE: "spinnaker.amazon.vpc.tag.directive";
2
- export const name: "spinnaker.amazon.vpc.tag.directive";
@@ -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}&region=${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
- });