@spinnaker/titus 0.0.0-2025.1-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 (185) hide show
  1. package/CHANGELOG.md +2661 -0
  2. package/LICENSE.txt +203 -0
  3. package/dist/domain/IJobDisruptionBudget.d.ts +35 -0
  4. package/dist/domain/ITitusCredentials.d.ts +5 -0
  5. package/dist/domain/ITitusInstance.d.ts +28 -0
  6. package/dist/domain/ITitusScalingPolicy.d.ts +4 -0
  7. package/dist/domain/ITitusServerGroup.d.ts +52 -0
  8. package/dist/domain/ITitusServiceJobProcesses.d.ts +3 -0
  9. package/dist/domain/index.d.ts +5 -0
  10. package/dist/help/titus.help.d.ts +1 -0
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.js +12828 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/instance/details/TitusInstanceDetails.d.ts +25 -0
  15. package/dist/instance/details/TitusInstanceDns.d.ts +8 -0
  16. package/dist/instance/details/TitusInstanceInformation.d.ts +7 -0
  17. package/dist/instance/details/index.d.ts +4 -0
  18. package/dist/instance/details/titusInstanceDetailsUtils.d.ts +14 -0
  19. package/dist/pipeline/stages/cloneServerGroup/cloneServerGroupExecutionDetails.controller.d.ts +2 -0
  20. package/dist/pipeline/stages/cloneServerGroup/titusCloneServerGroupStage.d.ts +2 -0
  21. package/dist/pipeline/stages/destroyAsg/titusDestroyAsgStage.d.ts +2 -0
  22. package/dist/pipeline/stages/disableAsg/titusDisableAsgStage.d.ts +2 -0
  23. package/dist/pipeline/stages/disableCluster/titusDisableClusterStage.d.ts +2 -0
  24. package/dist/pipeline/stages/enableAsg/titusEnableAsgStage.d.ts +2 -0
  25. package/dist/pipeline/stages/findAmi/titusFindAmiStage.d.ts +2 -0
  26. package/dist/pipeline/stages/resizeAsg/titusResizeAsgStage.d.ts +2 -0
  27. package/dist/pipeline/stages/runJob/RunJobExecutionDetails.d.ts +16 -0
  28. package/dist/pipeline/stages/runJob/TitusRunJobStageConfig.d.ts +25 -0
  29. package/dist/pipeline/stages/runJob/TitusSecurityGroupPicker.d.ts +34 -0
  30. package/dist/pipeline/stages/runJob/titusRunJobStage.d.ts +1 -0
  31. package/dist/pipeline/stages/scaleDownCluster/titusScaleDownClusterStage.d.ts +2 -0
  32. package/dist/pipeline/stages/shrinkCluster/titusShrinkClusterStage.d.ts +2 -0
  33. package/dist/reactShims/index.d.ts +1 -0
  34. package/dist/reactShims/titus.react.injector.d.ts +11 -0
  35. package/dist/reactShims/titus.react.module.d.ts +1 -0
  36. package/dist/securityGroup/securityGroup.read.service.d.ts +2 -0
  37. package/dist/serverGroup/configure/ServerGroupCommandBuilder.d.ts +2 -0
  38. package/dist/serverGroup/configure/serverGroup.configure.titus.module.d.ts +2 -0
  39. package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +89 -0
  40. package/dist/serverGroup/configure/wizard/TitusCloneServerGroupModal.d.ts +31 -0
  41. package/dist/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.d.ts +28 -0
  42. package/dist/serverGroup/configure/wizard/pages/ServerGroupParameters.d.ts +15 -0
  43. package/dist/serverGroup/configure/wizard/pages/ServerGroupResources.d.ts +11 -0
  44. package/dist/serverGroup/configure/wizard/pages/TitusMapLayout.d.ts +4 -0
  45. package/dist/serverGroup/configure/wizard/pages/disruptionBudget/JobDisruptionBudget.d.ts +40 -0
  46. package/dist/serverGroup/configure/wizard/pages/disruptionBudget/PolicyOptions.d.ts +2 -0
  47. package/dist/serverGroup/configure/wizard/pages/disruptionBudget/RateOptions.d.ts +2 -0
  48. package/dist/serverGroup/configure/wizard/pages/disruptionBudget/WindowPicker.d.ts +23 -0
  49. package/dist/serverGroup/configure/wizard/pages/index.d.ts +4 -0
  50. package/dist/serverGroup/details/TitusCapacityDetailsSection.d.ts +11 -0
  51. package/dist/serverGroup/details/TitusLaunchConfigSection.d.ts +9 -0
  52. package/dist/serverGroup/details/TitusPackageDetailsSection.d.ts +6 -0
  53. package/dist/serverGroup/details/TitusSecurityGroups.d.ts +17 -0
  54. package/dist/serverGroup/details/capacityDetailsSection.component.d.ts +1 -0
  55. package/dist/serverGroup/details/disruptionBudget/DisruptionBudgetSection.d.ts +19 -0
  56. package/dist/serverGroup/details/disruptionBudget/EditDisruptionBudgetModal.d.ts +14 -0
  57. package/dist/serverGroup/details/index.d.ts +5 -0
  58. package/dist/serverGroup/details/launchConfigSection.component.d.ts +1 -0
  59. package/dist/serverGroup/details/resize/TitusResizeServerGroupModal.d.ts +8 -0
  60. package/dist/serverGroup/details/resize/useTaskMonitor.d.ts +25 -0
  61. package/dist/serverGroup/details/rollback/rollbackServerGroup.controller.d.ts +2 -0
  62. package/dist/serverGroup/details/scalingActivity/TitusScalingActivitiesModal.d.ts +7 -0
  63. package/dist/serverGroup/details/scalingPolicy/CreateScalingPolicyButton.d.ts +21 -0
  64. package/dist/serverGroup/details/scalingPolicy/TitusCustomScalingPolicy.d.ts +7 -0
  65. package/dist/serverGroup/details/scalingPolicy/createScalingPolicyButton.component.d.ts +1 -0
  66. package/dist/serverGroup/details/scalingPolicy/index.d.ts +3 -0
  67. package/dist/serverGroup/details/scalingPolicy/scalingPolicy.module.d.ts +1 -0
  68. package/dist/serverGroup/details/scalingPolicy/targetTracking/TitusTargetTrackingChart.d.ts +9 -0
  69. package/dist/serverGroup/details/scalingPolicy/targetTracking/UpsertTargetTrackingModal.d.ts +10 -0
  70. package/dist/serverGroup/details/scalingPolicy/titusCustomScalingPolicy.component.d.ts +1 -0
  71. package/dist/serverGroup/details/scalingPolicy/upsert/TitusScalingPolicyCommandBuilderService.d.ts +10 -0
  72. package/dist/serverGroup/details/scalingPolicy/upsert/UpsertScalingPolicyModal.d.ts +10 -0
  73. package/dist/serverGroup/details/sections/ITitusServerGroupDetailsSectionProps.d.ts +5 -0
  74. package/dist/serverGroup/details/serverGroupDetails.titus.controller.d.ts +2 -0
  75. package/dist/serverGroup/details/serviceJobProcesses/ServiceJobProcesses.d.ts +3 -0
  76. package/dist/serverGroup/details/serviceJobProcesses/ServiceJobProcessesSection.d.ts +7 -0
  77. package/dist/serverGroup/details/titusPackageDetailsSection.component.d.ts +1 -0
  78. package/dist/serverGroup/details/titusSecurityGroups.component.d.ts +1 -0
  79. package/dist/serverGroup/serverGroup.transformer.d.ts +2 -0
  80. package/dist/titus.module.d.ts +5 -0
  81. package/dist/titus.settings.d.ts +14 -0
  82. package/dist/validation/ApplicationNameValidator.d.ts +1 -0
  83. package/package.json +52 -0
  84. package/src/domain/IJobDisruptionBudget.ts +19 -0
  85. package/src/domain/ITitusCredentials.ts +6 -0
  86. package/src/domain/ITitusInstance.ts +30 -0
  87. package/src/domain/ITitusScalingPolicy.ts +5 -0
  88. package/src/domain/ITitusServerGroup.ts +56 -0
  89. package/src/domain/ITitusServiceJobProcesses.ts +3 -0
  90. package/src/domain/index.ts +5 -0
  91. package/src/help/titus.help.ts +99 -0
  92. package/src/index.ts +6 -0
  93. package/src/instance/details/TitusInstanceDetails.tsx +234 -0
  94. package/src/instance/details/TitusInstanceDns.tsx +40 -0
  95. package/src/instance/details/TitusInstanceInformation.tsx +42 -0
  96. package/src/instance/details/index.ts +4 -0
  97. package/src/instance/details/titusInstanceDetailsUtils.spec.ts +124 -0
  98. package/src/instance/details/titusInstanceDetailsUtils.ts +181 -0
  99. package/src/logo/titus.logo.less +5 -0
  100. package/src/logo/titus.logo.png +0 -0
  101. package/src/logo/titus.logo.svg +7 -0
  102. package/src/pipeline/stages/cloneServerGroup/cloneServerGroupExecutionDetails.controller.js +66 -0
  103. package/src/pipeline/stages/cloneServerGroup/cloneServerGroupExecutionDetails.html +46 -0
  104. package/src/pipeline/stages/cloneServerGroup/cloneServerGroupStage.html +106 -0
  105. package/src/pipeline/stages/cloneServerGroup/cloneServerGroupStepLabel.html +1 -0
  106. package/src/pipeline/stages/cloneServerGroup/titusCloneServerGroupStage.js +104 -0
  107. package/src/pipeline/stages/destroyAsg/destroyAsgStage.html +9 -0
  108. package/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html +1 -0
  109. package/src/pipeline/stages/destroyAsg/titusDestroyAsgStage.js +65 -0
  110. package/src/pipeline/stages/disableAsg/disableAsgStage.html +11 -0
  111. package/src/pipeline/stages/disableAsg/disableAsgStepLabel.html +1 -0
  112. package/src/pipeline/stages/disableAsg/titusDisableAsgStage.js +69 -0
  113. package/src/pipeline/stages/disableCluster/disableClusterStage.html +26 -0
  114. package/src/pipeline/stages/disableCluster/titusDisableClusterStage.js +85 -0
  115. package/src/pipeline/stages/enableAsg/enableAsgStage.html +11 -0
  116. package/src/pipeline/stages/enableAsg/enableAsgStepLabel.html +1 -0
  117. package/src/pipeline/stages/enableAsg/titusEnableAsgStage.js +73 -0
  118. package/src/pipeline/stages/findAmi/findAmiStage.html +24 -0
  119. package/src/pipeline/stages/findAmi/titusFindAmiStage.js +79 -0
  120. package/src/pipeline/stages/resizeAsg/resizeAsgStage.html +99 -0
  121. package/src/pipeline/stages/resizeAsg/resizeAsgStepLabel.html +1 -0
  122. package/src/pipeline/stages/resizeAsg/titusResizeAsgStage.js +125 -0
  123. package/src/pipeline/stages/runJob/RunJobExecutionDetails.tsx +165 -0
  124. package/src/pipeline/stages/runJob/TitusRunJobStageConfig.tsx +466 -0
  125. package/src/pipeline/stages/runJob/TitusSecurityGroupPicker.tsx +170 -0
  126. package/src/pipeline/stages/runJob/titusRunJobStage.ts +30 -0
  127. package/src/pipeline/stages/scaleDownCluster/scaleDownClusterStage.html +35 -0
  128. package/src/pipeline/stages/scaleDownCluster/titusScaleDownClusterStage.js +79 -0
  129. package/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html +34 -0
  130. package/src/pipeline/stages/shrinkCluster/titusShrinkClusterStage.js +73 -0
  131. package/src/reactShims/index.ts +1 -0
  132. package/src/reactShims/titus.react.injector.ts +17 -0
  133. package/src/reactShims/titus.react.module.ts +10 -0
  134. package/src/securityGroup/securityGroup.read.service.js +15 -0
  135. package/src/serverGroup/configure/ServerGroupCommandBuilder.js +247 -0
  136. package/src/serverGroup/configure/serverGroup.configure.titus.module.js +9 -0
  137. package/src/serverGroup/configure/serverGroupCommandBuilder.spec.js +63 -0
  138. package/src/serverGroup/configure/serverGroupConfiguration.service.ts +410 -0
  139. package/src/serverGroup/configure/wizard/TitusCloneServerGroupModal.tsx +293 -0
  140. package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.tsx +339 -0
  141. package/src/serverGroup/configure/wizard/pages/ServerGroupParameters.less +5 -0
  142. package/src/serverGroup/configure/wizard/pages/ServerGroupParameters.tsx +217 -0
  143. package/src/serverGroup/configure/wizard/pages/ServerGroupResources.tsx +200 -0
  144. package/src/serverGroup/configure/wizard/pages/TitusMapLayout.less +3 -0
  145. package/src/serverGroup/configure/wizard/pages/TitusMapLayout.tsx +29 -0
  146. package/src/serverGroup/configure/wizard/pages/disruptionBudget/JobDisruptionBudget.tsx +285 -0
  147. package/src/serverGroup/configure/wizard/pages/disruptionBudget/PolicyOptions.tsx +112 -0
  148. package/src/serverGroup/configure/wizard/pages/disruptionBudget/RateOptions.tsx +89 -0
  149. package/src/serverGroup/configure/wizard/pages/disruptionBudget/WindowPicker.tsx +202 -0
  150. package/src/serverGroup/configure/wizard/pages/index.ts +4 -0
  151. package/src/serverGroup/details/TitusCapacityDetailsSection.tsx +66 -0
  152. package/src/serverGroup/details/TitusLaunchConfigSection.tsx +35 -0
  153. package/src/serverGroup/details/TitusPackageDetailsSection.tsx +40 -0
  154. package/src/serverGroup/details/TitusSecurityGroups.tsx +107 -0
  155. package/src/serverGroup/details/capacityDetailsSection.component.ts +12 -0
  156. package/src/serverGroup/details/disruptionBudget/DisruptionBudgetSection.tsx +226 -0
  157. package/src/serverGroup/details/disruptionBudget/EditDisruptionBudgetModal.tsx +92 -0
  158. package/src/serverGroup/details/index.ts +5 -0
  159. package/src/serverGroup/details/launchConfigSection.component.ts +12 -0
  160. package/src/serverGroup/details/resize/TitusResizeServerGroupModal.tsx +302 -0
  161. package/src/serverGroup/details/resize/useTaskMonitor.ts +30 -0
  162. package/src/serverGroup/details/rollback/rollbackServerGroup.controller.js +149 -0
  163. package/src/serverGroup/details/rollback/rollbackServerGroup.html +133 -0
  164. package/src/serverGroup/details/scalingActivity/TitusScalingActivitiesModal.tsx +81 -0
  165. package/src/serverGroup/details/scalingPolicy/CreateScalingPolicyButton.tsx +102 -0
  166. package/src/serverGroup/details/scalingPolicy/TitusCustomScalingPolicy.tsx +13 -0
  167. package/src/serverGroup/details/scalingPolicy/createScalingPolicyButton.component.ts +15 -0
  168. package/src/serverGroup/details/scalingPolicy/index.js +3 -0
  169. package/src/serverGroup/details/scalingPolicy/scalingPolicy.module.ts +7 -0
  170. package/src/serverGroup/details/scalingPolicy/targetTracking/TitusTargetTrackingChart.tsx +57 -0
  171. package/src/serverGroup/details/scalingPolicy/targetTracking/UpsertTargetTrackingModal.tsx +82 -0
  172. package/src/serverGroup/details/scalingPolicy/titusCustomScalingPolicy.component.ts +17 -0
  173. package/src/serverGroup/details/scalingPolicy/upsert/TitusScalingPolicyCommandBuilderService.ts +115 -0
  174. package/src/serverGroup/details/scalingPolicy/upsert/UpsertScalingPolicyModal.tsx +157 -0
  175. package/src/serverGroup/details/sections/ITitusServerGroupDetailsSectionProps.ts +6 -0
  176. package/src/serverGroup/details/serverGroupDetails.html +191 -0
  177. package/src/serverGroup/details/serverGroupDetails.titus.controller.js +457 -0
  178. package/src/serverGroup/details/serviceJobProcesses/ServiceJobProcesses.ts +12 -0
  179. package/src/serverGroup/details/serviceJobProcesses/ServiceJobProcessesSection.tsx +136 -0
  180. package/src/serverGroup/details/titusPackageDetailsSection.component.ts +12 -0
  181. package/src/serverGroup/details/titusSecurityGroups.component.ts +12 -0
  182. package/src/serverGroup/serverGroup.transformer.js +90 -0
  183. package/src/titus.module.ts +95 -0
  184. package/src/titus.settings.ts +22 -0
  185. package/src/validation/ApplicationNameValidator.ts +43 -0
@@ -0,0 +1,466 @@
1
+ import { defaultsDeep, set } from 'lodash';
2
+ import React from 'react';
3
+
4
+ import type { IAggregatedAccounts, IRegion, IStageConfigProps } from '@spinnaker/core';
5
+ import {
6
+ AccountSelectInput,
7
+ AccountService,
8
+ AccountTag,
9
+ FirewallLabels,
10
+ HelpField,
11
+ MapEditor,
12
+ RegionSelectField,
13
+ SpelNumberInput,
14
+ SpinFormik,
15
+ StageConfigField,
16
+ } from '@spinnaker/core';
17
+ import type { IDockerImageAndTagChanges } from '@spinnaker/docker';
18
+ import { DockerImageAndTagSelector, DockerImageUtils } from '@spinnaker/docker';
19
+
20
+ import { TitusSecurityGroupPicker } from './TitusSecurityGroupPicker';
21
+ import type { IJobDisruptionBudget, ITitusResources } from '../../../domain';
22
+ import { IPv6CheckboxInput, JobDisruptionBudget } from '../../../serverGroup/configure/wizard/pages';
23
+ import { TitusProviderSettings } from '../../../titus.settings';
24
+
25
+ export interface ITitusRunJobStageConfigState {
26
+ credentials: string[];
27
+ regions: IRegion[];
28
+ loaded: boolean;
29
+ }
30
+
31
+ interface IClusterDefaults {
32
+ application: string;
33
+ containerAttributes: object;
34
+ env: object;
35
+ labels: object;
36
+ resources: ITitusResources;
37
+ retries: number;
38
+ runtimeLimitSecs: number;
39
+ securityGroups: string[];
40
+ iamProfile?: string;
41
+ }
42
+
43
+ export class TitusRunJobStageConfig extends React.Component<IStageConfigProps, ITitusRunJobStageConfigState> {
44
+ private credentialsKeyedByAccount: IAggregatedAccounts = {};
45
+ private defaultIamProfile = '';
46
+
47
+ public state: ITitusRunJobStageConfigState = {
48
+ credentials: [],
49
+ regions: [],
50
+ loaded: false,
51
+ };
52
+
53
+ public constructor(props: IStageConfigProps) {
54
+ super(props);
55
+ const { application, stage } = props;
56
+ stage.cluster = stage.cluster || {};
57
+ stage.waitForCompletion = stage.waitForCompletion === undefined ? true : stage.waitForCompletion;
58
+
59
+ if (stage.cluster.imageId && !stage.cluster.imageId.includes('${')) {
60
+ Object.assign(stage, DockerImageUtils.splitImageId(stage.cluster.imageId));
61
+ }
62
+
63
+ if (!stage.credentials && application.defaultCredentials.titus) {
64
+ stage.credentials = application.defaultCredentials.titus;
65
+ }
66
+
67
+ if (!stage.cluster.capacity) {
68
+ stage.cluster.capacity = {
69
+ min: 1,
70
+ max: 1,
71
+ desired: 1,
72
+ };
73
+ }
74
+
75
+ const defaultIamProfile = TitusProviderSettings.defaults.iamProfile || '{{application}}InstanceProfile';
76
+ this.defaultIamProfile = defaultIamProfile.replace('{{application}}', application.name);
77
+
78
+ const clusterDefaults: IClusterDefaults = {
79
+ application: application.name,
80
+ containerAttributes: {},
81
+ env: {},
82
+ labels: {},
83
+ resources: {
84
+ cpu: 1,
85
+ disk: 10000,
86
+ gpu: 0,
87
+ memory: 512,
88
+ networkMbps: 128,
89
+ },
90
+ retries: 0,
91
+ runtimeLimitSecs: 3600,
92
+ securityGroups: [] as string[],
93
+ };
94
+
95
+ if (stage.isNew) {
96
+ clusterDefaults.iamProfile = this.defaultIamProfile;
97
+ }
98
+
99
+ defaultsDeep(stage.cluster, clusterDefaults);
100
+
101
+ stage.cloudProvider = stage.cloudProvider || 'titus';
102
+ stage.deferredInitialization = true;
103
+ }
104
+
105
+ private setRegistry(account: string) {
106
+ if (account) {
107
+ this.props.stage.registry = this.credentialsKeyedByAccount[account].registry;
108
+ }
109
+ }
110
+
111
+ private updateRegions(account: string) {
112
+ let regions: IRegion[];
113
+ if (account) {
114
+ regions = this.credentialsKeyedByAccount[account].regions;
115
+ if (regions.map((r) => r.name).every((r) => r !== this.props.stage.cluster.region)) {
116
+ delete this.props.stage.cluster.region;
117
+ this.props.stageFieldUpdated();
118
+ }
119
+ } else {
120
+ regions = [];
121
+ }
122
+ this.setState({ regions });
123
+ }
124
+
125
+ private accountChanged = (account: string) => {
126
+ set(this.props.stage, 'account', account);
127
+ this.stageFieldChanged('credentials', account);
128
+ this.setRegistry(account);
129
+ this.updateRegions(account);
130
+
131
+ const accountDetails = this.credentialsKeyedByAccount[account];
132
+ const ipv6Default = accountDetails.environment === 'test' ? 'true' : 'false';
133
+ this.associateIPv6AddressChanged(ipv6Default);
134
+ };
135
+
136
+ private dockerChanged = (changes: IDockerImageAndTagChanges) => {
137
+ // Temporary until stage config section is no longer angular
138
+ const { imageId, ...rest } = changes;
139
+ Object.assign(this.props.stage, rest);
140
+ if (imageId) {
141
+ this.props.stage.cluster.imageId = imageId;
142
+ } else {
143
+ delete this.props.stage.cluster.imageId;
144
+ }
145
+ this.props.stageFieldUpdated();
146
+ this.forceUpdate();
147
+ };
148
+
149
+ private stageFieldChanged = (fieldIndex: string, value: any) => {
150
+ set(this.props.stage, fieldIndex, value);
151
+ this.props.stageFieldUpdated();
152
+ this.forceUpdate();
153
+ };
154
+
155
+ public componentDidMount() {
156
+ const { stage } = this.props;
157
+ AccountService.getCredentialsKeyedByAccount('titus').then((credentialsKeyedByAccount) => {
158
+ this.credentialsKeyedByAccount = credentialsKeyedByAccount;
159
+ const credentials = Object.keys(credentialsKeyedByAccount);
160
+ stage.credentials = stage.credentials || credentials[0];
161
+
162
+ this.setRegistry(stage.credentials);
163
+ this.updateRegions(stage.credentials);
164
+ this.setState({ credentials, loaded: true });
165
+
166
+ const account = this.credentialsKeyedByAccount[stage.credentials];
167
+ const defaultIPv6Address =
168
+ account.environment === 'test' &&
169
+ stage.cluster.containerAttributes['titusParameter.agent.assignIPv6Address'] === undefined
170
+ ? 'true'
171
+ : 'false';
172
+ this.associateIPv6AddressChanged(defaultIPv6Address);
173
+ });
174
+ }
175
+
176
+ private mapChanged = (key: string, values: { [key: string]: string }) => {
177
+ this.stageFieldChanged(key, values);
178
+ };
179
+
180
+ private groupsChanged = (groups: string[]) => {
181
+ this.stageFieldChanged('cluster.securityGroups', groups);
182
+ this.forceUpdate();
183
+ };
184
+
185
+ private associateIPv6AddressChanged = (value: string) => {
186
+ const oldAttributes = this.props.stage.cluster.containerAttributes || {};
187
+ const updatedAttributes = {
188
+ ...oldAttributes,
189
+ 'titusParameter.agent.assignIPv6Address': value,
190
+ };
191
+ this.mapChanged('cluster.containerAttributes', updatedAttributes);
192
+ };
193
+
194
+ public disruptionBudgetChanged = (values: IJobDisruptionBudget) => {
195
+ const { stage, stageFieldUpdated } = this.props;
196
+ stage.cluster.disruptionBudget = values;
197
+ stageFieldUpdated();
198
+ };
199
+
200
+ public render() {
201
+ const { application, stage } = this.props;
202
+ const { credentials, loaded, regions } = this.state;
203
+ const awsAccount = (this.credentialsKeyedByAccount[stage.credentials] || { awsAccount: '' }).awsAccount;
204
+
205
+ const entryPointList = stage.cluster.entryPointList?.length
206
+ ? stage.cluster.entryPointList.join(',')
207
+ : stage.cluster.entryPoint;
208
+ const cmdList = stage.cluster.cmdList?.length ? stage.cluster.cmdList.join(',') : stage.cluster.cmd;
209
+
210
+ return (
211
+ <div className="form-horizontal">
212
+ <div className="form-group">
213
+ <label className="col-md-3 sm-label-right">
214
+ <span className="label-text">Account</span>
215
+ </label>
216
+ <div className="col-md-5">
217
+ <AccountSelectInput
218
+ value={stage.credentials}
219
+ onChange={(evt: any) => this.accountChanged(evt.target.value)}
220
+ accounts={credentials}
221
+ provider="titus"
222
+ />
223
+ {stage.credentials !== undefined && (
224
+ <div className="small">
225
+ Uses resources from the Amazon account <AccountTag account={awsAccount} />
226
+ </div>
227
+ )}
228
+ </div>
229
+ </div>
230
+
231
+ <RegionSelectField
232
+ labelColumns={3}
233
+ fieldColumns={5}
234
+ component={stage.cluster}
235
+ field="region"
236
+ account={stage.credentials}
237
+ regions={regions}
238
+ onChange={(region) => this.stageFieldChanged('region', region)}
239
+ />
240
+
241
+ <DockerImageAndTagSelector
242
+ specifyTagByRegex={false}
243
+ account={stage.credentials}
244
+ digest={stage.digest}
245
+ imageId={stage.cluster.imageId}
246
+ organization={stage.organization}
247
+ registry={stage.registry}
248
+ repository={stage.repository}
249
+ tag={stage.tag}
250
+ showRegistry={false}
251
+ onChange={this.dockerChanged}
252
+ deferInitialization={stage.deferredInitialization}
253
+ />
254
+
255
+ <StageConfigField label="CPU(s)">
256
+ <SpelNumberInput
257
+ value={stage.cluster.resources.cpu}
258
+ onChange={(value) => this.stageFieldChanged('cluster.resources.cpu', value)}
259
+ required={true}
260
+ />
261
+ </StageConfigField>
262
+
263
+ <StageConfigField label="Memory (MB)">
264
+ <SpelNumberInput
265
+ onChange={(value) => this.stageFieldChanged('cluster.resources.memory', value)}
266
+ value={stage.cluster.resources.memory}
267
+ required={true}
268
+ />
269
+ </StageConfigField>
270
+
271
+ <StageConfigField label="Disk (MB)">
272
+ <SpelNumberInput
273
+ onChange={(value) => this.stageFieldChanged('cluster.resources.disk', value)}
274
+ value={stage.cluster.resources.disk}
275
+ required={true}
276
+ />
277
+ </StageConfigField>
278
+
279
+ <StageConfigField label="Network (Mbps)" helpKey="titus.deploy.network">
280
+ <SpelNumberInput
281
+ onChange={(value) => this.stageFieldChanged('cluster.resources.networkMbps', value)}
282
+ value={stage.cluster.resources.networkMbps}
283
+ required={true}
284
+ />
285
+ </StageConfigField>
286
+
287
+ <StageConfigField label="GPU(s)" helpKey="titus.deploy.gpu">
288
+ <SpelNumberInput
289
+ onChange={(value) => this.stageFieldChanged('cluster.resources.gpu', value)}
290
+ value={stage.cluster.resources.gpu}
291
+ required={true}
292
+ />
293
+ </StageConfigField>
294
+
295
+ <StageConfigField label="Entrypoint(s)" helpKey="titus.deploy.entrypoint">
296
+ <input
297
+ type="text"
298
+ className="form-control input-sm"
299
+ value={entryPointList}
300
+ onChange={(e) => this.stageFieldChanged('cluster.entryPointList', e.target.value.split(','))}
301
+ />
302
+ </StageConfigField>
303
+
304
+ <StageConfigField label="Command(s)" helpKey="titus.deploy.command">
305
+ <input
306
+ type="text"
307
+ className="form-control input-sm"
308
+ value={cmdList}
309
+ onChange={(e) => this.stageFieldChanged('cluster.cmdList', e.target.value.split(','))}
310
+ />
311
+ </StageConfigField>
312
+
313
+ <StageConfigField label="Runtime Limit (Seconds)" helpKey="titus.deploy.runtimeLimitSecs">
314
+ <SpelNumberInput
315
+ value={stage.cluster.runtimeLimitSecs}
316
+ onChange={(value) => this.stageFieldChanged('cluster.runtimeLimitSecs', value)}
317
+ min={1}
318
+ required={true}
319
+ />
320
+ </StageConfigField>
321
+
322
+ <StageConfigField label="Retries" helpKey="titus.deploy.retries">
323
+ <SpelNumberInput
324
+ onChange={(value) => this.stageFieldChanged('cluster.retries', value)}
325
+ value={stage.cluster.retries}
326
+ min={0}
327
+ required={true}
328
+ />
329
+ </StageConfigField>
330
+
331
+ <StageConfigField label="Property File" helpKey="titus.deploy.propertyFile">
332
+ <input
333
+ type="text"
334
+ className="form-control input-sm"
335
+ onChange={(e) => this.stageFieldChanged('propertyFile', e.target.value)}
336
+ value={stage.propertyFile}
337
+ />
338
+ </StageConfigField>
339
+
340
+ <div className="form-group">
341
+ <div className="col-md-9 col-md-offset-1">
342
+ <div className="checkbox">
343
+ <label>
344
+ <input
345
+ type="checkbox"
346
+ checked={stage.showAdvancedOptions}
347
+ onChange={(e) => this.stageFieldChanged('showAdvancedOptions', e.target.checked)}
348
+ />
349
+ <strong>Show Advanced Options</strong>
350
+ </label>
351
+ </div>
352
+ </div>
353
+ </div>
354
+
355
+ <div className={`${stage.showAdvancedOptions === true ? 'collapse.in' : 'collapse'}`}>
356
+ <div className="form-group">
357
+ <label className="col-md-3 sm-label-right">
358
+ <span className="label-text">IAM Instance Profile</span> <HelpField id="titus.deploy.iamProfile" />
359
+ </label>
360
+ <div className="col-md-4">
361
+ <input
362
+ type="text"
363
+ className="form-control input-sm"
364
+ value={stage.cluster.iamProfile}
365
+ placeholder={this.defaultIamProfile}
366
+ required={true}
367
+ onChange={(e) => this.stageFieldChanged('cluster.iamProfile', e.target.value)}
368
+ />
369
+ {!stage.isNew && !stage.cluster.iamProfile && (
370
+ <a
371
+ className="small clickable"
372
+ onClick={() => this.stageFieldChanged('cluster.iamProfile', this.defaultIamProfile)}
373
+ >
374
+ Use suggested default
375
+ </a>
376
+ )}
377
+ </div>
378
+ <div className="col-md-1 small" style={{ whiteSpace: 'nowrap', paddingLeft: '0px', paddingTop: '7px' }}>
379
+ in <AccountTag account={awsAccount} />
380
+ </div>
381
+ </div>
382
+
383
+ <StageConfigField label="Capacity Group" fieldColumns={4} helpKey="titus.job.capacityGroup">
384
+ <input
385
+ type="text"
386
+ className="form-control input-sm"
387
+ value={stage.cluster.capacityGroup || ''}
388
+ onChange={(e) => this.stageFieldChanged('cluster.capacityGroup', e.target.value)}
389
+ />
390
+ </StageConfigField>
391
+
392
+ <StageConfigField label={FirewallLabels.get('Firewalls')} helpKey="titus.job.securityGroups">
393
+ {(!stage.credentials || !stage.cluster.region) && (
394
+ <div>Account and region must be selected before {FirewallLabels.get('firewalls')} can be added</div>
395
+ )}
396
+ {loaded && stage.credentials && stage.cluster.region && (
397
+ <TitusSecurityGroupPicker
398
+ account={stage.credentials}
399
+ region={stage.cluster.region}
400
+ command={stage}
401
+ amazonAccount={awsAccount}
402
+ hideLabel={true}
403
+ groupsToEdit={stage.cluster.securityGroups}
404
+ onChange={this.groupsChanged}
405
+ />
406
+ )}
407
+ </StageConfigField>
408
+
409
+ <StageConfigField label="Associate IPv6 Address (Recommended)" helpKey="serverGroup.ipv6">
410
+ <IPv6CheckboxInput
411
+ value={stage.cluster.containerAttributes['titusParameter.agent.assignIPv6Address']}
412
+ onChange={(e) => this.associateIPv6AddressChanged(e.target.value)}
413
+ />
414
+ </StageConfigField>
415
+
416
+ <StageConfigField label={'Disruption Budget'} helpKey="titus.disruptionbudget.description">
417
+ <SpinFormik
418
+ initialValues={stage.cluster}
419
+ onSubmit={() => {}}
420
+ render={(formik) => (
421
+ <JobDisruptionBudget
422
+ formik={formik}
423
+ app={application}
424
+ runJobView={true}
425
+ onStageChange={this.disruptionBudgetChanged}
426
+ />
427
+ )}
428
+ />
429
+ </StageConfigField>
430
+
431
+ <StageConfigField label="Job Attributes (optional)">
432
+ <MapEditor
433
+ model={stage.cluster.labels}
434
+ allowEmpty={true}
435
+ onChange={(v: any) => this.mapChanged('cluster.labels', v)}
436
+ />
437
+ </StageConfigField>
438
+ <StageConfigField label="Container Attributes (optional)">
439
+ <MapEditor
440
+ model={stage.cluster.containerAttributes}
441
+ allowEmpty={true}
442
+ onChange={(v: any) => this.mapChanged('cluster.containerAttributes', v)}
443
+ />
444
+ </StageConfigField>
445
+ <StageConfigField label="Environment Variables (optional)">
446
+ <MapEditor
447
+ model={stage.cluster.env}
448
+ allowEmpty={true}
449
+ onChange={(v: any) => this.mapChanged('cluster.env', v)}
450
+ />
451
+ </StageConfigField>
452
+ </div>
453
+
454
+ <StageConfigField label="Wait for results" helpKey="titus.job.waitForCompletion">
455
+ <input
456
+ type="checkbox"
457
+ className="input-sm"
458
+ name="waitForCompletion"
459
+ checked={stage.waitForCompletion}
460
+ onChange={(e) => this.stageFieldChanged('waitForCompletion', e.target.checked)}
461
+ />
462
+ </StageConfigField>
463
+ </div>
464
+ );
465
+ }
466
+ }
@@ -0,0 +1,170 @@
1
+ import { chain, xor } from 'lodash';
2
+ import { $q } from 'ngimport';
3
+ import React from 'react';
4
+
5
+ import { SecurityGroupSelector, ServerGroupSecurityGroupsRemoved, VpcReader } from '@spinnaker/amazon';
6
+ import type { IAccountDetails, IAggregatedAccounts, ISecurityGroup, IVpc } from '@spinnaker/core';
7
+ import { AccountService, AccountTag, FirewallLabels, ReactInjector } from '@spinnaker/core';
8
+
9
+ export interface ITitusSecurityGroupPickerProps {
10
+ account: string;
11
+ region: string;
12
+ command: any;
13
+ groupsToEdit: string[];
14
+ hideLabel: boolean;
15
+ amazonAccount: string;
16
+ onChange: (groups: string[]) => void;
17
+ }
18
+
19
+ export interface ITitusSecurityGroupPickerState {
20
+ availableGroups: ISecurityGroup[];
21
+ removedGroups: string[];
22
+ loaded: boolean;
23
+ }
24
+
25
+ export class TitusSecurityGroupPicker extends React.Component<
26
+ ITitusSecurityGroupPickerProps,
27
+ ITitusSecurityGroupPickerState
28
+ > {
29
+ public state: ITitusSecurityGroupPickerState = {
30
+ availableGroups: [],
31
+ removedGroups: [],
32
+ loaded: false,
33
+ };
34
+
35
+ private credentials: IAggregatedAccounts = {};
36
+ private vpcs: IVpc[] = [];
37
+ private securityGroups: any = {};
38
+
39
+ constructor(props: ITitusSecurityGroupPickerProps) {
40
+ super(props);
41
+ }
42
+
43
+ private getCredentials(): IAccountDetails {
44
+ return this.credentials[this.props.command.credentials];
45
+ }
46
+
47
+ private getAwsAccount(): string {
48
+ return this.getCredentials().awsAccount;
49
+ }
50
+
51
+ private getRegion(): string {
52
+ return this.props.command.region || (this.props.command.cluster ? this.props.command.cluster.region : null);
53
+ }
54
+
55
+ private getVpcId(): string {
56
+ const credentials = this.getCredentials();
57
+ const match = this.vpcs.find(
58
+ (vpc) =>
59
+ vpc.name === credentials.awsVpc &&
60
+ vpc.account === credentials.awsAccount &&
61
+ vpc.region === this.getRegion() &&
62
+ vpc.cloudProvider === 'aws',
63
+ );
64
+ return match ? match.id : null;
65
+ }
66
+
67
+ private getRegionalSecurityGroups(): ISecurityGroup[] {
68
+ const newSecurityGroups: any = this.securityGroups[this.getAwsAccount()] || { aws: {} };
69
+ return chain<ISecurityGroup>(newSecurityGroups.aws[this.getRegion()])
70
+ .filter({ vpcId: this.getVpcId() })
71
+ .sortBy('name')
72
+ .value();
73
+ }
74
+
75
+ public refreshSecurityGroups(skipCommandReconfiguration?: boolean) {
76
+ return ReactInjector.cacheInitializer.refreshCache('securityGroups').then(() => {
77
+ return ReactInjector.securityGroupReader.getAllSecurityGroups().then((securityGroups) => {
78
+ this.securityGroups = securityGroups;
79
+ if (!skipCommandReconfiguration) {
80
+ this.configureSecurityGroupOptions();
81
+ }
82
+ });
83
+ });
84
+ }
85
+
86
+ private configureSecurityGroupOptions(): void {
87
+ let availableGroups = this.getRegionalSecurityGroups();
88
+ const oldAvailableGroups = this.state.availableGroups.length ? this.state.availableGroups : availableGroups;
89
+ let removedGroups: string[];
90
+
91
+ let groupsToEdit = this.props.groupsToEdit;
92
+ if (availableGroups && this.props.groupsToEdit) {
93
+ // not initializing - we are actually changing groups
94
+ const oldGroupNames: string[] = groupsToEdit.map((groupId: string) => {
95
+ const match = oldAvailableGroups.find((o) => o.id === groupId);
96
+ return match ? match.name : groupId;
97
+ });
98
+
99
+ const matchedGroups: ISecurityGroup[] = oldGroupNames
100
+ .map((groupId: string) => {
101
+ const securityGroup: any = availableGroups.find((o) => o.id === groupId || o.name === groupId);
102
+ return securityGroup ? securityGroup.name : null;
103
+ })
104
+ .map((groupName: string) => availableGroups.find((g) => g.name === groupName))
105
+ .filter((group: any) => group);
106
+
107
+ const matchedGroupNames: string[] = matchedGroups.map((g) => g.name);
108
+ removedGroups = xor(oldGroupNames, matchedGroupNames);
109
+ groupsToEdit = matchedGroups.map((g) => g.id);
110
+ this.props.onChange(groupsToEdit);
111
+ }
112
+ availableGroups = availableGroups.sort((a, b) => a.name.localeCompare(b.name));
113
+ this.setState({ availableGroups, loaded: true, removedGroups });
114
+ }
115
+
116
+ private clearRemoved = () => {
117
+ this.setState({ removedGroups: [] });
118
+ };
119
+
120
+ public componentDidMount() {
121
+ const credentialLoader = AccountService.getCredentialsKeyedByAccount('titus').then(
122
+ (credentials: IAggregatedAccounts) => {
123
+ this.credentials = credentials;
124
+ },
125
+ );
126
+ const groupLoader = ReactInjector.securityGroupReader.getAllSecurityGroups().then((groups) => {
127
+ this.securityGroups = groups;
128
+ });
129
+ const vpcLoader = VpcReader.listVpcs().then((vpcs: IVpc[]) => (this.vpcs = vpcs));
130
+ $q.all([credentialLoader, groupLoader, vpcLoader]).then(() => this.configureSecurityGroupOptions());
131
+ }
132
+
133
+ public componentWillReceiveProps(nextProps: ITitusSecurityGroupPickerProps) {
134
+ if (this.props.account !== nextProps.account || this.props.region !== nextProps.region) {
135
+ this.configureSecurityGroupOptions();
136
+ }
137
+ }
138
+
139
+ public render() {
140
+ const { command, hideLabel, groupsToEdit, amazonAccount } = this.props;
141
+ const { availableGroups, loaded, removedGroups } = this.state;
142
+
143
+ const firewallsLabel = FirewallLabels.get('firewalls');
144
+
145
+ if (loaded) {
146
+ return (
147
+ <div className="clearfix">
148
+ <ServerGroupSecurityGroupsRemoved removed={removedGroups} onClear={this.clearRemoved} />
149
+ <SecurityGroupSelector
150
+ command={command}
151
+ hideLabel={hideLabel}
152
+ availableGroups={availableGroups}
153
+ groupsToEdit={groupsToEdit}
154
+ refresh={() => this.refreshSecurityGroups() as any}
155
+ onChange={this.props.onChange}
156
+ helpKey="titus.deploy.securityGroups"
157
+ />
158
+
159
+ {amazonAccount && command.credentials !== undefined && (
160
+ <div className={`small ${!hideLabel ? 'col-md-offset-3 col-md-9' : ''}`}>
161
+ Uses {firewallsLabel} from the Amazon account <AccountTag account={amazonAccount} />
162
+ </div>
163
+ )}
164
+ </div>
165
+ );
166
+ }
167
+
168
+ return null;
169
+ }
170
+ }
@@ -0,0 +1,30 @@
1
+ import type { IStage } from '@spinnaker/core';
2
+ import { ExecutionDetailsTasks, Registry } from '@spinnaker/core';
3
+
4
+ import { RunJobExecutionDetails } from './RunJobExecutionDetails';
5
+ import { TitusRunJobStageConfig } from './TitusRunJobStageConfig';
6
+
7
+ Registry.pipeline.registerStage({
8
+ provides: 'runJob',
9
+ useBaseProvider: true,
10
+ restartable: true,
11
+ key: 'runJob',
12
+ cloudProvider: 'titus',
13
+ providesFor: ['aws', 'titus'],
14
+ component: TitusRunJobStageConfig,
15
+ executionDetailsSections: [RunJobExecutionDetails, ExecutionDetailsTasks],
16
+ accountExtractor: (stage: IStage) => [stage.context.credentials],
17
+ configAccountExtractor: (stage: IStage) => [stage.credentials],
18
+ supportsCustomTimeout: true,
19
+ validators: [
20
+ { type: 'requiredField', fieldName: 'cluster.iamProfile' },
21
+ { type: 'requiredField', fieldName: 'cluster.imageId' },
22
+ { type: 'requiredField', fieldName: 'credentials' },
23
+ { type: 'requiredField', fieldName: 'cluster.region' },
24
+ { type: 'requiredField', fieldName: 'cluster.resources.cpu' },
25
+ { type: 'requiredField', fieldName: 'cluster.resources.gpu' },
26
+ { type: 'requiredField', fieldName: 'cluster.resources.memory' },
27
+ { type: 'requiredField', fieldName: 'cluster.resources.disk' },
28
+ { type: 'requiredField', fieldName: 'cluster.runtimeLimitSecs' },
29
+ ],
30
+ });