@spinnaker/cloudrun 2026.2.2 → 2026.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cloudrun.module.d.ts +1 -1
- package/dist/common/ComponentUrlDetails.d.ts +7 -0
- package/dist/common/LoadBalancerMessage.d.ts +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/instance/details/CloudrunInstanceDetails.d.ts +23 -0
- package/dist/loadBalancer/configure/wizard/CloudrunLoadBalancerModal.d.ts +32 -0
- package/dist/loadBalancer/details/CloudrunLoadBalancerActions.d.ts +3 -0
- package/dist/loadBalancer/details/sections.d.ts +7 -0
- package/dist/loadBalancer/details/useCloudrunLoadBalancerDetails.d.ts +4 -0
- package/dist/loadBalancer/index.d.ts +4 -2
- package/dist/loadBalancer/loadBalancerTransformer.d.ts +2 -6
- package/dist/manifest/manifest.service.d.ts +5 -0
- package/dist/manifest/manifestCommandBuilder.service.d.ts +1 -1
- package/dist/pipeline/stages/deployManifest/manifestStatus/DeployStatus.d.ts +1 -0
- package/dist/pipeline/stages/deployManifest/manifestStatus/ManifestDetailsLink.d.ts +6 -3
- package/dist/pipeline/stages/editLoadBalancer/CloudrunEditLoadBalancerExecutionDetails.d.ts +7 -0
- package/dist/pipeline/stages/editLoadBalancer/CloudrunEditLoadBalancerStageConfig.d.ts +30 -0
- package/dist/pipeline/stages/editLoadBalancer/cloudrunEditLoadBalancerStage.d.ts +2 -1
- package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +4 -6
- package/dist/serverGroup/configure/wizard/BasicSettings.d.ts +5 -2
- package/dist/serverGroup/configure/wizard/serverGroupWizard.d.ts +18 -6
- package/dist/serverGroup/details/CloudrunServerGroupActions.d.ts +6 -0
- package/dist/serverGroup/details/cloudrunServerGroupDetailsGetter.d.ts +3 -0
- package/dist/serverGroup/details/sections.d.ts +5 -0
- package/dist/serverGroup/index.d.ts +3 -1
- package/dist/serverGroup/serverGroupTransformer.service.d.ts +1 -5
- package/package.json +4 -11
- package/src/cloudrun.module.spec.ts +22 -0
- package/src/cloudrun.module.ts +54 -59
- package/src/common/ComponentUrlDetails.tsx +25 -0
- package/src/common/LoadBalancerMessage.tsx +17 -0
- package/src/index.ts +2 -1
- package/src/instance/details/CloudrunInstanceDetails.tsx +164 -0
- package/src/loadBalancer/configure/wizard/CloudrunLoadBalancerModal.spec.tsx +85 -0
- package/src/loadBalancer/configure/wizard/CloudrunLoadBalancerModal.tsx +395 -0
- package/src/loadBalancer/details/CloudrunLoadBalancerActions.tsx +66 -0
- package/src/loadBalancer/details/sections.spec.ts +19 -0
- package/src/loadBalancer/details/sections.tsx +91 -0
- package/src/loadBalancer/details/useCloudrunLoadBalancerDetails.ts +56 -0
- package/src/loadBalancer/index.ts +4 -2
- package/src/loadBalancer/loadBalancerTransformer.ts +3 -13
- package/src/manifest/manifest.service.ts +18 -1
- package/src/manifest/manifestCommandBuilder.service.ts +46 -53
- package/src/pipeline/stages/deployManifest/manifestStatus/DeployStatus.tsx +33 -4
- package/src/pipeline/stages/deployManifest/manifestStatus/ManifestDetailsLink.spec.tsx +35 -0
- package/src/pipeline/stages/deployManifest/manifestStatus/ManifestDetailsLink.tsx +10 -5
- package/src/pipeline/stages/editLoadBalancer/CloudrunEditLoadBalancerExecutionDetails.tsx +70 -0
- package/src/pipeline/stages/editLoadBalancer/CloudrunEditLoadBalancerStageConfig.tsx +245 -0
- package/src/pipeline/stages/editLoadBalancer/cloudrunEditLoadBalancerStage.spec.ts +205 -0
- package/src/pipeline/stages/editLoadBalancer/cloudrunEditLoadBalancerStage.ts +14 -71
- package/src/serverGroup/configure/serverGroupCommandBuilder.service.ts +5 -15
- package/src/serverGroup/configure/wizard/BasicSettings.tsx +10 -8
- package/src/serverGroup/configure/wizard/serverGroupWizard.tsx +59 -15
- package/src/serverGroup/details/CloudrunServerGroupActions.tsx +80 -0
- package/src/serverGroup/details/cloudrunServerGroupDetailsGetter.spec.ts +46 -0
- package/src/serverGroup/details/cloudrunServerGroupDetailsGetter.ts +53 -0
- package/src/serverGroup/details/sections.tsx +60 -0
- package/src/serverGroup/index.ts +3 -1
- package/src/serverGroup/routerConsumers.spec.tsx +152 -0
- package/src/serverGroup/serverGroupTransformer.service.ts +2 -12
- package/dist/common/componentUrlDetails.component.d.ts +0 -1
- package/dist/common/conditionalDescriptionListItem.component.d.ts +0 -1
- package/dist/common/loadBalancerMessage.component.d.ts +0 -1
- package/dist/instance/details/details.controller.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/allocationConfigurationRow.component.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/basicSettings.component.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.d.ts +0 -1
- package/dist/loadBalancer/configure/wizard/wizard.controller.d.ts +0 -2
- package/dist/loadBalancer/details/details.controller.d.ts +0 -1
- package/dist/pipeline/pipeline.module.d.ts +0 -1
- package/dist/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.d.ts +0 -1
- package/dist/serverGroup/details/details.controller.d.ts +0 -1
- package/src/common/componentUrlDetails.component.ts +0 -22
- package/src/common/conditionalDescriptionListItem.component.ts +0 -37
- package/src/common/loadBalancerMessage.component.html +0 -11
- package/src/common/loadBalancerMessage.component.ts +0 -13
- package/src/instance/details/details.controller.ts +0 -84
- package/src/instance/details/details.html +0 -68
- package/src/loadBalancer/configure/wizard/allocationConfigurationRow.component.ts +0 -70
- package/src/loadBalancer/configure/wizard/basicSettings.component.html +0 -44
- package/src/loadBalancer/configure/wizard/basicSettings.component.ts +0 -86
- package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html +0 -45
- package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.ts +0 -84
- package/src/loadBalancer/configure/wizard/wizard.controller.ts +0 -157
- package/src/loadBalancer/configure/wizard/wizard.html +0 -39
- package/src/loadBalancer/configure/wizard/wizard.less +0 -9
- package/src/loadBalancer/details/details.controller.ts +0 -140
- package/src/loadBalancer/details/details.html +0 -89
- package/src/pipeline/pipeline.module.ts +0 -6
- package/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html +0 -33
- package/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html +0 -51
- package/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.ts +0 -65
- package/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html +0 -53
- package/src/serverGroup/details/details.controller.ts +0 -134
- package/src/serverGroup/details/details.html +0 -94
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Dropdown, MenuItem } from 'react-bootstrap';
|
|
3
|
+
|
|
4
|
+
import type { ILoadBalancerActionsProps, ILoadBalancerDeleteCommand } from '@spinnaker/core';
|
|
5
|
+
import { ConfirmationModalService, LoadBalancerWriter } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import type { ICloudrunLoadBalancer } from '../../common/domain';
|
|
8
|
+
import { CloudrunLoadBalancerModal } from '../configure/wizard/CloudrunLoadBalancerModal';
|
|
9
|
+
|
|
10
|
+
export function CloudrunLoadBalancerActions({ app, loadBalancer }: ILoadBalancerActionsProps) {
|
|
11
|
+
const cloudrunLoadBalancer = loadBalancer as ICloudrunLoadBalancer;
|
|
12
|
+
const canDeleteLoadBalancer = cloudrunLoadBalancer.name !== 'default';
|
|
13
|
+
|
|
14
|
+
const editLoadBalancer = () => {
|
|
15
|
+
CloudrunLoadBalancerModal.show({ app, loadBalancer: cloudrunLoadBalancer, isNew: false, forPipelineConfig: false });
|
|
16
|
+
};
|
|
17
|
+
const getConfirmationModalBodyHtml = (): string => {
|
|
18
|
+
const serverGroupNames = cloudrunLoadBalancer.serverGroups.map((serverGroup) => serverGroup.name);
|
|
19
|
+
if (!serverGroupNames?.length) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (serverGroupNames.length > 1) {
|
|
23
|
+
const listOfServerGroupNames = serverGroupNames.map((name) => `<li>${name}</li>`).join('');
|
|
24
|
+
return `<div class="alert alert-warning"><p>Deleting <b>${cloudrunLoadBalancer.name}</b> will destroy the following server groups:<ul>${listOfServerGroupNames}</ul></p></div>`;
|
|
25
|
+
}
|
|
26
|
+
return `<div class="alert alert-warning"><p>Deleting <b>${cloudrunLoadBalancer.name}</b> will destroy <b>${serverGroupNames[0]}</b>.</p></div>`;
|
|
27
|
+
};
|
|
28
|
+
const deleteLoadBalancer = () => {
|
|
29
|
+
const submitMethod = () => {
|
|
30
|
+
const command: ILoadBalancerDeleteCommand = {
|
|
31
|
+
cloudProvider: cloudrunLoadBalancer.cloudProvider,
|
|
32
|
+
loadBalancerName: cloudrunLoadBalancer.name,
|
|
33
|
+
credentials: cloudrunLoadBalancer.account,
|
|
34
|
+
};
|
|
35
|
+
return LoadBalancerWriter.deleteLoadBalancer(command, app);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
ConfirmationModalService.confirm({
|
|
39
|
+
header: `Really delete ${cloudrunLoadBalancer.name}?`,
|
|
40
|
+
buttonText: `Delete ${cloudrunLoadBalancer.name}`,
|
|
41
|
+
body: getConfirmationModalBodyHtml(),
|
|
42
|
+
account: cloudrunLoadBalancer.account,
|
|
43
|
+
taskMonitorConfig: {
|
|
44
|
+
application: app,
|
|
45
|
+
title: `Deleting ${cloudrunLoadBalancer.name}`,
|
|
46
|
+
},
|
|
47
|
+
submitMethod,
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Dropdown className="dropdown" id="cloudrun-load-balancer-actions-dropdown">
|
|
53
|
+
<Dropdown.Toggle className="btn btn-sm btn-primary dropdown-toggle">Load Balancer Actions</Dropdown.Toggle>
|
|
54
|
+
<Dropdown.Menu>
|
|
55
|
+
<MenuItem onClick={editLoadBalancer}>Edit Load Balancer</MenuItem>
|
|
56
|
+
{canDeleteLoadBalancer ? (
|
|
57
|
+
<MenuItem onClick={deleteLoadBalancer}>Delete Load Balancer</MenuItem>
|
|
58
|
+
) : (
|
|
59
|
+
<MenuItem disabled={true} title="You cannot delete a default service.">
|
|
60
|
+
Delete Load Balancer
|
|
61
|
+
</MenuItem>
|
|
62
|
+
)}
|
|
63
|
+
</Dropdown.Menu>
|
|
64
|
+
</Dropdown>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { compareCloudrunLoadBalancerServerGroups } from './sections';
|
|
2
|
+
|
|
3
|
+
describe('compareCloudrunLoadBalancerServerGroups', () => {
|
|
4
|
+
it('sorts enabled server groups first and names descending within each state', () => {
|
|
5
|
+
const sorted = [
|
|
6
|
+
{ name: 'alpha', isDisabled: true },
|
|
7
|
+
{ name: 'zulu', isDisabled: false },
|
|
8
|
+
{ name: 'alpha', isDisabled: false },
|
|
9
|
+
{ name: 'zulu', isDisabled: true },
|
|
10
|
+
].sort(compareCloudrunLoadBalancerServerGroups as any);
|
|
11
|
+
|
|
12
|
+
expect(sorted.map((serverGroup) => `${serverGroup.isDisabled}:${serverGroup.name}`)).toEqual([
|
|
13
|
+
'false:zulu',
|
|
14
|
+
'false:alpha',
|
|
15
|
+
'true:zulu',
|
|
16
|
+
'true:alpha',
|
|
17
|
+
]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { UISref } from '@uirouter/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { ILoadBalancerDetailsSectionProps } from '@spinnaker/core';
|
|
5
|
+
import { AccountTag, CollapsibleSection, HealthCounts } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import { ComponentUrlDetails } from '../../common/ComponentUrlDetails';
|
|
8
|
+
import type { ICloudrunLoadBalancer } from '../../common/domain';
|
|
9
|
+
|
|
10
|
+
export function compareCloudrunLoadBalancerServerGroups(a: any, b: any): number {
|
|
11
|
+
if (a.isDisabled !== b.isDisabled) {
|
|
12
|
+
return a.isDisabled ? 1 : -1;
|
|
13
|
+
}
|
|
14
|
+
return b.name.localeCompare(a.name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function CloudrunLoadBalancerDetailsSection({ loadBalancer }: ILoadBalancerDetailsSectionProps) {
|
|
18
|
+
const cloudrunLoadBalancer = loadBalancer as ICloudrunLoadBalancer;
|
|
19
|
+
return (
|
|
20
|
+
<CollapsibleSection heading="Load Balancer Details" defaultExpanded={true}>
|
|
21
|
+
<dl className="dl-horizontal dl-narrow">
|
|
22
|
+
<dt>In</dt>
|
|
23
|
+
<dd>
|
|
24
|
+
<AccountTag account={cloudrunLoadBalancer.account} />
|
|
25
|
+
</dd>
|
|
26
|
+
<dt>Region</dt>
|
|
27
|
+
<dd>{cloudrunLoadBalancer.region}</dd>
|
|
28
|
+
{!!cloudrunLoadBalancer.serverGroups.length && <dt>Server Groups</dt>}
|
|
29
|
+
{!!cloudrunLoadBalancer.serverGroups.length && (
|
|
30
|
+
<dd>
|
|
31
|
+
<ul>
|
|
32
|
+
{cloudrunLoadBalancer.serverGroups
|
|
33
|
+
.slice()
|
|
34
|
+
.sort(compareCloudrunLoadBalancerServerGroups)
|
|
35
|
+
.map((serverGroup) => (
|
|
36
|
+
<li key={serverGroup.name}>
|
|
37
|
+
<UISref
|
|
38
|
+
to="^.serverGroup"
|
|
39
|
+
params={{
|
|
40
|
+
region: serverGroup.region,
|
|
41
|
+
accountId: serverGroup.account,
|
|
42
|
+
serverGroup: serverGroup.name,
|
|
43
|
+
provider: 'cloudrun',
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
<a>{serverGroup.name}</a>
|
|
47
|
+
</UISref>
|
|
48
|
+
</li>
|
|
49
|
+
))}
|
|
50
|
+
</ul>
|
|
51
|
+
</dd>
|
|
52
|
+
)}
|
|
53
|
+
</dl>
|
|
54
|
+
</CollapsibleSection>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function CloudrunLoadBalancerStatusSection({ loadBalancer }: ILoadBalancerDetailsSectionProps) {
|
|
59
|
+
return (
|
|
60
|
+
<CollapsibleSection heading="Status" defaultExpanded={true}>
|
|
61
|
+
<HealthCounts className="pull-left" container={loadBalancer.instanceCounts} />
|
|
62
|
+
</CollapsibleSection>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function CloudrunLoadBalancerTrafficSplitSection({ loadBalancer }: ILoadBalancerDetailsSectionProps) {
|
|
67
|
+
const cloudrunLoadBalancer = loadBalancer as ICloudrunLoadBalancer;
|
|
68
|
+
return (
|
|
69
|
+
<CollapsibleSection heading="Traffic Split" defaultExpanded={true}>
|
|
70
|
+
<dl className="dl-horizontal dl-narrow">
|
|
71
|
+
<ul>
|
|
72
|
+
{cloudrunLoadBalancer.split?.trafficTargets?.map((trafficTarget) => (
|
|
73
|
+
<li key={trafficTarget.revisionName}>
|
|
74
|
+
{trafficTarget.revisionName}:<span className="pull-right">{trafficTarget.percent}</span>
|
|
75
|
+
</li>
|
|
76
|
+
))}
|
|
77
|
+
</ul>
|
|
78
|
+
</dl>
|
|
79
|
+
</CollapsibleSection>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function CloudrunLoadBalancerDnsSection({ loadBalancer }: ILoadBalancerDetailsSectionProps) {
|
|
84
|
+
return (
|
|
85
|
+
<CollapsibleSection heading="DNS" defaultExpanded={true}>
|
|
86
|
+
<dl className="dl-narrow">
|
|
87
|
+
<ComponentUrlDetails component={loadBalancer as any} />
|
|
88
|
+
</dl>
|
|
89
|
+
</CollapsibleSection>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useDataSource } from '@spinnaker/core';
|
|
4
|
+
import type { IUseDetailsHookProps, UseDetailsHook, UseDetailsResult } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
import type { ICloudrunLoadBalancer } from '../../common/domain';
|
|
7
|
+
|
|
8
|
+
export const useCloudrunLoadBalancerDetails: UseDetailsHook<ICloudrunLoadBalancer> = ({
|
|
9
|
+
app,
|
|
10
|
+
loadBalancerParams,
|
|
11
|
+
autoClose,
|
|
12
|
+
}: IUseDetailsHookProps): UseDetailsResult<ICloudrunLoadBalancer> => {
|
|
13
|
+
const dataSource = app.getDataSource('loadBalancers');
|
|
14
|
+
const { data: loadBalancers, loaded, error, refresh } = useDataSource<ICloudrunLoadBalancer[]>(dataSource);
|
|
15
|
+
const data = useMemo(() => {
|
|
16
|
+
if (!loaded || error) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const loadBalancer = findCloudrunLoadBalancer(loadBalancers, loadBalancerParams);
|
|
21
|
+
if (!loadBalancer) {
|
|
22
|
+
autoClose();
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return loadBalancer;
|
|
27
|
+
}, [
|
|
28
|
+
loaded,
|
|
29
|
+
error,
|
|
30
|
+
loadBalancers,
|
|
31
|
+
loadBalancerParams.name,
|
|
32
|
+
loadBalancerParams.accountId,
|
|
33
|
+
loadBalancerParams.region,
|
|
34
|
+
autoClose,
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
data,
|
|
39
|
+
loading: !loaded,
|
|
40
|
+
error: error || null,
|
|
41
|
+
refetch: async () => refresh(),
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function findCloudrunLoadBalancer(
|
|
46
|
+
loadBalancers: ICloudrunLoadBalancer[],
|
|
47
|
+
loadBalancerParams: IUseDetailsHookProps['loadBalancerParams'],
|
|
48
|
+
) {
|
|
49
|
+
return loadBalancers.find((candidate) => {
|
|
50
|
+
return (
|
|
51
|
+
candidate.name === loadBalancerParams.name &&
|
|
52
|
+
candidate.account === loadBalancerParams.accountId &&
|
|
53
|
+
candidate.region === loadBalancerParams.region
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export * from './loadBalancerTransformer';
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
2
|
+
export * from './configure/wizard/CloudrunLoadBalancerModal';
|
|
3
|
+
export * from './details/CloudrunLoadBalancerActions';
|
|
4
|
+
export * from './details/sections';
|
|
5
|
+
export * from './details/useCloudrunLoadBalancerDetails';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { module } from 'angular';
|
|
2
1
|
import { camelCase, chain, cloneDeep, filter, get, has, reduce } from 'lodash';
|
|
3
2
|
|
|
4
3
|
import type {
|
|
@@ -75,9 +74,7 @@ export class CloudrunLoadBalancerUpsertDescription implements ILoadBalancerUpser
|
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
export class CloudrunLoadBalancerTransformer {
|
|
78
|
-
public
|
|
79
|
-
constructor(private $q: ng.IQService) {}
|
|
80
|
-
public normalizeLoadBalancer(loadBalancer: ILoadBalancer): PromiseLike<ILoadBalancer> {
|
|
77
|
+
public normalizeLoadBalancer(loadBalancer: ILoadBalancer): Promise<ILoadBalancer> {
|
|
81
78
|
loadBalancer.provider = loadBalancer.type;
|
|
82
79
|
loadBalancer.instanceCounts = this.buildInstanceCounts(loadBalancer.serverGroups);
|
|
83
80
|
loadBalancer.instances = [];
|
|
@@ -95,13 +92,13 @@ export class CloudrunLoadBalancerTransformer {
|
|
|
95
92
|
|
|
96
93
|
const activeServerGroups = filter(loadBalancer.serverGroups, { isDisabled: false });
|
|
97
94
|
loadBalancer.instances = chain(activeServerGroups).map('instances').flatten().value() as IInstance[];
|
|
98
|
-
return
|
|
95
|
+
return Promise.resolve(loadBalancer);
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
public convertLoadBalancerForEditing(
|
|
102
99
|
loadBalancer: ICloudrunLoadBalancer,
|
|
103
100
|
application: Application,
|
|
104
|
-
):
|
|
101
|
+
): Promise<ICloudrunLoadBalancer> {
|
|
105
102
|
return application
|
|
106
103
|
.getDataSource('loadBalancers')
|
|
107
104
|
.ready()
|
|
@@ -159,10 +156,3 @@ export class CloudrunLoadBalancerTransformer {
|
|
|
159
156
|
return instance as IInstance;
|
|
160
157
|
}
|
|
161
158
|
}
|
|
162
|
-
|
|
163
|
-
export const CLOUDRUN_LOAD_BALANCER_TRANSFORMER = 'spinnaker.cloudrun.loadBalancer.transformer.service';
|
|
164
|
-
|
|
165
|
-
module(CLOUDRUN_LOAD_BALANCER_TRANSFORMER, []).service(
|
|
166
|
-
'cloudrunLoadBalancerTransformer',
|
|
167
|
-
CloudrunLoadBalancerTransformer,
|
|
168
|
-
);
|
|
@@ -10,6 +10,23 @@ export interface IStageManifest {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// A manifest that the artifact-store entity storage feature has replaced with a
|
|
14
|
+
// reference, rather than expanding it back into a real manifest before deck ever
|
|
15
|
+
// sees it (e.g. gate isn't configured to expand these on read).
|
|
16
|
+
export interface IStoredManifestReference {
|
|
17
|
+
type: string;
|
|
18
|
+
reference: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isStoredManifestReference(manifest: unknown): manifest is IStoredManifestReference {
|
|
22
|
+
return (
|
|
23
|
+
!!manifest &&
|
|
24
|
+
typeof manifest === 'object' &&
|
|
25
|
+
(manifest as IStoredManifestReference).type === 'remote/map/base64' &&
|
|
26
|
+
typeof (manifest as IStoredManifestReference).reference === 'string'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
13
30
|
export interface IManifestParams {
|
|
14
31
|
account: string;
|
|
15
32
|
location: string;
|
|
@@ -21,7 +38,7 @@ export type IManifestCallback = (manifest: IManifest) => void;
|
|
|
21
38
|
export class CloudrunManifestService {
|
|
22
39
|
public static subscribe(app: Application, params: IManifestParams, fn: IManifestCallback): () => void {
|
|
23
40
|
CloudrunManifestService.updateManifest(params, fn);
|
|
24
|
-
return app.onRefresh(
|
|
41
|
+
return app.onRefresh(() => CloudrunManifestService.updateManifest(params, fn));
|
|
25
42
|
}
|
|
26
43
|
|
|
27
44
|
private static updateManifest(params: IManifestParams, fn: IManifestCallback) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { cloneDeep } from 'lodash';
|
|
2
|
-
import { $q } from 'ngimport';
|
|
3
2
|
|
|
4
|
-
import type { Application,
|
|
3
|
+
import type { Application, IMoniker } from '@spinnaker/core';
|
|
5
4
|
import { AccountService } from '@spinnaker/core';
|
|
6
5
|
|
|
7
6
|
import { ManifestSource } from './ManifestSource';
|
|
@@ -56,61 +55,55 @@ export class CloudrunManifestCommandBuilder {
|
|
|
56
55
|
sourceManifest?: any,
|
|
57
56
|
sourceMoniker?: IMoniker,
|
|
58
57
|
sourceAccount?: string,
|
|
59
|
-
):
|
|
58
|
+
): Promise<ICloudrunManifestCommandData> {
|
|
60
59
|
const dataToFetch = {
|
|
61
60
|
accounts: AccountService.getAllAccountDetailsForProvider('cloudrun'),
|
|
62
61
|
artifactAccounts: AccountService.getArtifactAccounts(),
|
|
63
62
|
};
|
|
64
63
|
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
},
|
|
110
|
-
metadata: {
|
|
111
|
-
backingData,
|
|
112
|
-
},
|
|
113
|
-
} as ICloudrunManifestCommandData;
|
|
114
|
-
});
|
|
64
|
+
return Promise.all([dataToFetch.accounts, dataToFetch.artifactAccounts]).then(([accounts, artifactAccounts]) => {
|
|
65
|
+
const backingData = { accounts, artifactAccounts };
|
|
66
|
+
|
|
67
|
+
const account = accounts.some((a) => a.name === sourceAccount)
|
|
68
|
+
? accounts.find((a) => a.name === sourceAccount).name
|
|
69
|
+
: accounts.length
|
|
70
|
+
? accounts[0].name
|
|
71
|
+
: null;
|
|
72
|
+
|
|
73
|
+
let manifestArtifactAccount: string = null;
|
|
74
|
+
const [artifactAccountData] = artifactAccounts;
|
|
75
|
+
if (artifactAccountData) {
|
|
76
|
+
manifestArtifactAccount = artifactAccountData.name;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const cloudProvider = 'cloudrun';
|
|
80
|
+
const moniker = sourceMoniker || {
|
|
81
|
+
app: app.name,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const relationships = {
|
|
85
|
+
loadBalancers: [] as string[],
|
|
86
|
+
securityGroups: [] as string[],
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const versioned: any = null;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
command: {
|
|
93
|
+
cloudProvider,
|
|
94
|
+
manifest: null,
|
|
95
|
+
manifests: Array.isArray(sourceManifest) ? sourceManifest : sourceManifest != null ? [sourceManifest] : null,
|
|
96
|
+
relationships,
|
|
97
|
+
moniker,
|
|
98
|
+
account,
|
|
99
|
+
versioned,
|
|
100
|
+
manifestArtifactAccount,
|
|
101
|
+
source: ManifestSource.TEXT,
|
|
102
|
+
},
|
|
103
|
+
metadata: {
|
|
104
|
+
backingData,
|
|
105
|
+
},
|
|
106
|
+
} as ICloudrunManifestCommandData;
|
|
107
|
+
});
|
|
115
108
|
}
|
|
116
109
|
}
|
|
@@ -2,11 +2,11 @@ import { get } from 'lodash';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
4
|
import type { IExecutionDetailsSectionProps, IManifest } from '@spinnaker/core';
|
|
5
|
-
import { ExecutionDetailsSection, StageFailureMessage } from '@spinnaker/core';
|
|
5
|
+
import { ExecutionDetailsSection, ManifestYaml, StageFailureMessage } from '@spinnaker/core';
|
|
6
6
|
|
|
7
7
|
import { ManifestStatus } from './ManifestStatus';
|
|
8
|
-
import type { IStageManifest } from '../../../../manifest/manifest.service';
|
|
9
|
-
import { CloudrunManifestService } from '../../../../manifest/manifest.service';
|
|
8
|
+
import type { IStageManifest, IStoredManifestReference } from '../../../../manifest/manifest.service';
|
|
9
|
+
import { CloudrunManifestService, isStoredManifestReference } from '../../../../manifest/manifest.service';
|
|
10
10
|
|
|
11
11
|
export interface IManifestSubscription {
|
|
12
12
|
id: string;
|
|
@@ -36,7 +36,9 @@ export class DeployStatus extends React.Component<IExecutionDetailsSectionProps,
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
public componentDidUpdate(_prevProps: IExecutionDetailsSectionProps, prevState: IDeployStatusState) {
|
|
39
|
-
const manifests: IStageManifest[] = get(this.props.stage, ['context', 'outputs.manifests'], [])
|
|
39
|
+
const manifests: IStageManifest[] = get(this.props.stage, ['context', 'outputs.manifests'], [])
|
|
40
|
+
.filter((m) => !!m)
|
|
41
|
+
.filter((m) => !isStoredManifestReference(m));
|
|
40
42
|
const manifestIds = manifests.map((m) => CloudrunManifestService.manifestIdentifier(m)).sort();
|
|
41
43
|
if (prevState.manifestIds.join('') !== manifestIds.join('')) {
|
|
42
44
|
this.unsubscribeAll();
|
|
@@ -69,6 +71,16 @@ export class DeployStatus extends React.Component<IExecutionDetailsSectionProps,
|
|
|
69
71
|
this.state.subscriptions.forEach(({ unsubscribe }) => unsubscribe());
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
// Manifests the artifact-store entity storage feature replaced with a reference instead
|
|
75
|
+
// of expanding them before deck saw them. We can't determine their kind/name/namespace
|
|
76
|
+
// (and so can't subscribe to live status for them) without fetching the content, so we
|
|
77
|
+
// let the user load them on demand instead, same as a baked manifest's YAML.
|
|
78
|
+
private get storedManifestRefs(): IStoredManifestReference[] {
|
|
79
|
+
return get(this.props.stage, ['context', 'outputs.manifests'], [])
|
|
80
|
+
.filter((m: unknown) => !!m)
|
|
81
|
+
.filter(isStoredManifestReference);
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
private stageManifestToIManifest(manifest: IStageManifest, account: string): IManifest {
|
|
73
85
|
return {
|
|
74
86
|
name: get(manifest, 'metadata.name', ''),
|
|
@@ -102,6 +114,23 @@ export class DeployStatus extends React.Component<IExecutionDetailsSectionProps,
|
|
|
102
114
|
</div>
|
|
103
115
|
</div>
|
|
104
116
|
)}
|
|
117
|
+
{!!this.storedManifestRefs.length && (
|
|
118
|
+
<div className="row">
|
|
119
|
+
<div className="col-md-12">
|
|
120
|
+
<div className="well alert alert-info">
|
|
121
|
+
{this.storedManifestRefs.map((ref, i) => (
|
|
122
|
+
<div key={i}>
|
|
123
|
+
<ManifestYaml
|
|
124
|
+
linkName="Load Manifest Details"
|
|
125
|
+
modalTitle="Manifest"
|
|
126
|
+
manifestUri={ref.reference.replace(/^ref?:\/\//, '')}
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
))}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
105
134
|
</ExecutionDetailsSection>
|
|
106
135
|
);
|
|
107
136
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { shallow } from 'enzyme';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { AccountService } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
import { ManifestDetailsLinkComponent } from './ManifestDetailsLink';
|
|
7
|
+
|
|
8
|
+
describe('Cloud Run ManifestDetailsLink', () => {
|
|
9
|
+
it('builds its link through the injected state service', async () => {
|
|
10
|
+
spyOn(AccountService, 'getAccountDetails').and.returnValue(
|
|
11
|
+
Promise.resolve({ spinnakerKindMap: { Service: 'unclassified' } } as any) as any,
|
|
12
|
+
);
|
|
13
|
+
const href = jasmine.createSpy('href').and.returnValue('#/manifest');
|
|
14
|
+
const component = shallow(
|
|
15
|
+
<ManifestDetailsLinkComponent
|
|
16
|
+
{...({ router: {}, stateParams: {}, stateService: { href } } as any)}
|
|
17
|
+
accountId="test-account"
|
|
18
|
+
linkName="Manifest"
|
|
19
|
+
manifest={{ manifest: { kind: 'Service', metadata: { annotations: {}, name: 'test-service' } } } as any}
|
|
20
|
+
/>,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
await Promise.resolve();
|
|
24
|
+
component.update();
|
|
25
|
+
|
|
26
|
+
expect(href).toHaveBeenCalledWith('home.applications.application.insight.clusters.cloudrunResource', {
|
|
27
|
+
accountId: 'test-account',
|
|
28
|
+
cloudrunResource: 'service test-service',
|
|
29
|
+
provider: 'cloudrun',
|
|
30
|
+
reg: '',
|
|
31
|
+
region: '_',
|
|
32
|
+
});
|
|
33
|
+
expect(component.find('a').prop('href')).toBe('#/manifest');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { get, trim } from 'lodash';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
|
-
import type { IManifest } from '@spinnaker/core';
|
|
5
|
-
import { AccountService,
|
|
4
|
+
import type { IManifest, IRouterInjectedProps } from '@spinnaker/core';
|
|
5
|
+
import { AccountService, withRouter } from '@spinnaker/core';
|
|
6
6
|
|
|
7
7
|
const UNMAPPED_K8S_RESOURCE_STATE_KEY = 'cloudrunResource';
|
|
8
8
|
|
|
@@ -16,14 +16,17 @@ export interface IManifestDetailsState {
|
|
|
16
16
|
url: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export class
|
|
19
|
+
export class ManifestDetailsLinkComponent extends React.Component<
|
|
20
|
+
IManifestDetailsProps & IRouterInjectedProps,
|
|
21
|
+
IManifestDetailsState
|
|
22
|
+
> {
|
|
20
23
|
private spinnakerKindStateMap: { [k: string]: string } = {
|
|
21
24
|
// keys from clouddriver's CloudrunSpinnakerKindMap
|
|
22
25
|
serverGroupManagers: 'serverGroupManager',
|
|
23
26
|
serverGroups: 'serverGroup',
|
|
24
27
|
};
|
|
25
28
|
|
|
26
|
-
constructor(props: IManifestDetailsProps) {
|
|
29
|
+
constructor(props: IManifestDetailsProps & IRouterInjectedProps) {
|
|
27
30
|
super(props);
|
|
28
31
|
this.state = {
|
|
29
32
|
url: '',
|
|
@@ -73,7 +76,7 @@ export class ManifestDetailsLink extends React.Component<IManifestDetailsProps,
|
|
|
73
76
|
const spinnakerKind = this.spinnakerKindFromCloudrunKind(kind, account.spinnakerKindMap);
|
|
74
77
|
const stateKey = this.spinnakerKindStateMap[spinnakerKind] || UNMAPPED_K8S_RESOURCE_STATE_KEY;
|
|
75
78
|
const params = this.getStateParams(stateKey);
|
|
76
|
-
const url =
|
|
79
|
+
const url = this.props.stateService.href(`home.applications.application.insight.clusters.${stateKey}`, params);
|
|
77
80
|
this.setState({ url });
|
|
78
81
|
});
|
|
79
82
|
}
|
|
@@ -90,3 +93,5 @@ export class ManifestDetailsLink extends React.Component<IManifestDetailsProps,
|
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
}
|
|
96
|
+
|
|
97
|
+
export const ManifestDetailsLink = withRouter(ManifestDetailsLinkComponent);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { IExecutionDetailsSectionProps, ILoadBalancer } from '@spinnaker/core';
|
|
4
|
+
import { AccountTag, ExecutionDetailsSection, StageFailureMessage } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
import type { ICloudrunAllocationDescription } from '../../../loadBalancer/loadBalancerTransformer';
|
|
7
|
+
|
|
8
|
+
export function CloudrunEditLoadBalancerExecutionDetails(props: IExecutionDetailsSectionProps) {
|
|
9
|
+
const { current, name, stage } = props;
|
|
10
|
+
const loadBalancers = ((stage.context || {}).loadBalancers || []) as ILoadBalancer[];
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<ExecutionDetailsSection name={name} current={current}>
|
|
14
|
+
<div className="row">
|
|
15
|
+
<div className="col-md-12">
|
|
16
|
+
<table className="table table-condensed">
|
|
17
|
+
<thead>
|
|
18
|
+
<tr>
|
|
19
|
+
<th>Account</th>
|
|
20
|
+
<th>Name</th>
|
|
21
|
+
<th>Region</th>
|
|
22
|
+
<th>Allocations</th>
|
|
23
|
+
</tr>
|
|
24
|
+
</thead>
|
|
25
|
+
<tbody>
|
|
26
|
+
{loadBalancers.map((loadBalancer) => (
|
|
27
|
+
<tr key={getLoadBalancerCompositeKey(loadBalancer)}>
|
|
28
|
+
<td>
|
|
29
|
+
<AccountTag account={loadBalancer.credentials || loadBalancer.account} />
|
|
30
|
+
</td>
|
|
31
|
+
<td>{loadBalancer.name}</td>
|
|
32
|
+
<td>{loadBalancer.region}</td>
|
|
33
|
+
<td>{renderAllocations((loadBalancer as any).splitDescription?.allocationDescriptions)}</td>
|
|
34
|
+
</tr>
|
|
35
|
+
))}
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
<StageFailureMessage stage={stage} message={stage.failureMessage} />
|
|
41
|
+
</ExecutionDetailsSection>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
CloudrunEditLoadBalancerExecutionDetails.title = 'editLoadBalancerConfig';
|
|
46
|
+
|
|
47
|
+
export function getLoadBalancerCompositeKey(loadBalancer: Partial<ILoadBalancer>): string {
|
|
48
|
+
return [
|
|
49
|
+
loadBalancer.name || '',
|
|
50
|
+
loadBalancer.credentials || loadBalancer.account || '',
|
|
51
|
+
loadBalancer.region || '',
|
|
52
|
+
].join(':');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function renderAllocations(allocationDescriptions: ICloudrunAllocationDescription[] = []): React.ReactNode {
|
|
56
|
+
if (!allocationDescriptions.length) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<ul className="list-unstyled no-margin">
|
|
62
|
+
{allocationDescriptions.map((allocation, index) => (
|
|
63
|
+
<li key={`${allocation.revisionName || allocation.target || allocation.cluster || 'allocation'}:${index}`}>
|
|
64
|
+
{allocation.revisionName || allocation.target || allocation.cluster || 'Allocation'}:{' '}
|
|
65
|
+
<span>{allocation.percent}%</span>
|
|
66
|
+
</li>
|
|
67
|
+
))}
|
|
68
|
+
</ul>
|
|
69
|
+
);
|
|
70
|
+
}
|