@spinnaker/cloudrun 0.1.6
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 +70 -0
- package/LICENSE.txt +203 -0
- package/dist/cloudrun.module.d.ts +4 -0
- package/dist/cloudrun.settings.d.ts +7 -0
- package/dist/common/cloudrunHealth.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/domain/ICloudrunInstance.d.ts +20 -0
- package/dist/common/domain/ICloudrunLoadBalancer.d.ts +18 -0
- package/dist/common/domain/index.d.ts +2 -0
- package/dist/common/loadBalancerMessage.component.d.ts +1 -0
- package/dist/help/cloudrun.help.d.ts +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/details/details.controller.d.ts +1 -0
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/infrastructure.types.d.ts +18 -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/index.d.ts +3 -0
- package/dist/loadBalancer/loadBalancerTransformer.d.ts +38 -0
- package/dist/manifest/ManifestSource.d.ts +4 -0
- package/dist/manifest/index.d.ts +2 -0
- package/dist/manifest/manifest.service.d.ts +25 -0
- package/dist/manifest/manifestCommandBuilder.service.d.ts +30 -0
- package/dist/manifest/wizard/BasicSettings.d.ts +17 -0
- package/dist/pipeline/pipeline.module.d.ts +1 -0
- package/dist/pipeline/stages/deployManifest/DeployStageConfig.d.ts +15 -0
- package/dist/pipeline/stages/deployManifest/DeployStageForm.d.ts +23 -0
- package/dist/pipeline/stages/deployManifest/ManifestBindArtifactsSelector.d.ts +17 -0
- package/dist/pipeline/stages/deployManifest/deploy.validator.d.ts +2 -0
- package/dist/pipeline/stages/deployManifest/deployStage.d.ts +1 -0
- package/dist/pipeline/stages/deployManifest/manifestStatus/DeployStatus.d.ts +22 -0
- package/dist/pipeline/stages/deployManifest/manifestStatus/DeployStatusPills.d.ts +8 -0
- package/dist/pipeline/stages/deployManifest/manifestStatus/ManifestDetailsLink.d.ts +20 -0
- package/dist/pipeline/stages/deployManifest/manifestStatus/ManifestEvents.d.ts +9 -0
- package/dist/pipeline/stages/deployManifest/manifestStatus/ManifestStatus.d.ts +8 -0
- package/dist/pipeline/stages/deployManifest/serverGroupNamePreview.d.ts +9 -0
- package/dist/pipeline/stages/editLoadBalancer/cloudrunEditLoadBalancerStage.d.ts +1 -0
- package/dist/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.d.ts +1 -0
- package/dist/rolloutStrategy/redblack.strategy.d.ts +2 -0
- package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +74 -0
- package/dist/serverGroup/configure/wizard/BasicSettings.d.ts +29 -0
- package/dist/serverGroup/configure/wizard/ConfigFiles.d.ts +15 -0
- package/dist/serverGroup/configure/wizard/serverGroupWizard.d.ts +25 -0
- package/dist/serverGroup/details/details.controller.d.ts +1 -0
- package/dist/serverGroup/index.d.ts +3 -0
- package/dist/serverGroup/serverGroupTransformer.service.d.ts +31 -0
- package/package.json +57 -0
- package/src/cloudrun.module.ts +73 -0
- package/src/cloudrun.settings.ts +14 -0
- package/src/common/cloudrunHealth.ts +3 -0
- package/src/common/componentUrlDetails.component.ts +22 -0
- package/src/common/conditionalDescriptionListItem.component.ts +37 -0
- package/src/common/domain/ICloudrunInstance.ts +21 -0
- package/src/common/domain/ICloudrunLoadBalancer.ts +18 -0
- package/src/common/domain/index.ts +2 -0
- package/src/common/loadBalancerMessage.component.html +11 -0
- package/src/common/loadBalancerMessage.component.ts +13 -0
- package/src/help/cloudrun.help.ts +99 -0
- package/src/index.ts +3 -0
- package/src/instance/details/details.controller.ts +84 -0
- package/src/instance/details/details.html +68 -0
- package/src/interfaces/index.ts +1 -0
- package/src/interfaces/infrastructure.types.ts +23 -0
- package/src/loadBalancer/configure/wizard/allocationConfigurationRow.component.ts +70 -0
- package/src/loadBalancer/configure/wizard/basicSettings.component.html +44 -0
- package/src/loadBalancer/configure/wizard/basicSettings.component.ts +86 -0
- package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html +45 -0
- package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.ts +84 -0
- package/src/loadBalancer/configure/wizard/wizard.controller.ts +157 -0
- package/src/loadBalancer/configure/wizard/wizard.html +39 -0
- package/src/loadBalancer/configure/wizard/wizard.less +9 -0
- package/src/loadBalancer/details/details.controller.ts +140 -0
- package/src/loadBalancer/details/details.html +89 -0
- package/src/loadBalancer/index.ts +3 -0
- package/src/loadBalancer/loadBalancerTransformer.ts +168 -0
- package/src/logo/cloudrun.icon.svg +27 -0
- package/src/logo/cloudrun.logo.less +6 -0
- package/src/logo/cloudrun.logo.png +0 -0
- package/src/manifest/ManifestSource.ts +4 -0
- package/src/manifest/index.ts +2 -0
- package/src/manifest/manifest.service.ts +89 -0
- package/src/manifest/manifestCommandBuilder.service.ts +116 -0
- package/src/manifest/wizard/BasicSettings.tsx +57 -0
- package/src/pipeline/pipeline.module.ts +6 -0
- package/src/pipeline/stages/deployManifest/DeployStageConfig.tsx +73 -0
- package/src/pipeline/stages/deployManifest/DeployStageForm.tsx +185 -0
- package/src/pipeline/stages/deployManifest/ManifestBindArtifactsSelector.tsx +69 -0
- package/src/pipeline/stages/deployManifest/deploy.validator.ts +38 -0
- package/src/pipeline/stages/deployManifest/deployStage.ts +28 -0
- package/src/pipeline/stages/deployManifest/manifestStatus/DeployStatus.tsx +108 -0
- package/src/pipeline/stages/deployManifest/manifestStatus/DeployStatusPills.tsx +47 -0
- package/src/pipeline/stages/deployManifest/manifestStatus/ManifestDetailsLink.tsx +92 -0
- package/src/pipeline/stages/deployManifest/manifestStatus/ManifestEvents.tsx +87 -0
- package/src/pipeline/stages/deployManifest/manifestStatus/ManifestStatus.less +24 -0
- package/src/pipeline/stages/deployManifest/manifestStatus/ManifestStatus.tsx +46 -0
- package/src/pipeline/stages/deployManifest/serverGroupNamePreview.tsx +28 -0
- package/src/pipeline/stages/editLoadBalancer/cloudrunEditLoadBalancerStage.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 +53 -0
- package/src/rolloutStrategy/redblack.strategy.ts +7 -0
- package/src/serverGroup/configure/serverGroupCommandBuilder.service.ts +254 -0
- package/src/serverGroup/configure/wizard/BasicSettings.tsx +160 -0
- package/src/serverGroup/configure/wizard/ConfigFiles.tsx +75 -0
- package/src/serverGroup/configure/wizard/serverGroupWizard.tsx +150 -0
- package/src/serverGroup/details/details.controller.ts +134 -0
- package/src/serverGroup/details/details.html +94 -0
- package/src/serverGroup/index.ts +3 -0
- package/src/serverGroup/serverGroupTransformer.service.ts +61 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { get } from 'lodash';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { IExecutionDetailsSectionProps, IManifest } from '@spinnaker/core';
|
|
5
|
+
import { ExecutionDetailsSection, StageFailureMessage } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import { ManifestStatus } from './ManifestStatus';
|
|
8
|
+
import type { IStageManifest } from '../../../../manifest/manifest.service';
|
|
9
|
+
import { CloudrunManifestService } from '../../../../manifest/manifest.service';
|
|
10
|
+
|
|
11
|
+
export interface IManifestSubscription {
|
|
12
|
+
id: string;
|
|
13
|
+
unsubscribe: () => void;
|
|
14
|
+
manifest: IManifest;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface IDeployStatusState {
|
|
18
|
+
subscriptions: IManifestSubscription[];
|
|
19
|
+
manifestIds: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class DeployStatus extends React.Component<IExecutionDetailsSectionProps, IDeployStatusState> {
|
|
23
|
+
public static title = 'deployStatus';
|
|
24
|
+
|
|
25
|
+
constructor(props: IExecutionDetailsSectionProps) {
|
|
26
|
+
super(props);
|
|
27
|
+
this.state = { subscriptions: [], manifestIds: [] };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public componentDidMount() {
|
|
31
|
+
this.componentDidUpdate(this.props, this.state);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public componentWillUnmount() {
|
|
35
|
+
this.unsubscribeAll();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public componentDidUpdate(_prevProps: IExecutionDetailsSectionProps, prevState: IDeployStatusState) {
|
|
39
|
+
const manifests: IStageManifest[] = get(this.props.stage, ['context', 'outputs.manifests'], []).filter((m) => !!m);
|
|
40
|
+
const manifestIds = manifests.map((m) => CloudrunManifestService.manifestIdentifier(m)).sort();
|
|
41
|
+
if (prevState.manifestIds.join('') !== manifestIds.join('')) {
|
|
42
|
+
this.unsubscribeAll();
|
|
43
|
+
const subscriptions = manifests.map((manifest) => {
|
|
44
|
+
const id = CloudrunManifestService.manifestIdentifier(manifest);
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
unsubscribe: this.subscribeToManifestUpdates(id, manifest),
|
|
48
|
+
manifest: this.stageManifestToIManifest(manifest, this.props.stage.context.account),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
this.setState({ subscriptions, manifestIds });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private subscribeToManifestUpdates(id: string, manifest: IStageManifest): () => void {
|
|
56
|
+
const params = CloudrunManifestService.stageManifestToManifestParams(manifest, this.props.stage.context.account);
|
|
57
|
+
return CloudrunManifestService.subscribe(this.props.application, params, (updated: IManifest) => {
|
|
58
|
+
const idx = this.state.subscriptions.findIndex((sub) => sub.id === id);
|
|
59
|
+
if (idx !== -1) {
|
|
60
|
+
const subscription = { ...this.state.subscriptions[idx], manifest: updated };
|
|
61
|
+
const subscriptions = [...this.state.subscriptions];
|
|
62
|
+
subscriptions[idx] = subscription;
|
|
63
|
+
this.setState({ subscriptions });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private unsubscribeAll() {
|
|
69
|
+
this.state.subscriptions.forEach(({ unsubscribe }) => unsubscribe());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private stageManifestToIManifest(manifest: IStageManifest, account: string): IManifest {
|
|
73
|
+
return {
|
|
74
|
+
name: get(manifest, 'metadata.name', ''),
|
|
75
|
+
moniker: null,
|
|
76
|
+
account,
|
|
77
|
+
cloudProvider: 'cloudrun',
|
|
78
|
+
location: get(manifest, 'metadata.namespace', ''),
|
|
79
|
+
manifest: manifest,
|
|
80
|
+
status: {},
|
|
81
|
+
artifacts: [],
|
|
82
|
+
events: [],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public render() {
|
|
87
|
+
const { name: sectionName, current: currentSection, stage } = this.props;
|
|
88
|
+
const manifests: IManifest[] = this.state.subscriptions.filter((sub) => !!sub.manifest).map((sub) => sub.manifest);
|
|
89
|
+
return (
|
|
90
|
+
<ExecutionDetailsSection name={sectionName} current={currentSection}>
|
|
91
|
+
<StageFailureMessage stage={stage} message={stage.failureMessage} />
|
|
92
|
+
{manifests && (
|
|
93
|
+
<div className="row">
|
|
94
|
+
<div className="col-md-12">
|
|
95
|
+
<div className="well alert alert-info">
|
|
96
|
+
{manifests.map((manifest) => {
|
|
97
|
+
const uid =
|
|
98
|
+
manifest.manifest.metadata.uid || CloudrunManifestService.manifestIdentifier(manifest.manifest);
|
|
99
|
+
return <ManifestStatus key={uid} manifest={manifest} account={stage.context.account} />;
|
|
100
|
+
})}
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
</ExecutionDetailsSection>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { IManifest } from '@spinnaker/core';
|
|
3
|
+
|
|
4
|
+
interface IStatusClasses {
|
|
5
|
+
[key: string]: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const STATUS_PILLS: IStatusClasses = {
|
|
9
|
+
available: 'success',
|
|
10
|
+
stable: 'success',
|
|
11
|
+
paused: 'warn',
|
|
12
|
+
failed: 'danger',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export interface IDeployManifestStatusProps {
|
|
16
|
+
manifest: IManifest;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class DeployManifestStatusPills extends React.Component<IDeployManifestStatusProps> {
|
|
20
|
+
public render() {
|
|
21
|
+
const { manifest } = this.props;
|
|
22
|
+
if (manifest == null || manifest.status == null) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return Object.keys(manifest.status).map((statusKey, i) => {
|
|
26
|
+
const statusDescription = manifest.status[statusKey];
|
|
27
|
+
const statusClass = STATUS_PILLS[statusKey] || '';
|
|
28
|
+
const isStable = statusDescription.state;
|
|
29
|
+
const isUnstableWithMessage = !isStable && statusDescription.message;
|
|
30
|
+
return (
|
|
31
|
+
<span key={i}>
|
|
32
|
+
{isStable && (
|
|
33
|
+
<span title={statusDescription.message} className={`pill ${statusClass}`}>
|
|
34
|
+
{statusKey}
|
|
35
|
+
</span>
|
|
36
|
+
)}
|
|
37
|
+
{isUnstableWithMessage && (
|
|
38
|
+
<span title={statusDescription.message} className="pill warn">
|
|
39
|
+
{statusKey}
|
|
40
|
+
</span>
|
|
41
|
+
)}
|
|
42
|
+
<span> </span>
|
|
43
|
+
</span>
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { get, trim } from 'lodash';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { IManifest } from '@spinnaker/core';
|
|
5
|
+
import { AccountService, ReactInjector } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
const UNMAPPED_K8S_RESOURCE_STATE_KEY = 'cloudrunResource';
|
|
8
|
+
|
|
9
|
+
export interface IManifestDetailsProps {
|
|
10
|
+
manifest: IManifest;
|
|
11
|
+
linkName: string;
|
|
12
|
+
accountId: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IManifestDetailsState {
|
|
16
|
+
url: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class ManifestDetailsLink extends React.Component<IManifestDetailsProps, IManifestDetailsState> {
|
|
20
|
+
private spinnakerKindStateMap: { [k: string]: string } = {
|
|
21
|
+
// keys from clouddriver's CloudrunSpinnakerKindMap
|
|
22
|
+
serverGroupManagers: 'serverGroupManager',
|
|
23
|
+
serverGroups: 'serverGroup',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
constructor(props: IManifestDetailsProps) {
|
|
27
|
+
super(props);
|
|
28
|
+
this.state = {
|
|
29
|
+
url: '',
|
|
30
|
+
};
|
|
31
|
+
this.loadUrl();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private canOpen(): boolean {
|
|
35
|
+
return !!this.props.manifest.manifest && !!this.state.url;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private spinnakerKindFromCloudrunKind(kind: string, kindMap: { [k: string]: string }) {
|
|
39
|
+
const foundKind = Object.keys(kindMap).find((k) => k.toLowerCase() === kind.toLowerCase());
|
|
40
|
+
return kindMap[foundKind];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private resourceRegion(): string {
|
|
44
|
+
return trim(
|
|
45
|
+
get(this.props, ['manifest', 'manifest', 'metadata', 'annotations', 'artifact.spinnaker.io/location'], ''),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private getStateParams(stateKey: string): any {
|
|
50
|
+
const kind = this.props.manifest.manifest.kind.toLowerCase();
|
|
51
|
+
const name = this.props.manifest.manifest.metadata.name;
|
|
52
|
+
const region = this.resourceRegion();
|
|
53
|
+
const params: { [k: string]: string } = {
|
|
54
|
+
accountId: this.props.accountId,
|
|
55
|
+
provider: 'cloudrun',
|
|
56
|
+
region,
|
|
57
|
+
reg: region, // Filters the list of clusters on the Clusters screen to those in the same namespace
|
|
58
|
+
[stateKey]: `${kind} ${name}`,
|
|
59
|
+
};
|
|
60
|
+
if (!params.region && kind === 'namespace' && stateKey === UNMAPPED_K8S_RESOURCE_STATE_KEY) {
|
|
61
|
+
params.region = name;
|
|
62
|
+
}
|
|
63
|
+
if (!params.region || params.region === '') {
|
|
64
|
+
params.region = '_';
|
|
65
|
+
}
|
|
66
|
+
return params;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private loadUrl() {
|
|
70
|
+
const kind: string = get(this.props, ['manifest', 'manifest', 'kind'], '');
|
|
71
|
+
const { accountId } = this.props;
|
|
72
|
+
AccountService.getAccountDetails(accountId).then((account) => {
|
|
73
|
+
const spinnakerKind = this.spinnakerKindFromCloudrunKind(kind, account.spinnakerKindMap);
|
|
74
|
+
const stateKey = this.spinnakerKindStateMap[spinnakerKind] || UNMAPPED_K8S_RESOURCE_STATE_KEY;
|
|
75
|
+
const params = this.getStateParams(stateKey);
|
|
76
|
+
const url = ReactInjector.$state.href(`home.applications.application.insight.clusters.${stateKey}`, params);
|
|
77
|
+
this.setState({ url });
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public render() {
|
|
82
|
+
if (this.canOpen()) {
|
|
83
|
+
return (
|
|
84
|
+
<a href={this.state.url} className="clickable">
|
|
85
|
+
{this.props.linkName}
|
|
86
|
+
</a>
|
|
87
|
+
);
|
|
88
|
+
} else {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { get, trim } from 'lodash';
|
|
2
|
+
import { DateTime } from 'luxon';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import type { IManifest, IManifestEvent } from '@spinnaker/core';
|
|
6
|
+
import { JobEventBasedPodNameProvider, JobManifestPodLogs, relativeTime } from '@spinnaker/core';
|
|
7
|
+
|
|
8
|
+
export interface IManifestEventsProps {
|
|
9
|
+
manifest: IManifest;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class ManifestEvents extends React.Component<IManifestEventsProps> {
|
|
13
|
+
private pillStyle(e: IManifestEvent): string {
|
|
14
|
+
if (e.type === 'Warning') {
|
|
15
|
+
return 'alert';
|
|
16
|
+
} else if (e.type === 'Normal') {
|
|
17
|
+
return 'success';
|
|
18
|
+
} else {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public render() {
|
|
24
|
+
if (!this.props.manifest) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (this.props.manifest && (!this.props.manifest.events || this.props.manifest.events.length === 0)) {
|
|
28
|
+
return <div>No recent events found - Cloud Run does not store events for long.</div>;
|
|
29
|
+
}
|
|
30
|
+
const { events } = this.props.manifest;
|
|
31
|
+
const namespace = trim(
|
|
32
|
+
get(this.props.manifest, ['manifest', 'metadata', 'annotations', 'artifact.spinnaker.io/location'], ''),
|
|
33
|
+
);
|
|
34
|
+
return events.map((e, i) => {
|
|
35
|
+
const firstTimestamp = get(e, 'firstTimestamp', '');
|
|
36
|
+
const lastTimestamp = get(e, 'lastTimestamp', '');
|
|
37
|
+
let firstEpochMilliseconds = 0;
|
|
38
|
+
let lastEpochMilliseconds = 0;
|
|
39
|
+
if (firstTimestamp) {
|
|
40
|
+
firstEpochMilliseconds = DateTime.fromISO(firstTimestamp).toMillis();
|
|
41
|
+
}
|
|
42
|
+
if (lastTimestamp) {
|
|
43
|
+
lastEpochMilliseconds = DateTime.fromISO(lastTimestamp).toMillis();
|
|
44
|
+
}
|
|
45
|
+
return (
|
|
46
|
+
<div key={get(e, ['metadata', 'uid'], String(i))} className="info">
|
|
47
|
+
<div className="horizontal">
|
|
48
|
+
{e.count && (
|
|
49
|
+
<div className={`pill ${this.pillStyle(e)}`}>
|
|
50
|
+
{e.count} × <b>{e.reason}</b>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
{(e.firstTimestamp || e.lastTimestamp) && (
|
|
55
|
+
<div>
|
|
56
|
+
{e.firstTimestamp === e.lastTimestamp && (
|
|
57
|
+
<div>
|
|
58
|
+
<i>{relativeTime(firstEpochMilliseconds)}</i>
|
|
59
|
+
</div>
|
|
60
|
+
)}
|
|
61
|
+
{e.firstTimestamp !== e.lastTimestamp && (
|
|
62
|
+
<div>
|
|
63
|
+
<div>
|
|
64
|
+
First Occurrence: <i>{relativeTime(firstEpochMilliseconds)}</i>
|
|
65
|
+
</div>
|
|
66
|
+
<div>
|
|
67
|
+
Last Occurrence: <i>{relativeTime(lastEpochMilliseconds)}</i>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
<div>{e.message}</div>
|
|
74
|
+
<div>
|
|
75
|
+
<JobManifestPodLogs
|
|
76
|
+
account={this.props.manifest.account}
|
|
77
|
+
location={namespace}
|
|
78
|
+
podNamesProviders={[new JobEventBasedPodNameProvider(this.props.manifest, e)]}
|
|
79
|
+
linkName="Console Output (Raw)"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
{i !== events.length - 1 && <br />}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
dl.manifest-status {
|
|
2
|
+
display: block;
|
|
3
|
+
margin: 0;
|
|
4
|
+
|
|
5
|
+
dt,
|
|
6
|
+
dd {
|
|
7
|
+
display: inline-block;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
dt {
|
|
11
|
+
margin-right: 4px;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.manifest-support-links {
|
|
16
|
+
margin: 0 0 1em;
|
|
17
|
+
a {
|
|
18
|
+
margin-right: 2em;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.manifest-events {
|
|
23
|
+
margin: 0 0 2em;
|
|
24
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { dump } from 'js-yaml';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { IManifest } from '@spinnaker/core';
|
|
5
|
+
import { CopyToClipboard, ManifestYaml } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import { DeployManifestStatusPills } from './DeployStatusPills';
|
|
8
|
+
import { ManifestDetailsLink } from './ManifestDetailsLink';
|
|
9
|
+
import { ManifestEvents } from './ManifestEvents';
|
|
10
|
+
|
|
11
|
+
import './ManifestStatus.less';
|
|
12
|
+
|
|
13
|
+
export interface IManifestStatusProps {
|
|
14
|
+
account: string;
|
|
15
|
+
manifest: IManifest;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function ManifestStatus({ account, manifest }: IManifestStatusProps) {
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<dl className="manifest-status" key="manifest-status">
|
|
22
|
+
<dt>{manifest.manifest.kind}</dt>
|
|
23
|
+
<dd>
|
|
24
|
+
<CopyToClipboard
|
|
25
|
+
displayText={true}
|
|
26
|
+
text={manifest.manifest.metadata.name}
|
|
27
|
+
toolTip={`Copy ${manifest.manifest.metadata.name}`}
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
<DeployManifestStatusPills manifest={manifest} />
|
|
31
|
+
</dd>
|
|
32
|
+
</dl>
|
|
33
|
+
<div className="manifest-support-links" key="manifest-support-links">
|
|
34
|
+
<ManifestYaml
|
|
35
|
+
linkName="YAML"
|
|
36
|
+
manifestText={dump(manifest.manifest)}
|
|
37
|
+
modalTitle={manifest.manifest.metadata.name}
|
|
38
|
+
/>
|
|
39
|
+
<ManifestDetailsLink linkName="Details" manifest={manifest} accountId={account} />
|
|
40
|
+
</div>
|
|
41
|
+
<div className="manifest-events pad-left" key="manifest-events">
|
|
42
|
+
<ManifestEvents manifest={manifest} />
|
|
43
|
+
</div>
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface IServerGroupNamePreviewProps {
|
|
4
|
+
application: string;
|
|
5
|
+
stack: string;
|
|
6
|
+
details: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class ServerGroupNamePreview extends React.Component<IServerGroupNamePreviewProps> {
|
|
10
|
+
public render() {
|
|
11
|
+
const application = this.props.application ? this.props.application : '';
|
|
12
|
+
const stack = this.props.stack ? `-${this.props.stack}` : '';
|
|
13
|
+
|
|
14
|
+
const details = this.props.details ? `-${this.props.details}` : '';
|
|
15
|
+
return (
|
|
16
|
+
<pre style={{ textAlign: 'center' }}>
|
|
17
|
+
<p> Your server group will be in the cluster:</p>
|
|
18
|
+
<p>
|
|
19
|
+
<b>
|
|
20
|
+
{application}
|
|
21
|
+
{stack}
|
|
22
|
+
{details}
|
|
23
|
+
</b>
|
|
24
|
+
</p>
|
|
25
|
+
</pre>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -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 { CLOUDRUN_LOAD_BALANCER_CHOICE_MODAL_CTRL } from './loadBalancerChoice.modal.controller';
|
|
10
|
+
|
|
11
|
+
class CloudrunEditLoadBalancerStageCtrl 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 = 'cloudrun';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public addLoadBalancer(): void {
|
|
19
|
+
this.$uibModal
|
|
20
|
+
.open({
|
|
21
|
+
templateUrl: require('./loadBalancerChoice.modal.html'),
|
|
22
|
+
controller: `cloudrunLoadBalancerChoiceModelCtrl 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('cloudrun', '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 CLOUDRUN_EDIT_LOAD_BALANCER_STAGE = 'spinnaker.cloudrun.pipeline.stage.editLoadBalancerStage';
|
|
59
|
+
module(CLOUDRUN_EDIT_LOAD_BALANCER_STAGE, [CLOUDRUN_LOAD_BALANCER_CHOICE_MODAL_CTRL])
|
|
60
|
+
.config(() => {
|
|
61
|
+
Registry.pipeline.registerStage({
|
|
62
|
+
label: 'Edit Load Balancer (Cloud Run)',
|
|
63
|
+
description: 'Edits a load balancer',
|
|
64
|
+
key: 'upsertCloudrunLoadBalancers',
|
|
65
|
+
cloudProvider: 'cloudrun',
|
|
66
|
+
templateUrl: require('./editLoadBalancerStage.html'),
|
|
67
|
+
executionDetailsUrl: require('./editLoadBalancerExecutionDetails.html'),
|
|
68
|
+
executionConfigSections: ['editLoadBalancerConfig', 'taskStatus'],
|
|
69
|
+
controller: 'cloudrunEditLoadBalancerStageCtrl',
|
|
70
|
+
controllerAs: 'editLoadBalancerStageCtrl',
|
|
71
|
+
validators: [],
|
|
72
|
+
});
|
|
73
|
+
})
|
|
74
|
+
.controller('cloudrunEditLoadBalancerStageCtrl', CloudrunEditLoadBalancerStageCtrl);
|
|
@@ -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 CloudrunLoadBalancerChoiceModalCtrl 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('cloudrun', '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 === 'cloudrun',
|
|
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 CLOUDRUN_LOAD_BALANCER_CHOICE_MODAL_CTRL = 'spinnaker.Cloudrun.loadBalancerChoiceModal.controller';
|
|
62
|
+
module(CLOUDRUN_LOAD_BALANCER_CHOICE_MODAL_CTRL, []).controller(
|
|
63
|
+
'cloudrunLoadBalancerChoiceModelCtrl',
|
|
64
|
+
CloudrunLoadBalancerChoiceModalCtrl,
|
|
65
|
+
);
|