@spinnaker/appengine 2026.2.2 → 2026.3.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/dist/appengine.module.d.ts +7 -1
- package/dist/domain/index.d.ts +0 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/instance/details/AppengineInstanceDetails.d.ts +11 -0
- package/dist/loadBalancer/configure/AppengineCreateLoadBalancerModal.d.ts +29 -0
- package/dist/loadBalancer/details/AppengineLoadBalancerActions.d.ts +5 -0
- package/dist/loadBalancer/details/AppengineLoadBalancerDetailsSection.d.ts +3 -0
- package/dist/loadBalancer/details/index.d.ts +3 -0
- package/dist/loadBalancer/details/useAppengineLoadBalancerDetails.d.ts +3 -0
- package/dist/loadBalancer/transformer.d.ts +2 -6
- package/dist/pipeline/stages/AppengineExecutionDetails.d.ts +15 -0
- package/dist/pipeline/stages/AppengineServerGroupStageConfig.d.ts +12 -0
- package/dist/pipeline/stages/destroyAsg/appengineDestroyAsgStage.d.ts +1 -1
- package/dist/pipeline/stages/disableAsg/appengineDisableAsgStage.d.ts +1 -1
- package/dist/pipeline/stages/editLoadBalancer/AppengineEditLoadBalancerStageConfig.d.ts +3 -0
- package/dist/pipeline/stages/editLoadBalancer/appengineEditLoadBalancerStage.d.ts +1 -1
- package/dist/pipeline/stages/enableAsg/appengineEnableAsgStage.d.ts +1 -1
- package/dist/pipeline/stages/shrinkCluster/appengineShrinkClusterStage.d.ts +1 -1
- package/dist/pipeline/stages/startServerGroup/appengineStartServerGroupStage.d.ts +1 -1
- package/dist/pipeline/stages/stopServerGroup/appengineStopServerGroupStage.d.ts +1 -1
- package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +5 -9
- package/dist/serverGroup/configure/wizard/AppengineCloneServerGroupModal.d.ts +22 -0
- package/dist/serverGroup/details/AppengineServerGroupActions.d.ts +12 -0
- package/dist/serverGroup/details/AppengineServerGroupDetailsSection.d.ts +3 -0
- package/dist/serverGroup/details/appengineServerGroupDetailsGetter.d.ts +4 -0
- package/dist/serverGroup/details/index.d.ts +3 -0
- package/dist/serverGroup/transformer.d.ts +1 -5
- package/dist/serverGroup/writer/serverGroup.write.service.d.ts +15 -4
- package/package.json +4 -8
- package/src/appengine.module.spec.ts +194 -0
- package/src/appengine.module.ts +47 -58
- package/src/domain/index.ts +0 -1
- package/src/index.ts +1 -1
- package/src/instance/details/AppengineInstanceDetails.tsx +33 -0
- package/src/loadBalancer/configure/AppengineCreateLoadBalancerModal.tsx +342 -0
- package/src/loadBalancer/details/AppengineLoadBalancerActions.tsx +66 -0
- package/src/loadBalancer/details/AppengineLoadBalancerDetailsSection.tsx +42 -0
- package/src/loadBalancer/details/index.ts +3 -0
- package/src/loadBalancer/details/useAppengineLoadBalancerDetails.ts +37 -0
- package/src/loadBalancer/transformer.ts +3 -14
- package/src/pipeline/stages/AppengineExecutionDetails.tsx +90 -0
- package/src/pipeline/stages/AppengineServerGroupStageConfig.tsx +190 -0
- package/src/pipeline/stages/destroyAsg/appengineDestroyAsgStage.ts +17 -43
- package/src/pipeline/stages/disableAsg/appengineDisableAsgStage.ts +18 -53
- package/src/pipeline/stages/editLoadBalancer/AppengineEditLoadBalancerStageConfig.tsx +94 -0
- package/src/pipeline/stages/editLoadBalancer/appengineEditLoadBalancerStage.ts +14 -74
- package/src/pipeline/stages/enableAsg/appengineEnableAsgStage.ts +13 -48
- package/src/pipeline/stages/shrinkCluster/appengineShrinkClusterStage.ts +13 -61
- package/src/pipeline/stages/startServerGroup/appengineStartServerGroupStage.ts +17 -54
- package/src/pipeline/stages/stopServerGroup/appengineStopServerGroupStage.ts +17 -54
- package/src/serverGroup/configure/serverGroupCommandBuilder.service.ts +20 -19
- package/src/serverGroup/configure/wizard/AppengineCloneServerGroupModal.tsx +119 -0
- package/src/serverGroup/configure/wizard/serverGroupWizard.less +0 -8
- package/src/serverGroup/details/AppengineServerGroupActions.tsx +245 -0
- package/src/serverGroup/details/AppengineServerGroupDetailsSection.tsx +27 -0
- package/src/serverGroup/details/appengineServerGroupDetailsGetter.ts +62 -0
- package/src/serverGroup/details/index.ts +3 -0
- package/src/serverGroup/transformer.ts +3 -15
- package/src/serverGroup/writer/serverGroup.write.service.ts +57 -8
- package/dist/common/componentUrlDetails.component.d.ts +0 -1
- package/dist/common/conditionalDescriptionListItem.component.d.ts +0 -1
- package/dist/common/loadBalancerMessage.component.d.ts +0 -1
- package/dist/domain/IAppengineStageScope.d.ts +0 -18
- package/dist/instance/details/details.controller.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/advancedSettings.component.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/allocationConfigurationRow.component.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/basicSettings.component.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/wizard.controller.d.ts +0 -2
- package/dist/loadBalancer/details/details.controller.d.ts +0 -1
- package/dist/loadBalancer/loadBalancer.module.d.ts +0 -1
- package/dist/pipeline/pipeline.module.d.ts +0 -1
- package/dist/pipeline/stages/appengineStage.controller.d.ts +0 -12
- package/dist/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.d.ts +0 -1
- package/dist/serverGroup/configure/wizard/basicSettings.controller.d.ts +0 -1
- package/dist/serverGroup/configure/wizard/cloneServerGroup.controller.d.ts +0 -2
- package/dist/serverGroup/configure/wizard/configFileArtifactList.module.d.ts +0 -1
- package/dist/serverGroup/configure/wizard/configFiles.component.d.ts +0 -20
- package/dist/serverGroup/configure/wizard/dynamicBranchLabel.component.d.ts +0 -1
- package/dist/serverGroup/details/details.controller.d.ts +0 -1
- package/src/common/componentUrlDetails.component.ts +0 -29
- package/src/common/conditionalDescriptionListItem.component.ts +0 -37
- package/src/common/loadBalancerMessage.component.html +0 -33
- package/src/common/loadBalancerMessage.component.ts +0 -13
- package/src/domain/IAppengineStageScope.ts +0 -22
- package/src/instance/details/details.controller.ts +0 -116
- package/src/instance/details/details.html +0 -99
- package/src/loadBalancer/configure/wizard/advancedSettings.component.ts +0 -56
- package/src/loadBalancer/configure/wizard/allocationConfigurationRow.component.ts +0 -70
- package/src/loadBalancer/configure/wizard/basicSettings.component.html +0 -65
- package/src/loadBalancer/configure/wizard/basicSettings.component.ts +0 -98
- package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html +0 -117
- package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.ts +0 -108
- package/src/loadBalancer/configure/wizard/wizard.controller.ts +0 -163
- package/src/loadBalancer/configure/wizard/wizard.html +0 -44
- package/src/loadBalancer/configure/wizard/wizard.less +0 -17
- package/src/loadBalancer/details/details.controller.ts +0 -151
- package/src/loadBalancer/details/details.html +0 -102
- package/src/loadBalancer/loadBalancer.module.ts +0 -21
- package/src/pipeline/pipeline.module.ts +0 -20
- package/src/pipeline/stages/appengineStage.controller.ts +0 -45
- package/src/pipeline/stages/destroyAsg/destroyAsgStage.html +0 -16
- package/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html +0 -1
- package/src/pipeline/stages/disableAsg/disableAsgStage.html +0 -18
- package/src/pipeline/stages/disableAsg/disableAsgStepLabel.html +0 -1
- package/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html +0 -33
- package/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html +0 -51
- package/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.ts +0 -65
- package/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html +0 -45
- package/src/pipeline/stages/enableAsg/enableAsgStage.html +0 -18
- package/src/pipeline/stages/enableAsg/enableAsgStepLabel.html +0 -1
- package/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html +0 -41
- package/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html +0 -26
- package/src/pipeline/stages/startServerGroup/startServerGroupStage.html +0 -18
- package/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html +0 -1
- package/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html +0 -26
- package/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html +0 -18
- package/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html +0 -1
- package/src/serverGroup/configure/wizard/advancedSettings.html +0 -33
- package/src/serverGroup/configure/wizard/basicSettings.controller.ts +0 -151
- package/src/serverGroup/configure/wizard/basicSettings.html +0 -287
- package/src/serverGroup/configure/wizard/cloneServerGroup.controller.ts +0 -103
- package/src/serverGroup/configure/wizard/configFileArtifactList.module.ts +0 -18
- package/src/serverGroup/configure/wizard/configFiles.component.html +0 -81
- package/src/serverGroup/configure/wizard/configFiles.component.ts +0 -121
- package/src/serverGroup/configure/wizard/dynamicBranchLabel.component.ts +0 -19
- package/src/serverGroup/configure/wizard/serverGroupWizard.html +0 -39
- package/src/serverGroup/details/details.controller.ts +0 -463
- package/src/serverGroup/details/details.html +0 -229
package/src/domain/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import './appengine.module';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface IAppengineInstanceDetailsProps {
|
|
4
|
+
instance: {
|
|
5
|
+
account: string;
|
|
6
|
+
instanceId: string;
|
|
7
|
+
provider: string;
|
|
8
|
+
region: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function AppengineInstanceDetails({ instance }: IAppengineInstanceDetailsProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div className="details-panel">
|
|
15
|
+
<div className="header">
|
|
16
|
+
<div className="header-text horizontal middle">
|
|
17
|
+
<i className="fa icon-instance" />
|
|
18
|
+
<h3 className="horizontal middle space-between flex-1">{instance.instanceId}</h3>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div className="content">
|
|
22
|
+
<dl className="dl-horizontal dl-narrow">
|
|
23
|
+
<dt>Account</dt>
|
|
24
|
+
<dd>{instance.account}</dd>
|
|
25
|
+
<dt>Region</dt>
|
|
26
|
+
<dd>{instance.region}</dd>
|
|
27
|
+
<dt>Provider</dt>
|
|
28
|
+
<dd>{instance.provider}</dd>
|
|
29
|
+
</dl>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { cloneDeep } from 'lodash';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Modal } from 'react-bootstrap';
|
|
4
|
+
|
|
5
|
+
import type { ILoadBalancerModalProps } from '@spinnaker/core';
|
|
6
|
+
import { LoadBalancerWriter, ModalClose, noop, ReactModal, TaskMonitor, TaskMonitorWrapper } from '@spinnaker/core';
|
|
7
|
+
|
|
8
|
+
import type { IAppengineLoadBalancer } from '../../domain';
|
|
9
|
+
import type { IAppengineAllocationDescription } from '../transformer';
|
|
10
|
+
import { AppengineLoadBalancerUpsertDescription } from '../transformer';
|
|
11
|
+
|
|
12
|
+
const TARGET_OPTIONS = [
|
|
13
|
+
{ label: 'Current Server Group', value: 'current_asg_dynamic' },
|
|
14
|
+
{ label: 'Previous Server Group', value: 'ancestor_asg_dynamic' },
|
|
15
|
+
{ label: 'Oldest Server Group', value: 'oldest_asg_dynamic' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export function updateAllocationLocatorType(
|
|
19
|
+
allocation: IAppengineAllocationDescription,
|
|
20
|
+
locatorType: IAppengineAllocationDescription['locatorType'],
|
|
21
|
+
): IAppengineAllocationDescription {
|
|
22
|
+
const updated = { ...allocation, locatorType };
|
|
23
|
+
if (locatorType === 'targetCoordinate') {
|
|
24
|
+
delete updated.serverGroupName;
|
|
25
|
+
updated.target = updated.target || TARGET_OPTIONS[0].value;
|
|
26
|
+
} else {
|
|
27
|
+
delete updated.cluster;
|
|
28
|
+
delete updated.target;
|
|
29
|
+
}
|
|
30
|
+
return updated;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface IAppengineCreateLoadBalancerModalState {
|
|
34
|
+
taskMonitor: TaskMonitor;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class AppengineCreateLoadBalancerModal extends React.Component<
|
|
38
|
+
ILoadBalancerModalProps,
|
|
39
|
+
IAppengineCreateLoadBalancerModalState
|
|
40
|
+
> {
|
|
41
|
+
public static defaultProps: Partial<ILoadBalancerModalProps> = {
|
|
42
|
+
closeModal: noop,
|
|
43
|
+
dismissModal: noop,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
public static show(props: ILoadBalancerModalProps & { isNew?: boolean }): Promise<IAppengineLoadBalancer> {
|
|
47
|
+
return ReactModal.show(AppengineCreateLoadBalancerModal, props, { dialogClassName: 'modal-lg' });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public state: IAppengineCreateLoadBalancerModalState = {
|
|
51
|
+
taskMonitor: new TaskMonitor({
|
|
52
|
+
application: this.props.app,
|
|
53
|
+
title: 'Updating your load balancer',
|
|
54
|
+
onDismiss: () => this.props.dismissModal(),
|
|
55
|
+
onTaskComplete: () => this.props.app.getDataSource('loadBalancers').refresh(),
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
private command: AppengineLoadBalancerUpsertDescription = this.buildInitialCommand();
|
|
60
|
+
|
|
61
|
+
private buildInitialCommand(): AppengineLoadBalancerUpsertDescription {
|
|
62
|
+
const loadBalancer = (this.props.loadBalancer || {}) as IAppengineLoadBalancer;
|
|
63
|
+
const command = new AppengineLoadBalancerUpsertDescription({
|
|
64
|
+
...loadBalancer,
|
|
65
|
+
account: loadBalancer.account || loadBalancer.credentials,
|
|
66
|
+
credentials: loadBalancer.credentials || loadBalancer.account,
|
|
67
|
+
name: loadBalancer.name,
|
|
68
|
+
region: loadBalancer.region,
|
|
69
|
+
serverGroups: loadBalancer.serverGroups || [],
|
|
70
|
+
cloudProvider: 'appengine',
|
|
71
|
+
migrateTraffic: loadBalancer.migrateTraffic || false,
|
|
72
|
+
} as IAppengineLoadBalancer);
|
|
73
|
+
|
|
74
|
+
command.splitDescription = loadBalancer.splitDescription ||
|
|
75
|
+
(loadBalancer.split &&
|
|
76
|
+
AppengineLoadBalancerUpsertDescription.convertTrafficSplitToTrafficSplitDescription(loadBalancer.split)) || {
|
|
77
|
+
shardBy: 'UNSPECIFIED',
|
|
78
|
+
allocationDescriptions: (loadBalancer.serverGroups || []).slice(0, 1).map((serverGroup) => ({
|
|
79
|
+
allocation: 100,
|
|
80
|
+
locatorType: 'fromExisting',
|
|
81
|
+
serverGroupName: serverGroup.name,
|
|
82
|
+
})),
|
|
83
|
+
};
|
|
84
|
+
command.mapAllocationsToPercentages();
|
|
85
|
+
|
|
86
|
+
return command;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private update = (field: keyof IAppengineLoadBalancer, value: any) => {
|
|
90
|
+
(this.command as any)[field] = value;
|
|
91
|
+
this.forceUpdate();
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
private updateSplit = (field: string, value: any) => {
|
|
95
|
+
(this.command.splitDescription as any)[field] = value;
|
|
96
|
+
this.forceUpdate();
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
private updateAllocation = (index: number, field: string, value: any) => {
|
|
100
|
+
(this.command.splitDescription.allocationDescriptions[index] as any)[field] = value;
|
|
101
|
+
this.forceUpdate();
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
private updateAllocationLocatorType = (
|
|
105
|
+
index: number,
|
|
106
|
+
locatorType: IAppengineAllocationDescription['locatorType'],
|
|
107
|
+
) => {
|
|
108
|
+
this.command.splitDescription.allocationDescriptions[index] = updateAllocationLocatorType(
|
|
109
|
+
this.command.splitDescription.allocationDescriptions[index],
|
|
110
|
+
locatorType,
|
|
111
|
+
);
|
|
112
|
+
this.forceUpdate();
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
private addAllocation = () => {
|
|
116
|
+
this.command.splitDescription.allocationDescriptions.push({
|
|
117
|
+
allocation: 0,
|
|
118
|
+
locatorType: this.props.forPipelineConfig ? 'text' : 'fromExisting',
|
|
119
|
+
serverGroupName: '',
|
|
120
|
+
});
|
|
121
|
+
if (this.command.splitDescription.allocationDescriptions.length > 1 && !this.command.splitDescription.shardBy) {
|
|
122
|
+
this.command.splitDescription.shardBy = 'IP';
|
|
123
|
+
}
|
|
124
|
+
this.forceUpdate();
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
private removeAllocation = (index: number) => {
|
|
128
|
+
this.command.splitDescription.allocationDescriptions.splice(index, 1);
|
|
129
|
+
this.forceUpdate();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
private allocationTotal = () => {
|
|
133
|
+
return this.command.splitDescription.allocationDescriptions.reduce((sum, description) => {
|
|
134
|
+
return sum + Number(description.allocation || 0);
|
|
135
|
+
}, 0);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
private submit = () => {
|
|
139
|
+
const description = cloneDeep(this.command);
|
|
140
|
+
description.mapAllocationsToDecimals();
|
|
141
|
+
delete description.serverGroups;
|
|
142
|
+
|
|
143
|
+
if (this.props.forPipelineConfig) {
|
|
144
|
+
this.props.closeModal(description as any);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.state.taskMonitor.submit(() => LoadBalancerWriter.upsertLoadBalancer(description, this.props.app, 'Update'));
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
private renderAllocationTarget(allocation: IAppengineAllocationDescription, index: number) {
|
|
152
|
+
if (!this.props.forPipelineConfig) {
|
|
153
|
+
return (
|
|
154
|
+
<input
|
|
155
|
+
className="form-control input-sm"
|
|
156
|
+
onChange={(event) => this.updateAllocation(index, 'serverGroupName', event.target.value)}
|
|
157
|
+
value={allocation.serverGroupName || ''}
|
|
158
|
+
/>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div>
|
|
164
|
+
<div className="radio">
|
|
165
|
+
<label>
|
|
166
|
+
<input
|
|
167
|
+
checked={allocation.locatorType === 'text'}
|
|
168
|
+
onChange={() => this.updateAllocationLocatorType(index, 'text')}
|
|
169
|
+
type="radio"
|
|
170
|
+
/>{' '}
|
|
171
|
+
Text input
|
|
172
|
+
</label>
|
|
173
|
+
</div>
|
|
174
|
+
<div className="radio">
|
|
175
|
+
<label>
|
|
176
|
+
<input
|
|
177
|
+
checked={allocation.locatorType === 'targetCoordinate'}
|
|
178
|
+
onChange={() => this.updateAllocationLocatorType(index, 'targetCoordinate')}
|
|
179
|
+
type="radio"
|
|
180
|
+
/>{' '}
|
|
181
|
+
Target coordinate
|
|
182
|
+
</label>
|
|
183
|
+
</div>
|
|
184
|
+
<div className="radio">
|
|
185
|
+
<label>
|
|
186
|
+
<input
|
|
187
|
+
checked={allocation.locatorType === 'fromExisting'}
|
|
188
|
+
onChange={() => this.updateAllocationLocatorType(index, 'fromExisting')}
|
|
189
|
+
type="radio"
|
|
190
|
+
/>{' '}
|
|
191
|
+
Existing server group
|
|
192
|
+
</label>
|
|
193
|
+
</div>
|
|
194
|
+
{allocation.locatorType === 'targetCoordinate' ? (
|
|
195
|
+
<div>
|
|
196
|
+
<input
|
|
197
|
+
className="form-control input-sm"
|
|
198
|
+
onChange={(event) => this.updateAllocation(index, 'cluster', event.target.value)}
|
|
199
|
+
placeholder="Cluster"
|
|
200
|
+
value={allocation.cluster || ''}
|
|
201
|
+
/>
|
|
202
|
+
<select
|
|
203
|
+
className="form-control input-sm"
|
|
204
|
+
onChange={(event) => this.updateAllocation(index, 'target', event.target.value)}
|
|
205
|
+
value={allocation.target || TARGET_OPTIONS[0].value}
|
|
206
|
+
>
|
|
207
|
+
{TARGET_OPTIONS.map((target) => (
|
|
208
|
+
<option key={target.value} value={target.value}>
|
|
209
|
+
{target.label}
|
|
210
|
+
</option>
|
|
211
|
+
))}
|
|
212
|
+
</select>
|
|
213
|
+
</div>
|
|
214
|
+
) : (
|
|
215
|
+
<input
|
|
216
|
+
className="form-control input-sm"
|
|
217
|
+
onChange={(event) => this.updateAllocation(index, 'serverGroupName', event.target.value)}
|
|
218
|
+
placeholder={allocation.locatorType === 'text' ? 'Server group expression or name' : 'Server group'}
|
|
219
|
+
value={allocation.serverGroupName || ''}
|
|
220
|
+
/>
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
public render() {
|
|
227
|
+
const { dismissModal, isNew } = this.props as ILoadBalancerModalProps & { isNew?: boolean };
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<div>
|
|
231
|
+
<ModalClose dismiss={dismissModal} />
|
|
232
|
+
<Modal.Header>
|
|
233
|
+
<Modal.Title>{isNew ? 'Create' : 'Edit'} App Engine Load Balancer</Modal.Title>
|
|
234
|
+
</Modal.Header>
|
|
235
|
+
<Modal.Body>
|
|
236
|
+
<div className="form-horizontal">
|
|
237
|
+
<label>Name</label>
|
|
238
|
+
<input
|
|
239
|
+
className="form-control input-sm"
|
|
240
|
+
onChange={(event) => this.update('name', event.target.value)}
|
|
241
|
+
value={this.command.name || ''}
|
|
242
|
+
/>
|
|
243
|
+
<label>Account</label>
|
|
244
|
+
<input
|
|
245
|
+
className="form-control input-sm"
|
|
246
|
+
onChange={(event) => {
|
|
247
|
+
this.update('credentials', event.target.value);
|
|
248
|
+
this.update('account', event.target.value);
|
|
249
|
+
}}
|
|
250
|
+
value={this.command.credentials || this.command.account || ''}
|
|
251
|
+
/>
|
|
252
|
+
<label>Region</label>
|
|
253
|
+
<input
|
|
254
|
+
className="form-control input-sm"
|
|
255
|
+
onChange={(event) => this.update('region', event.target.value)}
|
|
256
|
+
value={this.command.region || ''}
|
|
257
|
+
/>
|
|
258
|
+
<div className="checkbox">
|
|
259
|
+
<label>
|
|
260
|
+
<input
|
|
261
|
+
checked={!!this.command.migrateTraffic}
|
|
262
|
+
onChange={(event) => this.update('migrateTraffic', event.target.checked)}
|
|
263
|
+
type="checkbox"
|
|
264
|
+
/>{' '}
|
|
265
|
+
Migrate traffic
|
|
266
|
+
</label>
|
|
267
|
+
</div>
|
|
268
|
+
{(this.command.splitDescription.allocationDescriptions.length > 1 || this.command.migrateTraffic) && (
|
|
269
|
+
<div>
|
|
270
|
+
<label>Shard By</label>
|
|
271
|
+
<select
|
|
272
|
+
className="form-control input-sm"
|
|
273
|
+
onChange={(event) => this.updateSplit('shardBy', event.target.value)}
|
|
274
|
+
value={this.command.splitDescription.shardBy || 'UNSPECIFIED'}
|
|
275
|
+
>
|
|
276
|
+
<option value="UNSPECIFIED">Unspecified</option>
|
|
277
|
+
<option value="IP">IP</option>
|
|
278
|
+
<option value="COOKIE">Cookie</option>
|
|
279
|
+
</select>
|
|
280
|
+
</div>
|
|
281
|
+
)}
|
|
282
|
+
<label>Allocations</label>
|
|
283
|
+
<table className="table table-condensed">
|
|
284
|
+
<thead>
|
|
285
|
+
<tr>
|
|
286
|
+
<th>Server Group</th>
|
|
287
|
+
<th>Allocation %</th>
|
|
288
|
+
<th />
|
|
289
|
+
</tr>
|
|
290
|
+
</thead>
|
|
291
|
+
<tbody>
|
|
292
|
+
{this.command.splitDescription.allocationDescriptions.map((allocation, index) => (
|
|
293
|
+
<tr key={index}>
|
|
294
|
+
<td>{this.renderAllocationTarget(allocation, index)}</td>
|
|
295
|
+
<td>
|
|
296
|
+
<input
|
|
297
|
+
className="form-control input-sm"
|
|
298
|
+
min={0}
|
|
299
|
+
onChange={(event) => this.updateAllocation(index, 'allocation', Number(event.target.value))}
|
|
300
|
+
type="number"
|
|
301
|
+
value={allocation.allocation || 0}
|
|
302
|
+
/>
|
|
303
|
+
</td>
|
|
304
|
+
<td>
|
|
305
|
+
<button
|
|
306
|
+
className="btn btn-sm btn-link"
|
|
307
|
+
onClick={() => this.removeAllocation(index)}
|
|
308
|
+
type="button"
|
|
309
|
+
>
|
|
310
|
+
<span className="glyphicon glyphicon-trash" />
|
|
311
|
+
</button>
|
|
312
|
+
</td>
|
|
313
|
+
</tr>
|
|
314
|
+
))}
|
|
315
|
+
</tbody>
|
|
316
|
+
</table>
|
|
317
|
+
<button className="btn btn-block btn-sm add-new" onClick={this.addAllocation} type="button">
|
|
318
|
+
<span className="glyphicon glyphicon-plus-sign" /> Add allocation
|
|
319
|
+
</button>
|
|
320
|
+
{this.allocationTotal() !== 100 && (
|
|
321
|
+
<div className="alert alert-warning">Traffic allocations must add up to 100%.</div>
|
|
322
|
+
)}
|
|
323
|
+
</div>
|
|
324
|
+
</Modal.Body>
|
|
325
|
+
<TaskMonitorWrapper monitor={this.state.taskMonitor} />
|
|
326
|
+
<Modal.Footer>
|
|
327
|
+
<button className="btn btn-default" onClick={() => dismissModal()} type="button">
|
|
328
|
+
Cancel
|
|
329
|
+
</button>
|
|
330
|
+
<button
|
|
331
|
+
className="btn btn-primary"
|
|
332
|
+
disabled={this.allocationTotal() !== 100}
|
|
333
|
+
onClick={this.submit}
|
|
334
|
+
type="button"
|
|
335
|
+
>
|
|
336
|
+
{isNew ? 'Create' : 'Done'}
|
|
337
|
+
</button>
|
|
338
|
+
</Modal.Footer>
|
|
339
|
+
</div>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { ILoadBalancerActionsProps, ILoadBalancerDeleteCommand } from '@spinnaker/core';
|
|
4
|
+
import { ConfirmationModalService, LoadBalancerWriter } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
import { AppengineCreateLoadBalancerModal } from '../configure/AppengineCreateLoadBalancerModal';
|
|
7
|
+
|
|
8
|
+
export function canDeleteAppengineLoadBalancer(loadBalancer: any): boolean {
|
|
9
|
+
return loadBalancer.name !== 'default';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getDeleteLoadBalancerWarning(loadBalancer: any): string {
|
|
13
|
+
const serverGroupNames = (loadBalancer.serverGroups || []).map((serverGroup: any) => serverGroup.name);
|
|
14
|
+
if (!serverGroupNames.length) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (serverGroupNames.length === 1) {
|
|
19
|
+
return `<div class="alert alert-warning"><p>Deleting <b>${loadBalancer.name}</b> will destroy <b>${serverGroupNames[0]}</b>.</p></div>`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return `<div class="alert alert-warning"><p>Deleting <b>${
|
|
23
|
+
loadBalancer.name
|
|
24
|
+
}</b> will destroy the following server groups:<ul>${serverGroupNames
|
|
25
|
+
.map((name: string) => `<li>${name}</li>`)
|
|
26
|
+
.join('')}</ul></p></div>`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function AppengineLoadBalancerActions({ app, loadBalancer }: ILoadBalancerActionsProps) {
|
|
30
|
+
const deleteLoadBalancer = () => {
|
|
31
|
+
const command: ILoadBalancerDeleteCommand = {
|
|
32
|
+
cloudProvider: loadBalancer.cloudProvider,
|
|
33
|
+
loadBalancerName: loadBalancer.name,
|
|
34
|
+
credentials: loadBalancer.account,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
ConfirmationModalService.confirm({
|
|
38
|
+
header: `Really delete ${loadBalancer.name}?`,
|
|
39
|
+
buttonText: `Delete ${loadBalancer.name}`,
|
|
40
|
+
body: getDeleteLoadBalancerWarning(loadBalancer),
|
|
41
|
+
account: loadBalancer.account,
|
|
42
|
+
taskMonitorConfig: { application: app, title: `Deleting ${loadBalancer.name}` },
|
|
43
|
+
submitMethod: () => LoadBalancerWriter.deleteLoadBalancer(command, app),
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="actions">
|
|
49
|
+
<button
|
|
50
|
+
className="btn btn-sm btn-default"
|
|
51
|
+
onClick={() => AppengineCreateLoadBalancerModal.show({ app, loadBalancer, isNew: false })}
|
|
52
|
+
type="button"
|
|
53
|
+
>
|
|
54
|
+
Edit
|
|
55
|
+
</button>
|
|
56
|
+
<button
|
|
57
|
+
className="btn btn-sm btn-default"
|
|
58
|
+
disabled={!canDeleteAppengineLoadBalancer(loadBalancer)}
|
|
59
|
+
onClick={deleteLoadBalancer}
|
|
60
|
+
type="button"
|
|
61
|
+
>
|
|
62
|
+
Delete
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { ILoadBalancerDetailsSectionProps } from '@spinnaker/core';
|
|
4
|
+
|
|
5
|
+
import type { IAppengineLoadBalancer } from '../../domain';
|
|
6
|
+
|
|
7
|
+
export function AppengineLoadBalancerDetailsSection({ loadBalancer }: ILoadBalancerDetailsSectionProps) {
|
|
8
|
+
const appengineLoadBalancer = loadBalancer as IAppengineLoadBalancer;
|
|
9
|
+
const allocations = appengineLoadBalancer.split?.allocations || {};
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="content-section">
|
|
13
|
+
<div className="content-section-heading">Traffic Split</div>
|
|
14
|
+
<dl className="dl-horizontal dl-narrow">
|
|
15
|
+
<dt>Account</dt>
|
|
16
|
+
<dd>{appengineLoadBalancer.account || appengineLoadBalancer.credentials}</dd>
|
|
17
|
+
<dt>Region</dt>
|
|
18
|
+
<dd>{appengineLoadBalancer.region}</dd>
|
|
19
|
+
<dt>Shard By</dt>
|
|
20
|
+
<dd>{appengineLoadBalancer.split?.shardBy || 'Unspecified'}</dd>
|
|
21
|
+
</dl>
|
|
22
|
+
{Object.keys(allocations).length > 0 && (
|
|
23
|
+
<table className="table table-condensed">
|
|
24
|
+
<thead>
|
|
25
|
+
<tr>
|
|
26
|
+
<th>Server Group</th>
|
|
27
|
+
<th>Allocation</th>
|
|
28
|
+
</tr>
|
|
29
|
+
</thead>
|
|
30
|
+
<tbody>
|
|
31
|
+
{Object.entries(allocations).map(([serverGroup, allocation]) => (
|
|
32
|
+
<tr key={serverGroup}>
|
|
33
|
+
<td>{serverGroup}</td>
|
|
34
|
+
<td>{Math.round(allocation * 1000) / 10}%</td>
|
|
35
|
+
</tr>
|
|
36
|
+
))}
|
|
37
|
+
</tbody>
|
|
38
|
+
</table>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { IUseDetailsHookProps, UseDetailsResult } from '@spinnaker/core';
|
|
4
|
+
|
|
5
|
+
import type { IAppengineLoadBalancer } from '../../domain';
|
|
6
|
+
|
|
7
|
+
export function useAppengineLoadBalancerDetails({
|
|
8
|
+
app,
|
|
9
|
+
loadBalancerParams,
|
|
10
|
+
autoClose,
|
|
11
|
+
}: IUseDetailsHookProps): UseDetailsResult<IAppengineLoadBalancer> {
|
|
12
|
+
const [data, setData] = useState<IAppengineLoadBalancer>();
|
|
13
|
+
const [loading, setLoading] = useState(true);
|
|
14
|
+
|
|
15
|
+
const refetch = async () => {
|
|
16
|
+
setLoading(true);
|
|
17
|
+
await app.getDataSource('loadBalancers').ready();
|
|
18
|
+
const loadBalancer = app.getDataSource('loadBalancers').data.find((candidate: IAppengineLoadBalancer) => {
|
|
19
|
+
return candidate.name === loadBalancerParams.name && candidate.account === loadBalancerParams.accountId;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
setData(loadBalancer);
|
|
23
|
+
setLoading(false);
|
|
24
|
+
|
|
25
|
+
if (!loadBalancer) {
|
|
26
|
+
autoClose();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
refetch();
|
|
32
|
+
const unsubscribe = app.getDataSource('loadBalancers').onRefresh(refetch);
|
|
33
|
+
return () => unsubscribe();
|
|
34
|
+
}, [loadBalancerParams.name, loadBalancerParams.accountId, loadBalancerParams.region]);
|
|
35
|
+
|
|
36
|
+
return { data, loading, error: null, refetch };
|
|
37
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { module } from 'angular';
|
|
2
1
|
import { camelCase, chain, cloneDeep, filter, get, has, reduce } from 'lodash';
|
|
3
2
|
|
|
4
3
|
import type {
|
|
@@ -76,10 +75,7 @@ export class AppengineLoadBalancerUpsertDescription implements ILoadBalancerUpse
|
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
export class AppengineLoadBalancerTransformer {
|
|
79
|
-
public
|
|
80
|
-
constructor(private $q: ng.IQService) {}
|
|
81
|
-
|
|
82
|
-
public normalizeLoadBalancer(loadBalancer: ILoadBalancer): PromiseLike<ILoadBalancer> {
|
|
78
|
+
public normalizeLoadBalancer(loadBalancer: ILoadBalancer): Promise<ILoadBalancer> {
|
|
83
79
|
loadBalancer.provider = loadBalancer.type;
|
|
84
80
|
loadBalancer.instanceCounts = this.buildInstanceCounts(loadBalancer.serverGroups);
|
|
85
81
|
loadBalancer.instances = [];
|
|
@@ -97,13 +93,13 @@ export class AppengineLoadBalancerTransformer {
|
|
|
97
93
|
|
|
98
94
|
const activeServerGroups = filter(loadBalancer.serverGroups, { isDisabled: false });
|
|
99
95
|
loadBalancer.instances = chain(activeServerGroups).map('instances').flatten().value() as IInstance[];
|
|
100
|
-
return
|
|
96
|
+
return Promise.resolve(loadBalancer);
|
|
101
97
|
}
|
|
102
98
|
|
|
103
99
|
public convertLoadBalancerForEditing(
|
|
104
100
|
loadBalancer: IAppengineLoadBalancer,
|
|
105
101
|
application: Application,
|
|
106
|
-
):
|
|
102
|
+
): Promise<IAppengineLoadBalancer> {
|
|
107
103
|
return application
|
|
108
104
|
.getDataSource('loadBalancers')
|
|
109
105
|
.ready()
|
|
@@ -161,10 +157,3 @@ export class AppengineLoadBalancerTransformer {
|
|
|
161
157
|
return instance as IInstance;
|
|
162
158
|
}
|
|
163
159
|
}
|
|
164
|
-
|
|
165
|
-
export const APPENGINE_LOAD_BALANCER_TRANSFORMER = 'spinnaker.appengine.loadBalancer.transformer.service';
|
|
166
|
-
|
|
167
|
-
module(APPENGINE_LOAD_BALANCER_TRANSFORMER, []).service(
|
|
168
|
-
'appengineLoadBalancerTransformer',
|
|
169
|
-
AppengineLoadBalancerTransformer,
|
|
170
|
-
);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { IExecutionDetailsSectionProps } from '@spinnaker/core';
|
|
4
|
+
import { ExecutionDetailsSection } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
function FailureMessages({ stage }: Pick<IExecutionDetailsSectionProps, 'stage'>) {
|
|
7
|
+
const failures = (stage.context?.exception?.details?.errors || stage.context?.failureMessages || []) as string[];
|
|
8
|
+
if (!failures.length) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="alert alert-danger">
|
|
14
|
+
{failures.map((message, index) => (
|
|
15
|
+
<p key={index}>{message}</p>
|
|
16
|
+
))}
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getServerGroupDisplayName(context: any): string {
|
|
22
|
+
return context.cluster || context.serverGroupName;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ServerGroupConfigDetails(props: IExecutionDetailsSectionProps) {
|
|
26
|
+
const { stage } = props;
|
|
27
|
+
return (
|
|
28
|
+
<div>
|
|
29
|
+
<dl className="dl-horizontal dl-narrow">
|
|
30
|
+
<dt>Account</dt>
|
|
31
|
+
<dd>{stage.context.credentials || stage.context.account}</dd>
|
|
32
|
+
<dt>Region</dt>
|
|
33
|
+
<dd>{stage.context.region}</dd>
|
|
34
|
+
<dt>{stage.context.cluster ? 'Cluster' : 'Server Group'}</dt>
|
|
35
|
+
<dd>{getServerGroupDisplayName(stage.context)}</dd>
|
|
36
|
+
{stage.context.target && [
|
|
37
|
+
<dt key="target-label">Target</dt>,
|
|
38
|
+
<dd key="target-value">{stage.context.target}</dd>,
|
|
39
|
+
]}
|
|
40
|
+
</dl>
|
|
41
|
+
<FailureMessages stage={stage} />
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function AppengineStartServerGroupExecutionDetails(props: IExecutionDetailsSectionProps) {
|
|
47
|
+
return (
|
|
48
|
+
<ExecutionDetailsSection name={AppengineStartServerGroupExecutionDetails.title} current={props.current}>
|
|
49
|
+
<ServerGroupConfigDetails {...props} />
|
|
50
|
+
</ExecutionDetailsSection>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
AppengineStartServerGroupExecutionDetails.title = 'startServerGroupConfig';
|
|
54
|
+
|
|
55
|
+
export function AppengineStopServerGroupExecutionDetails(props: IExecutionDetailsSectionProps) {
|
|
56
|
+
return (
|
|
57
|
+
<ExecutionDetailsSection name={AppengineStopServerGroupExecutionDetails.title} current={props.current}>
|
|
58
|
+
<ServerGroupConfigDetails {...props} />
|
|
59
|
+
</ExecutionDetailsSection>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
AppengineStopServerGroupExecutionDetails.title = 'stopServerGroupConfig';
|
|
63
|
+
|
|
64
|
+
export function AppengineEditLoadBalancerExecutionDetails(props: IExecutionDetailsSectionProps) {
|
|
65
|
+
const loadBalancers = props.stage.context.loadBalancers || [];
|
|
66
|
+
return (
|
|
67
|
+
<ExecutionDetailsSection name={AppengineEditLoadBalancerExecutionDetails.title} current={props.current}>
|
|
68
|
+
<table className="table table-condensed">
|
|
69
|
+
<thead>
|
|
70
|
+
<tr>
|
|
71
|
+
<th>Account</th>
|
|
72
|
+
<th>Name</th>
|
|
73
|
+
<th>Region</th>
|
|
74
|
+
</tr>
|
|
75
|
+
</thead>
|
|
76
|
+
<tbody>
|
|
77
|
+
{loadBalancers.map((loadBalancer: any, index: number) => (
|
|
78
|
+
<tr key={index}>
|
|
79
|
+
<td>{loadBalancer.credentials || loadBalancer.account}</td>
|
|
80
|
+
<td>{loadBalancer.name || loadBalancer.loadBalancerName}</td>
|
|
81
|
+
<td>{loadBalancer.region}</td>
|
|
82
|
+
</tr>
|
|
83
|
+
))}
|
|
84
|
+
</tbody>
|
|
85
|
+
</table>
|
|
86
|
+
<FailureMessages stage={props.stage} />
|
|
87
|
+
</ExecutionDetailsSection>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
AppengineEditLoadBalancerExecutionDetails.title = 'editLoadBalancerConfig';
|