@spinnaker/amazon 0.12.0 → 0.12.4

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.
@@ -3,11 +3,12 @@ import IInjectorService = angular.auto.IInjectorService;
3
3
  import type { FunctionReader } from '@spinnaker/core';
4
4
  import { ReactInject } from '@spinnaker/core';
5
5
  import type { EvaluateCloudFormationChangeSetExecutionService } from '../pipeline/stages/deployCloudFormation/evaluateCloudFormationChangeSetExecution.service';
6
+ import type { AwsServerGroupCommandBuilder } from '../serverGroup/configure/serverGroupCommandBuilder.service';
6
7
  import type { AwsServerGroupConfigurationService } from '../serverGroup/configure/serverGroupConfiguration.service';
7
8
  import type { AwsServerGroupTransformer } from '../serverGroup/serverGroup.transformer';
8
9
  export declare class AwsReactInject extends ReactInject {
9
10
  get awsInstanceTypeService(): any;
10
- get awsServerGroupCommandBuilder(): any;
11
+ get awsServerGroupCommandBuilder(): AwsServerGroupCommandBuilder;
11
12
  get awsServerGroupConfigurationService(): AwsServerGroupConfigurationService;
12
13
  get awsServerGroupTransformer(): AwsServerGroupTransformer;
13
14
  get functionReader(): FunctionReader;
@@ -1,2 +1,17 @@
1
- export const AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE: "spinnaker.amazon.serverGroupCommandBuilder.service";
2
- export const name: "spinnaker.amazon.serverGroupCommandBuilder.service";
1
+ import type { Application } from '@spinnaker/core';
2
+ import type { IAmazonServerGroup, IAmazonServerGroupView } from '../../domain';
3
+ import type { IAmazonServerGroupCommand, IAmazonServerGroupDeployConfiguration } from './serverGroupConfiguration.service';
4
+ export declare const AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE = "spinnaker.amazon.serverGroupCommandBuilder.service";
5
+ export declare const name = "spinnaker.amazon.serverGroupCommandBuilder.service";
6
+ export interface AwsServerGroupCommandBuilder {
7
+ buildNewServerGroupCommand(application: Application, defaults?: {
8
+ account?: string;
9
+ region?: string;
10
+ subnet?: string;
11
+ mode?: string;
12
+ }): PromiseLike<Partial<IAmazonServerGroupCommand>>;
13
+ buildServerGroupCommandFromExisting(application: Application, serverGroup: IAmazonServerGroupView, mode?: string): PromiseLike<Partial<IAmazonServerGroupCommand>>;
14
+ buildNewServerGroupCommandForPipeline(): PromiseLike<Partial<IAmazonServerGroupCommand>>;
15
+ buildServerGroupCommandFromPipeline(application: Application, originalCluster: IAmazonServerGroupDeployConfiguration): PromiseLike<Partial<IAmazonServerGroupCommand>>;
16
+ buildUpdateServerGroupCommand(serverGroup: IAmazonServerGroup): Partial<IAmazonServerGroupCommand>;
17
+ }
@@ -19,6 +19,7 @@ export interface IAmazonServerGroupCommandBackingData extends IServerGroupComman
19
19
  scalingProcesses: IScalingProcess[];
20
20
  }
21
21
  export interface IAmazonServerGroupCommandViewState extends IServerGroupCommandViewState {
22
+ isNew: boolean;
22
23
  dirty: IAmazonServerGroupCommandDirty;
23
24
  spelTargetGroups: string[];
24
25
  spelLoadBalancers: string[];
@@ -26,14 +27,31 @@ export interface IAmazonServerGroupCommandViewState extends IServerGroupCommandV
26
27
  }
27
28
  export interface IAmazonInstanceTypeOverride {
28
29
  instanceType: string;
29
- weightedCapacity?: number;
30
+ weightedCapacity?: string;
30
31
  priority?: number;
31
32
  }
33
+ /**
34
+ * We model server group commands in two subtly different ways.
35
+ * It's unclear what the intention is, but we should converge on a single model eventually.
36
+ *
37
+ * This model is stored in a Aws Deploy Stage under `clusters[]`.
38
+ * It is also sent across the wire during a deploy task from infrastructure view
39
+ * (i.e., "clone server group", "create server group")
40
+ */
41
+ export interface IAmazonServerGroupDeployConfiguration extends IAmazonServerGroupCommand_Internal {
42
+ account: string;
43
+ availabilityZones: {
44
+ [region: string]: string[];
45
+ };
46
+ }
47
+ interface IAmazonServerGroupCommand_Internal extends IAmazonServerGroupCommand {
48
+ availabilityZones: any;
49
+ }
32
50
  export interface IAmazonServerGroupCommand extends IServerGroupCommand {
33
- viewState: IAmazonServerGroupCommandViewState;
34
51
  associateIPv6Address?: boolean;
35
52
  associatePublicIpAddress: boolean;
36
53
  backingData: IAmazonServerGroupCommandBackingData;
54
+ base64UserData: string;
37
55
  copySourceCustomBlockDeviceMappings: boolean;
38
56
  ebsOptimized: boolean;
39
57
  healthCheckGracePeriod: number;
@@ -48,6 +66,13 @@ export interface IAmazonServerGroupCommand extends IServerGroupCommand {
48
66
  setLaunchTemplate?: boolean;
49
67
  unlimitedCpuCredits?: boolean;
50
68
  capacityRebalance?: boolean;
69
+ ramdiskId?: string;
70
+ viewState: IAmazonServerGroupCommandViewState;
71
+ source: {
72
+ account: string;
73
+ region: string;
74
+ asgName: string;
75
+ };
51
76
  onDemandAllocationStrategy?: string;
52
77
  onDemandBaseCapacity?: number;
53
78
  onDemandPercentageAboveBaseCapacity?: number;
@@ -93,3 +118,4 @@ export declare class AwsServerGroupConfigurationService {
93
118
  attachEventHandlers(cmd: IAmazonServerGroupCommand): void;
94
119
  }
95
120
  export declare const AWS_SERVER_GROUP_CONFIGURATION_SERVICE = "spinnaker.amazon.serverGroup.configure.service";
121
+ export {};
@@ -1,3 +1,4 @@
1
+ import type { IAmazonServerGroupCommand, IAmazonServerGroupDeployConfiguration } from './configure';
1
2
  import type { IAmazonServerGroup, IAmazonServerGroupView, IScalingPolicy, IScalingPolicyView, ITargetTrackingPolicy } from '../domain';
2
3
  export declare class AwsServerGroupTransformer {
3
4
  private addComparator;
@@ -6,7 +7,7 @@ export declare class AwsServerGroupTransformer {
6
7
  normalizeServerGroupDetails(serverGroup: IAmazonServerGroup): IAmazonServerGroupView;
7
8
  normalizeServerGroup(serverGroup: IAmazonServerGroup): PromiseLike<IAmazonServerGroup>;
8
9
  private addVpcNameToServerGroup;
9
- convertServerGroupCommandToDeployConfiguration(base: any): any;
10
+ convertServerGroupCommandToDeployConfiguration(base: IAmazonServerGroupCommand): IAmazonServerGroupDeployConfiguration;
10
11
  constructNewStepScalingPolicyTemplate(serverGroup: IAmazonServerGroup): IScalingPolicy;
11
12
  constructNewTargetTrackingPolicyTemplate(): ITargetTrackingPolicy;
12
13
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@spinnaker/amazon",
3
3
  "license": "Apache-2.0",
4
- "version": "0.12.0",
4
+ "version": "0.12.4",
5
5
  "module": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
7
7
  "scripts": {
@@ -13,7 +13,7 @@
13
13
  "lib": "npm run build"
14
14
  },
15
15
  "dependencies": {
16
- "@spinnaker/core": "^0.15.0",
16
+ "@spinnaker/core": "^0.17.0",
17
17
  "@uirouter/angularjs": "1.0.26",
18
18
  "@uirouter/core": "6.0.8",
19
19
  "@uirouter/react": "1.0.7",
@@ -41,7 +41,7 @@
41
41
  "devDependencies": {
42
42
  "@spinnaker/eslint-plugin": "^3.0.1",
43
43
  "@spinnaker/mocks": "1.0.7",
44
- "@spinnaker/scripts": "^0.2.3",
44
+ "@spinnaker/scripts": "^0.2.4",
45
45
  "@types/angular": "1.6.26",
46
46
  "@types/angular-ui-bootstrap": "0.13.41",
47
47
  "@types/classnames": "2.2.0",
@@ -55,5 +55,5 @@
55
55
  "shx": "0.3.3",
56
56
  "typescript": "4.3.5"
57
57
  },
58
- "gitHead": "c4b42b16cee7d8746087e9e1bcc4b00751dd4bc7"
58
+ "gitHead": "fd69b27a052ccb2a170efdbe0b971a8636c96447"
59
59
  }
@@ -39,7 +39,7 @@ export interface ILicenseConfig {
39
39
  export interface IMetadataOptions {
40
40
  httpEndpoint?: 'disabled' | 'enabled';
41
41
  httpPutResponseHopLimit?: number;
42
- httpsTokens?: 'required' | 'optional';
42
+ httpTokens?: 'required' | 'optional';
43
43
  state?: 'pending' | 'applied';
44
44
  }
45
45
 
@@ -5,16 +5,27 @@ import type { IScalingPolicyView } from './IAmazonScalingPolicy';
5
5
  import type { IScalingPolicy } from './IScalingPolicy';
6
6
  import type { ISuspendedProcess } from './IScalingProcess';
7
7
 
8
+ export interface IAmazonAsgTag {
9
+ key: string;
10
+ value: string;
11
+ propagateAtLaunch: boolean;
12
+ resourceId: string;
13
+ resourceType: string;
14
+ }
15
+
8
16
  export interface IAmazonAsg extends IAsg {
9
17
  availabilityZones: string[];
18
+ autoScalingGroupName: string;
10
19
  defaultCooldown: number;
11
20
  healthCheckType: string;
12
21
  healthCheckGracePeriod: number;
22
+ loadBalancerNames: string[];
13
23
  terminationPolicies: string[];
14
24
  enabledMetrics: Array<{ metric: string }>;
15
25
  vpczoneIdentifier?: string;
16
26
  suspendedProcesses?: ISuspendedProcess[];
17
27
  capacityRebalance?: boolean;
28
+ tags: IAmazonAsgTag[];
18
29
  }
19
30
 
20
31
  export interface IAmazonServerGroup extends IServerGroup {
@@ -3,6 +3,7 @@ import IInjectorService = angular.auto.IInjectorService;
3
3
  import type { FunctionReader } from '@spinnaker/core';
4
4
  import { ReactInject } from '@spinnaker/core';
5
5
  import type { EvaluateCloudFormationChangeSetExecutionService } from '../pipeline/stages/deployCloudFormation/evaluateCloudFormationChangeSetExecution.service';
6
+ import type { AwsServerGroupCommandBuilder } from '../serverGroup/configure/serverGroupCommandBuilder.service';
6
7
 
7
8
  import type { AwsServerGroupConfigurationService } from '../serverGroup/configure/serverGroupConfiguration.service';
8
9
  import type { AwsServerGroupTransformer } from '../serverGroup/serverGroup.transformer';
@@ -10,7 +11,7 @@ import type { AwsServerGroupTransformer } from '../serverGroup/serverGroup.trans
10
11
  // prettier-ignore
11
12
  export class AwsReactInject extends ReactInject {
12
13
  public get awsInstanceTypeService() { return this.$injector.get('awsInstanceTypeService') as any; }
13
- public get awsServerGroupCommandBuilder() { return this.$injector.get('awsServerGroupCommandBuilder') as any; }
14
+ public get awsServerGroupCommandBuilder() { return this.$injector.get('awsServerGroupCommandBuilder') as AwsServerGroupCommandBuilder; }
14
15
  public get awsServerGroupConfigurationService() { return this.$injector.get('awsServerGroupConfigurationService') as AwsServerGroupConfigurationService; }
15
16
  public get awsServerGroupTransformer() { return this.$injector.get('awsServerGroupTransformer') as AwsServerGroupTransformer; }
16
17
 
@@ -1,6 +1,8 @@
1
+ import type { IQService } from 'angular';
1
2
  import * as angular from 'angular';
2
3
  import _ from 'lodash';
3
4
 
5
+ import type { Application, InstanceTypeService } from '@spinnaker/core';
4
6
  import {
5
7
  AccountService,
6
8
  DeploymentStrategyRegistry,
@@ -8,13 +10,45 @@ import {
8
10
  NameUtils,
9
11
  SubnetReader,
10
12
  } from '@spinnaker/core';
11
- import { AWSProviderSettings } from '../../aws.settings';
12
13
 
14
+ import { AWSProviderSettings } from '../../aws.settings';
15
+ import type { IAmazonLaunchTemplateOverrides, ILaunchTemplateData } from '../../domain';
16
+ import type { IAmazonServerGroup, IAmazonServerGroupView, INetworkInterface } from '../../domain';
17
+ import type {
18
+ AwsServerGroupConfigurationService,
19
+ IAmazonInstanceTypeOverride,
20
+ IAmazonServerGroupCommand,
21
+ IAmazonServerGroupCommandViewState,
22
+ IAmazonServerGroupDeployConfiguration,
23
+ } from './serverGroupConfiguration.service';
13
24
  import { AWS_SERVER_GROUP_CONFIGURATION_SERVICE } from './serverGroupConfiguration.service';
14
25
 
15
26
  export const AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE =
16
27
  'spinnaker.amazon.serverGroupCommandBuilder.service';
17
28
  export const name = AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE; // for backwards compatibility
29
+
30
+ export interface AwsServerGroupCommandBuilder {
31
+ buildNewServerGroupCommand(
32
+ application: Application,
33
+ defaults?: { account?: string; region?: string; subnet?: string; mode?: string },
34
+ ): PromiseLike<Partial<IAmazonServerGroupCommand>>;
35
+
36
+ buildServerGroupCommandFromExisting(
37
+ application: Application,
38
+ serverGroup: IAmazonServerGroupView,
39
+ mode?: string,
40
+ ): PromiseLike<Partial<IAmazonServerGroupCommand>>;
41
+
42
+ buildNewServerGroupCommandForPipeline(): PromiseLike<Partial<IAmazonServerGroupCommand>>;
43
+
44
+ buildServerGroupCommandFromPipeline(
45
+ application: Application,
46
+ originalCluster: IAmazonServerGroupDeployConfiguration,
47
+ ): PromiseLike<Partial<IAmazonServerGroupCommand>>;
48
+
49
+ buildUpdateServerGroupCommand(serverGroup: IAmazonServerGroup): Partial<IAmazonServerGroupCommand>;
50
+ }
51
+
18
52
  angular
19
53
  .module(AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE, [
20
54
  INSTANCE_TYPE_SERVICE,
@@ -24,9 +58,15 @@ angular
24
58
  '$q',
25
59
  'instanceTypeService',
26
60
  'awsServerGroupConfigurationService',
27
- function ($q, instanceTypeService, awsServerGroupConfigurationService) {
28
- function buildNewServerGroupCommand(application, defaults) {
29
- defaults = defaults || {};
61
+ function (
62
+ $q: IQService,
63
+ instanceTypeService: InstanceTypeService,
64
+ awsServerGroupConfigurationService: AwsServerGroupConfigurationService,
65
+ ) {
66
+ function buildNewServerGroupCommand(
67
+ application: Application,
68
+ defaults: { account?: string; region?: string; subnet?: string; mode?: string } = {},
69
+ ) {
30
70
  const credentialsLoader = AccountService.getCredentialsKeyedByAccount('aws');
31
71
 
32
72
  const defaultCredentials =
@@ -45,14 +85,14 @@ angular
45
85
  .then(function ([preferredZones, credentialsKeyedByAccount]) {
46
86
  const credentials = credentialsKeyedByAccount[defaultCredentials];
47
87
  const keyPair = credentials ? credentials.defaultKeyPair : null;
48
- const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});
88
+ const applicationAwsSettings = application.attributes?.providerSettings?.aws ?? {};
49
89
 
50
90
  let defaultIamRole = AWSProviderSettings.defaults.iamRole || 'BaseIAMRole';
51
91
  defaultIamRole = defaultIamRole.replace('{{application}}', application.name);
52
92
 
53
93
  const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;
54
94
 
55
- const command = {
95
+ const command: Partial<IAmazonServerGroupCommand> = {
56
96
  application: application.name,
57
97
  credentials: defaultCredentials,
58
98
  region: defaultRegion,
@@ -94,45 +134,39 @@ angular
94
134
  disableStrategySelection: true,
95
135
  dirty: {},
96
136
  submitButtonLabel: getSubmitButtonLabel(defaults.mode || 'create'),
97
- },
137
+ } as IAmazonServerGroupCommandViewState,
98
138
  };
99
139
 
100
- if (
101
- application.attributes &&
102
- application.attributes.platformHealthOnlyShowOverride &&
103
- application.attributes.platformHealthOnly
104
- ) {
140
+ if (application.attributes?.platformHealthOnlyShowOverride && application.attributes?.platformHealthOnly) {
105
141
  command.interestingHealthProviderNames = ['Amazon'];
106
142
  }
107
143
 
108
- if (
109
- defaultCredentials === 'test' &&
110
- AWSProviderSettings.serverGroups &&
111
- AWSProviderSettings.serverGroups.enableIPv6
112
- ) {
144
+ if (defaultCredentials === 'test' && AWSProviderSettings.serverGroups?.enableIPv6) {
113
145
  command.associateIPv6Address = true;
114
146
  }
115
147
 
116
- if (AWSProviderSettings.serverGroups && AWSProviderSettings.serverGroups.enableIMDSv2) {
148
+ if (AWSProviderSettings.serverGroups?.enableIMDSv2) {
117
149
  /**
118
150
  * Older SDKs do not support IMDSv2. A timestamp can be optionally configured at which any apps created after can safely default to using IMDSv2.
119
151
  */
120
152
  const appAgeRequirement = AWSProviderSettings.serverGroups.defaultIMDSv2AppAgeLimit;
121
- const creationDate = application.attributes && application.attributes.createTs;
153
+ const creationDate = application.attributes?.createTs;
122
154
 
123
- command.requireIMDSv2 =
124
- appAgeRequirement && creationDate && Number(creationDate) > appAgeRequirement ? true : false;
155
+ command.requireIMDSv2 = appAgeRequirement && creationDate && Number(creationDate) > appAgeRequirement;
125
156
  }
126
157
 
127
158
  return command;
128
159
  });
129
160
  }
130
161
 
131
- function buildServerGroupCommandFromPipeline(application, originalCluster) {
162
+ function buildServerGroupCommandFromPipeline(
163
+ application: Application,
164
+ originalCluster: IAmazonServerGroupDeployConfiguration,
165
+ ) {
132
166
  const pipelineCluster = _.cloneDeep(originalCluster);
133
167
  const region = Object.keys(pipelineCluster.availabilityZones)[0];
134
168
 
135
- let instanceTypes = pipelineCluster.launchTemplateOverridesForInstanceType
169
+ const instanceTypes = pipelineCluster.launchTemplateOverridesForInstanceType
136
170
  ? pipelineCluster.launchTemplateOverridesForInstanceType.map((o) => o.instanceType)
137
171
  : [pipelineCluster.instanceType];
138
172
  const instanceTypeCategoryLoader = instanceTypeService.getCategoryForMultipleInstanceTypes(
@@ -184,11 +218,11 @@ angular
184
218
  return $q.when({
185
219
  viewState: {
186
220
  requiresTemplateSelection: true,
187
- },
221
+ } as IAmazonServerGroupCommandViewState,
188
222
  });
189
223
  }
190
224
 
191
- function getSubmitButtonLabel(mode) {
225
+ function getSubmitButtonLabel(mode: string) {
192
226
  switch (mode) {
193
227
  case 'createPipeline':
194
228
  return 'Add';
@@ -201,33 +235,39 @@ angular
201
235
  }
202
236
  }
203
237
 
204
- function buildUpdateServerGroupCommand(serverGroup) {
205
- const command = {
238
+ function buildUpdateServerGroupCommand(serverGroup: IAmazonServerGroup) {
239
+ const command = ({
206
240
  type: 'modifyAsg',
207
241
  asgs: [{ asgName: serverGroup.name, region: serverGroup.region }],
208
242
  cooldown: serverGroup.asg.defaultCooldown,
209
- enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map((m) => m.metric),
243
+ enabledMetrics: (serverGroup.asg?.enabledMetrics ?? []).map((m) => m.metric),
210
244
  healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
211
245
  healthCheckType: serverGroup.asg.healthCheckType,
212
246
  terminationPolicies: angular.copy(serverGroup.asg.terminationPolicies),
213
247
  credentials: serverGroup.account,
214
248
  capacityRebalance: serverGroup.asg.capacityRebalance,
215
- };
249
+ } as Partial<IAmazonServerGroupCommand>) as IAmazonServerGroupCommand;
216
250
  awsServerGroupConfigurationService.configureUpdateCommand(command);
217
251
  return command;
218
252
  }
219
253
 
220
- function buildServerGroupCommandFromExisting(application, serverGroup, mode = 'clone') {
254
+ function buildServerGroupCommandFromExisting(
255
+ application: Application,
256
+ serverGroup: IAmazonServerGroupView,
257
+ mode = 'clone',
258
+ ) {
221
259
  const preferredZonesLoader = AccountService.getPreferredZonesByAccount('aws');
222
260
  const subnetsLoader = SubnetReader.listSubnets();
261
+
223
262
  const serverGroupName = NameUtils.parseServerGroupName(serverGroup.asg.autoScalingGroupName);
224
263
 
225
264
  let instanceTypes;
226
265
  if (serverGroup.mixedInstancesPolicy) {
227
- const ltOverrides = _.get(serverGroup, 'mixedInstancesPolicy.launchTemplateOverridesForInstanceType');
266
+ const ltOverrides = serverGroup.mixedInstancesPolicy?.launchTemplateOverridesForInstanceType;
267
+ // note: single launch template case is currently the only supported case for mixed instances policy
228
268
  instanceTypes = ltOverrides
229
269
  ? ltOverrides.map((o) => o.instanceType)
230
- : [_.get(serverGroup, 'mixedInstancesPolicy.launchTemplates[0].launchTemplateData.instanceType')]; // note: single launch template case is currently the only supported case for mixed instances policy
270
+ : [serverGroup.mixedInstancesPolicy?.launchTemplates[0]?.launchTemplateData?.instanceType];
231
271
  } else if (serverGroup.launchTemplate) {
232
272
  instanceTypes = [_.get(serverGroup, 'launchTemplate.launchTemplateData.instanceType')];
233
273
  } else if (serverGroup.launchConfig) {
@@ -240,7 +280,7 @@ angular
240
280
 
241
281
  return $q
242
282
  .all([preferredZonesLoader, subnetsLoader, instanceTypeCategoryLoader])
243
- .then(function ([preferredZones, subnets, instanceProfile]) {
283
+ .then(([preferredZones, subnets, instanceProfile]) => {
244
284
  const zones = serverGroup.asg.availabilityZones.sort();
245
285
  let usePreferredZones = false;
246
286
  const preferredZonesForAccount = preferredZones[serverGroup.account];
@@ -252,10 +292,10 @@ angular
252
292
  // These processes should never be copied over, as the affect launching instances and enabling traffic
253
293
  const enabledProcesses = ['Launch', 'Terminate', 'AddToLoadBalancer'];
254
294
 
255
- const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});
295
+ const applicationAwsSettings = application.attributes?.providerSettings?.aws ?? {};
256
296
  const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;
257
297
 
258
- const existingTags = {};
298
+ const existingTags: { [key: string]: string } = {};
259
299
  // These tags are applied by Clouddriver (if configured to do so), regardless of what the user might enter
260
300
  // Might be worth feature flagging this if it turns out other folks are hard-coding these values
261
301
  const reservedTags = ['spinnaker:application', 'spinnaker:stack', 'spinnaker:details'];
@@ -267,7 +307,7 @@ angular
267
307
  });
268
308
  }
269
309
 
270
- const command = {
310
+ const command: Partial<IAmazonServerGroupCommand> = {
271
311
  application: application.name,
272
312
  strategy: '',
273
313
  stack: serverGroupName.stack,
@@ -310,14 +350,10 @@ angular
310
350
  submitButtonLabel: getSubmitButtonLabel(mode),
311
351
  isNew: false,
312
352
  dirty: {},
313
- },
353
+ } as IAmazonServerGroupCommandViewState,
314
354
  };
315
355
 
316
- if (
317
- application.attributes &&
318
- application.attributes.platformHealthOnlyShowOverride &&
319
- application.attributes.platformHealthOnly
320
- ) {
356
+ if (application.attributes?.platformHealthOnlyShowOverride && application.attributes?.platformHealthOnly) {
321
357
  command.interestingHealthProviderNames = ['Amazon'];
322
358
  }
323
359
 
@@ -333,7 +369,7 @@ angular
333
369
  const vpcZoneIdentifier = serverGroup.asg.vpczoneIdentifier;
334
370
  if (vpcZoneIdentifier !== '') {
335
371
  const subnetId = vpcZoneIdentifier.split(',')[0];
336
- const subnet = _.chain(subnets).find({ id: subnetId }).value();
372
+ const subnet = subnets.find((x) => x.id === subnetId);
337
373
  command.subnetType = subnet.purpose;
338
374
  command.vpcId = subnet.vpcId;
339
375
  } else {
@@ -361,23 +397,22 @@ angular
361
397
  }
362
398
 
363
399
  if (serverGroup.launchTemplate || serverGroup.mixedInstancesPolicy) {
364
- let launchTemplateData, spotMaxPrice;
400
+ let launchTemplateData: ILaunchTemplateData, spotMaxPrice: string;
365
401
  if (serverGroup.launchTemplate) {
366
402
  launchTemplateData = serverGroup.launchTemplate.launchTemplateData;
367
- spotMaxPrice = _.get(launchTemplateData, 'instanceMarketOptions.spotOptions.maxPrice');
368
-
403
+ spotMaxPrice = launchTemplateData.instanceMarketOptions?.spotOptions?.maxPrice;
369
404
  command.instanceType = launchTemplateData.instanceType;
370
405
  command.viewState.useSimpleInstanceTypeSelector = true;
371
406
  }
372
407
 
373
408
  if (serverGroup.mixedInstancesPolicy) {
374
409
  const mip = serverGroup.mixedInstancesPolicy;
375
- launchTemplateData = _.get(mip, 'launchTemplates[0].launchTemplateData'); // note: single launch template case is currently the only supported case for mixed instances policy
376
- spotMaxPrice = _.get(mip, 'instancesDistribution.spotMaxPrice');
377
-
410
+ // note: single launch template case is currently the only supported case for mixed instances policy
411
+ launchTemplateData = mip?.launchTemplates?.[0]?.launchTemplateData;
412
+ spotMaxPrice = mip?.instancesDistribution?.spotMaxPrice;
378
413
  command.securityGroups = launchTemplateData.networkInterfaces
379
- ? (launchTemplateData.networkInterfaces.find((ni) => ni.deviceIndex === 0) || {}).groups
380
- : _.get(launchTemplateData, 'securityGroups');
414
+ ? (launchTemplateData.networkInterfaces.find((ni) => ni.deviceIndex === 0) ?? {}).groups
415
+ : launchTemplateData.securityGroups;
381
416
  command.onDemandAllocationStrategy = mip.instancesDistribution.onDemandAllocationStrategy;
382
417
  command.onDemandBaseCapacity = mip.instancesDistribution.onDemandBaseCapacity;
383
418
  command.onDemandPercentageAboveBaseCapacity =
@@ -412,9 +447,7 @@ angular
412
447
  instanceMonitoring: launchTemplateData.monitoring && launchTemplateData.monitoring.enabled,
413
448
  ebsOptimized: launchTemplateData.ebsOptimized,
414
449
  spotPrice: spotMaxPrice || undefined,
415
- requireIMDSv2: Boolean(
416
- launchTemplateData.metadataOptions && launchTemplateData.metadataOptions.httpsTokens === 'required',
417
- ),
450
+ requireIMDSv2: Boolean(launchTemplateData.metadataOptions?.httpTokens === 'required'),
418
451
  unlimitedCpuCredits: launchTemplateData.creditSpecification
419
452
  ? launchTemplateData.creditSpecification.cpuCredits === 'unlimited'
420
453
  : undefined,
@@ -437,8 +470,8 @@ angular
437
470
 
438
471
  if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.networkInterfaces) {
439
472
  const networkInterface =
440
- serverGroup.launchTemplate.launchTemplateData.networkInterfaces.find((ni) => ni.deviceIndex === 0) ||
441
- {};
473
+ serverGroup.launchTemplate.launchTemplateData.networkInterfaces.find((ni) => ni.deviceIndex === 0) ??
474
+ ({} as INetworkInterface);
442
475
  command.securityGroups = networkInterface.groups;
443
476
  }
444
477
 
@@ -447,7 +480,9 @@ angular
447
480
  }
448
481
 
449
482
  // Since Deck allows changing priority of instance types via drag handle, fill priority field explicitly if empty
450
- function getInstanceTypesWithPriority(instanceTypeOverrides) {
483
+ function getInstanceTypesWithPriority(
484
+ instanceTypeOverrides: IAmazonLaunchTemplateOverrides[],
485
+ ): IAmazonInstanceTypeOverride[] {
451
486
  let explicitPriority = 1;
452
487
  return _.sortBy(instanceTypeOverrides, ['priority']).map((override) => {
453
488
  const { instanceType, weightedCapacity } = override;
@@ -462,7 +497,9 @@ angular
462
497
  });
463
498
  }
464
499
 
465
- function isSimpleModeEnabled(command) {
500
+ function isSimpleModeEnabled(
501
+ command: IAmazonServerGroupDeployConfiguration | Partial<IAmazonServerGroupCommand>,
502
+ ) {
466
503
  const isAdvancedModeEnabledInCommand =
467
504
  command.onDemandAllocationStrategy ||
468
505
  command.onDemandBaseCapacity ||
@@ -475,11 +512,11 @@ angular
475
512
  }
476
513
 
477
514
  return {
478
- buildNewServerGroupCommand: buildNewServerGroupCommand,
479
- buildServerGroupCommandFromExisting: buildServerGroupCommandFromExisting,
480
- buildNewServerGroupCommandForPipeline: buildNewServerGroupCommandForPipeline,
481
- buildServerGroupCommandFromPipeline: buildServerGroupCommandFromPipeline,
482
- buildUpdateServerGroupCommand: buildUpdateServerGroupCommand,
483
- };
515
+ buildNewServerGroupCommand,
516
+ buildServerGroupCommandFromExisting,
517
+ buildNewServerGroupCommandForPipeline,
518
+ buildServerGroupCommandFromPipeline,
519
+ buildUpdateServerGroupCommand,
520
+ } as AwsServerGroupCommandBuilder;
484
521
  },
485
522
  ]);
@@ -80,6 +80,7 @@ export interface IAmazonServerGroupCommandBackingData extends IServerGroupComman
80
80
  }
81
81
 
82
82
  export interface IAmazonServerGroupCommandViewState extends IServerGroupCommandViewState {
83
+ isNew: boolean;
83
84
  dirty: IAmazonServerGroupCommandDirty;
84
85
  spelTargetGroups: string[];
85
86
  spelLoadBalancers: string[];
@@ -88,15 +89,35 @@ export interface IAmazonServerGroupCommandViewState extends IServerGroupCommandV
88
89
 
89
90
  export interface IAmazonInstanceTypeOverride {
90
91
  instanceType: string;
91
- weightedCapacity?: number;
92
+ weightedCapacity?: string;
92
93
  priority?: number;
93
94
  }
94
95
 
96
+ /**
97
+ * We model server group commands in two subtly different ways.
98
+ * It's unclear what the intention is, but we should converge on a single model eventually.
99
+ *
100
+ * This model is stored in a Aws Deploy Stage under `clusters[]`.
101
+ * It is also sent across the wire during a deploy task from infrastructure view
102
+ * (i.e., "clone server group", "create server group")
103
+ */
104
+ export interface IAmazonServerGroupDeployConfiguration extends IAmazonServerGroupCommand_Internal {
105
+ account: string;
106
+ availabilityZones: {
107
+ [region: string]: string[];
108
+ };
109
+ }
110
+
111
+ interface IAmazonServerGroupCommand_Internal extends IAmazonServerGroupCommand {
112
+ // widen 'string[]' to 'any' so we can narrow it to an incompatible type in IAmazonServerGroupDeployConfiguration
113
+ availabilityZones: any;
114
+ }
115
+
95
116
  export interface IAmazonServerGroupCommand extends IServerGroupCommand {
96
- viewState: IAmazonServerGroupCommandViewState;
97
117
  associateIPv6Address?: boolean;
98
118
  associatePublicIpAddress: boolean;
99
119
  backingData: IAmazonServerGroupCommandBackingData;
120
+ base64UserData: string;
100
121
  copySourceCustomBlockDeviceMappings: boolean;
101
122
  ebsOptimized: boolean;
102
123
  healthCheckGracePeriod: number;
@@ -111,6 +132,13 @@ export interface IAmazonServerGroupCommand extends IServerGroupCommand {
111
132
  setLaunchTemplate?: boolean;
112
133
  unlimitedCpuCredits?: boolean;
113
134
  capacityRebalance?: boolean;
135
+ ramdiskId?: string;
136
+ viewState: IAmazonServerGroupCommandViewState;
137
+ source: {
138
+ account: string;
139
+ region: string;
140
+ asgName: string;
141
+ };
114
142
  onDemandAllocationStrategy?: string;
115
143
  onDemandBaseCapacity?: number;
116
144
  onDemandPercentageAboveBaseCapacity?: number;
@@ -52,14 +52,15 @@ export function InstanceTypeTable(props: IInstanceTypeTableProps) {
52
52
 
53
53
  const addOrUpdateInstanceType = (type: string, weight: string) => {
54
54
  const weightNum = Number(weight);
55
+ const weightedCapacity = isNaN(weightNum) || weightNum === 0 ? undefined : weightNum.toString();
55
56
  const itemToUpdate = selectedInstanceTypesMap.has(type)
56
57
  ? {
57
58
  ...selectedInstanceTypesMap.get(type), // update existing item
58
- weightedCapacity: !isNaN(weightNum) && weightNum !== 0 ? weightNum : undefined,
59
+ weightedCapacity,
59
60
  }
60
61
  : {
61
62
  instanceType: type, // new item
62
- weightedCapacity: !isNaN(weightNum) && weightNum !== 0 ? weightNum : undefined,
63
+ weightedCapacity,
63
64
  priority:
64
65
  1 +
65
66
  Array.from(selectedInstanceTypesMap.values()).reduce(
@@ -65,7 +65,8 @@ export class ServerGroupInstanceType
65
65
  }
66
66
  if (
67
67
  some(weightsSpecified, function (instanceType) {
68
- return instanceType.weightedCapacity < 1 || instanceType.weightedCapacity > 999;
68
+ const weightedCapacity = Number(instanceType.weightedCapacity);
69
+ return weightedCapacity < 1 || weightedCapacity > 999;
69
70
  })
70
71
  ) {
71
72
  errors.instanceType = 'Weighted capacity must be a number between 1 and 999.';