@spinnaker/ecs 2026.2.2 → 2026.3.0

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 (209) hide show
  1. package/dist/ecs.module.d.ts +3 -1
  2. package/dist/ecsCluster/ecsCluster.read.service.d.ts +2 -3
  3. package/dist/iamRoles/iamRole.read.service.d.ts +1 -2
  4. package/dist/index.d.ts +5 -0
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/instance/details/EcsInstanceDetails.d.ts +50 -0
  8. package/dist/loadBalancer/loadBalancer.transformer.d.ts +1 -5
  9. package/dist/loadBalancer/targetGroup.states.d.ts +2 -1
  10. package/dist/metricAlarm/metricAlarm.read.service.d.ts +1 -2
  11. package/dist/pipeline/stages/cloneServerGroup/ecsCloneServerGroupStage.d.ts +1 -0
  12. package/dist/pipeline/stages/common/EcsServerGroupStepLabel.d.ts +7 -0
  13. package/dist/pipeline/stages/common/EcsStageConfigs.d.ts +11 -0
  14. package/dist/pipeline/stages/destroyAsg/ecsDestroyAsgStage.d.ts +1 -2
  15. package/dist/pipeline/stages/disableAsg/ecsDisableAsgStage.d.ts +1 -2
  16. package/dist/pipeline/stages/disableCluster/ecsDisableClusterStage.d.ts +1 -2
  17. package/dist/pipeline/stages/enableAsg/ecsEnableAsgStage.d.ts +1 -2
  18. package/dist/pipeline/stages/findImageFromTags/EcsFindImageFromTagsExecutionDetails.d.ts +5 -0
  19. package/dist/pipeline/stages/findImageFromTags/ecsFindImageFromTagStage.d.ts +1 -2
  20. package/dist/pipeline/stages/resizeAsg/ecsResizeAsgStage.d.ts +1 -2
  21. package/dist/pipeline/stages/scaleDownCluster/ecsScaleDownClusterStage.d.ts +1 -2
  22. package/dist/pipeline/stages/shrinkCluster/ecsShrinkClusterStage.d.ts +1 -2
  23. package/dist/placementStrategy/placementStrategy.service.d.ts +0 -1
  24. package/dist/secrets/secret.read.service.d.ts +1 -2
  25. package/dist/securityGroup/details/EcsSecurityGroupDetails.d.ts +45 -0
  26. package/dist/securityGroup/securityGroup.reader.d.ts +0 -1
  27. package/dist/securityGroup/securityGroup.transformer.d.ts +3 -2
  28. package/dist/serverGroup/configure/dockerImage.util.d.ts +3 -0
  29. package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +111 -2
  30. package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +13 -63
  31. package/dist/serverGroup/configure/wizard/EcsCloneServerGroupModal.d.ts +64 -0
  32. package/dist/serverGroup/configure/wizard/capacityProvider/CapacityProvider.d.ts +29 -4
  33. package/dist/serverGroup/configure/wizard/container/Container.d.ts +3 -3
  34. package/dist/serverGroup/configure/wizard/networking/Networking.d.ts +3 -3
  35. package/dist/serverGroup/configure/wizard/pages/AdvancedSettings.d.ts +3 -0
  36. package/dist/serverGroup/configure/wizard/pages/BasicSettings.d.ts +3 -0
  37. package/dist/serverGroup/configure/wizard/pages/ContainerSettings.d.ts +3 -0
  38. package/dist/serverGroup/configure/wizard/pages/HorizontalScalingSettings.d.ts +3 -0
  39. package/dist/serverGroup/configure/wizard/pages/LoggingSettings.d.ts +3 -0
  40. package/dist/serverGroup/configure/wizard/pages/NetworkingSettings.d.ts +3 -0
  41. package/dist/serverGroup/configure/wizard/pages/ServiceDiscoverySettings.d.ts +3 -0
  42. package/dist/serverGroup/configure/wizard/pages/TaskDefinitionSettings.d.ts +3 -0
  43. package/dist/serverGroup/configure/wizard/pages/common.d.ts +8 -0
  44. package/dist/serverGroup/configure/wizard/pages/validation.d.ts +22 -0
  45. package/dist/serverGroup/configure/wizard/serviceDiscovery/ServiceDiscovery.d.ts +5 -3
  46. package/dist/serverGroup/configure/wizard/taskDefinition/TaskDefinition.d.ts +3 -3
  47. package/dist/serverGroup/details/EcsServerGroupActions.d.ts +6 -0
  48. package/dist/serverGroup/details/ecsServerGroupDetailsGetter.d.ts +3 -0
  49. package/dist/serverGroup/details/resize/EcsResizeServerGroupModal.d.ts +43 -0
  50. package/dist/serverGroup/details/resize/index.d.ts +1 -0
  51. package/dist/serverGroup/details/rollback/EcsRollbackServerGroupModal.d.ts +46 -0
  52. package/dist/serverGroup/details/rollback/index.d.ts +1 -0
  53. package/dist/serverGroup/details/sections/EcsServerGroupDetailsSections.d.ts +9 -0
  54. package/dist/serverGroup/details/sections/EcsServerGroupEventsSection.d.ts +3 -0
  55. package/dist/serverGroup/details/sections/EcsServerGroupInformationSection.d.ts +3 -0
  56. package/dist/serverGroup/events/EcsServerGroupEventsModal.d.ts +22 -0
  57. package/dist/serverGroup/events/EventsLink.d.ts +2 -0
  58. package/dist/serverGroup/events/serverGroupEventsReader.service.d.ts +1 -1
  59. package/dist/serverGroup/serverGroup.transformer.d.ts +1 -2
  60. package/dist/serviceDiscovery/serviceDiscovery.read.service.d.ts +1 -1
  61. package/package.json +3 -9
  62. package/src/ecs.module.spec.ts +86 -0
  63. package/src/ecs.module.ts +68 -84
  64. package/src/ecsCluster/ecsCluster.read.service.ts +3 -9
  65. package/src/iamRoles/iamRole.read.service.ts +1 -7
  66. package/src/index.ts +6 -0
  67. package/src/instance/details/EcsInstanceDetails.spec.tsx +426 -0
  68. package/src/instance/details/EcsInstanceDetails.tsx +474 -0
  69. package/src/loadBalancer/details/loadBalancerDetails.tsx +1 -1
  70. package/src/loadBalancer/details/targetGroupDetails.tsx +1 -1
  71. package/src/loadBalancer/loadBalancer.transformer.ts +2 -7
  72. package/src/loadBalancer/targetGroup.states.ts +55 -53
  73. package/src/metricAlarm/metricAlarm.read.service.ts +1 -7
  74. package/src/pipeline/stages/cloneServerGroup/ecsCloneServerGroupStage.js +22 -94
  75. package/src/pipeline/stages/common/EcsServerGroupStepLabel.tsx +16 -0
  76. package/src/pipeline/stages/common/EcsStageConfigs.tsx +587 -0
  77. package/src/pipeline/stages/destroyAsg/ecsDestroyAsgStage.js +29 -65
  78. package/src/pipeline/stages/disableAsg/ecsDisableAsgStage.js +27 -70
  79. package/src/pipeline/stages/disableCluster/ecsDisableClusterStage.js +21 -84
  80. package/src/pipeline/stages/enableAsg/ecsEnableAsgStage.js +22 -73
  81. package/src/pipeline/stages/findImageFromTags/EcsFindImageFromTagsExecutionDetails.tsx +40 -0
  82. package/src/pipeline/stages/findImageFromTags/ecsFindImageFromTagStage.js +12 -24
  83. package/src/pipeline/stages/resizeAsg/ecsResizeAsgStage.js +30 -128
  84. package/src/pipeline/stages/scaleDownCluster/ecsScaleDownClusterStage.js +24 -78
  85. package/src/pipeline/stages/shrinkCluster/ecsShrinkClusterStage.js +19 -73
  86. package/src/placementStrategy/placementStrategy.service.ts +0 -6
  87. package/src/secrets/secret.read.service.ts +1 -7
  88. package/src/securityGroup/details/EcsSecurityGroupDetails.spec.tsx +245 -0
  89. package/src/securityGroup/details/EcsSecurityGroupDetails.tsx +285 -0
  90. package/src/securityGroup/securityGroup.reader.ts +0 -5
  91. package/src/securityGroup/securityGroup.transformer.js +5 -13
  92. package/src/serverGroup/configure/dockerImage.util.ts +29 -0
  93. package/src/serverGroup/configure/serverGroupCommandBuilder.service.js +215 -278
  94. package/src/serverGroup/configure/serverGroupCommandBuilder.service.spec.ts +83 -0
  95. package/src/serverGroup/configure/serverGroupConfiguration.service.ts +16 -633
  96. package/src/serverGroup/configure/wizard/EcsCloneServerGroupModal.spec.tsx +2078 -0
  97. package/src/serverGroup/configure/wizard/EcsCloneServerGroupModal.tsx +712 -0
  98. package/src/serverGroup/configure/wizard/capacityProvider/CapacityProvider.tsx +98 -49
  99. package/src/serverGroup/configure/wizard/container/Container.spec.tsx +20 -8
  100. package/src/serverGroup/configure/wizard/container/Container.tsx +53 -24
  101. package/src/serverGroup/configure/wizard/networking/Networking.tsx +60 -38
  102. package/src/serverGroup/configure/wizard/pages/AdvancedSettings.tsx +290 -0
  103. package/src/serverGroup/configure/wizard/pages/BasicSettings.tsx +101 -0
  104. package/src/serverGroup/configure/wizard/pages/ContainerSettings.tsx +10 -0
  105. package/src/serverGroup/configure/wizard/pages/HorizontalScalingSettings.tsx +137 -0
  106. package/src/serverGroup/configure/wizard/pages/LoggingSettings.tsx +60 -0
  107. package/src/serverGroup/configure/wizard/pages/NetworkingSettings.tsx +10 -0
  108. package/src/serverGroup/configure/wizard/pages/ServiceDiscoverySettings.tsx +10 -0
  109. package/src/serverGroup/configure/wizard/pages/TaskDefinitionSettings.tsx +59 -0
  110. package/src/serverGroup/configure/wizard/pages/common.ts +10 -0
  111. package/src/serverGroup/configure/wizard/pages/validation.tsx +190 -0
  112. package/src/serverGroup/configure/wizard/serviceDiscovery/ServiceDiscovery.tsx +83 -34
  113. package/src/serverGroup/configure/wizard/taskDefinition/TaskDefinition.spec.tsx +21 -8
  114. package/src/serverGroup/configure/wizard/taskDefinition/TaskDefinition.tsx +63 -33
  115. package/src/serverGroup/details/EcsServerGroupActions.spec.tsx +244 -0
  116. package/src/serverGroup/details/EcsServerGroupActions.tsx +152 -0
  117. package/src/serverGroup/details/ecsServerGroupDetailsGetter.spec.ts +110 -0
  118. package/src/serverGroup/details/ecsServerGroupDetailsGetter.ts +73 -0
  119. package/src/serverGroup/details/resize/EcsResizeServerGroupModal.spec.tsx +130 -0
  120. package/src/serverGroup/details/resize/EcsResizeServerGroupModal.tsx +263 -0
  121. package/src/serverGroup/details/resize/index.ts +1 -0
  122. package/src/serverGroup/details/rollback/EcsRollbackServerGroupModal.spec.tsx +173 -0
  123. package/src/serverGroup/details/rollback/EcsRollbackServerGroupModal.tsx +285 -0
  124. package/src/serverGroup/details/rollback/index.ts +1 -0
  125. package/src/serverGroup/details/sections/EcsServerGroupDetailsSections.spec.tsx +130 -0
  126. package/src/serverGroup/details/sections/EcsServerGroupDetailsSections.tsx +172 -0
  127. package/src/serverGroup/details/sections/EcsServerGroupEventsSection.tsx +14 -0
  128. package/src/serverGroup/details/sections/EcsServerGroupInformationSection.tsx +25 -0
  129. package/src/serverGroup/events/EcsServerGroupEventsModal.spec.tsx +109 -0
  130. package/src/serverGroup/events/EcsServerGroupEventsModal.tsx +112 -0
  131. package/src/serverGroup/events/EventsLink.tsx +18 -0
  132. package/src/serverGroup/events/serverGroupEventsReader.service.spec.ts +41 -0
  133. package/src/serverGroup/events/serverGroupEventsReader.service.ts +2 -8
  134. package/src/serverGroup/serverGroup.transformer.spec.ts +10 -24
  135. package/src/serverGroup/serverGroup.transformer.ts +1 -5
  136. package/src/serviceDiscovery/serviceDiscovery.read.service.ts +1 -1
  137. package/dist/common/common.module.d.ts +0 -1
  138. package/dist/common/footer.component.d.ts +0 -1
  139. package/dist/instance/details/instance.details.controller.d.ts +0 -2
  140. package/dist/securityGroup/details/securityGroupDetail.controller.d.ts +0 -2
  141. package/dist/securityGroup/securityGroup.module.d.ts +0 -1
  142. package/dist/serverGroup/configure/wizard/CloneServerGroup.ecs.controller.d.ts +0 -2
  143. package/dist/serverGroup/configure/wizard/advancedSettings/advancedSettings.component.d.ts +0 -2
  144. package/dist/serverGroup/configure/wizard/horizontalScaling/horizontalScaling.component.d.ts +0 -2
  145. package/dist/serverGroup/configure/wizard/location/ServerGroupBasicSettings.controller.d.ts +0 -2
  146. package/dist/serverGroup/configure/wizard/logging/logging.component.d.ts +0 -1
  147. package/dist/serverGroup/details/resize/resizeCapacity.component.d.ts +0 -1
  148. package/dist/serverGroup/details/resize/resizeServerGroup.controller.d.ts +0 -2
  149. package/dist/serverGroup/details/rollback/rollbackServerGroup.controller.d.ts +0 -2
  150. package/dist/serverGroup/details/serverGroupDetails.ecs.controller.d.ts +0 -2
  151. package/dist/serverGroup/details/serverGroupDetails.module.d.ts +0 -1
  152. package/dist/serverGroup/events/events.component.d.ts +0 -3
  153. package/dist/serverGroup/events/events.controller.d.ts +0 -19
  154. package/dist/serverGroup/serverGroup.module.d.ts +0 -1
  155. package/src/common/common.module.ts +0 -6
  156. package/src/common/footer.component.ts +0 -29
  157. package/src/instance/details/instance.details.controller.js +0 -356
  158. package/src/instance/details/instanceDetails.html +0 -204
  159. package/src/pipeline/stages/cloneServerGroup/cloneServerGroupStage.html +0 -101
  160. package/src/pipeline/stages/cloneServerGroup/cloneServerGroupStepLabel.html +0 -1
  161. package/src/pipeline/stages/destroyAsg/destroyAsgStage.html +0 -9
  162. package/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html +0 -1
  163. package/src/pipeline/stages/disableAsg/disableAsgStage.html +0 -11
  164. package/src/pipeline/stages/disableAsg/disableAsgStepLabel.html +0 -1
  165. package/src/pipeline/stages/disableCluster/disableClusterStage.html +0 -26
  166. package/src/pipeline/stages/enableAsg/enableAsgStage.html +0 -11
  167. package/src/pipeline/stages/enableAsg/enableAsgStepLabel.html +0 -1
  168. package/src/pipeline/stages/findImageFromTags/findImageFromTagsExecutionDetails.html +0 -36
  169. package/src/pipeline/stages/findImageFromTags/findImageFromTagsStage.html +0 -9
  170. package/src/pipeline/stages/resizeAsg/resizeAsgStage.html +0 -87
  171. package/src/pipeline/stages/resizeAsg/resizeAsgStepLabel.html +0 -1
  172. package/src/pipeline/stages/scaleDownCluster/scaleDownClusterStage.html +0 -35
  173. package/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html +0 -34
  174. package/src/securityGroup/details/securityGroupDetail.controller.js +0 -147
  175. package/src/securityGroup/details/securityGroupDetail.html +0 -104
  176. package/src/securityGroup/securityGroup.module.ts +0 -12
  177. package/src/serverGroup/configure/wizard/CloneServerGroup.ecs.controller.js +0 -259
  178. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.component.html +0 -194
  179. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.component.js +0 -17
  180. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.html +0 -10
  181. package/src/serverGroup/configure/wizard/capacityProvider/capacityProvider.html +0 -11
  182. package/src/serverGroup/configure/wizard/container/container.html +0 -9
  183. package/src/serverGroup/configure/wizard/horizontalScaling/horizontalScaling.component.html +0 -118
  184. package/src/serverGroup/configure/wizard/horizontalScaling/horizontalScaling.component.js +0 -20
  185. package/src/serverGroup/configure/wizard/horizontalScaling/horizontalScaling.html +0 -13
  186. package/src/serverGroup/configure/wizard/location/ServerGroupBasicSettings.controller.js +0 -41
  187. package/src/serverGroup/configure/wizard/location/basicSettings.html +0 -137
  188. package/src/serverGroup/configure/wizard/logging/logging.component.html +0 -29
  189. package/src/serverGroup/configure/wizard/logging/logging.component.ts +0 -12
  190. package/src/serverGroup/configure/wizard/logging/logging.html +0 -7
  191. package/src/serverGroup/configure/wizard/networking/networking.html +0 -9
  192. package/src/serverGroup/configure/wizard/serverGroupWizard.html +0 -55
  193. package/src/serverGroup/configure/wizard/serviceDiscovery/serviceDiscovery.html +0 -11
  194. package/src/serverGroup/configure/wizard/taskDefinition/taskDefinition.html +0 -51
  195. package/src/serverGroup/configure/wizard/templateSelection/templateSelection.html +0 -9
  196. package/src/serverGroup/configure/wizard/verticalScaling/verticalScaling.html +0 -10
  197. package/src/serverGroup/details/resize/resizeCapacity.component.html +0 -94
  198. package/src/serverGroup/details/resize/resizeCapacity.component.ts +0 -14
  199. package/src/serverGroup/details/resize/resizeServerGroup.controller.js +0 -85
  200. package/src/serverGroup/details/resize/resizeServerGroup.html +0 -28
  201. package/src/serverGroup/details/rollback/rollbackServerGroup.controller.js +0 -97
  202. package/src/serverGroup/details/rollback/rollbackServerGroup.html +0 -67
  203. package/src/serverGroup/details/serverGroupDetails.ecs.controller.js +0 -366
  204. package/src/serverGroup/details/serverGroupDetails.html +0 -216
  205. package/src/serverGroup/details/serverGroupDetails.module.ts +0 -5
  206. package/src/serverGroup/events/events.component.ts +0 -35
  207. package/src/serverGroup/events/events.controller.ts +0 -45
  208. package/src/serverGroup/events/events.html +0 -36
  209. package/src/serverGroup/serverGroup.module.ts +0 -6
@@ -0,0 +1,712 @@
1
+ import { get, uniq, uniqBy } from 'lodash';
2
+ import React from 'react';
3
+
4
+ import type {
5
+ Application,
6
+ DeckRuntimeServices,
7
+ IModalComponentProps,
8
+ IRouterInjectedProps,
9
+ ISubnet,
10
+ IWizardPageInjectedProps,
11
+ } from '@spinnaker/core';
12
+ import {
13
+ AccountService,
14
+ DeckRuntimeContext,
15
+ DeployInitializer,
16
+ NameUtils,
17
+ noop,
18
+ ReactModal,
19
+ REST,
20
+ TaskMonitor,
21
+ withRouter,
22
+ WizardModal,
23
+ WizardPage,
24
+ } from '@spinnaker/core';
25
+
26
+ import { normalizeEcsDockerImage } from '../dockerImage.util';
27
+ import { EcsClusterReader } from '../../../ecsCluster/ecsCluster.read.service';
28
+ import { IamRoleReader } from '../../../iamRoles/iamRole.read.service';
29
+ import { MetricAlarmReader } from '../../../metricAlarm/metricAlarm.read.service';
30
+ import { AdvancedSettings } from './pages/AdvancedSettings';
31
+ import { BasicSettings } from './pages/BasicSettings';
32
+ import { ContainerSettings } from './pages/ContainerSettings';
33
+ import { HorizontalScalingSettings } from './pages/HorizontalScalingSettings';
34
+ import { LoggingSettings } from './pages/LoggingSettings';
35
+ import { NetworkingSettings } from './pages/NetworkingSettings';
36
+ import { ServiceDiscoverySettings } from './pages/ServiceDiscoverySettings';
37
+ import { TaskDefinitionSettings } from './pages/TaskDefinitionSettings';
38
+ import {
39
+ EcsWizardPageValidation,
40
+ validateEcsBasicSettings,
41
+ validateEcsCapacity,
42
+ validateEcsContainer,
43
+ validateEcsServerGroup,
44
+ validateEcsServiceDiscovery,
45
+ validateEcsTaskDefinition,
46
+ } from './pages/validation';
47
+ import { PlacementStrategyService } from '../../../placementStrategy/placementStrategy.service';
48
+ import { SecretReader } from '../../../secrets/secret.read.service';
49
+ import type {
50
+ IEcsCapacityProviderStrategyItem,
51
+ IEcsDockerImage,
52
+ IEcsServerGroupCommand,
53
+ IEcsTargetGroupMapping,
54
+ } from '../serverGroupConfiguration.service';
55
+ import { ServiceDiscoveryReader } from '../../../serviceDiscovery/serviceDiscovery.read.service';
56
+
57
+ type EcsFormikProps = IWizardPageInjectedProps<IEcsServerGroupCommand>['formik'];
58
+
59
+ export interface IEcsCloneServerGroupModalProps extends IModalComponentProps {
60
+ title: string;
61
+ application: Application;
62
+ command: IEcsServerGroupCommand;
63
+ }
64
+
65
+ interface IEcsCloneServerGroupModalState {
66
+ command: IEcsServerGroupCommand;
67
+ loaded: boolean;
68
+ requiresTemplateSelection: boolean;
69
+ taskMonitor: TaskMonitor;
70
+ }
71
+
72
+ export class EcsCloneServerGroupModalComponent extends React.Component<
73
+ IEcsCloneServerGroupModalProps & IRouterInjectedProps,
74
+ IEcsCloneServerGroupModalState
75
+ > {
76
+ public static contextType = DeckRuntimeContext;
77
+ public declare context: React.ContextType<typeof DeckRuntimeContext>;
78
+
79
+ private command: IEcsServerGroupCommand;
80
+ private configureRequest = 0;
81
+ private formik: EcsFormikProps = null;
82
+ private unmounted = false;
83
+ private applicationRefreshUnsubscribe?: () => void;
84
+
85
+ public static defaultProps: Partial<IEcsCloneServerGroupModalProps> = {
86
+ closeModal: noop,
87
+ dismissModal: noop,
88
+ };
89
+
90
+ private ecsClusterReader = new EcsClusterReader();
91
+ private iamRoleReader = new IamRoleReader();
92
+ private metricAlarmReader = new MetricAlarmReader();
93
+ private placementStrategyService = new PlacementStrategyService();
94
+ private secretReader = new SecretReader();
95
+
96
+ public static show(
97
+ props: IEcsCloneServerGroupModalProps,
98
+ runtimeServices: DeckRuntimeServices,
99
+ ): Promise<IEcsServerGroupCommand> {
100
+ const modalProps = { dialogClassName: 'wizard-modal modal-lg' };
101
+ return ReactModal.show(EcsCloneServerGroupModal, props, modalProps, runtimeServices);
102
+ }
103
+
104
+ constructor(props: IEcsCloneServerGroupModalProps & IRouterInjectedProps) {
105
+ super(props);
106
+ this.ensureCommandShape(props.command);
107
+ this.command = props.command;
108
+ this.state = {
109
+ command: props.command,
110
+ loaded: false,
111
+ requiresTemplateSelection: get(props, 'command.viewState.requiresTemplateSelection', false),
112
+ taskMonitor: new TaskMonitor({
113
+ application: props.application,
114
+ title: 'Creating your server group',
115
+ onDismiss: () => this.props.dismissModal(),
116
+ onTaskComplete: this.onTaskComplete,
117
+ }),
118
+ };
119
+ }
120
+
121
+ public componentDidMount(): void {
122
+ if (this.state.requiresTemplateSelection) {
123
+ this.setState({ loaded: true });
124
+ return;
125
+ }
126
+ this.configureCommand();
127
+ }
128
+
129
+ public componentWillUnmount(): void {
130
+ this.unmounted = true;
131
+ this.clearApplicationRefreshSubscription();
132
+ }
133
+
134
+ private onTaskComplete = () => {
135
+ this.clearApplicationRefreshSubscription();
136
+ this.applicationRefreshUnsubscribe = this.props.application.serverGroups.onNextRefresh(this.onApplicationRefresh);
137
+ this.props.application.serverGroups.refresh();
138
+ };
139
+
140
+ private onApplicationRefresh = (): void => {
141
+ this.clearApplicationRefreshSubscription();
142
+ if (this.unmounted) {
143
+ return;
144
+ }
145
+
146
+ const { command, taskMonitor } = this.state;
147
+ const cloneStage = taskMonitor.task?.execution?.stages?.find((stage: any) => stage.type === 'cloneServerGroup');
148
+ const newServerGroupName = cloneStage?.context?.['deploy.server.groups']?.[command.region];
149
+ if (!newServerGroupName) {
150
+ return;
151
+ }
152
+
153
+ let transitionTo = '^.^.^.clusters.serverGroup';
154
+ if (this.props.stateService.includes('**.clusters.serverGroup')) {
155
+ transitionTo = '^.serverGroup';
156
+ }
157
+ if (this.props.stateService.includes('**.clusters.cluster.serverGroup')) {
158
+ transitionTo = '^.^.serverGroup';
159
+ }
160
+ if (this.props.stateService.includes('**.clusters')) {
161
+ transitionTo = '.serverGroup';
162
+ }
163
+ this.props.stateService.go(transitionTo, {
164
+ accountId: command.credentials,
165
+ provider: 'ecs',
166
+ region: command.region,
167
+ serverGroup: newServerGroupName,
168
+ });
169
+ };
170
+
171
+ private clearApplicationRefreshSubscription = (): void => {
172
+ this.applicationRefreshUnsubscribe?.();
173
+ this.applicationRefreshUnsubscribe = undefined;
174
+ };
175
+
176
+ private templateSelected = () => {
177
+ this.ensureCommandShape(this.props.command);
178
+ this.setState(
179
+ {
180
+ command: this.props.command,
181
+ loaded: false,
182
+ requiresTemplateSelection: false,
183
+ },
184
+ () => {
185
+ this.configureCommand();
186
+ },
187
+ );
188
+ };
189
+
190
+ private configureCommand = (imageQuery = '', command = this.state.command): Promise<void> => {
191
+ const request = ++this.configureRequest;
192
+ this.ensureCommandShape(command);
193
+ this.command = command;
194
+ return this.loadBackingData(command, imageQuery, request).then(() => {
195
+ if (!this.unmounted && request === this.configureRequest) {
196
+ const configuredCommand = this.command;
197
+ configuredCommand.availabilityZones = command.availabilityZones;
198
+ configuredCommand.backingData = command.backingData;
199
+ configuredCommand.region = command.region;
200
+ this.attachEventHandlers(configuredCommand);
201
+ this.syncCommand(configuredCommand);
202
+ this.setState({ loaded: true });
203
+ }
204
+ });
205
+ };
206
+
207
+ private safe<T>(promise: PromiseLike<T>, fallback: T): Promise<T> {
208
+ return Promise.resolve(promise).catch(() => fallback);
209
+ }
210
+
211
+ private loadBackingData(
212
+ command: IEcsServerGroupCommand,
213
+ imageQuery: string,
214
+ request = this.configureRequest,
215
+ ): Promise<void> {
216
+ return Promise.all([
217
+ this.safe(AccountService.getCredentialsKeyedByAccount('ecs'), {}),
218
+ this.safe(REST('/loadBalancers').query({ provider: 'ecs' }).get(), []),
219
+ this.safe(REST('/subnets/ecs').get(), []),
220
+ this.safe(this.iamRoleReader.listRoles('ecs'), []),
221
+ this.safe(this.ecsClusterReader.listClusters(), []),
222
+ this.safe(this.ecsClusterReader.describeClusters(command.credentials, command.region), []),
223
+ this.safe(this.metricAlarmReader.listMetricAlarms(), []),
224
+ this.safe(REST('/securityGroups').get(), {}),
225
+ this.safe(this.secretReader.listSecrets(), []),
226
+ this.safe(ServiceDiscoveryReader.listServiceDiscoveryRegistries(), []),
227
+ imageQuery
228
+ ? this.safe(REST('/images/find').query({ provider: 'dockerRegistry', count: 50, q: imageQuery }).get(), [])
229
+ : command.backingData.images || [],
230
+ ]).then(
231
+ ([
232
+ credentialsKeyedByAccount,
233
+ loadBalancers,
234
+ subnets,
235
+ iamRoles,
236
+ ecsClusters,
237
+ capacityProviderDetails,
238
+ metricAlarms,
239
+ securityGroups,
240
+ secrets,
241
+ serviceDiscoveryRegistries,
242
+ images,
243
+ ]) => {
244
+ if (this.unmounted || request !== this.configureRequest) {
245
+ return;
246
+ }
247
+ const loadBalancerList = Array.isArray(loadBalancers) ? loadBalancers : [];
248
+ const subnetList = Array.isArray(subnets) ? subnets : [];
249
+ const iamRoleList = Array.isArray(iamRoles) ? iamRoles : [];
250
+ const clusterList = Array.isArray(ecsClusters) ? ecsClusters : [];
251
+ const capacityProviderList = Array.isArray(capacityProviderDetails) ? capacityProviderDetails : [];
252
+ const metricAlarmList = Array.isArray(metricAlarms) ? metricAlarms : [];
253
+ const securityGroupData = securityGroups || {};
254
+ const secretList = Array.isArray(secrets) ? secrets : [];
255
+ const serviceDiscoveryRegistryList = Array.isArray(serviceDiscoveryRegistries)
256
+ ? serviceDiscoveryRegistries
257
+ : [];
258
+ const imageList = Array.isArray(images) ? images : [];
259
+ const backingData: any = command.backingData;
260
+ backingData.credentialsKeyedByAccount = credentialsKeyedByAccount;
261
+ backingData.loadBalancers = loadBalancerList;
262
+ backingData.subnets = subnetList;
263
+ backingData.iamRoles = iamRoleList;
264
+ backingData.ecsClusters = clusterList;
265
+ backingData.capacityProviderDetails = capacityProviderList;
266
+ backingData.metricAlarms = metricAlarmList;
267
+ backingData.securityGroups = securityGroupData;
268
+ backingData.launchTypes = ['EC2', 'FARGATE'];
269
+ backingData.networkModes = ['bridge', 'host', 'awsvpc', 'none', 'default'];
270
+ backingData.secrets = secretList;
271
+ backingData.serviceDiscoveryRegistries = serviceDiscoveryRegistryList;
272
+ backingData.images = this.normalizeImages(command, imageList as IEcsDockerImage[]);
273
+ backingData.filtered = {
274
+ ...backingData.filtered,
275
+ availableCapacityProviders: this.getAvailableCapacityProviders(command, capacityProviderList),
276
+ defaultCapacityProviderStrategy: this.getDefaultCapacityProviderStrategy(command, capacityProviderList),
277
+ ecsClusters: this.getClusterNames(command, clusterList),
278
+ iamRoles: this.getIamRoles(command, iamRoleList),
279
+ images: backingData.images,
280
+ metricAlarms: metricAlarmList,
281
+ securityGroupNames: this.getSecurityGroupNames(command, securityGroupData, subnetList),
282
+ secrets: this.getSecrets(command, secretList),
283
+ serviceDiscoveryRegistries: this.getServiceDiscoveryRegistries(command, serviceDiscoveryRegistryList),
284
+ subnetTypes: this.getSubnetTypes(command, subnetList),
285
+ targetGroups: this.getTargetGroups(command, loadBalancerList),
286
+ };
287
+ this.reconcileLocation(command);
288
+ this.attachEventHandlers(command);
289
+ },
290
+ );
291
+ }
292
+
293
+ private ensureCommandShape(command: IEcsServerGroupCommand): void {
294
+ command.backingData = command.backingData || ({} as any);
295
+ command.backingData.filtered = command.backingData.filtered || ({} as any);
296
+ command.backingData.filtered.availableCapacityProviders =
297
+ command.backingData.filtered.availableCapacityProviders || [];
298
+ command.backingData.filtered.defaultCapacityProviderStrategy =
299
+ command.backingData.filtered.defaultCapacityProviderStrategy || [];
300
+ command.backingData.filtered.ecsClusters = command.backingData.filtered.ecsClusters || [];
301
+ command.backingData.filtered.iamRoles = command.backingData.filtered.iamRoles || [];
302
+ command.backingData.filtered.images = command.backingData.filtered.images || [];
303
+ command.backingData.filtered.metricAlarms = command.backingData.filtered.metricAlarms || [];
304
+ command.backingData.filtered.securityGroupNames = command.backingData.filtered.securityGroupNames || [];
305
+ command.backingData.filtered.secrets = command.backingData.filtered.secrets || [];
306
+ command.backingData.filtered.serviceDiscoveryRegistries =
307
+ command.backingData.filtered.serviceDiscoveryRegistries || [];
308
+ command.backingData.filtered.subnetTypes = command.backingData.filtered.subnetTypes || [];
309
+ command.backingData.filtered.targetGroups = command.backingData.filtered.targetGroups || [];
310
+ command.backingData.iamRoles = command.backingData.iamRoles || [];
311
+ command.backingData.launchTypes = command.backingData.launchTypes || ['EC2', 'FARGATE'];
312
+ command.backingData.metricAlarms = command.backingData.metricAlarms || [];
313
+ command.backingData.networkModes = command.backingData.networkModes || [
314
+ 'bridge',
315
+ 'host',
316
+ 'awsvpc',
317
+ 'none',
318
+ 'default',
319
+ ];
320
+ command.backingData.secrets = command.backingData.secrets || [];
321
+ command.backingData.serviceDiscoveryRegistries = command.backingData.serviceDiscoveryRegistries || [];
322
+ command.containerMappings = command.containerMappings || [];
323
+ command.serviceDiscoveryAssociations = command.serviceDiscoveryAssociations || [];
324
+ command.targetGroupMappings = command.targetGroupMappings || [];
325
+ command.taskDefinitionArtifact = command.taskDefinitionArtifact || {};
326
+ command.viewState = command.viewState || ({} as any);
327
+ command.viewState.dirty = command.viewState.dirty || {};
328
+ command.useTaskDefinitionArtifact = command.useTaskDefinitionArtifact === true;
329
+ }
330
+
331
+ private attachEventHandlers(command: IEcsServerGroupCommand): void {
332
+ command.subnetTypeChanged = (changedCommand: IEcsServerGroupCommand = command) => {
333
+ changedCommand.backingData.filtered.securityGroupNames = this.getSecurityGroupNames(
334
+ changedCommand,
335
+ changedCommand.backingData.securityGroups || {},
336
+ changedCommand.backingData.subnets || [],
337
+ );
338
+ return { dirty: {} };
339
+ };
340
+ command.regionChanged = (changedCommand: IEcsServerGroupCommand = command) => {
341
+ this.reconcileLocation(changedCommand);
342
+ this.syncCommand(changedCommand);
343
+ this.configureCommand('', changedCommand);
344
+ return { dirty: {} };
345
+ };
346
+ command.credentialsChanged = (changedCommand: IEcsServerGroupCommand = command) => {
347
+ this.reconcileLocation(changedCommand);
348
+ this.syncCommand(changedCommand);
349
+ this.configureCommand('', changedCommand);
350
+ return { dirty: {} };
351
+ };
352
+ command.placementStrategyNameChanged = (changedCommand: IEcsServerGroupCommand = command) => {
353
+ changedCommand.placementStrategySequence = this.placementStrategyService.getPredefinedStrategy(
354
+ changedCommand.placementStrategyName,
355
+ );
356
+ return { dirty: {} };
357
+ };
358
+ command.clusterChanged = () => {
359
+ command.moniker = NameUtils.getMoniker(command.application, command.stack, command.freeFormDetails);
360
+ };
361
+ }
362
+
363
+ private reconcileLocation(command: IEcsServerGroupCommand): void {
364
+ const account = command.backingData.credentialsKeyedByAccount?.[command.credentials];
365
+ const regions = account?.regions || [];
366
+ command.backingData.filtered.regions = regions;
367
+
368
+ const selectedRegion = regions.find((region: any) => region.name === command.region);
369
+ if (!selectedRegion) {
370
+ command.region = null;
371
+ }
372
+ const availabilityZones = selectedRegion?.availabilityZones || [];
373
+ command.backingData.filtered.availabilityZones = availabilityZones;
374
+ command.availabilityZones = availabilityZones;
375
+ }
376
+
377
+ private normalizeImages(command: IEcsServerGroupCommand, images: IEcsDockerImage[]): IEcsDockerImage[] {
378
+ const commandImages = [
379
+ ...(command.viewState.contextImages || []),
380
+ command.imageDescription,
381
+ ...command.containerMappings.map((mapping) => mapping.imageDescription),
382
+ ].filter(Boolean);
383
+ return uniqBy(
384
+ [...images, ...commandImages].map((image) => normalizeEcsDockerImage(image)),
385
+ (image) => image.imageId || image.id || image.name,
386
+ ).filter(Boolean);
387
+ }
388
+
389
+ private getSubnetTypes(command: IEcsServerGroupCommand, subnets: ISubnet[]): ISubnet[] {
390
+ return subnets.filter((subnet) => {
391
+ const matchesAccount = !command.credentials || subnet.account === command.credentials;
392
+ const matchesRegion = !command.region || subnet.region === command.region;
393
+ return matchesAccount && matchesRegion && subnet.purpose;
394
+ });
395
+ }
396
+
397
+ private getClusterNames(command: IEcsServerGroupCommand, clusters: any[]): string[] {
398
+ return uniq(
399
+ clusters
400
+ .filter((cluster) => {
401
+ const matchesAccount = !command.credentials || cluster.account === command.credentials;
402
+ const matchesRegion = !command.region || cluster.region === command.region;
403
+ return matchesAccount && matchesRegion;
404
+ })
405
+ .map((cluster) => cluster.name)
406
+ .concat(command.ecsClusterName || []),
407
+ ).filter(Boolean);
408
+ }
409
+
410
+ private getIamRoles(command: IEcsServerGroupCommand, iamRoles: any[]): string[] {
411
+ const roleNames = iamRoles
412
+ .filter((role) => role.accountName === command.credentials)
413
+ .map((role) => role.name)
414
+ .filter(Boolean);
415
+ return roleNames.length ? ['None (No IAM role)', ...roleNames] : roleNames;
416
+ }
417
+
418
+ private getSecrets(command: IEcsServerGroupCommand, secrets: any[]): string[] {
419
+ return secrets
420
+ .filter((secret) => secret.account === command.credentials && secret.region === command.region)
421
+ .map((secret) => secret.name)
422
+ .filter(Boolean);
423
+ }
424
+
425
+ private getServiceDiscoveryRegistries(command: IEcsServerGroupCommand, registries: any[]): any[] {
426
+ return registries
427
+ .filter((registry) => registry.account === command.credentials && registry.region === command.region)
428
+ .map((registry) => ({
429
+ ...registry,
430
+ displayName: registry.displayName || `${registry.name} (${registry.id})`,
431
+ }));
432
+ }
433
+
434
+ private getSecurityGroupNames(command: IEcsServerGroupCommand, securityGroups: any, subnets: ISubnet[]): string[] {
435
+ const subnetPurpose = command.subnetTypes?.[0] || command.subnetType;
436
+ if (!subnetPurpose) {
437
+ return [];
438
+ }
439
+
440
+ const subnet = subnets.find(
441
+ (candidate) =>
442
+ candidate.account === command.credentials &&
443
+ candidate.region === command.region &&
444
+ candidate.purpose === subnetPurpose,
445
+ );
446
+ const vpcId = subnet?.vpcId;
447
+ if (!vpcId) {
448
+ return [];
449
+ }
450
+
451
+ return (securityGroups?.[command.credentials]?.ecs?.[command.region] || [])
452
+ .filter((securityGroup: any) => securityGroup.vpcId === vpcId)
453
+ .map((securityGroup: any) => securityGroup.name)
454
+ .filter(Boolean);
455
+ }
456
+
457
+ private getTargetGroups(command: IEcsServerGroupCommand, loadBalancers: any[]): string[] {
458
+ const fromLoadBalancers = loadBalancers.flatMap((loadBalancer) =>
459
+ (loadBalancer.accounts || [])
460
+ .filter((account: any) => account.name === command.credentials)
461
+ .flatMap((account: any) =>
462
+ (account.regions || [])
463
+ .filter((region: any) => region.name === command.region)
464
+ .flatMap((region: any) =>
465
+ (region.loadBalancers || []).flatMap((regionLoadBalancer: any) =>
466
+ (regionLoadBalancer.targetGroups || []).map((targetGroup: any) =>
467
+ typeof targetGroup === 'string' ? targetGroup : targetGroup.targetGroupName,
468
+ ),
469
+ ),
470
+ ),
471
+ ),
472
+ );
473
+ const fromCommand = (command.targetGroupMappings || []).map(
474
+ (mapping: IEcsTargetGroupMapping) => mapping.targetGroup,
475
+ );
476
+ return uniq([...fromLoadBalancers, ...fromCommand, command.targetGroup]).filter(Boolean);
477
+ }
478
+
479
+ private getAvailableCapacityProviders(command: IEcsServerGroupCommand, capacityProviderDetails: any[]): string[] {
480
+ return uniq(
481
+ capacityProviderDetails
482
+ .filter((details) => !command.ecsClusterName || details.clusterName === command.ecsClusterName)
483
+ .flatMap((details) => details.capacityProviders || []),
484
+ );
485
+ }
486
+
487
+ private getDefaultCapacityProviderStrategy(
488
+ command: IEcsServerGroupCommand,
489
+ capacityProviderDetails: any[],
490
+ ): IEcsCapacityProviderStrategyItem[] {
491
+ return capacityProviderDetails
492
+ .filter((details) => !command.ecsClusterName || details.clusterName === command.ecsClusterName)
493
+ .flatMap((details) => details.defaultCapacityProviderStrategy || []);
494
+ }
495
+
496
+ private syncCommand(command: IEcsServerGroupCommand, formik = this.formik): void {
497
+ this.command = command;
498
+ formik?.setValues(command);
499
+ this.setState({ command });
500
+ }
501
+
502
+ private updateCommand = (formik: EcsFormikProps, field: string, value: any) => {
503
+ if (field === 'credentials' || field === 'region' || field === 'ecsClusterName' || field === 'subnetTypes') {
504
+ this.configureRequest += 1;
505
+ }
506
+ formik.setFieldValue(field, value);
507
+ const command = { ...this.command, [field]: value };
508
+ if (field === 'stack' || field === 'freeFormDetails') {
509
+ command.clusterChanged(command);
510
+ }
511
+ if (field === 'credentials') {
512
+ command.credentialsChanged(command);
513
+ }
514
+ if (field === 'region') {
515
+ command.regionChanged(command);
516
+ }
517
+ if (field === 'subnetTypes') {
518
+ command.subnetTypeChanged(command);
519
+ }
520
+ if (field === 'placementStrategyName') {
521
+ command.placementStrategyNameChanged(command);
522
+ }
523
+ if (field === 'useTaskDefinitionArtifact') {
524
+ command.serviceDiscoveryAssociations = (command.serviceDiscoveryAssociations || []).map((association) => ({
525
+ ...association,
526
+ containerName: value ? association.containerName || '' : null,
527
+ }));
528
+ }
529
+ if (field !== 'credentials' && field !== 'region') {
530
+ this.syncCommand(command, formik);
531
+ }
532
+ this.setState({ command }, () => {
533
+ if (field === 'ecsClusterName' || field === 'subnetTypes') {
534
+ this.configureCommand('', command);
535
+ }
536
+ });
537
+ };
538
+
539
+ private submit = (command: IEcsServerGroupCommand = this.state.command) => {
540
+ const { taskMonitor } = this.state;
541
+ this.setState({ command });
542
+ const mode = command.viewState?.mode;
543
+ if (mode === 'editPipeline' || mode === 'createPipeline') {
544
+ return this.props.closeModal(command);
545
+ }
546
+ return taskMonitor.submit(() =>
547
+ this.context.services.serverGroupWriter.cloneServerGroup(command as any, this.props.application),
548
+ );
549
+ };
550
+
551
+ public render() {
552
+ const { application, dismissModal, title } = this.props;
553
+ const { command, loaded, requiresTemplateSelection } = this.state;
554
+
555
+ if (requiresTemplateSelection) {
556
+ return (
557
+ <DeployInitializer
558
+ application={application}
559
+ cloudProvider="ecs"
560
+ command={command as any}
561
+ onDismiss={dismissModal}
562
+ onTemplateSelected={this.templateSelected}
563
+ />
564
+ );
565
+ }
566
+
567
+ return (
568
+ <WizardModal<IEcsServerGroupCommand>
569
+ closeModal={this.submit}
570
+ dismissModal={dismissModal}
571
+ heading={title}
572
+ initialValues={command}
573
+ loading={!loaded}
574
+ submitButtonLabel={command.viewState?.submitButtonLabel || 'Done'}
575
+ taskMonitor={this.state.taskMonitor}
576
+ validate={validateEcsServerGroup}
577
+ render={({ formik, nextIdx, wizard }) => {
578
+ this.formik = formik;
579
+ const configureFormikCommand = (query = '') => this.configureCommand(query, formik.values);
580
+ const updateFormikCommand = (field: string, value: any) => this.updateCommand(formik, field, value);
581
+ return (
582
+ <>
583
+ <WizardPage
584
+ label="Basic Settings"
585
+ order={nextIdx()}
586
+ render={({ innerRef }) => (
587
+ <EcsWizardPageValidation ref={innerRef} validator={validateEcsBasicSettings}>
588
+ <BasicSettings
589
+ application={application}
590
+ command={formik.values}
591
+ configureCommand={configureFormikCommand}
592
+ onFieldChange={updateFormikCommand}
593
+ />
594
+ </EcsWizardPageValidation>
595
+ )}
596
+ wizard={wizard}
597
+ />
598
+ <WizardPage
599
+ label="Networking"
600
+ order={nextIdx()}
601
+ render={() => (
602
+ <NetworkingSettings
603
+ application={application}
604
+ command={formik.values}
605
+ configureCommand={configureFormikCommand}
606
+ onFieldChange={updateFormikCommand}
607
+ />
608
+ )}
609
+ wizard={wizard}
610
+ />
611
+ <WizardPage
612
+ label="Task Definition"
613
+ order={nextIdx()}
614
+ render={({ innerRef }) => (
615
+ <EcsWizardPageValidation ref={innerRef} validator={validateEcsTaskDefinition}>
616
+ <TaskDefinitionSettings
617
+ application={application}
618
+ command={formik.values}
619
+ configureCommand={configureFormikCommand}
620
+ onFieldChange={updateFormikCommand}
621
+ />
622
+ </EcsWizardPageValidation>
623
+ )}
624
+ wizard={wizard}
625
+ />
626
+ {!formik.values.useTaskDefinitionArtifact && (
627
+ <WizardPage
628
+ label="Container"
629
+ order={nextIdx()}
630
+ render={({ innerRef }) => (
631
+ <EcsWizardPageValidation ref={innerRef} validator={validateEcsContainer}>
632
+ <ContainerSettings
633
+ application={application}
634
+ command={formik.values}
635
+ configureCommand={configureFormikCommand}
636
+ onFieldChange={updateFormikCommand}
637
+ />
638
+ </EcsWizardPageValidation>
639
+ )}
640
+ wizard={wizard}
641
+ />
642
+ )}
643
+ <WizardPage
644
+ label="Horizontal Scaling"
645
+ order={nextIdx()}
646
+ render={({ innerRef }) => (
647
+ <EcsWizardPageValidation ref={innerRef} validator={validateEcsCapacity}>
648
+ <HorizontalScalingSettings
649
+ application={application}
650
+ command={formik.values}
651
+ configureCommand={configureFormikCommand}
652
+ onFieldChange={updateFormikCommand}
653
+ />
654
+ </EcsWizardPageValidation>
655
+ )}
656
+ wizard={wizard}
657
+ />
658
+ {!formik.values.useTaskDefinitionArtifact && (
659
+ <WizardPage
660
+ label="Logging"
661
+ order={nextIdx()}
662
+ render={() => (
663
+ <LoggingSettings
664
+ application={application}
665
+ command={formik.values}
666
+ configureCommand={configureFormikCommand}
667
+ onFieldChange={updateFormikCommand}
668
+ />
669
+ )}
670
+ wizard={wizard}
671
+ />
672
+ )}
673
+ <WizardPage
674
+ label="Service Discovery"
675
+ order={nextIdx()}
676
+ render={({ innerRef }) => (
677
+ <EcsWizardPageValidation ref={innerRef} validator={validateEcsServiceDiscovery}>
678
+ <ServiceDiscoverySettings
679
+ application={application}
680
+ command={formik.values}
681
+ configureCommand={configureFormikCommand}
682
+ onFieldChange={updateFormikCommand}
683
+ />
684
+ </EcsWizardPageValidation>
685
+ )}
686
+ wizard={wizard}
687
+ />
688
+ <WizardPage
689
+ label="Advanced Settings"
690
+ order={nextIdx()}
691
+ render={() => (
692
+ <AdvancedSettings
693
+ application={application}
694
+ command={formik.values}
695
+ configureCommand={configureFormikCommand}
696
+ onFieldChange={updateFormikCommand}
697
+ />
698
+ )}
699
+ wizard={wizard}
700
+ />
701
+ </>
702
+ );
703
+ }}
704
+ />
705
+ );
706
+ }
707
+ }
708
+
709
+ export const EcsCloneServerGroupModal = Object.assign(
710
+ withRouter<IEcsCloneServerGroupModalProps & IRouterInjectedProps>(EcsCloneServerGroupModalComponent),
711
+ { show: EcsCloneServerGroupModalComponent.show },
712
+ );