@spinnaker/appengine 2025.2.3 → 2025.4.0-rc1
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/index.js +1 -4083
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -1,4084 +1,2 @@
|
|
|
1
|
-
import { module, extend, copy } from 'angular';
|
|
2
|
-
import { HelpContentsRegistry, ConfirmationModalService, RecentHistoryService, InstanceReader, InstanceWriter, StageConstants, AppListExtractor, LoadBalancerWriter, TaskMonitor, AccountService, Registry, CloudProviderRegistry, FormikFormField, ReactSelectInput, StageArtifactSelectorDelegate, excludeAllTypesExcept, ArtifactTypePatterns, FormikStageConfig, FormValidator, ExecutionDetailsTasks, ExecutionArtifactTab, SETTINGS, StorageAccountReader, NgAppEngineDeployArtifactDelegate, ExpectedArtifactSelectorViewController, NgAppengineConfigArtifactDelegate, SERVER_GROUP_WRITER, StageArtifactSelector, withErrorBoundary, TaskExecutor, ServerGroupWarningMessageService, ServerGroupReader, ApplicationNameValidator, DeploymentStrategyRegistry } from '@spinnaker/core';
|
|
3
|
-
import { cloneDeep, flattenDeep, uniq, difference, filter, chain, has, camelCase, get, reduce, set, merge, map, mapValues } from 'lodash';
|
|
4
|
-
import React from 'react';
|
|
5
|
-
import { Subject, from } from 'rxjs';
|
|
6
|
-
import { takeUntil } from 'rxjs/operators';
|
|
7
|
-
import { react2angular } from 'react2angular';
|
|
8
|
-
import classNames from 'classnames';
|
|
9
|
-
|
|
10
|
-
const appengineComponentUrlDetailsComponent = {
|
|
11
|
-
bindings: { component: "<" },
|
|
12
|
-
template: `
|
|
13
|
-
<dt>HTTPS</dt>
|
|
14
|
-
<dl class="small">
|
|
15
|
-
<a href="{{$ctrl.component.httpsUrl}}" target="_blank">{{$ctrl.component.httpsUrl}}</a>
|
|
16
|
-
<copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"
|
|
17
|
-
tool-tip="'Copy URL to clipboard'"
|
|
18
|
-
text="$ctrl.component.httpsUrl"></copy-to-clipboard>
|
|
19
|
-
</dl>
|
|
20
|
-
<dt>HTTP</dt>
|
|
21
|
-
<dl class="small">
|
|
22
|
-
<a href="{{$ctrl.component.httpUrl}}" target="_blank">{{$ctrl.component.httpUrl}}</a>
|
|
23
|
-
<copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"
|
|
24
|
-
tool-tip="'Copy URL to clipboard'"
|
|
25
|
-
text="$ctrl.component.httpUrl"></copy-to-clipboard>
|
|
26
|
-
</dl>
|
|
27
|
-
`
|
|
28
|
-
};
|
|
29
|
-
const APPENGINE_COMPONENT_URL_DETAILS = "spinnaker.appengine.componentUrlDetails.component";
|
|
30
|
-
module(APPENGINE_COMPONENT_URL_DETAILS, []).component("appengineComponentUrlDetails", appengineComponentUrlDetailsComponent);
|
|
31
|
-
|
|
32
|
-
class AppengineConditionalDescriptionListItemCtrl {
|
|
33
|
-
constructor($filter) {
|
|
34
|
-
this.$filter = $filter;
|
|
35
|
-
}
|
|
36
|
-
$onInit() {
|
|
37
|
-
if (!this.label) {
|
|
38
|
-
this.label = this.$filter("robotToHuman")(this.key);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
AppengineConditionalDescriptionListItemCtrl.$inject = ["$filter"];
|
|
43
|
-
const appengineConditionalDescriptionListItem = {
|
|
44
|
-
bindings: { label: "@", key: "@", component: "<" },
|
|
45
|
-
transclude: {
|
|
46
|
-
keyLabel: "?keyText",
|
|
47
|
-
valueLabel: "?valueLabel"
|
|
48
|
-
},
|
|
49
|
-
template: `
|
|
50
|
-
<dt ng-if="$ctrl.component[$ctrl.key]">{{$ctrl.label}}<span ng-transclude="keyLabel"></span></dt>
|
|
51
|
-
<dd ng-if="$ctrl.component[$ctrl.key]">{{$ctrl.component[$ctrl.key]}}<span ng-transclude="valueLabel"></span></dd>
|
|
52
|
-
`,
|
|
53
|
-
controller: AppengineConditionalDescriptionListItemCtrl
|
|
54
|
-
};
|
|
55
|
-
const APPENGINE_CONDITIONAL_DESCRIPTION_LIST_ITEM = "spinnaker.appengine.conditionalDescriptionListItem";
|
|
56
|
-
module(APPENGINE_CONDITIONAL_DESCRIPTION_LIST_ITEM, []).component("appengineConditionalDtDd", appengineConditionalDescriptionListItem);
|
|
57
|
-
|
|
58
|
-
const appengineLoadBalancerMessageComponent = {
|
|
59
|
-
bindings: { showCreateMessage: "<", columnOffset: "@", columns: "@" },
|
|
60
|
-
templateUrl: "appengine/src/common/loadBalancerMessage.component.html"
|
|
61
|
-
};
|
|
62
|
-
const APPENGINE_LOAD_BALANCER_CREATE_MESSAGE = "spinnaker.appengine.loadBalancer.createMessage.component";
|
|
63
|
-
module(APPENGINE_LOAD_BALANCER_CREATE_MESSAGE, []).component("appengineLoadBalancerMessage", appengineLoadBalancerMessageComponent);
|
|
64
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
65
|
-
templateCache.put("appengine/src/common/loadBalancerMessage.component.html", `<div class="row">
|
|
66
|
-
<div class="col-md-offset-{{ $ctrl.columnOffset || 0 }} col-md-{{ $ctrl.columns || 12 }}">
|
|
67
|
-
<div class="well">
|
|
68
|
-
<p>
|
|
69
|
-
<span ng-if="$ctrl.showCreateMessage">Spinnaker cannot create a load balancer for App Engine.</span>
|
|
70
|
-
A Spinnaker load balancer maps to an App Engine service, which is specified in a version's
|
|
71
|
-
<code>app.yaml</code>.
|
|
72
|
-
</p>
|
|
73
|
-
<p>For example, the following <code>app.yaml</code></p>
|
|
74
|
-
<pre class="text-left" style="white-space: pre-line">
|
|
75
|
-
runtime: python27
|
|
76
|
-
api_version: 1
|
|
77
|
-
…
|
|
78
|
-
service: mobile
|
|
79
|
-
…
|
|
80
|
-
</pre
|
|
81
|
-
>
|
|
82
|
-
<p>
|
|
83
|
-
deploys to the <code>mobile</code> service. If you do not specify a service, your version will be deployed to
|
|
84
|
-
the <code>default</code> service.
|
|
85
|
-
</p>
|
|
86
|
-
<p>
|
|
87
|
-
If a service does not exist when a version is deployed, it will be created. It will then be editable as a load
|
|
88
|
-
balancer within Spinnaker.
|
|
89
|
-
</p>
|
|
90
|
-
<p>
|
|
91
|
-
<a href="https://cloud.google.com/appengine/docs/python/config/appref" target="_blank"
|
|
92
|
-
>App Engine Documentation</a
|
|
93
|
-
>
|
|
94
|
-
</p>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
`);
|
|
99
|
-
}]);
|
|
100
|
-
|
|
101
|
-
const helpContents = [
|
|
102
|
-
{
|
|
103
|
-
key: "appengine.serverGroup.gcs.repositoryUrl",
|
|
104
|
-
value: `The full URL to the GCS bucket or TAR file containing the source files for this deployment,
|
|
105
|
-
including 'gs://'. For example, <b>gs://my-bucket/my-app</b> or <b>gs://my-bucket/app.tar</b>.`
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
key: "appengine.serverGroup.git.repositoryUrl",
|
|
109
|
-
value: `The full URL to the git repository containing the source files for this deployment,
|
|
110
|
-
including protocol. For example, <b>https://github.com/spinnaker/deck.git</b>`
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
key: "appengine.serverGroup.gitCredentialType",
|
|
114
|
-
value: `The credential type that will be used to access the git repository for this deployment.
|
|
115
|
-
You can configure these credentials alongside your App Engine credentials.`
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
key: "appengine.serverGroup.branch",
|
|
119
|
-
value: "The name of the branch in the above git repository to be used for this deployment."
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
key: "appengine.serverGroup.applicationDirectoryRoot",
|
|
123
|
-
value: `(Optional) Path to the directory root of the application to be deployed,
|
|
124
|
-
starting from the root of the git repository. This is the directory from which
|
|
125
|
-
<code>gcloud app deploy</code> will be run.`
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
key: "appengine.serverGroup.configFilepaths",
|
|
129
|
-
value: `Paths to App Engine application configuration files, starting from the application directory root,
|
|
130
|
-
specified above. In most cases, this will be <code>app.yaml</code> or <code>cron.yaml</code>,
|
|
131
|
-
but could be <code>path/to/app.yaml</code> or <code>path/to/cron.yaml</code>.`
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
key: "appengine.serverGroup.configFiles",
|
|
135
|
-
value: `<p>(Optional) The contents of an App Engine config file (e.g., an <code>app.yaml</code> or
|
|
136
|
-
<code>cron.yaml</code> file). These files should not conflict with the config filepaths above:
|
|
137
|
-
if you include, for example, the contents of an <code>app.yaml</code>
|
|
138
|
-
file here, you should <b>not</b> specify the file path to an <code>app.yaml</code> above.<br></p>
|
|
139
|
-
<p>If this is a pipeline stage, you can use Spinnaker Pipeline Expressions here.</p>`
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
key: "appengine.serverGroup.configFilesRequired",
|
|
143
|
-
value: `<p>The contents of an App Engine config file (e.g., an <code>app.yaml</code> or
|
|
144
|
-
<code>cron.yaml</code> file).</p>
|
|
145
|
-
<p>An <code>app.yaml</code> file is required and must have <code>runtime: custom</code> in
|
|
146
|
-
order to deploy successfully.</p>
|
|
147
|
-
<p>If this is a pipeline stage, you can use Spinnaker Pipeline Expressions here.</p>`
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
key: "appengine.serverGroup.matchBranchOnRegex",
|
|
151
|
-
value: `(Optional) A Jenkins trigger may produce details from multiple repositories and branches.
|
|
152
|
-
Spinnaker will use the regex specified here to help resolve a branch for the deployment.
|
|
153
|
-
If Spinnaker cannot resolve exactly one branch from the trigger, this pipeline will fail.`
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
key: "appengine.serverGroup.promote",
|
|
157
|
-
value: "If selected, the newly deployed server group will receive all traffic."
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
key: "appengine.serverGroup.stopPreviousVersion",
|
|
161
|
-
value: `If selected, the previously running server group in this server group's <b>service</b>
|
|
162
|
-
(Spinnaker load balancer) will be stopped. This option will be respected only if this server group will
|
|
163
|
-
be receiving all traffic and the previous server group is using manual scaling.`
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
key: "appengine.serverGroup.containerImageUrl",
|
|
167
|
-
value: `The full URL to the container image to use for deployment. The URL must be one of the valid GCR hostnames,
|
|
168
|
-
for example <b>gcr.io/my-project/image:tag</b>`
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
key: "appengine.serverGroup.suppress-version-string",
|
|
172
|
-
value: `Spinnaker automatically versions your server groups. This means deployments through Spinnaker receive a
|
|
173
|
-
short version string at the end of their name, like "v001". In most cases you will want to keep this
|
|
174
|
-
version as part of the name. Preventing this string from being added to your server group will stop
|
|
175
|
-
it from being considered when rolling back, promoting new versions or executing deployment strategies.
|
|
176
|
-
If you are certain that you do not want the version string applied to this server group then check
|
|
177
|
-
this box.`
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
key: "appengine.loadBalancer.shardBy.cookie",
|
|
181
|
-
value: 'Diversion based on a specially named cookie, "GOOGAPPUID." The cookie must be set by the application itself or no diversion will occur.'
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
key: "appengine.loadBalancer.shardBy.ip",
|
|
185
|
-
value: "Diversion based on applying the modulus operation to a fingerprint of the IP address."
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
key: "appengine.loadBalancer.migrateTraffic",
|
|
189
|
-
value: `If selected, traffic will be gradually shifted to a single version. For gradual traffic migration,
|
|
190
|
-
the target version must be located within instances that are configured for
|
|
191
|
-
both warmup requests and automatic scaling.
|
|
192
|
-
Gradual traffic migration is not supported in the App Engine flexible environment.`
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
key: "appengine.loadBalancer.allocations",
|
|
196
|
-
value: "An allocation is the percent of traffic directed to a server group."
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
key: "appengine.loadBalancer.textLocator",
|
|
200
|
-
value: `Either the name of a server group, or a Spinnaker Pipeline Expression
|
|
201
|
-
that resolves to the name of a server group.`
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
key: "appengine.instance.availability",
|
|
205
|
-
value: `
|
|
206
|
-
An instance's <b>availability</b> is determined by its version (Spinnaker server group).
|
|
207
|
-
<ul>
|
|
208
|
-
<li>Manual scaling versions use resident instances</li>
|
|
209
|
-
<li>Basic scaling versions use dynamic instances</li>
|
|
210
|
-
<li>Auto scaling versions use dynamic instances - but if you specify a number, N,
|
|
211
|
-
of minimum idle instances, the first N instances will be resident,
|
|
212
|
-
and additional dynamic instances will be created as necessary.
|
|
213
|
-
</li>
|
|
214
|
-
</ul>`
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
key: "appengine.instance.averageLatency",
|
|
218
|
-
value: "Average latency over the last minute in milliseconds."
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
key: "appengine.instance.vmStatus",
|
|
222
|
-
value: "Status of the virtual machine where this instance lives."
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
key: "appengine.instance.qps",
|
|
226
|
-
value: "Average queries per second over the last minute."
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
key: "appengine.instance.errors",
|
|
230
|
-
value: "Number of errors since this instance was started."
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
key: "appengine.instance.requests",
|
|
234
|
-
value: "Number of requests since this instance was started."
|
|
235
|
-
}
|
|
236
|
-
];
|
|
237
|
-
helpContents.forEach((entry) => HelpContentsRegistry.register(entry.key, entry.value));
|
|
238
|
-
|
|
239
|
-
class AppengineInstanceDetailsController {
|
|
240
|
-
constructor($q, app, instance) {
|
|
241
|
-
this.$q = $q;
|
|
242
|
-
this.app = app;
|
|
243
|
-
this.state = { loading: true };
|
|
244
|
-
this.upToolTip = "An App Engine instance is 'Up' if a load balancer is directing traffic to its server group.";
|
|
245
|
-
this.outOfServiceToolTip = `
|
|
246
|
-
An App Engine instance is 'Out Of Service' if no load balancers are directing traffic to its server group.`;
|
|
247
|
-
this.app.ready().then(() => this.retrieveInstance(instance)).then((instanceDetails) => {
|
|
248
|
-
this.instance = instanceDetails;
|
|
249
|
-
this.state.loading = false;
|
|
250
|
-
}).catch(() => {
|
|
251
|
-
this.instanceIdNotFound = instance.instanceId;
|
|
252
|
-
this.state.loading = false;
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
terminateInstance() {
|
|
256
|
-
const instance = cloneDeep(this.instance);
|
|
257
|
-
const shortName = `${this.instance.name.substring(0, 10)}...`;
|
|
258
|
-
instance.placement = {};
|
|
259
|
-
instance.instanceId = instance.name;
|
|
260
|
-
const taskMonitor = {
|
|
261
|
-
application: this.app,
|
|
262
|
-
title: "Terminating " + shortName,
|
|
263
|
-
onTaskComplete() {
|
|
264
|
-
if (this.$state.includes("**.instanceDetails", { instanceId: instance.name })) {
|
|
265
|
-
this.$state.go("^");
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
const submitMethod = () => {
|
|
270
|
-
return InstanceWriter.terminateInstance(instance, this.app, { cloudProvider: "appengine" });
|
|
271
|
-
};
|
|
272
|
-
ConfirmationModalService.confirm({
|
|
273
|
-
header: "Really terminate " + shortName + "?",
|
|
274
|
-
buttonText: "Terminate " + shortName,
|
|
275
|
-
account: instance.account,
|
|
276
|
-
taskMonitorConfig: taskMonitor,
|
|
277
|
-
submitMethod
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
retrieveInstance(instance) {
|
|
281
|
-
const instanceLocatorPredicate = (dataSource) => {
|
|
282
|
-
return dataSource.instances.some((possibleMatch) => possibleMatch.id === instance.instanceId);
|
|
283
|
-
};
|
|
284
|
-
const dataSources = flattenDeep([
|
|
285
|
-
this.app.getDataSource("serverGroups").data,
|
|
286
|
-
this.app.getDataSource("loadBalancers").data,
|
|
287
|
-
this.app.getDataSource("loadBalancers").data.map((loadBalancer) => loadBalancer.serverGroups)
|
|
288
|
-
]);
|
|
289
|
-
const instanceManager = dataSources.find(instanceLocatorPredicate);
|
|
290
|
-
if (instanceManager) {
|
|
291
|
-
const recentHistoryExtraData = {
|
|
292
|
-
region: instanceManager.region,
|
|
293
|
-
account: instanceManager.account
|
|
294
|
-
};
|
|
295
|
-
if (instanceManager.category === "serverGroup") {
|
|
296
|
-
recentHistoryExtraData.serverGroup = instanceManager.name;
|
|
297
|
-
}
|
|
298
|
-
RecentHistoryService.addExtraDataToLatest("instances", recentHistoryExtraData);
|
|
299
|
-
return InstanceReader.getInstanceDetails(instanceManager.account, instanceManager.region, instance.instanceId).then((instanceDetails) => {
|
|
300
|
-
instanceDetails.account = instanceManager.account;
|
|
301
|
-
instanceDetails.region = instanceManager.region;
|
|
302
|
-
return instanceDetails;
|
|
303
|
-
});
|
|
304
|
-
} else {
|
|
305
|
-
return this.$q.reject();
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
AppengineInstanceDetailsController.$inject = ["$q", "app", "instance"];
|
|
310
|
-
const APPENGINE_INSTANCE_DETAILS_CTRL = "spinnaker.appengine.instanceDetails.controller";
|
|
311
|
-
module(APPENGINE_INSTANCE_DETAILS_CTRL, []).controller("appengineInstanceDetailsCtrl", AppengineInstanceDetailsController);
|
|
312
|
-
|
|
313
|
-
class AppengineLoadBalancerAdvancedSettingsCtrl {
|
|
314
|
-
constructor() {
|
|
315
|
-
this.state = { error: false };
|
|
316
|
-
}
|
|
317
|
-
disableMigrateTraffic() {
|
|
318
|
-
if (this.loadBalancer.splitDescription.allocationDescriptions.length !== 1) {
|
|
319
|
-
return true;
|
|
320
|
-
} else {
|
|
321
|
-
const targetServerGroupName = this.loadBalancer.splitDescription.allocationDescriptions[0].serverGroupName;
|
|
322
|
-
const targetServerGroup = this.loadBalancer.serverGroups.find((candidate) => candidate.name === targetServerGroupName);
|
|
323
|
-
if (targetServerGroup) {
|
|
324
|
-
return !targetServerGroup.allowsGradualTrafficMigration;
|
|
325
|
-
} else {
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
const appengineLoadBalancerAdvancedSettingsComponent = {
|
|
332
|
-
bindings: { loadBalancer: "=", application: "<" },
|
|
333
|
-
template: `
|
|
334
|
-
<ng-form name="advancedSettingsForm">
|
|
335
|
-
<div class="row">
|
|
336
|
-
<div class="form-group">
|
|
337
|
-
<div class="col-md-3 sm-label-right">
|
|
338
|
-
Migrate Traffic <help-field key="appengine.loadBalancer.migrateTraffic"></help-field>
|
|
339
|
-
</div>
|
|
340
|
-
<div class="col-md-9">
|
|
341
|
-
<div class="checkbox">
|
|
342
|
-
<input type="checkbox" ng-disabled="$ctrl.disableMigrateTraffic() && !($ctrl.loadBalancer.migrateTraffic = false)" ng-model="$ctrl.loadBalancer.migrateTraffic">
|
|
343
|
-
</div>
|
|
344
|
-
</div>
|
|
345
|
-
</div>
|
|
346
|
-
</div>
|
|
347
|
-
</ng-form>
|
|
348
|
-
`,
|
|
349
|
-
controller: AppengineLoadBalancerAdvancedSettingsCtrl
|
|
350
|
-
};
|
|
351
|
-
const APPENGINE_LOAD_BALANCER_ADVANCED_SETTINGS = "spinnaker.appengine.loadBalancer.advancedSettings.component";
|
|
352
|
-
module(APPENGINE_LOAD_BALANCER_ADVANCED_SETTINGS, []).component("appengineLoadBalancerAdvancedSettings", appengineLoadBalancerAdvancedSettingsComponent);
|
|
353
|
-
|
|
354
|
-
class AppengineAllocationConfigurationRowCtrl {
|
|
355
|
-
getServerGroupOptions() {
|
|
356
|
-
if (this.allocationDescription.serverGroupName) {
|
|
357
|
-
return uniq(this.serverGroupOptions.concat(this.allocationDescription.serverGroupName));
|
|
358
|
-
} else {
|
|
359
|
-
return this.serverGroupOptions;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
const appengineAllocationConfigurationRowComponent = {
|
|
364
|
-
bindings: {
|
|
365
|
-
allocationDescription: "<",
|
|
366
|
-
removeAllocation: "&",
|
|
367
|
-
serverGroupOptions: "<",
|
|
368
|
-
onAllocationChange: "&"
|
|
369
|
-
},
|
|
370
|
-
template: `
|
|
371
|
-
<div class="form-group">
|
|
372
|
-
<div class="row">
|
|
373
|
-
<div class="col-md-7">
|
|
374
|
-
<ui-select ng-model="$ctrl.allocationDescription.serverGroupName"
|
|
375
|
-
on-select="$ctrl.onAllocationChange()"
|
|
376
|
-
class="form-control input-sm">
|
|
377
|
-
<ui-select-match placeholder="Select...">
|
|
378
|
-
{{$select.selected}}
|
|
379
|
-
</ui-select-match>
|
|
380
|
-
<ui-select-choices repeat="serverGroup as serverGroup in $ctrl.getServerGroupOptions() | filter: $select.search">
|
|
381
|
-
<div ng-bind-html="serverGroup | highlight: $select.search"></div>
|
|
382
|
-
</ui-select-choices>
|
|
383
|
-
</ui-select>
|
|
384
|
-
</div>
|
|
385
|
-
<div class="col-md-3">
|
|
386
|
-
<div class="input-group input-group-sm">
|
|
387
|
-
<input type="number"
|
|
388
|
-
ng-model="$ctrl.allocationDescription.allocation"
|
|
389
|
-
required
|
|
390
|
-
class="form-control input-sm"
|
|
391
|
-
min="0"
|
|
392
|
-
ng-change="$ctrl.onAllocationChange()"
|
|
393
|
-
max="100"/>
|
|
394
|
-
<span class="input-group-addon">%</span>
|
|
395
|
-
</div>
|
|
396
|
-
</div>
|
|
397
|
-
<div class="col-md-2">
|
|
398
|
-
<a class="btn btn-link sm-label" ng-click="$ctrl.removeAllocation()">
|
|
399
|
-
<span class="glyphicon glyphicon-trash"></span>
|
|
400
|
-
</a>
|
|
401
|
-
</div>
|
|
402
|
-
</div>
|
|
403
|
-
</div>
|
|
404
|
-
`,
|
|
405
|
-
controller: AppengineAllocationConfigurationRowCtrl
|
|
406
|
-
};
|
|
407
|
-
const APPENGINE_ALLOCATION_CONFIGURATION_ROW = "spinnaker.appengine.allocationConfigurationRow.component";
|
|
408
|
-
module(APPENGINE_ALLOCATION_CONFIGURATION_ROW, []).component("appengineAllocationConfigurationRow", appengineAllocationConfigurationRowComponent);
|
|
409
|
-
|
|
410
|
-
class AppengineLoadBalancerSettingsController {
|
|
411
|
-
$onInit() {
|
|
412
|
-
this.updateServerGroupOptions();
|
|
413
|
-
}
|
|
414
|
-
addAllocation() {
|
|
415
|
-
const remainingServerGroups = this.serverGroupsWithoutAllocation();
|
|
416
|
-
if (remainingServerGroups.length) {
|
|
417
|
-
this.loadBalancer.splitDescription.allocationDescriptions.push({
|
|
418
|
-
serverGroupName: remainingServerGroups[0],
|
|
419
|
-
allocation: 0,
|
|
420
|
-
locatorType: "fromExisting"
|
|
421
|
-
});
|
|
422
|
-
if (this.loadBalancer.splitDescription.allocationDescriptions.length > 1 && !this.loadBalancer.splitDescription.shardBy) {
|
|
423
|
-
this.loadBalancer.splitDescription.shardBy = "IP";
|
|
424
|
-
}
|
|
425
|
-
this.updateServerGroupOptions();
|
|
426
|
-
} else if (this.forPipelineConfig) {
|
|
427
|
-
this.loadBalancer.splitDescription.allocationDescriptions.push({
|
|
428
|
-
allocation: 0,
|
|
429
|
-
locatorType: "text",
|
|
430
|
-
serverGroupName: ""
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
removeAllocation(index) {
|
|
435
|
-
this.loadBalancer.splitDescription.allocationDescriptions.splice(index, 1);
|
|
436
|
-
this.updateServerGroupOptions();
|
|
437
|
-
}
|
|
438
|
-
allocationIsInvalid() {
|
|
439
|
-
return this.loadBalancer.splitDescription.allocationDescriptions.reduce((sum, allocationDescription) => sum + allocationDescription.allocation, 0) !== 100;
|
|
440
|
-
}
|
|
441
|
-
updateServerGroupOptions() {
|
|
442
|
-
this.serverGroupOptions = this.serverGroupsWithoutAllocation();
|
|
443
|
-
}
|
|
444
|
-
showAddButton() {
|
|
445
|
-
if (this.forPipelineConfig) {
|
|
446
|
-
return true;
|
|
447
|
-
} else {
|
|
448
|
-
return this.serverGroupsWithoutAllocation().length > 0;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
showShardByOptions() {
|
|
452
|
-
return this.loadBalancer.splitDescription.allocationDescriptions.length > 1 || this.loadBalancer.migrateTraffic;
|
|
453
|
-
}
|
|
454
|
-
initializeAsTextInput(serverGroupName) {
|
|
455
|
-
if (this.forPipelineConfig) {
|
|
456
|
-
return !this.loadBalancer.serverGroups.map((serverGroup) => serverGroup.name).includes(serverGroupName);
|
|
457
|
-
} else {
|
|
458
|
-
return false;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
serverGroupsWithoutAllocation() {
|
|
462
|
-
const serverGroupsWithAllocation = this.loadBalancer.splitDescription.allocationDescriptions.map((description) => description.serverGroupName);
|
|
463
|
-
const allServerGroups = this.loadBalancer.serverGroups.map((serverGroup) => serverGroup.name);
|
|
464
|
-
return difference(allServerGroups, serverGroupsWithAllocation);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
const appengineLoadBalancerSettingsComponent = {
|
|
468
|
-
bindings: { loadBalancer: "=", forPipelineConfig: "<", application: "<" },
|
|
469
|
-
controller: AppengineLoadBalancerSettingsController,
|
|
470
|
-
templateUrl: "appengine/src/loadBalancer/configure/wizard/basicSettings.component.html"
|
|
471
|
-
};
|
|
472
|
-
const APPENGINE_LOAD_BALANCER_BASIC_SETTINGS = "spinnaker.appengine.loadBalancerSettings.component";
|
|
473
|
-
module(APPENGINE_LOAD_BALANCER_BASIC_SETTINGS, []).component("appengineLoadBalancerBasicSettings", appengineLoadBalancerSettingsComponent);
|
|
474
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
475
|
-
templateCache.put("appengine/src/loadBalancer/configure/wizard/basicSettings.component.html", `<ng-form name="basicSettingsForm">
|
|
476
|
-
<div class="row">
|
|
477
|
-
<div class="form-group">
|
|
478
|
-
<div class="col-md-3 sm-label-right">
|
|
479
|
-
Allocations
|
|
480
|
-
<help-field key="appengine.loadBalancer.allocations"></help-field>
|
|
481
|
-
</div>
|
|
482
|
-
<div class="col-md-9">
|
|
483
|
-
<div ng-if="!$ctrl.forPipelineConfig">
|
|
484
|
-
<appengine-allocation-configuration-row
|
|
485
|
-
ng-repeat="description in $ctrl.loadBalancer.splitDescription.allocationDescriptions"
|
|
486
|
-
allocation-description="description"
|
|
487
|
-
server-group-options="$ctrl.serverGroupOptions"
|
|
488
|
-
on-allocation-change="$ctrl.updateServerGroupOptions()"
|
|
489
|
-
remove-allocation="$ctrl.removeAllocation($index)"
|
|
490
|
-
>
|
|
491
|
-
</appengine-allocation-configuration-row>
|
|
492
|
-
</div>
|
|
493
|
-
<div ng-if="$ctrl.forPipelineConfig">
|
|
494
|
-
<appengine-stage-allocation-configuration-row
|
|
495
|
-
ng-repeat="description in $ctrl.loadBalancer.splitDescription.allocationDescriptions"
|
|
496
|
-
allocation-description="description"
|
|
497
|
-
application="$ctrl.application"
|
|
498
|
-
region="{{ $ctrl.loadBalancer.region }}"
|
|
499
|
-
account="{{ $ctrl.loadBalancer.account || $ctrl.loadBalancer.credentials }}"
|
|
500
|
-
server-group-options="$ctrl.serverGroupOptions"
|
|
501
|
-
on-allocation-change="$ctrl.updateServerGroupOptions()"
|
|
502
|
-
remove-allocation="$ctrl.removeAllocation($index)"
|
|
503
|
-
>
|
|
504
|
-
</appengine-stage-allocation-configuration-row>
|
|
505
|
-
</div>
|
|
506
|
-
<button class="add-new col-md-11" ng-if="$ctrl.showAddButton()" ng-click="$ctrl.addAllocation()">
|
|
507
|
-
<span class="glyphicon glyphicon-plus-sign"></span> Add allocation
|
|
508
|
-
</button>
|
|
509
|
-
</div>
|
|
510
|
-
</div>
|
|
511
|
-
<div class="form-group" ng-if="$ctrl.allocationIsInvalid()">
|
|
512
|
-
<div class="col-md-12 text-center">
|
|
513
|
-
<p class="alert alert-warning">Allocations must sum to 100%.</p>
|
|
514
|
-
</div>
|
|
515
|
-
</div>
|
|
516
|
-
</div>
|
|
517
|
-
|
|
518
|
-
<div class="row" ng-if="$ctrl.showShardByOptions()">
|
|
519
|
-
<div class="form-group">
|
|
520
|
-
<div class="col-md-3 sm-label-right">Shard By</div>
|
|
521
|
-
<div class="col-md-9">
|
|
522
|
-
<form class="form-inline">
|
|
523
|
-
<div class="form-group">
|
|
524
|
-
<label class="radio-inline">
|
|
525
|
-
<input type="radio" ng-model="$ctrl.loadBalancer.splitDescription.shardBy" value="IP" />
|
|
526
|
-
IP
|
|
527
|
-
<help-field key="appengine.loadBalancer.shardBy.ip"></help-field>
|
|
528
|
-
</label>
|
|
529
|
-
<label class="radio-inline">
|
|
530
|
-
<input type="radio" ng-model="$ctrl.loadBalancer.splitDescription.shardBy" value="COOKIE" />
|
|
531
|
-
Cookie
|
|
532
|
-
<help-field key="appengine.loadBalancer.shardBy.cookie"></help-field>
|
|
533
|
-
</label>
|
|
534
|
-
</div>
|
|
535
|
-
</form>
|
|
536
|
-
</div>
|
|
537
|
-
</div>
|
|
538
|
-
</div>
|
|
539
|
-
</ng-form>
|
|
540
|
-
`);
|
|
541
|
-
}]);
|
|
542
|
-
|
|
543
|
-
class AppengineStageAllocationLabelCtrl {
|
|
544
|
-
static mapTargetCoordinateToLabel(targetCoordinate) {
|
|
545
|
-
const target = StageConstants.TARGET_LIST.find((t) => t.val === targetCoordinate);
|
|
546
|
-
if (target) {
|
|
547
|
-
return target.label;
|
|
548
|
-
} else {
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
$doCheck() {
|
|
553
|
-
this.setInputViewValue();
|
|
554
|
-
}
|
|
555
|
-
setInputViewValue() {
|
|
556
|
-
switch (this.allocationDescription.locatorType) {
|
|
557
|
-
case "text":
|
|
558
|
-
this.inputViewValue = this.allocationDescription.serverGroupName;
|
|
559
|
-
break;
|
|
560
|
-
case "fromExisting":
|
|
561
|
-
this.inputViewValue = this.allocationDescription.serverGroupName;
|
|
562
|
-
break;
|
|
563
|
-
case "targetCoordinate":
|
|
564
|
-
if (this.allocationDescription.cluster && this.allocationDescription.target) {
|
|
565
|
-
const targetLabel = AppengineStageAllocationLabelCtrl.mapTargetCoordinateToLabel(this.allocationDescription.target);
|
|
566
|
-
this.inputViewValue = `${targetLabel} (${this.allocationDescription.cluster})`;
|
|
567
|
-
} else {
|
|
568
|
-
this.inputViewValue = null;
|
|
569
|
-
}
|
|
570
|
-
break;
|
|
571
|
-
default:
|
|
572
|
-
this.inputViewValue = null;
|
|
573
|
-
break;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
const appengineStageAllocationLabel = {
|
|
578
|
-
bindings: { allocationDescription: "<" },
|
|
579
|
-
controller: AppengineStageAllocationLabelCtrl,
|
|
580
|
-
template: `<input ng-model="$ctrl.inputViewValue" type="text" class="form-control input-sm" readonly/>`
|
|
581
|
-
};
|
|
582
|
-
class AppengineStageAllocationConfigurationRowCtrl {
|
|
583
|
-
constructor() {
|
|
584
|
-
this.targets = StageConstants.TARGET_LIST;
|
|
585
|
-
}
|
|
586
|
-
$onInit() {
|
|
587
|
-
const clusterFilter = AppListExtractor.clusterFilterForCredentialsAndRegion(this.account, this.region);
|
|
588
|
-
this.clusterList = AppListExtractor.getClusters([this.application], clusterFilter);
|
|
589
|
-
}
|
|
590
|
-
getServerGroupOptions() {
|
|
591
|
-
if (this.allocationDescription.serverGroupName) {
|
|
592
|
-
return uniq(this.serverGroupOptions.concat(this.allocationDescription.serverGroupName));
|
|
593
|
-
} else {
|
|
594
|
-
return this.serverGroupOptions;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
onLocatorTypeChange() {
|
|
598
|
-
if (!this.serverGroupOptions.includes(this.allocationDescription.serverGroupName)) {
|
|
599
|
-
delete this.allocationDescription.serverGroupName;
|
|
600
|
-
}
|
|
601
|
-
this.onAllocationChange();
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
const appengineStageAllocationConfigurationRow = {
|
|
605
|
-
bindings: {
|
|
606
|
-
application: "<",
|
|
607
|
-
region: "@",
|
|
608
|
-
account: "@",
|
|
609
|
-
allocationDescription: "<",
|
|
610
|
-
removeAllocation: "&",
|
|
611
|
-
serverGroupOptions: "<",
|
|
612
|
-
onAllocationChange: "&"
|
|
613
|
-
},
|
|
614
|
-
controller: AppengineStageAllocationConfigurationRowCtrl,
|
|
615
|
-
templateUrl: "appengine/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html"
|
|
616
|
-
};
|
|
617
|
-
const APPENGINE_STAGE_ALLOCATION_CONFIGURATION_ROW = "spinnaker.appengine.stageAllocationConfigurationRow.component";
|
|
618
|
-
module(APPENGINE_STAGE_ALLOCATION_CONFIGURATION_ROW, []).component("appengineStageAllocationConfigurationRow", appengineStageAllocationConfigurationRow).component("appengineStageAllocationLabel", appengineStageAllocationLabel);
|
|
619
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
620
|
-
templateCache.put("appengine/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html", `<div class="form-group">
|
|
621
|
-
<div class="row">
|
|
622
|
-
<div class="col-md-7">
|
|
623
|
-
<appengine-stage-allocation-label allocation-description="$ctrl.allocationDescription">
|
|
624
|
-
</appengine-stage-allocation-label>
|
|
625
|
-
</div>
|
|
626
|
-
<div class="col-md-3">
|
|
627
|
-
<div class="input-group input-group-sm">
|
|
628
|
-
<input
|
|
629
|
-
type="number"
|
|
630
|
-
ng-model="$ctrl.allocationDescription.allocation"
|
|
631
|
-
required
|
|
632
|
-
class="form-control input-sm"
|
|
633
|
-
min="0"
|
|
634
|
-
ng-change="$ctrl.onAllocationChange()"
|
|
635
|
-
max="100"
|
|
636
|
-
/>
|
|
637
|
-
<span class="input-group-addon">%</span>
|
|
638
|
-
</div>
|
|
639
|
-
</div>
|
|
640
|
-
<div class="col-md-2">
|
|
641
|
-
<a class="btn btn-link sm-label" ng-click="$ctrl.removeAllocation()">
|
|
642
|
-
<span class="glyphicon glyphicon-trash"></span>
|
|
643
|
-
</a>
|
|
644
|
-
</div>
|
|
645
|
-
</div>
|
|
646
|
-
</div>
|
|
647
|
-
<div class="form-group">
|
|
648
|
-
<div class="well col-md-11" style="padding-top: 5px; padding-bottom: 10px">
|
|
649
|
-
<div class="row">
|
|
650
|
-
<div class="form-group">
|
|
651
|
-
<div class="col-md-3 sm-label-right">Locator</div>
|
|
652
|
-
<div class="col-md-7">
|
|
653
|
-
<form>
|
|
654
|
-
<div class="form-group">
|
|
655
|
-
<label class="small" style="font-weight: normal">
|
|
656
|
-
<input
|
|
657
|
-
type="radio"
|
|
658
|
-
ng-change="$ctrl.onLocatorTypeChange()"
|
|
659
|
-
ng-model="$ctrl.allocationDescription.locatorType"
|
|
660
|
-
value="fromExisting"
|
|
661
|
-
/>
|
|
662
|
-
Existing server group
|
|
663
|
-
</label>
|
|
664
|
-
<br />
|
|
665
|
-
<label class="small" style="font-weight: normal">
|
|
666
|
-
<input
|
|
667
|
-
type="radio"
|
|
668
|
-
ng-change="$ctrl.onLocatorTypeChange()"
|
|
669
|
-
ng-model="$ctrl.allocationDescription.locatorType"
|
|
670
|
-
value="targetCoordinate"
|
|
671
|
-
/>
|
|
672
|
-
Coordinates
|
|
673
|
-
</label>
|
|
674
|
-
<br />
|
|
675
|
-
<label class="small" style="font-weight: normal">
|
|
676
|
-
<input
|
|
677
|
-
type="radio"
|
|
678
|
-
ng-change="$ctrl.onLocatorTypeChange()"
|
|
679
|
-
ng-model="$ctrl.allocationDescription.locatorType"
|
|
680
|
-
value="text"
|
|
681
|
-
/>
|
|
682
|
-
Text input
|
|
683
|
-
</label>
|
|
684
|
-
</div>
|
|
685
|
-
</form>
|
|
686
|
-
</div>
|
|
687
|
-
</div>
|
|
688
|
-
</div>
|
|
689
|
-
<div class="row">
|
|
690
|
-
<div class="form-group" ng-switch on="$ctrl.allocationDescription.locatorType">
|
|
691
|
-
<div ng-switch-when="fromExisting">
|
|
692
|
-
<div class="col-md-3 sm-label-right" style="padding-left: 0px">Server group</div>
|
|
693
|
-
<div class="col-md-7">
|
|
694
|
-
<ui-select
|
|
695
|
-
ng-model="$ctrl.allocationDescription.serverGroupName"
|
|
696
|
-
on-select="$ctrl.onAllocationChange()"
|
|
697
|
-
class="form-control input-sm"
|
|
698
|
-
>
|
|
699
|
-
<ui-select-match placeholder="Select...">
|
|
700
|
-
{{ $select.selected }}
|
|
701
|
-
</ui-select-match>
|
|
702
|
-
<ui-select-choices
|
|
703
|
-
repeat="serverGroup as serverGroup in $ctrl.getServerGroupOptions() | filter: $select.search"
|
|
704
|
-
>
|
|
705
|
-
<div ng-bind-html="serverGroup | highlight: $select.search"></div>
|
|
706
|
-
</ui-select-choices>
|
|
707
|
-
</ui-select>
|
|
708
|
-
</div>
|
|
709
|
-
</div>
|
|
710
|
-
<div ng-switch-when="targetCoordinate">
|
|
711
|
-
<div class="col-md-3 sm-label-right">Cluster</div>
|
|
712
|
-
<div class="col-md-7">
|
|
713
|
-
<cluster-selector
|
|
714
|
-
class="small"
|
|
715
|
-
clusters="$ctrl.clusterList"
|
|
716
|
-
model="$ctrl.allocationDescription.cluster"
|
|
717
|
-
></cluster-selector>
|
|
718
|
-
</div>
|
|
719
|
-
<div class="col-md-3 sm-label-right">Target</div>
|
|
720
|
-
<div class="col-md-7">
|
|
721
|
-
<target-select model="$ctrl.allocationDescription" options="$ctrl.targets"></target-select>
|
|
722
|
-
</div>
|
|
723
|
-
</div>
|
|
724
|
-
<div ng-switch-when="text">
|
|
725
|
-
<div class="col-md-3 sm-label-right">
|
|
726
|
-
Text
|
|
727
|
-
<help-field key="appengine.loadBalancer.textLocator"></help-field>
|
|
728
|
-
</div>
|
|
729
|
-
<div class="col-md-7">
|
|
730
|
-
<input class="form-control input-sm" type="text" ng-model="$ctrl.allocationDescription.serverGroupName" />
|
|
731
|
-
</div>
|
|
732
|
-
</div>
|
|
733
|
-
</div>
|
|
734
|
-
</div>
|
|
735
|
-
</div>
|
|
736
|
-
</div>
|
|
737
|
-
`);
|
|
738
|
-
}]);
|
|
739
|
-
|
|
740
|
-
class AppengineLoadBalancerUpsertDescription {
|
|
741
|
-
static convertTrafficSplitToTrafficSplitDescription(split) {
|
|
742
|
-
const allocationDescriptions = reduce(split.allocations, (acc, allocation, serverGroupName) => {
|
|
743
|
-
return acc.concat({ serverGroupName, allocation, locatorType: "fromExisting" });
|
|
744
|
-
}, []);
|
|
745
|
-
return { shardBy: split.shardBy, allocationDescriptions };
|
|
746
|
-
}
|
|
747
|
-
constructor(loadBalancer) {
|
|
748
|
-
this.credentials = loadBalancer.account || loadBalancer.credentials;
|
|
749
|
-
this.account = this.credentials;
|
|
750
|
-
this.cloudProvider = loadBalancer.cloudProvider;
|
|
751
|
-
this.loadBalancerName = loadBalancer.name;
|
|
752
|
-
this.name = loadBalancer.name;
|
|
753
|
-
this.region = loadBalancer.region;
|
|
754
|
-
this.migrateTraffic = loadBalancer.migrateTraffic || false;
|
|
755
|
-
this.serverGroups = loadBalancer.serverGroups;
|
|
756
|
-
}
|
|
757
|
-
mapAllocationsToDecimals() {
|
|
758
|
-
this.splitDescription.allocationDescriptions.forEach((description) => {
|
|
759
|
-
description.allocation = description.allocation / 100;
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
mapAllocationsToPercentages() {
|
|
763
|
-
this.splitDescription.allocationDescriptions.forEach((description) => {
|
|
764
|
-
description.allocation = Math.round(description.allocation * 1e3) / 10;
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
class AppengineLoadBalancerTransformer {
|
|
769
|
-
constructor($q) {
|
|
770
|
-
this.$q = $q;
|
|
771
|
-
}
|
|
772
|
-
normalizeLoadBalancer(loadBalancer) {
|
|
773
|
-
loadBalancer.provider = loadBalancer.type;
|
|
774
|
-
loadBalancer.instanceCounts = this.buildInstanceCounts(loadBalancer.serverGroups);
|
|
775
|
-
loadBalancer.instances = [];
|
|
776
|
-
loadBalancer.serverGroups.forEach((serverGroup) => {
|
|
777
|
-
serverGroup.account = loadBalancer.account;
|
|
778
|
-
serverGroup.region = loadBalancer.region;
|
|
779
|
-
if (serverGroup.detachedInstances) {
|
|
780
|
-
serverGroup.detachedInstances = serverGroup.detachedInstances.map((id) => ({ id }));
|
|
781
|
-
}
|
|
782
|
-
serverGroup.instances = serverGroup.instances.concat(serverGroup.detachedInstances || []).map((instance) => this.transformInstance(instance, loadBalancer));
|
|
783
|
-
});
|
|
784
|
-
const activeServerGroups = filter(loadBalancer.serverGroups, { isDisabled: false });
|
|
785
|
-
loadBalancer.instances = chain(activeServerGroups).map("instances").flatten().value();
|
|
786
|
-
return this.$q.resolve(loadBalancer);
|
|
787
|
-
}
|
|
788
|
-
convertLoadBalancerForEditing(loadBalancer, application) {
|
|
789
|
-
return application.getDataSource("loadBalancers").ready().then(() => {
|
|
790
|
-
const upToDateLoadBalancer = application.getDataSource("loadBalancers").data.find((candidate) => {
|
|
791
|
-
return candidate.name === loadBalancer.name && (candidate.account === loadBalancer.account || candidate.account === loadBalancer.credentials);
|
|
792
|
-
});
|
|
793
|
-
if (upToDateLoadBalancer) {
|
|
794
|
-
loadBalancer.serverGroups = cloneDeep(upToDateLoadBalancer.serverGroups);
|
|
795
|
-
}
|
|
796
|
-
return loadBalancer;
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
convertLoadBalancerToUpsertDescription(loadBalancer) {
|
|
800
|
-
return new AppengineLoadBalancerUpsertDescription(loadBalancer);
|
|
801
|
-
}
|
|
802
|
-
buildInstanceCounts(serverGroups) {
|
|
803
|
-
const instanceCounts = chain(serverGroups).map("instances").flatten().reduce((acc, instance) => {
|
|
804
|
-
if (has(instance, "health.state")) {
|
|
805
|
-
acc[camelCase(instance.health.state)]++;
|
|
806
|
-
}
|
|
807
|
-
return acc;
|
|
808
|
-
}, { up: 0, down: 0, outOfService: 0, succeeded: 0, failed: 0, starting: 0, unknown: 0 }).value();
|
|
809
|
-
instanceCounts.outOfService += chain(serverGroups).map("detachedInstances").flatten().value().length;
|
|
810
|
-
return instanceCounts;
|
|
811
|
-
}
|
|
812
|
-
transformInstance(instance, loadBalancer) {
|
|
813
|
-
instance.provider = loadBalancer.type;
|
|
814
|
-
instance.account = loadBalancer.account;
|
|
815
|
-
instance.region = loadBalancer.region;
|
|
816
|
-
instance.loadBalancers = [loadBalancer.name];
|
|
817
|
-
const health = instance.health || {};
|
|
818
|
-
instance.healthState = get(instance, "health.state") || "OutOfService";
|
|
819
|
-
instance.health = [health];
|
|
820
|
-
return instance;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
AppengineLoadBalancerTransformer.$inject = ["$q"];
|
|
824
|
-
const APPENGINE_LOAD_BALANCER_TRANSFORMER = "spinnaker.appengine.loadBalancer.transformer.service";
|
|
825
|
-
module(APPENGINE_LOAD_BALANCER_TRANSFORMER, []).service("appengineLoadBalancerTransformer", AppengineLoadBalancerTransformer);
|
|
826
|
-
|
|
827
|
-
function styleInject(css, ref) {
|
|
828
|
-
if ( ref === void 0 ) ref = {};
|
|
829
|
-
var insertAt = ref.insertAt;
|
|
830
|
-
|
|
831
|
-
if (!css || typeof document === 'undefined') { return; }
|
|
832
|
-
|
|
833
|
-
var head = document.head || document.getElementsByTagName('head')[0];
|
|
834
|
-
var style = document.createElement('style');
|
|
835
|
-
style.type = 'text/css';
|
|
836
|
-
|
|
837
|
-
if (insertAt === 'top') {
|
|
838
|
-
if (head.firstChild) {
|
|
839
|
-
head.insertBefore(style, head.firstChild);
|
|
840
|
-
} else {
|
|
841
|
-
head.appendChild(style);
|
|
842
|
-
}
|
|
843
|
-
} else {
|
|
844
|
-
head.appendChild(style);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
if (style.styleSheet) {
|
|
848
|
-
style.styleSheet.cssText = css;
|
|
849
|
-
} else {
|
|
850
|
-
style.appendChild(document.createTextNode(css));
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
var css_248z$2 = "appengine-load-balancer-basic-settings a.btn.btn-link {\n padding: 0;\n}\nappengine-load-balancer-basic-settings .form-group {\n margin-top: 0.4rem;\n}\nappengine-load-balancer-advanced-settings .checkbox input[type='checkbox'] {\n margin: 0 0 0.1rem 0;\n}\n";
|
|
855
|
-
styleInject(css_248z$2);
|
|
856
|
-
|
|
857
|
-
class AppengineLoadBalancerWizardController {
|
|
858
|
-
constructor($scope, $state, $uibModalInstance, application, loadBalancer, isNew, forPipelineConfig, appengineLoadBalancerTransformer, wizardSubFormValidation) {
|
|
859
|
-
this.$scope = $scope;
|
|
860
|
-
this.$state = $state;
|
|
861
|
-
this.$uibModalInstance = $uibModalInstance;
|
|
862
|
-
this.application = application;
|
|
863
|
-
this.isNew = isNew;
|
|
864
|
-
this.forPipelineConfig = forPipelineConfig;
|
|
865
|
-
this.appengineLoadBalancerTransformer = appengineLoadBalancerTransformer;
|
|
866
|
-
this.wizardSubFormValidation = wizardSubFormValidation;
|
|
867
|
-
this.state = { loading: true };
|
|
868
|
-
this.submitButtonLabel = this.forPipelineConfig ? "Done" : "Update";
|
|
869
|
-
if (this.isNew) {
|
|
870
|
-
this.heading = "Create New Load Balancer";
|
|
871
|
-
} else {
|
|
872
|
-
this.heading = `Edit ${[
|
|
873
|
-
loadBalancer.name,
|
|
874
|
-
loadBalancer.region,
|
|
875
|
-
loadBalancer.account || loadBalancer.credentials
|
|
876
|
-
].join(":")}`;
|
|
877
|
-
this.appengineLoadBalancerTransformer.convertLoadBalancerForEditing(loadBalancer, application).then((convertedLoadBalancer) => {
|
|
878
|
-
this.loadBalancer = this.appengineLoadBalancerTransformer.convertLoadBalancerToUpsertDescription(convertedLoadBalancer);
|
|
879
|
-
if (loadBalancer.split && !this.loadBalancer.splitDescription) {
|
|
880
|
-
this.loadBalancer.splitDescription = AppengineLoadBalancerUpsertDescription.convertTrafficSplitToTrafficSplitDescription(loadBalancer.split);
|
|
881
|
-
} else {
|
|
882
|
-
this.loadBalancer.splitDescription = loadBalancer.splitDescription;
|
|
883
|
-
}
|
|
884
|
-
this.loadBalancer.mapAllocationsToPercentages();
|
|
885
|
-
this.setTaskMonitor();
|
|
886
|
-
this.initializeFormValidation();
|
|
887
|
-
this.state.loading = false;
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
submit() {
|
|
892
|
-
const description = cloneDeep(this.loadBalancer);
|
|
893
|
-
description.mapAllocationsToDecimals();
|
|
894
|
-
delete description.serverGroups;
|
|
895
|
-
if (this.forPipelineConfig) {
|
|
896
|
-
return this.$uibModalInstance.close(description);
|
|
897
|
-
} else {
|
|
898
|
-
return this.taskMonitor.submit(() => {
|
|
899
|
-
return LoadBalancerWriter.upsertLoadBalancer(description, this.application, "Update");
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
cancel() {
|
|
904
|
-
this.$uibModalInstance.dismiss();
|
|
905
|
-
}
|
|
906
|
-
showSubmitButton() {
|
|
907
|
-
return this.wizardSubFormValidation.subFormsAreValid();
|
|
908
|
-
}
|
|
909
|
-
setTaskMonitor() {
|
|
910
|
-
this.taskMonitor = new TaskMonitor({
|
|
911
|
-
application: this.application,
|
|
912
|
-
title: "Updating your load balancer",
|
|
913
|
-
modalInstance: this.$uibModalInstance,
|
|
914
|
-
onTaskComplete: () => this.onTaskComplete()
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
initializeFormValidation() {
|
|
918
|
-
this.wizardSubFormValidation.config({ form: "form", scope: this.$scope }).register({
|
|
919
|
-
page: "basic-settings",
|
|
920
|
-
subForm: "basicSettingsForm",
|
|
921
|
-
validators: [
|
|
922
|
-
{
|
|
923
|
-
watchString: "ctrl.loadBalancer.splitDescription",
|
|
924
|
-
validator: (splitDescription) => {
|
|
925
|
-
return splitDescription.allocationDescriptions.reduce((sum, description) => sum + description.allocation, 0) === 100;
|
|
926
|
-
},
|
|
927
|
-
watchDeep: true
|
|
928
|
-
}
|
|
929
|
-
]
|
|
930
|
-
}).register({ page: "advanced-settings", subForm: "advancedSettingsForm" });
|
|
931
|
-
}
|
|
932
|
-
onTaskComplete() {
|
|
933
|
-
this.application.getDataSource("loadBalancers").refresh();
|
|
934
|
-
this.application.getDataSource("loadBalancers").onNextRefresh(this.$scope, () => this.onApplicationRefresh());
|
|
935
|
-
}
|
|
936
|
-
onApplicationRefresh() {
|
|
937
|
-
if (this.$scope.$$destroyed) {
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
this.$uibModalInstance.dismiss();
|
|
941
|
-
const newStateParams = {
|
|
942
|
-
name: this.loadBalancer.name,
|
|
943
|
-
accountId: this.loadBalancer.credentials,
|
|
944
|
-
region: this.loadBalancer.region,
|
|
945
|
-
provider: "appengine"
|
|
946
|
-
};
|
|
947
|
-
if (!this.$state.includes("**.loadBalancerDetails")) {
|
|
948
|
-
this.$state.go(".loadBalancerDetails", newStateParams);
|
|
949
|
-
} else {
|
|
950
|
-
this.$state.go("^.loadBalancerDetails", newStateParams);
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
AppengineLoadBalancerWizardController.$inject = [
|
|
955
|
-
"$scope",
|
|
956
|
-
"$state",
|
|
957
|
-
"$uibModalInstance",
|
|
958
|
-
"application",
|
|
959
|
-
"loadBalancer",
|
|
960
|
-
"isNew",
|
|
961
|
-
"forPipelineConfig",
|
|
962
|
-
"appengineLoadBalancerTransformer",
|
|
963
|
-
"wizardSubFormValidation"
|
|
964
|
-
];
|
|
965
|
-
const APPENGINE_LOAD_BALANCER_WIZARD_CTRL = "spinnaker.appengine.loadBalancer.wizard.controller";
|
|
966
|
-
module(APPENGINE_LOAD_BALANCER_WIZARD_CTRL, []).controller("appengineLoadBalancerWizardCtrl", AppengineLoadBalancerWizardController);
|
|
967
|
-
|
|
968
|
-
class AppengineLoadBalancerDetailsController {
|
|
969
|
-
constructor($uibModal, $state, $scope, loadBalancer, app) {
|
|
970
|
-
this.$uibModal = $uibModal;
|
|
971
|
-
this.$state = $state;
|
|
972
|
-
this.$scope = $scope;
|
|
973
|
-
this.app = app;
|
|
974
|
-
this.state = { loading: true };
|
|
975
|
-
this.dispatchRules = [];
|
|
976
|
-
this.loadBalancerFromParams = loadBalancer;
|
|
977
|
-
this.app.getDataSource("loadBalancers").ready().then(() => this.extractLoadBalancer());
|
|
978
|
-
}
|
|
979
|
-
editLoadBalancer() {
|
|
980
|
-
this.$uibModal.open({
|
|
981
|
-
templateUrl: "appengine/src/loadBalancer/configure/wizard/wizard.html",
|
|
982
|
-
controller: "appengineLoadBalancerWizardCtrl as ctrl",
|
|
983
|
-
size: "lg",
|
|
984
|
-
resolve: {
|
|
985
|
-
application: () => this.app,
|
|
986
|
-
loadBalancer: () => cloneDeep(this.loadBalancer),
|
|
987
|
-
isNew: () => false,
|
|
988
|
-
forPipelineConfig: () => false
|
|
989
|
-
}
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
deleteLoadBalancer() {
|
|
993
|
-
const taskMonitor = {
|
|
994
|
-
application: this.app,
|
|
995
|
-
title: "Deleting " + this.loadBalancer.name
|
|
996
|
-
};
|
|
997
|
-
const submitMethod = () => {
|
|
998
|
-
const loadBalancer = {
|
|
999
|
-
cloudProvider: this.loadBalancer.cloudProvider,
|
|
1000
|
-
loadBalancerName: this.loadBalancer.name,
|
|
1001
|
-
credentials: this.loadBalancer.account
|
|
1002
|
-
};
|
|
1003
|
-
return LoadBalancerWriter.deleteLoadBalancer(loadBalancer, this.app);
|
|
1004
|
-
};
|
|
1005
|
-
ConfirmationModalService.confirm({
|
|
1006
|
-
header: "Really delete " + this.loadBalancer.name + "?",
|
|
1007
|
-
buttonText: "Delete " + this.loadBalancer.name,
|
|
1008
|
-
body: this.getConfirmationModalBodyHtml(),
|
|
1009
|
-
account: this.loadBalancer.account,
|
|
1010
|
-
taskMonitorConfig: taskMonitor,
|
|
1011
|
-
submitMethod
|
|
1012
|
-
});
|
|
1013
|
-
}
|
|
1014
|
-
canDeleteLoadBalancer() {
|
|
1015
|
-
return this.loadBalancer.name !== "default";
|
|
1016
|
-
}
|
|
1017
|
-
extractLoadBalancer() {
|
|
1018
|
-
this.loadBalancer = this.app.getDataSource("loadBalancers").data.find((test) => {
|
|
1019
|
-
return test.name === this.loadBalancerFromParams.name && test.account === this.loadBalancerFromParams.accountId;
|
|
1020
|
-
});
|
|
1021
|
-
if (this.loadBalancer) {
|
|
1022
|
-
this.state.loading = false;
|
|
1023
|
-
this.buildDispatchRules();
|
|
1024
|
-
this.app.getDataSource("loadBalancers").onRefresh(this.$scope, () => this.extractLoadBalancer());
|
|
1025
|
-
} else {
|
|
1026
|
-
this.autoClose();
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
buildDispatchRules() {
|
|
1030
|
-
this.dispatchRules = [];
|
|
1031
|
-
if (this.loadBalancer && this.loadBalancer.dispatchRules) {
|
|
1032
|
-
this.loadBalancer.dispatchRules.forEach((rule) => {
|
|
1033
|
-
if (rule.service === this.loadBalancer.name) {
|
|
1034
|
-
this.dispatchRules.push(rule.domain + rule.path);
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
getConfirmationModalBodyHtml() {
|
|
1040
|
-
const serverGroupNames = this.loadBalancer.serverGroups.map((serverGroup) => serverGroup.name);
|
|
1041
|
-
const hasAny = serverGroupNames ? serverGroupNames.length > 0 : false;
|
|
1042
|
-
const hasMoreThanOne = serverGroupNames ? serverGroupNames.length > 1 : false;
|
|
1043
|
-
if (hasAny) {
|
|
1044
|
-
if (hasMoreThanOne) {
|
|
1045
|
-
const listOfServerGroupNames = serverGroupNames.map((name) => `<li>${name}</li>`).join("");
|
|
1046
|
-
return `<div class="alert alert-warning">
|
|
1047
|
-
<p>
|
|
1048
|
-
Deleting <b>${this.loadBalancer.name}</b> will destroy the following server groups:
|
|
1049
|
-
<ul>
|
|
1050
|
-
${listOfServerGroupNames}
|
|
1051
|
-
</ul>
|
|
1052
|
-
</p>
|
|
1053
|
-
</div>
|
|
1054
|
-
`;
|
|
1055
|
-
} else {
|
|
1056
|
-
return `<div class="alert alert-warning">
|
|
1057
|
-
<p>
|
|
1058
|
-
Deleting <b>${this.loadBalancer.name}</b> will destroy <b>${serverGroupNames[0]}</b>.
|
|
1059
|
-
</p>
|
|
1060
|
-
</div>
|
|
1061
|
-
`;
|
|
1062
|
-
}
|
|
1063
|
-
} else {
|
|
1064
|
-
return null;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
autoClose() {
|
|
1068
|
-
if (this.$scope.$$destroyed) {
|
|
1069
|
-
return;
|
|
1070
|
-
} else {
|
|
1071
|
-
this.$state.params.allowModalToStayOpen = true;
|
|
1072
|
-
this.$state.go("^", null, { location: "replace" });
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
AppengineLoadBalancerDetailsController.$inject = ["$uibModal", "$state", "$scope", "loadBalancer", "app"];
|
|
1077
|
-
const APPENGINE_LOAD_BALANCER_DETAILS_CTRL = "spinnaker.appengine.loadBalancerDetails.controller";
|
|
1078
|
-
module(APPENGINE_LOAD_BALANCER_DETAILS_CTRL, []).controller("appengineLoadBalancerDetailsCtrl", AppengineLoadBalancerDetailsController);
|
|
1079
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1080
|
-
templateCache.put("appengine/src/loadBalancer/configure/wizard/wizard.html", `<form name="form">
|
|
1081
|
-
<div ng-if="ctrl.state.loading && !ctrl.isNew" style="height: 200px" class="horizontal center middle">
|
|
1082
|
-
<loading-spinner size="'small'"></loading-spinner>
|
|
1083
|
-
</div>
|
|
1084
|
-
<v2-modal-wizard
|
|
1085
|
-
heading="{{::ctrl.heading}}"
|
|
1086
|
-
task-monitor="ctrl.taskMonitor"
|
|
1087
|
-
dismiss="$dismiss()"
|
|
1088
|
-
ng-if="!ctrl.state.loading || ctrl.isNew"
|
|
1089
|
-
>
|
|
1090
|
-
<div ng-if="!ctrl.isNew">
|
|
1091
|
-
<v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">
|
|
1092
|
-
<appengine-load-balancer-basic-settings
|
|
1093
|
-
load-balancer="ctrl.loadBalancer"
|
|
1094
|
-
application="ctrl.application"
|
|
1095
|
-
for-pipeline-config="ctrl.forPipelineConfig"
|
|
1096
|
-
></appengine-load-balancer-basic-settings>
|
|
1097
|
-
</v2-wizard-page>
|
|
1098
|
-
<v2-wizard-page key="advanced-settings" label="Advanced Settings" mark-complete-on-view="false">
|
|
1099
|
-
<appengine-load-balancer-advanced-settings
|
|
1100
|
-
load-balancer="ctrl.loadBalancer"
|
|
1101
|
-
></appengine-load-balancer-advanced-settings>
|
|
1102
|
-
</v2-wizard-page>
|
|
1103
|
-
</div>
|
|
1104
|
-
</v2-modal-wizard>
|
|
1105
|
-
<appengine-load-balancer-message
|
|
1106
|
-
ng-if="ctrl.isNew"
|
|
1107
|
-
column-offset="1"
|
|
1108
|
-
columns="10"
|
|
1109
|
-
show-create-message="true"
|
|
1110
|
-
></appengine-load-balancer-message>
|
|
1111
|
-
<div class="modal-footer">
|
|
1112
|
-
<button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>
|
|
1113
|
-
<submit-button
|
|
1114
|
-
ng-if="!ctrl.isNew && ctrl.showSubmitButton()"
|
|
1115
|
-
label="ctrl.submitButtonLabel"
|
|
1116
|
-
is-disabled="appengineLoadBalancerForm.$invalid || ctrl.taskMonitor.submitting || ctrl.state.loading"
|
|
1117
|
-
submitting="ctrl.taskMonitor.submitting"
|
|
1118
|
-
on-click="ctrl.submit()"
|
|
1119
|
-
is-new="ctrl.isNew"
|
|
1120
|
-
>
|
|
1121
|
-
</submit-button>
|
|
1122
|
-
</div>
|
|
1123
|
-
</form>
|
|
1124
|
-
`);
|
|
1125
|
-
}]);
|
|
1126
|
-
|
|
1127
|
-
const APPENGINE_LOAD_BALANCER_MODULE = "spinnaker.appengine.loadBalancer.module";
|
|
1128
|
-
module(APPENGINE_LOAD_BALANCER_MODULE, [
|
|
1129
|
-
APPENGINE_ALLOCATION_CONFIGURATION_ROW,
|
|
1130
|
-
APPENGINE_LOAD_BALANCER_DETAILS_CTRL,
|
|
1131
|
-
APPENGINE_LOAD_BALANCER_ADVANCED_SETTINGS,
|
|
1132
|
-
APPENGINE_LOAD_BALANCER_BASIC_SETTINGS,
|
|
1133
|
-
APPENGINE_LOAD_BALANCER_TRANSFORMER,
|
|
1134
|
-
APPENGINE_LOAD_BALANCER_WIZARD_CTRL,
|
|
1135
|
-
APPENGINE_STAGE_ALLOCATION_CONFIGURATION_ROW
|
|
1136
|
-
]);
|
|
1137
|
-
|
|
1138
|
-
var logo = "appengine.logoc2c312af6aa99037.png";
|
|
1139
|
-
|
|
1140
|
-
class AppengineHealth {
|
|
1141
|
-
}
|
|
1142
|
-
AppengineHealth.PLATFORM = "App Engine Service";
|
|
1143
|
-
|
|
1144
|
-
class AppengineStageCtrl {
|
|
1145
|
-
constructor($scope) {
|
|
1146
|
-
this.$scope = $scope;
|
|
1147
|
-
$scope.platformHealth = AppengineHealth.PLATFORM;
|
|
1148
|
-
}
|
|
1149
|
-
setStageRegion() {
|
|
1150
|
-
const selected = this.$scope.accounts.find((account) => account.name === this.$scope.stage.credentials);
|
|
1151
|
-
if (selected && selected.name) {
|
|
1152
|
-
AccountService.getAccountDetails(selected.name).then((accountDetails) => {
|
|
1153
|
-
this.$scope.stage.region = accountDetails.region;
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
setStageCloudProvider() {
|
|
1158
|
-
this.$scope.stage.cloudProvider = "appengine";
|
|
1159
|
-
}
|
|
1160
|
-
setAccounts() {
|
|
1161
|
-
return AccountService.listAccounts("appengine").then((accounts) => {
|
|
1162
|
-
this.$scope.accounts = accounts;
|
|
1163
|
-
});
|
|
1164
|
-
}
|
|
1165
|
-
setTargets() {
|
|
1166
|
-
this.$scope.targets = StageConstants.TARGET_LIST;
|
|
1167
|
-
if (!this.$scope.stage.target) {
|
|
1168
|
-
this.$scope.stage.target = this.$scope.targets[0].val;
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
setStageCredentials() {
|
|
1172
|
-
if (!this.$scope.stage.credentials && this.$scope.application.defaultCredentials.appengine) {
|
|
1173
|
-
this.$scope.stage.credentials = this.$scope.application.defaultCredentials.appengine;
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
AppengineStageCtrl.$inject = ["$scope"];
|
|
1178
|
-
|
|
1179
|
-
class AppengineDestroyAsgStageCtrl extends AppengineStageCtrl {
|
|
1180
|
-
constructor($scope) {
|
|
1181
|
-
super($scope);
|
|
1182
|
-
this.$scope = $scope;
|
|
1183
|
-
super.setAccounts().then(() => {
|
|
1184
|
-
super.setStageRegion();
|
|
1185
|
-
});
|
|
1186
|
-
super.setStageCloudProvider();
|
|
1187
|
-
super.setTargets();
|
|
1188
|
-
super.setStageCredentials();
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
AppengineDestroyAsgStageCtrl.$inject = ["$scope"];
|
|
1192
|
-
const APPENGINE_DESTROY_ASG_STAGE = "spinnaker.appengine.pipeline.stage.destroyAsgStage";
|
|
1193
|
-
module(APPENGINE_DESTROY_ASG_STAGE, []).config(() => {
|
|
1194
|
-
Registry.pipeline.registerStage({
|
|
1195
|
-
provides: "destroyServerGroup",
|
|
1196
|
-
key: "destroyServerGroup",
|
|
1197
|
-
cloudProvider: "appengine",
|
|
1198
|
-
templateUrl: "appengine/src/pipeline/stages/destroyAsg/destroyAsgStage.html",
|
|
1199
|
-
executionStepLabelUrl: "appengine/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",
|
|
1200
|
-
validators: [
|
|
1201
|
-
{
|
|
1202
|
-
type: "targetImpedance",
|
|
1203
|
-
message: "This pipeline will attempt to destroy a server group without deploying a new version into the same cluster."
|
|
1204
|
-
},
|
|
1205
|
-
{ type: "requiredField", fieldName: "cluster" },
|
|
1206
|
-
{ type: "requiredField", fieldName: "target" },
|
|
1207
|
-
{ type: "requiredField", fieldName: "credentials", fieldLabel: "account" }
|
|
1208
|
-
]
|
|
1209
|
-
});
|
|
1210
|
-
}).controller("appengineDestroyAsgStageCtrl", AppengineDestroyAsgStageCtrl);
|
|
1211
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1212
|
-
templateCache.put("appengine/src/pipeline/stages/destroyAsg/destroyAsgStage.html", `<div ng-controller="appengineDestroyAsgStageCtrl as destroyAsgStageCtrl" class="form-horizontal">
|
|
1213
|
-
<div ng-if="!pipeline.strategy">
|
|
1214
|
-
<account-region-cluster-selector
|
|
1215
|
-
application="application"
|
|
1216
|
-
single-region="true"
|
|
1217
|
-
disable-region-select="true"
|
|
1218
|
-
on-account-update="destroyAsgStageCtrl.setStageRegion()"
|
|
1219
|
-
component="stage"
|
|
1220
|
-
accounts="accounts"
|
|
1221
|
-
>
|
|
1222
|
-
</account-region-cluster-selector>
|
|
1223
|
-
</div>
|
|
1224
|
-
<stage-config-field label="Target">
|
|
1225
|
-
<target-select model="stage" options="targets"></target-select>
|
|
1226
|
-
</stage-config-field>
|
|
1227
|
-
</div>
|
|
1228
|
-
`);
|
|
1229
|
-
}]);
|
|
1230
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1231
|
-
templateCache.put("appengine/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html", `<span class="task-label"> Destroy Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>
|
|
1232
|
-
`);
|
|
1233
|
-
}]);
|
|
1234
|
-
|
|
1235
|
-
class AppengineDisableAsgStageCtrl extends AppengineStageCtrl {
|
|
1236
|
-
constructor($scope) {
|
|
1237
|
-
super($scope);
|
|
1238
|
-
this.$scope = $scope;
|
|
1239
|
-
super.setAccounts().then(() => {
|
|
1240
|
-
super.setStageRegion();
|
|
1241
|
-
});
|
|
1242
|
-
super.setStageCloudProvider();
|
|
1243
|
-
super.setTargets();
|
|
1244
|
-
super.setStageCredentials();
|
|
1245
|
-
if ($scope.stage.isNew && $scope.application.attributes.platformHealthOnlyShowOverride && $scope.application.attributes.platformHealthOnly) {
|
|
1246
|
-
$scope.stage.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
AppengineDisableAsgStageCtrl.$inject = ["$scope"];
|
|
1251
|
-
const APPENGINE_DISABLE_ASG_STAGE = "spinnaker.appengine.pipeline.stage.disableAsgStage";
|
|
1252
|
-
module(APPENGINE_DISABLE_ASG_STAGE, []).config(() => {
|
|
1253
|
-
Registry.pipeline.registerStage({
|
|
1254
|
-
provides: "disableServerGroup",
|
|
1255
|
-
key: "disableServerGroup",
|
|
1256
|
-
cloudProvider: "appengine",
|
|
1257
|
-
templateUrl: "appengine/src/pipeline/stages/disableAsg/disableAsgStage.html",
|
|
1258
|
-
executionStepLabelUrl: "appengine/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",
|
|
1259
|
-
validators: [
|
|
1260
|
-
{
|
|
1261
|
-
type: "targetImpedance",
|
|
1262
|
-
message: "This pipeline will attempt to disable a server group without deploying a new version into the same cluster."
|
|
1263
|
-
},
|
|
1264
|
-
{ type: "requiredField", fieldName: "cluster" },
|
|
1265
|
-
{ type: "requiredField", fieldName: "target" },
|
|
1266
|
-
{ type: "requiredField", fieldName: "credentials", fieldLabel: "account" }
|
|
1267
|
-
]
|
|
1268
|
-
});
|
|
1269
|
-
}).controller("appengineDisableAsgStageCtrl", AppengineDisableAsgStageCtrl);
|
|
1270
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1271
|
-
templateCache.put("appengine/src/pipeline/stages/disableAsg/disableAsgStage.html", `<div ng-controller="appengineDisableAsgStageCtrl as disableAsgStageCtrl" class="form-horizontal">
|
|
1272
|
-
<div ng-if="!pipeline.strategy">
|
|
1273
|
-
<account-region-cluster-selector
|
|
1274
|
-
application="application"
|
|
1275
|
-
component="stage"
|
|
1276
|
-
single-region="true"
|
|
1277
|
-
disable-region-select="true"
|
|
1278
|
-
on-account-update="disableAsgStageCtrl.setStageRegion()"
|
|
1279
|
-
accounts="accounts"
|
|
1280
|
-
>
|
|
1281
|
-
</account-region-cluster-selector>
|
|
1282
|
-
</div>
|
|
1283
|
-
<stage-config-field label="Target">
|
|
1284
|
-
<target-select model="stage" options="targets"></target-select>
|
|
1285
|
-
</stage-config-field>
|
|
1286
|
-
<stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">
|
|
1287
|
-
</stage-platform-health-override>
|
|
1288
|
-
</div>
|
|
1289
|
-
`);
|
|
1290
|
-
}]);
|
|
1291
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1292
|
-
templateCache.put("appengine/src/pipeline/stages/disableAsg/disableAsgStepLabel.html", `<span class="task-label"> Disable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>
|
|
1293
|
-
`);
|
|
1294
|
-
}]);
|
|
1295
|
-
|
|
1296
|
-
class AppengineLoadBalancerChoiceModalCtrl {
|
|
1297
|
-
constructor($uibModal, $uibModalInstance, application) {
|
|
1298
|
-
this.$uibModal = $uibModal;
|
|
1299
|
-
this.$uibModalInstance = $uibModalInstance;
|
|
1300
|
-
this.application = application;
|
|
1301
|
-
this.state = { loading: true };
|
|
1302
|
-
this.initialize();
|
|
1303
|
-
}
|
|
1304
|
-
submit() {
|
|
1305
|
-
const config = CloudProviderRegistry.getValue("appengine", "loadBalancer");
|
|
1306
|
-
const updatedLoadBalancerPromise = this.$uibModal.open({
|
|
1307
|
-
templateUrl: config.createLoadBalancerTemplateUrl,
|
|
1308
|
-
controller: `${config.createLoadBalancerController} as ctrl`,
|
|
1309
|
-
size: "lg",
|
|
1310
|
-
resolve: {
|
|
1311
|
-
application: () => this.application,
|
|
1312
|
-
loadBalancer: () => cloneDeep(this.selectedLoadBalancer),
|
|
1313
|
-
isNew: () => false,
|
|
1314
|
-
forPipelineConfig: () => true
|
|
1315
|
-
}
|
|
1316
|
-
}).result;
|
|
1317
|
-
this.$uibModalInstance.close(updatedLoadBalancerPromise);
|
|
1318
|
-
}
|
|
1319
|
-
cancel() {
|
|
1320
|
-
this.$uibModalInstance.dismiss();
|
|
1321
|
-
}
|
|
1322
|
-
initialize() {
|
|
1323
|
-
this.application.getDataSource("loadBalancers").ready().then(() => {
|
|
1324
|
-
this.loadBalancers = this.application.loadBalancers.data.filter((candidate) => candidate.cloudProvider === "appengine");
|
|
1325
|
-
if (this.loadBalancers.length) {
|
|
1326
|
-
this.selectedLoadBalancer = this.loadBalancers[0];
|
|
1327
|
-
}
|
|
1328
|
-
this.state.loading = false;
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
AppengineLoadBalancerChoiceModalCtrl.$inject = ["$uibModal", "$uibModalInstance", "application"];
|
|
1333
|
-
const APPENGINE_LOAD_BALANCER_CHOICE_MODAL_CTRL = "spinnaker.appengine.loadBalancerChoiceModal.controller";
|
|
1334
|
-
module(APPENGINE_LOAD_BALANCER_CHOICE_MODAL_CTRL, []).controller("appengineLoadBalancerChoiceModelCtrl", AppengineLoadBalancerChoiceModalCtrl);
|
|
1335
|
-
|
|
1336
|
-
class AppengineEditLoadBalancerStageCtrl {
|
|
1337
|
-
constructor($scope, $uibModal) {
|
|
1338
|
-
this.$scope = $scope;
|
|
1339
|
-
this.$uibModal = $uibModal;
|
|
1340
|
-
$scope.stage.loadBalancers = $scope.stage.loadBalancers || [];
|
|
1341
|
-
$scope.stage.cloudProvider = "appengine";
|
|
1342
|
-
}
|
|
1343
|
-
addLoadBalancer() {
|
|
1344
|
-
this.$uibModal.open({
|
|
1345
|
-
templateUrl: "appengine/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html",
|
|
1346
|
-
controller: `appengineLoadBalancerChoiceModelCtrl as ctrl`,
|
|
1347
|
-
resolve: {
|
|
1348
|
-
application: () => this.$scope.application
|
|
1349
|
-
}
|
|
1350
|
-
}).result.then((newLoadBalancer) => {
|
|
1351
|
-
this.$scope.stage.loadBalancers.push(newLoadBalancer);
|
|
1352
|
-
}).catch(() => {
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1355
|
-
editLoadBalancer(index) {
|
|
1356
|
-
const config = CloudProviderRegistry.getValue("appengine", "loadBalancer");
|
|
1357
|
-
this.$uibModal.open({
|
|
1358
|
-
templateUrl: config.createLoadBalancerTemplateUrl,
|
|
1359
|
-
controller: `${config.createLoadBalancerController} as ctrl`,
|
|
1360
|
-
size: "lg",
|
|
1361
|
-
resolve: {
|
|
1362
|
-
application: () => this.$scope.application,
|
|
1363
|
-
loadBalancer: () => cloneDeep(this.$scope.stage.loadBalancers[index]),
|
|
1364
|
-
isNew: () => false,
|
|
1365
|
-
forPipelineConfig: () => true
|
|
1366
|
-
}
|
|
1367
|
-
}).result.then((updatedLoadBalancer) => {
|
|
1368
|
-
this.$scope.stage.loadBalancers[index] = updatedLoadBalancer;
|
|
1369
|
-
}).catch(() => {
|
|
1370
|
-
});
|
|
1371
|
-
}
|
|
1372
|
-
removeLoadBalancer(index) {
|
|
1373
|
-
this.$scope.stage.loadBalancers.splice(index, 1);
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
AppengineEditLoadBalancerStageCtrl.$inject = ["$scope", "$uibModal"];
|
|
1377
|
-
const APPENGINE_EDIT_LOAD_BALANCER_STAGE = "spinnaker.appengine.pipeline.stage.editLoadBalancerStage";
|
|
1378
|
-
module(APPENGINE_EDIT_LOAD_BALANCER_STAGE, [APPENGINE_LOAD_BALANCER_CHOICE_MODAL_CTRL]).config(() => {
|
|
1379
|
-
Registry.pipeline.registerStage({
|
|
1380
|
-
label: "Edit Load Balancer",
|
|
1381
|
-
description: "Edits a load balancer",
|
|
1382
|
-
key: "upsertAppEngineLoadBalancers",
|
|
1383
|
-
cloudProvider: "appengine",
|
|
1384
|
-
templateUrl: "appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html",
|
|
1385
|
-
executionDetailsUrl: "appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html",
|
|
1386
|
-
executionConfigSections: ["editLoadBalancerConfig", "taskStatus"],
|
|
1387
|
-
controller: "appengineEditLoadBalancerStageCtrl",
|
|
1388
|
-
controllerAs: "editLoadBalancerStageCtrl",
|
|
1389
|
-
validators: []
|
|
1390
|
-
});
|
|
1391
|
-
}).controller("appengineEditLoadBalancerStageCtrl", AppengineEditLoadBalancerStageCtrl);
|
|
1392
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1393
|
-
templateCache.put("appengine/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html", `<div modal-page>
|
|
1394
|
-
<modal-close dismiss="$dismiss()"></modal-close>
|
|
1395
|
-
<div class="modal-header">
|
|
1396
|
-
<h4 class="modal-title">Select Load Balancer</h4>
|
|
1397
|
-
</div>
|
|
1398
|
-
<div class="modal-body" ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">
|
|
1399
|
-
<loading-spinner size="'small'"></loading-spinner>
|
|
1400
|
-
</div>
|
|
1401
|
-
<div class="modal-body" ng-if="!ctrl.state.loading">
|
|
1402
|
-
<div class="alert alert-warning" ng-if="ctrl.loadBalancers.length === 0">
|
|
1403
|
-
<p>This application has no App Engine load balancers.</p>
|
|
1404
|
-
</div>
|
|
1405
|
-
<form
|
|
1406
|
-
role="form"
|
|
1407
|
-
name="form"
|
|
1408
|
-
class="form-horizontal"
|
|
1409
|
-
ng-submit="ctrl.submit()"
|
|
1410
|
-
ng-if="ctrl.loadBalancers.length > 0"
|
|
1411
|
-
>
|
|
1412
|
-
<div class="form-group">
|
|
1413
|
-
<div class="col-md-3 sm-label-right">
|
|
1414
|
-
<b>Load Balancer</b>
|
|
1415
|
-
</div>
|
|
1416
|
-
<div class="col-md-7">
|
|
1417
|
-
<ui-select class="form-control input-sm" ng-model="ctrl.selectedLoadBalancer">
|
|
1418
|
-
<ui-select-match>
|
|
1419
|
-
<account-tag account="$select.selected.account"></account-tag>
|
|
1420
|
-
<span style="margin-left: 5px">{{$select.selected.name}}</span>
|
|
1421
|
-
</ui-select-match>
|
|
1422
|
-
<ui-select-choices repeat="loadBalancer in ctrl.loadBalancers | filter: $select.search">
|
|
1423
|
-
<account-tag account="loadBalancer.account"></account-tag>
|
|
1424
|
-
<span style="margin-left: 5px" ng-bind-html="loadBalancer.name"></span>
|
|
1425
|
-
</ui-select-choices>
|
|
1426
|
-
</ui-select>
|
|
1427
|
-
</div>
|
|
1428
|
-
</div>
|
|
1429
|
-
</form>
|
|
1430
|
-
</div>
|
|
1431
|
-
<div class="modal-footer">
|
|
1432
|
-
<button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>
|
|
1433
|
-
<button class="btn btn-primary" ng-if="ctrl.loadBalancers.length > 0" ng-click="ctrl.submit()">
|
|
1434
|
-
<span class="far fa-check-circle"></span> Edit
|
|
1435
|
-
</button>
|
|
1436
|
-
</div>
|
|
1437
|
-
</div>
|
|
1438
|
-
`);
|
|
1439
|
-
}]);
|
|
1440
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1441
|
-
templateCache.put("appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html", `<div class="well well-sm clearfix" ng-if="!pipeline.strategy">
|
|
1442
|
-
<div class="row">
|
|
1443
|
-
<div class="col-md-12">
|
|
1444
|
-
<h4 class="text-left">Load Balancers</h4>
|
|
1445
|
-
</div>
|
|
1446
|
-
</div>
|
|
1447
|
-
<div class="row">
|
|
1448
|
-
<div class="col-md-12">
|
|
1449
|
-
<table class="table table-condensed">
|
|
1450
|
-
<thead>
|
|
1451
|
-
<tr>
|
|
1452
|
-
<th>Account</th>
|
|
1453
|
-
<th>Name</th>
|
|
1454
|
-
<th>Region</th>
|
|
1455
|
-
<th>Actions</th>
|
|
1456
|
-
</tr>
|
|
1457
|
-
</thead>
|
|
1458
|
-
<tbody>
|
|
1459
|
-
<tr ng-repeat="loadBalancer in stage.loadBalancers">
|
|
1460
|
-
<td>
|
|
1461
|
-
<account-tag account="loadBalancer.credentials"></account-tag>
|
|
1462
|
-
</td>
|
|
1463
|
-
<td>{{ loadBalancer.name }}</td>
|
|
1464
|
-
<td>{{ loadBalancer.region }}</td>
|
|
1465
|
-
<td class="condensed-actions">
|
|
1466
|
-
<a class="btn btn-sm btn-link" href ng-click="editLoadBalancerStageCtrl.editLoadBalancer($index)">
|
|
1467
|
-
<span class="glyphicon glyphicon-edit" uib-tooltip="Edit"></span
|
|
1468
|
-
></a>
|
|
1469
|
-
<a
|
|
1470
|
-
class="btn btn-sm btn-link pad-left"
|
|
1471
|
-
href
|
|
1472
|
-
ng-click="editLoadBalancerStageCtrl.removeLoadBalancer($index)"
|
|
1473
|
-
>
|
|
1474
|
-
<span class="glyphicon glyphicon-trash" uib-tooltip="Remove"></span>
|
|
1475
|
-
</a>
|
|
1476
|
-
</td>
|
|
1477
|
-
</tr>
|
|
1478
|
-
</tbody>
|
|
1479
|
-
<tfoot>
|
|
1480
|
-
<tr>
|
|
1481
|
-
<td colspan="8">
|
|
1482
|
-
<button class="btn btn-block btn-sm add-new" ng-click="editLoadBalancerStageCtrl.addLoadBalancer()">
|
|
1483
|
-
<span class="glyphicon glyphicon-plus-sign"></span> Add load balancer
|
|
1484
|
-
</button>
|
|
1485
|
-
</td>
|
|
1486
|
-
</tr>
|
|
1487
|
-
</tfoot>
|
|
1488
|
-
</table>
|
|
1489
|
-
</div>
|
|
1490
|
-
</div>
|
|
1491
|
-
</div>
|
|
1492
|
-
`);
|
|
1493
|
-
}]);
|
|
1494
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1495
|
-
templateCache.put("appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html", `<div ng-controller="BaseExecutionDetailsCtrl">
|
|
1496
|
-
<execution-details-section-nav sections="configSections"></execution-details-section-nav>
|
|
1497
|
-
<div class="step-section-details" ng-if="detailsSection === 'editLoadBalancerConfig'">
|
|
1498
|
-
<div class="row">
|
|
1499
|
-
<div class="col-md-12">
|
|
1500
|
-
<table class="table table-condensed">
|
|
1501
|
-
<thead>
|
|
1502
|
-
<tr>
|
|
1503
|
-
<th>Account</th>
|
|
1504
|
-
<th>Name</th>
|
|
1505
|
-
<th>Region</th>
|
|
1506
|
-
</tr>
|
|
1507
|
-
</thead>
|
|
1508
|
-
<tbody>
|
|
1509
|
-
<tr ng-repeat="loadBalancer in stage.context.loadBalancers">
|
|
1510
|
-
<td>
|
|
1511
|
-
<account-tag account="loadBalancer.credentials"></account-tag>
|
|
1512
|
-
</td>
|
|
1513
|
-
<td>{{ loadBalancer.name }}</td>
|
|
1514
|
-
<td>{{ loadBalancer.region }}</td>
|
|
1515
|
-
</tr>
|
|
1516
|
-
</tbody>
|
|
1517
|
-
</table>
|
|
1518
|
-
</div>
|
|
1519
|
-
</div>
|
|
1520
|
-
<stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>
|
|
1521
|
-
</div>
|
|
1522
|
-
<div class="step-section-details" ng-if="detailsSection === 'taskStatus'">
|
|
1523
|
-
<div class="row">
|
|
1524
|
-
<execution-step-details item="stage"></execution-step-details>
|
|
1525
|
-
</div>
|
|
1526
|
-
</div>
|
|
1527
|
-
</div>
|
|
1528
|
-
`);
|
|
1529
|
-
}]);
|
|
1530
|
-
|
|
1531
|
-
class AppengineEnableAsgStageCtrl extends AppengineStageCtrl {
|
|
1532
|
-
constructor($scope) {
|
|
1533
|
-
super($scope);
|
|
1534
|
-
this.$scope = $scope;
|
|
1535
|
-
super.setAccounts().then(() => {
|
|
1536
|
-
super.setStageRegion();
|
|
1537
|
-
});
|
|
1538
|
-
super.setStageCloudProvider();
|
|
1539
|
-
super.setTargets();
|
|
1540
|
-
super.setStageCredentials();
|
|
1541
|
-
if ($scope.stage.isNew && $scope.application.attributes.platformHealthOnlyShowOverride && $scope.application.attributes.platformHealthOnly) {
|
|
1542
|
-
$scope.stage.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
AppengineEnableAsgStageCtrl.$inject = ["$scope"];
|
|
1547
|
-
const APPENGINE_ENABLE_ASG_STAGE = "spinnaker.appengine.pipeline.stage.enableAsgStage";
|
|
1548
|
-
module(APPENGINE_ENABLE_ASG_STAGE, []).config(() => {
|
|
1549
|
-
Registry.pipeline.registerStage({
|
|
1550
|
-
provides: "enableServerGroup",
|
|
1551
|
-
key: "enableServerGroup",
|
|
1552
|
-
cloudProvider: "appengine",
|
|
1553
|
-
templateUrl: "appengine/src/pipeline/stages/enableAsg/enableAsgStage.html",
|
|
1554
|
-
executionStepLabelUrl: "appengine/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",
|
|
1555
|
-
validators: [
|
|
1556
|
-
{ type: "requiredField", fieldName: "cluster" },
|
|
1557
|
-
{ type: "requiredField", fieldName: "target" },
|
|
1558
|
-
{ type: "requiredField", fieldName: "credentials", fieldLabel: "account" }
|
|
1559
|
-
]
|
|
1560
|
-
});
|
|
1561
|
-
}).controller("appengineEnableAsgStageCtrl", AppengineEnableAsgStageCtrl);
|
|
1562
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1563
|
-
templateCache.put("appengine/src/pipeline/stages/enableAsg/enableAsgStage.html", `<div ng-controller="appengineEnableAsgStageCtrl as enableAsgStageCtrl" class="form-horizontal">
|
|
1564
|
-
<div ng-if="!pipeline.strategy">
|
|
1565
|
-
<account-region-cluster-selector
|
|
1566
|
-
application="application"
|
|
1567
|
-
component="stage"
|
|
1568
|
-
single-region="true"
|
|
1569
|
-
disable-region-select="true"
|
|
1570
|
-
on-account-update="enableAsgStageCtrl.setStageRegion()"
|
|
1571
|
-
accounts="accounts"
|
|
1572
|
-
>
|
|
1573
|
-
</account-region-cluster-selector>
|
|
1574
|
-
</div>
|
|
1575
|
-
<stage-config-field label="Target">
|
|
1576
|
-
<target-select model="stage" options="targets"></target-select>
|
|
1577
|
-
</stage-config-field>
|
|
1578
|
-
<stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">
|
|
1579
|
-
</stage-platform-health-override>
|
|
1580
|
-
</div>
|
|
1581
|
-
`);
|
|
1582
|
-
}]);
|
|
1583
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1584
|
-
templateCache.put("appengine/src/pipeline/stages/enableAsg/enableAsgStepLabel.html", `<span class="task-label"> Enable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>
|
|
1585
|
-
`);
|
|
1586
|
-
}]);
|
|
1587
|
-
|
|
1588
|
-
class AppengineShrinkClusterStageCtrl extends AppengineStageCtrl {
|
|
1589
|
-
constructor($scope) {
|
|
1590
|
-
super($scope);
|
|
1591
|
-
this.$scope = $scope;
|
|
1592
|
-
super.setAccounts().then(() => {
|
|
1593
|
-
super.setStageRegion();
|
|
1594
|
-
});
|
|
1595
|
-
super.setStageCloudProvider();
|
|
1596
|
-
super.setStageCredentials();
|
|
1597
|
-
const stage = $scope.stage;
|
|
1598
|
-
if (stage.shrinkToSize === void 0) {
|
|
1599
|
-
stage.shrinkToSize = 1;
|
|
1600
|
-
}
|
|
1601
|
-
if (stage.allowDeleteActive === void 0) {
|
|
1602
|
-
stage.allowDeleteActive = false;
|
|
1603
|
-
}
|
|
1604
|
-
if (stage.retainLargerOverNewer === void 0) {
|
|
1605
|
-
stage.retainLargerOverNewer = "false";
|
|
1606
|
-
}
|
|
1607
|
-
stage.retainLargerOverNewer = stage.retainLargerOverNewer.toString();
|
|
1608
|
-
}
|
|
1609
|
-
pluralize(str, val) {
|
|
1610
|
-
return val === 1 ? str : str + "s";
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
AppengineShrinkClusterStageCtrl.$inject = ["$scope"];
|
|
1614
|
-
const APPENGINE_SHRINK_CLUSTER_STAGE = "spinnaker.appengine.pipeline.stage.shrinkClusterStage";
|
|
1615
|
-
module(APPENGINE_SHRINK_CLUSTER_STAGE, []).config(function() {
|
|
1616
|
-
Registry.pipeline.registerStage({
|
|
1617
|
-
provides: "shrinkCluster",
|
|
1618
|
-
key: "shrinkCluster",
|
|
1619
|
-
cloudProvider: "appengine",
|
|
1620
|
-
templateUrl: "appengine/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",
|
|
1621
|
-
validators: [
|
|
1622
|
-
{ type: "requiredField", fieldName: "cluster" },
|
|
1623
|
-
{ type: "requiredField", fieldName: "shrinkToSize", fieldLabel: "shrink to [X] Server Groups" },
|
|
1624
|
-
{ type: "requiredField", fieldName: "credentials", fieldLabel: "account" }
|
|
1625
|
-
]
|
|
1626
|
-
});
|
|
1627
|
-
}).controller("appengineShrinkClusterStageCtrl", AppengineShrinkClusterStageCtrl);
|
|
1628
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1629
|
-
templateCache.put("appengine/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html", `<div ng-controller="appengineShrinkClusterStageCtrl as shrinkClusterStageCtrl" class="form-horizontal">
|
|
1630
|
-
<div ng-if="!pipeline.strategy">
|
|
1631
|
-
<account-region-cluster-selector
|
|
1632
|
-
application="application"
|
|
1633
|
-
component="stage"
|
|
1634
|
-
single-region="true"
|
|
1635
|
-
disable-region-select="true"
|
|
1636
|
-
on-account-update="shrinkClusterStageCtrl.setStageRegion()"
|
|
1637
|
-
accounts="accounts"
|
|
1638
|
-
>
|
|
1639
|
-
</account-region-cluster-selector>
|
|
1640
|
-
</div>
|
|
1641
|
-
<stage-config-field label="Shrink Options">
|
|
1642
|
-
<div class="form-inline">
|
|
1643
|
-
Shrink to
|
|
1644
|
-
<input
|
|
1645
|
-
type="number"
|
|
1646
|
-
min="0"
|
|
1647
|
-
required
|
|
1648
|
-
ng-model="stage.shrinkToSize"
|
|
1649
|
-
class="form-control input-sm"
|
|
1650
|
-
style="width: 50px"
|
|
1651
|
-
/>
|
|
1652
|
-
{{shrinkClusterStageCtrl.pluralize('server group', stage.shrinkToSize)}}, keeping the
|
|
1653
|
-
<select class="form-control input-sm" ng-model="stage.retainLargerOverNewer" style="width: 100px">
|
|
1654
|
-
<option value="true">largest</option>
|
|
1655
|
-
<option value="false">newest</option>
|
|
1656
|
-
</select>
|
|
1657
|
-
</div>
|
|
1658
|
-
</stage-config-field>
|
|
1659
|
-
<div class="form-group">
|
|
1660
|
-
<div class="col-md-offset-3 col-md-6 checkbox">
|
|
1661
|
-
<label>
|
|
1662
|
-
<input type="checkbox" ng-model="stage.allowDeleteActive" />
|
|
1663
|
-
Allow deletion of active server groups
|
|
1664
|
-
</label>
|
|
1665
|
-
</div>
|
|
1666
|
-
</div>
|
|
1667
|
-
<stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">
|
|
1668
|
-
</stage-platform-health-override>
|
|
1669
|
-
</div>
|
|
1670
|
-
`);
|
|
1671
|
-
}]);
|
|
1672
|
-
|
|
1673
|
-
class AppengineStartServerGroupStageCtrl extends AppengineStageCtrl {
|
|
1674
|
-
constructor($scope) {
|
|
1675
|
-
super($scope);
|
|
1676
|
-
this.$scope = $scope;
|
|
1677
|
-
super.setAccounts().then(() => {
|
|
1678
|
-
super.setStageRegion();
|
|
1679
|
-
});
|
|
1680
|
-
super.setStageCloudProvider();
|
|
1681
|
-
super.setTargets();
|
|
1682
|
-
super.setStageCredentials();
|
|
1683
|
-
if ($scope.stage.isNew && $scope.application.attributes.platformHealthOnlyShowOverride && $scope.application.attributes.platformHealthOnly) {
|
|
1684
|
-
$scope.stage.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
AppengineStartServerGroupStageCtrl.$inject = ["$scope"];
|
|
1689
|
-
const APPENGINE_START_SERVER_GROUP_STAGE = "spinnaker.appengine.pipeline.stage.startServerGroupStage";
|
|
1690
|
-
module(APPENGINE_START_SERVER_GROUP_STAGE, []).config(() => {
|
|
1691
|
-
Registry.pipeline.registerStage({
|
|
1692
|
-
label: "Start Server Group",
|
|
1693
|
-
description: "Starts a server group.",
|
|
1694
|
-
key: "startAppEngineServerGroup",
|
|
1695
|
-
templateUrl: "appengine/src/pipeline/stages/startServerGroup/startServerGroupStage.html",
|
|
1696
|
-
executionDetailsUrl: "appengine/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html",
|
|
1697
|
-
executionConfigSections: ["startServerGroupConfig", "taskStatus"],
|
|
1698
|
-
executionStepLabelUrl: "appengine/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html",
|
|
1699
|
-
controller: "appengineStartServerGroupStageCtrl",
|
|
1700
|
-
controllerAs: "startServerGroupStageCtrl",
|
|
1701
|
-
validators: [
|
|
1702
|
-
{ type: "requiredField", fieldName: "cluster" },
|
|
1703
|
-
{ type: "requiredField", fieldName: "target" },
|
|
1704
|
-
{ type: "requiredField", fieldName: "credentials", fieldLabel: "account" }
|
|
1705
|
-
],
|
|
1706
|
-
cloudProvider: "appengine"
|
|
1707
|
-
});
|
|
1708
|
-
}).controller("appengineStartServerGroupStageCtrl", AppengineStartServerGroupStageCtrl);
|
|
1709
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1710
|
-
templateCache.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupStage.html", `<div ng-controller="appengineStartServerGroupStageCtrl as startServerGroupStageCtrl" class="form-horizontal">
|
|
1711
|
-
<div ng-if="!pipeline.strategy">
|
|
1712
|
-
<account-region-cluster-selector
|
|
1713
|
-
application="application"
|
|
1714
|
-
component="stage"
|
|
1715
|
-
single-region="true"
|
|
1716
|
-
disable-region-select="true"
|
|
1717
|
-
on-account-update="startServerGroupStageCtrl.setStageRegion()"
|
|
1718
|
-
accounts="accounts"
|
|
1719
|
-
>
|
|
1720
|
-
</account-region-cluster-selector>
|
|
1721
|
-
</div>
|
|
1722
|
-
<stage-config-field label="Target">
|
|
1723
|
-
<target-select model="stage" options="targets"></target-select>
|
|
1724
|
-
</stage-config-field>
|
|
1725
|
-
<stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">
|
|
1726
|
-
</stage-platform-health-override>
|
|
1727
|
-
</div>
|
|
1728
|
-
`);
|
|
1729
|
-
}]);
|
|
1730
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1731
|
-
templateCache.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html", `<div ng-controller="BaseExecutionDetailsCtrl">
|
|
1732
|
-
<execution-details-section-nav sections="configSections"></execution-details-section-nav>
|
|
1733
|
-
<div class="step-section-details" ng-if="detailsSection === 'startServerGroupConfig'">
|
|
1734
|
-
<div class="row">
|
|
1735
|
-
<div class="col-md-9">
|
|
1736
|
-
<dl class="dl-narrow dl-horizontal">
|
|
1737
|
-
<dt>Account</dt>
|
|
1738
|
-
<dd>
|
|
1739
|
-
<account-tag account="stage.context.credentials"></account-tag>
|
|
1740
|
-
</dd>
|
|
1741
|
-
<dt>Region</dt>
|
|
1742
|
-
<dd>{{stage.context.region}}</dd>
|
|
1743
|
-
<dt>Server Group</dt>
|
|
1744
|
-
<dd>{{stage.context.serverGroupName}}</dd>
|
|
1745
|
-
</dl>
|
|
1746
|
-
</div>
|
|
1747
|
-
</div>
|
|
1748
|
-
<stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>
|
|
1749
|
-
</div>
|
|
1750
|
-
|
|
1751
|
-
<div class="step-section-details" ng-if="detailsSection === 'taskStatus'">
|
|
1752
|
-
<div class="row">
|
|
1753
|
-
<execution-step-details item="stage"></execution-step-details>
|
|
1754
|
-
</div>
|
|
1755
|
-
</div>
|
|
1756
|
-
</div>
|
|
1757
|
-
`);
|
|
1758
|
-
}]);
|
|
1759
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1760
|
-
templateCache.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html", `<span class="task-label"> Start Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>
|
|
1761
|
-
`);
|
|
1762
|
-
}]);
|
|
1763
|
-
|
|
1764
|
-
class AppengineStopServerGroupStageCtrl extends AppengineStageCtrl {
|
|
1765
|
-
constructor($scope) {
|
|
1766
|
-
super($scope);
|
|
1767
|
-
this.$scope = $scope;
|
|
1768
|
-
super.setAccounts().then(() => {
|
|
1769
|
-
super.setStageRegion();
|
|
1770
|
-
});
|
|
1771
|
-
super.setStageCloudProvider();
|
|
1772
|
-
super.setTargets();
|
|
1773
|
-
super.setStageCredentials();
|
|
1774
|
-
if ($scope.stage.isNew && $scope.application.attributes.platformHealthOnlyShowOverride && $scope.application.attributes.platformHealthOnly) {
|
|
1775
|
-
$scope.stage.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
AppengineStopServerGroupStageCtrl.$inject = ["$scope"];
|
|
1780
|
-
const APPENGINE_STOP_SERVER_GROUP_STAGE = "spinnaker.appengine.pipeline.stage.stopServerGroupStage";
|
|
1781
|
-
module(APPENGINE_STOP_SERVER_GROUP_STAGE, []).config(() => {
|
|
1782
|
-
Registry.pipeline.registerStage({
|
|
1783
|
-
label: "Stop Server Group",
|
|
1784
|
-
description: "Stops a server group.",
|
|
1785
|
-
key: "stopAppEngineServerGroup",
|
|
1786
|
-
templateUrl: "appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html",
|
|
1787
|
-
executionDetailsUrl: "appengine/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html",
|
|
1788
|
-
executionConfigSections: ["stopServerGroupConfig", "taskStatus"],
|
|
1789
|
-
executionStepLabelUrl: "appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html",
|
|
1790
|
-
controller: "appengineStopServerGroupStageCtrl",
|
|
1791
|
-
controllerAs: "stopServerGroupStageCtrl",
|
|
1792
|
-
validators: [
|
|
1793
|
-
{ type: "requiredField", fieldName: "cluster" },
|
|
1794
|
-
{ type: "requiredField", fieldName: "target" },
|
|
1795
|
-
{ type: "requiredField", fieldName: "credentials", fieldLabel: "account" }
|
|
1796
|
-
],
|
|
1797
|
-
cloudProvider: "appengine"
|
|
1798
|
-
});
|
|
1799
|
-
}).controller("appengineStopServerGroupStageCtrl", AppengineStopServerGroupStageCtrl);
|
|
1800
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1801
|
-
templateCache.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html", `<div ng-controller="appengineStopServerGroupStageCtrl as stopServerGroupStageCtrl" class="form-horizontal">
|
|
1802
|
-
<div ng-if="!pipeline.strategy">
|
|
1803
|
-
<account-region-cluster-selector
|
|
1804
|
-
application="application"
|
|
1805
|
-
component="stage"
|
|
1806
|
-
single-region="true"
|
|
1807
|
-
disable-region-select="true"
|
|
1808
|
-
on-account-update="stopServerGroupStageCtrl.setStageRegion()"
|
|
1809
|
-
accounts="accounts"
|
|
1810
|
-
>
|
|
1811
|
-
</account-region-cluster-selector>
|
|
1812
|
-
</div>
|
|
1813
|
-
<stage-config-field label="Target">
|
|
1814
|
-
<target-select model="stage" options="targets"></target-select>
|
|
1815
|
-
</stage-config-field>
|
|
1816
|
-
<stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">
|
|
1817
|
-
</stage-platform-health-override>
|
|
1818
|
-
</div>
|
|
1819
|
-
`);
|
|
1820
|
-
}]);
|
|
1821
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1822
|
-
templateCache.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html", `<div ng-controller="appengineStopServerGroupExecutionDetailsCtrl">
|
|
1823
|
-
<execution-details-section-nav sections="configSections"></execution-details-section-nav>
|
|
1824
|
-
<div class="step-section-details" ng-if="detailsSection === 'stopServerGroupConfig'">
|
|
1825
|
-
<div class="row">
|
|
1826
|
-
<div class="col-md-9">
|
|
1827
|
-
<dl class="dl-narrow dl-horizontal">
|
|
1828
|
-
<dt>Account</dt>
|
|
1829
|
-
<dd>
|
|
1830
|
-
<account-tag account="stage.context.credentials"></account-tag>
|
|
1831
|
-
</dd>
|
|
1832
|
-
<dt>Region</dt>
|
|
1833
|
-
<dd>{{stage.context.region}}</dd>
|
|
1834
|
-
<dt>Server Group</dt>
|
|
1835
|
-
<dd>{{stage.context.serverGroupName}}</dd>
|
|
1836
|
-
</dl>
|
|
1837
|
-
</div>
|
|
1838
|
-
</div>
|
|
1839
|
-
<stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>
|
|
1840
|
-
</div>
|
|
1841
|
-
|
|
1842
|
-
<div class="step-section-details" ng-if="detailsSection === 'taskStatus'">
|
|
1843
|
-
<div class="row">
|
|
1844
|
-
<execution-step-details item="stage"></execution-step-details>
|
|
1845
|
-
</div>
|
|
1846
|
-
</div>
|
|
1847
|
-
</div>
|
|
1848
|
-
`);
|
|
1849
|
-
}]);
|
|
1850
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
1851
|
-
templateCache.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html", `<span class="task-label"> Stop Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>
|
|
1852
|
-
`);
|
|
1853
|
-
}]);
|
|
1854
|
-
|
|
1855
|
-
const APPENGINE_PIPELINE_MODULE = "spinnaker.appengine.pipeline.module";
|
|
1856
|
-
module(APPENGINE_PIPELINE_MODULE, [
|
|
1857
|
-
APPENGINE_DESTROY_ASG_STAGE,
|
|
1858
|
-
APPENGINE_DISABLE_ASG_STAGE,
|
|
1859
|
-
APPENGINE_EDIT_LOAD_BALANCER_STAGE,
|
|
1860
|
-
APPENGINE_ENABLE_ASG_STAGE,
|
|
1861
|
-
APPENGINE_SHRINK_CLUSTER_STAGE,
|
|
1862
|
-
APPENGINE_START_SERVER_GROUP_STAGE,
|
|
1863
|
-
APPENGINE_STOP_SERVER_GROUP_STAGE
|
|
1864
|
-
]);
|
|
1865
|
-
|
|
1866
|
-
class FormikAccountRegionSelector extends React.Component {
|
|
1867
|
-
constructor(props) {
|
|
1868
|
-
super(props);
|
|
1869
|
-
this.destroy$ = new Subject();
|
|
1870
|
-
this.setRegionList = (credentials) => {
|
|
1871
|
-
const { application } = this.props;
|
|
1872
|
-
const accountFilter = (serverGroup) => serverGroup ? serverGroup.account === credentials : true;
|
|
1873
|
-
from(application.ready()).pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
1874
|
-
const availableRegions = AppListExtractor.getRegions([application], accountFilter);
|
|
1875
|
-
availableRegions.sort();
|
|
1876
|
-
this.setState({ availableRegions });
|
|
1877
|
-
});
|
|
1878
|
-
};
|
|
1879
|
-
this.accountChanged = (credentials) => {
|
|
1880
|
-
this.setRegionList(credentials);
|
|
1881
|
-
};
|
|
1882
|
-
const credentialsField = props.credentialsField || "credentials";
|
|
1883
|
-
this.state = {
|
|
1884
|
-
availableRegions: [],
|
|
1885
|
-
cloudProvider: props.cloudProvider,
|
|
1886
|
-
componentName: props.componentName || "",
|
|
1887
|
-
credentialsField
|
|
1888
|
-
};
|
|
1889
|
-
}
|
|
1890
|
-
componentDidMount() {
|
|
1891
|
-
const { componentName, formik } = this.props;
|
|
1892
|
-
const { credentialsField } = this.state;
|
|
1893
|
-
const credentials = get(formik.values, componentName ? `${componentName}.${credentialsField}` : `${credentialsField}`, void 0);
|
|
1894
|
-
this.setRegionList(credentials);
|
|
1895
|
-
}
|
|
1896
|
-
componentWillUnmount() {
|
|
1897
|
-
this.destroy$.next();
|
|
1898
|
-
}
|
|
1899
|
-
render() {
|
|
1900
|
-
const { accounts } = this.props;
|
|
1901
|
-
const { credentialsField, availableRegions, componentName } = this.state;
|
|
1902
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
1903
|
-
className: "col-md-9"
|
|
1904
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
1905
|
-
className: "sp-margin-m-bottom"
|
|
1906
|
-
}, /* @__PURE__ */ React.createElement(FormikFormField, {
|
|
1907
|
-
name: componentName ? `${componentName}.${credentialsField}` : `${credentialsField}`,
|
|
1908
|
-
label: "Account",
|
|
1909
|
-
input: (props) => /* @__PURE__ */ React.createElement(ReactSelectInput, {
|
|
1910
|
-
...props,
|
|
1911
|
-
stringOptions: accounts && accounts.map((acc) => acc.name),
|
|
1912
|
-
clearable: false
|
|
1913
|
-
}),
|
|
1914
|
-
onChange: this.accountChanged,
|
|
1915
|
-
required: true
|
|
1916
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
1917
|
-
className: "sp-margin-m-bottom"
|
|
1918
|
-
}, /* @__PURE__ */ React.createElement(FormikFormField, {
|
|
1919
|
-
name: componentName ? `${componentName}.region` : "region",
|
|
1920
|
-
label: "Region",
|
|
1921
|
-
input: (props) => /* @__PURE__ */ React.createElement(ReactSelectInput, {
|
|
1922
|
-
...props,
|
|
1923
|
-
stringOptions: availableRegions,
|
|
1924
|
-
clearable: false
|
|
1925
|
-
}),
|
|
1926
|
-
required: true
|
|
1927
|
-
})));
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
|
|
1931
|
-
const _DeployAppengineConfigForm = class extends React.Component {
|
|
1932
|
-
constructor() {
|
|
1933
|
-
super(...arguments);
|
|
1934
|
-
this.destroy$ = new Subject();
|
|
1935
|
-
this.state = {
|
|
1936
|
-
accounts: []
|
|
1937
|
-
};
|
|
1938
|
-
this.onTemplateArtifactEdited = (artifact, name) => {
|
|
1939
|
-
this.props.formik.setFieldValue(`${name}.id`, null);
|
|
1940
|
-
this.props.formik.setFieldValue(`${name}.artifact`, artifact);
|
|
1941
|
-
this.props.formik.setFieldValue(`${name}.account`, artifact.artifactAccount);
|
|
1942
|
-
};
|
|
1943
|
-
this.onTemplateArtifactSelected = (id, name) => {
|
|
1944
|
-
this.props.formik.setFieldValue(`${name}.id`, id);
|
|
1945
|
-
this.props.formik.setFieldValue(`${name}.artifact`, null);
|
|
1946
|
-
};
|
|
1947
|
-
this.removeInputArtifact = (name) => {
|
|
1948
|
-
this.props.formik.setFieldValue(name, null);
|
|
1949
|
-
};
|
|
1950
|
-
this.getInputArtifact = (stage, name) => {
|
|
1951
|
-
if (!stage[name]) {
|
|
1952
|
-
return {
|
|
1953
|
-
account: "",
|
|
1954
|
-
id: ""
|
|
1955
|
-
};
|
|
1956
|
-
} else {
|
|
1957
|
-
return stage[name];
|
|
1958
|
-
}
|
|
1959
|
-
};
|
|
1960
|
-
}
|
|
1961
|
-
componentDidMount() {
|
|
1962
|
-
from(AccountService.listAccounts("appengine")).pipe(takeUntil(this.destroy$)).subscribe((accounts) => this.setState({ accounts }));
|
|
1963
|
-
}
|
|
1964
|
-
render() {
|
|
1965
|
-
const stage = this.props.formik.values;
|
|
1966
|
-
const accounts = this.state.accounts;
|
|
1967
|
-
return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
1968
|
-
className: "col-md-offset-0 col-md-9"
|
|
1969
|
-
}, /* @__PURE__ */ React.createElement("h4", null, "Basic Settings")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(FormikAccountRegionSelector, {
|
|
1970
|
-
componentName: "",
|
|
1971
|
-
accounts,
|
|
1972
|
-
application: this.props.application,
|
|
1973
|
-
cloudProvider: "appengine",
|
|
1974
|
-
credentialsField: "account",
|
|
1975
|
-
formik: this.props.formik
|
|
1976
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
1977
|
-
className: "col-md-offset-0 col-md-9"
|
|
1978
|
-
}, /* @__PURE__ */ React.createElement("h4", null, "Configuration Settings")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
1979
|
-
className: "col-md-offset-1 col-md-9"
|
|
1980
|
-
}, /* @__PURE__ */ React.createElement(StageArtifactSelectorDelegate, {
|
|
1981
|
-
artifact: this.getInputArtifact(stage, "cronArtifact").artifact,
|
|
1982
|
-
excludedArtifactTypePatterns: _DeployAppengineConfigForm.excludedArtifactTypes,
|
|
1983
|
-
expectedArtifactId: this.getInputArtifact(stage, "cronArtifact").id,
|
|
1984
|
-
label: "Cron Artifact",
|
|
1985
|
-
onArtifactEdited: (artifact) => {
|
|
1986
|
-
this.onTemplateArtifactEdited(artifact, "cronArtifact");
|
|
1987
|
-
},
|
|
1988
|
-
helpKey: "",
|
|
1989
|
-
onExpectedArtifactSelected: (artifact) => this.onTemplateArtifactSelected(artifact.id, "cronArtifact"),
|
|
1990
|
-
pipeline: this.props.pipeline,
|
|
1991
|
-
stage
|
|
1992
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
1993
|
-
className: "col-md-1"
|
|
1994
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
1995
|
-
className: "form-control-static"
|
|
1996
|
-
}, /* @__PURE__ */ React.createElement("button", {
|
|
1997
|
-
onClick: () => this.removeInputArtifact("cronArtifact")
|
|
1998
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
1999
|
-
className: "glyphicon glyphicon-trash"
|
|
2000
|
-
}), /* @__PURE__ */ React.createElement("span", {
|
|
2001
|
-
className: "sr-only"
|
|
2002
|
-
}, "Remove field"))))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
2003
|
-
className: "col-md-offset-1 col-md-9"
|
|
2004
|
-
}, /* @__PURE__ */ React.createElement(StageArtifactSelectorDelegate, {
|
|
2005
|
-
artifact: this.getInputArtifact(stage, "dispatchArtifact").artifact,
|
|
2006
|
-
excludedArtifactTypePatterns: _DeployAppengineConfigForm.excludedArtifactTypes,
|
|
2007
|
-
expectedArtifactId: this.getInputArtifact(stage, "dispatchArtifact").id,
|
|
2008
|
-
label: "Dispatch Artifact",
|
|
2009
|
-
onArtifactEdited: (artifact) => {
|
|
2010
|
-
this.onTemplateArtifactEdited(artifact, "dispatchArtifact");
|
|
2011
|
-
},
|
|
2012
|
-
helpKey: "",
|
|
2013
|
-
onExpectedArtifactSelected: (artifact) => this.onTemplateArtifactSelected(artifact.id, "dispatchArtifact"),
|
|
2014
|
-
pipeline: this.props.pipeline,
|
|
2015
|
-
stage
|
|
2016
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
2017
|
-
className: "col-md-1"
|
|
2018
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
2019
|
-
className: "form-control-static"
|
|
2020
|
-
}, /* @__PURE__ */ React.createElement("button", {
|
|
2021
|
-
onClick: () => this.removeInputArtifact("dispatchArtifact")
|
|
2022
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
2023
|
-
className: "glyphicon glyphicon-trash"
|
|
2024
|
-
}), /* @__PURE__ */ React.createElement("span", {
|
|
2025
|
-
className: "sr-only"
|
|
2026
|
-
}, "Remove field"))))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
2027
|
-
className: "col-md-offset-1 col-md-9"
|
|
2028
|
-
}, /* @__PURE__ */ React.createElement(StageArtifactSelectorDelegate, {
|
|
2029
|
-
artifact: this.getInputArtifact(stage, "indexArtifact").artifact,
|
|
2030
|
-
excludedArtifactTypePatterns: _DeployAppengineConfigForm.excludedArtifactTypes,
|
|
2031
|
-
expectedArtifactId: this.getInputArtifact(stage, "indexArtifact").id,
|
|
2032
|
-
label: "Index Artifact",
|
|
2033
|
-
onArtifactEdited: (artifact) => {
|
|
2034
|
-
this.onTemplateArtifactEdited(artifact, "indexArtifact");
|
|
2035
|
-
},
|
|
2036
|
-
helpKey: "",
|
|
2037
|
-
onExpectedArtifactSelected: (artifact) => this.onTemplateArtifactSelected(artifact.id, "indexArtifact"),
|
|
2038
|
-
pipeline: this.props.pipeline,
|
|
2039
|
-
stage
|
|
2040
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
2041
|
-
className: "col-md-1"
|
|
2042
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
2043
|
-
className: "form-control-static"
|
|
2044
|
-
}, /* @__PURE__ */ React.createElement("button", {
|
|
2045
|
-
onClick: () => this.removeInputArtifact("indexArtifact")
|
|
2046
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
2047
|
-
className: "glyphicon glyphicon-trash"
|
|
2048
|
-
}), /* @__PURE__ */ React.createElement("span", {
|
|
2049
|
-
className: "sr-only"
|
|
2050
|
-
}, "Remove field"))))), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", {
|
|
2051
|
-
className: "col-md-offset-1 col-md-9"
|
|
2052
|
-
}, /* @__PURE__ */ React.createElement(StageArtifactSelectorDelegate, {
|
|
2053
|
-
artifact: this.getInputArtifact(stage, "queueArtifact").artifact,
|
|
2054
|
-
excludedArtifactTypePatterns: _DeployAppengineConfigForm.excludedArtifactTypes,
|
|
2055
|
-
expectedArtifactId: this.getInputArtifact(stage, "queueArtifact").id,
|
|
2056
|
-
label: "Queue Artifact",
|
|
2057
|
-
onArtifactEdited: (artifact) => {
|
|
2058
|
-
this.onTemplateArtifactEdited(artifact, "queueArtifact");
|
|
2059
|
-
},
|
|
2060
|
-
helpKey: "",
|
|
2061
|
-
onExpectedArtifactSelected: (artifact) => this.onTemplateArtifactSelected(artifact.id, "queueArtifact"),
|
|
2062
|
-
pipeline: this.props.pipeline,
|
|
2063
|
-
stage
|
|
2064
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
2065
|
-
className: "col-md-1"
|
|
2066
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
2067
|
-
className: "form-control-static"
|
|
2068
|
-
}, /* @__PURE__ */ React.createElement("button", {
|
|
2069
|
-
onClick: () => this.removeInputArtifact("queueArtifact")
|
|
2070
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
2071
|
-
className: "glyphicon glyphicon-trash"
|
|
2072
|
-
}), /* @__PURE__ */ React.createElement("span", {
|
|
2073
|
-
className: "sr-only"
|
|
2074
|
-
}, "Remove field"))))));
|
|
2075
|
-
}
|
|
2076
|
-
};
|
|
2077
|
-
let DeployAppengineConfigForm = _DeployAppengineConfigForm;
|
|
2078
|
-
DeployAppengineConfigForm.excludedArtifactTypes = excludeAllTypesExcept(ArtifactTypePatterns.BITBUCKET_FILE, ArtifactTypePatterns.CUSTOM_OBJECT, ArtifactTypePatterns.EMBEDDED_BASE64, ArtifactTypePatterns.GCS_OBJECT, ArtifactTypePatterns.GITHUB_FILE, ArtifactTypePatterns.GITLAB_FILE, ArtifactTypePatterns.S3_OBJECT, ArtifactTypePatterns.HTTP_FILE, ArtifactTypePatterns.ORACLE_OBJECT);
|
|
2079
|
-
|
|
2080
|
-
function DeployAppengineConfigurationConfig({ application, pipeline, stage, updateStage }) {
|
|
2081
|
-
const stageWithDefaults = React.useMemo(() => {
|
|
2082
|
-
return {
|
|
2083
|
-
...cloneDeep(stage)
|
|
2084
|
-
};
|
|
2085
|
-
}, []);
|
|
2086
|
-
return /* @__PURE__ */ React.createElement(FormikStageConfig, {
|
|
2087
|
-
application,
|
|
2088
|
-
onChange: updateStage,
|
|
2089
|
-
pipeline,
|
|
2090
|
-
stage: stageWithDefaults,
|
|
2091
|
-
validate: validateDeployAppengineConfigurationStage,
|
|
2092
|
-
render: (props) => /* @__PURE__ */ React.createElement(DeployAppengineConfigForm, {
|
|
2093
|
-
...props
|
|
2094
|
-
})
|
|
2095
|
-
});
|
|
2096
|
-
}
|
|
2097
|
-
function validateDeployAppengineConfigurationStage(stage) {
|
|
2098
|
-
const formValidator = new FormValidator(stage);
|
|
2099
|
-
formValidator.field("account").required();
|
|
2100
|
-
formValidator.field("region").required();
|
|
2101
|
-
return formValidator.validateForm();
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
const DEPLOY_APPENGINE_CONFIG_STAGE_KEY = "deployAppEngineConfiguration";
|
|
2105
|
-
Registry.pipeline.registerStage({
|
|
2106
|
-
label: "Deploy App Engine Configuration",
|
|
2107
|
-
description: "Deploy index, dispatch, cron, and queue configuration to App Engine.",
|
|
2108
|
-
key: DEPLOY_APPENGINE_CONFIG_STAGE_KEY,
|
|
2109
|
-
component: DeployAppengineConfigurationConfig,
|
|
2110
|
-
producesArtifacts: false,
|
|
2111
|
-
cloudProvider: "appengine",
|
|
2112
|
-
executionDetailsSections: [ExecutionDetailsTasks, ExecutionArtifactTab],
|
|
2113
|
-
validateFn: validateDeployAppengineConfigurationStage
|
|
2114
|
-
});
|
|
2115
|
-
|
|
2116
|
-
const AppengineProviderSettings = SETTINGS.providers.appengine || { defaults: {} };
|
|
2117
|
-
if (AppengineProviderSettings) {
|
|
2118
|
-
AppengineProviderSettings.resetToOriginal = SETTINGS.resetProvider("appengine");
|
|
2119
|
-
}
|
|
2120
|
-
|
|
2121
|
-
var AppengineSourceType;
|
|
2122
|
-
(function(AppengineSourceType2) {
|
|
2123
|
-
AppengineSourceType2["GCS"] = "gcs";
|
|
2124
|
-
AppengineSourceType2["GIT"] = "git";
|
|
2125
|
-
AppengineSourceType2["ARTIFACT"] = "artifact";
|
|
2126
|
-
AppengineSourceType2["CONTAINER_IMAGE"] = "containerImage";
|
|
2127
|
-
})(AppengineSourceType || (AppengineSourceType = {}));
|
|
2128
|
-
const _AppengineServerGroupCommandBuilder = class {
|
|
2129
|
-
constructor($q) {
|
|
2130
|
-
this.$q = $q;
|
|
2131
|
-
}
|
|
2132
|
-
static getTriggerOptions(pipeline) {
|
|
2133
|
-
return (pipeline.triggers || []).filter((trigger) => trigger.type === "git" || trigger.type === "jenkins" || trigger.type === "travis").map((trigger) => {
|
|
2134
|
-
if (trigger.type === "git") {
|
|
2135
|
-
return {
|
|
2136
|
-
source: trigger.source,
|
|
2137
|
-
project: trigger.project,
|
|
2138
|
-
slug: trigger.slug,
|
|
2139
|
-
branch: trigger.branch,
|
|
2140
|
-
type: "git"
|
|
2141
|
-
};
|
|
2142
|
-
} else {
|
|
2143
|
-
return { master: trigger.master, job: trigger.job, type: trigger.type };
|
|
2144
|
-
}
|
|
2145
|
-
});
|
|
2146
|
-
}
|
|
2147
|
-
static getExpectedArtifacts(pipeline) {
|
|
2148
|
-
return pipeline.expectedArtifacts || [];
|
|
2149
|
-
}
|
|
2150
|
-
buildNewServerGroupCommand(app, selectedProvider, mode = "create") {
|
|
2151
|
-
if (selectedProvider == null) {
|
|
2152
|
-
selectedProvider = "appengine";
|
|
2153
|
-
}
|
|
2154
|
-
const dataToFetch = {
|
|
2155
|
-
accounts: AccountService.getAllAccountDetailsForProvider("appengine"),
|
|
2156
|
-
storageAccounts: StorageAccountReader.getStorageAccounts()
|
|
2157
|
-
};
|
|
2158
|
-
const viewState = {
|
|
2159
|
-
mode,
|
|
2160
|
-
submitButtonLabel: this.getSubmitButtonLabel(mode),
|
|
2161
|
-
disableStrategySelection: mode === "create"
|
|
2162
|
-
};
|
|
2163
|
-
return this.$q.all(dataToFetch).then((backingData) => {
|
|
2164
|
-
const credentials = this.getCredentials(backingData.accounts);
|
|
2165
|
-
const region = this.getRegion(backingData.accounts, credentials);
|
|
2166
|
-
return {
|
|
2167
|
-
application: app.name,
|
|
2168
|
-
backingData,
|
|
2169
|
-
viewState,
|
|
2170
|
-
fromArtifact: false,
|
|
2171
|
-
credentials,
|
|
2172
|
-
region,
|
|
2173
|
-
selectedProvider,
|
|
2174
|
-
interestingHealthProviderNames: [],
|
|
2175
|
-
sourceType: AppengineSourceType.GIT
|
|
2176
|
-
};
|
|
2177
|
-
});
|
|
2178
|
-
}
|
|
2179
|
-
buildServerGroupCommandFromExisting(app, serverGroup) {
|
|
2180
|
-
return this.buildNewServerGroupCommand(app, "appengine", "clone").then((command) => {
|
|
2181
|
-
command.stack = serverGroup.stack;
|
|
2182
|
-
command.freeFormDetails = serverGroup.detail;
|
|
2183
|
-
return command;
|
|
2184
|
-
});
|
|
2185
|
-
}
|
|
2186
|
-
buildNewServerGroupCommandForPipeline(_stage, pipeline) {
|
|
2187
|
-
return this.$q.when({
|
|
2188
|
-
viewState: {
|
|
2189
|
-
pipeline,
|
|
2190
|
-
stage: _stage
|
|
2191
|
-
},
|
|
2192
|
-
backingData: {
|
|
2193
|
-
triggerOptions: _AppengineServerGroupCommandBuilder.getTriggerOptions(pipeline),
|
|
2194
|
-
expectedArtifacts: _AppengineServerGroupCommandBuilder.getExpectedArtifacts(pipeline)
|
|
2195
|
-
}
|
|
2196
|
-
});
|
|
2197
|
-
}
|
|
2198
|
-
buildServerGroupCommandFromPipeline(app, cluster, _stage, pipeline) {
|
|
2199
|
-
return this.buildNewServerGroupCommand(app, "appengine", "editPipeline").then((command) => {
|
|
2200
|
-
command = {
|
|
2201
|
-
...command,
|
|
2202
|
-
...cluster,
|
|
2203
|
-
backingData: {
|
|
2204
|
-
...command.backingData,
|
|
2205
|
-
triggerOptions: _AppengineServerGroupCommandBuilder.getTriggerOptions(pipeline),
|
|
2206
|
-
expectedArtifacts: _AppengineServerGroupCommandBuilder.getExpectedArtifacts(pipeline)
|
|
2207
|
-
},
|
|
2208
|
-
credentials: cluster.account || command.credentials,
|
|
2209
|
-
viewState: {
|
|
2210
|
-
...command.viewState,
|
|
2211
|
-
stage: _stage,
|
|
2212
|
-
pipeline
|
|
2213
|
-
}
|
|
2214
|
-
};
|
|
2215
|
-
return command;
|
|
2216
|
-
});
|
|
2217
|
-
}
|
|
2218
|
-
getCredentials(accounts) {
|
|
2219
|
-
const accountNames = (accounts || []).map((account) => account.name);
|
|
2220
|
-
const defaultCredentials = AppengineProviderSettings.defaults.account;
|
|
2221
|
-
return accountNames.includes(defaultCredentials) ? defaultCredentials : accountNames[0];
|
|
2222
|
-
}
|
|
2223
|
-
getRegion(accounts, credentials) {
|
|
2224
|
-
const account = accounts.find((_account) => _account.name === credentials);
|
|
2225
|
-
return account ? account.region : null;
|
|
2226
|
-
}
|
|
2227
|
-
getSubmitButtonLabel(mode) {
|
|
2228
|
-
switch (mode) {
|
|
2229
|
-
case "createPipeline":
|
|
2230
|
-
return "Add";
|
|
2231
|
-
case "editPipeline":
|
|
2232
|
-
return "Done";
|
|
2233
|
-
case "clone":
|
|
2234
|
-
return "Clone";
|
|
2235
|
-
default:
|
|
2236
|
-
return "Create";
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
};
|
|
2240
|
-
let AppengineServerGroupCommandBuilder = _AppengineServerGroupCommandBuilder;
|
|
2241
|
-
AppengineServerGroupCommandBuilder.$inject = ["$q"];
|
|
2242
|
-
const APPENGINE_SERVER_GROUP_COMMAND_BUILDER = "spinnaker.appengine.serverGroupCommandBuilder.service";
|
|
2243
|
-
module(APPENGINE_SERVER_GROUP_COMMAND_BUILDER, []).service("appengineServerGroupCommandBuilder", AppengineServerGroupCommandBuilder);
|
|
2244
|
-
|
|
2245
|
-
class AppengineServerGroupBasicSettingsCtrl {
|
|
2246
|
-
constructor($scope, $state, $controller, $uibModalStack) {
|
|
2247
|
-
this.$scope = $scope;
|
|
2248
|
-
this.excludedGcsArtifactTypes = excludeAllTypesExcept(ArtifactTypePatterns.GCS_OBJECT);
|
|
2249
|
-
this.excludedContainerArtifactTypes = excludeAllTypesExcept(ArtifactTypePatterns.DOCKER_IMAGE);
|
|
2250
|
-
this.onExpectedArtifactEdited = (artifact) => {
|
|
2251
|
-
this.$scope.$applyAsync(() => {
|
|
2252
|
-
this.$scope.command.expectedArtifactId = null;
|
|
2253
|
-
this.$scope.command.expectedArtifact = artifact;
|
|
2254
|
-
});
|
|
2255
|
-
};
|
|
2256
|
-
this.onExpectedArtifactSelected = (expectedArtifact) => {
|
|
2257
|
-
this.onChangeExpectedArtifactId(expectedArtifact.id);
|
|
2258
|
-
};
|
|
2259
|
-
this.onChangeExpectedArtifactId = (artifactId) => {
|
|
2260
|
-
this.$scope.$applyAsync(() => {
|
|
2261
|
-
this.$scope.command.expectedArtifactId = artifactId;
|
|
2262
|
-
this.$scope.command.expectedArtifact = null;
|
|
2263
|
-
});
|
|
2264
|
-
};
|
|
2265
|
-
this.onExpectedArtifactAccountSelected = (accountName) => {
|
|
2266
|
-
this.$scope.$applyAsync(() => {
|
|
2267
|
-
this.$scope.command.storageAccountName = accountName;
|
|
2268
|
-
});
|
|
2269
|
-
};
|
|
2270
|
-
extend(this, $controller("BasicSettingsMixin", {
|
|
2271
|
-
$scope,
|
|
2272
|
-
imageReader: null,
|
|
2273
|
-
$uibModalStack,
|
|
2274
|
-
$state
|
|
2275
|
-
}));
|
|
2276
|
-
if (!this.$scope.command.gitCredentialType) {
|
|
2277
|
-
this.onAccountChange();
|
|
2278
|
-
}
|
|
2279
|
-
this.$scope.containerArtifactDelegate = new NgAppEngineDeployArtifactDelegate($scope, [
|
|
2280
|
-
ArtifactTypePatterns.DOCKER_IMAGE
|
|
2281
|
-
]);
|
|
2282
|
-
this.$scope.containerArtifactController = new ExpectedArtifactSelectorViewController(this.$scope.containerArtifactDelegate);
|
|
2283
|
-
this.$scope.gcsArtifactDelegate = new NgAppEngineDeployArtifactDelegate($scope, [ArtifactTypePatterns.GCS_OBJECT]);
|
|
2284
|
-
this.$scope.gcsArtifactController = new ExpectedArtifactSelectorViewController(this.$scope.gcsArtifactDelegate);
|
|
2285
|
-
}
|
|
2286
|
-
isGitSource() {
|
|
2287
|
-
return this.$scope.command.sourceType === AppengineSourceType.GIT;
|
|
2288
|
-
}
|
|
2289
|
-
isGcsSource() {
|
|
2290
|
-
return this.$scope.command.sourceType === AppengineSourceType.GCS;
|
|
2291
|
-
}
|
|
2292
|
-
isContainerImageSource() {
|
|
2293
|
-
return this.$scope.command.sourceType === AppengineSourceType.CONTAINER_IMAGE;
|
|
2294
|
-
}
|
|
2295
|
-
toggleResolveViaTrigger() {
|
|
2296
|
-
this.$scope.command.fromTrigger = !this.$scope.command.fromTrigger;
|
|
2297
|
-
delete this.$scope.command.trigger;
|
|
2298
|
-
delete this.$scope.command.branch;
|
|
2299
|
-
}
|
|
2300
|
-
onTriggerChange() {
|
|
2301
|
-
set(this, "$scope.command.trigger.matchBranchOnRegex", void 0);
|
|
2302
|
-
}
|
|
2303
|
-
onAccountChange() {
|
|
2304
|
-
const account = this.findAccountInBackingData();
|
|
2305
|
-
if (account) {
|
|
2306
|
-
this.$scope.command.gitCredentialType = this.getSupportedGitCredentialTypes()[0];
|
|
2307
|
-
this.$scope.command.region = account.region;
|
|
2308
|
-
} else {
|
|
2309
|
-
this.$scope.command.gitCredentialType = "NONE";
|
|
2310
|
-
delete this.$scope.command.region;
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
getSupportedGitCredentialTypes() {
|
|
2314
|
-
const account = this.findAccountInBackingData();
|
|
2315
|
-
if (account && account.supportedGitCredentialTypes) {
|
|
2316
|
-
return account.supportedGitCredentialTypes;
|
|
2317
|
-
} else {
|
|
2318
|
-
return ["NONE"];
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
humanReadableGitCredentialType(type) {
|
|
2322
|
-
switch (type) {
|
|
2323
|
-
case "HTTPS_USERNAME_PASSWORD":
|
|
2324
|
-
return "HTTPS with username and password";
|
|
2325
|
-
case "HTTPS_GITHUB_OAUTH_TOKEN":
|
|
2326
|
-
return "HTTPS with Github OAuth token";
|
|
2327
|
-
case "SSH":
|
|
2328
|
-
return "SSH";
|
|
2329
|
-
case "NONE":
|
|
2330
|
-
return "No credentials";
|
|
2331
|
-
default:
|
|
2332
|
-
return "No credentials";
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
findAccountInBackingData() {
|
|
2336
|
-
return this.$scope.command.backingData.accounts.find((account) => {
|
|
2337
|
-
return this.$scope.command.credentials === account.name;
|
|
2338
|
-
});
|
|
2339
|
-
}
|
|
2340
|
-
}
|
|
2341
|
-
AppengineServerGroupBasicSettingsCtrl.$inject = ["$scope", "$state", "$controller", "$uibModalStack"];
|
|
2342
|
-
const APPENGINE_SERVER_GROUP_BASIC_SETTINGS_CTRL = "spinnaker.appengine.basicSettings.controller";
|
|
2343
|
-
module(APPENGINE_SERVER_GROUP_BASIC_SETTINGS_CTRL, []).controller("appengineServerGroupBasicSettingsCtrl", AppengineServerGroupBasicSettingsCtrl);
|
|
2344
|
-
|
|
2345
|
-
var css_248z$1 = ".appengine-server-group-wizard input[type='checkbox'] {\n margin-top: 0.75rem;\n}\n.appengine-server-group-wizard help-field.help-field-absolute span {\n position: absolute;\n top: 7px;\n right: 2px;\n}\n.appengine-server-group-wizard .artifact-configuration-section {\n border-bottom: 1px solid #eee;\n padding: 0 0 4px 0;\n margin: 0 0 5px 0;\n}\n.appengine-server-group-wizard .artifact-configuration-section.last-entry {\n border-bottom: none;\n}\n.appengine-server-group-wizard .artifact-configuration-section .Select,\n.appengine-server-group-wizard .artifact-configuration-section input {\n margin: 0 0 1px;\n}\n";
|
|
2346
|
-
styleInject(css_248z$1);
|
|
2347
|
-
|
|
2348
|
-
class ConfigArtifact {
|
|
2349
|
-
constructor($scope, pair = { id: "", account: "" }) {
|
|
2350
|
-
var _a;
|
|
2351
|
-
const unserializable = { configurable: false, enumerable: false, writable: false };
|
|
2352
|
-
this.id = pair == null ? void 0 : pair.id;
|
|
2353
|
-
this.account = pair.account || ((_a = pair == null ? void 0 : pair.artifact) == null ? void 0 : _a.artifactAccount);
|
|
2354
|
-
this.artifact = pair == null ? void 0 : pair.artifact;
|
|
2355
|
-
Object.defineProperty(this, "$scope", { ...unserializable, value: $scope });
|
|
2356
|
-
const delegate = new NgAppengineConfigArtifactDelegate(this);
|
|
2357
|
-
const controller = new ExpectedArtifactSelectorViewController(delegate);
|
|
2358
|
-
Object.defineProperty(this, "delegate", { ...unserializable, value: delegate });
|
|
2359
|
-
Object.defineProperty(this, "controller", { ...unserializable, value: controller });
|
|
2360
|
-
}
|
|
2361
|
-
}
|
|
2362
|
-
class AppengineConfigFileConfigurerCtrl {
|
|
2363
|
-
constructor($scope) {
|
|
2364
|
-
this.$scope = $scope;
|
|
2365
|
-
this.artifactAccounts = [];
|
|
2366
|
-
this.updateConfigArtifacts = (configArtifacts) => {
|
|
2367
|
-
this.$scope.$applyAsync(() => {
|
|
2368
|
-
this.command.configArtifacts = configArtifacts;
|
|
2369
|
-
});
|
|
2370
|
-
};
|
|
2371
|
-
}
|
|
2372
|
-
$onInit() {
|
|
2373
|
-
if (!this.command.configFiles) {
|
|
2374
|
-
this.command.configFiles = [];
|
|
2375
|
-
}
|
|
2376
|
-
if (!this.command.configArtifacts) {
|
|
2377
|
-
this.command.configArtifacts = [];
|
|
2378
|
-
}
|
|
2379
|
-
if (!this.$scope.command) {
|
|
2380
|
-
this.$scope.command = this.command;
|
|
2381
|
-
}
|
|
2382
|
-
this.command.configArtifacts = this.command.configArtifacts.map((artifactAccountPair) => {
|
|
2383
|
-
return new ConfigArtifact(this.$scope, artifactAccountPair);
|
|
2384
|
-
});
|
|
2385
|
-
AccountService.getArtifactAccounts().then((accounts) => {
|
|
2386
|
-
this.artifactAccounts = accounts;
|
|
2387
|
-
this.command.configArtifacts.forEach((a) => {
|
|
2388
|
-
a.delegate.setAccounts(accounts);
|
|
2389
|
-
a.controller.updateAccounts(a.delegate.getSelectedExpectedArtifact());
|
|
2390
|
-
});
|
|
2391
|
-
});
|
|
2392
|
-
}
|
|
2393
|
-
addConfigFile() {
|
|
2394
|
-
this.command.configFiles.push("");
|
|
2395
|
-
}
|
|
2396
|
-
addConfigArtifact() {
|
|
2397
|
-
const artifact = new ConfigArtifact(this.$scope, { id: "", account: "" });
|
|
2398
|
-
artifact.delegate.setAccounts(this.artifactAccounts);
|
|
2399
|
-
artifact.controller.updateAccounts(artifact.delegate.getSelectedExpectedArtifact());
|
|
2400
|
-
this.command.configArtifacts.push(artifact);
|
|
2401
|
-
}
|
|
2402
|
-
deleteConfigFile(index) {
|
|
2403
|
-
this.command.configFiles.splice(index, 1);
|
|
2404
|
-
}
|
|
2405
|
-
deleteConfigArtifact(index) {
|
|
2406
|
-
this.command.configArtifacts.splice(index, 1);
|
|
2407
|
-
}
|
|
2408
|
-
mapTabToSpaces(event) {
|
|
2409
|
-
if (event.which === 9) {
|
|
2410
|
-
event.preventDefault();
|
|
2411
|
-
const cursorPosition = event.target.selectionStart;
|
|
2412
|
-
const inputValue = event.target.value;
|
|
2413
|
-
event.target.value = `${inputValue.substring(0, cursorPosition)} ${inputValue.substring(cursorPosition)}`;
|
|
2414
|
-
event.target.selectionStart += 2;
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
isContainerImageSource() {
|
|
2418
|
-
return this.command.sourceType === AppengineSourceType.CONTAINER_IMAGE;
|
|
2419
|
-
}
|
|
2420
|
-
}
|
|
2421
|
-
AppengineConfigFileConfigurerCtrl.$inject = ["$scope"];
|
|
2422
|
-
const appengineConfigFileConfigurerComponent = {
|
|
2423
|
-
bindings: { command: "=" },
|
|
2424
|
-
controller: AppengineConfigFileConfigurerCtrl,
|
|
2425
|
-
templateUrl: "appengine/src/serverGroup/configure/wizard/configFiles.component.html"
|
|
2426
|
-
};
|
|
2427
|
-
const APPENGINE_CONFIG_FILE_CONFIGURER = "spinnaker.appengine.configFileConfigurer.component";
|
|
2428
|
-
module(APPENGINE_CONFIG_FILE_CONFIGURER, []).component("appengineConfigFileConfigurer", appengineConfigFileConfigurerComponent);
|
|
2429
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
2430
|
-
templateCache.put("appengine/src/serverGroup/configure/wizard/configFiles.component.html", `<div class="form-horizontal container-fluid">
|
|
2431
|
-
<div class="form-group" ng-if="!$ctrl.isContainerImageSource()">
|
|
2432
|
-
<div class="col-md-3 sm-label-right">
|
|
2433
|
-
Application Root
|
|
2434
|
-
<help-field class="help-field-absolute" key="appengine.serverGroup.applicationDirectoryRoot"></help-field>
|
|
2435
|
-
</div>
|
|
2436
|
-
<div class="col-md-7">
|
|
2437
|
-
<input
|
|
2438
|
-
type="text"
|
|
2439
|
-
class="form-control input-sm"
|
|
2440
|
-
name="applicationDirectoryRoot"
|
|
2441
|
-
ng-model="$ctrl.command.applicationDirectoryRoot"
|
|
2442
|
-
/>
|
|
2443
|
-
</div>
|
|
2444
|
-
</div>
|
|
2445
|
-
|
|
2446
|
-
<div class="form-group" ng-if="!$ctrl.isContainerImageSource()">
|
|
2447
|
-
<div class="col-md-3 sm-label-right">
|
|
2448
|
-
Config Filepaths <help-field key="appengine.serverGroup.configFilepaths"></help-field>
|
|
2449
|
-
</div>
|
|
2450
|
-
<div class="col-md-7">
|
|
2451
|
-
<ui-select
|
|
2452
|
-
multiple
|
|
2453
|
-
tagging
|
|
2454
|
-
tagging-label=""
|
|
2455
|
-
style="width: 380px"
|
|
2456
|
-
name="configFilepaths"
|
|
2457
|
-
ng-model="$ctrl.command.configFilepaths"
|
|
2458
|
-
class="form-control input-sm"
|
|
2459
|
-
>
|
|
2460
|
-
<ui-select-match>{{ $item }}</ui-select-match>
|
|
2461
|
-
<ui-select-choices repeat="filepath in []">
|
|
2462
|
-
<span ng-bind-html="filepath"></span>
|
|
2463
|
-
</ui-select-choices>
|
|
2464
|
-
</ui-select>
|
|
2465
|
-
</div>
|
|
2466
|
-
</div>
|
|
2467
|
-
|
|
2468
|
-
<div class="form-group">
|
|
2469
|
-
<div class="col-md-3 sm-label-right">
|
|
2470
|
-
Config Files
|
|
2471
|
-
<span ng-if="!$ctrl.isContainerImageSource()">
|
|
2472
|
-
<help-field key="appengine.serverGroup.configFiles"></help-field>
|
|
2473
|
-
</span>
|
|
2474
|
-
<span ng-if="$ctrl.isContainerImageSource()">
|
|
2475
|
-
<help-field key="appengine.serverGroup.configFilesRequired"></help-field>
|
|
2476
|
-
</span>
|
|
2477
|
-
</div>
|
|
2478
|
-
<div ng-repeat="configFile in $ctrl.command.configFiles track by $index">
|
|
2479
|
-
<div class="col-md-7" ng-class="{ 'col-md-offset-3': $index > 0 }" style="margin-top: 5px">
|
|
2480
|
-
<textarea
|
|
2481
|
-
cols="46"
|
|
2482
|
-
rows="10"
|
|
2483
|
-
class="small"
|
|
2484
|
-
spellcheck="false"
|
|
2485
|
-
ng-keydown="$ctrl.mapTabToSpaces($event)"
|
|
2486
|
-
style="font-family: Menlo, Monaco, Consolas, 'Courier New', monospace"
|
|
2487
|
-
ng-model="$ctrl.command.configFiles[$index]"
|
|
2488
|
-
></textarea>
|
|
2489
|
-
</div>
|
|
2490
|
-
<div class="col-md-1" style="margin-top: 5px">
|
|
2491
|
-
<button type="button" class="btn btn-sm btn-default" ng-click="$ctrl.deleteConfigFile($index)">
|
|
2492
|
-
<span class="glyphicon glyphicon-trash"></span> Delete
|
|
2493
|
-
</button>
|
|
2494
|
-
</div>
|
|
2495
|
-
</div>
|
|
2496
|
-
<div class="col-md-7" ng-class="{ 'col-md-offset-3': $ctrl.command.configFiles.length > 0 }">
|
|
2497
|
-
<button class="btn btn-block btn-add-trigger add-new" ng-click="$ctrl.addConfigFile()">
|
|
2498
|
-
<span class="glyphicon glyphicon-plus-sign"></span> Add Config File
|
|
2499
|
-
</button>
|
|
2500
|
-
</div>
|
|
2501
|
-
<config-file-artifact-list
|
|
2502
|
-
ng-if="$ctrl.command.viewState.pipeline"
|
|
2503
|
-
config-artifacts="$ctrl.command.configArtifacts"
|
|
2504
|
-
pipeline="$ctrl.command.viewState.pipeline"
|
|
2505
|
-
stage="$ctrl.command.viewState.stage"
|
|
2506
|
-
update-config-artifacts="$ctrl.updateConfigArtifacts"
|
|
2507
|
-
>
|
|
2508
|
-
</config-file-artifact-list>
|
|
2509
|
-
</div>
|
|
2510
|
-
</div>
|
|
2511
|
-
`);
|
|
2512
|
-
}]);
|
|
2513
|
-
|
|
2514
|
-
const appengineDynamicBranchLabelComponent = {
|
|
2515
|
-
bindings: { trigger: "<" },
|
|
2516
|
-
template: `
|
|
2517
|
-
<span ng-if="$ctrl.trigger.type === 'git'">
|
|
2518
|
-
Resolved at runtime by <b>{{$ctrl.trigger.source}}</b> trigger: {{$ctrl.trigger.project}}/{{$ctrl.trigger.slug}}<span ng-if="$ctrl.trigger.branch">:{{$ctrl.trigger.branch}}</span>
|
|
2519
|
-
</span>
|
|
2520
|
-
<span ng-if="$ctrl.trigger.type === 'jenkins'">
|
|
2521
|
-
Resolved at runtime by <b>Jenkins</b> trigger: {{$ctrl.trigger.master}}/{{$ctrl.trigger.job}}
|
|
2522
|
-
</span>
|
|
2523
|
-
`
|
|
2524
|
-
};
|
|
2525
|
-
const APPENGINE_DYNAMIC_BRANCH_LABEL = "spinnaker.appengine.dynamicBranchLabel.component";
|
|
2526
|
-
module(APPENGINE_DYNAMIC_BRANCH_LABEL, []).component("appengineDynamicBranchLabel", appengineDynamicBranchLabelComponent);
|
|
2527
|
-
|
|
2528
|
-
class AppengineCloneServerGroupCtrl {
|
|
2529
|
-
constructor($scope, $uibModalInstance, serverGroupCommand, application, serverGroupWriter, appengineServerGroupCommandBuilder) {
|
|
2530
|
-
this.$scope = $scope;
|
|
2531
|
-
this.$uibModalInstance = $uibModalInstance;
|
|
2532
|
-
this.serverGroupCommand = serverGroupCommand;
|
|
2533
|
-
this.application = application;
|
|
2534
|
-
this.serverGroupWriter = serverGroupWriter;
|
|
2535
|
-
this.pages = {
|
|
2536
|
-
basicSettings: "appengine/src/serverGroup/configure/wizard/basicSettings.html",
|
|
2537
|
-
advancedSettings: "appengine/src/serverGroup/configure/wizard/advancedSettings.html"
|
|
2538
|
-
};
|
|
2539
|
-
this.state = {
|
|
2540
|
-
loading: true
|
|
2541
|
-
};
|
|
2542
|
-
if (["create", "clone", "editPipeline"].includes(get(serverGroupCommand, "viewState.mode"))) {
|
|
2543
|
-
this.$scope.command = serverGroupCommand;
|
|
2544
|
-
this.state.loading = false;
|
|
2545
|
-
this.initialize();
|
|
2546
|
-
} else {
|
|
2547
|
-
appengineServerGroupCommandBuilder.buildNewServerGroupCommand(application, "appengine", "createPipeline").then((constructedCommand) => {
|
|
2548
|
-
this.$scope.command = merge(constructedCommand, serverGroupCommand);
|
|
2549
|
-
this.$scope.command.viewState.pipeline = serverGroupCommand.viewState.pipeline;
|
|
2550
|
-
this.$scope.command.viewState.stage = serverGroupCommand.viewState.stage;
|
|
2551
|
-
this.state.loading = false;
|
|
2552
|
-
this.initialize();
|
|
2553
|
-
});
|
|
2554
|
-
}
|
|
2555
|
-
}
|
|
2556
|
-
cancel() {
|
|
2557
|
-
this.$uibModalInstance.dismiss();
|
|
2558
|
-
}
|
|
2559
|
-
submit() {
|
|
2560
|
-
const mode = this.$scope.command.viewState.mode;
|
|
2561
|
-
if (["editPipeline", "createPipeline"].includes(mode)) {
|
|
2562
|
-
return this.$uibModalInstance.close(this.$scope.command);
|
|
2563
|
-
} else {
|
|
2564
|
-
const command = copy(this.$scope.command);
|
|
2565
|
-
command.viewState.mode = "create";
|
|
2566
|
-
const submitMethod = () => this.serverGroupWriter.cloneServerGroup(command, this.$scope.application);
|
|
2567
|
-
this.taskMonitor.submit(submitMethod);
|
|
2568
|
-
return null;
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
initialize() {
|
|
2572
|
-
this.$scope.application = this.application;
|
|
2573
|
-
this.taskMonitor = new TaskMonitor({
|
|
2574
|
-
application: this.application,
|
|
2575
|
-
title: "Creating your server group",
|
|
2576
|
-
modalInstance: this.$uibModalInstance
|
|
2577
|
-
});
|
|
2578
|
-
this.$scope.showPlatformHealthOnlyOverride = this.application.attributes.platformHealthOnlyShowOverride;
|
|
2579
|
-
this.$scope.platformHealth = AppengineHealth.PLATFORM;
|
|
2580
|
-
if (this.application.attributes.platformHealthOnly) {
|
|
2581
|
-
this.$scope.command.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
2582
|
-
}
|
|
2583
|
-
}
|
|
2584
|
-
}
|
|
2585
|
-
AppengineCloneServerGroupCtrl.$inject = [
|
|
2586
|
-
"$scope",
|
|
2587
|
-
"$uibModalInstance",
|
|
2588
|
-
"serverGroupCommand",
|
|
2589
|
-
"application",
|
|
2590
|
-
"serverGroupWriter",
|
|
2591
|
-
"appengineServerGroupCommandBuilder"
|
|
2592
|
-
];
|
|
2593
|
-
const APPENGINE_CLONE_SERVER_GROUP_CTRL = "spinnaker.appengine.cloneServerGroup.controller";
|
|
2594
|
-
module(APPENGINE_CLONE_SERVER_GROUP_CTRL, [
|
|
2595
|
-
SERVER_GROUP_WRITER,
|
|
2596
|
-
APPENGINE_DYNAMIC_BRANCH_LABEL,
|
|
2597
|
-
APPENGINE_CONFIG_FILE_CONFIGURER
|
|
2598
|
-
]).controller("appengineCloneServerGroupCtrl", AppengineCloneServerGroupCtrl);
|
|
2599
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
2600
|
-
templateCache.put("appengine/src/serverGroup/configure/wizard/basicSettings.html", `<div class="container-fluid form-horizontal" ng-controller="appengineServerGroupBasicSettingsCtrl as basicSettingsCtrl">
|
|
2601
|
-
<ng-form name="basicSettings">
|
|
2602
|
-
<div class="form-group">
|
|
2603
|
-
<div class="col-md-3 sm-label-right">Account</div>
|
|
2604
|
-
<div class="col-md-7">
|
|
2605
|
-
<account-select-field
|
|
2606
|
-
read-only="command.viewState.readOnlyFields.credentials"
|
|
2607
|
-
component="command"
|
|
2608
|
-
field="credentials"
|
|
2609
|
-
on-change="basicSettingsCtrl.onAccountChange()"
|
|
2610
|
-
accounts="command.backingData.accounts"
|
|
2611
|
-
provider="'appengine'"
|
|
2612
|
-
></account-select-field>
|
|
2613
|
-
</div>
|
|
2614
|
-
</div>
|
|
2615
|
-
|
|
2616
|
-
<div class="form-group">
|
|
2617
|
-
<div class="col-md-3 sm-label-right">Region</div>
|
|
2618
|
-
<div class="col-md-7">
|
|
2619
|
-
<input type="text" readonly class="form-control input-sm" name="region" ng-model="command.region" />
|
|
2620
|
-
</div>
|
|
2621
|
-
</div>
|
|
2622
|
-
|
|
2623
|
-
<div class="form-group">
|
|
2624
|
-
<div class="col-md-3 sm-label-right">
|
|
2625
|
-
Stack
|
|
2626
|
-
<help-field key="aws.serverGroup.stack"></help-field>
|
|
2627
|
-
</div>
|
|
2628
|
-
<div class="col-md-7">
|
|
2629
|
-
<input
|
|
2630
|
-
type="text"
|
|
2631
|
-
class="form-control input-sm no-spel"
|
|
2632
|
-
ng-pattern="basicSettingsCtrl.stackPattern"
|
|
2633
|
-
name="stack"
|
|
2634
|
-
ng-model="command.stack"
|
|
2635
|
-
/>
|
|
2636
|
-
</div>
|
|
2637
|
-
</div>
|
|
2638
|
-
<div class="form-group row slide-in" ng-if="basicSettings.stack.$error.pattern">
|
|
2639
|
-
<div class="col-sm-9 col-sm-offset-2 error-message">
|
|
2640
|
-
<span>Only dot(.) and underscore(_) special characters are allowed in the Stack field.</span>
|
|
2641
|
-
</div>
|
|
2642
|
-
</div>
|
|
2643
|
-
<div class="form-group">
|
|
2644
|
-
<div class="col-md-3 sm-label-right">
|
|
2645
|
-
Detail
|
|
2646
|
-
<help-field key="aws.serverGroup.detail"></help-field>
|
|
2647
|
-
</div>
|
|
2648
|
-
<div class="col-md-7">
|
|
2649
|
-
<input
|
|
2650
|
-
type="text"
|
|
2651
|
-
class="form-control input-sm no-spel"
|
|
2652
|
-
ng-pattern="basicSettingsCtrl.detailPattern"
|
|
2653
|
-
name="details"
|
|
2654
|
-
ng-model="command.freeFormDetails"
|
|
2655
|
-
/>
|
|
2656
|
-
</div>
|
|
2657
|
-
</div>
|
|
2658
|
-
|
|
2659
|
-
<div class="form-group row slide-in" ng-if="basicSettings.details.$error.pattern">
|
|
2660
|
-
<div class="col-sm-9 col-sm-offset-2 error-message">
|
|
2661
|
-
<span>Only dot(.), underscore(_), and dash(-) special characters are allowed in the Detail field.</span>
|
|
2662
|
-
</div>
|
|
2663
|
-
</div>
|
|
2664
|
-
|
|
2665
|
-
<div class="form-group row">
|
|
2666
|
-
<label class="col-md-3 sm-label-right">Source Type</label>
|
|
2667
|
-
<div class="col-md-7">
|
|
2668
|
-
<div class="radio radio-inline">
|
|
2669
|
-
<label> <input type="radio" ng-model="command.sourceType" value="git" /> Git </label>
|
|
2670
|
-
</div>
|
|
2671
|
-
<div class="radio radio-inline">
|
|
2672
|
-
<label> <input type="radio" ng-model="command.sourceType" value="gcs" /> GCS </label>
|
|
2673
|
-
</div>
|
|
2674
|
-
<div class="radio radio-inline">
|
|
2675
|
-
<label> <input type="radio" ng-model="command.sourceType" value="containerImage" /> Container Image </label>
|
|
2676
|
-
</div>
|
|
2677
|
-
</div>
|
|
2678
|
-
</div>
|
|
2679
|
-
|
|
2680
|
-
<div ng-if="basicSettingsCtrl.isGcsSource()">
|
|
2681
|
-
<div class="form-group row">
|
|
2682
|
-
<label class="col-md-3 sm-label-right">Resolve URL</label>
|
|
2683
|
-
<div class="col-md-7">
|
|
2684
|
-
<div class="radio radio-inline">
|
|
2685
|
-
<label> <input type="radio" ng-model="command.fromArtifact" ng-value="false" /> via text input </label>
|
|
2686
|
-
</div>
|
|
2687
|
-
<div class="radio radio-inline" ng-if="command.viewState.pipeline">
|
|
2688
|
-
<label>
|
|
2689
|
-
<input type="radio" ng-model="command.fromArtifact" ng-value="true" /> via pipeline artifact
|
|
2690
|
-
</label>
|
|
2691
|
-
</div>
|
|
2692
|
-
</div>
|
|
2693
|
-
</div>
|
|
2694
|
-
<stage-artifact-selector-delegate
|
|
2695
|
-
ng-if="command.fromArtifact"
|
|
2696
|
-
artifact="command.expectedArtifact"
|
|
2697
|
-
excluded-artifact-type-patterns="basicSettingsCtrl.excludedGcsArtifactTypes"
|
|
2698
|
-
expected-artifact-id="command.expectedArtifactId"
|
|
2699
|
-
field-columns="7"
|
|
2700
|
-
label="'Expected Artifact'"
|
|
2701
|
-
on-artifact-edited="basicSettingsCtrl.onExpectedArtifactEdited"
|
|
2702
|
-
on-expected-artifact-selected="basicSettingsCtrl.onExpectedArtifactSelected"
|
|
2703
|
-
pipeline="command.viewState.pipeline"
|
|
2704
|
-
stage="command.viewState.stage"
|
|
2705
|
-
>
|
|
2706
|
-
</stage-artifact-selector-delegate>
|
|
2707
|
-
<div class="form-group" ng-if="!command.fromArtifact">
|
|
2708
|
-
<div class="col-md-3 sm-label-right">
|
|
2709
|
-
GCS URL
|
|
2710
|
-
<help-field class="help-field-absolute" key="appengine.serverGroup.gcs.repositoryUrl"></help-field>
|
|
2711
|
-
</div>
|
|
2712
|
-
<div class="col-md-7">
|
|
2713
|
-
<input type="text" required class="form-control input-sm" name="gcsUrl" ng-model="command.repositoryUrl" />
|
|
2714
|
-
</div>
|
|
2715
|
-
</div>
|
|
2716
|
-
</div>
|
|
2717
|
-
|
|
2718
|
-
<div ng-if="basicSettingsCtrl.isGitSource()">
|
|
2719
|
-
<div class="form-group">
|
|
2720
|
-
<div class="col-md-3 sm-label-right">
|
|
2721
|
-
Git Repository URL
|
|
2722
|
-
<help-field class="help-field-absolute" key="appengine.serverGroup.git.repositoryUrl"></help-field>
|
|
2723
|
-
</div>
|
|
2724
|
-
<div class="col-md-7">
|
|
2725
|
-
<input type="text" required class="form-control input-sm" name="gitRepo" ng-model="command.repositoryUrl" />
|
|
2726
|
-
</div>
|
|
2727
|
-
</div>
|
|
2728
|
-
|
|
2729
|
-
<div class="form-group">
|
|
2730
|
-
<div class="col-md-3 sm-label-right">
|
|
2731
|
-
Git Credential Type
|
|
2732
|
-
<help-field class="help-field-absolute" key="appengine.serverGroup.gitCredentialType"></help-field>
|
|
2733
|
-
</div>
|
|
2734
|
-
<div class="col-md-7">
|
|
2735
|
-
<select
|
|
2736
|
-
class="form-control input-sm"
|
|
2737
|
-
ng-options="basicSettingsCtrl.humanReadableGitCredentialType(type) for type in basicSettingsCtrl.getSupportedGitCredentialTypes()"
|
|
2738
|
-
ng-model="command.gitCredentialType"
|
|
2739
|
-
></select>
|
|
2740
|
-
</div>
|
|
2741
|
-
</div>
|
|
2742
|
-
|
|
2743
|
-
<div class="form-group">
|
|
2744
|
-
<div class="col-md-3 sm-label-right">Branch <help-field key="appengine.serverGroup.branch"></help-field></div>
|
|
2745
|
-
<div class="col-md-7">
|
|
2746
|
-
<input
|
|
2747
|
-
ng-if="!command.fromTrigger"
|
|
2748
|
-
type="text"
|
|
2749
|
-
required
|
|
2750
|
-
class="form-control input-sm"
|
|
2751
|
-
name="branch"
|
|
2752
|
-
ng-model="command.branch"
|
|
2753
|
-
/>
|
|
2754
|
-
|
|
2755
|
-
<ui-select
|
|
2756
|
-
ng-if="command.fromTrigger"
|
|
2757
|
-
ng-model="command.trigger"
|
|
2758
|
-
class="form-control input-sm"
|
|
2759
|
-
on-select="basicSettingsCtrl.onTriggerChange()"
|
|
2760
|
-
required
|
|
2761
|
-
>
|
|
2762
|
-
<ui-select-match allow-clear>
|
|
2763
|
-
<appengine-dynamic-branch-label trigger="command.trigger"></appengine-dynamic-branch-label>
|
|
2764
|
-
</ui-select-match>
|
|
2765
|
-
<ui-select-choices repeat="trigger in command.backingData.triggerOptions">
|
|
2766
|
-
<appengine-dynamic-branch-label trigger="trigger"></appengine-dynamic-branch-label>
|
|
2767
|
-
</ui-select-choices>
|
|
2768
|
-
</ui-select>
|
|
2769
|
-
</div>
|
|
2770
|
-
|
|
2771
|
-
<div
|
|
2772
|
-
class="col-md-7 col-md-offset-3"
|
|
2773
|
-
ng-if="command.viewState.mode === 'createPipeline' || command.viewState.mode === 'editPipeline'"
|
|
2774
|
-
>
|
|
2775
|
-
<span class="pull-right small" ng-if="!command.fromTrigger">
|
|
2776
|
-
<a href ng-click="basicSettingsCtrl.toggleResolveViaTrigger()">Resolve via trigger</a>
|
|
2777
|
-
</span>
|
|
2778
|
-
<span class="pull-right small" ng-if="command.fromTrigger">
|
|
2779
|
-
<a href ng-click="basicSettingsCtrl.toggleResolveViaTrigger()">Click for text input</a>
|
|
2780
|
-
</span>
|
|
2781
|
-
</div>
|
|
2782
|
-
</div>
|
|
2783
|
-
</div>
|
|
2784
|
-
|
|
2785
|
-
<div ng-if="basicSettingsCtrl.isContainerImageSource()">
|
|
2786
|
-
<div class="form-group">
|
|
2787
|
-
<label class="col-md-3 sm-label-right">Resolve URL</label>
|
|
2788
|
-
<div class="col-md-7">
|
|
2789
|
-
<div class="radio radio-inline">
|
|
2790
|
-
<label> <input type="radio" ng-model="command.fromArtifact" ng-value="false" /> via text input </label>
|
|
2791
|
-
</div>
|
|
2792
|
-
<div class="radio radio-inline" ng-if="command.viewState.pipeline">
|
|
2793
|
-
<label>
|
|
2794
|
-
<input type="radio" ng-model="command.fromArtifact" ng-value="true" /> via pipeline artifact
|
|
2795
|
-
</label>
|
|
2796
|
-
</div>
|
|
2797
|
-
</div>
|
|
2798
|
-
</div>
|
|
2799
|
-
<stage-artifact-selector-delegate
|
|
2800
|
-
ng-if="command.fromArtifact"
|
|
2801
|
-
artifact="command.expectedArtifact"
|
|
2802
|
-
excluded-artifact-type-patterns="basicSettingsCtrl.excludedContainerArtifactTypes"
|
|
2803
|
-
expected-artifact-id="command.expectedArtifactId"
|
|
2804
|
-
field-columns="7"
|
|
2805
|
-
label="'Expected Artifact'"
|
|
2806
|
-
on-artifact-edited="basicSettingsCtrl.onExpectedArtifactEdited"
|
|
2807
|
-
on-expected-artifact-selected="basicSettingsCtrl.onExpectedArtifactSelected"
|
|
2808
|
-
pipeline="command.viewState.pipeline"
|
|
2809
|
-
stage="command.viewState.stage"
|
|
2810
|
-
>
|
|
2811
|
-
</stage-artifact-selector-delegate>
|
|
2812
|
-
<div class="form-group" ng-if="!command.fromArtifact">
|
|
2813
|
-
<div class="col-md-3 sm-label-right">
|
|
2814
|
-
Image URL
|
|
2815
|
-
<help-field key="appengine.serverGroup.containerImageUrl"></help-field>
|
|
2816
|
-
</div>
|
|
2817
|
-
<div class="col-md-7">
|
|
2818
|
-
<input
|
|
2819
|
-
type="text"
|
|
2820
|
-
required
|
|
2821
|
-
class="form-control input-sm"
|
|
2822
|
-
name="containerImageUrl"
|
|
2823
|
-
ng-model="command.containerImageUrl"
|
|
2824
|
-
/>
|
|
2825
|
-
</div>
|
|
2826
|
-
</div>
|
|
2827
|
-
</div>
|
|
2828
|
-
|
|
2829
|
-
<div ng-if="command.trigger.type === 'jenkins'" class="form-group">
|
|
2830
|
-
<div class="col-md-7 col-md-offset-3">
|
|
2831
|
-
<div class="form-inline">
|
|
2832
|
-
<small>Match branch from trigger on regex</small>
|
|
2833
|
-
<help-field key="appengine.serverGroup.matchBranchOnRegex"></help-field>
|
|
2834
|
-
<input
|
|
2835
|
-
type="text"
|
|
2836
|
-
style="width: 140px"
|
|
2837
|
-
class="form-control input-sm pull-right"
|
|
2838
|
-
name="matchOnRegex"
|
|
2839
|
-
ng-model="command.trigger.matchBranchOnRegex"
|
|
2840
|
-
/>
|
|
2841
|
-
</div>
|
|
2842
|
-
</div>
|
|
2843
|
-
</div>
|
|
2844
|
-
|
|
2845
|
-
<deployment-strategy-selector
|
|
2846
|
-
field-columns="7"
|
|
2847
|
-
ng-if="!command.viewState.disableStrategySelection"
|
|
2848
|
-
command="command"
|
|
2849
|
-
></deployment-strategy-selector>
|
|
2850
|
-
|
|
2851
|
-
<div class="form-group" ng-if="!command.viewState.hideClusterNamePreview">
|
|
2852
|
-
<div class="col-md-12">
|
|
2853
|
-
<div class="well-compact" ng-class="basicSettingsCtrl.showPreviewAsWarning() ? 'alert alert-warning' : 'well'">
|
|
2854
|
-
<h5 class="text-center">
|
|
2855
|
-
<p>Your server group will be in the cluster:</p>
|
|
2856
|
-
<p>
|
|
2857
|
-
<strong>
|
|
2858
|
-
{{basicSettingsCtrl.getNamePreview()}}
|
|
2859
|
-
<span ng-if="basicSettingsCtrl.createsNewCluster()"> (new cluster)</span>
|
|
2860
|
-
</strong>
|
|
2861
|
-
</p>
|
|
2862
|
-
<div
|
|
2863
|
-
class="text-left"
|
|
2864
|
-
ng-if="!basicSettingsCtrl.createsNewCluster() && command.viewState.mode === 'create' && latestServerGroup"
|
|
2865
|
-
>
|
|
2866
|
-
<p>There is already a server group in this cluster. Do you want to clone it?</p>
|
|
2867
|
-
<p>
|
|
2868
|
-
Cloning copies the entire configuration from the selected server group, allowing you to modify whichever
|
|
2869
|
-
fields (e.g. image) you need to change in the new server group.
|
|
2870
|
-
</p>
|
|
2871
|
-
<p>
|
|
2872
|
-
To clone a server group, select "Clone" from the "Server Group Actions" menu in the details view of the
|
|
2873
|
-
server group.
|
|
2874
|
-
</p>
|
|
2875
|
-
<p>
|
|
2876
|
-
<a href ng-click="basicSettingsCtrl.navigateToLatestServerGroup()">
|
|
2877
|
-
Go to details for {{latestServerGroup.name}}
|
|
2878
|
-
</a>
|
|
2879
|
-
</p>
|
|
2880
|
-
</div>
|
|
2881
|
-
</h5>
|
|
2882
|
-
</div>
|
|
2883
|
-
</div>
|
|
2884
|
-
</div>
|
|
2885
|
-
</ng-form>
|
|
2886
|
-
</div>
|
|
2887
|
-
`);
|
|
2888
|
-
}]);
|
|
2889
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
2890
|
-
templateCache.put("appengine/src/serverGroup/configure/wizard/advancedSettings.html", `<div class="form-horizontal container-fluid">
|
|
2891
|
-
<div class="form-group">
|
|
2892
|
-
<div class="col-md-4 sm-label-right">Promote <help-field key="appengine.serverGroup.promote"></help-field></div>
|
|
2893
|
-
<div class="col-md-7">
|
|
2894
|
-
<input type="checkbox" ng-model="command.promote" />
|
|
2895
|
-
</div>
|
|
2896
|
-
</div>
|
|
2897
|
-
|
|
2898
|
-
<div class="form-group">
|
|
2899
|
-
<div class="col-md-4 sm-label-right">
|
|
2900
|
-
Stop Previous Version <help-field key="appengine.serverGroup.stopPreviousVersion"></help-field>
|
|
2901
|
-
</div>
|
|
2902
|
-
<div class="col-md-7">
|
|
2903
|
-
<input type="checkbox" ng-model="command.stopPreviousVersion" />
|
|
2904
|
-
</div>
|
|
2905
|
-
</div>
|
|
2906
|
-
<div class="form-group" ng-if="showPlatformHealthOnlyOverride">
|
|
2907
|
-
<div class="col-md-4 sm-label-right">Task Completion</div>
|
|
2908
|
-
<div class="col-md-7">
|
|
2909
|
-
<platform-health-override command="command" platform-health-type="platformHealth"> </platform-health-override>
|
|
2910
|
-
</div>
|
|
2911
|
-
</div>
|
|
2912
|
-
|
|
2913
|
-
<div class="form-group">
|
|
2914
|
-
<div class="col-md-4 sm-label-right">
|
|
2915
|
-
Suppress Version String
|
|
2916
|
-
<help-field key="appengine.serverGroup.suppress-version-string"></help-field>
|
|
2917
|
-
</div>
|
|
2918
|
-
<div class="col-md-7">
|
|
2919
|
-
<input type="checkbox" ng-model="command.suppressVersionString" />
|
|
2920
|
-
</div>
|
|
2921
|
-
</div>
|
|
2922
|
-
</div>
|
|
2923
|
-
`);
|
|
2924
|
-
}]);
|
|
2925
|
-
|
|
2926
|
-
const ConfigFileArtifactList = (props) => {
|
|
2927
|
-
const addConfigArtifact = () => {
|
|
2928
|
-
props.updateConfigArtifacts(props.configArtifacts.concat([{ id: "", account: "" }]));
|
|
2929
|
-
};
|
|
2930
|
-
const deleteConfigArtifact = (index) => {
|
|
2931
|
-
const newConfigArtifacts = [...props.configArtifacts];
|
|
2932
|
-
newConfigArtifacts.splice(index, 1);
|
|
2933
|
-
props.updateConfigArtifacts(newConfigArtifacts);
|
|
2934
|
-
};
|
|
2935
|
-
const onExpectedArtifactEdited = (artifact, index) => {
|
|
2936
|
-
const newConfigArtifacts = [...props.configArtifacts];
|
|
2937
|
-
newConfigArtifacts.splice(index, 1, { ...newConfigArtifacts[index], id: null, artifact });
|
|
2938
|
-
props.updateConfigArtifacts(newConfigArtifacts);
|
|
2939
|
-
};
|
|
2940
|
-
const onExpectedArtifactSelected = (expectedArtifact, index) => {
|
|
2941
|
-
onChangeExpectedArtifactId(expectedArtifact.id, index);
|
|
2942
|
-
};
|
|
2943
|
-
const onChangeExpectedArtifactId = (id, index) => {
|
|
2944
|
-
const newConfigArtifacts = [...props.configArtifacts];
|
|
2945
|
-
newConfigArtifacts.splice(index, 1, { ...newConfigArtifacts[index], id, artifact: null });
|
|
2946
|
-
props.updateConfigArtifacts(newConfigArtifacts);
|
|
2947
|
-
};
|
|
2948
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.configArtifacts.map((a, i) => {
|
|
2949
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
2950
|
-
key: a.id,
|
|
2951
|
-
className: classNames("artifact-configuration-section col-md-12", {
|
|
2952
|
-
"last-entry": props.configArtifacts.length - 1 === i
|
|
2953
|
-
})
|
|
2954
|
-
}, /* @__PURE__ */ React.createElement("div", {
|
|
2955
|
-
className: "col-md-9"
|
|
2956
|
-
}, /* @__PURE__ */ React.createElement(StageArtifactSelector, {
|
|
2957
|
-
artifact: a.artifact,
|
|
2958
|
-
excludedArtifactTypePatterns: [],
|
|
2959
|
-
expectedArtifactId: a.artifact == null ? a.id : null,
|
|
2960
|
-
onArtifactEdited: (artifact) => {
|
|
2961
|
-
onExpectedArtifactEdited(artifact, i);
|
|
2962
|
-
},
|
|
2963
|
-
onExpectedArtifactSelected: (expectedArtifact) => {
|
|
2964
|
-
onExpectedArtifactSelected(expectedArtifact, i);
|
|
2965
|
-
},
|
|
2966
|
-
pipeline: props.pipeline,
|
|
2967
|
-
stage: props.stage
|
|
2968
|
-
})), /* @__PURE__ */ React.createElement("div", {
|
|
2969
|
-
className: "col-md-1"
|
|
2970
|
-
}, /* @__PURE__ */ React.createElement("button", {
|
|
2971
|
-
type: "button",
|
|
2972
|
-
className: "btn btn-sm btn-default",
|
|
2973
|
-
onClick: () => deleteConfigArtifact(i)
|
|
2974
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
2975
|
-
className: "glyphicon glyphicon-trash"
|
|
2976
|
-
}), " Delete")));
|
|
2977
|
-
}), /* @__PURE__ */ React.createElement("div", {
|
|
2978
|
-
className: "col-md-7 col-md-offset-3"
|
|
2979
|
-
}, /* @__PURE__ */ React.createElement("button", {
|
|
2980
|
-
className: "btn btn-block btn-add-trigger add-new",
|
|
2981
|
-
onClick: () => addConfigArtifact()
|
|
2982
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
2983
|
-
className: "glyphicon glyphicon-plus-sign"
|
|
2984
|
-
}), " Add Config Artifact")));
|
|
2985
|
-
};
|
|
2986
|
-
|
|
2987
|
-
const CONFIG_FILE_ARTIFACT_LIST = "spinnaker.appengine.configFileArtifactList.component";
|
|
2988
|
-
module(CONFIG_FILE_ARTIFACT_LIST, []).component("configFileArtifactList", react2angular(withErrorBoundary(ConfigFileArtifactList, "configFileArtifactList"), [
|
|
2989
|
-
"configArtifacts",
|
|
2990
|
-
"pipeline",
|
|
2991
|
-
"stage",
|
|
2992
|
-
"updateConfigArtifacts"
|
|
2993
|
-
]));
|
|
2994
|
-
|
|
2995
|
-
class AppengineServerGroupWriter {
|
|
2996
|
-
startServerGroup(serverGroup, application) {
|
|
2997
|
-
const job = this.buildJob(serverGroup, application, "startAppEngineServerGroup");
|
|
2998
|
-
const command = {
|
|
2999
|
-
job: [job],
|
|
3000
|
-
application,
|
|
3001
|
-
description: `Start Server Group: ${serverGroup.name}`
|
|
3002
|
-
};
|
|
3003
|
-
return TaskExecutor.executeTask(command);
|
|
3004
|
-
}
|
|
3005
|
-
stopServerGroup(serverGroup, application) {
|
|
3006
|
-
const job = this.buildJob(serverGroup, application, "stopAppEngineServerGroup");
|
|
3007
|
-
const command = {
|
|
3008
|
-
job: [job],
|
|
3009
|
-
application,
|
|
3010
|
-
description: `Stop Server Group: ${serverGroup.name}`
|
|
3011
|
-
};
|
|
3012
|
-
return TaskExecutor.executeTask(command);
|
|
3013
|
-
}
|
|
3014
|
-
buildJob(serverGroup, application, type) {
|
|
3015
|
-
return {
|
|
3016
|
-
type,
|
|
3017
|
-
region: serverGroup.region,
|
|
3018
|
-
serverGroupName: serverGroup.name,
|
|
3019
|
-
credentials: serverGroup.account,
|
|
3020
|
-
cloudProvider: "appengine",
|
|
3021
|
-
application: application.name
|
|
3022
|
-
};
|
|
3023
|
-
}
|
|
3024
|
-
}
|
|
3025
|
-
const APPENGINE_SERVER_GROUP_WRITER = "spinnaker.appengine.serverGroup.write.service";
|
|
3026
|
-
module(APPENGINE_SERVER_GROUP_WRITER, []).service("appengineServerGroupWriter", AppengineServerGroupWriter);
|
|
3027
|
-
|
|
3028
|
-
const _AppengineServerGroupDetailsController = class {
|
|
3029
|
-
constructor($state, $scope, $uibModal, serverGroup, app, serverGroupWriter, appengineServerGroupWriter, appengineServerGroupCommandBuilder) {
|
|
3030
|
-
this.$state = $state;
|
|
3031
|
-
this.$scope = $scope;
|
|
3032
|
-
this.$uibModal = $uibModal;
|
|
3033
|
-
this.app = app;
|
|
3034
|
-
this.serverGroupWriter = serverGroupWriter;
|
|
3035
|
-
this.appengineServerGroupWriter = appengineServerGroupWriter;
|
|
3036
|
-
this.appengineServerGroupCommandBuilder = appengineServerGroupCommandBuilder;
|
|
3037
|
-
this.state = { loading: true };
|
|
3038
|
-
this.app.ready().then(() => this.extractServerGroup(serverGroup)).then(() => {
|
|
3039
|
-
if (!this.$scope.$$destroyed) {
|
|
3040
|
-
this.app.getDataSource("serverGroups").onRefresh(this.$scope, () => this.extractServerGroup(serverGroup));
|
|
3041
|
-
}
|
|
3042
|
-
}).catch(() => this.autoClose());
|
|
3043
|
-
}
|
|
3044
|
-
static buildExpectedAllocationsTable(expectedAllocations) {
|
|
3045
|
-
const tableRows = map(expectedAllocations, (allocation, serverGroupName) => {
|
|
3046
|
-
return `
|
|
3047
|
-
<tr>
|
|
3048
|
-
<td>${serverGroupName}</td>
|
|
3049
|
-
<td>${allocation * 100}%</td>
|
|
3050
|
-
</tr>`;
|
|
3051
|
-
}).join("");
|
|
3052
|
-
return `
|
|
3053
|
-
<table class="table table-condensed">
|
|
3054
|
-
<thead>
|
|
3055
|
-
<tr>
|
|
3056
|
-
<th>Server Group</th>
|
|
3057
|
-
<th>Allocation</th>
|
|
3058
|
-
</tr>
|
|
3059
|
-
</thead>
|
|
3060
|
-
<tbody>
|
|
3061
|
-
${tableRows}
|
|
3062
|
-
</tbody>
|
|
3063
|
-
</table>`;
|
|
3064
|
-
}
|
|
3065
|
-
canDisableServerGroup() {
|
|
3066
|
-
if (this.serverGroup) {
|
|
3067
|
-
if (this.serverGroup.disabled) {
|
|
3068
|
-
return false;
|
|
3069
|
-
}
|
|
3070
|
-
const expectedAllocations = this.expectedAllocationsAfterDisableOperation(this.serverGroup, this.app);
|
|
3071
|
-
if (expectedAllocations) {
|
|
3072
|
-
return Object.keys(expectedAllocations).length > 0;
|
|
3073
|
-
} else {
|
|
3074
|
-
return false;
|
|
3075
|
-
}
|
|
3076
|
-
} else {
|
|
3077
|
-
return false;
|
|
3078
|
-
}
|
|
3079
|
-
}
|
|
3080
|
-
canDestroyServerGroup() {
|
|
3081
|
-
if (this.serverGroup) {
|
|
3082
|
-
if (this.serverGroup.disabled) {
|
|
3083
|
-
return true;
|
|
3084
|
-
}
|
|
3085
|
-
const expectedAllocations = this.expectedAllocationsAfterDisableOperation(this.serverGroup, this.app);
|
|
3086
|
-
if (expectedAllocations) {
|
|
3087
|
-
return Object.keys(expectedAllocations).length > 0;
|
|
3088
|
-
} else {
|
|
3089
|
-
return false;
|
|
3090
|
-
}
|
|
3091
|
-
} else {
|
|
3092
|
-
return false;
|
|
3093
|
-
}
|
|
3094
|
-
}
|
|
3095
|
-
destroyServerGroup() {
|
|
3096
|
-
const stateParams = {
|
|
3097
|
-
name: this.serverGroup.name,
|
|
3098
|
-
accountId: this.serverGroup.account,
|
|
3099
|
-
region: this.serverGroup.region
|
|
3100
|
-
};
|
|
3101
|
-
const taskMonitor = {
|
|
3102
|
-
application: this.app,
|
|
3103
|
-
title: "Destroying " + this.serverGroup.name,
|
|
3104
|
-
onTaskComplete: () => {
|
|
3105
|
-
if (this.$state.includes("**.serverGroup", stateParams)) {
|
|
3106
|
-
this.$state.go("^");
|
|
3107
|
-
}
|
|
3108
|
-
}
|
|
3109
|
-
};
|
|
3110
|
-
const submitMethod = (params) => this.serverGroupWriter.destroyServerGroup(this.serverGroup, this.app, params);
|
|
3111
|
-
const confirmationModalParams = {
|
|
3112
|
-
header: "Really destroy " + this.serverGroup.name + "?",
|
|
3113
|
-
buttonText: "Destroy " + this.serverGroup.name,
|
|
3114
|
-
account: this.serverGroup.account,
|
|
3115
|
-
taskMonitorConfig: taskMonitor,
|
|
3116
|
-
submitMethod,
|
|
3117
|
-
askForReason: true,
|
|
3118
|
-
platformHealthOnlyShowOverride: this.app.attributes.platformHealthOnlyShowOverride,
|
|
3119
|
-
platformHealthType: AppengineHealth.PLATFORM,
|
|
3120
|
-
body: this.getBodyTemplate(this.serverGroup, this.app),
|
|
3121
|
-
interestingHealthProviderNames: []
|
|
3122
|
-
};
|
|
3123
|
-
if (this.app.attributes.platformHealthOnlyShowOverride && this.app.attributes.platformHealthOnly) {
|
|
3124
|
-
confirmationModalParams.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
3125
|
-
}
|
|
3126
|
-
ConfirmationModalService.confirm(confirmationModalParams);
|
|
3127
|
-
}
|
|
3128
|
-
enableServerGroup() {
|
|
3129
|
-
const taskMonitor = {
|
|
3130
|
-
application: this.app,
|
|
3131
|
-
title: "Enabling " + this.serverGroup.name
|
|
3132
|
-
};
|
|
3133
|
-
const submitMethod = (params) => this.serverGroupWriter.enableServerGroup(this.serverGroup, this.app, { ...params });
|
|
3134
|
-
const modalBody = `<div class="well well-sm">
|
|
3135
|
-
<p>
|
|
3136
|
-
Enabling <b>${this.serverGroup.name}</b> will set its traffic allocation for
|
|
3137
|
-
<b>${this.serverGroup.loadBalancers[0]}</b> to 100%.
|
|
3138
|
-
</p>
|
|
3139
|
-
<p>
|
|
3140
|
-
If you would like more fine-grained control over your server groups' allocations,
|
|
3141
|
-
edit <b>${this.serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.
|
|
3142
|
-
</p>
|
|
3143
|
-
</div>
|
|
3144
|
-
`;
|
|
3145
|
-
const confirmationModalParams = {
|
|
3146
|
-
header: "Really enable " + this.serverGroup.name + "?",
|
|
3147
|
-
buttonText: "Enable " + this.serverGroup.name,
|
|
3148
|
-
body: modalBody,
|
|
3149
|
-
account: this.serverGroup.account,
|
|
3150
|
-
taskMonitorConfig: taskMonitor,
|
|
3151
|
-
platformHealthOnlyShowOverride: this.app.attributes.platformHealthOnlyShowOverride,
|
|
3152
|
-
platformHealthType: AppengineHealth.PLATFORM,
|
|
3153
|
-
submitMethod,
|
|
3154
|
-
askForReason: true,
|
|
3155
|
-
interestingHealthProviderNames: []
|
|
3156
|
-
};
|
|
3157
|
-
if (this.app.attributes.platformHealthOnlyShowOverride && this.app.attributes.platformHealthOnly) {
|
|
3158
|
-
confirmationModalParams.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
3159
|
-
}
|
|
3160
|
-
ConfirmationModalService.confirm(confirmationModalParams);
|
|
3161
|
-
}
|
|
3162
|
-
disableServerGroup() {
|
|
3163
|
-
const taskMonitor = {
|
|
3164
|
-
application: this.app,
|
|
3165
|
-
title: "Disabling " + this.serverGroup.name
|
|
3166
|
-
};
|
|
3167
|
-
const submitMethod = (params) => this.serverGroupWriter.disableServerGroup(this.serverGroup, this.app.name, params);
|
|
3168
|
-
const expectedAllocations = this.expectedAllocationsAfterDisableOperation(this.serverGroup, this.app);
|
|
3169
|
-
const modalBody = `<div class="well well-sm">
|
|
3170
|
-
<p>
|
|
3171
|
-
For App Engine, a disable operation sets this server group's allocation
|
|
3172
|
-
to 0% and sets the other enabled server groups' allocations to their relative proportions
|
|
3173
|
-
before the disable operation. The approximate allocations that will result from this operation are shown below.
|
|
3174
|
-
</p>
|
|
3175
|
-
<p>
|
|
3176
|
-
If you would like more fine-grained control over your server groups' allocations,
|
|
3177
|
-
edit <b>${this.serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.
|
|
3178
|
-
</p>
|
|
3179
|
-
<div class="row">
|
|
3180
|
-
<div class="col-md-12">
|
|
3181
|
-
${_AppengineServerGroupDetailsController.buildExpectedAllocationsTable(expectedAllocations)}
|
|
3182
|
-
</div>
|
|
3183
|
-
</div>
|
|
3184
|
-
</div>
|
|
3185
|
-
`;
|
|
3186
|
-
const confirmationModalParams = {
|
|
3187
|
-
header: "Really disable " + this.serverGroup.name + "?",
|
|
3188
|
-
buttonText: "Disable " + this.serverGroup.name,
|
|
3189
|
-
body: modalBody,
|
|
3190
|
-
account: this.serverGroup.account,
|
|
3191
|
-
taskMonitorConfig: taskMonitor,
|
|
3192
|
-
platformHealthOnlyShowOverride: this.app.attributes.platformHealthOnlyShowOverride,
|
|
3193
|
-
platformHealthType: AppengineHealth.PLATFORM,
|
|
3194
|
-
submitMethod,
|
|
3195
|
-
askForReason: true,
|
|
3196
|
-
interestingHealthProviderNames: []
|
|
3197
|
-
};
|
|
3198
|
-
if (this.app.attributes.platformHealthOnlyShowOverride && this.app.attributes.platformHealthOnly) {
|
|
3199
|
-
confirmationModalParams.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
3200
|
-
}
|
|
3201
|
-
ConfirmationModalService.confirm(confirmationModalParams);
|
|
3202
|
-
}
|
|
3203
|
-
stopServerGroup() {
|
|
3204
|
-
const taskMonitor = {
|
|
3205
|
-
application: this.app,
|
|
3206
|
-
title: "Stopping " + this.serverGroup.name
|
|
3207
|
-
};
|
|
3208
|
-
const submitMethod = () => this.appengineServerGroupWriter.stopServerGroup(this.serverGroup, this.app);
|
|
3209
|
-
let modalBody;
|
|
3210
|
-
if (!this.serverGroup.disabled) {
|
|
3211
|
-
modalBody = `<div class="alert alert-danger">
|
|
3212
|
-
<p>Stopping this server group will scale it down to zero instances.</p>
|
|
3213
|
-
<p>
|
|
3214
|
-
This server group is currently serving traffic from <b>${this.serverGroup.loadBalancers[0]}</b>.
|
|
3215
|
-
Traffic directed to this server group after it has been stopped will not be handled.
|
|
3216
|
-
</p>
|
|
3217
|
-
</div>`;
|
|
3218
|
-
}
|
|
3219
|
-
const confirmationModalParams = {
|
|
3220
|
-
header: "Really stop " + this.serverGroup.name + "?",
|
|
3221
|
-
buttonText: "Stop " + this.serverGroup.name,
|
|
3222
|
-
account: this.serverGroup.account,
|
|
3223
|
-
body: modalBody,
|
|
3224
|
-
platformHealthOnlyShowOverride: this.app.attributes.platformHealthOnlyShowOverride,
|
|
3225
|
-
platformHealthType: AppengineHealth.PLATFORM,
|
|
3226
|
-
taskMonitorConfig: taskMonitor,
|
|
3227
|
-
submitMethod,
|
|
3228
|
-
askForReason: true
|
|
3229
|
-
};
|
|
3230
|
-
ConfirmationModalService.confirm(confirmationModalParams);
|
|
3231
|
-
}
|
|
3232
|
-
startServerGroup() {
|
|
3233
|
-
const taskMonitor = {
|
|
3234
|
-
application: this.app,
|
|
3235
|
-
title: "Starting " + this.serverGroup.name
|
|
3236
|
-
};
|
|
3237
|
-
const submitMethod = () => this.appengineServerGroupWriter.startServerGroup(this.serverGroup, this.app);
|
|
3238
|
-
const confirmationModalParams = {
|
|
3239
|
-
header: "Really start " + this.serverGroup.name + "?",
|
|
3240
|
-
buttonText: "Start " + this.serverGroup.name,
|
|
3241
|
-
account: this.serverGroup.account,
|
|
3242
|
-
platformHealthOnlyShowOverride: this.app.attributes.platformHealthOnlyShowOverride,
|
|
3243
|
-
platformHealthType: AppengineHealth.PLATFORM,
|
|
3244
|
-
taskMonitorConfig: taskMonitor,
|
|
3245
|
-
submitMethod,
|
|
3246
|
-
askForReason: true
|
|
3247
|
-
};
|
|
3248
|
-
ConfirmationModalService.confirm(confirmationModalParams);
|
|
3249
|
-
}
|
|
3250
|
-
cloneServerGroup() {
|
|
3251
|
-
this.$uibModal.open({
|
|
3252
|
-
templateUrl: "appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",
|
|
3253
|
-
controller: "appengineCloneServerGroupCtrl as ctrl",
|
|
3254
|
-
size: "lg",
|
|
3255
|
-
resolve: {
|
|
3256
|
-
title: () => "Clone " + this.serverGroup.name,
|
|
3257
|
-
application: () => this.app,
|
|
3258
|
-
serverGroup: () => this.serverGroup,
|
|
3259
|
-
serverGroupCommand: () => this.appengineServerGroupCommandBuilder.buildServerGroupCommandFromExisting(this.app, this.serverGroup)
|
|
3260
|
-
}
|
|
3261
|
-
});
|
|
3262
|
-
}
|
|
3263
|
-
canStartServerGroup() {
|
|
3264
|
-
if (this.canStartOrStopServerGroup()) {
|
|
3265
|
-
return this.serverGroup.servingStatus === "STOPPED";
|
|
3266
|
-
} else {
|
|
3267
|
-
return false;
|
|
3268
|
-
}
|
|
3269
|
-
}
|
|
3270
|
-
canStopServerGroup() {
|
|
3271
|
-
if (this.canStartOrStopServerGroup()) {
|
|
3272
|
-
return this.serverGroup.servingStatus === "SERVING";
|
|
3273
|
-
} else {
|
|
3274
|
-
return false;
|
|
3275
|
-
}
|
|
3276
|
-
}
|
|
3277
|
-
canStartOrStopServerGroup() {
|
|
3278
|
-
var _a;
|
|
3279
|
-
const isFlex = this.serverGroup.env === "FLEXIBLE";
|
|
3280
|
-
return isFlex || ["MANUAL", "BASIC"].includes((_a = this.serverGroup.scalingPolicy) == null ? void 0 : _a.type);
|
|
3281
|
-
}
|
|
3282
|
-
getBodyTemplate(serverGroup, app) {
|
|
3283
|
-
let template = "";
|
|
3284
|
-
const params = {};
|
|
3285
|
-
ServerGroupWarningMessageService.addDestroyWarningMessage(app, serverGroup, params);
|
|
3286
|
-
if (params.body) {
|
|
3287
|
-
template += params.body;
|
|
3288
|
-
}
|
|
3289
|
-
if (!serverGroup.disabled) {
|
|
3290
|
-
const expectedAllocations = this.expectedAllocationsAfterDisableOperation(serverGroup, app);
|
|
3291
|
-
template += `
|
|
3292
|
-
<div class="well well-sm">
|
|
3293
|
-
<p>
|
|
3294
|
-
A destroy operation will first disable this server group.
|
|
3295
|
-
</p>
|
|
3296
|
-
<p>
|
|
3297
|
-
For App Engine, a disable operation sets this server group's allocation
|
|
3298
|
-
to 0% and sets the other enabled server groups' allocations to their relative proportions
|
|
3299
|
-
before the disable operation. The approximate allocations that will result from this operation are shown below.
|
|
3300
|
-
</p>
|
|
3301
|
-
<p>
|
|
3302
|
-
If you would like more fine-grained control over your server groups' allocations,
|
|
3303
|
-
edit <b>${serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.
|
|
3304
|
-
</p>
|
|
3305
|
-
<div class="row">
|
|
3306
|
-
<div class="col-md-12">
|
|
3307
|
-
${_AppengineServerGroupDetailsController.buildExpectedAllocationsTable(expectedAllocations)}
|
|
3308
|
-
</div>
|
|
3309
|
-
</div>
|
|
3310
|
-
</div>
|
|
3311
|
-
`;
|
|
3312
|
-
}
|
|
3313
|
-
return template;
|
|
3314
|
-
}
|
|
3315
|
-
expectedAllocationsAfterDisableOperation(serverGroup, app) {
|
|
3316
|
-
const loadBalancer = app.getDataSource("loadBalancers").data.find((toCheck) => {
|
|
3317
|
-
var _a, _b;
|
|
3318
|
-
const allocations = (_b = (_a = toCheck.split) == null ? void 0 : _a.allocations) != null ? _b : {};
|
|
3319
|
-
const enabledServerGroups = Object.keys(allocations);
|
|
3320
|
-
return enabledServerGroups.includes(serverGroup.name);
|
|
3321
|
-
});
|
|
3322
|
-
if (loadBalancer) {
|
|
3323
|
-
let allocations = cloneDeep(loadBalancer.split.allocations);
|
|
3324
|
-
delete allocations[serverGroup.name];
|
|
3325
|
-
const denominator = reduce(allocations, (partialSum, allocation) => partialSum + allocation, 0);
|
|
3326
|
-
const precision = loadBalancer.split.shardBy === "COOKIE" ? 1e3 : 100;
|
|
3327
|
-
allocations = mapValues(allocations, (allocation) => Math.round(allocation / denominator * precision) / precision);
|
|
3328
|
-
return allocations;
|
|
3329
|
-
} else {
|
|
3330
|
-
return null;
|
|
3331
|
-
}
|
|
3332
|
-
}
|
|
3333
|
-
autoClose() {
|
|
3334
|
-
if (this.$scope.$$destroyed) {
|
|
3335
|
-
return;
|
|
3336
|
-
} else {
|
|
3337
|
-
this.$state.params.allowModalToStayOpen = true;
|
|
3338
|
-
this.$state.go("^", null, { location: "replace" });
|
|
3339
|
-
}
|
|
3340
|
-
}
|
|
3341
|
-
extractServerGroup(fromParams) {
|
|
3342
|
-
return ServerGroupReader.getServerGroup(this.app.name, fromParams.accountId, fromParams.region, fromParams.name).then((serverGroupDetails) => {
|
|
3343
|
-
let fromApp = this.app.getDataSource("serverGroups").data.find((toCheck) => {
|
|
3344
|
-
return toCheck.name === fromParams.name && toCheck.account === fromParams.accountId && toCheck.region === fromParams.region;
|
|
3345
|
-
});
|
|
3346
|
-
if (!fromApp) {
|
|
3347
|
-
this.app.getDataSource("loadBalancers").data.some((loadBalancer) => {
|
|
3348
|
-
if (loadBalancer.account === fromParams.accountId) {
|
|
3349
|
-
return loadBalancer.serverGroups.some((toCheck) => {
|
|
3350
|
-
let result = false;
|
|
3351
|
-
if (toCheck.name === fromParams.name) {
|
|
3352
|
-
fromApp = toCheck;
|
|
3353
|
-
result = true;
|
|
3354
|
-
}
|
|
3355
|
-
return result;
|
|
3356
|
-
});
|
|
3357
|
-
} else {
|
|
3358
|
-
return false;
|
|
3359
|
-
}
|
|
3360
|
-
});
|
|
3361
|
-
}
|
|
3362
|
-
this.serverGroup = { ...serverGroupDetails, ...fromApp };
|
|
3363
|
-
this.state.loading = false;
|
|
3364
|
-
});
|
|
3365
|
-
}
|
|
3366
|
-
};
|
|
3367
|
-
let AppengineServerGroupDetailsController = _AppengineServerGroupDetailsController;
|
|
3368
|
-
AppengineServerGroupDetailsController.$inject = [
|
|
3369
|
-
"$state",
|
|
3370
|
-
"$scope",
|
|
3371
|
-
"$uibModal",
|
|
3372
|
-
"serverGroup",
|
|
3373
|
-
"app",
|
|
3374
|
-
"serverGroupWriter",
|
|
3375
|
-
"appengineServerGroupWriter",
|
|
3376
|
-
"appengineServerGroupCommandBuilder"
|
|
3377
|
-
];
|
|
3378
|
-
const APPENGINE_SERVER_GROUP_DETAILS_CTRL = "spinnaker.appengine.serverGroup.details.controller";
|
|
3379
|
-
module(APPENGINE_SERVER_GROUP_DETAILS_CTRL, [APPENGINE_SERVER_GROUP_WRITER, SERVER_GROUP_WRITER]).controller("appengineServerGroupDetailsCtrl", AppengineServerGroupDetailsController);
|
|
3380
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
3381
|
-
templateCache.put("appengine/src/serverGroup/configure/wizard/serverGroupWizard.html", `<form name="form" class="form-horizontal appengine-server-group-wizard" novalidate>
|
|
3382
|
-
<div ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">
|
|
3383
|
-
<loading-spinner size="'small'"></loading-spinner>
|
|
3384
|
-
</div>
|
|
3385
|
-
<div>
|
|
3386
|
-
<v2-modal-wizard
|
|
3387
|
-
ng-if="!ctrl.state.loading"
|
|
3388
|
-
heading="Create Server Group"
|
|
3389
|
-
task-monitor="ctrl.taskMonitor"
|
|
3390
|
-
dismiss="$dismiss()"
|
|
3391
|
-
>
|
|
3392
|
-
<v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">
|
|
3393
|
-
<ng-include src="ctrl.pages.basicSettings"></ng-include>
|
|
3394
|
-
</v2-wizard-page>
|
|
3395
|
-
<v2-wizard-page key="load-balancer" label="Config Files" mark-complete-on-view="false">
|
|
3396
|
-
<appengine-config-file-configurer command="command"></appengine-config-file-configurer>
|
|
3397
|
-
</v2-wizard-page>
|
|
3398
|
-
<v2-wizard-page key="load-balancer" label="Load Balancer" done="true">
|
|
3399
|
-
<appengine-load-balancer-message show-create-message="false"></appengine-load-balancer-message>
|
|
3400
|
-
</v2-wizard-page>
|
|
3401
|
-
<v2-wizard-page key="advanced-settings" label="Advanced Settings" done="true">
|
|
3402
|
-
<ng-include src="ctrl.pages.advancedSettings"></ng-include>
|
|
3403
|
-
</v2-wizard-page>
|
|
3404
|
-
</v2-modal-wizard>
|
|
3405
|
-
<div class="modal-footer" ng-if="!state.loading">
|
|
3406
|
-
<button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">
|
|
3407
|
-
Cancel
|
|
3408
|
-
</button>
|
|
3409
|
-
<submit-button
|
|
3410
|
-
ng-if="form.$valid"
|
|
3411
|
-
is-disabled="form.$invalid || ctrl.taskMonitor.submitting"
|
|
3412
|
-
label="command.viewState.submitButtonLabel"
|
|
3413
|
-
submitting="ctrl.taskMonitor.submitting"
|
|
3414
|
-
on-click="ctrl.submit()"
|
|
3415
|
-
is-new="true"
|
|
3416
|
-
></submit-button>
|
|
3417
|
-
</div>
|
|
3418
|
-
</div>
|
|
3419
|
-
</form>
|
|
3420
|
-
`);
|
|
3421
|
-
}]);
|
|
3422
|
-
|
|
3423
|
-
class AppengineDeployDescription {
|
|
3424
|
-
constructor(command) {
|
|
3425
|
-
this.cloudProvider = "appengine";
|
|
3426
|
-
this.provider = "appengine";
|
|
3427
|
-
this.credentials = command.credentials;
|
|
3428
|
-
this.account = command.credentials;
|
|
3429
|
-
this.application = command.application;
|
|
3430
|
-
this.stack = command.stack;
|
|
3431
|
-
this.freeFormDetails = command.freeFormDetails;
|
|
3432
|
-
this.repositoryUrl = command.repositoryUrl;
|
|
3433
|
-
this.branch = command.branch;
|
|
3434
|
-
this.configFilepaths = command.configFilepaths;
|
|
3435
|
-
this.promote = command.promote;
|
|
3436
|
-
this.stopPreviousVersion = command.stopPreviousVersion;
|
|
3437
|
-
this.type = command.type;
|
|
3438
|
-
this.region = command.region;
|
|
3439
|
-
this.strategy = command.strategy;
|
|
3440
|
-
this.strategyApplication = command.strategyApplication;
|
|
3441
|
-
this.strategyPipeline = command.strategyPipeline;
|
|
3442
|
-
this.fromTrigger = command.fromTrigger;
|
|
3443
|
-
this.trigger = command.trigger;
|
|
3444
|
-
this.gitCredentialType = command.gitCredentialType;
|
|
3445
|
-
this.configFiles = command.configFiles;
|
|
3446
|
-
this.configArtifacts = command.configArtifacts.filter((a) => !!a.id || !!a.artifact);
|
|
3447
|
-
this.applicationDirectoryRoot = command.applicationDirectoryRoot;
|
|
3448
|
-
this.interestingHealthProviderNames = command.interestingHealthProviderNames || [];
|
|
3449
|
-
this.expectedArtifactId = command.expectedArtifactId;
|
|
3450
|
-
this.expectedArtifact = command.expectedArtifact;
|
|
3451
|
-
this.fromArtifact = command.fromArtifact;
|
|
3452
|
-
this.sourceType = command.sourceType;
|
|
3453
|
-
this.storageAccountName = command.storageAccountName;
|
|
3454
|
-
this.containerImageUrl = command.containerImageUrl;
|
|
3455
|
-
this.suppressVersionString = command.suppressVersionString;
|
|
3456
|
-
}
|
|
3457
|
-
}
|
|
3458
|
-
class AppengineServerGroupTransformer {
|
|
3459
|
-
constructor($q) {
|
|
3460
|
-
this.$q = $q;
|
|
3461
|
-
}
|
|
3462
|
-
normalizeServerGroup(serverGroup) {
|
|
3463
|
-
return this.$q.resolve(serverGroup);
|
|
3464
|
-
}
|
|
3465
|
-
convertServerGroupCommandToDeployConfiguration(command) {
|
|
3466
|
-
return new AppengineDeployDescription(command);
|
|
3467
|
-
}
|
|
3468
|
-
}
|
|
3469
|
-
AppengineServerGroupTransformer.$inject = ["$q"];
|
|
3470
|
-
const APPENGINE_SERVER_GROUP_TRANSFORMER = "spinnaker.appengine.serverGroup.transformer.service";
|
|
3471
|
-
module(APPENGINE_SERVER_GROUP_TRANSFORMER, []).service("appengineServerGroupTransformer", AppengineServerGroupTransformer);
|
|
3472
|
-
|
|
3473
|
-
class AppengineApplicationNameValidator {
|
|
3474
|
-
validate(name = "") {
|
|
3475
|
-
const warnings = [];
|
|
3476
|
-
const errors = [];
|
|
3477
|
-
if (name.length) {
|
|
3478
|
-
this.validateSpecialCharacters(name, errors);
|
|
3479
|
-
this.validateLength(name, warnings, errors);
|
|
3480
|
-
}
|
|
3481
|
-
return { warnings, errors };
|
|
3482
|
-
}
|
|
3483
|
-
validateSpecialCharacters(name, errors) {
|
|
3484
|
-
const pattern = /^[a-z0-9]*$/g;
|
|
3485
|
-
if (!pattern.test(name)) {
|
|
3486
|
-
errors.push("Only numbers and lowercase letters are allowed.");
|
|
3487
|
-
}
|
|
3488
|
-
}
|
|
3489
|
-
validateLength(name, warnings, errors) {
|
|
3490
|
-
if (name.length > 58) {
|
|
3491
|
-
errors.push("The maximum length for an App Engine application name is 63 characters.");
|
|
3492
|
-
return;
|
|
3493
|
-
}
|
|
3494
|
-
if (name.length > 48) {
|
|
3495
|
-
if (name.length >= 56) {
|
|
3496
|
-
warnings.push("You will not be able to include a stack or detail field for clusters.");
|
|
3497
|
-
} else {
|
|
3498
|
-
const remaining = 56 - name.length;
|
|
3499
|
-
warnings.push(`If you plan to include a stack or detail field for clusters, you will only have
|
|
3500
|
-
${remaining} character${remaining > 1 ? "s" : ""} to do so.`);
|
|
3501
|
-
}
|
|
3502
|
-
}
|
|
3503
|
-
}
|
|
3504
|
-
}
|
|
3505
|
-
ApplicationNameValidator.registerValidator("appengine", new AppengineApplicationNameValidator());
|
|
3506
|
-
|
|
3507
|
-
var css_248z = ".cloud-provider-logo .icon-appengine {\n -webkit-mask-image: url(\"data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 18.1.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22 width%3D%22128px%22%09 height%3D%22128px%22 viewBox%3D%2215 15 90 90%22 xml%3Aspace%3D%22preserve%22%3E%3Cfilter id%3D%22invert%22%3E %3CfeColorMatrix in%3D%22SourceGraphic%22 type%3D%22matrix%22 values%3D%22-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0%22%2F%3E%3C%2Ffilter%3E%3Cg id%3D%22art%22 filter%3D%22url(%23invert)%22%3E%09%3Cg%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9988%2C40.9577c-12.7278%2C0-23.0452%2C10.3167-23.0452%2C23.0464%09%09%09c0%2C12.726%2C10.3174%2C23.0463%2C23.0452%2C23.0463c12.7273%2C0%2C23.0446-10.3203%2C23.0446-23.0463%09%09%09C87.0434%2C51.2744%2C76.7261%2C40.9577%2C63.9988%2C40.9577 M63.9988%2C81.4887c-9.6584%2C0-17.4876-7.8286-17.4876-17.4846%09%09%09c0-9.6584%2C7.8291-17.4877%2C17.4876-17.4877c9.6578%2C0%2C17.4858%2C7.8292%2C17.4858%2C17.4877%09%09%09C81.4846%2C73.6601%2C73.6566%2C81.4887%2C63.9988%2C81.4887%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M100.1431%2C61.274l-9.6079-3.0195c0.3997%2C1.8539%2C0.615%2C3.7759%2C0.615%2C5.7478%09%09%09c0%2C1.3791-0.1047%2C2.7337-0.3021%2C4.0569h9.295c0.8587-0.2515%2C1.4327-0.7846%2C1.4327-1.5765v-3.6345%09%09%09C101.5759%2C62.0575%2C101.0018%2C61.5094%2C100.1431%2C61.274%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9923%2C36.8497c1.9303%2C0%2C3.8119%2C0.2046%2C5.6285%2C0.5884l-3.4443-9.5729%09%09%09c-0.2515-0.8606-0.7852-1.434-1.5765-1.434h-1.4392c-0.7913%2C0-1.3407%2C0.5734-1.5761%2C1.434l-2.9924%2C9.5236%09%09%09C60.3372%2C37.0368%2C62.1425%2C36.8497%2C63.9923%2C36.8497%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M36.8468%2C64.0023c0-1.9719%2C0.2154-3.8932%2C0.6145-5.7478l-9.6073%2C3.0195%09%09%09c-0.8587%2C0.2354-1.4322%2C0.7835-1.4322%2C1.5754v3.6344c0%2C0.7907%2C0.5735%2C1.3238%2C1.4322%2C1.5753h9.2955%09%09%09C36.9515%2C66.7366%2C36.8468%2C65.3814%2C36.8468%2C64.0023%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M72.2276%2C56.4819l-2.1802%2C2.1794v-0.0048c-1.5651-1.5639-3.6549-2.4274-5.8687-2.4274%09%09%09c-2.2192%2C0-4.303%2C0.8659-5.8704%2C2.4322c-3.2362%2C3.2355-3.2362%2C8.5037%2C0%2C11.7399l-2.1802%2C2.1783%09%09%09c2.0627%2C2.0609%2C4.9096%2C3.3383%2C8.0483%2C3.3383c6.2803%2C0%2C11.3896-5.1081%2C11.3896-11.3895%09%09%09C75.566%2C61.3883%2C74.289%2C58.5421%2C72.2276%2C56.4819 M67.7284%2C67.7342c-1.0278%2C1.0302-2.3799%2C1.5459-3.7284%2C1.5459%09%09%09c-1.3526%2C0-2.7012-0.5156-3.7313-1.5459c-2.0616-2.0615-2.0616-5.4018%2C0-7.4639c1.0301-1.0314%2C2.3787-1.544%2C3.7313-1.544%09%09%09c1.3485%2C0%2C2.7006%2C0.5127%2C3.7284%2C1.544C69.79%2C62.3312%2C69.79%2C65.6727%2C67.7284%2C67.7342%22%2F%3E%09%3C%2Fg%3E%3C%2Fg%3E%3Cg id%3D%22Guides%22 style%3D%22display%3Anone%3B%22%3E%3C%2Fg%3E%3C%2Fsvg%3E\");\n mask-image: url(\"data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 18.1.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22 width%3D%22128px%22%09 height%3D%22128px%22 viewBox%3D%2215 15 90 90%22 xml%3Aspace%3D%22preserve%22%3E%3Cfilter id%3D%22invert%22%3E %3CfeColorMatrix in%3D%22SourceGraphic%22 type%3D%22matrix%22 values%3D%22-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0%22%2F%3E%3C%2Ffilter%3E%3Cg id%3D%22art%22 filter%3D%22url(%23invert)%22%3E%09%3Cg%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9988%2C40.9577c-12.7278%2C0-23.0452%2C10.3167-23.0452%2C23.0464%09%09%09c0%2C12.726%2C10.3174%2C23.0463%2C23.0452%2C23.0463c12.7273%2C0%2C23.0446-10.3203%2C23.0446-23.0463%09%09%09C87.0434%2C51.2744%2C76.7261%2C40.9577%2C63.9988%2C40.9577 M63.9988%2C81.4887c-9.6584%2C0-17.4876-7.8286-17.4876-17.4846%09%09%09c0-9.6584%2C7.8291-17.4877%2C17.4876-17.4877c9.6578%2C0%2C17.4858%2C7.8292%2C17.4858%2C17.4877%09%09%09C81.4846%2C73.6601%2C73.6566%2C81.4887%2C63.9988%2C81.4887%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M100.1431%2C61.274l-9.6079-3.0195c0.3997%2C1.8539%2C0.615%2C3.7759%2C0.615%2C5.7478%09%09%09c0%2C1.3791-0.1047%2C2.7337-0.3021%2C4.0569h9.295c0.8587-0.2515%2C1.4327-0.7846%2C1.4327-1.5765v-3.6345%09%09%09C101.5759%2C62.0575%2C101.0018%2C61.5094%2C100.1431%2C61.274%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9923%2C36.8497c1.9303%2C0%2C3.8119%2C0.2046%2C5.6285%2C0.5884l-3.4443-9.5729%09%09%09c-0.2515-0.8606-0.7852-1.434-1.5765-1.434h-1.4392c-0.7913%2C0-1.3407%2C0.5734-1.5761%2C1.434l-2.9924%2C9.5236%09%09%09C60.3372%2C37.0368%2C62.1425%2C36.8497%2C63.9923%2C36.8497%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M36.8468%2C64.0023c0-1.9719%2C0.2154-3.8932%2C0.6145-5.7478l-9.6073%2C3.0195%09%09%09c-0.8587%2C0.2354-1.4322%2C0.7835-1.4322%2C1.5754v3.6344c0%2C0.7907%2C0.5735%2C1.3238%2C1.4322%2C1.5753h9.2955%09%09%09C36.9515%2C66.7366%2C36.8468%2C65.3814%2C36.8468%2C64.0023%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M72.2276%2C56.4819l-2.1802%2C2.1794v-0.0048c-1.5651-1.5639-3.6549-2.4274-5.8687-2.4274%09%09%09c-2.2192%2C0-4.303%2C0.8659-5.8704%2C2.4322c-3.2362%2C3.2355-3.2362%2C8.5037%2C0%2C11.7399l-2.1802%2C2.1783%09%09%09c2.0627%2C2.0609%2C4.9096%2C3.3383%2C8.0483%2C3.3383c6.2803%2C0%2C11.3896-5.1081%2C11.3896-11.3895%09%09%09C75.566%2C61.3883%2C74.289%2C58.5421%2C72.2276%2C56.4819 M67.7284%2C67.7342c-1.0278%2C1.0302-2.3799%2C1.5459-3.7284%2C1.5459%09%09%09c-1.3526%2C0-2.7012-0.5156-3.7313-1.5459c-2.0616-2.0615-2.0616-5.4018%2C0-7.4639c1.0301-1.0314%2C2.3787-1.544%2C3.7313-1.544%09%09%09c1.3485%2C0%2C2.7006%2C0.5127%2C3.7284%2C1.544C69.79%2C62.3312%2C69.79%2C65.6727%2C67.7284%2C67.7342%22%2F%3E%09%3C%2Fg%3E%3C%2Fg%3E%3Cg id%3D%22Guides%22 style%3D%22display%3Anone%3B%22%3E%3C%2Fg%3E%3C%2Fsvg%3E\");\n background-color: #4285f4;\n}\n";
|
|
3508
|
-
styleInject(css_248z);
|
|
3509
|
-
|
|
3510
|
-
const APPENGINE_MODULE = "spinnaker.appengine";
|
|
3511
|
-
module(APPENGINE_MODULE, [
|
|
3512
|
-
APPENGINE_CLONE_SERVER_GROUP_CTRL,
|
|
3513
|
-
APPENGINE_COMPONENT_URL_DETAILS,
|
|
3514
|
-
APPENGINE_CONDITIONAL_DESCRIPTION_LIST_ITEM,
|
|
3515
|
-
APPENGINE_INSTANCE_DETAILS_CTRL,
|
|
3516
|
-
APPENGINE_LOAD_BALANCER_CREATE_MESSAGE,
|
|
3517
|
-
APPENGINE_LOAD_BALANCER_MODULE,
|
|
3518
|
-
APPENGINE_PIPELINE_MODULE,
|
|
3519
|
-
APPENGINE_SERVER_GROUP_BASIC_SETTINGS_CTRL,
|
|
3520
|
-
APPENGINE_SERVER_GROUP_COMMAND_BUILDER,
|
|
3521
|
-
APPENGINE_SERVER_GROUP_DETAILS_CTRL,
|
|
3522
|
-
APPENGINE_SERVER_GROUP_TRANSFORMER,
|
|
3523
|
-
APPENGINE_SERVER_GROUP_WRITER,
|
|
3524
|
-
CONFIG_FILE_ARTIFACT_LIST
|
|
3525
|
-
]).config(() => {
|
|
3526
|
-
CloudProviderRegistry.registerProvider("appengine", {
|
|
3527
|
-
name: "App Engine",
|
|
3528
|
-
instance: {
|
|
3529
|
-
detailsTemplateUrl: "appengine/src/instance/details/details.html",
|
|
3530
|
-
detailsController: "appengineInstanceDetailsCtrl"
|
|
3531
|
-
},
|
|
3532
|
-
serverGroup: {
|
|
3533
|
-
transformer: "appengineServerGroupTransformer",
|
|
3534
|
-
detailsController: "appengineServerGroupDetailsCtrl",
|
|
3535
|
-
detailsTemplateUrl: "appengine/src/serverGroup/details/details.html",
|
|
3536
|
-
commandBuilder: "appengineServerGroupCommandBuilder",
|
|
3537
|
-
cloneServerGroupController: "appengineCloneServerGroupCtrl",
|
|
3538
|
-
cloneServerGroupTemplateUrl: "appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",
|
|
3539
|
-
skipUpstreamStageCheck: true
|
|
3540
|
-
},
|
|
3541
|
-
loadBalancer: {
|
|
3542
|
-
transformer: "appengineLoadBalancerTransformer",
|
|
3543
|
-
createLoadBalancerTemplateUrl: "appengine/src/loadBalancer/configure/wizard/wizard.html",
|
|
3544
|
-
createLoadBalancerController: "appengineLoadBalancerWizardCtrl",
|
|
3545
|
-
detailsTemplateUrl: "appengine/src/loadBalancer/details/details.html",
|
|
3546
|
-
detailsController: "appengineLoadBalancerDetailsCtrl"
|
|
3547
|
-
},
|
|
3548
|
-
logo: {
|
|
3549
|
-
path: logo
|
|
3550
|
-
}
|
|
3551
|
-
});
|
|
3552
|
-
});
|
|
3553
|
-
DeploymentStrategyRegistry.registerProvider("appengine", ["custom"]);
|
|
3554
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
3555
|
-
templateCache.put("appengine/src/instance/details/details.html", `<div class="details-panel">
|
|
3556
|
-
<div class="header">
|
|
3557
|
-
<instance-details-header
|
|
3558
|
-
health-state="ctrl.instance.healthState"
|
|
3559
|
-
instance-id="ctrl.instance ? ctrl.instance.name : ctrl.instanceIdNotFound"
|
|
3560
|
-
loading="ctrl.state.loading"
|
|
3561
|
-
standalone="false"
|
|
3562
|
-
></instance-details-header>
|
|
3563
|
-
<div ng-if="!ctrl.state.loading">
|
|
3564
|
-
<div class="actions">
|
|
3565
|
-
<div class="dropdown" uib-dropdown dropdown-append-to-body>
|
|
3566
|
-
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>
|
|
3567
|
-
Instance Actions <span class="caret"></span>
|
|
3568
|
-
</button>
|
|
3569
|
-
<ul class="dropdown-menu" uib-dropdown-menu role="menu">
|
|
3570
|
-
<li><a href ng-click="ctrl.terminateInstance()">Terminate</a></li>
|
|
3571
|
-
</ul>
|
|
3572
|
-
</div>
|
|
3573
|
-
</div>
|
|
3574
|
-
</div>
|
|
3575
|
-
</div>
|
|
3576
|
-
<div class="content" ng-if="!ctrl.state.loading && ctrl.instance">
|
|
3577
|
-
<collapsible-section heading="Instance Information" expanded="true">
|
|
3578
|
-
<dl class="dl-horizontal dl-narrow">
|
|
3579
|
-
<dt>Launched</dt>
|
|
3580
|
-
<dd ng-if="ctrl.instance.launchTime">{{ctrl.instance.launchTime | timestamp}}</dd>
|
|
3581
|
-
<dt>In</dt>
|
|
3582
|
-
<dd><account-tag account="ctrl.instance.account" pad="right"></account-tag>{{}}</dd>
|
|
3583
|
-
<dt ng-if="ctrl.instance.serverGroup">Server Group</dt>
|
|
3584
|
-
<dd ng-if="ctrl.instance.serverGroup">
|
|
3585
|
-
<a
|
|
3586
|
-
ui-sref="^.serverGroup({region: ctrl.instance.region,
|
|
3587
|
-
accountId: ctrl.instance.account,
|
|
3588
|
-
serverGroup: ctrl.instance.serverGroup,
|
|
3589
|
-
provider: 'appengine'})"
|
|
3590
|
-
>{{ctrl.instance.serverGroup}}</a
|
|
3591
|
-
>
|
|
3592
|
-
</dd>
|
|
3593
|
-
<dt>Region</dt>
|
|
3594
|
-
<dd>{{ctrl.instance.region}}</dd>
|
|
3595
|
-
<appengine-conditional-dt-dd
|
|
3596
|
-
component="ctrl.instance"
|
|
3597
|
-
key="vmZoneName"
|
|
3598
|
-
label="Zone"
|
|
3599
|
-
></appengine-conditional-dt-dd>
|
|
3600
|
-
</dl>
|
|
3601
|
-
</collapsible-section>
|
|
3602
|
-
<collapsible-section heading="Status" expanded="true">
|
|
3603
|
-
<dl>
|
|
3604
|
-
<dt>Load Balancer</dt>
|
|
3605
|
-
<dd>
|
|
3606
|
-
<span
|
|
3607
|
-
class="pull-left"
|
|
3608
|
-
uib-tooltip="{{ctrl.instance.healthState === 'Up' ? ctrl.upToolTip : ctrl.outOfServiceToolTip}}"
|
|
3609
|
-
tooltip-placement="right"
|
|
3610
|
-
>
|
|
3611
|
-
<span class="glyphicon glyphicon-{{ctrl.instance.healthState}}-triangle"></span>
|
|
3612
|
-
{{ctrl.instance.loadBalancers[0]}}
|
|
3613
|
-
</span>
|
|
3614
|
-
</dd>
|
|
3615
|
-
</dl>
|
|
3616
|
-
</collapsible-section>
|
|
3617
|
-
<collapsible-section heading="Metrics" expanded="true">
|
|
3618
|
-
<dl>
|
|
3619
|
-
<appengine-conditional-dt-dd component="ctrl.instance" key="instanceStatus" label="Availability">
|
|
3620
|
-
<key-label><help-field key="appengine.instance.availability"></help-field></key-label>
|
|
3621
|
-
</appengine-conditional-dt-dd>
|
|
3622
|
-
<appengine-conditional-dt-dd component="ctrl.instance" key="averageLatency">
|
|
3623
|
-
<key-label><help-field key="appengine.instance.averageLatency"></help-field></key-label>
|
|
3624
|
-
<value-label>ms</value-label>
|
|
3625
|
-
</appengine-conditional-dt-dd>
|
|
3626
|
-
<appengine-conditional-dt-dd component="ctrl.instance" key="errors">
|
|
3627
|
-
<key-label><help-field key="appengine.instance.errors"></help-field></key-label>
|
|
3628
|
-
</appengine-conditional-dt-dd>
|
|
3629
|
-
<appengine-conditional-dt-dd component="ctrl.instance" key="qps" label="QPS">
|
|
3630
|
-
<key-label><help-field key="appengine.instance.qps"></help-field></key-label>
|
|
3631
|
-
</appengine-conditional-dt-dd>
|
|
3632
|
-
<appengine-conditional-dt-dd component="ctrl.instance" key="requests">
|
|
3633
|
-
<key-label><help-field key="appengine.instance.requests"></help-field></key-label>
|
|
3634
|
-
</appengine-conditional-dt-dd>
|
|
3635
|
-
<appengine-conditional-dt-dd component="ctrl.instance" key="vmStatus" label="VM Status">
|
|
3636
|
-
<key-label><help-field key="appengine.instance.vmStatus"></help-field></key-label>
|
|
3637
|
-
</appengine-conditional-dt-dd>
|
|
3638
|
-
<appengine-conditional-dt-dd
|
|
3639
|
-
component="ctrl.instance"
|
|
3640
|
-
key="vmDebugEnabled"
|
|
3641
|
-
label="Debug Enabled"
|
|
3642
|
-
></appengine-conditional-dt-dd>
|
|
3643
|
-
</dl>
|
|
3644
|
-
</collapsible-section>
|
|
3645
|
-
</div>
|
|
3646
|
-
<div class="content" ng-if="!ctrl.state.loading && !ctrl.instance">
|
|
3647
|
-
<div class="content-section">
|
|
3648
|
-
<div class="content-body text-center">
|
|
3649
|
-
<h3>Instance not found.</h3>
|
|
3650
|
-
</div>
|
|
3651
|
-
</div>
|
|
3652
|
-
</div>
|
|
3653
|
-
</div>
|
|
3654
|
-
`);
|
|
3655
|
-
}]);
|
|
3656
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
3657
|
-
templateCache.put("appengine/src/serverGroup/details/details.html", `<div class="details-panel" ng-class="{ disabled: ctrl.serverGroup.isDisabled || ctrl.serverGroup.disabled}">
|
|
3658
|
-
<div class="header" ng-if="ctrl.state.loading">
|
|
3659
|
-
<div class="close-button">
|
|
3660
|
-
<a class="btn btn-link" ui-sref="^">
|
|
3661
|
-
<span class="glyphicon glyphicon-remove"></span>
|
|
3662
|
-
</a>
|
|
3663
|
-
</div>
|
|
3664
|
-
<div class="horizontal center middle">
|
|
3665
|
-
<loading-spinner size="'small'"></loading-spinner>
|
|
3666
|
-
</div>
|
|
3667
|
-
</div>
|
|
3668
|
-
|
|
3669
|
-
<div class="header" ng-if="!ctrl.state.loading">
|
|
3670
|
-
<div class="close-button">
|
|
3671
|
-
<a class="btn btn-link" ui-sref="^">
|
|
3672
|
-
<span class="glyphicon glyphicon-remove"></span>
|
|
3673
|
-
</a>
|
|
3674
|
-
</div>
|
|
3675
|
-
<div class="header-text horizontal middle">
|
|
3676
|
-
<cloud-provider-logo provider="ctrl.serverGroup.type" height="'36px'" width="'36px'"></cloud-provider-logo>
|
|
3677
|
-
<h3 class="horizontal middle space-between flex-1" select-on-dbl-click>{{ctrl.serverGroup.name}}</h3>
|
|
3678
|
-
</div>
|
|
3679
|
-
<div class="actions">
|
|
3680
|
-
<div class="dropdown" uib-dropdown dropdown-append-to-body>
|
|
3681
|
-
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>
|
|
3682
|
-
Server Group Actions <span class="caret"></span>
|
|
3683
|
-
</button>
|
|
3684
|
-
<ul class="dropdown-menu" uib-dropdown-menu role="menu">
|
|
3685
|
-
<li ng-if="ctrl.canStopServerGroup()">
|
|
3686
|
-
<a href ng-click="ctrl.stopServerGroup()"> Stop </a>
|
|
3687
|
-
</li>
|
|
3688
|
-
<li ng-if="ctrl.canStartServerGroup()">
|
|
3689
|
-
<a href ng-click="ctrl.startServerGroup()"> Start </a>
|
|
3690
|
-
</li>
|
|
3691
|
-
<li ng-if="ctrl.serverGroup.disabled">
|
|
3692
|
-
<a href ng-click="ctrl.enableServerGroup()"> Enable </a>
|
|
3693
|
-
</li>
|
|
3694
|
-
<li ng-if="!ctrl.serverGroup.disabled && ctrl.canDisableServerGroup()">
|
|
3695
|
-
<a href ng-click="ctrl.disableServerGroup()"> Disable </a>
|
|
3696
|
-
</li>
|
|
3697
|
-
<li
|
|
3698
|
-
ng-if="!ctrl.serverGroup.disabled && !ctrl.canDisableServerGroup()"
|
|
3699
|
-
uib-tooltip="You cannot disable a server group if it is the
|
|
3700
|
-
only server group receiving traffic from a load balancer."
|
|
3701
|
-
class="disabled"
|
|
3702
|
-
>
|
|
3703
|
-
<a href> Disable </a>
|
|
3704
|
-
</li>
|
|
3705
|
-
<li ng-if="ctrl.canDestroyServerGroup()">
|
|
3706
|
-
<a href ng-click="ctrl.destroyServerGroup()">Destroy</a>
|
|
3707
|
-
</li>
|
|
3708
|
-
<li
|
|
3709
|
-
ng-if="!ctrl.canDestroyServerGroup()"
|
|
3710
|
-
uib-tooltip="You cannot destroy a server group if it is the only server group
|
|
3711
|
-
receiving traffic from a load balancer. You may be able to delete
|
|
3712
|
-
this server group's load balancer."
|
|
3713
|
-
class="disabled"
|
|
3714
|
-
>
|
|
3715
|
-
<a href>Destroy</a>
|
|
3716
|
-
</li>
|
|
3717
|
-
<li
|
|
3718
|
-
uib-tooltip="It is not possible to clone an App Engine server group's full
|
|
3719
|
-
launch configuration. However, clicking this button will allow
|
|
3720
|
-
you to deploy into this server group's cluster."
|
|
3721
|
-
>
|
|
3722
|
-
<a href ng-click="ctrl.cloneServerGroup()">Clone</a>
|
|
3723
|
-
</li>
|
|
3724
|
-
</ul>
|
|
3725
|
-
</div>
|
|
3726
|
-
</div>
|
|
3727
|
-
</div>
|
|
3728
|
-
|
|
3729
|
-
<div class="content" ng-if="!ctrl.state.loading">
|
|
3730
|
-
<div class="band band-info" ng-if="ctrl.serverGroup.isDisabled || ctrl.serverGroup.disabled">Disabled</div>
|
|
3731
|
-
<server-group-running-tasks-details
|
|
3732
|
-
server-group="ctrl.serverGroup"
|
|
3733
|
-
application="ctrl.app"
|
|
3734
|
-
></server-group-running-tasks-details>
|
|
3735
|
-
<collapsible-section heading="Server Group Information" expanded="true">
|
|
3736
|
-
<dl class="dl-horizontal dl-narrow">
|
|
3737
|
-
<dt>Created</dt>
|
|
3738
|
-
<dd>{{ctrl.serverGroup.createdTime | timestamp}}</dd>
|
|
3739
|
-
<dt>In</dt>
|
|
3740
|
-
<dd><account-tag account="ctrl.serverGroup.account"></account-tag></dd>
|
|
3741
|
-
<dt>Region</dt>
|
|
3742
|
-
<dd>{{ctrl.serverGroup.region}}</dd>
|
|
3743
|
-
<appengine-conditional-dt-dd
|
|
3744
|
-
component="ctrl.serverGroup"
|
|
3745
|
-
key="env"
|
|
3746
|
-
label="Environment"
|
|
3747
|
-
></appengine-conditional-dt-dd>
|
|
3748
|
-
<appengine-conditional-dt-dd component="ctrl.serverGroup" key="instanceClass"></appengine-conditional-dt-dd>
|
|
3749
|
-
</dl>
|
|
3750
|
-
</collapsible-section>
|
|
3751
|
-
<collapsible-section heading="Size" expanded="true">
|
|
3752
|
-
<dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.capacity.min === ctrl.serverGroup.capacity.max">
|
|
3753
|
-
<dt>Min/Max</dt>
|
|
3754
|
-
<dd>{{ctrl.serverGroup.capacity.min}}</dd>
|
|
3755
|
-
<dt>Current</dt>
|
|
3756
|
-
<dd>{{ctrl.serverGroup.instances.length}}</dd>
|
|
3757
|
-
</dl>
|
|
3758
|
-
<dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.capacity.min !== ctrl.serverGroup.capacity.max">
|
|
3759
|
-
<dt>Min</dt>
|
|
3760
|
-
<dd>{{ctrl.serverGroup.capacity.min}}</dd>
|
|
3761
|
-
<dt>Max</dt>
|
|
3762
|
-
<dd>{{ctrl.serverGroup.capacity.max}}</dd>
|
|
3763
|
-
<dt>Current</dt>
|
|
3764
|
-
<dd>{{ctrl.serverGroup.instances.length}}</dd>
|
|
3765
|
-
</dl>
|
|
3766
|
-
</collapsible-section>
|
|
3767
|
-
<collapsible-section heading="Health" expanded="true">
|
|
3768
|
-
<dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup">
|
|
3769
|
-
<dt>Instances</dt>
|
|
3770
|
-
<dd>
|
|
3771
|
-
<health-counts container="ctrl.serverGroup.instanceCounts" class="pull-left"></health-counts>
|
|
3772
|
-
</dd>
|
|
3773
|
-
</dl>
|
|
3774
|
-
</collapsible-section>
|
|
3775
|
-
<collapsible-section heading="DNS" expanded="true">
|
|
3776
|
-
<dl class="dl-narrow">
|
|
3777
|
-
<appengine-component-url-details component="ctrl.serverGroup"></appengine-component-url-details>
|
|
3778
|
-
</dl>
|
|
3779
|
-
</collapsible-section>
|
|
3780
|
-
<collapsible-section heading="Scaling Policy" expanded="true" ng-if="ctrl.serverGroup.scalingPolicy">
|
|
3781
|
-
<dl>
|
|
3782
|
-
<appengine-conditional-dt-dd
|
|
3783
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3784
|
-
key="type"
|
|
3785
|
-
></appengine-conditional-dt-dd>
|
|
3786
|
-
<!--MANUAL SCALING PROPERTIES-->
|
|
3787
|
-
<appengine-conditional-dt-dd
|
|
3788
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3789
|
-
key="instances"
|
|
3790
|
-
></appengine-conditional-dt-dd>
|
|
3791
|
-
<!--BASIC SCALING PROPERTIES-->
|
|
3792
|
-
<appengine-conditional-dt-dd
|
|
3793
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3794
|
-
key="idleTimeout"
|
|
3795
|
-
></appengine-conditional-dt-dd>
|
|
3796
|
-
<appengine-conditional-dt-dd
|
|
3797
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3798
|
-
key="maxInstances"
|
|
3799
|
-
></appengine-conditional-dt-dd>
|
|
3800
|
-
<!--AUTOMATIC SCALING PROPERTIES-->
|
|
3801
|
-
<appengine-conditional-dt-dd
|
|
3802
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3803
|
-
key="coolDownPeriod"
|
|
3804
|
-
></appengine-conditional-dt-dd>
|
|
3805
|
-
<appengine-conditional-dt-dd
|
|
3806
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3807
|
-
key="maxConcurrentRequests"
|
|
3808
|
-
></appengine-conditional-dt-dd>
|
|
3809
|
-
<appengine-conditional-dt-dd
|
|
3810
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3811
|
-
key="maxTotalInstances"
|
|
3812
|
-
></appengine-conditional-dt-dd>
|
|
3813
|
-
<appengine-conditional-dt-dd
|
|
3814
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3815
|
-
key="minTotalInstances"
|
|
3816
|
-
></appengine-conditional-dt-dd>
|
|
3817
|
-
<appengine-conditional-dt-dd
|
|
3818
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3819
|
-
key="maxIdleInstances"
|
|
3820
|
-
></appengine-conditional-dt-dd>
|
|
3821
|
-
<appengine-conditional-dt-dd
|
|
3822
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3823
|
-
key="minIdleInstances"
|
|
3824
|
-
></appengine-conditional-dt-dd>
|
|
3825
|
-
<appengine-conditional-dt-dd
|
|
3826
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3827
|
-
key="maxPendingLatency"
|
|
3828
|
-
></appengine-conditional-dt-dd>
|
|
3829
|
-
<appengine-conditional-dt-dd
|
|
3830
|
-
component="ctrl.serverGroup.scalingPolicy"
|
|
3831
|
-
key="minPendingLatency"
|
|
3832
|
-
></appengine-conditional-dt-dd>
|
|
3833
|
-
<appengine-conditional-dt-dd
|
|
3834
|
-
component="ctrl.serverGroup.scalingPolicy.cpuUtilization"
|
|
3835
|
-
key="aggregationWindowLength"
|
|
3836
|
-
></appengine-conditional-dt-dd>
|
|
3837
|
-
<appengine-conditional-dt-dd
|
|
3838
|
-
component="ctrl.serverGroup.scalingPolicy.cpuUtilization"
|
|
3839
|
-
key="targetUtilization"
|
|
3840
|
-
label="Target CPU Utilization"
|
|
3841
|
-
></appengine-conditional-dt-dd>
|
|
3842
|
-
<appengine-conditional-dt-dd
|
|
3843
|
-
component="ctrl.serverGroup.scalingPolicy.diskUtilization"
|
|
3844
|
-
key="targetReadBytesPerSecond"
|
|
3845
|
-
></appengine-conditional-dt-dd>
|
|
3846
|
-
<appengine-conditional-dt-dd
|
|
3847
|
-
component="ctrl.serverGroup.scalingPolicy.diskUtilization"
|
|
3848
|
-
key="targetReadOpsPerSecond"
|
|
3849
|
-
></appengine-conditional-dt-dd>
|
|
3850
|
-
<appengine-conditional-dt-dd
|
|
3851
|
-
component="ctrl.serverGroup.scalingPolicy.diskUtilization"
|
|
3852
|
-
key="targetWriteBytesPerSecond"
|
|
3853
|
-
></appengine-conditional-dt-dd>
|
|
3854
|
-
<appengine-conditional-dt-dd
|
|
3855
|
-
component="ctrl.serverGroup.scalingPolicy.diskUtilization"
|
|
3856
|
-
key="targetWriteOpsPerSecond"
|
|
3857
|
-
></appengine-conditional-dt-dd>
|
|
3858
|
-
<appengine-conditional-dt-dd
|
|
3859
|
-
component="ctrl.serverGroup.scalingPolicy.networkUtilization"
|
|
3860
|
-
key="targetReceivedBytesPerSecond"
|
|
3861
|
-
></appengine-conditional-dt-dd>
|
|
3862
|
-
<appengine-conditional-dt-dd
|
|
3863
|
-
component="ctrl.serverGroup.scalingPolicy.networkUtilization"
|
|
3864
|
-
key="targetReceivedPacketsPerSecond"
|
|
3865
|
-
></appengine-conditional-dt-dd>
|
|
3866
|
-
<appengine-conditional-dt-dd
|
|
3867
|
-
component="ctrl.serverGroup.scalingPolicy.networkUtilization"
|
|
3868
|
-
key="targetSentBytesPerSecond"
|
|
3869
|
-
></appengine-conditional-dt-dd>
|
|
3870
|
-
<appengine-conditional-dt-dd
|
|
3871
|
-
component="ctrl.serverGroup.scalingPolicy.networkUtilization"
|
|
3872
|
-
key="targetSentPacketsPerSecond"
|
|
3873
|
-
></appengine-conditional-dt-dd>
|
|
3874
|
-
<appengine-conditional-dt-dd
|
|
3875
|
-
component="ctrl.serverGroup.scalingPolicy.requestUtilization"
|
|
3876
|
-
key="targetConcurrentRequests"
|
|
3877
|
-
></appengine-conditional-dt-dd>
|
|
3878
|
-
<appengine-conditional-dt-dd
|
|
3879
|
-
component="ctrl.serverGroup.scalingPolicy.requestUtilization"
|
|
3880
|
-
key="targetRequestCountPerSecond"
|
|
3881
|
-
></appengine-conditional-dt-dd>
|
|
3882
|
-
</dl>
|
|
3883
|
-
</collapsible-section>
|
|
3884
|
-
</div>
|
|
3885
|
-
</div>
|
|
3886
|
-
`);
|
|
3887
|
-
}]);
|
|
3888
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
3889
|
-
templateCache.put("appengine/src/serverGroup/configure/wizard/serverGroupWizard.html", `<form name="form" class="form-horizontal appengine-server-group-wizard" novalidate>
|
|
3890
|
-
<div ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">
|
|
3891
|
-
<loading-spinner size="'small'"></loading-spinner>
|
|
3892
|
-
</div>
|
|
3893
|
-
<div>
|
|
3894
|
-
<v2-modal-wizard
|
|
3895
|
-
ng-if="!ctrl.state.loading"
|
|
3896
|
-
heading="Create Server Group"
|
|
3897
|
-
task-monitor="ctrl.taskMonitor"
|
|
3898
|
-
dismiss="$dismiss()"
|
|
3899
|
-
>
|
|
3900
|
-
<v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">
|
|
3901
|
-
<ng-include src="ctrl.pages.basicSettings"></ng-include>
|
|
3902
|
-
</v2-wizard-page>
|
|
3903
|
-
<v2-wizard-page key="load-balancer" label="Config Files" mark-complete-on-view="false">
|
|
3904
|
-
<appengine-config-file-configurer command="command"></appengine-config-file-configurer>
|
|
3905
|
-
</v2-wizard-page>
|
|
3906
|
-
<v2-wizard-page key="load-balancer" label="Load Balancer" done="true">
|
|
3907
|
-
<appengine-load-balancer-message show-create-message="false"></appengine-load-balancer-message>
|
|
3908
|
-
</v2-wizard-page>
|
|
3909
|
-
<v2-wizard-page key="advanced-settings" label="Advanced Settings" done="true">
|
|
3910
|
-
<ng-include src="ctrl.pages.advancedSettings"></ng-include>
|
|
3911
|
-
</v2-wizard-page>
|
|
3912
|
-
</v2-modal-wizard>
|
|
3913
|
-
<div class="modal-footer" ng-if="!state.loading">
|
|
3914
|
-
<button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">
|
|
3915
|
-
Cancel
|
|
3916
|
-
</button>
|
|
3917
|
-
<submit-button
|
|
3918
|
-
ng-if="form.$valid"
|
|
3919
|
-
is-disabled="form.$invalid || ctrl.taskMonitor.submitting"
|
|
3920
|
-
label="command.viewState.submitButtonLabel"
|
|
3921
|
-
submitting="ctrl.taskMonitor.submitting"
|
|
3922
|
-
on-click="ctrl.submit()"
|
|
3923
|
-
is-new="true"
|
|
3924
|
-
></submit-button>
|
|
3925
|
-
</div>
|
|
3926
|
-
</div>
|
|
3927
|
-
</form>
|
|
3928
|
-
`);
|
|
3929
|
-
}]);
|
|
3930
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
3931
|
-
templateCache.put("appengine/src/loadBalancer/configure/wizard/wizard.html", `<form name="form">
|
|
3932
|
-
<div ng-if="ctrl.state.loading && !ctrl.isNew" style="height: 200px" class="horizontal center middle">
|
|
3933
|
-
<loading-spinner size="'small'"></loading-spinner>
|
|
3934
|
-
</div>
|
|
3935
|
-
<v2-modal-wizard
|
|
3936
|
-
heading="{{::ctrl.heading}}"
|
|
3937
|
-
task-monitor="ctrl.taskMonitor"
|
|
3938
|
-
dismiss="$dismiss()"
|
|
3939
|
-
ng-if="!ctrl.state.loading || ctrl.isNew"
|
|
3940
|
-
>
|
|
3941
|
-
<div ng-if="!ctrl.isNew">
|
|
3942
|
-
<v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">
|
|
3943
|
-
<appengine-load-balancer-basic-settings
|
|
3944
|
-
load-balancer="ctrl.loadBalancer"
|
|
3945
|
-
application="ctrl.application"
|
|
3946
|
-
for-pipeline-config="ctrl.forPipelineConfig"
|
|
3947
|
-
></appengine-load-balancer-basic-settings>
|
|
3948
|
-
</v2-wizard-page>
|
|
3949
|
-
<v2-wizard-page key="advanced-settings" label="Advanced Settings" mark-complete-on-view="false">
|
|
3950
|
-
<appengine-load-balancer-advanced-settings
|
|
3951
|
-
load-balancer="ctrl.loadBalancer"
|
|
3952
|
-
></appengine-load-balancer-advanced-settings>
|
|
3953
|
-
</v2-wizard-page>
|
|
3954
|
-
</div>
|
|
3955
|
-
</v2-modal-wizard>
|
|
3956
|
-
<appengine-load-balancer-message
|
|
3957
|
-
ng-if="ctrl.isNew"
|
|
3958
|
-
column-offset="1"
|
|
3959
|
-
columns="10"
|
|
3960
|
-
show-create-message="true"
|
|
3961
|
-
></appengine-load-balancer-message>
|
|
3962
|
-
<div class="modal-footer">
|
|
3963
|
-
<button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>
|
|
3964
|
-
<submit-button
|
|
3965
|
-
ng-if="!ctrl.isNew && ctrl.showSubmitButton()"
|
|
3966
|
-
label="ctrl.submitButtonLabel"
|
|
3967
|
-
is-disabled="appengineLoadBalancerForm.$invalid || ctrl.taskMonitor.submitting || ctrl.state.loading"
|
|
3968
|
-
submitting="ctrl.taskMonitor.submitting"
|
|
3969
|
-
on-click="ctrl.submit()"
|
|
3970
|
-
is-new="ctrl.isNew"
|
|
3971
|
-
>
|
|
3972
|
-
</submit-button>
|
|
3973
|
-
</div>
|
|
3974
|
-
</form>
|
|
3975
|
-
`);
|
|
3976
|
-
}]);
|
|
3977
|
-
window.angular.module("ng").run(["$templateCache", function(templateCache) {
|
|
3978
|
-
templateCache.put("appengine/src/loadBalancer/details/details.html", `<div class="details-panel">
|
|
3979
|
-
<div ng-if="ctrl.state.loading" class="header">
|
|
3980
|
-
<div class="close-button">
|
|
3981
|
-
<a class="btn btn-link" ui-sref="^">
|
|
3982
|
-
<span class="glyphicon glyphicon-remove"></span>
|
|
3983
|
-
</a>
|
|
3984
|
-
</div>
|
|
3985
|
-
<div class="horizontal center middle">
|
|
3986
|
-
<loading-spinner size="'small'"></loading-spinner>
|
|
3987
|
-
</div>
|
|
3988
|
-
</div>
|
|
3989
|
-
|
|
3990
|
-
<div ng-if="!ctrl.state.loading" class="header">
|
|
3991
|
-
<div class="close-button">
|
|
3992
|
-
<a class="btn btn-link" ui-sref="^">
|
|
3993
|
-
<span class="glyphicon glyphicon-remove"></span>
|
|
3994
|
-
</a>
|
|
3995
|
-
</div>
|
|
3996
|
-
<div class="header-text horizontal middle">
|
|
3997
|
-
<i class="fa icon-sitemap"></i>
|
|
3998
|
-
<h3 class="horizontal middle space-between flex-1" select-on-dbl-click>{{ctrl.loadBalancer.name}}</h3>
|
|
3999
|
-
</div>
|
|
4000
|
-
<div>
|
|
4001
|
-
<div class="actions">
|
|
4002
|
-
<div class="dropdown" uib-dropdown dropdown-append-to-body>
|
|
4003
|
-
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>
|
|
4004
|
-
Load Balancer Actions <span class="caret"></span>
|
|
4005
|
-
</button>
|
|
4006
|
-
<ul class="dropdown-menu" uib-dropdown-menu role="menu">
|
|
4007
|
-
<li><a href ng-click="ctrl.editLoadBalancer()">Edit Load Balancer</a></li>
|
|
4008
|
-
<li ng-if="ctrl.canDeleteLoadBalancer()">
|
|
4009
|
-
<a href ng-click="ctrl.deleteLoadBalancer()">Delete Load Balancer</a>
|
|
4010
|
-
</li>
|
|
4011
|
-
<li
|
|
4012
|
-
ng-if="!ctrl.canDeleteLoadBalancer()"
|
|
4013
|
-
uib-tooltip="You cannot delete a default service."
|
|
4014
|
-
class="disabled"
|
|
4015
|
-
>
|
|
4016
|
-
<a href>Delete Load Balancer</a>
|
|
4017
|
-
</li>
|
|
4018
|
-
</ul>
|
|
4019
|
-
</div>
|
|
4020
|
-
</div>
|
|
4021
|
-
</div>
|
|
4022
|
-
</div>
|
|
4023
|
-
|
|
4024
|
-
<div ng-if="!ctrl.state.loading" class="content">
|
|
4025
|
-
<collapsible-section heading="Load Balancer Details" expanded="true">
|
|
4026
|
-
<dl class="dl-horizontal dl-narrow">
|
|
4027
|
-
<dt>In</dt>
|
|
4028
|
-
<dd><account-tag account="ctrl.loadBalancer.account" pad="right"></account-tag></dd>
|
|
4029
|
-
<dt>Region</dt>
|
|
4030
|
-
<dd>{{ctrl.loadBalancer.region}}</dd>
|
|
4031
|
-
<dt ng-if="ctrl.loadBalancer.serverGroups.length">Server Groups</dt>
|
|
4032
|
-
<dd ng-if="ctrl.loadBalancer.serverGroups.length">
|
|
4033
|
-
<ul>
|
|
4034
|
-
<li ng-repeat="serverGroup in ctrl.loadBalancer.serverGroups | orderBy: ['isDisabled', '-name']">
|
|
4035
|
-
<a
|
|
4036
|
-
ui-sref="^.serverGroup({region: serverGroup.region,
|
|
4037
|
-
accountId: serverGroup.account,
|
|
4038
|
-
serverGroup: serverGroup.name,
|
|
4039
|
-
provider: 'appengine'})"
|
|
4040
|
-
>
|
|
4041
|
-
{{serverGroup.name}}
|
|
4042
|
-
</a>
|
|
4043
|
-
</li>
|
|
4044
|
-
</ul>
|
|
4045
|
-
</dd>
|
|
4046
|
-
</dl>
|
|
4047
|
-
</collapsible-section>
|
|
4048
|
-
<collapsible-section heading="Status" expanded="true">
|
|
4049
|
-
<health-counts class="pull-left" container="ctrl.loadBalancer.instanceCounts"></health-counts>
|
|
4050
|
-
</collapsible-section>
|
|
4051
|
-
<collapsible-section heading="Traffic Split" expanded="true">
|
|
4052
|
-
<dl class="dl-horizontal dl-narrow">
|
|
4053
|
-
<dt ng-if="ctrl.loadBalancer.split.shardBy">Shard By</dt>
|
|
4054
|
-
<dd ng-if="ctrl.loadBalancer.split.shardBy">
|
|
4055
|
-
{{ctrl.loadBalancer.split.shardBy}}
|
|
4056
|
-
<help-field
|
|
4057
|
-
key="appengine.loadBalancer.shardBy.{{ctrl.loadBalancer.split.shardBy.toLowerCase()}}"
|
|
4058
|
-
></help-field>
|
|
4059
|
-
</dd>
|
|
4060
|
-
<hr ng-if="ctrl.loadBalancer.split.shardBy" />
|
|
4061
|
-
<ul>
|
|
4062
|
-
<li ng-repeat="(serverGroup, percent) in ctrl.loadBalancer.split.allocations">
|
|
4063
|
-
{{serverGroup}}:<span class="pull-right">{{percent | decimalToPercent}}</span>
|
|
4064
|
-
</li>
|
|
4065
|
-
</ul>
|
|
4066
|
-
</dl>
|
|
4067
|
-
</collapsible-section>
|
|
4068
|
-
<collapsible-section heading="DNS" expanded="true">
|
|
4069
|
-
<dl class="dl-narrow">
|
|
4070
|
-
<appengine-component-url-details component="ctrl.loadBalancer"></appengine-component-url-details>
|
|
4071
|
-
</dl>
|
|
4072
|
-
</collapsible-section>
|
|
4073
|
-
<collapsible-section heading="Dispatch Rules" expanded="true" ng-if="ctrl.dispatchRules.length > 0">
|
|
4074
|
-
<dl class="dl-horizontal dl-narrow">
|
|
4075
|
-
<span ng-repeat-start="rule in ctrl.dispatchRules">{{rule}}</span><br ng-repeat-end />
|
|
4076
|
-
</dl>
|
|
4077
|
-
</collapsible-section>
|
|
4078
|
-
</div>
|
|
4079
|
-
</div>
|
|
4080
|
-
`);
|
|
4081
|
-
}]);
|
|
4082
|
-
|
|
4083
|
-
export { APPENGINE_MODULE };
|
|
1
|
+
import{module as e,extend as n,copy as t}from"angular";import{HelpContentsRegistry as a,ConfirmationModalService as i,RecentHistoryService as r,InstanceReader as o,InstanceWriter as l,StageConstants as s,AppListExtractor as c,LoadBalancerWriter as p,TaskMonitor as d,AccountService as g,Registry as u,CloudProviderRegistry as m,FormikFormField as h,ReactSelectInput as v,StageArtifactSelectorDelegate as f,excludeAllTypesExcept as b,ArtifactTypePatterns as y,FormikStageConfig as C,FormValidator as S,ExecutionDetailsTasks as w,ExecutionArtifactTab as G,SETTINGS as A,StorageAccountReader as k,NgAppEngineDeployArtifactDelegate as $,ExpectedArtifactSelectorViewController as B,NgAppengineConfigArtifactDelegate as x,SERVER_GROUP_WRITER as T,StageArtifactSelector as D,withErrorBoundary as E,TaskExecutor as F,ServerGroupWarningMessageService as L,ServerGroupReader as P,ApplicationNameValidator as I,DeploymentStrategyRegistry as O}from"@spinnaker/core";import{cloneDeep as N,flattenDeep as z,uniq as R,difference as M,filter as U,chain as H,has as q,camelCase as V,get as j,reduce as W,set as _,merge as J,map as K,mapValues as Y}from"lodash";import Q from"react";import{Subject as X,from as Z}from"rxjs";import{takeUntil as ee}from"rxjs/operators";import{react2angular as ne}from"react2angular";import te from"classnames";const ae={bindings:{component:"<"},template:'\n <dt>HTTPS</dt>\n <dl class="small">\n <a href="{{$ctrl.component.httpsUrl}}" target="_blank">{{$ctrl.component.httpsUrl}}</a>\n <copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"\n tool-tip="\'Copy URL to clipboard\'"\n text="$ctrl.component.httpsUrl"></copy-to-clipboard>\n </dl>\n <dt>HTTP</dt>\n <dl class="small">\n <a href="{{$ctrl.component.httpUrl}}" target="_blank">{{$ctrl.component.httpUrl}}</a>\n <copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"\n tool-tip="\'Copy URL to clipboard\'"\n text="$ctrl.component.httpUrl"></copy-to-clipboard>\n </dl>\n '},ie="spinnaker.appengine.componentUrlDetails.component";e(ie,[]).component("appengineComponentUrlDetails",ae);class re{constructor(e){this.$filter=e}$onInit(){this.label||(this.label=this.$filter("robotToHuman")(this.key))}}re.$inject=["$filter"];const oe={bindings:{label:"@",key:"@",component:"<"},transclude:{keyLabel:"?keyText",valueLabel:"?valueLabel"},template:'\n <dt ng-if="$ctrl.component[$ctrl.key]">{{$ctrl.label}}<span ng-transclude="keyLabel"></span></dt>\n <dd ng-if="$ctrl.component[$ctrl.key]">{{$ctrl.component[$ctrl.key]}}<span ng-transclude="valueLabel"></span></dd>\n ',controller:re},le="spinnaker.appengine.conditionalDescriptionListItem";e(le,[]).component("appengineConditionalDtDd",oe);const se="spinnaker.appengine.loadBalancer.createMessage.component";e(se,[]).component("appengineLoadBalancerMessage",{bindings:{showCreateMessage:"<",columnOffset:"@",columns:"@"},templateUrl:"appengine/src/common/loadBalancerMessage.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/common/loadBalancerMessage.component.html",'<div class="row">\n <div class="col-md-offset-{{ $ctrl.columnOffset || 0 }} col-md-{{ $ctrl.columns || 12 }}">\n <div class="well">\n <p>\n <span ng-if="$ctrl.showCreateMessage">Spinnaker cannot create a load balancer for App Engine.</span>\n A Spinnaker load balancer maps to an App Engine service, which is specified in a version\'s\n <code>app.yaml</code>.\n </p>\n <p>For example, the following <code>app.yaml</code></p>\n <pre class="text-left" style="white-space: pre-line">\n \t\t\truntime: python27\n \t\t\tapi_version: 1\n …\n \t\t\tservice: mobile\n …\n \t\t</pre\n >\n <p>\n deploys to the <code>mobile</code> service. If you do not specify a service, your version will be deployed to\n the <code>default</code> service.\n </p>\n <p>\n If a service does not exist when a version is deployed, it will be created. It will then be editable as a load\n balancer within Spinnaker.\n </p>\n <p>\n <a href="https://cloud.google.com/appengine/docs/python/config/appref" target="_blank"\n >App Engine Documentation</a\n >\n </p>\n </div>\n </div>\n</div>\n')}]);[{key:"appengine.serverGroup.gcs.repositoryUrl",value:"The full URL to the GCS bucket or TAR file containing the source files for this deployment,\n including 'gs://'. For example, <b>gs://my-bucket/my-app</b> or <b>gs://my-bucket/app.tar</b>."},{key:"appengine.serverGroup.git.repositoryUrl",value:"The full URL to the git repository containing the source files for this deployment,\n including protocol. For example, <b>https://github.com/spinnaker/deck.git</b>"},{key:"appengine.serverGroup.gitCredentialType",value:"The credential type that will be used to access the git repository for this deployment.\n You can configure these credentials alongside your App Engine credentials."},{key:"appengine.serverGroup.branch",value:"The name of the branch in the above git repository to be used for this deployment."},{key:"appengine.serverGroup.applicationDirectoryRoot",value:"(Optional) Path to the directory root of the application to be deployed,\n starting from the root of the git repository. This is the directory from which\n <code>gcloud app deploy</code> will be run."},{key:"appengine.serverGroup.configFilepaths",value:"Paths to App Engine application configuration files, starting from the application directory root,\n specified above. In most cases, this will be <code>app.yaml</code> or <code>cron.yaml</code>,\n but could be <code>path/to/app.yaml</code> or <code>path/to/cron.yaml</code>."},{key:"appengine.serverGroup.configFiles",value:"<p>(Optional) The contents of an App Engine config file (e.g., an <code>app.yaml</code> or\n <code>cron.yaml</code> file). These files should not conflict with the config filepaths above:\n if you include, for example, the contents of an <code>app.yaml</code>\n file here, you should <b>not</b> specify the file path to an <code>app.yaml</code> above.<br></p>\n <p>If this is a pipeline stage, you can use Spinnaker Pipeline Expressions here.</p>"},{key:"appengine.serverGroup.configFilesRequired",value:"<p>The contents of an App Engine config file (e.g., an <code>app.yaml</code> or\n <code>cron.yaml</code> file).</p>\n <p>An <code>app.yaml</code> file is required and must have <code>runtime: custom</code> in\n order to deploy successfully.</p>\n <p>If this is a pipeline stage, you can use Spinnaker Pipeline Expressions here.</p>"},{key:"appengine.serverGroup.matchBranchOnRegex",value:"(Optional) A Jenkins trigger may produce details from multiple repositories and branches.\n Spinnaker will use the regex specified here to help resolve a branch for the deployment.\n If Spinnaker cannot resolve exactly one branch from the trigger, this pipeline will fail."},{key:"appengine.serverGroup.promote",value:"If selected, the newly deployed server group will receive all traffic."},{key:"appengine.serverGroup.stopPreviousVersion",value:"If selected, the previously running server group in this server group's <b>service</b>\n (Spinnaker load balancer) will be stopped. This option will be respected only if this server group will\n be receiving all traffic and the previous server group is using manual scaling."},{key:"appengine.serverGroup.containerImageUrl",value:"The full URL to the container image to use for deployment. The URL must be one of the valid GCR hostnames,\n for example <b>gcr.io/my-project/image:tag</b>"},{key:"appengine.serverGroup.suppress-version-string",value:'Spinnaker automatically versions your server groups. This means deployments through Spinnaker receive a\n short version string at the end of their name, like "v001". In most cases you will want to keep this\n version as part of the name. Preventing this string from being added to your server group will stop\n it from being considered when rolling back, promoting new versions or executing deployment strategies.\n If you are certain that you do not want the version string applied to this server group then check\n this box.'},{key:"appengine.loadBalancer.shardBy.cookie",value:'Diversion based on a specially named cookie, "GOOGAPPUID." The cookie must be set by the application itself or no diversion will occur.'},{key:"appengine.loadBalancer.shardBy.ip",value:"Diversion based on applying the modulus operation to a fingerprint of the IP address."},{key:"appengine.loadBalancer.migrateTraffic",value:"If selected, traffic will be gradually shifted to a single version. For gradual traffic migration,\n the target version must be located within instances that are configured for\n both warmup requests and automatic scaling.\n Gradual traffic migration is not supported in the App Engine flexible environment."},{key:"appengine.loadBalancer.allocations",value:"An allocation is the percent of traffic directed to a server group."},{key:"appengine.loadBalancer.textLocator",value:"Either the name of a server group, or a Spinnaker Pipeline Expression\n that resolves to the name of a server group."},{key:"appengine.instance.availability",value:"\n An instance's <b>availability</b> is determined by its version (Spinnaker server group).\n <ul>\n <li>Manual scaling versions use resident instances</li>\n <li>Basic scaling versions use dynamic instances</li>\n <li>Auto scaling versions use dynamic instances - but if you specify a number, N,\n of minimum idle instances, the first N instances will be resident,\n and additional dynamic instances will be created as necessary.\n </li>\n </ul>"},{key:"appengine.instance.averageLatency",value:"Average latency over the last minute in milliseconds."},{key:"appengine.instance.vmStatus",value:"Status of the virtual machine where this instance lives."},{key:"appengine.instance.qps",value:"Average queries per second over the last minute."},{key:"appengine.instance.errors",value:"Number of errors since this instance was started."},{key:"appengine.instance.requests",value:"Number of requests since this instance was started."}].forEach((e=>a.register(e.key,e.value)));class ce{constructor(e,n,t){this.$q=e,this.app=n,this.state={loading:!0},this.upToolTip="An App Engine instance is 'Up' if a load balancer is directing traffic to its server group.",this.outOfServiceToolTip="\n An App Engine instance is 'Out Of Service' if no load balancers are directing traffic to its server group.",this.app.ready().then((()=>this.retrieveInstance(t))).then((e=>{this.instance=e,this.state.loading=!1})).catch((()=>{this.instanceIdNotFound=t.instanceId,this.state.loading=!1}))}terminateInstance(){const e=N(this.instance),n=`${this.instance.name.substring(0,10)}...`;e.placement={},e.instanceId=e.name;const t={application:this.app,title:"Terminating "+n,onTaskComplete(){this.$state.includes("**.instanceDetails",{instanceId:e.name})&&this.$state.go("^")}};i.confirm({header:"Really terminate "+n+"?",buttonText:"Terminate "+n,account:e.account,taskMonitorConfig:t,submitMethod:()=>l.terminateInstance(e,this.app,{cloudProvider:"appengine"})})}retrieveInstance(e){const n=z([this.app.getDataSource("serverGroups").data,this.app.getDataSource("loadBalancers").data,this.app.getDataSource("loadBalancers").data.map((e=>e.serverGroups))]).find((n=>n.instances.some((n=>n.id===e.instanceId))));if(n){const t={region:n.region,account:n.account};return"serverGroup"===n.category&&(t.serverGroup=n.name),r.addExtraDataToLatest("instances",t),o.getInstanceDetails(n.account,n.region,e.instanceId).then((e=>(e.account=n.account,e.region=n.region,e)))}return this.$q.reject()}}ce.$inject=["$q","app","instance"];const pe="spinnaker.appengine.instanceDetails.controller";e(pe,[]).controller("appengineInstanceDetailsCtrl",ce);const de={bindings:{loadBalancer:"=",application:"<"},template:'\n <ng-form name="advancedSettingsForm">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Migrate Traffic <help-field key="appengine.loadBalancer.migrateTraffic"></help-field>\n </div>\n <div class="col-md-9">\n <div class="checkbox">\n <input type="checkbox" ng-disabled="$ctrl.disableMigrateTraffic() && !($ctrl.loadBalancer.migrateTraffic = false)" ng-model="$ctrl.loadBalancer.migrateTraffic">\n </div>\n </div>\n </div>\n </div>\n </ng-form>\n ',controller:class{constructor(){this.state={error:!1}}disableMigrateTraffic(){if(1!==this.loadBalancer.splitDescription.allocationDescriptions.length)return!0;{const e=this.loadBalancer.splitDescription.allocationDescriptions[0].serverGroupName,n=this.loadBalancer.serverGroups.find((n=>n.name===e));return!!n&&!n.allowsGradualTrafficMigration}}}},ge="spinnaker.appengine.loadBalancer.advancedSettings.component";e(ge,[]).component("appengineLoadBalancerAdvancedSettings",de);const ue={bindings:{allocationDescription:"<",removeAllocation:"&",serverGroupOptions:"<",onAllocationChange:"&"},template:'\n <div class="form-group">\n <div class="row">\n <div class="col-md-7">\n <ui-select ng-model="$ctrl.allocationDescription.serverGroupName"\n on-select="$ctrl.onAllocationChange()"\n class="form-control input-sm">\n <ui-select-match placeholder="Select...">\n {{$select.selected}}\n </ui-select-match>\n <ui-select-choices repeat="serverGroup as serverGroup in $ctrl.getServerGroupOptions() | filter: $select.search">\n <div ng-bind-html="serverGroup | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-3">\n <div class="input-group input-group-sm">\n <input type="number"\n ng-model="$ctrl.allocationDescription.allocation"\n required\n class="form-control input-sm"\n min="0"\n ng-change="$ctrl.onAllocationChange()"\n max="100"/>\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div class="col-md-2">\n <a class="btn btn-link sm-label" ng-click="$ctrl.removeAllocation()">\n <span class="glyphicon glyphicon-trash"></span>\n </a>\n </div>\n </div>\n </div>\n ',controller:class{getServerGroupOptions(){return this.allocationDescription.serverGroupName?R(this.serverGroupOptions.concat(this.allocationDescription.serverGroupName)):this.serverGroupOptions}}},me="spinnaker.appengine.allocationConfigurationRow.component";e(me,[]).component("appengineAllocationConfigurationRow",ue);const he={bindings:{loadBalancer:"=",forPipelineConfig:"<",application:"<"},controller:class{$onInit(){this.updateServerGroupOptions()}addAllocation(){const e=this.serverGroupsWithoutAllocation();e.length?(this.loadBalancer.splitDescription.allocationDescriptions.push({serverGroupName:e[0],allocation:0,locatorType:"fromExisting"}),this.loadBalancer.splitDescription.allocationDescriptions.length>1&&!this.loadBalancer.splitDescription.shardBy&&(this.loadBalancer.splitDescription.shardBy="IP"),this.updateServerGroupOptions()):this.forPipelineConfig&&this.loadBalancer.splitDescription.allocationDescriptions.push({allocation:0,locatorType:"text",serverGroupName:""})}removeAllocation(e){this.loadBalancer.splitDescription.allocationDescriptions.splice(e,1),this.updateServerGroupOptions()}allocationIsInvalid(){return 100!==this.loadBalancer.splitDescription.allocationDescriptions.reduce(((e,n)=>e+n.allocation),0)}updateServerGroupOptions(){this.serverGroupOptions=this.serverGroupsWithoutAllocation()}showAddButton(){return!!this.forPipelineConfig||this.serverGroupsWithoutAllocation().length>0}showShardByOptions(){return this.loadBalancer.splitDescription.allocationDescriptions.length>1||this.loadBalancer.migrateTraffic}initializeAsTextInput(e){return!!this.forPipelineConfig&&!this.loadBalancer.serverGroups.map((e=>e.name)).includes(e)}serverGroupsWithoutAllocation(){const e=this.loadBalancer.splitDescription.allocationDescriptions.map((e=>e.serverGroupName)),n=this.loadBalancer.serverGroups.map((e=>e.name));return M(n,e)}},templateUrl:"appengine/src/loadBalancer/configure/wizard/basicSettings.component.html"},ve="spinnaker.appengine.loadBalancerSettings.component";e(ve,[]).component("appengineLoadBalancerBasicSettings",he),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/basicSettings.component.html",'<ng-form name="basicSettingsForm">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Allocations\n <help-field key="appengine.loadBalancer.allocations"></help-field>\n </div>\n <div class="col-md-9">\n <div ng-if="!$ctrl.forPipelineConfig">\n <appengine-allocation-configuration-row\n ng-repeat="description in $ctrl.loadBalancer.splitDescription.allocationDescriptions"\n allocation-description="description"\n server-group-options="$ctrl.serverGroupOptions"\n on-allocation-change="$ctrl.updateServerGroupOptions()"\n remove-allocation="$ctrl.removeAllocation($index)"\n >\n </appengine-allocation-configuration-row>\n </div>\n <div ng-if="$ctrl.forPipelineConfig">\n <appengine-stage-allocation-configuration-row\n ng-repeat="description in $ctrl.loadBalancer.splitDescription.allocationDescriptions"\n allocation-description="description"\n application="$ctrl.application"\n region="{{ $ctrl.loadBalancer.region }}"\n account="{{ $ctrl.loadBalancer.account || $ctrl.loadBalancer.credentials }}"\n server-group-options="$ctrl.serverGroupOptions"\n on-allocation-change="$ctrl.updateServerGroupOptions()"\n remove-allocation="$ctrl.removeAllocation($index)"\n >\n </appengine-stage-allocation-configuration-row>\n </div>\n <button class="add-new col-md-11" ng-if="$ctrl.showAddButton()" ng-click="$ctrl.addAllocation()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add allocation\n </button>\n </div>\n </div>\n <div class="form-group" ng-if="$ctrl.allocationIsInvalid()">\n <div class="col-md-12 text-center">\n <p class="alert alert-warning">Allocations must sum to 100%.</p>\n </div>\n </div>\n </div>\n\n <div class="row" ng-if="$ctrl.showShardByOptions()">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Shard By</div>\n <div class="col-md-9">\n <form class="form-inline">\n <div class="form-group">\n <label class="radio-inline">\n <input type="radio" ng-model="$ctrl.loadBalancer.splitDescription.shardBy" value="IP" />\n IP\n <help-field key="appengine.loadBalancer.shardBy.ip"></help-field>\n </label>\n <label class="radio-inline">\n <input type="radio" ng-model="$ctrl.loadBalancer.splitDescription.shardBy" value="COOKIE" />\n Cookie\n <help-field key="appengine.loadBalancer.shardBy.cookie"></help-field>\n </label>\n </div>\n </form>\n </div>\n </div>\n </div>\n</ng-form>\n')}]);class fe{static mapTargetCoordinateToLabel(e){const n=s.TARGET_LIST.find((n=>n.val===e));return n?n.label:null}$doCheck(){this.setInputViewValue()}setInputViewValue(){switch(this.allocationDescription.locatorType){case"text":case"fromExisting":this.inputViewValue=this.allocationDescription.serverGroupName;break;case"targetCoordinate":if(this.allocationDescription.cluster&&this.allocationDescription.target){const e=fe.mapTargetCoordinateToLabel(this.allocationDescription.target);this.inputViewValue=`${e} (${this.allocationDescription.cluster})`}else this.inputViewValue=null;break;default:this.inputViewValue=null}}}const be={bindings:{allocationDescription:"<"},controller:fe,template:'<input ng-model="$ctrl.inputViewValue" type="text" class="form-control input-sm" readonly/>'};const ye={bindings:{application:"<",region:"@",account:"@",allocationDescription:"<",removeAllocation:"&",serverGroupOptions:"<",onAllocationChange:"&"},controller:class{constructor(){this.targets=s.TARGET_LIST}$onInit(){const e=c.clusterFilterForCredentialsAndRegion(this.account,this.region);this.clusterList=c.getClusters([this.application],e)}getServerGroupOptions(){return this.allocationDescription.serverGroupName?R(this.serverGroupOptions.concat(this.allocationDescription.serverGroupName)):this.serverGroupOptions}onLocatorTypeChange(){this.serverGroupOptions.includes(this.allocationDescription.serverGroupName)||delete this.allocationDescription.serverGroupName,this.onAllocationChange()}},templateUrl:"appengine/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html"},Ce="spinnaker.appengine.stageAllocationConfigurationRow.component";e(Ce,[]).component("appengineStageAllocationConfigurationRow",ye).component("appengineStageAllocationLabel",be),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html",'<div class="form-group">\n <div class="row">\n <div class="col-md-7">\n <appengine-stage-allocation-label allocation-description="$ctrl.allocationDescription">\n </appengine-stage-allocation-label>\n </div>\n <div class="col-md-3">\n <div class="input-group input-group-sm">\n <input\n type="number"\n ng-model="$ctrl.allocationDescription.allocation"\n required\n class="form-control input-sm"\n min="0"\n ng-change="$ctrl.onAllocationChange()"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div class="col-md-2">\n <a class="btn btn-link sm-label" ng-click="$ctrl.removeAllocation()">\n <span class="glyphicon glyphicon-trash"></span>\n </a>\n </div>\n </div>\n</div>\n<div class="form-group">\n <div class="well col-md-11" style="padding-top: 5px; padding-bottom: 10px">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Locator</div>\n <div class="col-md-7">\n <form>\n <div class="form-group">\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="fromExisting"\n />\n Existing server group\n </label>\n <br />\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="targetCoordinate"\n />\n Coordinates\n </label>\n <br />\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="text"\n />\n Text input\n </label>\n </div>\n </form>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="form-group" ng-switch on="$ctrl.allocationDescription.locatorType">\n <div ng-switch-when="fromExisting">\n <div class="col-md-3 sm-label-right" style="padding-left: 0px">Server group</div>\n <div class="col-md-7">\n <ui-select\n ng-model="$ctrl.allocationDescription.serverGroupName"\n on-select="$ctrl.onAllocationChange()"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected }}\n </ui-select-match>\n <ui-select-choices\n repeat="serverGroup as serverGroup in $ctrl.getServerGroupOptions() | filter: $select.search"\n >\n <div ng-bind-html="serverGroup | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n <div ng-switch-when="targetCoordinate">\n <div class="col-md-3 sm-label-right">Cluster</div>\n <div class="col-md-7">\n <cluster-selector\n class="small"\n clusters="$ctrl.clusterList"\n model="$ctrl.allocationDescription.cluster"\n ></cluster-selector>\n </div>\n <div class="col-md-3 sm-label-right">Target</div>\n <div class="col-md-7">\n <target-select model="$ctrl.allocationDescription" options="$ctrl.targets"></target-select>\n </div>\n </div>\n <div ng-switch-when="text">\n <div class="col-md-3 sm-label-right">\n Text\n <help-field key="appengine.loadBalancer.textLocator"></help-field>\n </div>\n <div class="col-md-7">\n <input class="form-control input-sm" type="text" ng-model="$ctrl.allocationDescription.serverGroupName" />\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n')}]);class Se{static convertTrafficSplitToTrafficSplitDescription(e){const n=W(e.allocations,((e,n,t)=>e.concat({serverGroupName:t,allocation:n,locatorType:"fromExisting"})),[]);return{shardBy:e.shardBy,allocationDescriptions:n}}constructor(e){this.credentials=e.account||e.credentials,this.account=this.credentials,this.cloudProvider=e.cloudProvider,this.loadBalancerName=e.name,this.name=e.name,this.region=e.region,this.migrateTraffic=e.migrateTraffic||!1,this.serverGroups=e.serverGroups}mapAllocationsToDecimals(){this.splitDescription.allocationDescriptions.forEach((e=>{e.allocation=e.allocation/100}))}mapAllocationsToPercentages(){this.splitDescription.allocationDescriptions.forEach((e=>{e.allocation=Math.round(1e3*e.allocation)/10}))}}class we{constructor(e){this.$q=e}normalizeLoadBalancer(e){e.provider=e.type,e.instanceCounts=this.buildInstanceCounts(e.serverGroups),e.instances=[],e.serverGroups.forEach((n=>{n.account=e.account,n.region=e.region,n.detachedInstances&&(n.detachedInstances=n.detachedInstances.map((e=>({id:e})))),n.instances=n.instances.concat(n.detachedInstances||[]).map((n=>this.transformInstance(n,e)))}));const n=U(e.serverGroups,{isDisabled:!1});return e.instances=H(n).map("instances").flatten().value(),this.$q.resolve(e)}convertLoadBalancerForEditing(e,n){return n.getDataSource("loadBalancers").ready().then((()=>{const t=n.getDataSource("loadBalancers").data.find((n=>n.name===e.name&&(n.account===e.account||n.account===e.credentials)));return t&&(e.serverGroups=N(t.serverGroups)),e}))}convertLoadBalancerToUpsertDescription(e){return new Se(e)}buildInstanceCounts(e){const n=H(e).map("instances").flatten().reduce(((e,n)=>(q(n,"health.state")&&e[V(n.health.state)]++,e)),{up:0,down:0,outOfService:0,succeeded:0,failed:0,starting:0,unknown:0}).value();return n.outOfService+=H(e).map("detachedInstances").flatten().value().length,n}transformInstance(e,n){e.provider=n.type,e.account=n.account,e.region=n.region,e.loadBalancers=[n.name];const t=e.health||{};return e.healthState=j(e,"health.state")||"OutOfService",e.health=[t],e}}we.$inject=["$q"];const Ge="spinnaker.appengine.loadBalancer.transformer.service";function Ae(e,n){void 0===n&&(n={});var t=n.insertAt;if(e&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css","top"===t&&a.firstChild?a.insertBefore(i,a.firstChild):a.appendChild(i),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(document.createTextNode(e))}}e(Ge,[]).service("appengineLoadBalancerTransformer",we);Ae("appengine-load-balancer-basic-settings a.btn.btn-link {\n padding: 0;\n}\nappengine-load-balancer-basic-settings .form-group {\n margin-top: 0.4rem;\n}\nappengine-load-balancer-advanced-settings .checkbox input[type='checkbox'] {\n margin: 0 0 0.1rem 0;\n}\n");class ke{constructor(e,n,t,a,i,r,o,l,s){this.$scope=e,this.$state=n,this.$uibModalInstance=t,this.application=a,this.isNew=r,this.forPipelineConfig=o,this.appengineLoadBalancerTransformer=l,this.wizardSubFormValidation=s,this.state={loading:!0},this.submitButtonLabel=this.forPipelineConfig?"Done":"Update",this.isNew?this.heading="Create New Load Balancer":(this.heading=`Edit ${[i.name,i.region,i.account||i.credentials].join(":")}`,this.appengineLoadBalancerTransformer.convertLoadBalancerForEditing(i,a).then((e=>{this.loadBalancer=this.appengineLoadBalancerTransformer.convertLoadBalancerToUpsertDescription(e),i.split&&!this.loadBalancer.splitDescription?this.loadBalancer.splitDescription=Se.convertTrafficSplitToTrafficSplitDescription(i.split):this.loadBalancer.splitDescription=i.splitDescription,this.loadBalancer.mapAllocationsToPercentages(),this.setTaskMonitor(),this.initializeFormValidation(),this.state.loading=!1})))}submit(){const e=N(this.loadBalancer);return e.mapAllocationsToDecimals(),delete e.serverGroups,this.forPipelineConfig?this.$uibModalInstance.close(e):this.taskMonitor.submit((()=>p.upsertLoadBalancer(e,this.application,"Update")))}cancel(){this.$uibModalInstance.dismiss()}showSubmitButton(){return this.wizardSubFormValidation.subFormsAreValid()}setTaskMonitor(){this.taskMonitor=new d({application:this.application,title:"Updating your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete()})}initializeFormValidation(){this.wizardSubFormValidation.config({form:"form",scope:this.$scope}).register({page:"basic-settings",subForm:"basicSettingsForm",validators:[{watchString:"ctrl.loadBalancer.splitDescription",validator:e=>100===e.allocationDescriptions.reduce(((e,n)=>e+n.allocation),0),watchDeep:!0}]}).register({page:"advanced-settings",subForm:"advancedSettingsForm"})}onTaskComplete(){this.application.getDataSource("loadBalancers").refresh(),this.application.getDataSource("loadBalancers").onNextRefresh(this.$scope,(()=>this.onApplicationRefresh()))}onApplicationRefresh(){if(this.$scope.$$destroyed)return;this.$uibModalInstance.dismiss();const e={name:this.loadBalancer.name,accountId:this.loadBalancer.credentials,region:this.loadBalancer.region,provider:"appengine"};this.$state.includes("**.loadBalancerDetails")?this.$state.go("^.loadBalancerDetails",e):this.$state.go(".loadBalancerDetails",e)}}ke.$inject=["$scope","$state","$uibModalInstance","application","loadBalancer","isNew","forPipelineConfig","appengineLoadBalancerTransformer","wizardSubFormValidation"];const $e="spinnaker.appengine.loadBalancer.wizard.controller";e($e,[]).controller("appengineLoadBalancerWizardCtrl",ke);class Be{constructor(e,n,t,a,i){this.$uibModal=e,this.$state=n,this.$scope=t,this.app=i,this.state={loading:!0},this.dispatchRules=[],this.loadBalancerFromParams=a,this.app.getDataSource("loadBalancers").ready().then((()=>this.extractLoadBalancer()))}editLoadBalancer(){this.$uibModal.open({templateUrl:"appengine/src/loadBalancer/configure/wizard/wizard.html",controller:"appengineLoadBalancerWizardCtrl as ctrl",size:"lg",resolve:{application:()=>this.app,loadBalancer:()=>N(this.loadBalancer),isNew:()=>!1,forPipelineConfig:()=>!1}})}deleteLoadBalancer(){const e={application:this.app,title:"Deleting "+this.loadBalancer.name};i.confirm({header:"Really delete "+this.loadBalancer.name+"?",buttonText:"Delete "+this.loadBalancer.name,body:this.getConfirmationModalBodyHtml(),account:this.loadBalancer.account,taskMonitorConfig:e,submitMethod:()=>{const e={cloudProvider:this.loadBalancer.cloudProvider,loadBalancerName:this.loadBalancer.name,credentials:this.loadBalancer.account};return p.deleteLoadBalancer(e,this.app)}})}canDeleteLoadBalancer(){return"default"!==this.loadBalancer.name}extractLoadBalancer(){this.loadBalancer=this.app.getDataSource("loadBalancers").data.find((e=>e.name===this.loadBalancerFromParams.name&&e.account===this.loadBalancerFromParams.accountId)),this.loadBalancer?(this.state.loading=!1,this.buildDispatchRules(),this.app.getDataSource("loadBalancers").onRefresh(this.$scope,(()=>this.extractLoadBalancer()))):this.autoClose()}buildDispatchRules(){this.dispatchRules=[],this.loadBalancer&&this.loadBalancer.dispatchRules&&this.loadBalancer.dispatchRules.forEach((e=>{e.service===this.loadBalancer.name&&this.dispatchRules.push(e.domain+e.path)}))}getConfirmationModalBodyHtml(){const e=this.loadBalancer.serverGroups.map((e=>e.name)),n=!!e&&e.length>0,t=!!e&&e.length>1;if(n){if(t){const n=e.map((e=>`<li>${e}</li>`)).join("");return`<div class="alert alert-warning">\n <p>\n Deleting <b>${this.loadBalancer.name}</b> will destroy the following server groups:\n <ul>\n ${n}\n </ul>\n </p>\n </div>\n `}return`<div class="alert alert-warning">\n <p>\n Deleting <b>${this.loadBalancer.name}</b> will destroy <b>${e[0]}</b>.\n </p>\n </div>\n `}return null}autoClose(){this.$scope.$$destroyed||(this.$state.params.allowModalToStayOpen=!0,this.$state.go("^",null,{location:"replace"}))}}Be.$inject=["$uibModal","$state","$scope","loadBalancer","app"];const xe="spinnaker.appengine.loadBalancerDetails.controller";e(xe,[]).controller("appengineLoadBalancerDetailsCtrl",Be),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/wizard.html",'<form name="form">\n <div ng-if="ctrl.state.loading && !ctrl.isNew" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{::ctrl.heading}}"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n ng-if="!ctrl.state.loading || ctrl.isNew"\n >\n <div ng-if="!ctrl.isNew">\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <appengine-load-balancer-basic-settings\n load-balancer="ctrl.loadBalancer"\n application="ctrl.application"\n for-pipeline-config="ctrl.forPipelineConfig"\n ></appengine-load-balancer-basic-settings>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" mark-complete-on-view="false">\n <appengine-load-balancer-advanced-settings\n load-balancer="ctrl.loadBalancer"\n ></appengine-load-balancer-advanced-settings>\n </v2-wizard-page>\n </div>\n </v2-modal-wizard>\n <appengine-load-balancer-message\n ng-if="ctrl.isNew"\n column-offset="1"\n columns="10"\n show-create-message="true"\n ></appengine-load-balancer-message>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n ng-if="!ctrl.isNew && ctrl.showSubmitButton()"\n label="ctrl.submitButtonLabel"\n is-disabled="appengineLoadBalancerForm.$invalid || ctrl.taskMonitor.submitting || ctrl.state.loading"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n >\n </submit-button>\n </div>\n</form>\n')}]);const Te="spinnaker.appengine.loadBalancer.module";e(Te,[me,xe,ge,ve,Ge,$e,Ce]);class De{}De.PLATFORM="App Engine Service";class Ee{constructor(e){this.$scope=e,e.platformHealth=De.PLATFORM}setStageRegion(){const e=this.$scope.accounts.find((e=>e.name===this.$scope.stage.credentials));e&&e.name&&g.getAccountDetails(e.name).then((e=>{this.$scope.stage.region=e.region}))}setStageCloudProvider(){this.$scope.stage.cloudProvider="appengine"}setAccounts(){return g.listAccounts("appengine").then((e=>{this.$scope.accounts=e}))}setTargets(){this.$scope.targets=s.TARGET_LIST,this.$scope.stage.target||(this.$scope.stage.target=this.$scope.targets[0].val)}setStageCredentials(){!this.$scope.stage.credentials&&this.$scope.application.defaultCredentials.appengine&&(this.$scope.stage.credentials=this.$scope.application.defaultCredentials.appengine)}}Ee.$inject=["$scope"];class Fe extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials()}}Fe.$inject=["$scope"];const Le="spinnaker.appengine.pipeline.stage.destroyAsgStage";e(Le,[]).config((()=>{u.pipeline.registerStage({provides:"destroyServerGroup",key:"destroyServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/destroyAsg/destroyAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",validators:[{type:"targetImpedance",message:"This pipeline will attempt to destroy a server group without deploying a new version into the same cluster."},{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("appengineDestroyAsgStageCtrl",Fe),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/destroyAsg/destroyAsgStage.html",'<div ng-controller="appengineDestroyAsgStageCtrl as destroyAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n single-region="true"\n disable-region-select="true"\n on-account-update="destroyAsgStageCtrl.setStageRegion()"\n component="stage"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",'<span class="task-label"> Destroy Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class Pe extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Pe.$inject=["$scope"];const Ie="spinnaker.appengine.pipeline.stage.disableAsgStage";e(Ie,[]).config((()=>{u.pipeline.registerStage({provides:"disableServerGroup",key:"disableServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/disableAsg/disableAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",validators:[{type:"targetImpedance",message:"This pipeline will attempt to disable a server group without deploying a new version into the same cluster."},{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("appengineDisableAsgStageCtrl",Pe),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/disableAsg/disableAsgStage.html",'<div ng-controller="appengineDisableAsgStageCtrl as disableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="disableAsgStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",'<span class="task-label"> Disable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class Oe{constructor(e,n,t){this.$uibModal=e,this.$uibModalInstance=n,this.application=t,this.state={loading:!0},this.initialize()}submit(){const e=m.getValue("appengine","loadBalancer"),n=this.$uibModal.open({templateUrl:e.createLoadBalancerTemplateUrl,controller:`${e.createLoadBalancerController} as ctrl`,size:"lg",resolve:{application:()=>this.application,loadBalancer:()=>N(this.selectedLoadBalancer),isNew:()=>!1,forPipelineConfig:()=>!0}}).result;this.$uibModalInstance.close(n)}cancel(){this.$uibModalInstance.dismiss()}initialize(){this.application.getDataSource("loadBalancers").ready().then((()=>{this.loadBalancers=this.application.loadBalancers.data.filter((e=>"appengine"===e.cloudProvider)),this.loadBalancers.length&&(this.selectedLoadBalancer=this.loadBalancers[0]),this.state.loading=!1}))}}Oe.$inject=["$uibModal","$uibModalInstance","application"];const Ne="spinnaker.appengine.loadBalancerChoiceModal.controller";e(Ne,[]).controller("appengineLoadBalancerChoiceModelCtrl",Oe);class ze{constructor(e,n){this.$scope=e,this.$uibModal=n,e.stage.loadBalancers=e.stage.loadBalancers||[],e.stage.cloudProvider="appengine"}addLoadBalancer(){this.$uibModal.open({templateUrl:"appengine/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html",controller:"appengineLoadBalancerChoiceModelCtrl as ctrl",resolve:{application:()=>this.$scope.application}}).result.then((e=>{this.$scope.stage.loadBalancers.push(e)})).catch((()=>{}))}editLoadBalancer(e){const n=m.getValue("appengine","loadBalancer");this.$uibModal.open({templateUrl:n.createLoadBalancerTemplateUrl,controller:`${n.createLoadBalancerController} as ctrl`,size:"lg",resolve:{application:()=>this.$scope.application,loadBalancer:()=>N(this.$scope.stage.loadBalancers[e]),isNew:()=>!1,forPipelineConfig:()=>!0}}).result.then((n=>{this.$scope.stage.loadBalancers[e]=n})).catch((()=>{}))}removeLoadBalancer(e){this.$scope.stage.loadBalancers.splice(e,1)}}ze.$inject=["$scope","$uibModal"];const Re="spinnaker.appengine.pipeline.stage.editLoadBalancerStage";e(Re,[Ne]).config((()=>{u.pipeline.registerStage({label:"Edit Load Balancer",description:"Edits a load balancer",key:"upsertAppEngineLoadBalancers",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html",executionConfigSections:["editLoadBalancerConfig","taskStatus"],controller:"appengineEditLoadBalancerStageCtrl",controllerAs:"editLoadBalancerStageCtrl",validators:[]})})).controller("appengineEditLoadBalancerStageCtrl",ze),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html",'<div modal-page>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Select Load Balancer</h4>\n </div>\n <div class="modal-body" ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div class="modal-body" ng-if="!ctrl.state.loading">\n <div class="alert alert-warning" ng-if="ctrl.loadBalancers.length === 0">\n <p>This application has no App Engine load balancers.</p>\n </div>\n <form\n role="form"\n name="form"\n class="form-horizontal"\n ng-submit="ctrl.submit()"\n ng-if="ctrl.loadBalancers.length > 0"\n >\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b>Load Balancer</b>\n </div>\n <div class="col-md-7">\n <ui-select class="form-control input-sm" ng-model="ctrl.selectedLoadBalancer">\n <ui-select-match>\n <account-tag account="$select.selected.account"></account-tag>\n <span style="margin-left: 5px">{{$select.selected.name}}</span>\n </ui-select-match>\n <ui-select-choices repeat="loadBalancer in ctrl.loadBalancers | filter: $select.search">\n <account-tag account="loadBalancer.account"></account-tag>\n <span style="margin-left: 5px" ng-bind-html="loadBalancer.name"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n </form>\n </div>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <button class="btn btn-primary" ng-if="ctrl.loadBalancers.length > 0" ng-click="ctrl.submit()">\n <span class="far fa-check-circle"></span> Edit\n </button>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html",'<div class="well well-sm clearfix" ng-if="!pipeline.strategy">\n <div class="row">\n <div class="col-md-12">\n <h4 class="text-left">Load Balancers</h4>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Account</th>\n <th>Name</th>\n <th>Region</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="loadBalancer in stage.loadBalancers">\n <td>\n <account-tag account="loadBalancer.credentials"></account-tag>\n </td>\n <td>{{ loadBalancer.name }}</td>\n <td>{{ loadBalancer.region }}</td>\n <td class="condensed-actions">\n <a class="btn btn-sm btn-link" href ng-click="editLoadBalancerStageCtrl.editLoadBalancer($index)">\n <span class="glyphicon glyphicon-edit" uib-tooltip="Edit"></span\n ></a>\n <a\n class="btn btn-sm btn-link pad-left"\n href\n ng-click="editLoadBalancerStageCtrl.removeLoadBalancer($index)"\n >\n <span class="glyphicon glyphicon-trash" uib-tooltip="Remove"></span>\n </a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="8">\n <button class="btn btn-block btn-sm add-new" ng-click="editLoadBalancerStageCtrl.addLoadBalancer()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add load balancer\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html",'<div ng-controller="BaseExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'editLoadBalancerConfig\'">\n <div class="row">\n <div class="col-md-12">\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Account</th>\n <th>Name</th>\n <th>Region</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="loadBalancer in stage.context.loadBalancers">\n <td>\n <account-tag account="loadBalancer.credentials"></account-tag>\n </td>\n <td>{{ loadBalancer.name }}</td>\n <td>{{ loadBalancer.region }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]);class Me extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Me.$inject=["$scope"];const Ue="spinnaker.appengine.pipeline.stage.enableAsgStage";e(Ue,[]).config((()=>{u.pipeline.registerStage({provides:"enableServerGroup",key:"enableServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/enableAsg/enableAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("appengineEnableAsgStageCtrl",Me),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/enableAsg/enableAsgStage.html",'<div ng-controller="appengineEnableAsgStageCtrl as enableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="enableAsgStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",'<span class="task-label"> Enable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class He extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setStageCredentials();const n=e.stage;void 0===n.shrinkToSize&&(n.shrinkToSize=1),void 0===n.allowDeleteActive&&(n.allowDeleteActive=!1),void 0===n.retainLargerOverNewer&&(n.retainLargerOverNewer="false"),n.retainLargerOverNewer=n.retainLargerOverNewer.toString()}pluralize(e,n){return 1===n?e:e+"s"}}He.$inject=["$scope"];const qe="spinnaker.appengine.pipeline.stage.shrinkClusterStage";e(qe,[]).config((function(){u.pipeline.registerStage({provides:"shrinkCluster",key:"shrinkCluster",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"shrinkToSize",fieldLabel:"shrink to [X] Server Groups"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("appengineShrinkClusterStageCtrl",He),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",'<div ng-controller="appengineShrinkClusterStageCtrl as shrinkClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="shrinkClusterStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Shrink Options">\n <div class="form-inline">\n Shrink to\n <input\n type="number"\n min="0"\n required\n ng-model="stage.shrinkToSize"\n class="form-control input-sm"\n style="width: 50px"\n />\n {{shrinkClusterStageCtrl.pluralize(\'server group\', stage.shrinkToSize)}}, keeping the\n <select class="form-control input-sm" ng-model="stage.retainLargerOverNewer" style="width: 100px">\n <option value="true">largest</option>\n <option value="false">newest</option>\n </select>\n </div>\n </stage-config-field>\n <div class="form-group">\n <div class="col-md-offset-3 col-md-6 checkbox">\n <label>\n <input type="checkbox" ng-model="stage.allowDeleteActive" />\n Allow deletion of active server groups\n </label>\n </div>\n </div>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]);class Ve extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Ve.$inject=["$scope"];const je="spinnaker.appengine.pipeline.stage.startServerGroupStage";e(je,[]).config((()=>{u.pipeline.registerStage({label:"Start Server Group",description:"Starts a server group.",key:"startAppEngineServerGroup",templateUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html",executionConfigSections:["startServerGroupConfig","taskStatus"],executionStepLabelUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html",controller:"appengineStartServerGroupStageCtrl",controllerAs:"startServerGroupStageCtrl",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}],cloudProvider:"appengine"})})).controller("appengineStartServerGroupStageCtrl",Ve),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupStage.html",'<div ng-controller="appengineStartServerGroupStageCtrl as startServerGroupStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="startServerGroupStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html",'<div ng-controller="BaseExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'startServerGroupConfig\'">\n <div class="row">\n <div class="col-md-9">\n <dl class="dl-narrow dl-horizontal">\n <dt>Account</dt>\n <dd>\n <account-tag account="stage.context.credentials"></account-tag>\n </dd>\n <dt>Region</dt>\n <dd>{{stage.context.region}}</dd>\n <dt>Server Group</dt>\n <dd>{{stage.context.serverGroupName}}</dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html",'<span class="task-label"> Start Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class We extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}We.$inject=["$scope"];const _e="spinnaker.appengine.pipeline.stage.stopServerGroupStage";e(_e,[]).config((()=>{u.pipeline.registerStage({label:"Stop Server Group",description:"Stops a server group.",key:"stopAppEngineServerGroup",templateUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html",executionConfigSections:["stopServerGroupConfig","taskStatus"],executionStepLabelUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html",controller:"appengineStopServerGroupStageCtrl",controllerAs:"stopServerGroupStageCtrl",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}],cloudProvider:"appengine"})})).controller("appengineStopServerGroupStageCtrl",We),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html",'<div ng-controller="appengineStopServerGroupStageCtrl as stopServerGroupStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="stopServerGroupStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html",'<div ng-controller="appengineStopServerGroupExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'stopServerGroupConfig\'">\n <div class="row">\n <div class="col-md-9">\n <dl class="dl-narrow dl-horizontal">\n <dt>Account</dt>\n <dd>\n <account-tag account="stage.context.credentials"></account-tag>\n </dd>\n <dt>Region</dt>\n <dd>{{stage.context.region}}</dd>\n <dt>Server Group</dt>\n <dd>{{stage.context.serverGroupName}}</dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html",'<span class="task-label"> Stop Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const Je="spinnaker.appengine.pipeline.module";e(Je,[Le,Ie,Re,Ue,qe,je,_e]);class Ke extends Q.Component{constructor(e){super(e),this.destroy$=new X,this.setRegionList=e=>{const{application:n}=this.props,t=n=>!n||n.account===e;Z(n.ready()).pipe(ee(this.destroy$)).subscribe((()=>{const e=c.getRegions([n],t);e.sort(),this.setState({availableRegions:e})}))},this.accountChanged=e=>{this.setRegionList(e)};const n=e.credentialsField||"credentials";this.state={availableRegions:[],cloudProvider:e.cloudProvider,componentName:e.componentName||"",credentialsField:n}}componentDidMount(){const{componentName:e,formik:n}=this.props,{credentialsField:t}=this.state,a=j(n.values,e?`${e}.${t}`:`${t}`,void 0);this.setRegionList(a)}componentWillUnmount(){this.destroy$.next()}render(){const{accounts:e}=this.props,{credentialsField:n,availableRegions:t,componentName:a}=this.state;return Q.createElement("div",{className:"col-md-9"},Q.createElement("div",{className:"sp-margin-m-bottom"},Q.createElement(h,{name:a?`${a}.${n}`:`${n}`,label:"Account",input:n=>Q.createElement(v,{...n,stringOptions:e&&e.map((e=>e.name)),clearable:!1}),onChange:this.accountChanged,required:!0})),Q.createElement("div",{className:"sp-margin-m-bottom"},Q.createElement(h,{name:a?`${a}.region`:"region",label:"Region",input:e=>Q.createElement(v,{...e,stringOptions:t,clearable:!1}),required:!0})))}}const Ye=class extends Q.Component{constructor(){super(...arguments),this.destroy$=new X,this.state={accounts:[]},this.onTemplateArtifactEdited=(e,n)=>{this.props.formik.setFieldValue(`${n}.id`,null),this.props.formik.setFieldValue(`${n}.artifact`,e),this.props.formik.setFieldValue(`${n}.account`,e.artifactAccount)},this.onTemplateArtifactSelected=(e,n)=>{this.props.formik.setFieldValue(`${n}.id`,e),this.props.formik.setFieldValue(`${n}.artifact`,null)},this.removeInputArtifact=e=>{this.props.formik.setFieldValue(e,null)},this.getInputArtifact=(e,n)=>e[n]?e[n]:{account:"",id:""}}componentDidMount(){Z(g.listAccounts("appengine")).pipe(ee(this.destroy$)).subscribe((e=>this.setState({accounts:e})))}render(){const e=this.props.formik.values,n=this.state.accounts;return Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-0 col-md-9"},Q.createElement("h4",null,"Basic Settings")),Q.createElement("div",null,Q.createElement(Ke,{componentName:"",accounts:n,application:this.props.application,cloudProvider:"appengine",credentialsField:"account",formik:this.props.formik})),Q.createElement("div",{className:"col-md-offset-0 col-md-9"},Q.createElement("h4",null,"Configuration Settings")),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(e,"cronArtifact").artifact,excludedArtifactTypePatterns:Ye.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(e,"cronArtifact").id,label:"Cron Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"cronArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"cronArtifact"),pipeline:this.props.pipeline,stage:e})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("cronArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(e,"dispatchArtifact").artifact,excludedArtifactTypePatterns:Ye.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(e,"dispatchArtifact").id,label:"Dispatch Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"dispatchArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"dispatchArtifact"),pipeline:this.props.pipeline,stage:e})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("dispatchArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(e,"indexArtifact").artifact,excludedArtifactTypePatterns:Ye.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(e,"indexArtifact").id,label:"Index Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"indexArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"indexArtifact"),pipeline:this.props.pipeline,stage:e})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("indexArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(e,"queueArtifact").artifact,excludedArtifactTypePatterns:Ye.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(e,"queueArtifact").id,label:"Queue Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"queueArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"queueArtifact"),pipeline:this.props.pipeline,stage:e})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("queueArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))))}};let Qe=Ye;function Xe(e){const n=new S(e);return n.field("account").required(),n.field("region").required(),n.validateForm()}Qe.excludedArtifactTypes=b(y.BITBUCKET_FILE,y.CUSTOM_OBJECT,y.EMBEDDED_BASE64,y.GCS_OBJECT,y.GITHUB_FILE,y.GITLAB_FILE,y.S3_OBJECT,y.HTTP_FILE,y.ORACLE_OBJECT);u.pipeline.registerStage({label:"Deploy App Engine Configuration",description:"Deploy index, dispatch, cron, and queue configuration to App Engine.",key:"deployAppEngineConfiguration",component:function({application:e,pipeline:n,stage:t,updateStage:a}){const i=Q.useMemo((()=>({...N(t)})),[]);return Q.createElement(C,{application:e,onChange:a,pipeline:n,stage:i,validate:Xe,render:e=>Q.createElement(Qe,{...e})})},producesArtifacts:!1,cloudProvider:"appengine",executionDetailsSections:[w,G],validateFn:Xe});const Ze=A.providers.appengine||{defaults:{}};var en,nn;Ze&&(Ze.resetToOriginal=A.resetProvider("appengine")),(nn=en||(en={})).GCS="gcs",nn.GIT="git",nn.ARTIFACT="artifact",nn.CONTAINER_IMAGE="containerImage";const tn=class{constructor(e){this.$q=e}static getTriggerOptions(e){return(e.triggers||[]).filter((e=>"git"===e.type||"jenkins"===e.type||"travis"===e.type)).map((e=>"git"===e.type?{source:e.source,project:e.project,slug:e.slug,branch:e.branch,type:"git"}:{master:e.master,job:e.job,type:e.type}))}static getExpectedArtifacts(e){return e.expectedArtifacts||[]}buildNewServerGroupCommand(e,n,t="create"){null==n&&(n="appengine");const a={accounts:g.getAllAccountDetailsForProvider("appengine"),storageAccounts:k.getStorageAccounts()},i={mode:t,submitButtonLabel:this.getSubmitButtonLabel(t),disableStrategySelection:"create"===t};return this.$q.all(a).then((t=>{const a=this.getCredentials(t.accounts),r=this.getRegion(t.accounts,a);return{application:e.name,backingData:t,viewState:i,fromArtifact:!1,credentials:a,region:r,selectedProvider:n,interestingHealthProviderNames:[],sourceType:en.GIT}}))}buildServerGroupCommandFromExisting(e,n){return this.buildNewServerGroupCommand(e,"appengine","clone").then((e=>(e.stack=n.stack,e.freeFormDetails=n.detail,e)))}buildNewServerGroupCommandForPipeline(e,n){return this.$q.when({viewState:{pipeline:n,stage:e},backingData:{triggerOptions:tn.getTriggerOptions(n),expectedArtifacts:tn.getExpectedArtifacts(n)}})}buildServerGroupCommandFromPipeline(e,n,t,a){return this.buildNewServerGroupCommand(e,"appengine","editPipeline").then((e=>e={...e,...n,backingData:{...e.backingData,triggerOptions:tn.getTriggerOptions(a),expectedArtifacts:tn.getExpectedArtifacts(a)},credentials:n.account||e.credentials,viewState:{...e.viewState,stage:t,pipeline:a}}))}getCredentials(e){const n=(e||[]).map((e=>e.name)),t=Ze.defaults.account;return n.includes(t)?t:n[0]}getRegion(e,n){const t=e.find((e=>e.name===n));return t?t.region:null}getSubmitButtonLabel(e){switch(e){case"createPipeline":return"Add";case"editPipeline":return"Done";case"clone":return"Clone";default:return"Create"}}};let an=tn;an.$inject=["$q"];const rn="spinnaker.appengine.serverGroupCommandBuilder.service";e(rn,[]).service("appengineServerGroupCommandBuilder",an);class on{constructor(e,t,a,i){this.$scope=e,this.excludedGcsArtifactTypes=b(y.GCS_OBJECT),this.excludedContainerArtifactTypes=b(y.DOCKER_IMAGE),this.onExpectedArtifactEdited=e=>{this.$scope.$applyAsync((()=>{this.$scope.command.expectedArtifactId=null,this.$scope.command.expectedArtifact=e}))},this.onExpectedArtifactSelected=e=>{this.onChangeExpectedArtifactId(e.id)},this.onChangeExpectedArtifactId=e=>{this.$scope.$applyAsync((()=>{this.$scope.command.expectedArtifactId=e,this.$scope.command.expectedArtifact=null}))},this.onExpectedArtifactAccountSelected=e=>{this.$scope.$applyAsync((()=>{this.$scope.command.storageAccountName=e}))},n(this,a("BasicSettingsMixin",{$scope:e,imageReader:null,$uibModalStack:i,$state:t})),this.$scope.command.gitCredentialType||this.onAccountChange(),this.$scope.containerArtifactDelegate=new $(e,[y.DOCKER_IMAGE]),this.$scope.containerArtifactController=new B(this.$scope.containerArtifactDelegate),this.$scope.gcsArtifactDelegate=new $(e,[y.GCS_OBJECT]),this.$scope.gcsArtifactController=new B(this.$scope.gcsArtifactDelegate)}isGitSource(){return this.$scope.command.sourceType===en.GIT}isGcsSource(){return this.$scope.command.sourceType===en.GCS}isContainerImageSource(){return this.$scope.command.sourceType===en.CONTAINER_IMAGE}toggleResolveViaTrigger(){this.$scope.command.fromTrigger=!this.$scope.command.fromTrigger,delete this.$scope.command.trigger,delete this.$scope.command.branch}onTriggerChange(){_(this,"$scope.command.trigger.matchBranchOnRegex",void 0)}onAccountChange(){const e=this.findAccountInBackingData();e?(this.$scope.command.gitCredentialType=this.getSupportedGitCredentialTypes()[0],this.$scope.command.region=e.region):(this.$scope.command.gitCredentialType="NONE",delete this.$scope.command.region)}getSupportedGitCredentialTypes(){const e=this.findAccountInBackingData();return e&&e.supportedGitCredentialTypes?e.supportedGitCredentialTypes:["NONE"]}humanReadableGitCredentialType(e){switch(e){case"HTTPS_USERNAME_PASSWORD":return"HTTPS with username and password";case"HTTPS_GITHUB_OAUTH_TOKEN":return"HTTPS with Github OAuth token";case"SSH":return"SSH";default:return"No credentials"}}findAccountInBackingData(){return this.$scope.command.backingData.accounts.find((e=>this.$scope.command.credentials===e.name))}}on.$inject=["$scope","$state","$controller","$uibModalStack"];const ln="spinnaker.appengine.basicSettings.controller";e(ln,[]).controller("appengineServerGroupBasicSettingsCtrl",on);Ae(".appengine-server-group-wizard input[type='checkbox'] {\n margin-top: 0.75rem;\n}\n.appengine-server-group-wizard help-field.help-field-absolute span {\n position: absolute;\n top: 7px;\n right: 2px;\n}\n.appengine-server-group-wizard .artifact-configuration-section {\n border-bottom: 1px solid #eee;\n padding: 0 0 4px 0;\n margin: 0 0 5px 0;\n}\n.appengine-server-group-wizard .artifact-configuration-section.last-entry {\n border-bottom: none;\n}\n.appengine-server-group-wizard .artifact-configuration-section .Select,\n.appengine-server-group-wizard .artifact-configuration-section input {\n margin: 0 0 1px;\n}\n");class sn{constructor(e,n={id:"",account:""}){var t;const a={configurable:!1,enumerable:!1,writable:!1};this.id=null==n?void 0:n.id,this.account=n.account||(null==(t=null==n?void 0:n.artifact)?void 0:t.artifactAccount),this.artifact=null==n?void 0:n.artifact,Object.defineProperty(this,"$scope",{...a,value:e});const i=new x(this),r=new B(i);Object.defineProperty(this,"delegate",{...a,value:i}),Object.defineProperty(this,"controller",{...a,value:r})}}class cn{constructor(e){this.$scope=e,this.artifactAccounts=[],this.updateConfigArtifacts=e=>{this.$scope.$applyAsync((()=>{this.command.configArtifacts=e}))}}$onInit(){this.command.configFiles||(this.command.configFiles=[]),this.command.configArtifacts||(this.command.configArtifacts=[]),this.$scope.command||(this.$scope.command=this.command),this.command.configArtifacts=this.command.configArtifacts.map((e=>new sn(this.$scope,e))),g.getArtifactAccounts().then((e=>{this.artifactAccounts=e,this.command.configArtifacts.forEach((n=>{n.delegate.setAccounts(e),n.controller.updateAccounts(n.delegate.getSelectedExpectedArtifact())}))}))}addConfigFile(){this.command.configFiles.push("")}addConfigArtifact(){const e=new sn(this.$scope,{id:"",account:""});e.delegate.setAccounts(this.artifactAccounts),e.controller.updateAccounts(e.delegate.getSelectedExpectedArtifact()),this.command.configArtifacts.push(e)}deleteConfigFile(e){this.command.configFiles.splice(e,1)}deleteConfigArtifact(e){this.command.configArtifacts.splice(e,1)}mapTabToSpaces(e){if(9===e.which){e.preventDefault();const n=e.target.selectionStart,t=e.target.value;e.target.value=`${t.substring(0,n)} ${t.substring(n)}`,e.target.selectionStart+=2}}isContainerImageSource(){return this.command.sourceType===en.CONTAINER_IMAGE}}cn.$inject=["$scope"];const pn={bindings:{command:"="},controller:cn,templateUrl:"appengine/src/serverGroup/configure/wizard/configFiles.component.html"},dn="spinnaker.appengine.configFileConfigurer.component";e(dn,[]).component("appengineConfigFileConfigurer",pn),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/configFiles.component.html",'<div class="form-horizontal container-fluid">\n <div class="form-group" ng-if="!$ctrl.isContainerImageSource()">\n <div class="col-md-3 sm-label-right">\n Application Root\n <help-field class="help-field-absolute" key="appengine.serverGroup.applicationDirectoryRoot"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm"\n name="applicationDirectoryRoot"\n ng-model="$ctrl.command.applicationDirectoryRoot"\n />\n </div>\n </div>\n\n <div class="form-group" ng-if="!$ctrl.isContainerImageSource()">\n <div class="col-md-3 sm-label-right">\n Config Filepaths <help-field key="appengine.serverGroup.configFilepaths"></help-field>\n </div>\n <div class="col-md-7">\n <ui-select\n multiple\n tagging\n tagging-label=""\n style="width: 380px"\n name="configFilepaths"\n ng-model="$ctrl.command.configFilepaths"\n class="form-control input-sm"\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="filepath in []">\n <span ng-bind-html="filepath"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Config Files\n <span ng-if="!$ctrl.isContainerImageSource()">\n <help-field key="appengine.serverGroup.configFiles"></help-field>\n </span>\n <span ng-if="$ctrl.isContainerImageSource()">\n <help-field key="appengine.serverGroup.configFilesRequired"></help-field>\n </span>\n </div>\n <div ng-repeat="configFile in $ctrl.command.configFiles track by $index">\n <div class="col-md-7" ng-class="{ \'col-md-offset-3\': $index > 0 }" style="margin-top: 5px">\n <textarea\n cols="46"\n rows="10"\n class="small"\n spellcheck="false"\n ng-keydown="$ctrl.mapTabToSpaces($event)"\n style="font-family: Menlo, Monaco, Consolas, \'Courier New\', monospace"\n ng-model="$ctrl.command.configFiles[$index]"\n ></textarea>\n </div>\n <div class="col-md-1" style="margin-top: 5px">\n <button type="button" class="btn btn-sm btn-default" ng-click="$ctrl.deleteConfigFile($index)">\n <span class="glyphicon glyphicon-trash"></span> Delete\n </button>\n </div>\n </div>\n <div class="col-md-7" ng-class="{ \'col-md-offset-3\': $ctrl.command.configFiles.length > 0 }">\n <button class="btn btn-block btn-add-trigger add-new" ng-click="$ctrl.addConfigFile()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Config File\n </button>\n </div>\n <config-file-artifact-list\n ng-if="$ctrl.command.viewState.pipeline"\n config-artifacts="$ctrl.command.configArtifacts"\n pipeline="$ctrl.command.viewState.pipeline"\n stage="$ctrl.command.viewState.stage"\n update-config-artifacts="$ctrl.updateConfigArtifacts"\n >\n </config-file-artifact-list>\n </div>\n</div>\n')}]);const gn={bindings:{trigger:"<"},template:'\n <span ng-if="$ctrl.trigger.type === \'git\'">\n Resolved at runtime by <b>{{$ctrl.trigger.source}}</b> trigger: {{$ctrl.trigger.project}}/{{$ctrl.trigger.slug}}<span ng-if="$ctrl.trigger.branch">:{{$ctrl.trigger.branch}}</span>\n </span>\n <span ng-if="$ctrl.trigger.type === \'jenkins\'">\n Resolved at runtime by <b>Jenkins</b> trigger: {{$ctrl.trigger.master}}/{{$ctrl.trigger.job}}\n </span>\n '},un="spinnaker.appengine.dynamicBranchLabel.component";e(un,[]).component("appengineDynamicBranchLabel",gn);class mn{constructor(e,n,t,a,i,r){this.$scope=e,this.$uibModalInstance=n,this.serverGroupCommand=t,this.application=a,this.serverGroupWriter=i,this.pages={basicSettings:"appengine/src/serverGroup/configure/wizard/basicSettings.html",advancedSettings:"appengine/src/serverGroup/configure/wizard/advancedSettings.html"},this.state={loading:!0},["create","clone","editPipeline"].includes(j(t,"viewState.mode"))?(this.$scope.command=t,this.state.loading=!1,this.initialize()):r.buildNewServerGroupCommand(a,"appengine","createPipeline").then((e=>{this.$scope.command=J(e,t),this.$scope.command.viewState.pipeline=t.viewState.pipeline,this.$scope.command.viewState.stage=t.viewState.stage,this.state.loading=!1,this.initialize()}))}cancel(){this.$uibModalInstance.dismiss()}submit(){const e=this.$scope.command.viewState.mode;if(["editPipeline","createPipeline"].includes(e))return this.$uibModalInstance.close(this.$scope.command);{const e=t(this.$scope.command);e.viewState.mode="create";const n=()=>this.serverGroupWriter.cloneServerGroup(e,this.$scope.application);return this.taskMonitor.submit(n),null}}initialize(){this.$scope.application=this.application,this.taskMonitor=new d({application:this.application,title:"Creating your server group",modalInstance:this.$uibModalInstance}),this.$scope.showPlatformHealthOnlyOverride=this.application.attributes.platformHealthOnlyShowOverride,this.$scope.platformHealth=De.PLATFORM,this.application.attributes.platformHealthOnly&&(this.$scope.command.interestingHealthProviderNames=[De.PLATFORM])}}mn.$inject=["$scope","$uibModalInstance","serverGroupCommand","application","serverGroupWriter","appengineServerGroupCommandBuilder"];const hn="spinnaker.appengine.cloneServerGroup.controller";e(hn,[T,un,dn]).controller("appengineCloneServerGroupCtrl",mn),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/basicSettings.html",'<div class="container-fluid form-horizontal" ng-controller="appengineServerGroupBasicSettingsCtrl as basicSettingsCtrl">\n <ng-form name="basicSettings">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Account</div>\n <div class="col-md-7">\n <account-select-field\n read-only="command.viewState.readOnlyFields.credentials"\n component="command"\n field="credentials"\n on-change="basicSettingsCtrl.onAccountChange()"\n accounts="command.backingData.accounts"\n provider="\'appengine\'"\n ></account-select-field>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Region</div>\n <div class="col-md-7">\n <input type="text" readonly class="form-control input-sm" name="region" ng-model="command.region" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Stack\n <help-field key="aws.serverGroup.stack"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm no-spel"\n ng-pattern="basicSettingsCtrl.stackPattern"\n name="stack"\n ng-model="command.stack"\n />\n </div>\n </div>\n <div class="form-group row slide-in" ng-if="basicSettings.stack.$error.pattern">\n <div class="col-sm-9 col-sm-offset-2 error-message">\n <span>Only dot(.) and underscore(_) special characters are allowed in the Stack field.</span>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Detail\n <help-field key="aws.serverGroup.detail"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm no-spel"\n ng-pattern="basicSettingsCtrl.detailPattern"\n name="details"\n ng-model="command.freeFormDetails"\n />\n </div>\n </div>\n\n <div class="form-group row slide-in" ng-if="basicSettings.details.$error.pattern">\n <div class="col-sm-9 col-sm-offset-2 error-message">\n <span>Only dot(.), underscore(_), and dash(-) special characters are allowed in the Detail field.</span>\n </div>\n </div>\n\n <div class="form-group row">\n <label class="col-md-3 sm-label-right">Source Type</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="git" /> Git </label>\n </div>\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="gcs" /> GCS </label>\n </div>\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="containerImage" /> Container Image </label>\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isGcsSource()">\n <div class="form-group row">\n <label class="col-md-3 sm-label-right">Resolve URL</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.fromArtifact" ng-value="false" /> via text input </label>\n </div>\n <div class="radio radio-inline" ng-if="command.viewState.pipeline">\n <label>\n <input type="radio" ng-model="command.fromArtifact" ng-value="true" /> via pipeline artifact\n </label>\n </div>\n </div>\n </div>\n <stage-artifact-selector-delegate\n ng-if="command.fromArtifact"\n artifact="command.expectedArtifact"\n excluded-artifact-type-patterns="basicSettingsCtrl.excludedGcsArtifactTypes"\n expected-artifact-id="command.expectedArtifactId"\n field-columns="7"\n label="\'Expected Artifact\'"\n on-artifact-edited="basicSettingsCtrl.onExpectedArtifactEdited"\n on-expected-artifact-selected="basicSettingsCtrl.onExpectedArtifactSelected"\n pipeline="command.viewState.pipeline"\n stage="command.viewState.stage"\n >\n </stage-artifact-selector-delegate>\n <div class="form-group" ng-if="!command.fromArtifact">\n <div class="col-md-3 sm-label-right">\n GCS URL\n <help-field class="help-field-absolute" key="appengine.serverGroup.gcs.repositoryUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input type="text" required class="form-control input-sm" name="gcsUrl" ng-model="command.repositoryUrl" />\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isGitSource()">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Git Repository URL\n <help-field class="help-field-absolute" key="appengine.serverGroup.git.repositoryUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input type="text" required class="form-control input-sm" name="gitRepo" ng-model="command.repositoryUrl" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Git Credential Type\n <help-field class="help-field-absolute" key="appengine.serverGroup.gitCredentialType"></help-field>\n </div>\n <div class="col-md-7">\n <select\n class="form-control input-sm"\n ng-options="basicSettingsCtrl.humanReadableGitCredentialType(type) for type in basicSettingsCtrl.getSupportedGitCredentialTypes()"\n ng-model="command.gitCredentialType"\n ></select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Branch <help-field key="appengine.serverGroup.branch"></help-field></div>\n <div class="col-md-7">\n <input\n ng-if="!command.fromTrigger"\n type="text"\n required\n class="form-control input-sm"\n name="branch"\n ng-model="command.branch"\n />\n\n <ui-select\n ng-if="command.fromTrigger"\n ng-model="command.trigger"\n class="form-control input-sm"\n on-select="basicSettingsCtrl.onTriggerChange()"\n required\n >\n <ui-select-match allow-clear>\n <appengine-dynamic-branch-label trigger="command.trigger"></appengine-dynamic-branch-label>\n </ui-select-match>\n <ui-select-choices repeat="trigger in command.backingData.triggerOptions">\n <appengine-dynamic-branch-label trigger="trigger"></appengine-dynamic-branch-label>\n </ui-select-choices>\n </ui-select>\n </div>\n\n <div\n class="col-md-7 col-md-offset-3"\n ng-if="command.viewState.mode === \'createPipeline\' || command.viewState.mode === \'editPipeline\'"\n >\n <span class="pull-right small" ng-if="!command.fromTrigger">\n <a href ng-click="basicSettingsCtrl.toggleResolveViaTrigger()">Resolve via trigger</a>\n </span>\n <span class="pull-right small" ng-if="command.fromTrigger">\n <a href ng-click="basicSettingsCtrl.toggleResolveViaTrigger()">Click for text input</a>\n </span>\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isContainerImageSource()">\n <div class="form-group">\n <label class="col-md-3 sm-label-right">Resolve URL</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.fromArtifact" ng-value="false" /> via text input </label>\n </div>\n <div class="radio radio-inline" ng-if="command.viewState.pipeline">\n <label>\n <input type="radio" ng-model="command.fromArtifact" ng-value="true" /> via pipeline artifact\n </label>\n </div>\n </div>\n </div>\n <stage-artifact-selector-delegate\n ng-if="command.fromArtifact"\n artifact="command.expectedArtifact"\n excluded-artifact-type-patterns="basicSettingsCtrl.excludedContainerArtifactTypes"\n expected-artifact-id="command.expectedArtifactId"\n field-columns="7"\n label="\'Expected Artifact\'"\n on-artifact-edited="basicSettingsCtrl.onExpectedArtifactEdited"\n on-expected-artifact-selected="basicSettingsCtrl.onExpectedArtifactSelected"\n pipeline="command.viewState.pipeline"\n stage="command.viewState.stage"\n >\n </stage-artifact-selector-delegate>\n <div class="form-group" ng-if="!command.fromArtifact">\n <div class="col-md-3 sm-label-right">\n Image URL\n <help-field key="appengine.serverGroup.containerImageUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n required\n class="form-control input-sm"\n name="containerImageUrl"\n ng-model="command.containerImageUrl"\n />\n </div>\n </div>\n </div>\n\n <div ng-if="command.trigger.type === \'jenkins\'" class="form-group">\n <div class="col-md-7 col-md-offset-3">\n <div class="form-inline">\n <small>Match branch from trigger on regex</small>\n <help-field key="appengine.serverGroup.matchBranchOnRegex"></help-field>\n <input\n type="text"\n style="width: 140px"\n class="form-control input-sm pull-right"\n name="matchOnRegex"\n ng-model="command.trigger.matchBranchOnRegex"\n />\n </div>\n </div>\n </div>\n\n <deployment-strategy-selector\n field-columns="7"\n ng-if="!command.viewState.disableStrategySelection"\n command="command"\n ></deployment-strategy-selector>\n\n <div class="form-group" ng-if="!command.viewState.hideClusterNamePreview">\n <div class="col-md-12">\n <div class="well-compact" ng-class="basicSettingsCtrl.showPreviewAsWarning() ? \'alert alert-warning\' : \'well\'">\n <h5 class="text-center">\n <p>Your server group will be in the cluster:</p>\n <p>\n <strong>\n {{basicSettingsCtrl.getNamePreview()}}\n <span ng-if="basicSettingsCtrl.createsNewCluster()"> (new cluster)</span>\n </strong>\n </p>\n <div\n class="text-left"\n ng-if="!basicSettingsCtrl.createsNewCluster() && command.viewState.mode === \'create\' && latestServerGroup"\n >\n <p>There is already a server group in this cluster. Do you want to clone it?</p>\n <p>\n Cloning copies the entire configuration from the selected server group, allowing you to modify whichever\n fields (e.g. image) you need to change in the new server group.\n </p>\n <p>\n To clone a server group, select "Clone" from the "Server Group Actions" menu in the details view of the\n server group.\n </p>\n <p>\n <a href ng-click="basicSettingsCtrl.navigateToLatestServerGroup()">\n Go to details for {{latestServerGroup.name}}\n </a>\n </p>\n </div>\n </h5>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/advancedSettings.html",'<div class="form-horizontal container-fluid">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Promote <help-field key="appengine.serverGroup.promote"></help-field></div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.promote" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Stop Previous Version <help-field key="appengine.serverGroup.stopPreviousVersion"></help-field>\n </div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.stopPreviousVersion" />\n </div>\n </div>\n <div class="form-group" ng-if="showPlatformHealthOnlyOverride">\n <div class="col-md-4 sm-label-right">Task Completion</div>\n <div class="col-md-7">\n <platform-health-override command="command" platform-health-type="platformHealth"> </platform-health-override>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Suppress Version String\n <help-field key="appengine.serverGroup.suppress-version-string"></help-field>\n </div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.suppressVersionString" />\n </div>\n </div>\n</div>\n')}]);const vn="spinnaker.appengine.configFileArtifactList.component";e(vn,[]).component("configFileArtifactList",ne(E((e=>{const n=(n,t)=>{const a=[...e.configArtifacts];a.splice(t,1,{...a[t],id:n,artifact:null}),e.updateConfigArtifacts(a)};return Q.createElement(Q.Fragment,null,e.configArtifacts.map(((t,a)=>Q.createElement("div",{key:t.id,className:te("artifact-configuration-section col-md-12",{"last-entry":e.configArtifacts.length-1===a})},Q.createElement("div",{className:"col-md-9"},Q.createElement(D,{artifact:t.artifact,excludedArtifactTypePatterns:[],expectedArtifactId:null==t.artifact?t.id:null,onArtifactEdited:n=>{((n,t)=>{const a=[...e.configArtifacts];a.splice(t,1,{...a[t],id:null,artifact:n}),e.updateConfigArtifacts(a)})(n,a)},onExpectedArtifactSelected:e=>{((e,t)=>{n(e.id,t)})(e,a)},pipeline:e.pipeline,stage:e.stage})),Q.createElement("div",{className:"col-md-1"},Q.createElement("button",{type:"button",className:"btn btn-sm btn-default",onClick:()=>(n=>{const t=[...e.configArtifacts];t.splice(n,1),e.updateConfigArtifacts(t)})(a)},Q.createElement("span",{className:"glyphicon glyphicon-trash"})," Delete"))))),Q.createElement("div",{className:"col-md-7 col-md-offset-3"},Q.createElement("button",{className:"btn btn-block btn-add-trigger add-new",onClick:()=>{e.updateConfigArtifacts(e.configArtifacts.concat([{id:"",account:""}]))}},Q.createElement("span",{className:"glyphicon glyphicon-plus-sign"})," Add Config Artifact")))}),"configFileArtifactList"),["configArtifacts","pipeline","stage","updateConfigArtifacts"]));const fn="spinnaker.appengine.serverGroup.write.service";e(fn,[]).service("appengineServerGroupWriter",class{startServerGroup(e,n){const t={job:[this.buildJob(e,n,"startAppEngineServerGroup")],application:n,description:`Start Server Group: ${e.name}`};return F.executeTask(t)}stopServerGroup(e,n){const t={job:[this.buildJob(e,n,"stopAppEngineServerGroup")],application:n,description:`Stop Server Group: ${e.name}`};return F.executeTask(t)}buildJob(e,n,t){return{type:t,region:e.region,serverGroupName:e.name,credentials:e.account,cloudProvider:"appengine",application:n.name}}});const bn=class{constructor(e,n,t,a,i,r,o,l){this.$state=e,this.$scope=n,this.$uibModal=t,this.app=i,this.serverGroupWriter=r,this.appengineServerGroupWriter=o,this.appengineServerGroupCommandBuilder=l,this.state={loading:!0},this.app.ready().then((()=>this.extractServerGroup(a))).then((()=>{this.$scope.$$destroyed||this.app.getDataSource("serverGroups").onRefresh(this.$scope,(()=>this.extractServerGroup(a)))})).catch((()=>this.autoClose()))}static buildExpectedAllocationsTable(e){return`\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Server Group</th>\n <th>Allocation</th>\n </tr>\n </thead>\n <tbody>\n ${K(e,((e,n)=>`\n <tr>\n <td>${n}</td>\n <td>${100*e}%</td>\n </tr>`)).join("")}\n </tbody>\n </table>`}canDisableServerGroup(){if(this.serverGroup){if(this.serverGroup.disabled)return!1;const e=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app);return!!e&&Object.keys(e).length>0}return!1}canDestroyServerGroup(){if(this.serverGroup){if(this.serverGroup.disabled)return!0;const e=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app);return!!e&&Object.keys(e).length>0}return!1}destroyServerGroup(){const e={name:this.serverGroup.name,accountId:this.serverGroup.account,region:this.serverGroup.region},n={application:this.app,title:"Destroying "+this.serverGroup.name,onTaskComplete:()=>{this.$state.includes("**.serverGroup",e)&&this.$state.go("^")}},t={header:"Really destroy "+this.serverGroup.name+"?",buttonText:"Destroy "+this.serverGroup.name,account:this.serverGroup.account,taskMonitorConfig:n,submitMethod:e=>this.serverGroupWriter.destroyServerGroup(this.serverGroup,this.app,e),askForReason:!0,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,body:this.getBodyTemplate(this.serverGroup,this.app),interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(t)}enableServerGroup(){const e={application:this.app,title:"Enabling "+this.serverGroup.name},n=`<div class="well well-sm">\n <p>\n Enabling <b>${this.serverGroup.name}</b> will set its traffic allocation for\n <b>${this.serverGroup.loadBalancers[0]}</b> to 100%.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${this.serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n </div>\n `,t={header:"Really enable "+this.serverGroup.name+"?",buttonText:"Enable "+this.serverGroup.name,body:n,account:this.serverGroup.account,taskMonitorConfig:e,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,submitMethod:e=>this.serverGroupWriter.enableServerGroup(this.serverGroup,this.app,{...e}),askForReason:!0,interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(t)}disableServerGroup(){const e={application:this.app,title:"Disabling "+this.serverGroup.name},n=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app),t=`<div class="well well-sm">\n <p>\n For App Engine, a disable operation sets this server group's allocation\n to 0% and sets the other enabled server groups' allocations to their relative proportions\n before the disable operation. The approximate allocations that will result from this operation are shown below.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${this.serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n <div class="row">\n <div class="col-md-12">\n ${bn.buildExpectedAllocationsTable(n)}\n </div>\n </div>\n </div>\n `,a={header:"Really disable "+this.serverGroup.name+"?",buttonText:"Disable "+this.serverGroup.name,body:t,account:this.serverGroup.account,taskMonitorConfig:e,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,submitMethod:e=>this.serverGroupWriter.disableServerGroup(this.serverGroup,this.app.name,e),askForReason:!0,interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(a.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(a)}stopServerGroup(){const e={application:this.app,title:"Stopping "+this.serverGroup.name};let n;this.serverGroup.disabled||(n=`<div class="alert alert-danger">\n <p>Stopping this server group will scale it down to zero instances.</p>\n <p>\n This server group is currently serving traffic from <b>${this.serverGroup.loadBalancers[0]}</b>.\n Traffic directed to this server group after it has been stopped will not be handled.\n </p>\n </div>`);const t={header:"Really stop "+this.serverGroup.name+"?",buttonText:"Stop "+this.serverGroup.name,account:this.serverGroup.account,body:n,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,taskMonitorConfig:e,submitMethod:()=>this.appengineServerGroupWriter.stopServerGroup(this.serverGroup,this.app),askForReason:!0};i.confirm(t)}startServerGroup(){const e={application:this.app,title:"Starting "+this.serverGroup.name},n={header:"Really start "+this.serverGroup.name+"?",buttonText:"Start "+this.serverGroup.name,account:this.serverGroup.account,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,taskMonitorConfig:e,submitMethod:()=>this.appengineServerGroupWriter.startServerGroup(this.serverGroup,this.app),askForReason:!0};i.confirm(n)}cloneServerGroup(){this.$uibModal.open({templateUrl:"appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",controller:"appengineCloneServerGroupCtrl as ctrl",size:"lg",resolve:{title:()=>"Clone "+this.serverGroup.name,application:()=>this.app,serverGroup:()=>this.serverGroup,serverGroupCommand:()=>this.appengineServerGroupCommandBuilder.buildServerGroupCommandFromExisting(this.app,this.serverGroup)}})}canStartServerGroup(){return!!this.canStartOrStopServerGroup()&&"STOPPED"===this.serverGroup.servingStatus}canStopServerGroup(){return!!this.canStartOrStopServerGroup()&&"SERVING"===this.serverGroup.servingStatus}canStartOrStopServerGroup(){var e;return"FLEXIBLE"===this.serverGroup.env||["MANUAL","BASIC"].includes(null==(e=this.serverGroup.scalingPolicy)?void 0:e.type)}getBodyTemplate(e,n){let t="";const a={};if(L.addDestroyWarningMessage(n,e,a),a.body&&(t+=a.body),!e.disabled){const a=this.expectedAllocationsAfterDisableOperation(e,n);t+=`\n <div class="well well-sm">\n <p>\n A destroy operation will first disable this server group.\n </p>\n <p>\n For App Engine, a disable operation sets this server group's allocation\n to 0% and sets the other enabled server groups' allocations to their relative proportions\n before the disable operation. The approximate allocations that will result from this operation are shown below.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${e.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n <div class="row">\n <div class="col-md-12">\n ${bn.buildExpectedAllocationsTable(a)}\n </div>\n </div>\n </div>\n `}return t}expectedAllocationsAfterDisableOperation(e,n){const t=n.getDataSource("loadBalancers").data.find((n=>{var t,a;const i=null!=(a=null==(t=n.split)?void 0:t.allocations)?a:{};return Object.keys(i).includes(e.name)}));if(t){let n=N(t.split.allocations);delete n[e.name];const a=W(n,((e,n)=>e+n),0),i="COOKIE"===t.split.shardBy?1e3:100;return n=Y(n,(e=>Math.round(e/a*i)/i)),n}return null}autoClose(){this.$scope.$$destroyed||(this.$state.params.allowModalToStayOpen=!0,this.$state.go("^",null,{location:"replace"}))}extractServerGroup(e){return P.getServerGroup(this.app.name,e.accountId,e.region,e.name).then((n=>{let t=this.app.getDataSource("serverGroups").data.find((n=>n.name===e.name&&n.account===e.accountId&&n.region===e.region));t||this.app.getDataSource("loadBalancers").data.some((n=>n.account===e.accountId&&n.serverGroups.some((n=>{let a=!1;return n.name===e.name&&(t=n,a=!0),a})))),this.serverGroup={...n,...t},this.state.loading=!1}))}};let yn=bn;yn.$inject=["$state","$scope","$uibModal","serverGroup","app","serverGroupWriter","appengineServerGroupWriter","appengineServerGroupCommandBuilder"];const Cn="spinnaker.appengine.serverGroup.details.controller";e(Cn,[fn,T]).controller("appengineServerGroupDetailsCtrl",yn),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal appengine-server-group-wizard" novalidate>\n <div ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div>\n <v2-modal-wizard\n ng-if="!ctrl.state.loading"\n heading="Create Server Group"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Config Files" mark-complete-on-view="false">\n <appengine-config-file-configurer command="command"></appengine-config-file-configurer>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Load Balancer" done="true">\n <appengine-load-balancer-message show-create-message="false"></appengine-load-balancer-message>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" done="true">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="!state.loading">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="form.$valid"\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="true"\n ></submit-button>\n </div>\n </div>\n</form>\n')}]);class Sn{constructor(e){this.cloudProvider="appengine",this.provider="appengine",this.credentials=e.credentials,this.account=e.credentials,this.application=e.application,this.stack=e.stack,this.freeFormDetails=e.freeFormDetails,this.repositoryUrl=e.repositoryUrl,this.branch=e.branch,this.configFilepaths=e.configFilepaths,this.promote=e.promote,this.stopPreviousVersion=e.stopPreviousVersion,this.type=e.type,this.region=e.region,this.strategy=e.strategy,this.strategyApplication=e.strategyApplication,this.strategyPipeline=e.strategyPipeline,this.fromTrigger=e.fromTrigger,this.trigger=e.trigger,this.gitCredentialType=e.gitCredentialType,this.configFiles=e.configFiles,this.configArtifacts=e.configArtifacts.filter((e=>!!e.id||!!e.artifact)),this.applicationDirectoryRoot=e.applicationDirectoryRoot,this.interestingHealthProviderNames=e.interestingHealthProviderNames||[],this.expectedArtifactId=e.expectedArtifactId,this.expectedArtifact=e.expectedArtifact,this.fromArtifact=e.fromArtifact,this.sourceType=e.sourceType,this.storageAccountName=e.storageAccountName,this.containerImageUrl=e.containerImageUrl,this.suppressVersionString=e.suppressVersionString}}class wn{constructor(e){this.$q=e}normalizeServerGroup(e){return this.$q.resolve(e)}convertServerGroupCommandToDeployConfiguration(e){return new Sn(e)}}wn.$inject=["$q"];const Gn="spinnaker.appengine.serverGroup.transformer.service";e(Gn,[]).service("appengineServerGroupTransformer",wn);I.registerValidator("appengine",new class{validate(e=""){const n=[],t=[];return e.length&&(this.validateSpecialCharacters(e,t),this.validateLength(e,n,t)),{warnings:n,errors:t}}validateSpecialCharacters(e,n){/^[a-z0-9]*$/g.test(e)||n.push("Only numbers and lowercase letters are allowed.")}validateLength(e,n,t){if(e.length>58)t.push("The maximum length for an App Engine application name is 63 characters.");else if(e.length>48)if(e.length>=56)n.push("You will not be able to include a stack or detail field for clusters.");else{const t=56-e.length;n.push(`If you plan to include a stack or detail field for clusters, you will only have\n ${t} character${t>1?"s":""} to do so.`)}}});Ae('.cloud-provider-logo .icon-appengine {\n -webkit-mask-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 18.1.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22 width%3D%22128px%22%09 height%3D%22128px%22 viewBox%3D%2215 15 90 90%22 xml%3Aspace%3D%22preserve%22%3E%3Cfilter id%3D%22invert%22%3E %3CfeColorMatrix in%3D%22SourceGraphic%22 type%3D%22matrix%22 values%3D%22-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0%22%2F%3E%3C%2Ffilter%3E%3Cg id%3D%22art%22 filter%3D%22url(%23invert)%22%3E%09%3Cg%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9988%2C40.9577c-12.7278%2C0-23.0452%2C10.3167-23.0452%2C23.0464%09%09%09c0%2C12.726%2C10.3174%2C23.0463%2C23.0452%2C23.0463c12.7273%2C0%2C23.0446-10.3203%2C23.0446-23.0463%09%09%09C87.0434%2C51.2744%2C76.7261%2C40.9577%2C63.9988%2C40.9577 M63.9988%2C81.4887c-9.6584%2C0-17.4876-7.8286-17.4876-17.4846%09%09%09c0-9.6584%2C7.8291-17.4877%2C17.4876-17.4877c9.6578%2C0%2C17.4858%2C7.8292%2C17.4858%2C17.4877%09%09%09C81.4846%2C73.6601%2C73.6566%2C81.4887%2C63.9988%2C81.4887%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M100.1431%2C61.274l-9.6079-3.0195c0.3997%2C1.8539%2C0.615%2C3.7759%2C0.615%2C5.7478%09%09%09c0%2C1.3791-0.1047%2C2.7337-0.3021%2C4.0569h9.295c0.8587-0.2515%2C1.4327-0.7846%2C1.4327-1.5765v-3.6345%09%09%09C101.5759%2C62.0575%2C101.0018%2C61.5094%2C100.1431%2C61.274%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9923%2C36.8497c1.9303%2C0%2C3.8119%2C0.2046%2C5.6285%2C0.5884l-3.4443-9.5729%09%09%09c-0.2515-0.8606-0.7852-1.434-1.5765-1.434h-1.4392c-0.7913%2C0-1.3407%2C0.5734-1.5761%2C1.434l-2.9924%2C9.5236%09%09%09C60.3372%2C37.0368%2C62.1425%2C36.8497%2C63.9923%2C36.8497%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M36.8468%2C64.0023c0-1.9719%2C0.2154-3.8932%2C0.6145-5.7478l-9.6073%2C3.0195%09%09%09c-0.8587%2C0.2354-1.4322%2C0.7835-1.4322%2C1.5754v3.6344c0%2C0.7907%2C0.5735%2C1.3238%2C1.4322%2C1.5753h9.2955%09%09%09C36.9515%2C66.7366%2C36.8468%2C65.3814%2C36.8468%2C64.0023%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M72.2276%2C56.4819l-2.1802%2C2.1794v-0.0048c-1.5651-1.5639-3.6549-2.4274-5.8687-2.4274%09%09%09c-2.2192%2C0-4.303%2C0.8659-5.8704%2C2.4322c-3.2362%2C3.2355-3.2362%2C8.5037%2C0%2C11.7399l-2.1802%2C2.1783%09%09%09c2.0627%2C2.0609%2C4.9096%2C3.3383%2C8.0483%2C3.3383c6.2803%2C0%2C11.3896-5.1081%2C11.3896-11.3895%09%09%09C75.566%2C61.3883%2C74.289%2C58.5421%2C72.2276%2C56.4819 M67.7284%2C67.7342c-1.0278%2C1.0302-2.3799%2C1.5459-3.7284%2C1.5459%09%09%09c-1.3526%2C0-2.7012-0.5156-3.7313-1.5459c-2.0616-2.0615-2.0616-5.4018%2C0-7.4639c1.0301-1.0314%2C2.3787-1.544%2C3.7313-1.544%09%09%09c1.3485%2C0%2C2.7006%2C0.5127%2C3.7284%2C1.544C69.79%2C62.3312%2C69.79%2C65.6727%2C67.7284%2C67.7342%22%2F%3E%09%3C%2Fg%3E%3C%2Fg%3E%3Cg id%3D%22Guides%22 style%3D%22display%3Anone%3B%22%3E%3C%2Fg%3E%3C%2Fsvg%3E");\n mask-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 18.1.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22 width%3D%22128px%22%09 height%3D%22128px%22 viewBox%3D%2215 15 90 90%22 xml%3Aspace%3D%22preserve%22%3E%3Cfilter id%3D%22invert%22%3E %3CfeColorMatrix in%3D%22SourceGraphic%22 type%3D%22matrix%22 values%3D%22-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0%22%2F%3E%3C%2Ffilter%3E%3Cg id%3D%22art%22 filter%3D%22url(%23invert)%22%3E%09%3Cg%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9988%2C40.9577c-12.7278%2C0-23.0452%2C10.3167-23.0452%2C23.0464%09%09%09c0%2C12.726%2C10.3174%2C23.0463%2C23.0452%2C23.0463c12.7273%2C0%2C23.0446-10.3203%2C23.0446-23.0463%09%09%09C87.0434%2C51.2744%2C76.7261%2C40.9577%2C63.9988%2C40.9577 M63.9988%2C81.4887c-9.6584%2C0-17.4876-7.8286-17.4876-17.4846%09%09%09c0-9.6584%2C7.8291-17.4877%2C17.4876-17.4877c9.6578%2C0%2C17.4858%2C7.8292%2C17.4858%2C17.4877%09%09%09C81.4846%2C73.6601%2C73.6566%2C81.4887%2C63.9988%2C81.4887%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M100.1431%2C61.274l-9.6079-3.0195c0.3997%2C1.8539%2C0.615%2C3.7759%2C0.615%2C5.7478%09%09%09c0%2C1.3791-0.1047%2C2.7337-0.3021%2C4.0569h9.295c0.8587-0.2515%2C1.4327-0.7846%2C1.4327-1.5765v-3.6345%09%09%09C101.5759%2C62.0575%2C101.0018%2C61.5094%2C100.1431%2C61.274%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9923%2C36.8497c1.9303%2C0%2C3.8119%2C0.2046%2C5.6285%2C0.5884l-3.4443-9.5729%09%09%09c-0.2515-0.8606-0.7852-1.434-1.5765-1.434h-1.4392c-0.7913%2C0-1.3407%2C0.5734-1.5761%2C1.434l-2.9924%2C9.5236%09%09%09C60.3372%2C37.0368%2C62.1425%2C36.8497%2C63.9923%2C36.8497%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M36.8468%2C64.0023c0-1.9719%2C0.2154-3.8932%2C0.6145-5.7478l-9.6073%2C3.0195%09%09%09c-0.8587%2C0.2354-1.4322%2C0.7835-1.4322%2C1.5754v3.6344c0%2C0.7907%2C0.5735%2C1.3238%2C1.4322%2C1.5753h9.2955%09%09%09C36.9515%2C66.7366%2C36.8468%2C65.3814%2C36.8468%2C64.0023%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M72.2276%2C56.4819l-2.1802%2C2.1794v-0.0048c-1.5651-1.5639-3.6549-2.4274-5.8687-2.4274%09%09%09c-2.2192%2C0-4.303%2C0.8659-5.8704%2C2.4322c-3.2362%2C3.2355-3.2362%2C8.5037%2C0%2C11.7399l-2.1802%2C2.1783%09%09%09c2.0627%2C2.0609%2C4.9096%2C3.3383%2C8.0483%2C3.3383c6.2803%2C0%2C11.3896-5.1081%2C11.3896-11.3895%09%09%09C75.566%2C61.3883%2C74.289%2C58.5421%2C72.2276%2C56.4819 M67.7284%2C67.7342c-1.0278%2C1.0302-2.3799%2C1.5459-3.7284%2C1.5459%09%09%09c-1.3526%2C0-2.7012-0.5156-3.7313-1.5459c-2.0616-2.0615-2.0616-5.4018%2C0-7.4639c1.0301-1.0314%2C2.3787-1.544%2C3.7313-1.544%09%09%09c1.3485%2C0%2C2.7006%2C0.5127%2C3.7284%2C1.544C69.79%2C62.3312%2C69.79%2C65.6727%2C67.7284%2C67.7342%22%2F%3E%09%3C%2Fg%3E%3C%2Fg%3E%3Cg id%3D%22Guides%22 style%3D%22display%3Anone%3B%22%3E%3C%2Fg%3E%3C%2Fsvg%3E");\n background-color: #4285f4;\n}\n');const An="spinnaker.appengine";e(An,[hn,ie,le,pe,se,Te,Je,ln,rn,Cn,Gn,fn,vn]).config((()=>{m.registerProvider("appengine",{name:"App Engine",instance:{detailsTemplateUrl:"appengine/src/instance/details/details.html",detailsController:"appengineInstanceDetailsCtrl"},serverGroup:{transformer:"appengineServerGroupTransformer",detailsController:"appengineServerGroupDetailsCtrl",detailsTemplateUrl:"appengine/src/serverGroup/details/details.html",commandBuilder:"appengineServerGroupCommandBuilder",cloneServerGroupController:"appengineCloneServerGroupCtrl",cloneServerGroupTemplateUrl:"appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",skipUpstreamStageCheck:!0},loadBalancer:{transformer:"appengineLoadBalancerTransformer",createLoadBalancerTemplateUrl:"appengine/src/loadBalancer/configure/wizard/wizard.html",createLoadBalancerController:"appengineLoadBalancerWizardCtrl",detailsTemplateUrl:"appengine/src/loadBalancer/details/details.html",detailsController:"appengineLoadBalancerDetailsCtrl"},logo:{path:"appengine.logoc2c312af6aa99037.png"}})})),O.registerProvider("appengine",["custom"]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/instance/details/details.html",'<div class="details-panel">\n <div class="header">\n <instance-details-header\n health-state="ctrl.instance.healthState"\n instance-id="ctrl.instance ? ctrl.instance.name : ctrl.instanceIdNotFound"\n loading="ctrl.state.loading"\n standalone="false"\n ></instance-details-header>\n <div ng-if="!ctrl.state.loading">\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Instance Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li><a href ng-click="ctrl.terminateInstance()">Terminate</a></li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!ctrl.state.loading && ctrl.instance">\n <collapsible-section heading="Instance Information" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Launched</dt>\n <dd ng-if="ctrl.instance.launchTime">{{ctrl.instance.launchTime | timestamp}}</dd>\n <dt>In</dt>\n <dd><account-tag account="ctrl.instance.account" pad="right"></account-tag>{{}}</dd>\n <dt ng-if="ctrl.instance.serverGroup">Server Group</dt>\n <dd ng-if="ctrl.instance.serverGroup">\n <a\n ui-sref="^.serverGroup({region: ctrl.instance.region,\n accountId: ctrl.instance.account,\n serverGroup: ctrl.instance.serverGroup,\n provider: \'appengine\'})"\n >{{ctrl.instance.serverGroup}}</a\n >\n </dd>\n <dt>Region</dt>\n <dd>{{ctrl.instance.region}}</dd>\n <appengine-conditional-dt-dd\n component="ctrl.instance"\n key="vmZoneName"\n label="Zone"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <dl>\n <dt>Load Balancer</dt>\n <dd>\n <span\n class="pull-left"\n uib-tooltip="{{ctrl.instance.healthState === \'Up\' ? ctrl.upToolTip : ctrl.outOfServiceToolTip}}"\n tooltip-placement="right"\n >\n <span class="glyphicon glyphicon-{{ctrl.instance.healthState}}-triangle"></span>\n {{ctrl.instance.loadBalancers[0]}}\n </span>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Metrics" expanded="true">\n <dl>\n <appengine-conditional-dt-dd component="ctrl.instance" key="instanceStatus" label="Availability">\n <key-label><help-field key="appengine.instance.availability"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="averageLatency">\n <key-label><help-field key="appengine.instance.averageLatency"></help-field></key-label>\n <value-label>ms</value-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="errors">\n <key-label><help-field key="appengine.instance.errors"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="qps" label="QPS">\n <key-label><help-field key="appengine.instance.qps"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="requests">\n <key-label><help-field key="appengine.instance.requests"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="vmStatus" label="VM Status">\n <key-label><help-field key="appengine.instance.vmStatus"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.instance"\n key="vmDebugEnabled"\n label="Debug Enabled"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n </div>\n <div class="content" ng-if="!ctrl.state.loading && !ctrl.instance">\n <div class="content-section">\n <div class="content-body text-center">\n <h3>Instance not found.</h3>\n </div>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/details/details.html",'<div class="details-panel" ng-class="{ disabled: ctrl.serverGroup.isDisabled || ctrl.serverGroup.disabled}">\n <div class="header" ng-if="ctrl.state.loading">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n </div>\n\n <div class="header" ng-if="!ctrl.state.loading">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="header-text horizontal middle">\n <cloud-provider-logo provider="ctrl.serverGroup.type" height="\'36px\'" width="\'36px\'"></cloud-provider-logo>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>{{ctrl.serverGroup.name}}</h3>\n </div>\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Server Group Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li ng-if="ctrl.canStopServerGroup()">\n <a href ng-click="ctrl.stopServerGroup()"> Stop </a>\n </li>\n <li ng-if="ctrl.canStartServerGroup()">\n <a href ng-click="ctrl.startServerGroup()"> Start </a>\n </li>\n <li ng-if="ctrl.serverGroup.disabled">\n <a href ng-click="ctrl.enableServerGroup()"> Enable </a>\n </li>\n <li ng-if="!ctrl.serverGroup.disabled && ctrl.canDisableServerGroup()">\n <a href ng-click="ctrl.disableServerGroup()"> Disable </a>\n </li>\n <li\n ng-if="!ctrl.serverGroup.disabled && !ctrl.canDisableServerGroup()"\n uib-tooltip="You cannot disable a server group if it is the\n only server group receiving traffic from a load balancer."\n class="disabled"\n >\n <a href> Disable </a>\n </li>\n <li ng-if="ctrl.canDestroyServerGroup()">\n <a href ng-click="ctrl.destroyServerGroup()">Destroy</a>\n </li>\n <li\n ng-if="!ctrl.canDestroyServerGroup()"\n uib-tooltip="You cannot destroy a server group if it is the only server group\n receiving traffic from a load balancer. You may be able to delete\n this server group\'s load balancer."\n class="disabled"\n >\n <a href>Destroy</a>\n </li>\n <li\n uib-tooltip="It is not possible to clone an App Engine server group\'s full\n launch configuration. However, clicking this button will allow\n you to deploy into this server group\'s cluster."\n >\n <a href ng-click="ctrl.cloneServerGroup()">Clone</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n\n <div class="content" ng-if="!ctrl.state.loading">\n <div class="band band-info" ng-if="ctrl.serverGroup.isDisabled || ctrl.serverGroup.disabled">Disabled</div>\n <server-group-running-tasks-details\n server-group="ctrl.serverGroup"\n application="ctrl.app"\n ></server-group-running-tasks-details>\n <collapsible-section heading="Server Group Information" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Created</dt>\n <dd>{{ctrl.serverGroup.createdTime | timestamp}}</dd>\n <dt>In</dt>\n <dd><account-tag account="ctrl.serverGroup.account"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{ctrl.serverGroup.region}}</dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup"\n key="env"\n label="Environment"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.serverGroup" key="instanceClass"></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Size" expanded="true">\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.capacity.min === ctrl.serverGroup.capacity.max">\n <dt>Min/Max</dt>\n <dd>{{ctrl.serverGroup.capacity.min}}</dd>\n <dt>Current</dt>\n <dd>{{ctrl.serverGroup.instances.length}}</dd>\n </dl>\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.capacity.min !== ctrl.serverGroup.capacity.max">\n <dt>Min</dt>\n <dd>{{ctrl.serverGroup.capacity.min}}</dd>\n <dt>Max</dt>\n <dd>{{ctrl.serverGroup.capacity.max}}</dd>\n <dt>Current</dt>\n <dd>{{ctrl.serverGroup.instances.length}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Health" expanded="true">\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup">\n <dt>Instances</dt>\n <dd>\n <health-counts container="ctrl.serverGroup.instanceCounts" class="pull-left"></health-counts>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="DNS" expanded="true">\n <dl class="dl-narrow">\n <appengine-component-url-details component="ctrl.serverGroup"></appengine-component-url-details>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Scaling Policy" expanded="true" ng-if="ctrl.serverGroup.scalingPolicy">\n <dl>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="type"\n ></appengine-conditional-dt-dd>\n \x3c!--MANUAL SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="instances"\n ></appengine-conditional-dt-dd>\n \x3c!--BASIC SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="idleTimeout"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxInstances"\n ></appengine-conditional-dt-dd>\n \x3c!--AUTOMATIC SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="coolDownPeriod"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxConcurrentRequests"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxTotalInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minTotalInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxIdleInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minIdleInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxPendingLatency"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minPendingLatency"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.cpuUtilization"\n key="aggregationWindowLength"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.cpuUtilization"\n key="targetUtilization"\n label="Target CPU Utilization"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetReadBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetReadOpsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetWriteBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetWriteOpsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetReceivedBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetReceivedPacketsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetSentBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetSentPacketsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.requestUtilization"\n key="targetConcurrentRequests"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.requestUtilization"\n key="targetRequestCountPerSecond"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal appengine-server-group-wizard" novalidate>\n <div ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div>\n <v2-modal-wizard\n ng-if="!ctrl.state.loading"\n heading="Create Server Group"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Config Files" mark-complete-on-view="false">\n <appengine-config-file-configurer command="command"></appengine-config-file-configurer>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Load Balancer" done="true">\n <appengine-load-balancer-message show-create-message="false"></appengine-load-balancer-message>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" done="true">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="!state.loading">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="form.$valid"\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="true"\n ></submit-button>\n </div>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/wizard.html",'<form name="form">\n <div ng-if="ctrl.state.loading && !ctrl.isNew" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{::ctrl.heading}}"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n ng-if="!ctrl.state.loading || ctrl.isNew"\n >\n <div ng-if="!ctrl.isNew">\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <appengine-load-balancer-basic-settings\n load-balancer="ctrl.loadBalancer"\n application="ctrl.application"\n for-pipeline-config="ctrl.forPipelineConfig"\n ></appengine-load-balancer-basic-settings>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" mark-complete-on-view="false">\n <appengine-load-balancer-advanced-settings\n load-balancer="ctrl.loadBalancer"\n ></appengine-load-balancer-advanced-settings>\n </v2-wizard-page>\n </div>\n </v2-modal-wizard>\n <appengine-load-balancer-message\n ng-if="ctrl.isNew"\n column-offset="1"\n columns="10"\n show-create-message="true"\n ></appengine-load-balancer-message>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n ng-if="!ctrl.isNew && ctrl.showSubmitButton()"\n label="ctrl.submitButtonLabel"\n is-disabled="appengineLoadBalancerForm.$invalid || ctrl.taskMonitor.submitting || ctrl.state.loading"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n >\n </submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/details/details.html",'<div class="details-panel">\n <div ng-if="ctrl.state.loading" class="header">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n </div>\n\n <div ng-if="!ctrl.state.loading" class="header">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="header-text horizontal middle">\n <i class="fa icon-sitemap"></i>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>{{ctrl.loadBalancer.name}}</h3>\n </div>\n <div>\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Load Balancer Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li><a href ng-click="ctrl.editLoadBalancer()">Edit Load Balancer</a></li>\n <li ng-if="ctrl.canDeleteLoadBalancer()">\n <a href ng-click="ctrl.deleteLoadBalancer()">Delete Load Balancer</a>\n </li>\n <li\n ng-if="!ctrl.canDeleteLoadBalancer()"\n uib-tooltip="You cannot delete a default service."\n class="disabled"\n >\n <a href>Delete Load Balancer</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n\n <div ng-if="!ctrl.state.loading" class="content">\n <collapsible-section heading="Load Balancer Details" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>In</dt>\n <dd><account-tag account="ctrl.loadBalancer.account" pad="right"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{ctrl.loadBalancer.region}}</dd>\n <dt ng-if="ctrl.loadBalancer.serverGroups.length">Server Groups</dt>\n <dd ng-if="ctrl.loadBalancer.serverGroups.length">\n <ul>\n <li ng-repeat="serverGroup in ctrl.loadBalancer.serverGroups | orderBy: [\'isDisabled\', \'-name\']">\n <a\n ui-sref="^.serverGroup({region: serverGroup.region,\n accountId: serverGroup.account,\n serverGroup: serverGroup.name,\n provider: \'appengine\'})"\n >\n {{serverGroup.name}}\n </a>\n </li>\n </ul>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <health-counts class="pull-left" container="ctrl.loadBalancer.instanceCounts"></health-counts>\n </collapsible-section>\n <collapsible-section heading="Traffic Split" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt ng-if="ctrl.loadBalancer.split.shardBy">Shard By</dt>\n <dd ng-if="ctrl.loadBalancer.split.shardBy">\n {{ctrl.loadBalancer.split.shardBy}}\n <help-field\n key="appengine.loadBalancer.shardBy.{{ctrl.loadBalancer.split.shardBy.toLowerCase()}}"\n ></help-field>\n </dd>\n <hr ng-if="ctrl.loadBalancer.split.shardBy" />\n <ul>\n <li ng-repeat="(serverGroup, percent) in ctrl.loadBalancer.split.allocations">\n {{serverGroup}}:<span class="pull-right">{{percent | decimalToPercent}}</span>\n </li>\n </ul>\n </dl>\n </collapsible-section>\n <collapsible-section heading="DNS" expanded="true">\n <dl class="dl-narrow">\n <appengine-component-url-details component="ctrl.loadBalancer"></appengine-component-url-details>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Dispatch Rules" expanded="true" ng-if="ctrl.dispatchRules.length > 0">\n <dl class="dl-horizontal dl-narrow">\n <span ng-repeat-start="rule in ctrl.dispatchRules">{{rule}}</span><br ng-repeat-end />\n </dl>\n </collapsible-section>\n </div>\n</div>\n')}]);export{An as APPENGINE_MODULE};
|
|
4084
2
|
//# sourceMappingURL=index.js.map
|