@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,339 @@
1
+ import type { FormikErrors, FormikProps } from 'formik';
2
+ import { Field } from 'formik';
3
+ import React from 'react';
4
+
5
+ import { AWSProviderSettings, SubnetSelectField } from '@spinnaker/amazon';
6
+ import type { Application, IServerGroup, IWizardPageComponent } from '@spinnaker/core';
7
+ import {
8
+ AccountSelectInput,
9
+ AccountTag,
10
+ DeployingIntoManagedClusterWarning,
11
+ DeploymentStrategySelector,
12
+ HelpField,
13
+ NameUtils,
14
+ ReactInjector,
15
+ RegionSelectField,
16
+ ServerGroupDetailsField,
17
+ ServerGroupNamePreview,
18
+ } from '@spinnaker/core';
19
+ import { DockerImageAndTagSelector, DockerImageUtils } from '@spinnaker/docker';
20
+
21
+ import type { ITitusServerGroupCommand } from '../../../configure/serverGroupConfiguration.service';
22
+ import { TitusProviderSettings } from '../../../../titus.settings';
23
+
24
+ const isNotExpressionLanguage = (field: string) => field && !field.includes('${');
25
+
26
+ // Allow dot, underscore, and spel
27
+ const isStackPattern = (stack: string) => (isNotExpressionLanguage(stack) ? /^([\w.]+|\${[^}]+})*$/.test(stack) : true);
28
+
29
+ // Allow dot, underscore, caret, tilde, dash and spel
30
+ const isDetailPattern = (detail: string) =>
31
+ isNotExpressionLanguage(detail) ? /^([\w.^~-]+|\${[^}]+})*$/.test(detail) : true;
32
+
33
+ export interface IServerGroupBasicSettingsProps {
34
+ app: Application;
35
+ formik: FormikProps<ITitusServerGroupCommand>;
36
+ }
37
+
38
+ export interface IServerGroupBasicSettingsState {
39
+ namePreview: string;
40
+ createsNewCluster: boolean;
41
+ latestServerGroup: IServerGroup;
42
+ }
43
+
44
+ export class ServerGroupBasicSettings
45
+ extends React.Component<IServerGroupBasicSettingsProps, IServerGroupBasicSettingsState>
46
+ implements IWizardPageComponent<ITitusServerGroupCommand> {
47
+ constructor(props: IServerGroupBasicSettingsProps) {
48
+ super(props);
49
+
50
+ const { values, setFieldValue } = this.props.formik;
51
+ if (values.imageId && !values.imageId.includes('${')) {
52
+ const { digest, organization, repository, tag } = DockerImageUtils.splitImageId(values.imageId);
53
+ setFieldValue('digest', digest);
54
+ setFieldValue('organization', organization);
55
+ setFieldValue('repository', repository);
56
+ setFieldValue('tag', tag);
57
+ }
58
+
59
+ this.state = {
60
+ ...this.getStateFromProps(props),
61
+ };
62
+ }
63
+
64
+ private getStateFromProps(props: IServerGroupBasicSettingsProps) {
65
+ const { app } = props;
66
+ const { values } = props.formik;
67
+ const namePreview = NameUtils.getClusterName(app.name, values.stack, values.freeFormDetails);
68
+ const createsNewCluster = !app.clusters.find((c) => c.name === namePreview);
69
+
70
+ const inCluster = (app.serverGroups.data as IServerGroup[])
71
+ .filter((serverGroup) => {
72
+ return (
73
+ serverGroup.cluster === namePreview &&
74
+ serverGroup.account === values.credentials &&
75
+ serverGroup.region === values.region
76
+ );
77
+ })
78
+ .sort((a, b) => a.createdTime - b.createdTime);
79
+ const latestServerGroup = inCluster.length ? inCluster.pop() : null;
80
+
81
+ return { namePreview, createsNewCluster, latestServerGroup };
82
+ }
83
+
84
+ private accountUpdated = (account: string): void => {
85
+ const { setFieldValue, values } = this.props.formik;
86
+ values.credentials = account;
87
+ values.credentialsChanged(values);
88
+ setFieldValue('account', account);
89
+ setFieldValue('credentials', account);
90
+
91
+ const accountDetails = values.backingData.credentialsKeyedByAccount[account];
92
+
93
+ const newAttr = {
94
+ ...values.containerAttributes,
95
+ 'titusParameter.agent.assignIPv6Address': accountDetails.environment === 'test' ? 'true' : 'false',
96
+ };
97
+ setFieldValue('containerAttributes', newAttr);
98
+ };
99
+
100
+ private regionUpdated = (region: string): void => {
101
+ const { values, setFieldValue } = this.props.formik;
102
+ values.region = region;
103
+ values.regionChanged(values);
104
+ setFieldValue('region', region);
105
+ };
106
+
107
+ public validate(values: ITitusServerGroupCommand) {
108
+ const errors: FormikErrors<ITitusServerGroupCommand> = {};
109
+
110
+ if (!isStackPattern(values.stack)) {
111
+ errors.stack = 'Only dot(.) and underscore(_) special characters are allowed in the Stack field.';
112
+ }
113
+
114
+ if (!isDetailPattern(values.freeFormDetails)) {
115
+ errors.freeFormDetails =
116
+ 'Only dot(.), underscore(_), caret (^), tilde (~), and dash(-) special characters are allowed in the Detail field.';
117
+ }
118
+
119
+ if (!values.viewState.disableImageSelection) {
120
+ if (!values.imageId) {
121
+ errors.imageId = 'Image is required.';
122
+ }
123
+ }
124
+
125
+ // this error is added exclusively to disable the "create/clone" button - it is not visible aside from the warning
126
+ // rendered by the DeployingIntoManagedClusterWarning component
127
+ if (values.resourceSummary) {
128
+ errors.resourceSummary = { id: 'Cluster is managed' };
129
+ }
130
+
131
+ return errors;
132
+ }
133
+
134
+ private navigateToLatestServerGroup = () => {
135
+ const { values } = this.props.formik;
136
+ const { latestServerGroup } = this.state;
137
+
138
+ const params = {
139
+ provider: values.selectedProvider,
140
+ accountId: latestServerGroup.account,
141
+ region: latestServerGroup.region,
142
+ serverGroup: latestServerGroup.name,
143
+ };
144
+
145
+ const { $state } = ReactInjector;
146
+ if ($state.is('home.applications.application.insight.clusters')) {
147
+ $state.go('.serverGroup', params);
148
+ } else {
149
+ $state.go('^.serverGroup', params);
150
+ }
151
+ };
152
+
153
+ private stackChanged = (stack: string) => {
154
+ const { formik } = this.props;
155
+ formik.setFieldValue('stack', stack);
156
+ formik.values.clusterChanged(formik.values);
157
+ };
158
+
159
+ public componentWillReceiveProps(nextProps: IServerGroupBasicSettingsProps) {
160
+ this.setState(this.getStateFromProps(nextProps));
161
+ }
162
+
163
+ private strategyChanged = (values: ITitusServerGroupCommand, strategy: any) => {
164
+ values.onStrategyChange(values, strategy);
165
+ this.props.formik.setFieldValue('strategy', strategy.key);
166
+ };
167
+
168
+ private dockerValuesChanged = (dockerValues: any) => {
169
+ Object.keys(dockerValues).forEach((key) => {
170
+ this.props.formik.setFieldValue(key, dockerValues[key]);
171
+ });
172
+ };
173
+
174
+ private onStrategyFieldChange = (key: string, value: any) => {
175
+ this.props.formik.setFieldValue(key, value);
176
+ };
177
+
178
+ private onSubnetChange = () => {
179
+ const { setFieldValue, values } = this.props.formik;
180
+ values.subnetChanged(values);
181
+ setFieldValue('subnetType', values.subnetType);
182
+ };
183
+
184
+ public render() {
185
+ const { app, formik } = this.props;
186
+ const { errors, setFieldValue, values } = formik;
187
+ const { createsNewCluster, latestServerGroup, namePreview } = this.state;
188
+
189
+ const accounts = values.backingData.accounts;
190
+ const readOnlyFields = values.viewState.readOnlyFields || {};
191
+
192
+ const customImage = values.imageId && values.imageId !== '${trigger.properties.imageName}';
193
+
194
+ const defaultSubnetTypes = []
195
+ .concat(TitusProviderSettings.defaults?.subnetType)
196
+ .concat(AWSProviderSettings.defaults?.subnetType)
197
+ .filter((x) => !!x);
198
+
199
+ return (
200
+ <div className="container-fluid form-horizontal">
201
+ <DeployingIntoManagedClusterWarning app={app} formik={formik} />
202
+ <div className="form-group">
203
+ <div className="col-md-3 sm-label-right">Account</div>
204
+ <div className="col-md-7">
205
+ <AccountSelectInput
206
+ value={values.credentials}
207
+ onChange={(evt: any) => this.accountUpdated(evt.target.value)}
208
+ readOnly={readOnlyFields.credentials}
209
+ accounts={accounts}
210
+ provider="titus"
211
+ />
212
+ {values.credentials !== undefined && (
213
+ <div className="small">
214
+ Uses resources from the Amazon account{' '}
215
+ <AccountTag
216
+ account={
217
+ values.backingData.credentialsKeyedByAccount[values.credentials] &&
218
+ values.backingData.credentialsKeyedByAccount[values.credentials].awsAccount
219
+ }
220
+ />
221
+ </div>
222
+ )}
223
+ </div>
224
+ </div>
225
+ <RegionSelectField
226
+ readOnly={readOnlyFields.region}
227
+ labelColumns={3}
228
+ component={values}
229
+ field="region"
230
+ account={values.credentials}
231
+ regions={values.backingData.filtered.regions}
232
+ onChange={this.regionUpdated}
233
+ />
234
+ <SubnetSelectField
235
+ application={app}
236
+ component={values}
237
+ field="subnetType"
238
+ helpKey="titus.serverGroup.subnet"
239
+ labelColumns={3}
240
+ onChange={this.onSubnetChange}
241
+ region={values.region}
242
+ subnets={values.backingData.filtered.subnetPurposes}
243
+ defaultSubnetTypes={defaultSubnetTypes}
244
+ recommendedSubnetTypes={TitusProviderSettings.serverGroups?.recommendedSubnets}
245
+ showSubnetWarning={true}
246
+ />
247
+ <div className="form-group">
248
+ <div className="col-md-3 sm-label-right">
249
+ Stack <HelpField id="aws.serverGroup.stack" />
250
+ </div>
251
+ <div className="col-md-7">
252
+ <input
253
+ type="text"
254
+ className="form-control input-sm no-spel"
255
+ value={values.stack || ''}
256
+ onChange={(e) => this.stackChanged(e.target.value)}
257
+ />
258
+ </div>
259
+ </div>
260
+ {errors.stack && (
261
+ <div className="form-group row slide-in">
262
+ <div className="col-sm-9 col-sm-offset-2 error-message">
263
+ <span>{errors.stack}</span>
264
+ </div>
265
+ </div>
266
+ )}
267
+
268
+ <ServerGroupDetailsField app={app} formik={formik} />
269
+
270
+ {!values.viewState.disableImageSelection && (
271
+ <DockerImageAndTagSelector
272
+ specifyTagByRegex={false}
273
+ account={values.credentials}
274
+ digest={values.digest}
275
+ imageId={values.imageId}
276
+ organization={values.organization}
277
+ registry={values.registry}
278
+ repository={values.repository}
279
+ tag={values.tag}
280
+ showRegistry={false}
281
+ deferInitialization={values.deferredInitialization}
282
+ onChange={this.dockerValuesChanged}
283
+ />
284
+ )}
285
+ {values.viewState.disableImageSelection && customImage && (
286
+ <div className="form-group">
287
+ <div className="col-md-3 sm-label-right">
288
+ <b>Image</b> <HelpField id="titus.deploy.imageId" />
289
+ </div>
290
+ <div className="col-md-7 sp-padding-xs-yaxis">{values.imageId}</div>
291
+ </div>
292
+ )}
293
+ <div className="form-group">
294
+ <div className="col-md-3 sm-label-right">
295
+ <b>Entrypoint</b>
296
+ </div>
297
+ <div className="col-md-7">
298
+ <Field type="text" className="form-control input-sm no-spel" name="entryPoint" />
299
+ </div>
300
+ </div>
301
+
302
+ <div className="form-group">
303
+ <div className="col-md-3 sm-label-right">
304
+ Traffic <HelpField id="titus.serverGroup.traffic" />
305
+ </div>
306
+ <div className="col-md-7">
307
+ <div className="checkbox">
308
+ <label>
309
+ <input
310
+ type="checkbox"
311
+ checked={values.inService}
312
+ onChange={(e) => setFieldValue('inService', e.target.checked)}
313
+ disabled={values.strategy !== '' && values.strategy !== 'custom'}
314
+ />{' '}
315
+ Send client requests to new instances
316
+ </label>
317
+ </div>
318
+ </div>
319
+ </div>
320
+ {!values.viewState.disableStrategySelection && values.selectedProvider && (
321
+ <DeploymentStrategySelector
322
+ command={values}
323
+ onFieldChange={this.onStrategyFieldChange}
324
+ onStrategyChange={this.strategyChanged}
325
+ />
326
+ )}
327
+ {!values.viewState.hideClusterNamePreview && (
328
+ <ServerGroupNamePreview
329
+ createsNewCluster={createsNewCluster}
330
+ latestServerGroupName={latestServerGroup?.name}
331
+ mode={values.viewState.mode}
332
+ namePreview={namePreview}
333
+ navigateToLatestServerGroup={this.navigateToLatestServerGroup}
334
+ />
335
+ )}
336
+ </div>
337
+ );
338
+ }
339
+ }
@@ -0,0 +1,5 @@
1
+ .ServerGroupParameters {
2
+ .StandardFieldLayout_Label {
3
+ min-width: 160px;
4
+ }
5
+ }
@@ -0,0 +1,217 @@
1
+ import type { FormikProps } from 'formik';
2
+ import { intersection, set, union } from 'lodash';
3
+ import React from 'react';
4
+
5
+ import type { Application, IFormInputProps, IWizardPageComponent } from '@spinnaker/core';
6
+ import {
7
+ AccountTag,
8
+ CheckboxInput,
9
+ ChecklistInput,
10
+ FormikFormField,
11
+ FormValidator,
12
+ HelpField,
13
+ LayoutProvider,
14
+ MapEditorInput,
15
+ PlatformHealthOverrideInput,
16
+ robotToHuman,
17
+ SelectInput,
18
+ TextInput,
19
+ useFormInputValueMapper,
20
+ } from '@spinnaker/core';
21
+
22
+ import { TitusMapLayout } from './TitusMapLayout';
23
+ import type { ITitusServerGroupCommand } from '../../../configure/serverGroupConfiguration.service';
24
+ import { processesList } from '../../../details/serviceJobProcesses/ServiceJobProcesses';
25
+
26
+ import './ServerGroupParameters.less';
27
+
28
+ export interface IServerGroupParametersProps {
29
+ app: Application;
30
+ formik: FormikProps<ITitusServerGroupCommand>;
31
+ }
32
+
33
+ const migrationPolicyOptions = [
34
+ { label: 'System Default', value: 'systemDefault' },
35
+ { label: 'Self Managed', value: 'selfManaged' },
36
+ ];
37
+
38
+ function ServiceJobProcessesInput(props: IFormInputProps) {
39
+ const allProcesses = union(processesList, Object.keys(props.value));
40
+ // Map to a list of strings (formik -> input)
41
+ const mapToInputValue = (value: Record<string, boolean>) =>
42
+ Object.entries(value)
43
+ .filter(([_key, val]) => val === true)
44
+ .map(([key, _val]) => key);
45
+
46
+ // Map to an Object, but include all known processes (input -> formik)
47
+ const mapFromInputValue = (value: string[]): Record<string, boolean> =>
48
+ allProcesses.reduce((acc, process) => {
49
+ acc[process] = value.includes(process);
50
+ return acc;
51
+ }, {} as Record<string, boolean>);
52
+
53
+ const mappedProps = useFormInputValueMapper(props, mapToInputValue, mapFromInputValue);
54
+
55
+ const options = allProcesses.map((process) => {
56
+ return { label: robotToHuman(process), value: process };
57
+ });
58
+
59
+ return <ChecklistInput {...mappedProps} options={options} />;
60
+ }
61
+
62
+ export function IPv6CheckboxInput(props: IFormInputProps) {
63
+ const mappedProps = useFormInputValueMapper(
64
+ props,
65
+ (val: string) => val === 'true', // formik -> checkbox
66
+ (_val, e) => (e.target.checked ? 'true' : 'false'), // checkbox -> formik
67
+ );
68
+ return <CheckboxInput {...mappedProps} />;
69
+ }
70
+
71
+ const IamInstanceProfileInput = (props: IFormInputProps & { awsAccount: string; setDefaultIamProfile: () => void }) => {
72
+ const { awsAccount, setDefaultIamProfile, ...inputProps } = props;
73
+ return (
74
+ <div className="flex-container-h baseline margin-between-md">
75
+ <TextInput {...inputProps} />
76
+ {props.value ? (
77
+ <>
78
+ <span>in</span>
79
+ <AccountTag account={awsAccount} />
80
+ </>
81
+ ) : (
82
+ <button className="link" style={{ whiteSpace: 'nowrap' }} onClick={setDefaultIamProfile}>
83
+ Apply default value
84
+ </button>
85
+ )}
86
+ </div>
87
+ );
88
+ };
89
+
90
+ export class ServerGroupParameters
91
+ extends React.Component<IServerGroupParametersProps>
92
+ implements IWizardPageComponent<ITitusServerGroupCommand> {
93
+ constructor(props: IServerGroupParametersProps) {
94
+ super(props);
95
+ }
96
+
97
+ public validate(_values: ITitusServerGroupCommand) {
98
+ const validator = new FormValidator(_values);
99
+ validator.field('iamProfile', 'IAM Instance Profile').required();
100
+
101
+ const errors = validator.validateForm();
102
+
103
+ const duplicates = intersection(Object.keys(_values.constraints.soft), Object.keys(_values.constraints.hard));
104
+ duplicates.forEach((key) => {
105
+ set(errors, `constraints.soft.${key}`, `Constraint '${key}' must be either soft or hard, not both.`);
106
+ set(errors, `constraints.hard.${key}`, `Constraint '${key}' must be either soft or hard, not both.`);
107
+ });
108
+
109
+ Object.keys(_values.env || {})
110
+ .filter((key) => !key.startsWith('__MapEditorDuplicateKey'))
111
+ .forEach((key) => {
112
+ if (!key.match(/^[A-Za-z_].*/)) {
113
+ set(errors, `env.${key}`, 'Environment variable names must start with a letter or underscore');
114
+ } else if (!key.match(/[A-Za-z_][a-zA-Z0-9_]*$/)) {
115
+ set(errors, `env.${key}`, 'Environment variable names must contain only letter, numbers, or underscores');
116
+ }
117
+ });
118
+
119
+ return errors;
120
+ }
121
+
122
+ public render() {
123
+ const { app } = this.props;
124
+ const { setFieldValue, values } = this.props.formik;
125
+
126
+ const setDefaultIamProfile = () => setFieldValue('iamProfile', values.viewState.defaultIamProfile);
127
+ return (
128
+ <div className="ServerGroupParameters">
129
+ <FormikFormField
130
+ name="iamProfile"
131
+ label="IAM Instance Profile"
132
+ help={<HelpField id="titus.deploy.iamProfile" />}
133
+ input={(props) => (
134
+ <IamInstanceProfileInput
135
+ {...props}
136
+ awsAccount={values.backingData.credentialsKeyedByAccount[values.credentials]?.awsAccount}
137
+ setDefaultIamProfile={setDefaultIamProfile}
138
+ />
139
+ )}
140
+ />
141
+
142
+ <FormikFormField
143
+ name="capacityGroup"
144
+ label="Capacity Group"
145
+ help={<HelpField id="titus.deploy.capacityGroup" />}
146
+ input={(props) => <TextInput {...props} />}
147
+ />
148
+
149
+ <FormikFormField
150
+ name="migrationPolicy.type"
151
+ label="Migration Policy"
152
+ help={<HelpField id="titus.deploy.migrationPolicy" />}
153
+ input={(props) => <SelectInput options={migrationPolicyOptions} {...props} />}
154
+ />
155
+
156
+ <FormikFormField
157
+ name="serviceJobProcesses"
158
+ label="Service Job Processes"
159
+ input={(props) => <ServiceJobProcessesInput {...props} />}
160
+ />
161
+
162
+ <FormikFormField
163
+ name="containerAttributes['titusParameter.agent.assignIPv6Address']"
164
+ label="Associate IPv6 Address (Recommended)"
165
+ help={<HelpField id="serverGroup.ipv6" />}
166
+ input={(props) => <IPv6CheckboxInput {...props} />}
167
+ />
168
+
169
+ <hr />
170
+
171
+ <LayoutProvider value={TitusMapLayout}>
172
+ <FormikFormField
173
+ name="constraints.soft"
174
+ label="Soft Constraints "
175
+ help={<HelpField id="titus.deploy.softConstraints" />}
176
+ input={(props) => <MapEditorInput allowEmptyValues={true} {...props} />}
177
+ />
178
+
179
+ <FormikFormField
180
+ name="constraints.hard"
181
+ label="Hard Constraints"
182
+ help={<HelpField id="titus.deploy.hardConstraints" />}
183
+ input={(props) => <MapEditorInput allowEmptyValues={true} {...props} />}
184
+ />
185
+
186
+ <hr />
187
+
188
+ <FormikFormField
189
+ name="labels"
190
+ label="Job Attributes"
191
+ input={(props) => <MapEditorInput allowEmptyValues={true} {...props} />}
192
+ />
193
+
194
+ <FormikFormField
195
+ name="containerAttributes"
196
+ label="Container Attributes"
197
+ input={(props) => <MapEditorInput allowEmptyValues={true} {...props} />}
198
+ />
199
+
200
+ <FormikFormField
201
+ name="env"
202
+ label="Environment Variables"
203
+ input={(props) => <MapEditorInput allowEmptyValues={true} {...props} />}
204
+ />
205
+ </LayoutProvider>
206
+
207
+ {app.attributes.platformHealthOnlyShowOverride && (
208
+ <FormikFormField
209
+ name="interestingHealthProviderNames"
210
+ label="Task Completion"
211
+ input={(props) => <PlatformHealthOverrideInput {...props} platformHealthType="Titus" />}
212
+ />
213
+ )}
214
+ </div>
215
+ );
216
+ }
217
+ }