@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.
- package/CHANGELOG.md +2661 -0
- package/LICENSE.txt +203 -0
- package/dist/domain/IJobDisruptionBudget.d.ts +35 -0
- package/dist/domain/ITitusCredentials.d.ts +5 -0
- package/dist/domain/ITitusInstance.d.ts +28 -0
- package/dist/domain/ITitusScalingPolicy.d.ts +4 -0
- package/dist/domain/ITitusServerGroup.d.ts +52 -0
- package/dist/domain/ITitusServiceJobProcesses.d.ts +3 -0
- package/dist/domain/index.d.ts +5 -0
- package/dist/help/titus.help.d.ts +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +12828 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/details/TitusInstanceDetails.d.ts +25 -0
- package/dist/instance/details/TitusInstanceDns.d.ts +8 -0
- package/dist/instance/details/TitusInstanceInformation.d.ts +7 -0
- package/dist/instance/details/index.d.ts +4 -0
- package/dist/instance/details/titusInstanceDetailsUtils.d.ts +14 -0
- package/dist/pipeline/stages/cloneServerGroup/cloneServerGroupExecutionDetails.controller.d.ts +2 -0
- package/dist/pipeline/stages/cloneServerGroup/titusCloneServerGroupStage.d.ts +2 -0
- package/dist/pipeline/stages/destroyAsg/titusDestroyAsgStage.d.ts +2 -0
- package/dist/pipeline/stages/disableAsg/titusDisableAsgStage.d.ts +2 -0
- package/dist/pipeline/stages/disableCluster/titusDisableClusterStage.d.ts +2 -0
- package/dist/pipeline/stages/enableAsg/titusEnableAsgStage.d.ts +2 -0
- package/dist/pipeline/stages/findAmi/titusFindAmiStage.d.ts +2 -0
- package/dist/pipeline/stages/resizeAsg/titusResizeAsgStage.d.ts +2 -0
- package/dist/pipeline/stages/runJob/RunJobExecutionDetails.d.ts +16 -0
- package/dist/pipeline/stages/runJob/TitusRunJobStageConfig.d.ts +25 -0
- package/dist/pipeline/stages/runJob/TitusSecurityGroupPicker.d.ts +34 -0
- package/dist/pipeline/stages/runJob/titusRunJobStage.d.ts +1 -0
- package/dist/pipeline/stages/scaleDownCluster/titusScaleDownClusterStage.d.ts +2 -0
- package/dist/pipeline/stages/shrinkCluster/titusShrinkClusterStage.d.ts +2 -0
- package/dist/reactShims/index.d.ts +1 -0
- package/dist/reactShims/titus.react.injector.d.ts +11 -0
- package/dist/reactShims/titus.react.module.d.ts +1 -0
- package/dist/securityGroup/securityGroup.read.service.d.ts +2 -0
- package/dist/serverGroup/configure/ServerGroupCommandBuilder.d.ts +2 -0
- package/dist/serverGroup/configure/serverGroup.configure.titus.module.d.ts +2 -0
- package/dist/serverGroup/configure/serverGroupConfiguration.service.d.ts +89 -0
- package/dist/serverGroup/configure/wizard/TitusCloneServerGroupModal.d.ts +31 -0
- package/dist/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.d.ts +28 -0
- package/dist/serverGroup/configure/wizard/pages/ServerGroupParameters.d.ts +15 -0
- package/dist/serverGroup/configure/wizard/pages/ServerGroupResources.d.ts +11 -0
- package/dist/serverGroup/configure/wizard/pages/TitusMapLayout.d.ts +4 -0
- package/dist/serverGroup/configure/wizard/pages/disruptionBudget/JobDisruptionBudget.d.ts +40 -0
- package/dist/serverGroup/configure/wizard/pages/disruptionBudget/PolicyOptions.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/pages/disruptionBudget/RateOptions.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/pages/disruptionBudget/WindowPicker.d.ts +23 -0
- package/dist/serverGroup/configure/wizard/pages/index.d.ts +4 -0
- package/dist/serverGroup/details/TitusCapacityDetailsSection.d.ts +11 -0
- package/dist/serverGroup/details/TitusLaunchConfigSection.d.ts +9 -0
- package/dist/serverGroup/details/TitusPackageDetailsSection.d.ts +6 -0
- package/dist/serverGroup/details/TitusSecurityGroups.d.ts +17 -0
- package/dist/serverGroup/details/capacityDetailsSection.component.d.ts +1 -0
- package/dist/serverGroup/details/disruptionBudget/DisruptionBudgetSection.d.ts +19 -0
- package/dist/serverGroup/details/disruptionBudget/EditDisruptionBudgetModal.d.ts +14 -0
- package/dist/serverGroup/details/index.d.ts +5 -0
- package/dist/serverGroup/details/launchConfigSection.component.d.ts +1 -0
- package/dist/serverGroup/details/resize/TitusResizeServerGroupModal.d.ts +8 -0
- package/dist/serverGroup/details/resize/useTaskMonitor.d.ts +25 -0
- package/dist/serverGroup/details/rollback/rollbackServerGroup.controller.d.ts +2 -0
- package/dist/serverGroup/details/scalingActivity/TitusScalingActivitiesModal.d.ts +7 -0
- package/dist/serverGroup/details/scalingPolicy/CreateScalingPolicyButton.d.ts +21 -0
- package/dist/serverGroup/details/scalingPolicy/TitusCustomScalingPolicy.d.ts +7 -0
- package/dist/serverGroup/details/scalingPolicy/createScalingPolicyButton.component.d.ts +1 -0
- package/dist/serverGroup/details/scalingPolicy/index.d.ts +3 -0
- package/dist/serverGroup/details/scalingPolicy/scalingPolicy.module.d.ts +1 -0
- package/dist/serverGroup/details/scalingPolicy/targetTracking/TitusTargetTrackingChart.d.ts +9 -0
- package/dist/serverGroup/details/scalingPolicy/targetTracking/UpsertTargetTrackingModal.d.ts +10 -0
- package/dist/serverGroup/details/scalingPolicy/titusCustomScalingPolicy.component.d.ts +1 -0
- package/dist/serverGroup/details/scalingPolicy/upsert/TitusScalingPolicyCommandBuilderService.d.ts +10 -0
- package/dist/serverGroup/details/scalingPolicy/upsert/UpsertScalingPolicyModal.d.ts +10 -0
- package/dist/serverGroup/details/sections/ITitusServerGroupDetailsSectionProps.d.ts +5 -0
- package/dist/serverGroup/details/serverGroupDetails.titus.controller.d.ts +2 -0
- package/dist/serverGroup/details/serviceJobProcesses/ServiceJobProcesses.d.ts +3 -0
- package/dist/serverGroup/details/serviceJobProcesses/ServiceJobProcessesSection.d.ts +7 -0
- package/dist/serverGroup/details/titusPackageDetailsSection.component.d.ts +1 -0
- package/dist/serverGroup/details/titusSecurityGroups.component.d.ts +1 -0
- package/dist/serverGroup/serverGroup.transformer.d.ts +2 -0
- package/dist/titus.module.d.ts +5 -0
- package/dist/titus.settings.d.ts +14 -0
- package/dist/validation/ApplicationNameValidator.d.ts +1 -0
- package/package.json +52 -0
- package/src/domain/IJobDisruptionBudget.ts +19 -0
- package/src/domain/ITitusCredentials.ts +6 -0
- package/src/domain/ITitusInstance.ts +30 -0
- package/src/domain/ITitusScalingPolicy.ts +5 -0
- package/src/domain/ITitusServerGroup.ts +56 -0
- package/src/domain/ITitusServiceJobProcesses.ts +3 -0
- package/src/domain/index.ts +5 -0
- package/src/help/titus.help.ts +99 -0
- package/src/index.ts +6 -0
- package/src/instance/details/TitusInstanceDetails.tsx +234 -0
- package/src/instance/details/TitusInstanceDns.tsx +40 -0
- package/src/instance/details/TitusInstanceInformation.tsx +42 -0
- package/src/instance/details/index.ts +4 -0
- package/src/instance/details/titusInstanceDetailsUtils.spec.ts +124 -0
- package/src/instance/details/titusInstanceDetailsUtils.ts +181 -0
- package/src/logo/titus.logo.less +5 -0
- package/src/logo/titus.logo.png +0 -0
- package/src/logo/titus.logo.svg +7 -0
- package/src/pipeline/stages/cloneServerGroup/cloneServerGroupExecutionDetails.controller.js +66 -0
- package/src/pipeline/stages/cloneServerGroup/cloneServerGroupExecutionDetails.html +46 -0
- package/src/pipeline/stages/cloneServerGroup/cloneServerGroupStage.html +106 -0
- package/src/pipeline/stages/cloneServerGroup/cloneServerGroupStepLabel.html +1 -0
- package/src/pipeline/stages/cloneServerGroup/titusCloneServerGroupStage.js +104 -0
- package/src/pipeline/stages/destroyAsg/destroyAsgStage.html +9 -0
- package/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html +1 -0
- package/src/pipeline/stages/destroyAsg/titusDestroyAsgStage.js +65 -0
- package/src/pipeline/stages/disableAsg/disableAsgStage.html +11 -0
- package/src/pipeline/stages/disableAsg/disableAsgStepLabel.html +1 -0
- package/src/pipeline/stages/disableAsg/titusDisableAsgStage.js +69 -0
- package/src/pipeline/stages/disableCluster/disableClusterStage.html +26 -0
- package/src/pipeline/stages/disableCluster/titusDisableClusterStage.js +85 -0
- package/src/pipeline/stages/enableAsg/enableAsgStage.html +11 -0
- package/src/pipeline/stages/enableAsg/enableAsgStepLabel.html +1 -0
- package/src/pipeline/stages/enableAsg/titusEnableAsgStage.js +73 -0
- package/src/pipeline/stages/findAmi/findAmiStage.html +24 -0
- package/src/pipeline/stages/findAmi/titusFindAmiStage.js +79 -0
- package/src/pipeline/stages/resizeAsg/resizeAsgStage.html +99 -0
- package/src/pipeline/stages/resizeAsg/resizeAsgStepLabel.html +1 -0
- package/src/pipeline/stages/resizeAsg/titusResizeAsgStage.js +125 -0
- package/src/pipeline/stages/runJob/RunJobExecutionDetails.tsx +165 -0
- package/src/pipeline/stages/runJob/TitusRunJobStageConfig.tsx +466 -0
- package/src/pipeline/stages/runJob/TitusSecurityGroupPicker.tsx +170 -0
- package/src/pipeline/stages/runJob/titusRunJobStage.ts +30 -0
- package/src/pipeline/stages/scaleDownCluster/scaleDownClusterStage.html +35 -0
- package/src/pipeline/stages/scaleDownCluster/titusScaleDownClusterStage.js +79 -0
- package/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html +34 -0
- package/src/pipeline/stages/shrinkCluster/titusShrinkClusterStage.js +73 -0
- package/src/reactShims/index.ts +1 -0
- package/src/reactShims/titus.react.injector.ts +17 -0
- package/src/reactShims/titus.react.module.ts +10 -0
- package/src/securityGroup/securityGroup.read.service.js +15 -0
- package/src/serverGroup/configure/ServerGroupCommandBuilder.js +247 -0
- package/src/serverGroup/configure/serverGroup.configure.titus.module.js +9 -0
- package/src/serverGroup/configure/serverGroupCommandBuilder.spec.js +63 -0
- package/src/serverGroup/configure/serverGroupConfiguration.service.ts +410 -0
- package/src/serverGroup/configure/wizard/TitusCloneServerGroupModal.tsx +293 -0
- package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.tsx +339 -0
- package/src/serverGroup/configure/wizard/pages/ServerGroupParameters.less +5 -0
- package/src/serverGroup/configure/wizard/pages/ServerGroupParameters.tsx +217 -0
- package/src/serverGroup/configure/wizard/pages/ServerGroupResources.tsx +200 -0
- package/src/serverGroup/configure/wizard/pages/TitusMapLayout.less +3 -0
- package/src/serverGroup/configure/wizard/pages/TitusMapLayout.tsx +29 -0
- package/src/serverGroup/configure/wizard/pages/disruptionBudget/JobDisruptionBudget.tsx +285 -0
- package/src/serverGroup/configure/wizard/pages/disruptionBudget/PolicyOptions.tsx +112 -0
- package/src/serverGroup/configure/wizard/pages/disruptionBudget/RateOptions.tsx +89 -0
- package/src/serverGroup/configure/wizard/pages/disruptionBudget/WindowPicker.tsx +202 -0
- package/src/serverGroup/configure/wizard/pages/index.ts +4 -0
- package/src/serverGroup/details/TitusCapacityDetailsSection.tsx +66 -0
- package/src/serverGroup/details/TitusLaunchConfigSection.tsx +35 -0
- package/src/serverGroup/details/TitusPackageDetailsSection.tsx +40 -0
- package/src/serverGroup/details/TitusSecurityGroups.tsx +107 -0
- package/src/serverGroup/details/capacityDetailsSection.component.ts +12 -0
- package/src/serverGroup/details/disruptionBudget/DisruptionBudgetSection.tsx +226 -0
- package/src/serverGroup/details/disruptionBudget/EditDisruptionBudgetModal.tsx +92 -0
- package/src/serverGroup/details/index.ts +5 -0
- package/src/serverGroup/details/launchConfigSection.component.ts +12 -0
- package/src/serverGroup/details/resize/TitusResizeServerGroupModal.tsx +302 -0
- package/src/serverGroup/details/resize/useTaskMonitor.ts +30 -0
- package/src/serverGroup/details/rollback/rollbackServerGroup.controller.js +149 -0
- package/src/serverGroup/details/rollback/rollbackServerGroup.html +133 -0
- package/src/serverGroup/details/scalingActivity/TitusScalingActivitiesModal.tsx +81 -0
- package/src/serverGroup/details/scalingPolicy/CreateScalingPolicyButton.tsx +102 -0
- package/src/serverGroup/details/scalingPolicy/TitusCustomScalingPolicy.tsx +13 -0
- package/src/serverGroup/details/scalingPolicy/createScalingPolicyButton.component.ts +15 -0
- package/src/serverGroup/details/scalingPolicy/index.js +3 -0
- package/src/serverGroup/details/scalingPolicy/scalingPolicy.module.ts +7 -0
- package/src/serverGroup/details/scalingPolicy/targetTracking/TitusTargetTrackingChart.tsx +57 -0
- package/src/serverGroup/details/scalingPolicy/targetTracking/UpsertTargetTrackingModal.tsx +82 -0
- package/src/serverGroup/details/scalingPolicy/titusCustomScalingPolicy.component.ts +17 -0
- package/src/serverGroup/details/scalingPolicy/upsert/TitusScalingPolicyCommandBuilderService.ts +115 -0
- package/src/serverGroup/details/scalingPolicy/upsert/UpsertScalingPolicyModal.tsx +157 -0
- package/src/serverGroup/details/sections/ITitusServerGroupDetailsSectionProps.ts +6 -0
- package/src/serverGroup/details/serverGroupDetails.html +191 -0
- package/src/serverGroup/details/serverGroupDetails.titus.controller.js +457 -0
- package/src/serverGroup/details/serviceJobProcesses/ServiceJobProcesses.ts +12 -0
- package/src/serverGroup/details/serviceJobProcesses/ServiceJobProcessesSection.tsx +136 -0
- package/src/serverGroup/details/titusPackageDetailsSection.component.ts +12 -0
- package/src/serverGroup/details/titusSecurityGroups.component.ts +12 -0
- package/src/serverGroup/serverGroup.transformer.js +90 -0
- package/src/titus.module.ts +95 -0
- package/src/titus.settings.ts +22 -0
- 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,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
|
+
};
|