@spinnaker/appengine 0.0.0-main-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +981 -0
- package/LICENSE.txt +203 -0
- package/dist/appengine.logoc2c312af6aa99037.png +0 -0
- package/dist/appengine.module.d.ts +5 -0
- package/dist/appengine.settings.d.ts +7 -0
- package/dist/common/FormikAccountRegionSelector.d.ts +26 -0
- package/dist/common/appengineHealth.d.ts +3 -0
- package/dist/common/componentUrlDetails.component.d.ts +1 -0
- package/dist/common/conditionalDescriptionListItem.component.d.ts +1 -0
- package/dist/common/loadBalancerMessage.component.d.ts +1 -0
- package/dist/domain/IAppengineAccount.d.ts +6 -0
- package/dist/domain/IAppengineInstance.d.ts +20 -0
- package/dist/domain/IAppengineLoadBalancer.d.ts +19 -0
- package/dist/domain/IAppengineServerGroup.d.ts +11 -0
- package/dist/domain/IAppengineStageScope.d.ts +18 -0
- package/dist/domain/IAppengineTriggers.d.ts +11 -0
- package/dist/domain/index.d.ts +6 -0
- package/dist/helpContents/appengineHelpContents.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4084 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/details/details.controller.d.ts +1 -0
- package/dist/loadBalancer/configure/wizard/advancedSettings.component.d.ts +1 -0
- package/dist/loadBalancer/configure/wizard/allocationConfigurationRow.component.d.ts +1 -0
- package/dist/loadBalancer/configure/wizard/basicSettings.component.d.ts +1 -0
- package/dist/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.d.ts +1 -0
- package/dist/loadBalancer/configure/wizard/wizard.controller.d.ts +2 -0
- package/dist/loadBalancer/details/details.controller.d.ts +1 -0
- package/dist/loadBalancer/loadBalancer.module.d.ts +1 -0
- package/dist/loadBalancer/transformer.d.ts +40 -0
- package/dist/pipeline/pipeline.module.d.ts +1 -0
- package/dist/pipeline/stages/appengineStage.controller.d.ts +12 -0
- package/dist/pipeline/stages/deployAppengineConfig/DeployAppengineConfigForm.d.ts +16 -0
- package/dist/pipeline/stages/deployAppengineConfig/DeployAppengineConfigurationConfig.d.ts +5 -0
- package/dist/pipeline/stages/deployAppengineConfig/deployAppengineConfigStage.d.ts +1 -0
- package/dist/pipeline/stages/destroyAsg/appengineDestroyAsgStage.d.ts +1 -0
- package/dist/pipeline/stages/disableAsg/appengineDisableAsgStage.d.ts +1 -0
- package/dist/pipeline/stages/editLoadBalancer/appengineEditLoadBalancerStage.d.ts +1 -0
- package/dist/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.d.ts +1 -0
- package/dist/pipeline/stages/enableAsg/appengineEnableAsgStage.d.ts +1 -0
- package/dist/pipeline/stages/shrinkCluster/appengineShrinkClusterStage.d.ts +1 -0
- package/dist/pipeline/stages/startServerGroup/appengineStartServerGroupStage.d.ts +1 -0
- package/dist/pipeline/stages/stopServerGroup/appengineStopServerGroupStage.d.ts +1 -0
- package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +74 -0
- package/dist/serverGroup/configure/wizard/ConfigFileArtifactList.d.ts +10 -0
- package/dist/serverGroup/configure/wizard/basicSettings.controller.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/cloneServerGroup.controller.d.ts +2 -0
- package/dist/serverGroup/configure/wizard/configFileArtifactList.module.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/configFiles.component.d.ts +20 -0
- package/dist/serverGroup/configure/wizard/dynamicBranchLabel.component.d.ts +1 -0
- package/dist/serverGroup/details/details.controller.d.ts +1 -0
- package/dist/serverGroup/transformer.d.ts +45 -0
- package/dist/serverGroup/writer/serverGroup.write.service.d.ts +8 -0
- package/dist/validation/ApplicationNameValidator.d.ts +1 -0
- package/package.json +46 -0
- package/src/appengine.module.ts +70 -0
- package/src/appengine.settings.ts +14 -0
- package/src/common/FormikAccountRegionSelector.tsx +107 -0
- package/src/common/appengineHealth.ts +3 -0
- package/src/common/componentUrlDetails.component.ts +29 -0
- package/src/common/conditionalDescriptionListItem.component.ts +37 -0
- package/src/common/loadBalancerMessage.component.html +33 -0
- package/src/common/loadBalancerMessage.component.ts +13 -0
- package/src/domain/IAppengineAccount.ts +8 -0
- package/src/domain/IAppengineInstance.ts +21 -0
- package/src/domain/IAppengineLoadBalancer.ts +21 -0
- package/src/domain/IAppengineServerGroup.ts +13 -0
- package/src/domain/IAppengineStageScope.ts +22 -0
- package/src/domain/IAppengineTriggers.ts +12 -0
- package/src/domain/index.ts +6 -0
- package/src/helpContents/appengineHelpContents.ts +141 -0
- package/src/index.ts +1 -0
- package/src/instance/details/details.controller.ts +116 -0
- package/src/instance/details/details.html +99 -0
- package/src/loadBalancer/configure/wizard/advancedSettings.component.ts +56 -0
- package/src/loadBalancer/configure/wizard/allocationConfigurationRow.component.ts +70 -0
- package/src/loadBalancer/configure/wizard/basicSettings.component.html +65 -0
- package/src/loadBalancer/configure/wizard/basicSettings.component.ts +98 -0
- package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html +117 -0
- package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.ts +108 -0
- package/src/loadBalancer/configure/wizard/wizard.controller.ts +163 -0
- package/src/loadBalancer/configure/wizard/wizard.html +44 -0
- package/src/loadBalancer/configure/wizard/wizard.less +17 -0
- package/src/loadBalancer/details/details.controller.ts +151 -0
- package/src/loadBalancer/details/details.html +102 -0
- package/src/loadBalancer/loadBalancer.module.ts +21 -0
- package/src/loadBalancer/transformer.ts +170 -0
- package/src/logo/appengine.icon.svg +37 -0
- package/src/logo/appengine.logo.less +6 -0
- package/src/logo/appengine.logo.png +0 -0
- package/src/pipeline/pipeline.module.ts +20 -0
- package/src/pipeline/stages/appengineStage.controller.ts +45 -0
- package/src/pipeline/stages/deployAppengineConfig/DeployAppengineConfigForm.tsx +203 -0
- package/src/pipeline/stages/deployAppengineConfig/DeployAppengineConfigurationConfig.tsx +34 -0
- package/src/pipeline/stages/deployAppengineConfig/deployAppengineConfigStage.ts +19 -0
- package/src/pipeline/stages/destroyAsg/appengineDestroyAsgStage.ts +45 -0
- package/src/pipeline/stages/destroyAsg/destroyAsgStage.html +16 -0
- package/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html +1 -0
- package/src/pipeline/stages/disableAsg/appengineDisableAsgStage.ts +54 -0
- package/src/pipeline/stages/disableAsg/disableAsgStage.html +18 -0
- package/src/pipeline/stages/disableAsg/disableAsgStepLabel.html +1 -0
- package/src/pipeline/stages/editLoadBalancer/appengineEditLoadBalancerStage.ts +74 -0
- package/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html +33 -0
- package/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html +51 -0
- package/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.ts +65 -0
- package/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html +45 -0
- package/src/pipeline/stages/enableAsg/appengineEnableAsgStage.ts +49 -0
- package/src/pipeline/stages/enableAsg/enableAsgStage.html +18 -0
- package/src/pipeline/stages/enableAsg/enableAsgStepLabel.html +1 -0
- package/src/pipeline/stages/shrinkCluster/appengineShrinkClusterStage.ts +62 -0
- package/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html +41 -0
- package/src/pipeline/stages/startServerGroup/appengineStartServerGroupStage.ts +54 -0
- package/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html +26 -0
- package/src/pipeline/stages/startServerGroup/startServerGroupStage.html +18 -0
- package/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html +1 -0
- package/src/pipeline/stages/stopServerGroup/appengineStopServerGroupStage.ts +54 -0
- package/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html +26 -0
- package/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html +18 -0
- package/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html +1 -0
- package/src/serverGroup/configure/serverGroupCommandBuilder.service.ts +235 -0
- package/src/serverGroup/configure/wizard/ConfigFileArtifactList.tsx +81 -0
- package/src/serverGroup/configure/wizard/advancedSettings.html +33 -0
- package/src/serverGroup/configure/wizard/basicSettings.controller.ts +151 -0
- package/src/serverGroup/configure/wizard/basicSettings.html +287 -0
- package/src/serverGroup/configure/wizard/cloneServerGroup.controller.ts +103 -0
- package/src/serverGroup/configure/wizard/configFileArtifactList.module.ts +18 -0
- package/src/serverGroup/configure/wizard/configFileArtifactList.spec.tsx +78 -0
- package/src/serverGroup/configure/wizard/configFiles.component.html +81 -0
- package/src/serverGroup/configure/wizard/configFiles.component.ts +121 -0
- package/src/serverGroup/configure/wizard/dynamicBranchLabel.component.ts +19 -0
- package/src/serverGroup/configure/wizard/serverGroupWizard.html +39 -0
- package/src/serverGroup/configure/wizard/serverGroupWizard.less +28 -0
- package/src/serverGroup/details/details.controller.ts +463 -0
- package/src/serverGroup/details/details.html +229 -0
- package/src/serverGroup/transformer.ts +92 -0
- package/src/serverGroup/transformet.spec.ts +40 -0
- package/src/serverGroup/writer/serverGroup.write.service.ts +57 -0
- package/src/validation/ApplicationNameValidator.ts +40 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { from as observableFrom, Subject } from 'rxjs';
|
|
3
|
+
import { takeUntil } from 'rxjs/operators';
|
|
4
|
+
|
|
5
|
+
import type { IAccount, IArtifact, IExpectedArtifact, IFormikStageConfigInjectedProps } from '@spinnaker/core';
|
|
6
|
+
import {
|
|
7
|
+
AccountService,
|
|
8
|
+
ArtifactTypePatterns,
|
|
9
|
+
excludeAllTypesExcept,
|
|
10
|
+
StageArtifactSelectorDelegate,
|
|
11
|
+
} from '@spinnaker/core';
|
|
12
|
+
import { FormikAccountRegionSelector } from '../../../common/FormikAccountRegionSelector';
|
|
13
|
+
|
|
14
|
+
export interface IAppEngineDeployConfigSettingsState {
|
|
15
|
+
accounts: IAccount[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class DeployAppengineConfigForm extends React.Component<
|
|
19
|
+
IFormikStageConfigInjectedProps,
|
|
20
|
+
IAppEngineDeployConfigSettingsState
|
|
21
|
+
> {
|
|
22
|
+
private static readonly excludedArtifactTypes = excludeAllTypesExcept(
|
|
23
|
+
ArtifactTypePatterns.BITBUCKET_FILE,
|
|
24
|
+
ArtifactTypePatterns.CUSTOM_OBJECT,
|
|
25
|
+
ArtifactTypePatterns.EMBEDDED_BASE64,
|
|
26
|
+
ArtifactTypePatterns.GCS_OBJECT,
|
|
27
|
+
ArtifactTypePatterns.GITHUB_FILE,
|
|
28
|
+
ArtifactTypePatterns.GITLAB_FILE,
|
|
29
|
+
ArtifactTypePatterns.S3_OBJECT,
|
|
30
|
+
ArtifactTypePatterns.HTTP_FILE,
|
|
31
|
+
ArtifactTypePatterns.ORACLE_OBJECT,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
private destroy$ = new Subject();
|
|
35
|
+
public state: IAppEngineDeployConfigSettingsState = {
|
|
36
|
+
accounts: [],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
public componentDidMount() {
|
|
40
|
+
observableFrom(AccountService.listAccounts('appengine'))
|
|
41
|
+
.pipe(takeUntil(this.destroy$))
|
|
42
|
+
.subscribe((accounts) => this.setState({ accounts }));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private onTemplateArtifactEdited = (artifact: IArtifact, name: string) => {
|
|
46
|
+
this.props.formik.setFieldValue(`${name}.id`, null);
|
|
47
|
+
this.props.formik.setFieldValue(`${name}.artifact`, artifact);
|
|
48
|
+
this.props.formik.setFieldValue(`${name}.account`, artifact.artifactAccount);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
private onTemplateArtifactSelected = (id: string, name: string) => {
|
|
52
|
+
this.props.formik.setFieldValue(`${name}.id`, id);
|
|
53
|
+
this.props.formik.setFieldValue(`${name}.artifact`, null);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
private removeInputArtifact = (name: string) => {
|
|
57
|
+
this.props.formik.setFieldValue(name, null);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
private getInputArtifact = (stage: any, name: string) => {
|
|
61
|
+
if (!stage[name]) {
|
|
62
|
+
return {
|
|
63
|
+
account: '',
|
|
64
|
+
id: '',
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
return stage[name];
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
public render() {
|
|
72
|
+
const stage = this.props.formik.values;
|
|
73
|
+
const accounts = this.state.accounts;
|
|
74
|
+
return (
|
|
75
|
+
<div>
|
|
76
|
+
<div className="col-md-offset-0 col-md-9">
|
|
77
|
+
<h4>Basic Settings</h4>
|
|
78
|
+
</div>
|
|
79
|
+
<div>
|
|
80
|
+
<FormikAccountRegionSelector
|
|
81
|
+
componentName={''}
|
|
82
|
+
accounts={accounts}
|
|
83
|
+
application={this.props.application}
|
|
84
|
+
cloudProvider={'appengine'}
|
|
85
|
+
credentialsField={'account'}
|
|
86
|
+
formik={this.props.formik}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
<div className="col-md-offset-0 col-md-9">
|
|
90
|
+
<h4>Configuration Settings</h4>
|
|
91
|
+
</div>
|
|
92
|
+
<div>
|
|
93
|
+
<div className="col-md-offset-1 col-md-9">
|
|
94
|
+
<StageArtifactSelectorDelegate
|
|
95
|
+
artifact={this.getInputArtifact(stage, 'cronArtifact').artifact}
|
|
96
|
+
excludedArtifactTypePatterns={DeployAppengineConfigForm.excludedArtifactTypes}
|
|
97
|
+
expectedArtifactId={this.getInputArtifact(stage, 'cronArtifact').id}
|
|
98
|
+
label="Cron Artifact"
|
|
99
|
+
onArtifactEdited={(artifact) => {
|
|
100
|
+
this.onTemplateArtifactEdited(artifact, 'cronArtifact');
|
|
101
|
+
}}
|
|
102
|
+
helpKey={''}
|
|
103
|
+
onExpectedArtifactSelected={(artifact: IExpectedArtifact) =>
|
|
104
|
+
this.onTemplateArtifactSelected(artifact.id, 'cronArtifact')
|
|
105
|
+
}
|
|
106
|
+
pipeline={this.props.pipeline}
|
|
107
|
+
stage={stage}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
<div className="col-md-1">
|
|
111
|
+
<div className="form-control-static">
|
|
112
|
+
<button onClick={() => this.removeInputArtifact('cronArtifact')}>
|
|
113
|
+
<span className="glyphicon glyphicon-trash" />
|
|
114
|
+
<span className="sr-only">Remove field</span>
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
<div>
|
|
120
|
+
<div className="col-md-offset-1 col-md-9">
|
|
121
|
+
<StageArtifactSelectorDelegate
|
|
122
|
+
artifact={this.getInputArtifact(stage, 'dispatchArtifact').artifact}
|
|
123
|
+
excludedArtifactTypePatterns={DeployAppengineConfigForm.excludedArtifactTypes}
|
|
124
|
+
expectedArtifactId={this.getInputArtifact(stage, 'dispatchArtifact').id}
|
|
125
|
+
label="Dispatch Artifact"
|
|
126
|
+
onArtifactEdited={(artifact) => {
|
|
127
|
+
this.onTemplateArtifactEdited(artifact, 'dispatchArtifact');
|
|
128
|
+
}}
|
|
129
|
+
helpKey={''}
|
|
130
|
+
onExpectedArtifactSelected={(artifact: IExpectedArtifact) =>
|
|
131
|
+
this.onTemplateArtifactSelected(artifact.id, 'dispatchArtifact')
|
|
132
|
+
}
|
|
133
|
+
pipeline={this.props.pipeline}
|
|
134
|
+
stage={stage}
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
<div className="col-md-1">
|
|
138
|
+
<div className="form-control-static">
|
|
139
|
+
<button onClick={() => this.removeInputArtifact('dispatchArtifact')}>
|
|
140
|
+
<span className="glyphicon glyphicon-trash" />
|
|
141
|
+
<span className="sr-only">Remove field</span>
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<div>
|
|
147
|
+
<div className="col-md-offset-1 col-md-9">
|
|
148
|
+
<StageArtifactSelectorDelegate
|
|
149
|
+
artifact={this.getInputArtifact(stage, 'indexArtifact').artifact}
|
|
150
|
+
excludedArtifactTypePatterns={DeployAppengineConfigForm.excludedArtifactTypes}
|
|
151
|
+
expectedArtifactId={this.getInputArtifact(stage, 'indexArtifact').id}
|
|
152
|
+
label="Index Artifact"
|
|
153
|
+
onArtifactEdited={(artifact) => {
|
|
154
|
+
this.onTemplateArtifactEdited(artifact, 'indexArtifact');
|
|
155
|
+
}}
|
|
156
|
+
helpKey={''}
|
|
157
|
+
onExpectedArtifactSelected={(artifact: IExpectedArtifact) =>
|
|
158
|
+
this.onTemplateArtifactSelected(artifact.id, 'indexArtifact')
|
|
159
|
+
}
|
|
160
|
+
pipeline={this.props.pipeline}
|
|
161
|
+
stage={stage}
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
<div className="col-md-1">
|
|
165
|
+
<div className="form-control-static">
|
|
166
|
+
<button onClick={() => this.removeInputArtifact('indexArtifact')}>
|
|
167
|
+
<span className="glyphicon glyphicon-trash" />
|
|
168
|
+
<span className="sr-only">Remove field</span>
|
|
169
|
+
</button>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
<div>
|
|
174
|
+
<div className="col-md-offset-1 col-md-9">
|
|
175
|
+
<StageArtifactSelectorDelegate
|
|
176
|
+
artifact={this.getInputArtifact(stage, 'queueArtifact').artifact}
|
|
177
|
+
excludedArtifactTypePatterns={DeployAppengineConfigForm.excludedArtifactTypes}
|
|
178
|
+
expectedArtifactId={this.getInputArtifact(stage, 'queueArtifact').id}
|
|
179
|
+
label="Queue Artifact"
|
|
180
|
+
onArtifactEdited={(artifact) => {
|
|
181
|
+
this.onTemplateArtifactEdited(artifact, 'queueArtifact');
|
|
182
|
+
}}
|
|
183
|
+
helpKey={''}
|
|
184
|
+
onExpectedArtifactSelected={(artifact: IExpectedArtifact) =>
|
|
185
|
+
this.onTemplateArtifactSelected(artifact.id, 'queueArtifact')
|
|
186
|
+
}
|
|
187
|
+
pipeline={this.props.pipeline}
|
|
188
|
+
stage={stage}
|
|
189
|
+
/>
|
|
190
|
+
</div>
|
|
191
|
+
<div className="col-md-1">
|
|
192
|
+
<div className="form-control-static">
|
|
193
|
+
<button onClick={() => this.removeInputArtifact('queueArtifact')}>
|
|
194
|
+
<span className="glyphicon glyphicon-trash" />
|
|
195
|
+
<span className="sr-only">Remove field</span>
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { FormikErrors } from 'formik';
|
|
2
|
+
import { cloneDeep } from 'lodash';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import type { IStage, IStageConfigProps } from '@spinnaker/core';
|
|
6
|
+
import { FormikStageConfig, FormValidator } from '@spinnaker/core';
|
|
7
|
+
|
|
8
|
+
import { DeployAppengineConfigForm } from './DeployAppengineConfigForm';
|
|
9
|
+
|
|
10
|
+
export function DeployAppengineConfigurationConfig({ application, pipeline, stage, updateStage }: IStageConfigProps) {
|
|
11
|
+
const stageWithDefaults = React.useMemo(() => {
|
|
12
|
+
return {
|
|
13
|
+
...cloneDeep(stage),
|
|
14
|
+
};
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<FormikStageConfig
|
|
19
|
+
application={application}
|
|
20
|
+
onChange={updateStage}
|
|
21
|
+
pipeline={pipeline}
|
|
22
|
+
stage={stageWithDefaults}
|
|
23
|
+
validate={validateDeployAppengineConfigurationStage}
|
|
24
|
+
render={(props) => <DeployAppengineConfigForm {...props} />}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function validateDeployAppengineConfigurationStage(stage: IStage): FormikErrors<IStage> {
|
|
30
|
+
const formValidator = new FormValidator(stage);
|
|
31
|
+
formValidator.field('account').required();
|
|
32
|
+
formValidator.field('region').required();
|
|
33
|
+
return formValidator.validateForm();
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ExecutionArtifactTab, ExecutionDetailsTasks, Registry } from '@spinnaker/core';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DeployAppengineConfigurationConfig,
|
|
5
|
+
validateDeployAppengineConfigurationStage,
|
|
6
|
+
} from './DeployAppengineConfigurationConfig';
|
|
7
|
+
|
|
8
|
+
export const DEPLOY_APPENGINE_CONFIG_STAGE_KEY = 'deployAppEngineConfiguration';
|
|
9
|
+
|
|
10
|
+
Registry.pipeline.registerStage({
|
|
11
|
+
label: 'Deploy App Engine Configuration',
|
|
12
|
+
description: 'Deploy index, dispatch, cron, and queue configuration to App Engine.',
|
|
13
|
+
key: DEPLOY_APPENGINE_CONFIG_STAGE_KEY,
|
|
14
|
+
component: DeployAppengineConfigurationConfig,
|
|
15
|
+
producesArtifacts: false,
|
|
16
|
+
cloudProvider: 'appengine',
|
|
17
|
+
executionDetailsSections: [ExecutionDetailsTasks, ExecutionArtifactTab],
|
|
18
|
+
validateFn: validateDeployAppengineConfigurationStage,
|
|
19
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
|
|
3
|
+
import { Registry } from '@spinnaker/core';
|
|
4
|
+
|
|
5
|
+
import { AppengineStageCtrl } from '../appengineStage.controller';
|
|
6
|
+
import type { IAppengineStageScope } from '../../../domain';
|
|
7
|
+
|
|
8
|
+
class AppengineDestroyAsgStageCtrl extends AppengineStageCtrl {
|
|
9
|
+
public static $inject = ['$scope'];
|
|
10
|
+
constructor(protected $scope: IAppengineStageScope) {
|
|
11
|
+
super($scope);
|
|
12
|
+
|
|
13
|
+
super.setAccounts().then(() => {
|
|
14
|
+
super.setStageRegion();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
super.setStageCloudProvider();
|
|
18
|
+
super.setTargets();
|
|
19
|
+
super.setStageCredentials();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const APPENGINE_DESTROY_ASG_STAGE = 'spinnaker.appengine.pipeline.stage.destroyAsgStage';
|
|
24
|
+
|
|
25
|
+
module(APPENGINE_DESTROY_ASG_STAGE, [])
|
|
26
|
+
.config(() => {
|
|
27
|
+
Registry.pipeline.registerStage({
|
|
28
|
+
provides: 'destroyServerGroup',
|
|
29
|
+
key: 'destroyServerGroup',
|
|
30
|
+
cloudProvider: 'appengine',
|
|
31
|
+
templateUrl: require('./destroyAsgStage.html'),
|
|
32
|
+
executionStepLabelUrl: require('./destroyAsgStepLabel.html'),
|
|
33
|
+
validators: [
|
|
34
|
+
{
|
|
35
|
+
type: 'targetImpedance',
|
|
36
|
+
message:
|
|
37
|
+
'This pipeline will attempt to destroy a server group without deploying a new version into the same cluster.',
|
|
38
|
+
},
|
|
39
|
+
{ type: 'requiredField', fieldName: 'cluster' },
|
|
40
|
+
{ type: 'requiredField', fieldName: 'target' },
|
|
41
|
+
{ type: 'requiredField', fieldName: 'credentials', fieldLabel: 'account' },
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
})
|
|
45
|
+
.controller('appengineDestroyAsgStageCtrl', AppengineDestroyAsgStageCtrl);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<div ng-controller="appengineDestroyAsgStageCtrl as destroyAsgStageCtrl" class="form-horizontal">
|
|
2
|
+
<div ng-if="!pipeline.strategy">
|
|
3
|
+
<account-region-cluster-selector
|
|
4
|
+
application="application"
|
|
5
|
+
single-region="true"
|
|
6
|
+
disable-region-select="true"
|
|
7
|
+
on-account-update="destroyAsgStageCtrl.setStageRegion()"
|
|
8
|
+
component="stage"
|
|
9
|
+
accounts="accounts"
|
|
10
|
+
>
|
|
11
|
+
</account-region-cluster-selector>
|
|
12
|
+
</div>
|
|
13
|
+
<stage-config-field label="Target">
|
|
14
|
+
<target-select model="stage" options="targets"></target-select>
|
|
15
|
+
</stage-config-field>
|
|
16
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<span class="task-label"> Destroy Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
|
|
3
|
+
import { Registry } from '@spinnaker/core';
|
|
4
|
+
|
|
5
|
+
import { AppengineStageCtrl } from '../appengineStage.controller';
|
|
6
|
+
import { AppengineHealth } from '../../../common/appengineHealth';
|
|
7
|
+
import type { IAppengineStageScope } from '../../../domain';
|
|
8
|
+
|
|
9
|
+
class AppengineDisableAsgStageCtrl extends AppengineStageCtrl {
|
|
10
|
+
public static $inject = ['$scope'];
|
|
11
|
+
constructor(protected $scope: IAppengineStageScope) {
|
|
12
|
+
super($scope);
|
|
13
|
+
|
|
14
|
+
super.setAccounts().then(() => {
|
|
15
|
+
super.setStageRegion();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
super.setStageCloudProvider();
|
|
19
|
+
super.setTargets();
|
|
20
|
+
super.setStageCredentials();
|
|
21
|
+
|
|
22
|
+
if (
|
|
23
|
+
$scope.stage.isNew &&
|
|
24
|
+
$scope.application.attributes.platformHealthOnlyShowOverride &&
|
|
25
|
+
$scope.application.attributes.platformHealthOnly
|
|
26
|
+
) {
|
|
27
|
+
$scope.stage.interestingHealthProviderNames = [AppengineHealth.PLATFORM];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const APPENGINE_DISABLE_ASG_STAGE = 'spinnaker.appengine.pipeline.stage.disableAsgStage';
|
|
33
|
+
|
|
34
|
+
module(APPENGINE_DISABLE_ASG_STAGE, [])
|
|
35
|
+
.config(() => {
|
|
36
|
+
Registry.pipeline.registerStage({
|
|
37
|
+
provides: 'disableServerGroup',
|
|
38
|
+
key: 'disableServerGroup',
|
|
39
|
+
cloudProvider: 'appengine',
|
|
40
|
+
templateUrl: require('./disableAsgStage.html'),
|
|
41
|
+
executionStepLabelUrl: require('./disableAsgStepLabel.html'),
|
|
42
|
+
validators: [
|
|
43
|
+
{
|
|
44
|
+
type: 'targetImpedance',
|
|
45
|
+
message:
|
|
46
|
+
'This pipeline will attempt to disable a server group without deploying a new version into the same cluster.',
|
|
47
|
+
},
|
|
48
|
+
{ type: 'requiredField', fieldName: 'cluster' },
|
|
49
|
+
{ type: 'requiredField', fieldName: 'target' },
|
|
50
|
+
{ type: 'requiredField', fieldName: 'credentials', fieldLabel: 'account' },
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
})
|
|
54
|
+
.controller('appengineDisableAsgStageCtrl', AppengineDisableAsgStageCtrl);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<div ng-controller="appengineDisableAsgStageCtrl as disableAsgStageCtrl" class="form-horizontal">
|
|
2
|
+
<div ng-if="!pipeline.strategy">
|
|
3
|
+
<account-region-cluster-selector
|
|
4
|
+
application="application"
|
|
5
|
+
component="stage"
|
|
6
|
+
single-region="true"
|
|
7
|
+
disable-region-select="true"
|
|
8
|
+
on-account-update="disableAsgStageCtrl.setStageRegion()"
|
|
9
|
+
accounts="accounts"
|
|
10
|
+
>
|
|
11
|
+
</account-region-cluster-selector>
|
|
12
|
+
</div>
|
|
13
|
+
<stage-config-field label="Target">
|
|
14
|
+
<target-select model="stage" options="targets"></target-select>
|
|
15
|
+
</stage-config-field>
|
|
16
|
+
<stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">
|
|
17
|
+
</stage-platform-health-override>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<span class="task-label"> Disable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { IController } from 'angular';
|
|
2
|
+
import { module } from 'angular';
|
|
3
|
+
import type { IModalService } from 'angular-ui-bootstrap';
|
|
4
|
+
import { cloneDeep } from 'lodash';
|
|
5
|
+
|
|
6
|
+
import type { ILoadBalancer } from '@spinnaker/core';
|
|
7
|
+
import { CloudProviderRegistry, Registry } from '@spinnaker/core';
|
|
8
|
+
|
|
9
|
+
import { APPENGINE_LOAD_BALANCER_CHOICE_MODAL_CTRL } from './loadBalancerChoice.modal.controller';
|
|
10
|
+
|
|
11
|
+
class AppengineEditLoadBalancerStageCtrl implements IController {
|
|
12
|
+
public static $inject = ['$scope', '$uibModal'];
|
|
13
|
+
constructor(public $scope: any, private $uibModal: IModalService) {
|
|
14
|
+
$scope.stage.loadBalancers = $scope.stage.loadBalancers || [];
|
|
15
|
+
$scope.stage.cloudProvider = 'appengine';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public addLoadBalancer(): void {
|
|
19
|
+
this.$uibModal
|
|
20
|
+
.open({
|
|
21
|
+
templateUrl: require('./loadBalancerChoice.modal.html'),
|
|
22
|
+
controller: `appengineLoadBalancerChoiceModelCtrl as ctrl`,
|
|
23
|
+
resolve: {
|
|
24
|
+
application: () => this.$scope.application,
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
.result.then((newLoadBalancer: ILoadBalancer) => {
|
|
28
|
+
this.$scope.stage.loadBalancers.push(newLoadBalancer);
|
|
29
|
+
})
|
|
30
|
+
.catch(() => {});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public editLoadBalancer(index: number) {
|
|
34
|
+
const config = CloudProviderRegistry.getValue('appengine', 'loadBalancer');
|
|
35
|
+
this.$uibModal
|
|
36
|
+
.open({
|
|
37
|
+
templateUrl: config.createLoadBalancerTemplateUrl,
|
|
38
|
+
controller: `${config.createLoadBalancerController} as ctrl`,
|
|
39
|
+
size: 'lg',
|
|
40
|
+
resolve: {
|
|
41
|
+
application: () => this.$scope.application,
|
|
42
|
+
loadBalancer: () => cloneDeep(this.$scope.stage.loadBalancers[index]),
|
|
43
|
+
isNew: () => false,
|
|
44
|
+
forPipelineConfig: () => true,
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
.result.then((updatedLoadBalancer: ILoadBalancer) => {
|
|
48
|
+
this.$scope.stage.loadBalancers[index] = updatedLoadBalancer;
|
|
49
|
+
})
|
|
50
|
+
.catch(() => {});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public removeLoadBalancer(index: number): void {
|
|
54
|
+
this.$scope.stage.loadBalancers.splice(index, 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const APPENGINE_EDIT_LOAD_BALANCER_STAGE = 'spinnaker.appengine.pipeline.stage.editLoadBalancerStage';
|
|
59
|
+
module(APPENGINE_EDIT_LOAD_BALANCER_STAGE, [APPENGINE_LOAD_BALANCER_CHOICE_MODAL_CTRL])
|
|
60
|
+
.config(() => {
|
|
61
|
+
Registry.pipeline.registerStage({
|
|
62
|
+
label: 'Edit Load Balancer',
|
|
63
|
+
description: 'Edits a load balancer',
|
|
64
|
+
key: 'upsertAppEngineLoadBalancers',
|
|
65
|
+
cloudProvider: 'appengine',
|
|
66
|
+
templateUrl: require('./editLoadBalancerStage.html'),
|
|
67
|
+
executionDetailsUrl: require('./editLoadBalancerExecutionDetails.html'),
|
|
68
|
+
executionConfigSections: ['editLoadBalancerConfig', 'taskStatus'],
|
|
69
|
+
controller: 'appengineEditLoadBalancerStageCtrl',
|
|
70
|
+
controllerAs: 'editLoadBalancerStageCtrl',
|
|
71
|
+
validators: [],
|
|
72
|
+
});
|
|
73
|
+
})
|
|
74
|
+
.controller('appengineEditLoadBalancerStageCtrl', AppengineEditLoadBalancerStageCtrl);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<div ng-controller="BaseExecutionDetailsCtrl">
|
|
2
|
+
<execution-details-section-nav sections="configSections"></execution-details-section-nav>
|
|
3
|
+
<div class="step-section-details" ng-if="detailsSection === 'editLoadBalancerConfig'">
|
|
4
|
+
<div class="row">
|
|
5
|
+
<div class="col-md-12">
|
|
6
|
+
<table class="table table-condensed">
|
|
7
|
+
<thead>
|
|
8
|
+
<tr>
|
|
9
|
+
<th>Account</th>
|
|
10
|
+
<th>Name</th>
|
|
11
|
+
<th>Region</th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
<tr ng-repeat="loadBalancer in stage.context.loadBalancers">
|
|
16
|
+
<td>
|
|
17
|
+
<account-tag account="loadBalancer.credentials"></account-tag>
|
|
18
|
+
</td>
|
|
19
|
+
<td>{{ loadBalancer.name }}</td>
|
|
20
|
+
<td>{{ loadBalancer.region }}</td>
|
|
21
|
+
</tr>
|
|
22
|
+
</tbody>
|
|
23
|
+
</table>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="step-section-details" ng-if="detailsSection === 'taskStatus'">
|
|
29
|
+
<div class="row">
|
|
30
|
+
<execution-step-details item="stage"></execution-step-details>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<div class="well well-sm clearfix" ng-if="!pipeline.strategy">
|
|
2
|
+
<div class="row">
|
|
3
|
+
<div class="col-md-12">
|
|
4
|
+
<h4 class="text-left">Load Balancers</h4>
|
|
5
|
+
</div>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="row">
|
|
8
|
+
<div class="col-md-12">
|
|
9
|
+
<table class="table table-condensed">
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<th>Account</th>
|
|
13
|
+
<th>Name</th>
|
|
14
|
+
<th>Region</th>
|
|
15
|
+
<th>Actions</th>
|
|
16
|
+
</tr>
|
|
17
|
+
</thead>
|
|
18
|
+
<tbody>
|
|
19
|
+
<tr ng-repeat="loadBalancer in stage.loadBalancers">
|
|
20
|
+
<td>
|
|
21
|
+
<account-tag account="loadBalancer.credentials"></account-tag>
|
|
22
|
+
</td>
|
|
23
|
+
<td>{{ loadBalancer.name }}</td>
|
|
24
|
+
<td>{{ loadBalancer.region }}</td>
|
|
25
|
+
<td class="condensed-actions">
|
|
26
|
+
<a class="btn btn-sm btn-link" href ng-click="editLoadBalancerStageCtrl.editLoadBalancer($index)">
|
|
27
|
+
<span class="glyphicon glyphicon-edit" uib-tooltip="Edit"></span
|
|
28
|
+
></a>
|
|
29
|
+
<a
|
|
30
|
+
class="btn btn-sm btn-link pad-left"
|
|
31
|
+
href
|
|
32
|
+
ng-click="editLoadBalancerStageCtrl.removeLoadBalancer($index)"
|
|
33
|
+
>
|
|
34
|
+
<span class="glyphicon glyphicon-trash" uib-tooltip="Remove"></span>
|
|
35
|
+
</a>
|
|
36
|
+
</td>
|
|
37
|
+
</tr>
|
|
38
|
+
</tbody>
|
|
39
|
+
<tfoot>
|
|
40
|
+
<tr>
|
|
41
|
+
<td colspan="8">
|
|
42
|
+
<button class="btn btn-block btn-sm add-new" ng-click="editLoadBalancerStageCtrl.addLoadBalancer()">
|
|
43
|
+
<span class="glyphicon glyphicon-plus-sign"></span> Add load balancer
|
|
44
|
+
</button>
|
|
45
|
+
</td>
|
|
46
|
+
</tr>
|
|
47
|
+
</tfoot>
|
|
48
|
+
</table>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { IController } from 'angular';
|
|
2
|
+
import { module } from 'angular';
|
|
3
|
+
import type { IModalService, IModalServiceInstance } from 'angular-ui-bootstrap';
|
|
4
|
+
import { cloneDeep } from 'lodash';
|
|
5
|
+
|
|
6
|
+
import type { Application, ILoadBalancer } from '@spinnaker/core';
|
|
7
|
+
import { CloudProviderRegistry } from '@spinnaker/core';
|
|
8
|
+
|
|
9
|
+
class AppengineLoadBalancerChoiceModalCtrl implements IController {
|
|
10
|
+
public state = { loading: true };
|
|
11
|
+
public loadBalancers: ILoadBalancer[];
|
|
12
|
+
public selectedLoadBalancer: ILoadBalancer;
|
|
13
|
+
|
|
14
|
+
public static $inject = ['$uibModal', '$uibModalInstance', 'application'];
|
|
15
|
+
constructor(
|
|
16
|
+
private $uibModal: IModalService,
|
|
17
|
+
private $uibModalInstance: IModalServiceInstance,
|
|
18
|
+
private application: Application,
|
|
19
|
+
) {
|
|
20
|
+
this.initialize();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public submit(): void {
|
|
24
|
+
const config = CloudProviderRegistry.getValue('appengine', 'loadBalancer');
|
|
25
|
+
const updatedLoadBalancerPromise = this.$uibModal.open({
|
|
26
|
+
templateUrl: config.createLoadBalancerTemplateUrl,
|
|
27
|
+
controller: `${config.createLoadBalancerController} as ctrl`,
|
|
28
|
+
size: 'lg',
|
|
29
|
+
resolve: {
|
|
30
|
+
application: () => this.application,
|
|
31
|
+
loadBalancer: () => cloneDeep(this.selectedLoadBalancer),
|
|
32
|
+
isNew: () => false,
|
|
33
|
+
forPipelineConfig: () => true,
|
|
34
|
+
},
|
|
35
|
+
}).result;
|
|
36
|
+
|
|
37
|
+
this.$uibModalInstance.close(updatedLoadBalancerPromise);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public cancel(): void {
|
|
41
|
+
this.$uibModalInstance.dismiss();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private initialize(): void {
|
|
45
|
+
this.application
|
|
46
|
+
.getDataSource('loadBalancers')
|
|
47
|
+
.ready()
|
|
48
|
+
.then(() => {
|
|
49
|
+
this.loadBalancers = (this.application.loadBalancers.data as ILoadBalancer[]).filter(
|
|
50
|
+
(candidate) => candidate.cloudProvider === 'appengine',
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (this.loadBalancers.length) {
|
|
54
|
+
this.selectedLoadBalancer = this.loadBalancers[0];
|
|
55
|
+
}
|
|
56
|
+
this.state.loading = false;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const APPENGINE_LOAD_BALANCER_CHOICE_MODAL_CTRL = 'spinnaker.appengine.loadBalancerChoiceModal.controller';
|
|
62
|
+
module(APPENGINE_LOAD_BALANCER_CHOICE_MODAL_CTRL, []).controller(
|
|
63
|
+
'appengineLoadBalancerChoiceModelCtrl',
|
|
64
|
+
AppengineLoadBalancerChoiceModalCtrl,
|
|
65
|
+
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<div modal-page>
|
|
2
|
+
<modal-close dismiss="$dismiss()"></modal-close>
|
|
3
|
+
<div class="modal-header">
|
|
4
|
+
<h4 class="modal-title">Select Load Balancer</h4>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="modal-body" ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">
|
|
7
|
+
<loading-spinner size="'small'"></loading-spinner>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="modal-body" ng-if="!ctrl.state.loading">
|
|
10
|
+
<div class="alert alert-warning" ng-if="ctrl.loadBalancers.length === 0">
|
|
11
|
+
<p>This application has no App Engine load balancers.</p>
|
|
12
|
+
</div>
|
|
13
|
+
<form
|
|
14
|
+
role="form"
|
|
15
|
+
name="form"
|
|
16
|
+
class="form-horizontal"
|
|
17
|
+
ng-submit="ctrl.submit()"
|
|
18
|
+
ng-if="ctrl.loadBalancers.length > 0"
|
|
19
|
+
>
|
|
20
|
+
<div class="form-group">
|
|
21
|
+
<div class="col-md-3 sm-label-right">
|
|
22
|
+
<b>Load Balancer</b>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="col-md-7">
|
|
25
|
+
<ui-select class="form-control input-sm" ng-model="ctrl.selectedLoadBalancer">
|
|
26
|
+
<ui-select-match>
|
|
27
|
+
<account-tag account="$select.selected.account"></account-tag>
|
|
28
|
+
<span style="margin-left: 5px">{{$select.selected.name}}</span>
|
|
29
|
+
</ui-select-match>
|
|
30
|
+
<ui-select-choices repeat="loadBalancer in ctrl.loadBalancers | filter: $select.search">
|
|
31
|
+
<account-tag account="loadBalancer.account"></account-tag>
|
|
32
|
+
<span style="margin-left: 5px" ng-bind-html="loadBalancer.name"></span>
|
|
33
|
+
</ui-select-choices>
|
|
34
|
+
</ui-select>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</form>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="modal-footer">
|
|
40
|
+
<button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>
|
|
41
|
+
<button class="btn btn-primary" ng-if="ctrl.loadBalancers.length > 0" ng-click="ctrl.submit()">
|
|
42
|
+
<span class="far fa-check-circle"></span> Edit
|
|
43
|
+
</button>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|