@spinnaker/azure 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 (215) hide show
  1. package/dist/azure.module.d.ts +3 -1
  2. package/dist/image/image.reader.d.ts +4 -2
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/instance/azureInstanceType.service.d.ts +6 -2
  6. package/dist/instance/details/AzureInstanceDetails.d.ts +67 -0
  7. package/dist/loadBalancer/configure/AzureLoadBalancerChoiceModal.d.ts +10 -5
  8. package/dist/loadBalancer/configure/AzureLoadBalancerModal.d.ts +102 -0
  9. package/dist/loadBalancer/details/azureLoadBalancerDetails.d.ts +26 -0
  10. package/dist/loadBalancer/loadBalancer.transformer.d.ts +45 -2
  11. package/dist/pipeline/stages/AzureAccountRegionClusterSelector.d.ts +32 -0
  12. package/dist/pipeline/stages/bake/azureBakeStage.d.ts +46 -2
  13. package/dist/pipeline/stages/destroyAsg/azureDestroyAsgStage.d.ts +14 -2
  14. package/dist/pipeline/stages/disableAsg/azureDisableAsgStage.d.ts +4 -2
  15. package/dist/pipeline/stages/enableAsg/azureEnableAsgStage.d.ts +4 -2
  16. package/dist/securityGroup/configure/AzureSecurityGroupModal.d.ts +70 -0
  17. package/dist/securityGroup/details/AzureSecurityGroupDetails.d.ts +51 -0
  18. package/dist/securityGroup/securityGroup.reader.d.ts +3 -2
  19. package/dist/securityGroup/securityGroup.transformer.d.ts +3 -2
  20. package/dist/securityGroup/securityGroup.write.service.d.ts +9 -2
  21. package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +88 -2
  22. package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +30 -2
  23. package/dist/serverGroup/configure/wizard/AzureCloneServerGroupModal.d.ts +44 -0
  24. package/dist/serverGroup/configure/wizard/pages/ServerGroupAdvancedSettings.d.ts +8 -0
  25. package/dist/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.d.ts +12 -0
  26. package/dist/serverGroup/configure/wizard/pages/ServerGroupCapacityAndZones.d.ts +11 -0
  27. package/dist/serverGroup/configure/wizard/pages/ServerGroupHealthSettings.d.ts +10 -0
  28. package/dist/serverGroup/configure/wizard/pages/ServerGroupImageSettings.d.ts +12 -0
  29. package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +9 -0
  30. package/dist/serverGroup/configure/wizard/pages/ServerGroupLoadBalancers.d.ts +14 -0
  31. package/dist/serverGroup/configure/wizard/pages/ServerGroupNetworkSettings.d.ts +10 -0
  32. package/dist/serverGroup/configure/wizard/pages/ServerGroupSecurityGroups.d.ts +6 -0
  33. package/dist/serverGroup/configure/wizard/pages/ServerGroupTags.d.ts +9 -0
  34. package/dist/serverGroup/configure/wizard/pages/common.d.ts +14 -0
  35. package/dist/serverGroup/configure/wizard/pages/index.d.ts +10 -0
  36. package/dist/serverGroup/details/azureServerGroupDetails.d.ts +13 -0
  37. package/dist/serverGroup/details/rollback/RollbackServerGroupModal.d.ts +6 -4
  38. package/dist/serverGroup/serverGroup.transformer.d.ts +61 -2
  39. package/dist/validation/applicationName.validator.d.ts +6 -2
  40. package/package.json +5 -8
  41. package/src/azure.module.spec.ts +921 -0
  42. package/src/azure.module.ts +56 -68
  43. package/src/image/image.reader.js +4 -15
  44. package/src/image/image.reader.spec.js +4 -7
  45. package/src/instance/azureInstanceType.service.js +8 -16
  46. package/src/instance/azureInstanceType.service.spec.js +5 -13
  47. package/src/instance/details/AzureInstanceDetails.spec.tsx +364 -0
  48. package/src/instance/details/AzureInstanceDetails.tsx +533 -0
  49. package/src/loadBalancer/configure/AzureLoadBalancerChoiceModal.spec.tsx +125 -0
  50. package/src/loadBalancer/configure/AzureLoadBalancerChoiceModal.tsx +46 -26
  51. package/src/loadBalancer/configure/AzureLoadBalancerModal.spec.tsx +658 -0
  52. package/src/loadBalancer/configure/AzureLoadBalancerModal.tsx +960 -0
  53. package/src/loadBalancer/details/azureLoadBalancerDetails.spec.tsx +470 -0
  54. package/src/loadBalancer/details/azureLoadBalancerDetails.tsx +412 -0
  55. package/src/loadBalancer/loadBalancer.transformer.js +83 -93
  56. package/src/pipeline/stages/AzureAccountRegionClusterSelector.tsx +238 -0
  57. package/src/pipeline/stages/bake/azureBakeStage.tsx +723 -0
  58. package/src/pipeline/stages/destroyAsg/azureDestroyAsgStage.tsx +128 -0
  59. package/src/pipeline/stages/disableAsg/azureDisableAsgStage.tsx +92 -0
  60. package/src/pipeline/stages/enableAsg/azureEnableAsgStage.tsx +87 -0
  61. package/src/routerConsumers.spec.tsx +69 -0
  62. package/src/securityGroup/configure/AzureSecurityGroupModal.spec.tsx +558 -0
  63. package/src/securityGroup/configure/AzureSecurityGroupModal.tsx +782 -0
  64. package/src/securityGroup/details/AzureSecurityGroupDetails.spec.tsx +291 -0
  65. package/src/securityGroup/details/AzureSecurityGroupDetails.tsx +354 -0
  66. package/src/securityGroup/securityGroup.reader.js +3 -13
  67. package/src/securityGroup/securityGroup.transformer.js +3 -13
  68. package/src/securityGroup/securityGroup.write.service.spec.ts +105 -0
  69. package/src/securityGroup/securityGroup.write.service.ts +73 -0
  70. package/src/serverGroup/configure/serverGroupCommandBuilder.service.js +167 -166
  71. package/src/serverGroup/configure/serverGroupCommandBuilder.service.spec.js +63 -0
  72. package/src/serverGroup/configure/serverGroupConfiguration.service.js +325 -309
  73. package/src/serverGroup/configure/serverGroupConfiguration.service.spec.js +57 -53
  74. package/src/serverGroup/configure/wizard/AzureCloneServerGroupModal.spec.tsx +758 -0
  75. package/src/serverGroup/configure/wizard/AzureCloneServerGroupModal.tsx +349 -0
  76. package/src/serverGroup/configure/wizard/pages/ServerGroupAdvancedSettings.tsx +208 -0
  77. package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.tsx +122 -0
  78. package/src/serverGroup/configure/wizard/pages/ServerGroupCapacityAndZones.tsx +80 -0
  79. package/src/serverGroup/configure/wizard/pages/ServerGroupHealthSettings.tsx +97 -0
  80. package/src/serverGroup/configure/wizard/pages/ServerGroupImageSettings.tsx +151 -0
  81. package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +41 -0
  82. package/src/serverGroup/configure/wizard/pages/ServerGroupLoadBalancers.tsx +214 -0
  83. package/src/serverGroup/configure/wizard/pages/ServerGroupNetworkSettings.tsx +119 -0
  84. package/src/serverGroup/configure/wizard/pages/ServerGroupSecurityGroups.tsx +54 -0
  85. package/src/serverGroup/configure/wizard/pages/ServerGroupTags.tsx +40 -0
  86. package/src/serverGroup/configure/wizard/pages/common.tsx +72 -0
  87. package/src/serverGroup/configure/wizard/pages/index.ts +10 -0
  88. package/src/serverGroup/details/azureServerGroupDetails.spec.tsx +263 -0
  89. package/src/serverGroup/details/azureServerGroupDetails.tsx +318 -0
  90. package/src/serverGroup/details/rollback/RollbackServerGroupModal.tsx +24 -30
  91. package/src/serverGroup/serverGroup.transformer.js +11 -19
  92. package/src/serverGroup/serverGroup.transformer.spec.js +2 -6
  93. package/src/validation/applicationName.validator.js +14 -29
  94. package/dist/instance/details/instance.details.controller.d.ts +0 -2
  95. package/dist/loadBalancer/configure/createLoadBalancer.controller.d.ts +0 -2
  96. package/dist/loadBalancer/details/loadBalancerDetail.controller.d.ts +0 -2
  97. package/dist/pipeline/stages/bake/bakeExecutionDetails.controller.d.ts +0 -2
  98. package/dist/securityGroup/clone/cloneSecurityGroup.controller.d.ts +0 -2
  99. package/dist/securityGroup/configure/CreateSecurityGroupCtrl.d.ts +0 -2
  100. package/dist/securityGroup/configure/EditSecurityGroupCtrl.d.ts +0 -2
  101. package/dist/securityGroup/details/securityGroupDetail.controller.d.ts +0 -2
  102. package/dist/serverGroup/configure/serverGroup.configure.azure.module.d.ts +0 -2
  103. package/dist/serverGroup/configure/wizard/CloneServerGroup.azure.controller.d.ts +0 -2
  104. package/dist/serverGroup/configure/wizard/ServerGroupInstanceArchetype.controller.d.ts +0 -2
  105. package/dist/serverGroup/configure/wizard/ServerGroupInstanceType.controller.d.ts +0 -2
  106. package/dist/serverGroup/configure/wizard/advancedSettings/ServerGroupAdvancedSettings.controller.d.ts +0 -2
  107. package/dist/serverGroup/configure/wizard/advancedSettings/advancedSettingsSelector.directive.d.ts +0 -2
  108. package/dist/serverGroup/configure/wizard/basicSettings/ServerGroupBasicSettings.controller.d.ts +0 -2
  109. package/dist/serverGroup/configure/wizard/basicSettings/image.regional.filter.d.ts +0 -2
  110. package/dist/serverGroup/configure/wizard/capacity/capacitySelector.directive.d.ts +0 -2
  111. package/dist/serverGroup/configure/wizard/healthSettings/ServerGroupHealthSettings.controller.d.ts +0 -2
  112. package/dist/serverGroup/configure/wizard/healthSettings/healthSettingsSelector.directive.d.ts +0 -2
  113. package/dist/serverGroup/configure/wizard/image/ServerGroupImageSettings.controller.d.ts +0 -2
  114. package/dist/serverGroup/configure/wizard/image/imageSettingsSelector.directive.d.ts +0 -2
  115. package/dist/serverGroup/configure/wizard/loadBalancers/ServerGroupLoadBalancers.controller.d.ts +0 -2
  116. package/dist/serverGroup/configure/wizard/loadBalancers/serverGroupLoadBalancersSelector.directive.d.ts +0 -2
  117. package/dist/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettings.controller.d.ts +0 -2
  118. package/dist/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettingsSelector.directive.d.ts +0 -2
  119. package/dist/serverGroup/configure/wizard/securityGroup/ServerGroupSecurityGroups.controller.d.ts +0 -2
  120. package/dist/serverGroup/configure/wizard/securityGroup/serverGroupSecurityGroupsSelector.directive.d.ts +0 -2
  121. package/dist/serverGroup/configure/wizard/tags/tagsSelector.directive.d.ts +0 -2
  122. package/dist/serverGroup/configure/wizard/zones/zoneSelector.directive.d.ts +0 -2
  123. package/dist/serverGroup/details/serverGroup.details.module.d.ts +0 -2
  124. package/dist/serverGroup/details/serverGroupDetails.azure.controller.d.ts +0 -2
  125. package/src/instance/details/instance.details.controller.js +0 -371
  126. package/src/instance/details/instance.details.controller.spec.js +0 -129
  127. package/src/instance/details/instanceDetails.html +0 -43
  128. package/src/loadBalancer/configure/advancedSettings.html +0 -95
  129. package/src/loadBalancer/configure/createLoadBalancer.controller.js +0 -275
  130. package/src/loadBalancer/configure/createLoadBalancer.controller.spec.js +0 -39
  131. package/src/loadBalancer/configure/createLoadBalancer.html +0 -25
  132. package/src/loadBalancer/configure/createLoadBalancerProperties.html +0 -128
  133. package/src/loadBalancer/configure/editLoadBalancer.html +0 -26
  134. package/src/loadBalancer/configure/healthCheck.html +0 -74
  135. package/src/loadBalancer/configure/listeners.html +0 -50
  136. package/src/loadBalancer/details/loadBalancerDetail.controller.js +0 -181
  137. package/src/loadBalancer/details/loadBalancerDetail.controller.spec.js +0 -39
  138. package/src/loadBalancer/details/loadBalancerDetail.html +0 -128
  139. package/src/pipeline/stages/bake/azureBakeStage.js +0 -276
  140. package/src/pipeline/stages/bake/bakeExecutionDetails.controller.js +0 -40
  141. package/src/pipeline/stages/bake/bakeExecutionDetails.html +0 -50
  142. package/src/pipeline/stages/bake/bakeStage.html +0 -187
  143. package/src/pipeline/stages/destroyAsg/azureDestroyAsgStage.js +0 -73
  144. package/src/pipeline/stages/destroyAsg/destroyAsgStage.html +0 -9
  145. package/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html +0 -1
  146. package/src/pipeline/stages/disableAsg/azureDisableAsgStage.js +0 -65
  147. package/src/pipeline/stages/disableAsg/disableAsgStage.html +0 -11
  148. package/src/pipeline/stages/disableAsg/disableAsgStepLabel.html +0 -1
  149. package/src/pipeline/stages/enableAsg/azureEnableAsgStage.js +0 -70
  150. package/src/pipeline/stages/enableAsg/enableAsgStage.html +0 -11
  151. package/src/pipeline/stages/enableAsg/enableAsgStepLabel.html +0 -1
  152. package/src/securityGroup/clone/cloneSecurityGroup.controller.js +0 -162
  153. package/src/securityGroup/clone/cloneSecurityGroup.html +0 -8
  154. package/src/securityGroup/configure/CreateSecurityGroup.controller.spec.js +0 -78
  155. package/src/securityGroup/configure/CreateSecurityGroupCtrl.js +0 -265
  156. package/src/securityGroup/configure/EditSecurityGroupCtrl.js +0 -222
  157. package/src/securityGroup/configure/configSecurityGroup.mixin.controller.js +0 -262
  158. package/src/securityGroup/configure/createSecurityGroup.html +0 -20
  159. package/src/securityGroup/configure/createSecurityGroupIngress.html +0 -75
  160. package/src/securityGroup/configure/createSecurityGroupProperties.html +0 -88
  161. package/src/securityGroup/configure/editSecurityGroup.html +0 -19
  162. package/src/securityGroup/details/securityGroupDetail.controller.js +0 -142
  163. package/src/securityGroup/details/securityGroupDetail.html +0 -78
  164. package/src/securityGroup/securityGroup.write.service.js +0 -57
  165. package/src/serverGroup/configure/serverGroup.configure.azure.module.js +0 -47
  166. package/src/serverGroup/configure/wizard/CloneServerGroup.azure.controller.js +0 -247
  167. package/src/serverGroup/configure/wizard/ServerGroupInstanceArchetype.controller.js +0 -35
  168. package/src/serverGroup/configure/wizard/ServerGroupInstanceType.controller.js +0 -15
  169. package/src/serverGroup/configure/wizard/advancedSettings/ServerGroupAdvancedSettings.controller.js +0 -25
  170. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.html +0 -5
  171. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettingsSelector.directive.html +0 -138
  172. package/src/serverGroup/configure/wizard/advancedSettings/advancedSettingsSelector.directive.js +0 -43
  173. package/src/serverGroup/configure/wizard/basicSettings/ServerGroupBasicSettings.controller.js +0 -71
  174. package/src/serverGroup/configure/wizard/basicSettings/basicSettings.html +0 -131
  175. package/src/serverGroup/configure/wizard/basicSettings/image.regional.filter.js +0 -15
  176. package/src/serverGroup/configure/wizard/basicSettings/image.regional.filter.spec.js +0 -42
  177. package/src/serverGroup/configure/wizard/capacity/capacity.html +0 -5
  178. package/src/serverGroup/configure/wizard/capacity/capacitySelector.directive.html +0 -20
  179. package/src/serverGroup/configure/wizard/capacity/capacitySelector.directive.js +0 -21
  180. package/src/serverGroup/configure/wizard/capacity/zones.html +0 -5
  181. package/src/serverGroup/configure/wizard/healthSettings/ServerGroupHealthSettings.controller.js +0 -38
  182. package/src/serverGroup/configure/wizard/healthSettings/healthSettings.html +0 -5
  183. package/src/serverGroup/configure/wizard/healthSettings/healthSettingsSelector.directive.html +0 -32
  184. package/src/serverGroup/configure/wizard/healthSettings/healthSettingsSelector.directive.js +0 -22
  185. package/src/serverGroup/configure/wizard/image/ServerGroupImageSettings.controller.js +0 -34
  186. package/src/serverGroup/configure/wizard/image/imageSettings.html +0 -5
  187. package/src/serverGroup/configure/wizard/image/imageSettingsSelector.directive.html +0 -33
  188. package/src/serverGroup/configure/wizard/image/imageSettingsSelector.directive.js +0 -22
  189. package/src/serverGroup/configure/wizard/instanceArchetype.html +0 -33
  190. package/src/serverGroup/configure/wizard/instanceType/instanceType.html +0 -10
  191. package/src/serverGroup/configure/wizard/instanceType.html +0 -33
  192. package/src/serverGroup/configure/wizard/loadBalancers/ServerGroupLoadBalancers.controller.js +0 -137
  193. package/src/serverGroup/configure/wizard/loadBalancers/loadBalancers.html +0 -3
  194. package/src/serverGroup/configure/wizard/loadBalancers/serverGroupLoadBalancersSelector.directive.html +0 -41
  195. package/src/serverGroup/configure/wizard/loadBalancers/serverGroupLoadBalancersSelector.directive.js +0 -22
  196. package/src/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettings.controller.js +0 -44
  197. package/src/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettingsSelector.directive.html +0 -54
  198. package/src/serverGroup/configure/wizard/networkSettings/ServerGroupNetworkSettingsSelector.directive.js +0 -19
  199. package/src/serverGroup/configure/wizard/networkSettings/networkSettings.html +0 -3
  200. package/src/serverGroup/configure/wizard/securityGroup/ServerGroupSecurityGroups.controller.js +0 -28
  201. package/src/serverGroup/configure/wizard/securityGroup/securityGroups.html +0 -3
  202. package/src/serverGroup/configure/wizard/securityGroup/serverGroupSecurityGroupsSelector.directive.html +0 -40
  203. package/src/serverGroup/configure/wizard/securityGroup/serverGroupSecurityGroupsSelector.directive.js +0 -38
  204. package/src/serverGroup/configure/wizard/serverGroupWizard.html +0 -61
  205. package/src/serverGroup/configure/wizard/tags/tags.html +0 -5
  206. package/src/serverGroup/configure/wizard/tags/tagsSelector.directive.html +0 -16
  207. package/src/serverGroup/configure/wizard/tags/tagsSelector.directive.js +0 -30
  208. package/src/serverGroup/configure/wizard/templateSelection.html +0 -9
  209. package/src/serverGroup/configure/wizard/zones/zoneSelector.directive.html +0 -21
  210. package/src/serverGroup/configure/wizard/zones/zoneSelector.directive.js +0 -28
  211. package/src/serverGroup/details/serverGroup.details.module.js +0 -11
  212. package/src/serverGroup/details/serverGroupDetails.azure.controller.js +0 -260
  213. package/src/serverGroup/details/serverGroupDetails.html +0 -98
  214. package/src/subnet/subnetSelectField.directive.html +0 -28
  215. package/src/subnet/subnetSelectField.directive.js +0 -35
@@ -0,0 +1,533 @@
1
+ import { UISref } from '@uirouter/react';
2
+ import React from 'react';
3
+ import { Dropdown, MenuItem } from 'react-bootstrap';
4
+
5
+ import type { Application, IInstanceDetailsProps, IRouterInjectedProps } from '@spinnaker/core';
6
+ import {
7
+ AccountTag,
8
+ CollapsibleSection,
9
+ ConfirmationModalService,
10
+ InstanceDetailsHeader,
11
+ InstanceReader,
12
+ InstanceWriter,
13
+ RecentHistoryService,
14
+ timestamp,
15
+ withRouter,
16
+ } from '@spinnaker/core';
17
+
18
+ interface IAzureInstanceParams {
19
+ account?: string;
20
+ instanceId: string;
21
+ provider?: string;
22
+ region?: string;
23
+ }
24
+
25
+ interface IAzureInstanceDetailsProps extends Partial<IInstanceDetailsProps> {
26
+ app: Application;
27
+ initialInstance?: IAzureLoadedInstance;
28
+ instance?: IAzureInstanceParams;
29
+ $stateParams?: IInstanceDetailsProps['$stateParams'] & Partial<IAzureInstanceParams>;
30
+ }
31
+
32
+ interface IAzureLoadInstanceDetailsProps {
33
+ app: Application;
34
+ instance: IAzureInstanceParams;
35
+ }
36
+
37
+ interface IAzureInstanceLookupResult {
38
+ account?: string;
39
+ extraData?: { [key: string]: string };
40
+ loadBalancers?: string[];
41
+ region?: string;
42
+ summary?: any;
43
+ vpcId?: string;
44
+ }
45
+
46
+ export interface IAzureLoadedInstance {
47
+ [key: string]: any;
48
+ account?: string;
49
+ baseIpAddress?: string;
50
+ health?: any[];
51
+ healthMetrics?: any[];
52
+ instanceId?: string;
53
+ instanceIdNotFound?: string;
54
+ loadBalancers?: string[];
55
+ region?: string;
56
+ serverGroup?: string;
57
+ vpcId?: string;
58
+ }
59
+
60
+ function findInServerGroups(app: Application, instanceId: string): IAzureInstanceLookupResult | undefined {
61
+ const serverGroups = app.serverGroups?.data || [];
62
+ for (const serverGroup of serverGroups) {
63
+ const summary = serverGroup.instances?.find((possibleInstance: any) => possibleInstance.id === instanceId);
64
+ if (summary) {
65
+ return {
66
+ account: serverGroup.account,
67
+ extraData: { serverGroup: serverGroup.name, vpcId: serverGroup.vpcId },
68
+ loadBalancers: serverGroup.loadBalancers,
69
+ region: serverGroup.region,
70
+ summary,
71
+ vpcId: serverGroup.vpcId,
72
+ };
73
+ }
74
+ }
75
+ return undefined;
76
+ }
77
+
78
+ function findInLoadBalancerInstances(app: Application, instanceId: string): IAzureInstanceLookupResult | undefined {
79
+ const loadBalancers = app.loadBalancers?.data || [];
80
+ for (const loadBalancer of loadBalancers) {
81
+ const summary = loadBalancer.instances?.find((possibleInstance: any) => possibleInstance.id === instanceId);
82
+ if (summary) {
83
+ return {
84
+ account: loadBalancer.account,
85
+ loadBalancers: [loadBalancer.name],
86
+ region: loadBalancer.region,
87
+ summary,
88
+ vpcId: loadBalancer.vpcId,
89
+ };
90
+ }
91
+ }
92
+ return undefined;
93
+ }
94
+
95
+ function findInLoadBalancerServerGroups(app: Application, instanceId: string): IAzureInstanceLookupResult | undefined {
96
+ const loadBalancers = app.loadBalancers?.data || [];
97
+ for (const loadBalancer of loadBalancers) {
98
+ const serverGroups = (loadBalancer.serverGroups || []).filter((serverGroup: any) => !serverGroup.isDisabled);
99
+ for (const serverGroup of serverGroups) {
100
+ const summary = serverGroup.instances?.find((possibleInstance: any) => possibleInstance.id === instanceId);
101
+ if (summary) {
102
+ return {
103
+ account: loadBalancer.account,
104
+ loadBalancers: [loadBalancer.name],
105
+ region: loadBalancer.region,
106
+ summary,
107
+ vpcId: loadBalancer.vpcId,
108
+ };
109
+ }
110
+ }
111
+ }
112
+ return undefined;
113
+ }
114
+
115
+ function findInDisabledLoadBalancerServerGroups(
116
+ app: Application,
117
+ instanceId: string,
118
+ ): IAzureInstanceLookupResult | undefined {
119
+ const loadBalancers = app.loadBalancers?.data || [];
120
+ for (const loadBalancer of loadBalancers) {
121
+ const disabledServerGroups = (loadBalancer.serverGroups || []).filter((serverGroup: any) => serverGroup.isDisabled);
122
+ for (const serverGroup of disabledServerGroups) {
123
+ const summary = serverGroup.instances?.find((possibleInstance: any) => possibleInstance.id === instanceId);
124
+ if (summary) {
125
+ return {
126
+ account: loadBalancer.account,
127
+ loadBalancers: [loadBalancer.name],
128
+ region: loadBalancer.region,
129
+ summary,
130
+ vpcId: loadBalancer.vpcId,
131
+ };
132
+ }
133
+ }
134
+ }
135
+ return undefined;
136
+ }
137
+
138
+ function findAzureInstanceSummary(
139
+ app: Application,
140
+ instance: IAzureInstanceParams,
141
+ ): IAzureInstanceLookupResult | undefined {
142
+ if (!app.serverGroups) {
143
+ return { account: instance.account, loadBalancers: [], region: instance.region, summary: {} };
144
+ }
145
+
146
+ return (
147
+ findInServerGroups(app, instance.instanceId) ||
148
+ findInLoadBalancerInstances(app, instance.instanceId) ||
149
+ findInLoadBalancerServerGroups(app, instance.instanceId) ||
150
+ findInDisabledLoadBalancerServerGroups(app, instance.instanceId)
151
+ );
152
+ }
153
+
154
+ function extractHealthMetrics(summary: any, details: any, standalone: boolean): any[] {
155
+ const summaryHealth = standalone ? details.health : summary.health;
156
+ const displayableMetrics = (summaryHealth || []).filter(
157
+ (metric: any) => metric.type !== 'Azure' || metric.state !== 'Unknown',
158
+ );
159
+ const latestHealth = details.health || [];
160
+
161
+ return displayableMetrics.map((metric: any) => {
162
+ const latestMatch = latestHealth.find((latest: any) => latest.type === metric.type);
163
+ return latestMatch ? { ...latestMatch, ...metric } : metric;
164
+ });
165
+ }
166
+
167
+ export async function loadAzureInstanceDetails({
168
+ app,
169
+ instance,
170
+ }: IAzureLoadInstanceDetailsProps): Promise<IAzureLoadedInstance> {
171
+ const lookup = findAzureInstanceSummary(app, instance);
172
+
173
+ if (!lookup?.summary) {
174
+ return { instanceIdNotFound: instance.instanceId };
175
+ }
176
+
177
+ const { account, region } = lookup;
178
+ if (!account || !region) {
179
+ return { instanceIdNotFound: instance.instanceId };
180
+ }
181
+
182
+ RecentHistoryService.addExtraDataToLatest('instances', {
183
+ ...lookup.extraData,
184
+ account,
185
+ region,
186
+ });
187
+
188
+ const details = (await InstanceReader.getInstanceDetails(account, region, instance.instanceId)) as any;
189
+ const healthMetrics = extractHealthMetrics(lookup.summary, details, app.isStandalone);
190
+ const discoveryMetric = healthMetrics.find((metric: any) => metric.type === 'Discovery');
191
+ const loadedInstance = {
192
+ ...lookup.summary,
193
+ ...details,
194
+ account,
195
+ baseIpAddress: details.publicDnsName || details.privateIpAddress,
196
+ healthMetrics,
197
+ instanceId: details.instanceId || lookup.summary.instanceId || lookup.summary.id || instance.instanceId,
198
+ loadBalancers: lookup.loadBalancers,
199
+ region,
200
+ vpcId: lookup.vpcId,
201
+ } as IAzureLoadedInstance;
202
+
203
+ if (lookup.extraData?.serverGroup) {
204
+ loadedInstance.serverGroup = lookup.extraData.serverGroup;
205
+ }
206
+
207
+ if (discoveryMetric?.vipAddress) {
208
+ loadedInstance.vipAddress = discoveryMetric.vipAddress.includes(',')
209
+ ? discoveryMetric.vipAddress.split(',')
210
+ : [discoveryMetric.vipAddress];
211
+ }
212
+
213
+ return loadedInstance;
214
+ }
215
+
216
+ export class AzureInstanceDetailsComponent extends React.Component<
217
+ IAzureInstanceDetailsProps & IRouterInjectedProps,
218
+ { instance?: IAzureLoadedInstance; loading: boolean }
219
+ > {
220
+ public state = { instance: this.props.initialInstance, loading: !this.props.initialInstance };
221
+
222
+ private activeRequestId = 0;
223
+ private isUnmounted = false;
224
+ private unsubscribeFromRefresh: () => void;
225
+
226
+ public componentDidMount(): void {
227
+ if (this.props.initialInstance) {
228
+ return;
229
+ }
230
+
231
+ const { app } = this.props;
232
+ const retrieveWhenReady =
233
+ app.isStandalone || !app.serverGroups
234
+ ? Promise.resolve()
235
+ : Promise.all([app.serverGroups.ready(), app.loadBalancers.ready()]);
236
+ retrieveWhenReady.then(() => {
237
+ if (this.isUnmounted) {
238
+ return;
239
+ }
240
+ this.retrieveInstance();
241
+ if (!app.isStandalone && app.serverGroups?.onRefresh) {
242
+ this.unsubscribeFromRefresh = app.serverGroups.onRefresh(this.retrieveInstance);
243
+ }
244
+ });
245
+ }
246
+
247
+ public componentDidUpdate(prevProps: IAzureInstanceDetailsProps): void {
248
+ const previousParams = this.getInstanceParams(prevProps);
249
+ const currentParams = this.getInstanceParams();
250
+ if (
251
+ previousParams.account !== currentParams.account ||
252
+ previousParams.instanceId !== currentParams.instanceId ||
253
+ previousParams.region !== currentParams.region
254
+ ) {
255
+ this.retrieveInstance(true);
256
+ }
257
+ }
258
+
259
+ public componentWillUnmount(): void {
260
+ this.isUnmounted = true;
261
+ if (this.unsubscribeFromRefresh) {
262
+ this.unsubscribeFromRefresh();
263
+ }
264
+ }
265
+
266
+ private getInstanceParams(
267
+ props: IAzureInstanceDetailsProps & Partial<IRouterInjectedProps> = this.props,
268
+ ): IAzureInstanceParams {
269
+ const params = props.instance || props.$stateParams || props.stateParams;
270
+ return {
271
+ account: params?.account,
272
+ instanceId: params?.instanceId,
273
+ provider: params?.provider,
274
+ region: params?.region,
275
+ };
276
+ }
277
+
278
+ private retrieveInstance = (clearInstance = false): void => {
279
+ const requestId = ++this.activeRequestId;
280
+ const instanceParams = this.getInstanceParams();
281
+ this.setState(clearInstance ? { instance: undefined, loading: true } : { loading: true });
282
+ loadAzureInstanceDetails({ app: this.props.app, instance: instanceParams }).then(
283
+ (instance) => {
284
+ if (!this.isUnmounted && requestId === this.activeRequestId) {
285
+ this.setState({ instance, loading: false });
286
+ }
287
+ },
288
+ () => {
289
+ if (!this.isUnmounted && requestId === this.activeRequestId) {
290
+ this.setState({ loading: false });
291
+ this.props.stateService.go('^');
292
+ }
293
+ },
294
+ );
295
+ };
296
+
297
+ public render(): JSX.Element {
298
+ const { app } = this.props;
299
+ const { instance, loading } = this.state;
300
+ const instanceId = instance?.instanceId || instance?.instanceIdNotFound || this.getInstanceParams().instanceId;
301
+
302
+ return (
303
+ <div className="details-panel">
304
+ <div className="header">
305
+ <InstanceDetailsHeader
306
+ healthState={instance?.healthState}
307
+ instanceId={instanceId}
308
+ loading={loading}
309
+ standalone={app.isStandalone}
310
+ />
311
+ {!loading && instance && !instance.instanceIdNotFound && (
312
+ <div className="actions">
313
+ <AzureInstanceActionsComponent
314
+ app={app}
315
+ instance={instance}
316
+ router={this.props.router}
317
+ stateParams={this.props.stateParams}
318
+ stateService={this.props.stateService}
319
+ />
320
+ </div>
321
+ )}
322
+ </div>
323
+ {!loading && instance && !instance.instanceIdNotFound && (
324
+ <div className="content">
325
+ <AzureInstanceInformationSection instance={instance} />
326
+ </div>
327
+ )}
328
+ {!loading && instance?.instanceIdNotFound && (
329
+ <div className="content">
330
+ <div className="content-section">
331
+ <div className="content-body text-center">
332
+ <h3>Instance not found.</h3>
333
+ </div>
334
+ </div>
335
+ </div>
336
+ )}
337
+ </div>
338
+ );
339
+ }
340
+ }
341
+
342
+ function hasHealthState(instance: IAzureLoadedInstance, healthProviderType: string, state: string): boolean {
343
+ return (instance.health || []).some((health: any) => health.type === healthProviderType && health.state === state);
344
+ }
345
+
346
+ function canRegisterWithLoadBalancer(instance: IAzureLoadedInstance): boolean {
347
+ if (!instance.loadBalancers?.length) {
348
+ return false;
349
+ }
350
+ return (
351
+ hasHealthState(instance, 'LoadBalancer', 'OutOfService') ||
352
+ !(instance.health || []).some((health: any) => health.type === 'LoadBalancer')
353
+ );
354
+ }
355
+
356
+ function canRegisterWithDiscovery(instance: IAzureLoadedInstance): boolean {
357
+ return hasHealthState(instance, 'Discovery', 'OutOfService');
358
+ }
359
+
360
+ export function AzureInstanceActionsComponent({
361
+ app,
362
+ instance,
363
+ stateService,
364
+ }: {
365
+ app: Application;
366
+ instance: IAzureLoadedInstance;
367
+ } & IRouterInjectedProps): JSX.Element {
368
+ const loadBalancerNames = (instance.loadBalancers || []).join(' and ');
369
+ const closeIfCurrentInstance = () => {
370
+ if (stateService.includes('**.instanceDetails', { instanceId: instance.instanceId })) {
371
+ stateService.go('^');
372
+ }
373
+ };
374
+ const confirm = (
375
+ header: string,
376
+ buttonText: string,
377
+ title: string,
378
+ submitMethod: () => PromiseLike<any>,
379
+ closeOnComplete = false,
380
+ ) => {
381
+ ConfirmationModalService.confirm({
382
+ header,
383
+ buttonText,
384
+ account: instance.account,
385
+ taskMonitorConfig: {
386
+ application: app,
387
+ title,
388
+ onTaskComplete: closeOnComplete ? closeIfCurrentInstance : undefined,
389
+ },
390
+ submitMethod,
391
+ });
392
+ };
393
+
394
+ return (
395
+ <Dropdown className="dropdown" id="azure-instance-actions-dropdown">
396
+ <Dropdown.Toggle className="btn btn-sm btn-primary dropdown-toggle">Instance Actions</Dropdown.Toggle>
397
+ <Dropdown.Menu>
398
+ <MenuItem
399
+ onClick={() =>
400
+ confirm(
401
+ `Really terminate ${instance.instanceId}?`,
402
+ `Terminate ${instance.instanceId}`,
403
+ `Terminating ${instance.instanceId}`,
404
+ () => InstanceWriter.terminateInstance(instance as any, app),
405
+ true,
406
+ )
407
+ }
408
+ >
409
+ Terminate
410
+ </MenuItem>
411
+ {instance.serverGroup && (
412
+ <MenuItem
413
+ onClick={() =>
414
+ confirm(
415
+ `Really terminate ${instance.instanceId} and shrink ${instance.serverGroup}?`,
416
+ `Terminate ${instance.instanceId} and shrink ${instance.serverGroup}`,
417
+ `Terminating ${instance.instanceId} and shrinking server group`,
418
+ () => InstanceWriter.terminateInstanceAndShrinkServerGroup(instance as any, app),
419
+ true,
420
+ )
421
+ }
422
+ >
423
+ Terminate and Shrink Server Group
424
+ </MenuItem>
425
+ )}
426
+ <MenuItem
427
+ onClick={() =>
428
+ confirm(
429
+ `Really reboot ${instance.instanceId}?`,
430
+ `Reboot ${instance.instanceId}`,
431
+ `Rebooting ${instance.instanceId}`,
432
+ () => InstanceWriter.rebootInstance(instance as any, app),
433
+ )
434
+ }
435
+ >
436
+ Reboot
437
+ </MenuItem>
438
+ {canRegisterWithLoadBalancer(instance) && (
439
+ <MenuItem
440
+ onClick={() =>
441
+ confirm(
442
+ `Really register ${instance.instanceId} with ${loadBalancerNames}?`,
443
+ `Register ${instance.instanceId}`,
444
+ `Registering ${instance.instanceId} with ${loadBalancerNames}`,
445
+ () => InstanceWriter.registerInstanceWithLoadBalancer(instance as any, app),
446
+ )
447
+ }
448
+ >
449
+ Register with Load Balancer
450
+ </MenuItem>
451
+ )}
452
+ {(instance.health || []).some((health: any) => health.type === 'LoadBalancer') && (
453
+ <MenuItem
454
+ onClick={() =>
455
+ confirm(
456
+ `Really deregister ${instance.instanceId} from ${loadBalancerNames}?`,
457
+ `Deregister ${instance.instanceId}`,
458
+ `Deregistering ${instance.instanceId} from ${loadBalancerNames}`,
459
+ () => InstanceWriter.deregisterInstanceFromLoadBalancer(instance as any, app),
460
+ )
461
+ }
462
+ >
463
+ Deregister from Load Balancer
464
+ </MenuItem>
465
+ )}
466
+ {canRegisterWithDiscovery(instance) && (
467
+ <MenuItem
468
+ onClick={() =>
469
+ confirm(
470
+ `Really enable ${instance.instanceId} in discovery?`,
471
+ `Enable ${instance.instanceId}`,
472
+ `Enabling ${instance.instanceId} in discovery`,
473
+ () => InstanceWriter.enableInstanceInDiscovery(instance as any, app),
474
+ )
475
+ }
476
+ >
477
+ Enable in Discovery
478
+ </MenuItem>
479
+ )}
480
+ {(instance.health || []).some((health: any) => health.type === 'Discovery') && (
481
+ <MenuItem
482
+ onClick={() =>
483
+ confirm(
484
+ `Really disable ${instance.instanceId} in discovery?`,
485
+ `Disable ${instance.instanceId}`,
486
+ `Disabling ${instance.instanceId} in discovery`,
487
+ () => InstanceWriter.disableInstanceInDiscovery(instance as any, app),
488
+ )
489
+ }
490
+ >
491
+ Disable in Discovery
492
+ </MenuItem>
493
+ )}
494
+ </Dropdown.Menu>
495
+ </Dropdown>
496
+ );
497
+ }
498
+
499
+ export const AzureInstanceActions = withRouter(AzureInstanceActionsComponent);
500
+ export const AzureInstanceDetails = withRouter(AzureInstanceDetailsComponent);
501
+
502
+ export function AzureInstanceInformationSection({ instance }: { instance: IAzureLoadedInstance }): JSX.Element {
503
+ return (
504
+ <CollapsibleSection heading="Instance Information" defaultExpanded={true}>
505
+ <dl className="dl-horizontal dl-narrow">
506
+ <dt>Launched</dt>
507
+ <dd>{instance.launchTime ? timestamp(instance.launchTime) : '(Unknown)'}</dd>
508
+ <dt>In</dt>
509
+ <dd>
510
+ <AccountTag account={instance.account} /> {instance.region || '(Unknown)'}
511
+ </dd>
512
+ <dt>Type</dt>
513
+ <dd>{instance.instanceType || '(Unknown)'}</dd>
514
+ {instance.serverGroup && <dt>Server Group</dt>}
515
+ {instance.serverGroup && (
516
+ <dd>
517
+ <UISref
518
+ to="^.serverGroup"
519
+ params={{
520
+ region: instance.region,
521
+ accountId: instance.account,
522
+ serverGroup: instance.serverGroup,
523
+ provider: instance.provider,
524
+ }}
525
+ >
526
+ <a>{instance.serverGroup}</a>
527
+ </UISref>
528
+ </dd>
529
+ )}
530
+ </dl>
531
+ </CollapsibleSection>
532
+ );
533
+ }
@@ -0,0 +1,125 @@
1
+ import { ReactModal } from '@spinnaker/core';
2
+
3
+ import { AzureLoadBalancerTypes } from '../../utility';
4
+ import { AzureLoadBalancerChoiceModal } from './AzureLoadBalancerChoiceModal';
5
+ import { AzureLoadBalancerModal } from './AzureLoadBalancerModal';
6
+
7
+ describe('AzureLoadBalancerChoiceModal', () => {
8
+ it('declares support for pipeline config results', () => {
9
+ expect((AzureLoadBalancerChoiceModal as any).supportsPipelineConfig).toBe(true);
10
+ });
11
+
12
+ it('opens the configure modal directly when editing an existing load balancer', () => {
13
+ const application = { name: 'fnord' } as any;
14
+ const closeModal = jasmine.createSpy('closeModal');
15
+ const dismissModal = jasmine.createSpy('dismissModal');
16
+ const loadBalancer = { name: 'fnord-main', loadBalancerType: 'Azure Load Balancer' };
17
+ const configurePromise = Promise.resolve(loadBalancer);
18
+ const show = spyOn(AzureLoadBalancerModal, 'show').and.returnValue(configurePromise as any);
19
+ const reactModalShow = spyOn(ReactModal, 'show');
20
+
21
+ const result = AzureLoadBalancerChoiceModal.show({
22
+ application,
23
+ closeModal,
24
+ dismissModal,
25
+ loadBalancer,
26
+ isNew: false,
27
+ } as any);
28
+
29
+ expect(result).toBe(configurePromise as any);
30
+ expect(reactModalShow).not.toHaveBeenCalled();
31
+ expect(show).toHaveBeenCalledWith(
32
+ jasmine.objectContaining({
33
+ app: application,
34
+ application,
35
+ closeModal,
36
+ dismissModal,
37
+ loadBalancer,
38
+ isNew: false,
39
+ forPipelineConfig: false,
40
+ }),
41
+ );
42
+ });
43
+
44
+ it('opens the configure modal with the selected load balancer type', () => {
45
+ const application = { name: 'fnord' } as any;
46
+ const closeModal = jasmine.createSpy('closeModal');
47
+ const dismissModal = jasmine.createSpy('dismissModal');
48
+ const configurePromise = Promise.resolve();
49
+ const show = spyOn(AzureLoadBalancerModal, 'show').and.returnValue(configurePromise as any);
50
+ const modal = new AzureLoadBalancerChoiceModal({ application, closeModal, dismissModal } as any);
51
+ const selectedChoice = AzureLoadBalancerTypes[1];
52
+
53
+ modal.state = { ...modal.state, selectedChoice };
54
+ (modal as any).choose();
55
+
56
+ expect(dismissModal).not.toHaveBeenCalled();
57
+ expect(closeModal).toHaveBeenCalledWith(configurePromise);
58
+ expect(show).toHaveBeenCalledWith(
59
+ jasmine.objectContaining({
60
+ app: application,
61
+ application,
62
+ loadBalancer: null,
63
+ isNew: true,
64
+ forPipelineConfig: false,
65
+ loadBalancerType: selectedChoice,
66
+ }),
67
+ );
68
+ });
69
+
70
+ it('propagates the configure modal result to the choice modal caller', async () => {
71
+ const application = { name: 'fnord' } as any;
72
+ const closeModal = jasmine.createSpy('closeModal');
73
+ const dismissModal = jasmine.createSpy('dismissModal');
74
+ const loadBalancer = { name: 'fnord-main' };
75
+ const configurePromise = Promise.resolve(loadBalancer);
76
+ const show = spyOn(AzureLoadBalancerModal, 'show').and.returnValue(configurePromise as any);
77
+ const modal = new AzureLoadBalancerChoiceModal({
78
+ application,
79
+ closeModal,
80
+ dismissModal,
81
+ forPipelineConfig: true,
82
+ } as any);
83
+ const selectedChoice = AzureLoadBalancerTypes[1];
84
+
85
+ modal.state = { ...modal.state, selectedChoice };
86
+ (modal as any).choose();
87
+
88
+ expect(dismissModal).not.toHaveBeenCalled();
89
+ expect(show).toHaveBeenCalledWith({
90
+ app: application,
91
+ application,
92
+ closeModal,
93
+ dismissModal,
94
+ forPipelineConfig: true,
95
+ loadBalancer: null,
96
+ isNew: true,
97
+ loadBalancerType: selectedChoice,
98
+ });
99
+ expect(closeModal).toHaveBeenCalledWith(configurePromise);
100
+ await expectAsync(closeModal.calls.mostRecent().args[0]).toBeResolvedTo(loadBalancer);
101
+ });
102
+
103
+ it('propagates configure modal dismissal through the choice modal promise', async () => {
104
+ const application = { name: 'fnord' } as any;
105
+ const closeModal = jasmine.createSpy('closeModal');
106
+ const configurePromise = Promise.reject('cancelled');
107
+ spyOn(AzureLoadBalancerModal, 'show').and.returnValue(configurePromise as any);
108
+ const modal = new AzureLoadBalancerChoiceModal({ application, closeModal } as any);
109
+
110
+ (modal as any).choose();
111
+
112
+ expect(closeModal).toHaveBeenCalledWith(configurePromise);
113
+ await expectAsync(closeModal.calls.mostRecent().args[0]).toBeRejectedWith('cancelled');
114
+ });
115
+
116
+ it('initializes the selected choice from an existing load balancer type', () => {
117
+ const application = { name: 'fnord' } as any;
118
+ const modal = new AzureLoadBalancerChoiceModal({
119
+ application,
120
+ loadBalancer: { loadBalancerType: 'Azure Application Gateway' },
121
+ } as any);
122
+
123
+ expect(modal.state.selectedChoice).toBe(AzureLoadBalancerTypes[1]);
124
+ });
125
+ });