@spinnaker/titus 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 (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,226 @@
1
+ import { module } from 'angular';
2
+ import { isEqual } from 'lodash';
3
+ import prettyMilliseconds from 'pretty-ms';
4
+ import React from 'react';
5
+ import { react2angular } from 'react2angular';
6
+
7
+ import type { IServerGroupDetailsSectionProps } from '@spinnaker/core';
8
+ import { HelpField, withErrorBoundary } from '@spinnaker/core';
9
+
10
+ import { EditDisruptionBudgetModal } from './EditDisruptionBudgetModal';
11
+ import type { ITitusServerGroupCommand } from '../../configure/serverGroupConfiguration.service';
12
+ import { getDefaultJobDisruptionBudgetForApp } from '../../configure/serverGroupConfiguration.service';
13
+ import type { IFieldOption } from '../../configure/wizard/pages/disruptionBudget/JobDisruptionBudget';
14
+ import { DisruptionBudgetDescription } from '../../configure/wizard/pages/disruptionBudget/JobDisruptionBudget';
15
+ import { policyOptions } from '../../configure/wizard/pages/disruptionBudget/PolicyOptions';
16
+ import { rateOptions } from '../../configure/wizard/pages/disruptionBudget/RateOptions';
17
+ import type { IJobDisruptionBudget, ITitusServerGroup } from '../../../domain';
18
+ import { TitusReactInjector } from '../../../reactShims';
19
+
20
+ interface IDisruptionBudgetSection extends IServerGroupDetailsSectionProps {
21
+ serverGroup: ITitusServerGroup;
22
+ }
23
+
24
+ export class DisruptionBudgetSection extends React.Component<IDisruptionBudgetSection> {
25
+ private SectionHeading = ({
26
+ budget,
27
+ options,
28
+ label,
29
+ }: {
30
+ budget: IJobDisruptionBudget;
31
+ options: IFieldOption[];
32
+ label: string;
33
+ }): JSX.Element => {
34
+ const selected = options.find((o) => !!budget[o.field]);
35
+ if (!selected) {
36
+ return null;
37
+ }
38
+ return (
39
+ <div>
40
+ <div className="bold">{label}</div>
41
+ {selected.label} <HelpField content={selected.description} />
42
+ </div>
43
+ );
44
+ };
45
+
46
+ private Policy = ({ budget }: { budget: IJobDisruptionBudget }): JSX.Element => {
47
+ const { ParentheticalDuration } = this;
48
+ if (budget.availabilityPercentageLimit) {
49
+ return (
50
+ <div>
51
+ <div className="bold">Percentage of Healthy Containers</div>
52
+ {budget.availabilityPercentageLimit.percentageOfHealthyContainers} percent
53
+ </div>
54
+ );
55
+ }
56
+ if (budget.relocationLimit) {
57
+ return (
58
+ <div>
59
+ <div className="bold">Limit</div>
60
+ {budget.relocationLimit.limit} task{budget.relocationLimit.limit !== 1 && 's'}
61
+ </div>
62
+ );
63
+ }
64
+ if (budget.unhealthyTasksLimit) {
65
+ return (
66
+ <div>
67
+ <div className="bold">Limit of Unhealthy Containers</div>
68
+ {budget.unhealthyTasksLimit.limitOfUnhealthyContainers} container
69
+ {budget.unhealthyTasksLimit.limitOfUnhealthyContainers !== 1 && 's'}
70
+ </div>
71
+ );
72
+ }
73
+ if (budget.selfManaged) {
74
+ return (
75
+ <div>
76
+ <div className="bold">Relocation Time</div>
77
+ {budget.selfManaged.relocationTimeMs > 0 && (
78
+ <ParentheticalDuration durationMs={budget.selfManaged.relocationTimeMs} />
79
+ )}
80
+ {budget.selfManaged.relocationTimeMs === 0 && '0 ms (immediate)'}
81
+ </div>
82
+ );
83
+ }
84
+ return null;
85
+ };
86
+
87
+ // it's bad enough that we make users enter these values as milliseconds; let's not make them translate it
88
+ private ParentheticalDuration = ({ durationMs }: { durationMs: number }) => (
89
+ <span>
90
+ {durationMs} ms
91
+ {durationMs > 1000 && ` (${prettyMilliseconds(durationMs)})`}
92
+ </span>
93
+ );
94
+
95
+ private Rate = ({ budget }: { budget: IJobDisruptionBudget }): JSX.Element => {
96
+ const { ParentheticalDuration } = this;
97
+ if (budget.ratePerInterval) {
98
+ return (
99
+ <>
100
+ <div>
101
+ <div className="bold">Interval</div>
102
+ <ParentheticalDuration durationMs={budget.ratePerInterval.intervalMs} />
103
+ </div>
104
+ <div>
105
+ <div className="bold">Limit</div>
106
+ {budget.ratePerInterval.limitPerInterval} task{budget.ratePerInterval.limitPerInterval !== 1 && 's'}
107
+ </div>
108
+ </>
109
+ );
110
+ }
111
+ if (budget.ratePercentagePerInterval) {
112
+ return (
113
+ <>
114
+ <div>
115
+ <div className="bold">Interval</div>
116
+ <ParentheticalDuration durationMs={budget.ratePercentagePerInterval.intervalMs} />
117
+ </div>
118
+ <div>
119
+ <div className="bold">Percentage per Interval</div>
120
+ {budget.ratePercentagePerInterval.percentageLimitPerInterval} percent
121
+ </div>
122
+ </>
123
+ );
124
+ }
125
+ return null;
126
+ };
127
+
128
+ // given a collection of days, e.g. ['Monday', 'Tuesday', 'Wednesday', 'Saturday'], return 'Mon-Wed, Sat'
129
+ private groupedDays = (days: string[]): string => {
130
+ const allDays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
131
+ const sortedDays = days.sort((d1, d2) => allDays.indexOf(d1) - allDays.indexOf(d2));
132
+ const groups: string[] = [];
133
+ const currentGroup: string[] = [];
134
+ allDays.slice(allDays.indexOf(sortedDays[0])).forEach((d) => {
135
+ if (sortedDays.includes(d)) {
136
+ currentGroup.push(d);
137
+ } else {
138
+ if (currentGroup.length) {
139
+ groups.push(this.toDayRangeString(currentGroup));
140
+ currentGroup.length = 0;
141
+ }
142
+ }
143
+ });
144
+ if (currentGroup.length) {
145
+ groups.push(this.toDayRangeString(currentGroup));
146
+ }
147
+ return groups.join(', ');
148
+ };
149
+
150
+ private toDayRangeString(days: string[]) {
151
+ if (!days.length) {
152
+ return null;
153
+ }
154
+ if (days.length === 1) {
155
+ return days[0].substr(0, 3);
156
+ }
157
+ return `${days[0].substr(0, 3)}-${days[days.length - 1].substr(0, 3)}`;
158
+ }
159
+
160
+ private TimeWindows = ({ budget }: { budget: IJobDisruptionBudget }): JSX.Element => {
161
+ const { groupedDays } = this;
162
+ const hasWindows = budget.timeWindows && budget.timeWindows.length > 0;
163
+ return (
164
+ <>
165
+ <div className="bold">When can disruption occur?</div>
166
+ <div>
167
+ {hasWindows &&
168
+ budget.timeWindows.map((tw1, i1) => {
169
+ return tw1.hourlyTimeWindows.map((tw2, i2) => (
170
+ <div key={`${i1}.${i2}`}>
171
+ {groupedDays(tw1.days)}, {tw2.startHour}:00 - {tw2.endHour}:00 {tw1.timeZone}
172
+ </div>
173
+ ));
174
+ })}
175
+ {!hasWindows && 'Any time'}
176
+ </div>
177
+ </>
178
+ );
179
+ };
180
+
181
+ private editBudget = (): void => {
182
+ const { app, serverGroup } = this.props;
183
+ TitusReactInjector.titusServerGroupCommandBuilder
184
+ .buildServerGroupCommandFromExisting(app, serverGroup)
185
+ .then((command: ITitusServerGroupCommand) => {
186
+ EditDisruptionBudgetModal.show({ command, application: app, serverGroup });
187
+ });
188
+ };
189
+
190
+ public render() {
191
+ const { Policy, SectionHeading, Rate, TimeWindows } = this;
192
+ const serverGroup: ITitusServerGroup = this.props.serverGroup;
193
+ const hasDefaultMigrationPolicy =
194
+ !serverGroup.migrationPolicy || serverGroup.migrationPolicy.type === 'SystemDefault';
195
+ const defaultBudget = getDefaultJobDisruptionBudgetForApp(this.props.app);
196
+ const budget = serverGroup.disruptionBudget || defaultBudget;
197
+ const usingDefault = !hasDefaultMigrationPolicy && isEqual(budget, defaultBudget);
198
+ return (
199
+ <>
200
+ <DisruptionBudgetDescription />
201
+ {usingDefault && <div>(default policy)</div>}
202
+ <div className="bottom-border">
203
+ <SectionHeading budget={budget} options={policyOptions} label="Policy" />
204
+ <Policy budget={budget} />
205
+ </div>
206
+ <div className="bottom-border">
207
+ <SectionHeading budget={budget} options={rateOptions} label="Rate" />
208
+ <Rate budget={budget} />
209
+ </div>
210
+ <TimeWindows budget={budget} />
211
+ <div className="sp-margin-l-top">
212
+ <a className="clickable" onClick={this.editBudget}>
213
+ Edit Disruption Budget
214
+ </a>
215
+ </div>
216
+ </>
217
+ );
218
+ }
219
+ }
220
+
221
+ export const DISRUPTION_BUDGET_DETAILS_SECTION = 'spinnaker.titus.disruptionbudget.section';
222
+
223
+ module(DISRUPTION_BUDGET_DETAILS_SECTION, []).component(
224
+ 'titusDisruptionBudgetSection',
225
+ react2angular(withErrorBoundary(DisruptionBudgetSection, 'titusDisruptionBudgetSection'), ['serverGroup', 'app']),
226
+ );
@@ -0,0 +1,92 @@
1
+ import React from 'react';
2
+ import { Modal } from 'react-bootstrap';
3
+
4
+ import type { Application, IJob, IModalComponentProps } from '@spinnaker/core';
5
+ import {
6
+ ModalClose,
7
+ ReactModal,
8
+ SpinFormik,
9
+ SubmitButton,
10
+ TaskExecutor,
11
+ TaskMonitor,
12
+ TaskMonitorWrapper,
13
+ } from '@spinnaker/core';
14
+
15
+ import type { ITitusServerGroupCommand } from '../../configure/serverGroupConfiguration.service';
16
+ import { JobDisruptionBudget } from '../../configure/wizard/pages/disruptionBudget/JobDisruptionBudget';
17
+ import type { ITitusServerGroup } from '../../../domain';
18
+
19
+ export interface IEditDisruptionBudgetModalProps extends IModalComponentProps {
20
+ application: Application;
21
+ command: ITitusServerGroupCommand;
22
+ serverGroup: ITitusServerGroup;
23
+ }
24
+
25
+ export class EditDisruptionBudgetModal extends React.Component<IEditDisruptionBudgetModalProps> {
26
+ public static show(props: IEditDisruptionBudgetModalProps): Promise<void> {
27
+ return ReactModal.show(EditDisruptionBudgetModal, props);
28
+ }
29
+
30
+ private submit(values: ITitusServerGroupCommand, taskMonitor: TaskMonitor): void {
31
+ const { application, serverGroup } = this.props;
32
+ const job: IJob = {
33
+ type: 'upsertDisruptionBudget',
34
+ cloudProvider: 'titus',
35
+ credentials: serverGroup.account,
36
+ region: serverGroup.region,
37
+ jobId: serverGroup.id,
38
+ disruptionBudget: values.disruptionBudget,
39
+ };
40
+
41
+ taskMonitor.submit(() =>
42
+ TaskExecutor.executeTask({
43
+ job: [job],
44
+ application,
45
+ description: `Update Disruption Budget for ${serverGroup.name}`,
46
+ }),
47
+ );
48
+ }
49
+
50
+ public render() {
51
+ const { application, command, dismissModal } = this.props;
52
+ const taskMonitor = new TaskMonitor({
53
+ application,
54
+ title: 'Updating Job Disruption Budget',
55
+ modalInstance: TaskMonitor.modalInstanceEmulation(() => dismissModal()),
56
+ onTaskComplete: () => application.serverGroups.refresh(),
57
+ });
58
+
59
+ return (
60
+ <>
61
+ <TaskMonitorWrapper monitor={taskMonitor} />
62
+ <SpinFormik<ITitusServerGroupCommand>
63
+ initialValues={command}
64
+ onSubmit={(values: ITitusServerGroupCommand) => this.submit(values, taskMonitor)}
65
+ render={(formik) => (
66
+ <>
67
+ <ModalClose dismiss={dismissModal} />
68
+ <Modal.Header>
69
+ <Modal.Title>Update Disruption Budget</Modal.Title>
70
+ </Modal.Header>
71
+ <Modal.Body>
72
+ <JobDisruptionBudget formik={formik} app={application} />
73
+ </Modal.Body>
74
+ <Modal.Footer>
75
+ <button className="btn btn-default" onClick={dismissModal} type="button">
76
+ Cancel
77
+ </button>
78
+ <SubmitButton
79
+ onClick={() => this.submit(formik.values, taskMonitor)}
80
+ isDisabled={!formik.isValid}
81
+ isFormSubmit={true}
82
+ submitting={false}
83
+ label="Update Budget"
84
+ />
85
+ </Modal.Footer>
86
+ </>
87
+ )}
88
+ />
89
+ </>
90
+ );
91
+ }
92
+ }
@@ -0,0 +1,5 @@
1
+ export * from './TitusCapacityDetailsSection';
2
+ export * from './TitusSecurityGroups';
3
+ export * from './resize/TitusResizeServerGroupModal';
4
+ export * from './scalingActivity/TitusScalingActivitiesModal';
5
+ export * from './scalingPolicy';
@@ -0,0 +1,12 @@
1
+ import { module } from 'angular';
2
+ import { react2angular } from 'react2angular';
3
+
4
+ import { withErrorBoundary } from '@spinnaker/core';
5
+
6
+ import { TitusLaunchConfigSection } from './TitusLaunchConfigSection';
7
+
8
+ export const TITUS_SERVERGROUP_DETAILS_LAUNCHCONFIGSECTION = 'titus.servergroup.details.launchConfigSection';
9
+ module(TITUS_SERVERGROUP_DETAILS_LAUNCHCONFIGSECTION, []).component(
10
+ 'titusLaunchConfigSection',
11
+ react2angular(withErrorBoundary(TitusLaunchConfigSection, 'titusLaunchConfigSection'), ['serverGroup']),
12
+ );
@@ -0,0 +1,302 @@
1
+ import type { FormikContext } from 'formik';
2
+ import { Form } from 'formik';
3
+ import React from 'react';
4
+ import { Modal } from 'react-bootstrap';
5
+
6
+ import type { Application, ICapacity, IModalComponentProps } from '@spinnaker/core';
7
+ import {
8
+ FormikFormField,
9
+ MinMaxDesiredChanges,
10
+ ModalClose,
11
+ NumberInput,
12
+ PlatformHealthOverride,
13
+ ReactInjector,
14
+ SpinFormik,
15
+ TaskMonitorWrapper,
16
+ UserVerification,
17
+ ValidationMessage,
18
+ } from '@spinnaker/core';
19
+ import type { ITitusServerGroup } from '../../../domain';
20
+
21
+ import { useTaskMonitor } from './useTaskMonitor';
22
+
23
+ const { useState, useEffect, useMemo } = React;
24
+
25
+ export interface ITitusResizeServerGroupModalProps extends IModalComponentProps {
26
+ application: Application;
27
+ serverGroup: ITitusServerGroup;
28
+ }
29
+
30
+ interface ITitusResizeServerGroupCommand {
31
+ capacity: ICapacity;
32
+ serverGroupName: string;
33
+ instances: number;
34
+ interestingHealthProviderNames: string[];
35
+ region: string;
36
+ }
37
+
38
+ function surfacedErrorMessage(formik: FormikContext<ITitusResizeServerGroupCommand>) {
39
+ const capacityErrors = formik.errors.capacity || ({} as any);
40
+ const { min, max, desired } = capacityErrors;
41
+ return [min, max, desired].find((x) => !!x);
42
+ }
43
+
44
+ function SimpleMode({ formik, serverGroup, toggleMode }: IAdvancedModeProps) {
45
+ useEffect(() => {
46
+ formik.setFieldValue('capacity.min', formik.values.capacity.desired);
47
+ formik.setFieldValue('capacity.max', formik.values.capacity.desired);
48
+ }, [formik.values.capacity.desired]);
49
+
50
+ const errorMessage = surfacedErrorMessage(formik);
51
+
52
+ return (
53
+ <div>
54
+ <p>Sets min, max, and desired instance counts to the same value.</p>
55
+
56
+ <p>
57
+ To allow autoscaling, use the{' '}
58
+ <a className="clickable" onClick={toggleMode}>
59
+ Advanced Mode
60
+ </a>
61
+ .
62
+ </p>
63
+
64
+ <div className="form-group row">
65
+ <div className="col-md-3 sm-label-right">Current size</div>
66
+ <div className="col-md-4">
67
+ <div className="horizontal middle">
68
+ <input
69
+ type="number"
70
+ className="NumberInput form-control"
71
+ value={serverGroup.capacity.desired}
72
+ disabled={true}
73
+ />
74
+ <div className="sp-padding-xs-xaxis">instances</div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <div className="form-group">
80
+ <div className="col-md-3 sm-label-right">Resize to</div>
81
+ <div className="col-md-4">
82
+ <div className="horizontal middle">
83
+ <FormikFormField
84
+ name="capacity.desired"
85
+ input={(props) => <NumberInput {...props} min={0} />}
86
+ layout={({ input }) => <>{input}</>}
87
+ touched={true}
88
+ onChange={() => {}}
89
+ />
90
+ <div className="sp-padding-xs-xaxis">instances</div>
91
+ </div>
92
+ </div>
93
+ </div>
94
+
95
+ {!!errorMessage && (
96
+ <div className="col-md-offset-3 col-md-9">
97
+ <ValidationMessage message={errorMessage} type="error" />
98
+ </div>
99
+ )}
100
+
101
+ <div className="form-group">
102
+ <div className="col-md-3 sm-label-right">Changes</div>
103
+ <div className="col-md-9 sm-control-field">
104
+ <MinMaxDesiredChanges current={serverGroup.capacity} next={formik.values.capacity} />
105
+ </div>
106
+ </div>
107
+ </div>
108
+ );
109
+ }
110
+
111
+ interface IAdvancedModeProps {
112
+ formik: FormikContext<ITitusResizeServerGroupCommand>;
113
+ serverGroup: ITitusServerGroup;
114
+ toggleMode: () => void;
115
+ }
116
+
117
+ function AdvancedMode({ formik, serverGroup, toggleMode }: IAdvancedModeProps) {
118
+ const { min, max } = formik.values.capacity || ({} as any);
119
+
120
+ const DisabledNumberField = ({ value }: { value: string | number }) => (
121
+ <div className="col-md-2">
122
+ <input className="NumberInput form-control" type="number" disabled={true} value={value} />
123
+ </div>
124
+ );
125
+ const errorMessage = surfacedErrorMessage(formik);
126
+
127
+ return (
128
+ <div>
129
+ <p>Sets up auto-scaling for this server group.</p>
130
+ <p>
131
+ To disable auto-scaling, use the{' '}
132
+ <a className="clickable" onClick={toggleMode}>
133
+ Simple Mode
134
+ </a>
135
+ .
136
+ </p>
137
+
138
+ <div className="form-group bold">
139
+ <div className="col-md-2 col-md-offset-3">Min</div>
140
+ <div className="col-md-2">Max</div>
141
+ <div className="col-md-2">Desired</div>
142
+ </div>
143
+
144
+ <div className="form-group">
145
+ <div className="col-md-3 sm-label-right">Current</div>
146
+ <DisabledNumberField value={serverGroup.capacity.min} />
147
+ <DisabledNumberField value={serverGroup.capacity.max} />
148
+ <DisabledNumberField value={serverGroup.capacity.desired} />
149
+ </div>
150
+
151
+ <div className="form-group">
152
+ <div className="col-md-3 sm-label-right">Resize to</div>
153
+ <div className="col-md-2">
154
+ <FormikFormField
155
+ name="capacity.min"
156
+ input={(props) => <NumberInput {...props} min={0} max={max} />}
157
+ layout={({ input }) => <>{input}</>}
158
+ touched={true}
159
+ />
160
+ </div>
161
+
162
+ <div className="col-md-2">
163
+ <FormikFormField
164
+ name="capacity.max"
165
+ input={(props) => <NumberInput {...props} min={min} />}
166
+ layout={({ input }) => <>{input}</>}
167
+ touched={true}
168
+ />
169
+ </div>
170
+
171
+ <div className="col-md-2">
172
+ <FormikFormField
173
+ name="capacity.desired"
174
+ input={(props) => <NumberInput {...props} min={min} max={max} />}
175
+ layout={({ input }) => <>{input}</>}
176
+ touched={true}
177
+ />
178
+ </div>
179
+ </div>
180
+
181
+ {!!errorMessage && (
182
+ <div className="col-md-offset-3 col-md-9">
183
+ <ValidationMessage message={errorMessage} type="error" />
184
+ </div>
185
+ )}
186
+
187
+ <div className="form-group">
188
+ <div className="col-md-3 sm-label-right">Changes</div>
189
+ <div className="col-md-9 sm-control-field">
190
+ <MinMaxDesiredChanges current={serverGroup.capacity} next={formik.values.capacity} />
191
+ </div>
192
+ </div>
193
+ </div>
194
+ );
195
+ }
196
+
197
+ function validateResizeCommand(values: ITitusResizeServerGroupCommand) {
198
+ const { min, max, desired } = values.capacity;
199
+ const capacityErrors = {} as any;
200
+
201
+ // try to only show one error message at a time
202
+ if (min > max) {
203
+ capacityErrors.min = capacityErrors.max = 'Min cannot be larger than Max';
204
+ } else if (desired < min) {
205
+ capacityErrors.desired = capacityErrors.min = 'Desired cannot be smaller than Min';
206
+ } else if (desired > max) {
207
+ capacityErrors.desired = capacityErrors.max = 'Desired cannot be larger than Max';
208
+ }
209
+
210
+ if (Object.keys(capacityErrors).length) {
211
+ return { capacity: capacityErrors };
212
+ }
213
+
214
+ return {};
215
+ }
216
+
217
+ export function TitusResizeServerGroupModal(props: ITitusResizeServerGroupModalProps) {
218
+ const { serverGroup, application, dismissModal } = props;
219
+
220
+ const initialAdvancedMode = useMemo(() => {
221
+ const { min, max, desired } = serverGroup.capacity;
222
+ return desired !== max || desired !== min;
223
+ }, []);
224
+ const [advancedMode, setAdvancedMode] = useState(initialAdvancedMode);
225
+
226
+ const platformHealthOnlyShowOverride =
227
+ application.attributes && application.attributes.platformHealthOnlyShowOverride;
228
+ const [verified, setVerified] = useState<boolean>();
229
+
230
+ const taskMonitor = useTaskMonitor(
231
+ {
232
+ application,
233
+ title: `Resizing ${serverGroup.name}`,
234
+ onTaskComplete: () => application.getDataSource('serverGroups').refresh(true),
235
+ },
236
+ dismissModal,
237
+ );
238
+ const submit = (command: ITitusResizeServerGroupCommand) =>
239
+ taskMonitor.submit(() => ReactInjector.serverGroupWriter.resizeServerGroup(serverGroup, application, command));
240
+
241
+ const initialValues = { capacity: serverGroup.capacity } as ITitusResizeServerGroupCommand;
242
+
243
+ return (
244
+ <>
245
+ <TaskMonitorWrapper monitor={taskMonitor} />
246
+
247
+ <SpinFormik<ITitusResizeServerGroupCommand>
248
+ initialValues={initialValues}
249
+ validate={validateResizeCommand}
250
+ onSubmit={submit}
251
+ render={(formik) => {
252
+ return (
253
+ <>
254
+ <ModalClose dismiss={dismissModal} />
255
+ <Modal.Header>
256
+ <Modal.Title>Resize {serverGroup.name}</Modal.Title>
257
+ </Modal.Header>
258
+
259
+ <Modal.Body>
260
+ <Form className="form-horizontal">
261
+ {advancedMode ? (
262
+ <AdvancedMode formik={formik} serverGroup={serverGroup} toggleMode={() => setAdvancedMode(false)} />
263
+ ) : (
264
+ <SimpleMode formik={formik} serverGroup={serverGroup} toggleMode={() => setAdvancedMode(true)} />
265
+ )}
266
+
267
+ {platformHealthOnlyShowOverride && (
268
+ <PlatformHealthOverride
269
+ interestingHealthProviderNames={formik.values.interestingHealthProviderNames}
270
+ platformHealthType="Titus"
271
+ showHelpDetails={true}
272
+ onChange={(names) =>
273
+ formik.setFieldValue('interestingHealthProviderNames', names ? names : undefined)
274
+ }
275
+ />
276
+ )}
277
+ </Form>
278
+ </Modal.Body>
279
+
280
+ <Modal.Footer>
281
+ <UserVerification account={serverGroup.account} onValidChange={setVerified} />
282
+
283
+ <button className="btn btn-default" onClick={dismissModal}>
284
+ Cancel
285
+ </button>
286
+
287
+ <button
288
+ type="submit"
289
+ disabled={!verified || !formik.isValid}
290
+ className="btn btn-primary"
291
+ onClick={() => submit(formik.values)}
292
+ >
293
+ Submit
294
+ </button>
295
+ </Modal.Footer>
296
+ </>
297
+ );
298
+ }}
299
+ />
300
+ </>
301
+ );
302
+ }
@@ -0,0 +1,30 @@
1
+ import { useMemo } from 'react';
2
+ import type { ITaskMonitorConfig } from '@spinnaker/core';
3
+ import { TaskMonitor } from '@spinnaker/core';
4
+
5
+ /**
6
+ * React hook that returns a TaskMonitor
7
+ *
8
+ * @param config a ITaskMonitorConfig
9
+ * @param dismissModal a function that closes the modal enclosing the task monitor
10
+ *
11
+ * Example:
12
+ *
13
+ * function MyComponent(props) {
14
+ * const { application, serverGroup } = props;
15
+ * const title = `Resize ${serverGroup.name}`;
16
+ * const taskMonitor = useTaskMonitor({ application, title });
17
+ *
18
+ * return (
19
+ * <>
20
+ * <TaskMonitorWrapper taskMonitor={taskMonitor}>
21
+ * <form onSubmit={() => taskMonitor.submit(() => API.runSomeTask())}>
22
+ * </>
23
+ * )
24
+ * }
25
+ *
26
+ */
27
+ export const useTaskMonitor = (config: ITaskMonitorConfig, dismissModal: () => void) => {
28
+ const modalInstance = TaskMonitor.modalInstanceEmulation(() => dismissModal());
29
+ return useMemo(() => new TaskMonitor({ modalInstance, ...config }), [config.application, config.title]);
30
+ };