@spinnaker/kubernetes 0.0.0-main-2

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 (286) hide show
  1. package/CHANGELOG.md +1663 -0
  2. package/LICENSE.txt +203 -0
  3. package/dist/help/kubernetes.help.d.ts +1 -0
  4. package/dist/index.d.ts +7 -0
  5. package/dist/index.js +6024 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/instance/details/details.controller.d.ts +1 -0
  8. package/dist/instance/index.d.ts +1 -0
  9. package/dist/interfaces/index.d.ts +1 -0
  10. package/dist/interfaces/infrastructure.types.d.ts +24 -0
  11. package/dist/kubernetes.module.d.ts +6 -0
  12. package/dist/loadBalancer/details/details.controller.d.ts +1 -0
  13. package/dist/loadBalancer/index.d.ts +1 -0
  14. package/dist/loadBalancer/transformer.d.ts +1 -0
  15. package/dist/manifest/IManifestCoordinates.d.ts +5 -0
  16. package/dist/manifest/ManifestImageDetails.d.ts +18 -0
  17. package/dist/manifest/ManifestKindSearch.d.ts +13 -0
  18. package/dist/manifest/ManifestLabels.d.ts +15 -0
  19. package/dist/manifest/ManifestQos.d.ts +15 -0
  20. package/dist/manifest/ManifestResources.d.ts +35 -0
  21. package/dist/manifest/ManifestSource.d.ts +4 -0
  22. package/dist/manifest/annotationCustomSections.component.d.ts +1 -0
  23. package/dist/manifest/artifact/artifact.component.d.ts +1 -0
  24. package/dist/manifest/delete/delete.controller.d.ts +12 -0
  25. package/dist/manifest/delete/deleteOptionsForm.component.d.ts +1 -0
  26. package/dist/manifest/editor/json/JsonEditor.d.ts +12 -0
  27. package/dist/manifest/editor/json/jsonEditor.component.d.ts +1 -0
  28. package/dist/manifest/index.d.ts +10 -0
  29. package/dist/manifest/manifest.service.d.ts +25 -0
  30. package/dist/manifest/manifestCommandBuilder.service.d.ts +30 -0
  31. package/dist/manifest/manifestEvents.component.d.ts +1 -0
  32. package/dist/manifest/manifestImageDetails.component.d.ts +1 -0
  33. package/dist/manifest/manifestLabels.component.d.ts +1 -0
  34. package/dist/manifest/manifestQos.component.d.ts +1 -0
  35. package/dist/manifest/manifestResources.component.d.ts +1 -0
  36. package/dist/manifest/rollout/RollingRestart.d.ts +1 -0
  37. package/dist/manifest/rollout/pause.controller.d.ts +1 -0
  38. package/dist/manifest/rollout/resume.controller.d.ts +1 -0
  39. package/dist/manifest/rollout/undo.controller.d.ts +1 -0
  40. package/dist/manifest/scale/ScaleSettingsForm.d.ts +10 -0
  41. package/dist/manifest/scale/scale.controller.d.ts +8 -0
  42. package/dist/manifest/scale/scaleSettingsForm.component.d.ts +1 -0
  43. package/dist/manifest/selector/IManifestLabelSelector.d.ts +9 -0
  44. package/dist/manifest/selector/IManifestSelector.d.ts +30 -0
  45. package/dist/manifest/selector/ManifestSelector.d.ts +46 -0
  46. package/dist/manifest/selector/labelEditor/LabelEditor.d.ts +15 -0
  47. package/dist/manifest/selector/selector.component.d.ts +1 -0
  48. package/dist/manifest/status/ManifestCondition.d.ts +13 -0
  49. package/dist/manifest/status/condition.component.d.ts +1 -0
  50. package/dist/manifest/status/status.component.d.ts +1 -0
  51. package/dist/manifest/traffic/ManifestTrafficService.d.ts +12 -0
  52. package/dist/manifest/wizard/BasicSettings.d.ts +17 -0
  53. package/dist/manifest/wizard/ManifestEntry.d.ts +16 -0
  54. package/dist/manifest/wizard/ManifestWizard.d.ts +22 -0
  55. package/dist/pipelines/stages/ManifestCoordinates.d.ts +31 -0
  56. package/dist/pipelines/stages/ManifestExecutionDetails.d.ts +69 -0
  57. package/dist/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.d.ts +18 -0
  58. package/dist/pipelines/stages/deleteManifest/DeleteManifestStageConfig.d.ts +14 -0
  59. package/dist/pipelines/stages/deleteManifest/deleteManifestStage.d.ts +1 -0
  60. package/dist/pipelines/stages/deployManifest/CopyFromTemplateButton.d.ts +15 -0
  61. package/dist/pipelines/stages/deployManifest/DeployManifestStageConfig.d.ts +15 -0
  62. package/dist/pipelines/stages/deployManifest/DeployManifestStageForm.d.ts +31 -0
  63. package/dist/pipelines/stages/deployManifest/ManifestBindArtifactsSelector.d.ts +17 -0
  64. package/dist/pipelines/stages/deployManifest/ManifestCopier.d.ts +36 -0
  65. package/dist/pipelines/stages/deployManifest/ManifestDeploymentOptions.d.ts +34 -0
  66. package/dist/pipelines/stages/deployManifest/NamespaceSelector.d.ts +16 -0
  67. package/dist/pipelines/stages/deployManifest/deployManifest.validator.d.ts +2 -0
  68. package/dist/pipelines/stages/deployManifest/deployManifestStage.d.ts +1 -0
  69. package/dist/pipelines/stages/deployManifest/manifestStatus/DeployStatus.d.ts +23 -0
  70. package/dist/pipelines/stages/deployManifest/manifestStatus/DeployStatusPills.d.ts +8 -0
  71. package/dist/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.d.ts +20 -0
  72. package/dist/pipelines/stages/deployManifest/manifestStatus/ManifestEvents.d.ts +9 -0
  73. package/dist/pipelines/stages/deployManifest/manifestStatus/ManifestStatus.d.ts +8 -0
  74. package/dist/pipelines/stages/findArtifactsFromResource/FindArtifactsFromResourceConfig.d.ts +3 -0
  75. package/dist/pipelines/stages/findArtifactsFromResource/FindArtifactsFromResourceStageForm.d.ts +7 -0
  76. package/dist/pipelines/stages/findArtifactsFromResource/findArtifactsFromResourceStage.d.ts +1 -0
  77. package/dist/pipelines/stages/index.d.ts +8 -0
  78. package/dist/pipelines/stages/patchManifest/PatchManifestOptionsForm.d.ts +8 -0
  79. package/dist/pipelines/stages/patchManifest/PatchManifestStageConfig.d.ts +7 -0
  80. package/dist/pipelines/stages/patchManifest/PatchManifestStageForm.d.ts +21 -0
  81. package/dist/pipelines/stages/patchManifest/patchManifestStage.d.ts +4 -0
  82. package/dist/pipelines/stages/rolloutRestartManifest/RolloutRestartManifestStageConfig.d.ts +13 -0
  83. package/dist/pipelines/stages/rolloutRestartManifest/rolloutRestartManifestStage.d.ts +4 -0
  84. package/dist/pipelines/stages/runJob/KubernetesV2RunJobStageConfig.d.ts +31 -0
  85. package/dist/pipelines/stages/runJob/RunJobExecutionDetails.d.ts +7 -0
  86. package/dist/pipelines/stages/runJob/runJobStage.d.ts +1 -0
  87. package/dist/pipelines/stages/scaleManifest/ScaleManifestConfig.d.ts +3 -0
  88. package/dist/pipelines/stages/scaleManifest/ScaleManifestStageForm.d.ts +7 -0
  89. package/dist/pipelines/stages/scaleManifest/scaleManifestStage.d.ts +1 -0
  90. package/dist/pipelines/stages/traffic/ManifestTrafficStageConfig.d.ts +11 -0
  91. package/dist/pipelines/stages/traffic/disableManifest.stage.d.ts +1 -0
  92. package/dist/pipelines/stages/traffic/enableManifest.stage.d.ts +1 -0
  93. package/dist/pipelines/stages/undoRolloutManifest/UndoRolloutManifestConfig.d.ts +3 -0
  94. package/dist/pipelines/stages/undoRolloutManifest/UndoRolloutManifestStageForm.d.ts +7 -0
  95. package/dist/pipelines/stages/undoRolloutManifest/undoRolloutManifestStage.d.ts +1 -0
  96. package/dist/pipelines/stages/validators/manifestSelectorValidators.d.ts +2 -0
  97. package/dist/pipelines/validation/manifestSelector.validator.d.ts +8 -0
  98. package/dist/rawResource/component/K8sResources.d.ts +25 -0
  99. package/dist/rawResource/component/K8sResourcesFilters.d.ts +20 -0
  100. package/dist/rawResource/component/RawResourceUtils.d.ts +5 -0
  101. package/dist/rawResource/component/group/RawResouceGroup.d.ts +15 -0
  102. package/dist/rawResource/component/group/RawResource.d.ts +12 -0
  103. package/dist/rawResource/component/group/RawResourceDetails.d.ts +32 -0
  104. package/dist/rawResource/component/group/RawResourceGroups.d.ts +14 -0
  105. package/dist/rawResource/controller/FiltersPubSub.d.ts +12 -0
  106. package/dist/rawResource/index.d.ts +1 -0
  107. package/dist/rawResource/model/resource.d.ts +17 -0
  108. package/dist/rawResource/rawResource.dataSource.d.ts +2 -0
  109. package/dist/rawResource/rawResource.module.d.ts +1 -0
  110. package/dist/rawResource/rawResource.states.d.ts +6 -0
  111. package/dist/resources/ResourceDetails.d.ts +17 -0
  112. package/dist/resources/resources.state.d.ts +7 -0
  113. package/dist/rolloutStrategy/bluegreen.strategy.d.ts +2 -0
  114. package/dist/rolloutStrategy/highlander.strategy.d.ts +2 -0
  115. package/dist/rolloutStrategy/index.d.ts +1 -0
  116. package/dist/rolloutStrategy/none.strategy.d.ts +2 -0
  117. package/dist/rolloutStrategy/redblack.strategy.d.ts +2 -0
  118. package/dist/securityGroup/details/details.controller.d.ts +1 -0
  119. package/dist/securityGroup/index.d.ts +1 -0
  120. package/dist/securityGroup/securityGroup.reader.d.ts +4 -0
  121. package/dist/securityGroup/transformer.d.ts +1 -0
  122. package/dist/serverGroup/details/details.controller.d.ts +1 -0
  123. package/dist/serverGroup/details/resize/resize.controller.d.ts +1 -0
  124. package/dist/serverGroup/index.d.ts +4 -0
  125. package/dist/serverGroup/serverGroupCommandBuilder.service.d.ts +6 -0
  126. package/dist/serverGroup/serverGroupTransformer.service.d.ts +6 -0
  127. package/dist/serverGroupManager/details/details.controller.d.ts +1 -0
  128. package/dist/serverGroupManager/index.d.ts +1 -0
  129. package/dist/validation/applicationName.validator.d.ts +9 -0
  130. package/package.json +58 -0
  131. package/src/help/kubernetes.help.ts +236 -0
  132. package/src/index.ts +7 -0
  133. package/src/instance/details/details.controller.ts +186 -0
  134. package/src/instance/details/details.html +80 -0
  135. package/src/instance/index.ts +1 -0
  136. package/src/interfaces/index.ts +1 -0
  137. package/src/interfaces/infrastructure.types.ts +35 -0
  138. package/src/kubernetes.module.ts +144 -0
  139. package/src/loadBalancer/details/details.controller.ts +101 -0
  140. package/src/loadBalancer/details/details.html +187 -0
  141. package/src/loadBalancer/index.ts +1 -0
  142. package/src/loadBalancer/transformer.ts +60 -0
  143. package/src/logo/kubernetes.icon.svg +102 -0
  144. package/src/logo/kubernetes.logo.less +6 -0
  145. package/src/logo/kubernetes.logo.svg +1 -0
  146. package/src/manifest/IManifestCoordinates.ts +5 -0
  147. package/src/manifest/ManifestImageDetails.spec.tsx +76 -0
  148. package/src/manifest/ManifestImageDetails.tsx +64 -0
  149. package/src/manifest/ManifestKindSearch.tsx +31 -0
  150. package/src/manifest/ManifestLabels.tsx +31 -0
  151. package/src/manifest/ManifestQos.tsx +68 -0
  152. package/src/manifest/ManifestResources.tsx +69 -0
  153. package/src/manifest/ManifestSource.ts +4 -0
  154. package/src/manifest/annotationCustomSections.component.ts +114 -0
  155. package/src/manifest/artifact/artifact.component.ts +23 -0
  156. package/src/manifest/delete/delete.controller.ts +80 -0
  157. package/src/manifest/delete/delete.html +42 -0
  158. package/src/manifest/delete/deleteOptionsForm.component.ts +52 -0
  159. package/src/manifest/editor/json/JsonEditor.tsx +56 -0
  160. package/src/manifest/editor/json/jsonEditor.component.ts +12 -0
  161. package/src/manifest/index.ts +10 -0
  162. package/src/manifest/manifest.service.ts +90 -0
  163. package/src/manifest/manifestCommandBuilder.service.ts +126 -0
  164. package/src/manifest/manifestEvents.component.ts +12 -0
  165. package/src/manifest/manifestImageDetails.component.ts +12 -0
  166. package/src/manifest/manifestLabels.component.ts +12 -0
  167. package/src/manifest/manifestLabels.less +3 -0
  168. package/src/manifest/manifestQos.component.ts +12 -0
  169. package/src/manifest/manifestResources.component.ts +12 -0
  170. package/src/manifest/rollout/RollingRestart.tsx +54 -0
  171. package/src/manifest/rollout/pause.controller.ts +66 -0
  172. package/src/manifest/rollout/pause.html +28 -0
  173. package/src/manifest/rollout/resume.controller.ts +66 -0
  174. package/src/manifest/rollout/resume.html +28 -0
  175. package/src/manifest/rollout/undo.controller.ts +74 -0
  176. package/src/manifest/rollout/undo.html +44 -0
  177. package/src/manifest/scale/ScaleSettingsForm.tsx +43 -0
  178. package/src/manifest/scale/scale.controller.ts +75 -0
  179. package/src/manifest/scale/scale.html +32 -0
  180. package/src/manifest/scale/scaleSettingsForm.component.ts +11 -0
  181. package/src/manifest/selector/IManifestLabelSelector.ts +19 -0
  182. package/src/manifest/selector/IManifestSelector.ts +64 -0
  183. package/src/manifest/selector/ManifestSelector.spec.tsx +414 -0
  184. package/src/manifest/selector/ManifestSelector.tsx +424 -0
  185. package/src/manifest/selector/labelEditor/LabelEditor.spec.tsx +81 -0
  186. package/src/manifest/selector/labelEditor/LabelEditor.tsx +126 -0
  187. package/src/manifest/selector/labelEditor/labelEditor.less +3 -0
  188. package/src/manifest/selector/selector.component.ts +17 -0
  189. package/src/manifest/status/ManifestCondition.tsx +32 -0
  190. package/src/manifest/status/condition.component.ts +12 -0
  191. package/src/manifest/status/status.component.ts +36 -0
  192. package/src/manifest/traffic/ManifestTrafficService.spec.ts +30 -0
  193. package/src/manifest/traffic/ManifestTrafficService.ts +67 -0
  194. package/src/manifest/wizard/BasicSettings.tsx +57 -0
  195. package/src/manifest/wizard/ManifestEntry.tsx +46 -0
  196. package/src/manifest/wizard/ManifestWizard.tsx +94 -0
  197. package/src/pipelines/stages/ManifestCoordinates.spec.tsx +90 -0
  198. package/src/pipelines/stages/ManifestCoordinates.tsx +128 -0
  199. package/src/pipelines/stages/ManifestExecutionDetails.tsx +58 -0
  200. package/src/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.spec.tsx +85 -0
  201. package/src/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.tsx +87 -0
  202. package/src/pipelines/stages/deleteManifest/DeleteManifestStageConfig.tsx +62 -0
  203. package/src/pipelines/stages/deleteManifest/deleteManifestOptionsForm.less +9 -0
  204. package/src/pipelines/stages/deleteManifest/deleteManifestStage.ts +21 -0
  205. package/src/pipelines/stages/deployManifest/CopyFromTemplateButton.tsx +45 -0
  206. package/src/pipelines/stages/deployManifest/DeployManifestStageConfig.tsx +77 -0
  207. package/src/pipelines/stages/deployManifest/DeployManifestStageForm.tsx +265 -0
  208. package/src/pipelines/stages/deployManifest/ManifestBindArtifactsSelector.tsx +69 -0
  209. package/src/pipelines/stages/deployManifest/ManifestCopier.spec.ts +110 -0
  210. package/src/pipelines/stages/deployManifest/ManifestCopier.tsx +222 -0
  211. package/src/pipelines/stages/deployManifest/ManifestDeploymentOptions.spec.tsx +75 -0
  212. package/src/pipelines/stages/deployManifest/ManifestDeploymentOptions.tsx +203 -0
  213. package/src/pipelines/stages/deployManifest/NamespaceSelector.tsx +40 -0
  214. package/src/pipelines/stages/deployManifest/deployManifest.validator.ts +38 -0
  215. package/src/pipelines/stages/deployManifest/deployManifestStage.ts +28 -0
  216. package/src/pipelines/stages/deployManifest/manifestStatus/DeployStatus.less +11 -0
  217. package/src/pipelines/stages/deployManifest/manifestStatus/DeployStatus.tsx +120 -0
  218. package/src/pipelines/stages/deployManifest/manifestStatus/DeployStatusPills.tsx +47 -0
  219. package/src/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.tsx +92 -0
  220. package/src/pipelines/stages/deployManifest/manifestStatus/ManifestEvents.tsx +87 -0
  221. package/src/pipelines/stages/deployManifest/manifestStatus/ManifestStatus.less +24 -0
  222. package/src/pipelines/stages/deployManifest/manifestStatus/ManifestStatus.tsx +46 -0
  223. package/src/pipelines/stages/findArtifactsFromResource/FindArtifactsFromResourceConfig.tsx +35 -0
  224. package/src/pipelines/stages/findArtifactsFromResource/FindArtifactsFromResourceStageForm.tsx +37 -0
  225. package/src/pipelines/stages/findArtifactsFromResource/findArtifactsFromResourceStage.ts +21 -0
  226. package/src/pipelines/stages/index.ts +8 -0
  227. package/src/pipelines/stages/patchManifest/PatchManifestOptionsForm.tsx +35 -0
  228. package/src/pipelines/stages/patchManifest/PatchManifestStageConfig.tsx +57 -0
  229. package/src/pipelines/stages/patchManifest/PatchManifestStageForm.tsx +157 -0
  230. package/src/pipelines/stages/patchManifest/patchManifestStage.ts +30 -0
  231. package/src/pipelines/stages/rolloutRestartManifest/RolloutRestartManifestStageConfig.tsx +46 -0
  232. package/src/pipelines/stages/rolloutRestartManifest/rolloutRestartManifestStage.ts +20 -0
  233. package/src/pipelines/stages/runJob/KubernetesV2RunJobStageConfig.tsx +242 -0
  234. package/src/pipelines/stages/runJob/RunJobExecutionDetails.tsx +67 -0
  235. package/src/pipelines/stages/runJob/runJobStage.ts +39 -0
  236. package/src/pipelines/stages/scaleManifest/ScaleManifestConfig.tsx +38 -0
  237. package/src/pipelines/stages/scaleManifest/ScaleManifestStageForm.tsx +46 -0
  238. package/src/pipelines/stages/scaleManifest/scaleManifestStage.ts +24 -0
  239. package/src/pipelines/stages/traffic/ManifestTrafficStageConfig.tsx +44 -0
  240. package/src/pipelines/stages/traffic/disableManifest.stage.ts +26 -0
  241. package/src/pipelines/stages/traffic/enableManifest.stage.ts +26 -0
  242. package/src/pipelines/stages/undoRolloutManifest/UndoRolloutManifestConfig.tsx +37 -0
  243. package/src/pipelines/stages/undoRolloutManifest/UndoRolloutManifestStageForm.tsx +60 -0
  244. package/src/pipelines/stages/undoRolloutManifest/undoRolloutManifestStage.ts +21 -0
  245. package/src/pipelines/stages/validators/manifestSelectorValidators.ts +43 -0
  246. package/src/pipelines/validation/manifestSelector.validator.ts +36 -0
  247. package/src/rawResource/component/K8sResources.less +9 -0
  248. package/src/rawResource/component/K8sResources.tsx +112 -0
  249. package/src/rawResource/component/K8sResourcesFilters.tsx +112 -0
  250. package/src/rawResource/component/RawResourceUtils.ts +19 -0
  251. package/src/rawResource/component/group/RawResouceGroup.tsx +41 -0
  252. package/src/rawResource/component/group/RawResource.less +79 -0
  253. package/src/rawResource/component/group/RawResource.tsx +51 -0
  254. package/src/rawResource/component/group/RawResourceDetails.tsx +236 -0
  255. package/src/rawResource/component/group/RawResourceGroups.tsx +53 -0
  256. package/src/rawResource/controller/FiltersPubSub.ts +35 -0
  257. package/src/rawResource/index.ts +1 -0
  258. package/src/rawResource/model/resource.ts +18 -0
  259. package/src/rawResource/rawResource.dataSource.ts +39 -0
  260. package/src/rawResource/rawResource.module.ts +8 -0
  261. package/src/rawResource/rawResource.states.ts +64 -0
  262. package/src/resources/ResourceDetails.tsx +121 -0
  263. package/src/resources/resources.state.ts +45 -0
  264. package/src/rolloutStrategy/bluegreen.strategy.ts +7 -0
  265. package/src/rolloutStrategy/highlander.strategy.ts +7 -0
  266. package/src/rolloutStrategy/index.ts +6 -0
  267. package/src/rolloutStrategy/none.strategy.ts +8 -0
  268. package/src/rolloutStrategy/redblack.strategy.ts +7 -0
  269. package/src/securityGroup/details/details.controller.ts +118 -0
  270. package/src/securityGroup/details/details.html +91 -0
  271. package/src/securityGroup/index.ts +1 -0
  272. package/src/securityGroup/securityGroup.reader.ts +11 -0
  273. package/src/securityGroup/transformer.ts +19 -0
  274. package/src/serverGroup/details/details.controller.ts +207 -0
  275. package/src/serverGroup/details/details.html +153 -0
  276. package/src/serverGroup/details/resize/resize.controller.ts +73 -0
  277. package/src/serverGroup/details/resize/resize.html +57 -0
  278. package/src/serverGroup/index.ts +4 -0
  279. package/src/serverGroup/serverGroupCommandBuilder.service.ts +18 -0
  280. package/src/serverGroup/serverGroupTransformer.service.spec.ts +67 -0
  281. package/src/serverGroup/serverGroupTransformer.service.ts +34 -0
  282. package/src/serverGroupManager/details/details.controller.ts +191 -0
  283. package/src/serverGroupManager/details/details.html +136 -0
  284. package/src/serverGroupManager/index.ts +1 -0
  285. package/src/validation/applicationName.validator.spec.ts +19 -0
  286. package/src/validation/applicationName.validator.ts +44 -0
@@ -0,0 +1,69 @@
1
+ import React from 'react';
2
+
3
+ import type { IArtifact, IExpectedArtifact, IPipeline, IStage } from '@spinnaker/core';
4
+ import { ArtifactTypePatterns, StageArtifactSelector } from '@spinnaker/core';
5
+
6
+ export interface IManifestBindArtifact {
7
+ expectedArtifactId?: string;
8
+ artifact?: IArtifact;
9
+ }
10
+
11
+ export interface IManifestBindArtifactsSelectorProps {
12
+ pipeline: IPipeline;
13
+ stage: IStage;
14
+ bindings?: IManifestBindArtifact[];
15
+ onChangeBindings: (_: IManifestBindArtifact[]) => void;
16
+ }
17
+
18
+ export class ManifestBindArtifactsSelector extends React.Component<IManifestBindArtifactsSelectorProps> {
19
+ private onChangeBinding = (index: number, binding: IManifestBindArtifact) => {
20
+ const bindings = (this.props.bindings || []).slice(0);
21
+ bindings[index] = binding;
22
+ this.props.onChangeBindings(bindings);
23
+ };
24
+
25
+ private onRemoveBinding = (index: number) => {
26
+ const bindings = (this.props.bindings || []).slice(0);
27
+ bindings.splice(index, 1);
28
+ this.props.onChangeBindings(bindings);
29
+ };
30
+
31
+ public render() {
32
+ const { stage, pipeline, bindings } = this.props;
33
+
34
+ const renderSelect = (i: number, binding?: IManifestBindArtifact) => {
35
+ const key = (!binding && 'new') || binding.expectedArtifactId || (binding.artifact && binding.artifact.id);
36
+ return (
37
+ <div className="row" key={key}>
38
+ <div className="col-md-10">
39
+ <StageArtifactSelector
40
+ pipeline={pipeline}
41
+ stage={stage}
42
+ expectedArtifactId={binding && binding.expectedArtifactId}
43
+ artifact={!!binding && binding.artifact}
44
+ onArtifactEdited={(artifact: IArtifact) => this.onChangeBinding(i, { artifact: artifact })}
45
+ onExpectedArtifactSelected={(expectedArtifact: IExpectedArtifact) =>
46
+ this.onChangeBinding(i, { expectedArtifactId: expectedArtifact.id })
47
+ }
48
+ excludedArtifactIds={bindings.map((b) => b.expectedArtifactId)}
49
+ excludedArtifactTypePatterns={[ArtifactTypePatterns.FRONT50_PIPELINE_TEMPLATE]}
50
+ />
51
+ </div>
52
+ {binding && (
53
+ <div className="col-md-2">
54
+ <a className="glyphicon glyphicon-trash" onClick={() => this.onRemoveBinding(i)} />
55
+ </div>
56
+ )}
57
+ </div>
58
+ );
59
+ };
60
+ const renderSelectEditable = (binding: IManifestBindArtifact, i: number) => renderSelect(i, binding);
61
+
62
+ return (
63
+ <>
64
+ {bindings.map(renderSelectEditable)}
65
+ {renderSelect(bindings.length)}
66
+ </>
67
+ );
68
+ }
69
+ }
@@ -0,0 +1,110 @@
1
+ import type { IQService, IScope } from 'angular';
2
+ import { mock } from 'angular';
3
+
4
+ import type { Application } from '@spinnaker/core';
5
+ import { ApplicationModelBuilder, noop } from '@spinnaker/core';
6
+ import { ManifestCopier } from './ManifestCopier';
7
+
8
+ describe('<ManifestCopier />', () => {
9
+ let application: Application;
10
+
11
+ beforeEach(
12
+ mock.inject(($q: IQService, $rootScope: IScope) => {
13
+ const $scope = $rootScope.$new();
14
+ // The application model implicitly depends on a bunch of Angular things, which is why
15
+ // we need the Angular mock environment (even though we're testing a React component).
16
+ application = ApplicationModelBuilder.createApplicationForTests(
17
+ 'app',
18
+ {
19
+ key: 'serverGroups',
20
+ loader: () =>
21
+ $q.resolve([
22
+ // Replica sets in same cluster, no manager.
23
+ {
24
+ name: 'replicaSet my-replicaSet-v002',
25
+ region: 'default',
26
+ category: 'serverGroup',
27
+ account: 'my-k8s-account',
28
+ cloudProvider: 'kubernetes',
29
+ cluster: 'replicaSet my-replicaSet',
30
+ },
31
+ {
32
+ name: 'replicaSet my-replicaSet-v001',
33
+ region: 'default',
34
+ category: 'serverGroup',
35
+ account: 'my-k8s-account',
36
+ cloudProvider: 'kubernetes',
37
+ cluster: 'replicaSet my-replicaSet',
38
+ },
39
+ // Replica set managed by deployment.
40
+ {
41
+ name: 'replicaSet my-managed-replicaSet-v001',
42
+ region: 'default',
43
+ category: 'serverGroup',
44
+ account: 'my-k8s-account',
45
+ cloudProvider: 'kubernetes',
46
+ cluster: 'deployment my-deployment',
47
+ serverGroupManagers: [{ name: 'deployment my-deployment' }],
48
+ },
49
+ ]),
50
+ onLoad: (_app: Application, data: any) => $q.resolve(data),
51
+ defaultData: [],
52
+ },
53
+ {
54
+ key: 'serverGroupManagers',
55
+ loader: () =>
56
+ $q.resolve([
57
+ {
58
+ name: 'deployment my-deployment',
59
+ region: 'default',
60
+ account: 'my-k8s-account',
61
+ cloudProvider: 'kubernetes',
62
+ },
63
+ ]),
64
+ onLoad: (_app: Application, data: any) => $q.resolve(data),
65
+ defaultData: [],
66
+ },
67
+ {
68
+ key: 'securityGroups',
69
+ loader: () => $q.resolve([]),
70
+ onLoad: (_app: Application, data: any) => $q.resolve(data),
71
+ defaultData: [],
72
+ },
73
+ {
74
+ key: 'loadBalancers',
75
+ loader: () => $q.resolve([]),
76
+ onLoad: (_app: Application, data: any) => $q.resolve(data),
77
+ defaultData: [],
78
+ },
79
+ );
80
+ application.refresh();
81
+ $scope.$digest();
82
+ }),
83
+ );
84
+
85
+ describe('dropdown grouping & ordering', () => {
86
+ it('sorts deployments to the top of the list', () => {
87
+ const state = ManifestCopier.getState(buildProps(application));
88
+ expect(state.manifests.map((manifest) => manifest.name)[0]).toEqual('my-deployment');
89
+ });
90
+
91
+ it('only includes the most recent versioned resource', () => {
92
+ const state = ManifestCopier.getState(buildProps(application));
93
+ expect(state.manifests.map((manifest) => manifest.name)).toContain('my-replicaSet-v002');
94
+ expect(state.manifests.map((manifest) => manifest.name)).not.toContain('my-replicaSet-v001');
95
+ });
96
+
97
+ it('does not include managed server groups', () => {
98
+ const state = ManifestCopier.getState(buildProps(application));
99
+ expect(state.manifests.map((manifest) => manifest.name)).not.toContain('my-managed-replicaSet-v001');
100
+ });
101
+ });
102
+ });
103
+
104
+ const buildProps = (application: Application) => ({
105
+ application,
106
+ cloudProvider: 'kubernetes',
107
+ onDismiss: noop,
108
+ onManifestSelected: noop,
109
+ show: true,
110
+ });
@@ -0,0 +1,222 @@
1
+ import { groupBy, sortBy } from 'lodash';
2
+ import React from 'react';
3
+ import { Modal } from 'react-bootstrap';
4
+ import type { Option } from 'react-select';
5
+ import { from as observableFrom, Subject } from 'rxjs';
6
+ import { takeUntil } from 'rxjs/operators';
7
+
8
+ import type {
9
+ Application,
10
+ ILoadBalancer,
11
+ IManifest,
12
+ ISecurityGroup,
13
+ IServerGroup,
14
+ IServerGroupManager,
15
+ } from '@spinnaker/core';
16
+ import { AccountTag, ManifestReader, ModalClose, noop, robotToHuman, TetheredSelect } from '@spinnaker/core';
17
+
18
+ export interface IManifestCopierProps {
19
+ application: Application;
20
+ cloudProvider: string;
21
+ show: boolean;
22
+ onDismiss: () => void;
23
+ onManifestSelected: (manifest: IManifest) => void;
24
+ }
25
+
26
+ export interface IManifestCopierState {
27
+ selectedManifest: IManifestOption;
28
+ manifests: IManifestOption[];
29
+ }
30
+
31
+ export interface IManifestOption {
32
+ account: string;
33
+ location: string;
34
+ name: string;
35
+ kind: string;
36
+ data: any;
37
+ key: string;
38
+ }
39
+
40
+ const LAST_APPLIED_CONFIGURATION = 'kubectl.kubernetes.io/last-applied-configuration';
41
+
42
+ /*
43
+ * A modal that allows a user to copy a running Kubernetes resource.
44
+ **/
45
+ export class ManifestCopier extends React.Component<IManifestCopierProps, IManifestCopierState> {
46
+ private destroy$ = new Subject();
47
+
48
+ public static getState = (props: IManifestCopierProps) => {
49
+ let manifests: IManifestOption[] = [];
50
+
51
+ const serverGroups: IServerGroup[] = props.application.getDataSource('serverGroups').data.filter(
52
+ (s: IServerGroup) =>
53
+ s.cloudProvider === props.cloudProvider &&
54
+ s.category === 'serverGroup' &&
55
+ // Don't include managed server groups - just the manager (see below).
56
+ !(s.serverGroupManagers || []).length,
57
+ );
58
+
59
+ const grouped = groupBy(serverGroups, (serverGroup) =>
60
+ [serverGroup.cluster, serverGroup.account, serverGroup.region].join(':'),
61
+ );
62
+
63
+ Object.keys(grouped).forEach((key) => {
64
+ // Only include the most recent server group in a cluster (e.g., if v001 and v002 exist, only include v002).
65
+ const latest = sortBy(grouped[key], 'name').pop();
66
+ const [kind, name] = latest.name.split(' ');
67
+ manifests.push({
68
+ account: latest.account,
69
+ location: latest.region,
70
+ name: name,
71
+ kind: kind,
72
+ data: latest,
73
+ key: [latest.account, latest.region, latest.name].join(':'),
74
+ });
75
+ });
76
+
77
+ const serverGroupManagers: IServerGroupManager[] = props.application.getDataSource('serverGroupManagers').data;
78
+ const loadBalancers: ILoadBalancer[] = props.application.getDataSource('loadBalancers').data;
79
+ const firewalls: ISecurityGroup[] = props.application.getDataSource('securityGroups').data;
80
+
81
+ [...serverGroupManagers, ...loadBalancers, ...firewalls]
82
+ .filter(
83
+ (manifest: IServerGroupManager | ILoadBalancer | ISecurityGroup) =>
84
+ manifest.cloudProvider === props.cloudProvider,
85
+ )
86
+ .forEach((manifest: IServerGroupManager | ILoadBalancer | ISecurityGroup) => {
87
+ const [kind, name] = manifest.name.split(' ');
88
+ manifests.push({
89
+ account: manifest.account,
90
+ location: manifest.region,
91
+ name,
92
+ kind,
93
+ data: manifest,
94
+ key: [manifest.account, manifest.region, manifest.name].join(':'),
95
+ });
96
+ });
97
+
98
+ manifests = sortBy(
99
+ manifests,
100
+ (manifest) => {
101
+ // TODO(dpeach): Could load an account here, then use the spinnakerKind -> kubernetesKind map to
102
+ // construct a more maintainable ordering.
103
+ const order = [
104
+ // server group manager
105
+ 'deployment',
106
+ // server group
107
+ 'replicaSet',
108
+ 'statefulSet',
109
+ 'daemonSet',
110
+ 'jobs',
111
+ 'cronJob',
112
+ // load balancer
113
+ 'service',
114
+ 'ingress',
115
+ // firewall
116
+ 'networkPolicy',
117
+ ];
118
+ return order.includes(manifest.kind) ? order.indexOf(manifest.kind) : Number.MAX_SAFE_INTEGER;
119
+ },
120
+ ['kind', 'name', 'location'],
121
+ );
122
+
123
+ return {
124
+ selectedManifest: manifests.length ? manifests[0] : null,
125
+ manifests,
126
+ };
127
+ };
128
+
129
+ constructor(props: IManifestCopierProps) {
130
+ super(props);
131
+ this.state = ManifestCopier.getState(props);
132
+ }
133
+
134
+ public useManifest = (): void => {
135
+ const {
136
+ data: { account, region, name },
137
+ } = this.state.selectedManifest;
138
+ observableFrom(ManifestReader.getManifest(account, region, name))
139
+ .pipe(takeUntil(this.destroy$))
140
+ .subscribe((manifest: IManifest) => {
141
+ this.props.onManifestSelected(JSON.parse(manifest.manifest.metadata.annotations[LAST_APPLIED_CONFIGURATION]));
142
+ });
143
+ };
144
+
145
+ public manifestChanged = (option: Option) => {
146
+ this.setState({ selectedManifest: option as IManifestOption });
147
+ };
148
+
149
+ public componentWillUnmount() {
150
+ this.destroy$.next();
151
+ }
152
+
153
+ public render() {
154
+ const { selectedManifest, manifests } = this.state;
155
+
156
+ return (
157
+ <Modal show={this.props.show} onHide={noop}>
158
+ <ModalClose dismiss={this.props.onDismiss} />
159
+ <div>
160
+ <Modal.Header>
161
+ <Modal.Title>Copy Manifest</Modal.Title>
162
+ </Modal.Header>
163
+ <Modal.Body>
164
+ <form className="form-horizontal">
165
+ <div className="form-group">
166
+ <div className="col-md-4 col-md-offset-1 sm-label-left">
167
+ <b>Copy manifest from</b>
168
+ </div>
169
+ </div>
170
+ <div className="form-group">
171
+ <div className="col-md-10 col-md-offset-1">
172
+ <TetheredSelect
173
+ value={selectedManifest ? selectedManifest.key : null}
174
+ placeholder="Select..."
175
+ valueRenderer={this.manifestValueRenderer}
176
+ optionRenderer={this.manifestOptionRenderer}
177
+ options={manifests}
178
+ valueKey="key"
179
+ onChange={this.manifestChanged}
180
+ clearable={false}
181
+ />
182
+ </div>
183
+ </div>
184
+ </form>
185
+ </Modal.Body>
186
+ <div className="modal-footer">
187
+ {selectedManifest && (
188
+ <button className="btn btn-primary" onClick={this.useManifest}>
189
+ <span>Use this manifest</span>
190
+ <span className="glyphicon glyphicon-chevron-right" />
191
+ </button>
192
+ )}
193
+ {!selectedManifest && (
194
+ <button className="btn" onClick={this.props.onDismiss}>
195
+ <span>Cancel</span>
196
+ </button>
197
+ )}
198
+ </div>
199
+ </div>
200
+ </Modal>
201
+ );
202
+ }
203
+
204
+ private manifestValueRenderer = (option: Option) => {
205
+ const kindLabel = robotToHuman(option.kind).trim();
206
+ return (
207
+ <span>
208
+ <AccountTag account={option.account} />
209
+ {option.data && <span> {option.name}</span>} ({kindLabel} in {option.location})
210
+ </span>
211
+ );
212
+ };
213
+
214
+ private manifestOptionRenderer = (option: Option) => {
215
+ const kindLabel = robotToHuman(option.kind).trim();
216
+ return (
217
+ <h5>
218
+ <AccountTag account={option.account} /> {option.name} ({kindLabel} in {option.location})
219
+ </h5>
220
+ );
221
+ };
222
+ }
@@ -0,0 +1,75 @@
1
+ import { shallow } from 'enzyme';
2
+ import React from 'react';
3
+
4
+ import { StageConfigField } from '@spinnaker/core';
5
+
6
+ import type { IManifestDeploymentOptionsProps } from './ManifestDeploymentOptions';
7
+ import { defaultTrafficManagementConfig, ManifestDeploymentOptions } from './ManifestDeploymentOptions';
8
+
9
+ describe('<ManifestDeploymentOptions />', () => {
10
+ const onConfigChangeSpy = jasmine.createSpy('onConfigChangeSpy');
11
+ let wrapper: any;
12
+ let props: IManifestDeploymentOptionsProps;
13
+
14
+ beforeEach(() => {
15
+ props = {
16
+ accounts: [],
17
+ config: defaultTrafficManagementConfig,
18
+ onConfigChange: onConfigChangeSpy,
19
+ selectedAccount: null,
20
+ };
21
+ wrapper = shallow(<ManifestDeploymentOptions {...props} />);
22
+ });
23
+
24
+ describe('view', () => {
25
+ it('renders only the enable checkbox when config is disabled', () => {
26
+ expect(wrapper.find(StageConfigField).length).toEqual(1);
27
+ expect(wrapper.find('input[type="checkbox"]').length).toEqual(1);
28
+ });
29
+ it('renders config fields for `namespace`, `services`, `enableTraffic`, and `strategy` when config is enabled', () => {
30
+ props.config.enabled = true;
31
+ wrapper = shallow(<ManifestDeploymentOptions {...props} />);
32
+ expect(wrapper.find(StageConfigField).length).toEqual(5);
33
+ });
34
+ });
35
+
36
+ describe('functionality', () => {
37
+ it('updates `config.enabled` when enable checkbox is toggled', () => {
38
+ wrapper
39
+ .find('input[type="checkbox"]')
40
+ .at(0)
41
+ .simulate('change', { target: { checked: true } });
42
+ expect(onConfigChangeSpy).toHaveBeenCalledWith({
43
+ ...defaultTrafficManagementConfig,
44
+ enabled: true,
45
+ });
46
+ });
47
+ it('disables the traffic checkbox when a non-None rollout strategy is selected', () => {
48
+ props.config.options.strategy = 'redblack';
49
+ wrapper = shallow(<ManifestDeploymentOptions {...props} />);
50
+ expect(wrapper.find('input[type="checkbox"]').at(1).props().disabled).toEqual(true);
51
+ });
52
+ it('disables the traffic checkbox when blue/green rollout strategy is selected', () => {
53
+ props.config.options.strategy = 'bluegreen';
54
+ wrapper = shallow(<ManifestDeploymentOptions {...props} />);
55
+ expect(wrapper.find('input[type="checkbox"]').at(1).props().disabled).toEqual(true);
56
+ });
57
+
58
+ it('strategy bluegreen should not display warning label', () => {
59
+ props.config.options.strategy = 'bluegreen';
60
+ wrapper = shallow(<ManifestDeploymentOptions {...props} />);
61
+ expect(wrapper.find('p[id="redBlackWarning"]').exists()).toEqual(false);
62
+ });
63
+ it('strategy highlander should not display warning label', () => {
64
+ props.config.options.strategy = 'highlander';
65
+ wrapper = shallow(<ManifestDeploymentOptions {...props} />);
66
+ expect(wrapper.find('p[id="redBlackWarning"]').exists()).toEqual(false);
67
+ });
68
+
69
+ it('strategy redblack should display warning label', () => {
70
+ props.config.options.strategy = 'redblack';
71
+ wrapper = shallow(<ManifestDeploymentOptions {...props} />);
72
+ expect(wrapper.find('p[id="redBlackWarning"]').exists()).toEqual(true);
73
+ });
74
+ });
75
+ });
@@ -0,0 +1,203 @@
1
+ import { cloneDeep, map, set, split } from 'lodash';
2
+ import React from 'react';
3
+ import type { Option } from 'react-select';
4
+ import Select, { Creatable } from 'react-select';
5
+
6
+ import type { IAccountDetails, IDeploymentStrategy } from '@spinnaker/core';
7
+ import { Markdown, StageConfigField } from '@spinnaker/core';
8
+
9
+ import { NamespaceSelector } from './NamespaceSelector';
10
+ import { ManifestKindSearchService } from '../../../manifest/ManifestKindSearch';
11
+ import { rolloutStrategies } from '../../../rolloutStrategy';
12
+
13
+ export interface ITrafficManagementConfig {
14
+ enabled: boolean;
15
+ options: ITrafficManagementOptions;
16
+ }
17
+
18
+ export interface ITrafficManagementOptions {
19
+ namespace: string;
20
+ services: string[];
21
+ enableTraffic: boolean;
22
+ strategy: string;
23
+ }
24
+
25
+ export const defaultTrafficManagementConfig: ITrafficManagementConfig = {
26
+ enabled: false,
27
+ options: {
28
+ namespace: null,
29
+ services: [],
30
+ enableTraffic: false,
31
+ strategy: null,
32
+ },
33
+ };
34
+
35
+ export interface IManifestDeploymentOptionsProps {
36
+ accounts: IAccountDetails[];
37
+ config: ITrafficManagementConfig;
38
+ onConfigChange: (config: ITrafficManagementConfig) => void;
39
+ selectedAccount: string;
40
+ }
41
+
42
+ export interface IManifestDeploymentOptionsState {
43
+ services: string[];
44
+ showRedBlackWarningMessage: boolean;
45
+ }
46
+
47
+ export class ManifestDeploymentOptions extends React.Component<
48
+ IManifestDeploymentOptionsProps,
49
+ IManifestDeploymentOptionsState
50
+ > {
51
+ public state: IManifestDeploymentOptionsState = { services: [], showRedBlackWarningMessage: false };
52
+
53
+ private onConfigChange = (key: string, value: any): void => {
54
+ this.setState({ showRedBlackWarningMessage: false });
55
+ if (value === 'redblack') {
56
+ value = 'bluegreen';
57
+ this.setState({ showRedBlackWarningMessage: true });
58
+ }
59
+ this.updateProps(key, value);
60
+ };
61
+
62
+ private updateProps(key: string, value: any) {
63
+ const updatedConfig = cloneDeep(this.props.config);
64
+ set(updatedConfig, key, value);
65
+ this.props.onConfigChange(updatedConfig);
66
+ }
67
+
68
+ private fetchServices = (): void => {
69
+ const namespace = this.props.config.options.namespace;
70
+ const account = this.props.selectedAccount;
71
+ if (!namespace || !account) {
72
+ this.setState({
73
+ services: [],
74
+ });
75
+ }
76
+ ManifestKindSearchService.search('service', namespace, account).then((services) => {
77
+ this.setState({ services: map(services, 'name') });
78
+ });
79
+ };
80
+
81
+ private getServiceOptions = (): Array<Option<string>> => {
82
+ const options = this.state.services.map((service) => ({ label: split(service, ' ')[1], value: service }));
83
+ (this.props.config.options.services || []).forEach((service) => {
84
+ if (!this.state.services.includes(service)) {
85
+ options.push({ label: service, value: service });
86
+ }
87
+ });
88
+ return options;
89
+ };
90
+
91
+ private strategyOptionRenderer = (option: IDeploymentStrategy) => {
92
+ return (
93
+ <div className="body-regular">
94
+ <strong>
95
+ <Markdown tag="span" message={option.label} />
96
+ </strong>
97
+ <div>
98
+ <Markdown tag="span" message={option.description} />
99
+ </div>
100
+ </div>
101
+ );
102
+ };
103
+
104
+ public componentDidMount() {
105
+ this.fetchServices();
106
+ this.setState({ showRedBlackWarningMessage: false });
107
+ if (this.props.config.options.strategy === 'redblack') {
108
+ this.setState({ showRedBlackWarningMessage: true });
109
+ this.updateProps('options.strategy', 'bluegreen');
110
+ }
111
+ }
112
+
113
+ public componentDidUpdate(prevProps: IManifestDeploymentOptionsProps) {
114
+ if (prevProps.selectedAccount !== this.props.selectedAccount) {
115
+ this.updateProps('options.namespace', null);
116
+ }
117
+
118
+ if (prevProps.config.options.namespace !== this.props.config.options.namespace) {
119
+ this.updateProps('options.services', null);
120
+ this.fetchServices();
121
+ }
122
+
123
+ if (!this.props.config.options.enableTraffic && !!this.props.config.options.strategy) {
124
+ this.updateProps('options.enableTraffic', true);
125
+ }
126
+ }
127
+
128
+ public render() {
129
+ const { config } = this.props;
130
+ const { showRedBlackWarningMessage } = this.state;
131
+ return (
132
+ <>
133
+ <h4>Rollout Strategy Options</h4>
134
+ <StageConfigField helpKey="kubernetes.manifest.rolloutStrategyOptions" fieldColumns={8} label="Enable">
135
+ <div className="checkbox">
136
+ <label>
137
+ <input
138
+ checked={config.enabled}
139
+ onChange={(e) => this.onConfigChange('enabled', e.target.checked)}
140
+ type="checkbox"
141
+ />
142
+ Spinnaker manages traffic based on your selected strategy
143
+ </label>
144
+ </div>
145
+ </StageConfigField>
146
+ {config.enabled && (
147
+ <>
148
+ <StageConfigField fieldColumns={8} label="Service(s) Namespace">
149
+ <NamespaceSelector
150
+ createable={true}
151
+ onChange={(namespace: string): void => this.onConfigChange('options.namespace', namespace)}
152
+ accounts={this.props.accounts}
153
+ selectedAccount={this.props.selectedAccount}
154
+ selectedNamespace={config.options.namespace || ''}
155
+ />
156
+ </StageConfigField>
157
+ <StageConfigField fieldColumns={8} label="Service(s)">
158
+ <Creatable
159
+ clearable={false}
160
+ multi={true}
161
+ onChange={(options) => this.onConfigChange('options.services', map(options, 'value'))}
162
+ options={this.getServiceOptions()}
163
+ value={config.options.services}
164
+ />
165
+ </StageConfigField>
166
+ <StageConfigField fieldColumns={8} label="Traffic">
167
+ <div className="checkbox">
168
+ <label>
169
+ <input
170
+ checked={config.options.enableTraffic}
171
+ disabled={!!config.options.strategy}
172
+ onChange={(e) => this.onConfigChange('options.enableTraffic', e.target.checked)}
173
+ type="checkbox"
174
+ />
175
+ Send client requests to new pods
176
+ </label>
177
+ </div>
178
+ </StageConfigField>
179
+ <StageConfigField fieldColumns={8} helpKey="kubernetes.manifest.rolloutStrategy" label="Strategy">
180
+ <Select
181
+ id={'strategyDropdown'}
182
+ clearable={false}
183
+ onChange={(option: Option<IDeploymentStrategy>) => this.onConfigChange('options.strategy', option.key)}
184
+ options={rolloutStrategies}
185
+ optionRenderer={this.strategyOptionRenderer}
186
+ placeholder="None"
187
+ value={config.options.strategy}
188
+ valueKey="key"
189
+ valueRenderer={(o) => <>{o.label}</>}
190
+ />
191
+ {showRedBlackWarningMessage && (
192
+ <p id={'redBlackWarning'} style={{ color: 'orange' }}>
193
+ Warning: Red/black strategy is deprecated and will be removed soon. We automatically selected
194
+ blue/green instead!
195
+ </p>
196
+ )}
197
+ </StageConfigField>
198
+ </>
199
+ )}
200
+ </>
201
+ );
202
+ }
203
+ }