@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,89 @@
|
|
|
1
|
+
import type { Application, IManifest } from '@spinnaker/core';
|
|
2
|
+
import { ManifestReader } from '@spinnaker/core';
|
|
3
|
+
|
|
4
|
+
export interface IStageManifest {
|
|
5
|
+
kind: string;
|
|
6
|
+
apiVersion: string;
|
|
7
|
+
metadata: {
|
|
8
|
+
namespace: string;
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IManifestParams {
|
|
14
|
+
account: string;
|
|
15
|
+
location: string;
|
|
16
|
+
name: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type IManifestCallback = (manifest: IManifest) => void;
|
|
20
|
+
|
|
21
|
+
export class CloudrunManifestService {
|
|
22
|
+
public static subscribe(app: Application, params: IManifestParams, fn: IManifestCallback): () => void {
|
|
23
|
+
CloudrunManifestService.updateManifest(params, fn);
|
|
24
|
+
return app.onRefresh(null, () => CloudrunManifestService.updateManifest(params, fn));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private static updateManifest(params: IManifestParams, fn: IManifestCallback) {
|
|
28
|
+
ManifestReader.getManifest(params.account, params.location, params.name).then((manifest) => fn(manifest));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static manifestIdentifier(manifest: IStageManifest) {
|
|
32
|
+
const kind = manifest.kind.toLowerCase();
|
|
33
|
+
// manifest.metadata.namespace doesn't exist if it's a namespace being deployed
|
|
34
|
+
const namespace = (manifest.metadata.namespace || '_').toLowerCase();
|
|
35
|
+
const name = manifest.metadata.name.toLowerCase();
|
|
36
|
+
const apiVersion = (manifest.apiVersion || '_').toLowerCase();
|
|
37
|
+
// assuming this identifier is opaque and not parsed anywhere. Including the
|
|
38
|
+
// apiVersion will prevent collisions with CRD kinds without having any visible
|
|
39
|
+
// effect elsewhere
|
|
40
|
+
return `${namespace} ${kind} ${apiVersion} ${name}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static stageManifestToManifestParams(manifest: IStageManifest, account: string): IManifestParams {
|
|
44
|
+
return {
|
|
45
|
+
account,
|
|
46
|
+
name: CloudrunManifestService.scopedKind(manifest) + ' ' + manifest.metadata.name,
|
|
47
|
+
location: manifest.metadata.namespace == null ? '_' : manifest.metadata.namespace,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private static apiGroup(manifest: IStageManifest): string {
|
|
52
|
+
const parts = (manifest.apiVersion || '_').split('/');
|
|
53
|
+
if (parts.length < 2) {
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
return parts[0];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private static isCRDGroup(manifest: IStageManifest): boolean {
|
|
60
|
+
return !CloudrunManifestService.BUILT_IN_GROUPS.includes(CloudrunManifestService.apiGroup(manifest));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private static scopedKind(manifest: IStageManifest): string {
|
|
64
|
+
if (CloudrunManifestService.isCRDGroup(manifest)) {
|
|
65
|
+
return manifest.kind + '.' + CloudrunManifestService.apiGroup(manifest);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return manifest.kind;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private static readonly BUILT_IN_GROUPS = [
|
|
72
|
+
'',
|
|
73
|
+
'core',
|
|
74
|
+
'batch',
|
|
75
|
+
'apps',
|
|
76
|
+
'extensions',
|
|
77
|
+
'storage.k8s.io',
|
|
78
|
+
'apiextensions.k8s.io',
|
|
79
|
+
'apiregistration.k8s.io',
|
|
80
|
+
'policy',
|
|
81
|
+
'scheduling.k8s.io',
|
|
82
|
+
'settings.k8s.io',
|
|
83
|
+
'authorization.k8s.io',
|
|
84
|
+
'authentication.k8s.io',
|
|
85
|
+
'rbac.authorization.k8s.io',
|
|
86
|
+
'certifcates.k8s.io',
|
|
87
|
+
'networking.k8s.io',
|
|
88
|
+
];
|
|
89
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { cloneDeep } from 'lodash';
|
|
2
|
+
import { $q } from 'ngimport';
|
|
3
|
+
|
|
4
|
+
import type { Application, IAccountDetails, IArtifactAccount, IMoniker } from '@spinnaker/core';
|
|
5
|
+
import { AccountService } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import { ManifestSource } from './ManifestSource';
|
|
8
|
+
|
|
9
|
+
export interface ICloudrunManifestCommandData {
|
|
10
|
+
command: ICloudrunManifestCommand;
|
|
11
|
+
metadata: ICloudrunManifestCommandMetadata;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ICloudrunManifestCommand {
|
|
15
|
+
account: string;
|
|
16
|
+
cloudProvider: string;
|
|
17
|
+
manifest: any;
|
|
18
|
+
manifests: any[];
|
|
19
|
+
relationships: ICloudrunManifestSpinnakerRelationships;
|
|
20
|
+
moniker: IMoniker;
|
|
21
|
+
manifestArtifactId?: string;
|
|
22
|
+
manifestArtifactAccount?: string;
|
|
23
|
+
source: ManifestSource;
|
|
24
|
+
versioned?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ICloudrunManifestCommandMetadata {
|
|
28
|
+
backingData: any;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ICloudrunManifestSpinnakerRelationships {
|
|
32
|
+
loadBalancers?: string[];
|
|
33
|
+
securityGroups?: string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class CloudrunManifestCommandBuilder {
|
|
37
|
+
public static manifestCommandIsValid(command: ICloudrunManifestCommand): boolean {
|
|
38
|
+
if (!command.moniker) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!command.moniker.app) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public static copyAndCleanCommand(input: ICloudrunManifestCommand): ICloudrunManifestCommand {
|
|
50
|
+
const command = cloneDeep(input);
|
|
51
|
+
return command;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public static buildNewManifestCommand(
|
|
55
|
+
app: Application,
|
|
56
|
+
sourceManifest?: any,
|
|
57
|
+
sourceMoniker?: IMoniker,
|
|
58
|
+
sourceAccount?: string,
|
|
59
|
+
): PromiseLike<ICloudrunManifestCommandData> {
|
|
60
|
+
const dataToFetch = {
|
|
61
|
+
accounts: AccountService.getAllAccountDetailsForProvider('cloudrun'),
|
|
62
|
+
artifactAccounts: AccountService.getArtifactAccounts(),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return $q
|
|
66
|
+
.all(dataToFetch)
|
|
67
|
+
.then((backingData: { accounts: IAccountDetails[]; artifactAccounts: IArtifactAccount[] }) => {
|
|
68
|
+
const { accounts, artifactAccounts } = backingData;
|
|
69
|
+
|
|
70
|
+
const account = accounts.some((a) => a.name === sourceAccount)
|
|
71
|
+
? accounts.find((a) => a.name === sourceAccount).name
|
|
72
|
+
: accounts.length
|
|
73
|
+
? accounts[0].name
|
|
74
|
+
: null;
|
|
75
|
+
|
|
76
|
+
let manifestArtifactAccount: string = null;
|
|
77
|
+
const [artifactAccountData] = artifactAccounts;
|
|
78
|
+
if (artifactAccountData) {
|
|
79
|
+
manifestArtifactAccount = artifactAccountData.name;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const cloudProvider = 'cloudrun';
|
|
83
|
+
const moniker = sourceMoniker || {
|
|
84
|
+
app: app.name,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const relationships = {
|
|
88
|
+
loadBalancers: [] as string[],
|
|
89
|
+
securityGroups: [] as string[],
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const versioned: any = null;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
command: {
|
|
96
|
+
cloudProvider,
|
|
97
|
+
manifest: null,
|
|
98
|
+
manifests: Array.isArray(sourceManifest)
|
|
99
|
+
? sourceManifest
|
|
100
|
+
: sourceManifest != null
|
|
101
|
+
? [sourceManifest]
|
|
102
|
+
: null,
|
|
103
|
+
relationships,
|
|
104
|
+
moniker,
|
|
105
|
+
account,
|
|
106
|
+
versioned,
|
|
107
|
+
manifestArtifactAccount,
|
|
108
|
+
source: ManifestSource.TEXT,
|
|
109
|
+
},
|
|
110
|
+
metadata: {
|
|
111
|
+
backingData,
|
|
112
|
+
},
|
|
113
|
+
} as ICloudrunManifestCommandData;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FormikProps } from 'formik';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { IAccount } from '@spinnaker/core';
|
|
5
|
+
import { AccountSelectInput, HelpField } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import type { ICloudrunManifestCommandData } from '../manifestCommandBuilder.service';
|
|
8
|
+
|
|
9
|
+
export interface IManifestBasicSettingsProps {
|
|
10
|
+
accounts: IAccount[];
|
|
11
|
+
onAccountSelect: (account: string) => void;
|
|
12
|
+
selectedAccount: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function ManifestBasicSettings({ accounts, onAccountSelect, selectedAccount }: IManifestBasicSettingsProps) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="form-horizontal">
|
|
18
|
+
<div className="form-group">
|
|
19
|
+
<div className="col-md-3 sm-label-right">
|
|
20
|
+
Account <HelpField id="cloudrun.manifest.account" />
|
|
21
|
+
</div>
|
|
22
|
+
<div className="col-md-8">
|
|
23
|
+
<AccountSelectInput
|
|
24
|
+
value={selectedAccount}
|
|
25
|
+
onChange={(evt: any) => onAccountSelect(evt.target.value)}
|
|
26
|
+
readOnly={false}
|
|
27
|
+
accounts={accounts}
|
|
28
|
+
provider="cloudrun"
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface IWizardManifestBasicSettingsProps {
|
|
37
|
+
formik: FormikProps<ICloudrunManifestCommandData>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class WizardManifestBasicSettings extends React.Component<IWizardManifestBasicSettingsProps> {
|
|
41
|
+
private accountUpdated = (account: string): void => {
|
|
42
|
+
const { formik } = this.props;
|
|
43
|
+
formik.values.command.account = account;
|
|
44
|
+
formik.setFieldValue('account', account);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
public render() {
|
|
48
|
+
const { formik } = this.props;
|
|
49
|
+
return (
|
|
50
|
+
<ManifestBasicSettings
|
|
51
|
+
accounts={formik.values.metadata.backingData.accounts}
|
|
52
|
+
onAccountSelect={this.accountUpdated}
|
|
53
|
+
selectedAccount={formik.values.command.account}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
|
|
3
|
+
import { CLOUDRUN_EDIT_LOAD_BALANCER_STAGE } from './stages/editLoadBalancer/cloudrunEditLoadBalancerStage';
|
|
4
|
+
|
|
5
|
+
export const CLOUDRUN_PIPELINE_MODULE = 'spinnaker.cloudrun.pipeline.module';
|
|
6
|
+
module(CLOUDRUN_PIPELINE_MODULE, [CLOUDRUN_EDIT_LOAD_BALANCER_STAGE]);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { cloneDeep } from 'lodash';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { from as observableFrom, Subject } from 'rxjs';
|
|
4
|
+
import { takeUntil } from 'rxjs/operators';
|
|
5
|
+
|
|
6
|
+
import type { IAccountDetails, IStage, IStageConfigProps } from '@spinnaker/core';
|
|
7
|
+
import { AccountService, FormikStageConfig } from '@spinnaker/core';
|
|
8
|
+
|
|
9
|
+
import { DeployStageForm } from './DeployStageForm';
|
|
10
|
+
import { ManifestSource } from '../../../manifest/ManifestSource';
|
|
11
|
+
|
|
12
|
+
interface IDeployManifestStageConfigState {
|
|
13
|
+
accounts: IAccountDetails[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class DeployStageConfig extends React.Component<IStageConfigProps, IDeployManifestStageConfigState> {
|
|
17
|
+
private stage: IStage;
|
|
18
|
+
private destroy$ = new Subject();
|
|
19
|
+
|
|
20
|
+
public constructor(props: IStageConfigProps) {
|
|
21
|
+
super(props);
|
|
22
|
+
this.state = {
|
|
23
|
+
accounts: [],
|
|
24
|
+
};
|
|
25
|
+
const { stage: initialStageConfig } = props;
|
|
26
|
+
const stage = cloneDeep(initialStageConfig);
|
|
27
|
+
if (!stage.source) {
|
|
28
|
+
stage.source = ManifestSource.TEXT;
|
|
29
|
+
}
|
|
30
|
+
if (!stage.skipExpressionEvaluation) {
|
|
31
|
+
stage.skipExpressionEvaluation = false;
|
|
32
|
+
}
|
|
33
|
+
if (!stage.cloudProvider) {
|
|
34
|
+
stage.cloudProvider = 'cloudrun';
|
|
35
|
+
}
|
|
36
|
+
if (!stage.moniker) {
|
|
37
|
+
stage.moniker = {};
|
|
38
|
+
}
|
|
39
|
+
if (!stage.moniker.app) {
|
|
40
|
+
stage.moniker.app = props.application.name;
|
|
41
|
+
}
|
|
42
|
+
// Intentionally initializing the stage config only once in the constructor
|
|
43
|
+
// The stage config is then completely owned within FormikStageConfig's Formik state
|
|
44
|
+
this.stage = stage;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public componentDidMount() {
|
|
48
|
+
this.fetchAccounts();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private fetchAccounts = (): void => {
|
|
52
|
+
observableFrom(AccountService.getAllAccountDetailsForProvider('cloudrun'))
|
|
53
|
+
.pipe(takeUntil(this.destroy$))
|
|
54
|
+
.subscribe((accounts: IAccountDetails[]) => {
|
|
55
|
+
this.setState({ accounts });
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
public componentWillUnmount() {
|
|
60
|
+
this.destroy$.next();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public render() {
|
|
64
|
+
return (
|
|
65
|
+
<FormikStageConfig
|
|
66
|
+
{...this.props}
|
|
67
|
+
stage={this.stage}
|
|
68
|
+
onChange={this.props.updateStage}
|
|
69
|
+
render={(props) => <DeployStageForm {...props} accounts={this.state.accounts} />}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { capitalize, get, isEmpty, map } from 'lodash';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { Option } from 'react-select';
|
|
4
|
+
|
|
5
|
+
import type { IAccountDetails, IArtifact, IExpectedArtifact, IFormikStageConfigInjectedProps } from '@spinnaker/core';
|
|
6
|
+
import {
|
|
7
|
+
ArtifactTypePatterns,
|
|
8
|
+
CheckboxInput,
|
|
9
|
+
RadioButtonInput,
|
|
10
|
+
StageArtifactSelectorDelegate,
|
|
11
|
+
StageConfigField,
|
|
12
|
+
yamlDocumentsToString,
|
|
13
|
+
YamlEditor,
|
|
14
|
+
} from '@spinnaker/core';
|
|
15
|
+
|
|
16
|
+
import type { IManifestBindArtifact } from './ManifestBindArtifactsSelector';
|
|
17
|
+
import { ManifestBindArtifactsSelector } from './ManifestBindArtifactsSelector';
|
|
18
|
+
import { ManifestSource } from '../../../manifest/ManifestSource';
|
|
19
|
+
import { ManifestBasicSettings } from '../../../manifest/wizard/BasicSettings';
|
|
20
|
+
import { ServerGroupNamePreview } from './serverGroupNamePreview';
|
|
21
|
+
|
|
22
|
+
interface IDeployManifestStageConfigFormProps {
|
|
23
|
+
accounts: IAccountDetails[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface IDeployManifestStageConfigFormState {
|
|
27
|
+
rawManifest: string;
|
|
28
|
+
application: string;
|
|
29
|
+
stack: string;
|
|
30
|
+
details: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class DeployStageForm extends React.Component<
|
|
34
|
+
IDeployManifestStageConfigFormProps & IFormikStageConfigInjectedProps,
|
|
35
|
+
IDeployManifestStageConfigFormState
|
|
36
|
+
> {
|
|
37
|
+
private readonly excludedManifestArtifactTypes = [
|
|
38
|
+
ArtifactTypePatterns.FRONT50_PIPELINE_TEMPLATE,
|
|
39
|
+
ArtifactTypePatterns.MAVEN_FILE,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
public constructor(props: IDeployManifestStageConfigFormProps & IFormikStageConfigInjectedProps) {
|
|
43
|
+
super(props);
|
|
44
|
+
const stage = this.props.formik.values;
|
|
45
|
+
const manifests: any[] = get(props.formik.values, 'manifests');
|
|
46
|
+
const isTextManifest: boolean = get(props.formik.values, 'source') === ManifestSource.TEXT;
|
|
47
|
+
this.state = {
|
|
48
|
+
rawManifest: !isEmpty(manifests) && isTextManifest ? yamlDocumentsToString(manifests) : '',
|
|
49
|
+
application: this.props.application.name,
|
|
50
|
+
stack: get(stage, 'stack'),
|
|
51
|
+
details: get(stage, 'details'),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private getSourceOptions = (): Array<Option<string>> => {
|
|
56
|
+
return map([ManifestSource.TEXT, ManifestSource.ARTIFACT], (option) => ({
|
|
57
|
+
label: capitalize(option),
|
|
58
|
+
value: option,
|
|
59
|
+
}));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
private handleRawManifestChange = (rawManifest: string, manifests: any): void => {
|
|
63
|
+
this.setState({
|
|
64
|
+
rawManifest,
|
|
65
|
+
});
|
|
66
|
+
this.props.formik.setFieldValue('manifests', manifests);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
private onManifestArtifactSelected = (expectedArtifactId: string): void => {
|
|
70
|
+
this.props.formik.setFieldValue('manifestArtifactId', expectedArtifactId);
|
|
71
|
+
this.props.formik.setFieldValue('manifestArtifact', null);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
private onManifestArtifactEdited = (artifact: IArtifact) => {
|
|
75
|
+
this.props.formik.setFieldValue('manifestArtifactId', null);
|
|
76
|
+
this.props.formik.setFieldValue('manifestArtifact', artifact);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
private getRequiredArtifacts = (): IManifestBindArtifact[] => {
|
|
80
|
+
const { requiredArtifactIds, requiredArtifacts } = this.props.formik.values;
|
|
81
|
+
return (requiredArtifactIds || [])
|
|
82
|
+
.map((id: string) => ({ expectedArtifactId: id }))
|
|
83
|
+
.concat(requiredArtifacts || []);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
private onRequiredArtifactsChanged = (bindings: IManifestBindArtifact[]): void => {
|
|
87
|
+
this.props.formik.setFieldValue(
|
|
88
|
+
'requiredArtifactIds',
|
|
89
|
+
bindings.filter((b) => b.expectedArtifactId).map((b) => b.expectedArtifactId),
|
|
90
|
+
);
|
|
91
|
+
this.props.formik.setFieldValue(
|
|
92
|
+
'requiredArtifacts',
|
|
93
|
+
bindings.filter((b) => b.artifact),
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
public render() {
|
|
98
|
+
const stage = this.props.formik.values;
|
|
99
|
+
return (
|
|
100
|
+
<div className="form-horizontal">
|
|
101
|
+
<h4>Basic Settings</h4>
|
|
102
|
+
<ManifestBasicSettings
|
|
103
|
+
accounts={this.props.accounts}
|
|
104
|
+
onAccountSelect={(accountName) => this.props.formik.setFieldValue('account', accountName)}
|
|
105
|
+
selectedAccount={stage.account}
|
|
106
|
+
/>
|
|
107
|
+
<StageConfigField label="Application">
|
|
108
|
+
<input
|
|
109
|
+
className="form-control input-sm"
|
|
110
|
+
type="text"
|
|
111
|
+
value={this.props.application.name}
|
|
112
|
+
onChange={(event) => this.props.formik.setFieldValue('application', event.target.value)}
|
|
113
|
+
disabled
|
|
114
|
+
/>
|
|
115
|
+
</StageConfigField>
|
|
116
|
+
<StageConfigField label="Stack">
|
|
117
|
+
<input
|
|
118
|
+
className="form-control input-sm"
|
|
119
|
+
type="text"
|
|
120
|
+
value={stage.stack}
|
|
121
|
+
onChange={(event) => this.props.formik.setFieldValue('stack', event.target.value)}
|
|
122
|
+
/>
|
|
123
|
+
</StageConfigField>
|
|
124
|
+
<StageConfigField label="Details">
|
|
125
|
+
<input
|
|
126
|
+
className="form-control input-sm"
|
|
127
|
+
type="text"
|
|
128
|
+
value={stage.details}
|
|
129
|
+
onChange={(event) => this.props.formik.setFieldValue('details', event.target.value)}
|
|
130
|
+
/>
|
|
131
|
+
</StageConfigField>
|
|
132
|
+
<ServerGroupNamePreview
|
|
133
|
+
application={this.props.application.name}
|
|
134
|
+
stack={stage.stack}
|
|
135
|
+
details={stage.details}
|
|
136
|
+
></ServerGroupNamePreview>
|
|
137
|
+
|
|
138
|
+
<hr />
|
|
139
|
+
<h4>Manifest Configuration</h4>
|
|
140
|
+
<StageConfigField label="Manifest Source" helpKey="cloudrun.manifest.source">
|
|
141
|
+
<RadioButtonInput
|
|
142
|
+
options={this.getSourceOptions()}
|
|
143
|
+
onChange={(e: any) => this.props.formik.setFieldValue('source', e.target.value)}
|
|
144
|
+
value={stage.source}
|
|
145
|
+
/>
|
|
146
|
+
</StageConfigField>
|
|
147
|
+
{stage.source === ManifestSource.TEXT && (
|
|
148
|
+
<StageConfigField label="Manifest">
|
|
149
|
+
<YamlEditor onChange={this.handleRawManifestChange} value={this.state.rawManifest} />
|
|
150
|
+
</StageConfigField>
|
|
151
|
+
)}
|
|
152
|
+
{stage.source === ManifestSource.ARTIFACT && (
|
|
153
|
+
<>
|
|
154
|
+
<StageArtifactSelectorDelegate
|
|
155
|
+
artifact={stage.manifestArtifact}
|
|
156
|
+
excludedArtifactTypePatterns={this.excludedManifestArtifactTypes}
|
|
157
|
+
expectedArtifactId={stage.manifestArtifactId}
|
|
158
|
+
helpKey="cloudrun.manifest.expectedArtifact"
|
|
159
|
+
label="Manifest Artifact"
|
|
160
|
+
onArtifactEdited={this.onManifestArtifactEdited}
|
|
161
|
+
onExpectedArtifactSelected={(artifact: IExpectedArtifact) => this.onManifestArtifactSelected(artifact.id)}
|
|
162
|
+
pipeline={this.props.pipeline}
|
|
163
|
+
stage={stage}
|
|
164
|
+
/>
|
|
165
|
+
<StageConfigField label="Expression Evaluation" helpKey="cloudrun.manifest.skipExpressionEvaluation">
|
|
166
|
+
<CheckboxInput
|
|
167
|
+
checked={stage.skipExpressionEvaluation === true}
|
|
168
|
+
onChange={(e: any) => this.props.formik.setFieldValue('skipExpressionEvaluation', e.target.checked)}
|
|
169
|
+
text="Skip SpEL expression evaluation"
|
|
170
|
+
/>
|
|
171
|
+
</StageConfigField>
|
|
172
|
+
</>
|
|
173
|
+
)}
|
|
174
|
+
<StageConfigField label="Required Artifacts to Bind" helpKey="cloudrun.manifest.requiredArtifactsToBind">
|
|
175
|
+
<ManifestBindArtifactsSelector
|
|
176
|
+
bindings={this.getRequiredArtifacts()}
|
|
177
|
+
onChangeBindings={this.onRequiredArtifactsChanged}
|
|
178
|
+
pipeline={this.props.pipeline}
|
|
179
|
+
stage={stage}
|
|
180
|
+
/>
|
|
181
|
+
</StageConfigField>
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { IArtifact, IExpectedArtifact, IPipeline, IStage } from '@spinnaker/core';
|
|
4
|
+
import { ArtifactTypePatterns, StageArtifactSelector } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
export interface IManifestBindArtifact {
|
|
7
|
+
expectedArtifactId?: string;
|
|
8
|
+
artifact?: IArtifact;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface IManifestBindArtifactsSelectorProps {
|
|
12
|
+
pipeline: IPipeline;
|
|
13
|
+
stage: IStage;
|
|
14
|
+
bindings?: IManifestBindArtifact[];
|
|
15
|
+
onChangeBindings: (_: IManifestBindArtifact[]) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class ManifestBindArtifactsSelector extends React.Component<IManifestBindArtifactsSelectorProps> {
|
|
19
|
+
private onChangeBinding = (index: number, binding: IManifestBindArtifact) => {
|
|
20
|
+
const bindings = (this.props.bindings || []).slice(0);
|
|
21
|
+
bindings[index] = binding;
|
|
22
|
+
this.props.onChangeBindings(bindings);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
private onRemoveBinding = (index: number) => {
|
|
26
|
+
const bindings = (this.props.bindings || []).slice(0);
|
|
27
|
+
bindings.splice(index, 1);
|
|
28
|
+
this.props.onChangeBindings(bindings);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
public render() {
|
|
32
|
+
const { stage, pipeline, bindings } = this.props;
|
|
33
|
+
|
|
34
|
+
const renderSelect = (i: number, binding?: IManifestBindArtifact) => {
|
|
35
|
+
const key = (!binding && 'new') || binding.expectedArtifactId || (binding.artifact && binding.artifact.id);
|
|
36
|
+
return (
|
|
37
|
+
<div className="row" key={key}>
|
|
38
|
+
<div className="col-md-10">
|
|
39
|
+
<StageArtifactSelector
|
|
40
|
+
pipeline={pipeline}
|
|
41
|
+
stage={stage}
|
|
42
|
+
expectedArtifactId={binding && binding.expectedArtifactId}
|
|
43
|
+
artifact={!!binding && binding.artifact}
|
|
44
|
+
onArtifactEdited={(artifact: IArtifact) => this.onChangeBinding(i, { artifact: artifact })}
|
|
45
|
+
onExpectedArtifactSelected={(expectedArtifact: IExpectedArtifact) =>
|
|
46
|
+
this.onChangeBinding(i, { expectedArtifactId: expectedArtifact.id })
|
|
47
|
+
}
|
|
48
|
+
excludedArtifactIds={bindings.map((b) => b.expectedArtifactId)}
|
|
49
|
+
excludedArtifactTypePatterns={[ArtifactTypePatterns.FRONT50_PIPELINE_TEMPLATE]}
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
{binding && (
|
|
53
|
+
<div className="col-md-2">
|
|
54
|
+
<a className="glyphicon glyphicon-trash" onClick={() => this.onRemoveBinding(i)} />
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
const renderSelectEditable = (binding: IManifestBindArtifact, i: number) => renderSelect(i, binding);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
{bindings.map(renderSelectEditable)}
|
|
65
|
+
{renderSelect(bindings.length)}
|
|
66
|
+
</>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { get, isEmpty } from 'lodash';
|
|
2
|
+
|
|
3
|
+
import type { ICustomValidator, IPipeline, IStage, IValidatorConfig } from '@spinnaker/core';
|
|
4
|
+
|
|
5
|
+
import { ManifestSource } from '../../../manifest/ManifestSource';
|
|
6
|
+
import { strategyRedBlack } from '../../../rolloutStrategy/redblack.strategy';
|
|
7
|
+
|
|
8
|
+
const MAX_VERSION_HISTORY_ANNOTATION = 'strategy.spinnaker.io/max-version-history';
|
|
9
|
+
|
|
10
|
+
export const deployValidators = (): IValidatorConfig[] => {
|
|
11
|
+
return [
|
|
12
|
+
{ type: 'requiredField', fieldName: 'account', fieldLabel: 'Account' },
|
|
13
|
+
{ type: 'requiredField', fieldName: 'source', fieldLabel: 'Source' },
|
|
14
|
+
{
|
|
15
|
+
type: 'custom',
|
|
16
|
+
validate: (_pipeline: IPipeline, stage: IStage) => {
|
|
17
|
+
const enabled = get(stage, 'trafficManagement.enabled', false);
|
|
18
|
+
const services = get(stage, 'trafficManagement.options.services', []);
|
|
19
|
+
if (enabled && isEmpty(services)) {
|
|
20
|
+
return `Select at least one <strong>Service</strong> to enable Spinnaker-managed rollout strategy options.`;
|
|
21
|
+
}
|
|
22
|
+
if (enabled && stage.source === ManifestSource.TEXT) {
|
|
23
|
+
const manifests = get(stage, 'manifests', []);
|
|
24
|
+
const replicaSetManifests = manifests.filter((m) => m.kind === 'ReplicaSet');
|
|
25
|
+
const strategy = get(stage, 'trafficManagement.options.strategy');
|
|
26
|
+
const maxVersionHistory = parseInt(
|
|
27
|
+
get(replicaSetManifests, [0, 'metadata', 'annotations', MAX_VERSION_HISTORY_ANNOTATION]),
|
|
28
|
+
10,
|
|
29
|
+
);
|
|
30
|
+
if (strategy === strategyRedBlack.key && maxVersionHistory < 2) {
|
|
31
|
+
return `The max version history specified in your manifest conflicts with the behavior of the Red/Black rollout strategy. Please update your <strong>${MAX_VERSION_HISTORY_ANNOTATION}</strong> annotation to a value greater than or equal to 2.`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
},
|
|
36
|
+
} as ICustomValidator,
|
|
37
|
+
];
|
|
38
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IStage } from '@spinnaker/core';
|
|
2
|
+
import {
|
|
3
|
+
ArtifactReferenceService,
|
|
4
|
+
ExecutionArtifactTab,
|
|
5
|
+
ExecutionDetailsTasks,
|
|
6
|
+
ExpectedArtifactService,
|
|
7
|
+
Registry,
|
|
8
|
+
} from '@spinnaker/core';
|
|
9
|
+
|
|
10
|
+
import { DeployStageConfig } from './DeployStageConfig';
|
|
11
|
+
import { deployValidators } from './deploy.validator';
|
|
12
|
+
import { DeployStatus } from './manifestStatus/DeployStatus';
|
|
13
|
+
|
|
14
|
+
Registry.pipeline.registerStage({
|
|
15
|
+
label: 'Deploy (Cloud Run)',
|
|
16
|
+
description: 'Deploy a Cloud Run manifest yaml/json file.',
|
|
17
|
+
key: 'deployCloudrunManifest',
|
|
18
|
+
cloudProvider: 'cloudrun',
|
|
19
|
+
component: DeployStageConfig,
|
|
20
|
+
executionDetailsSections: [DeployStatus, ExecutionDetailsTasks, ExecutionArtifactTab],
|
|
21
|
+
producesArtifacts: true,
|
|
22
|
+
supportsCustomTimeout: true,
|
|
23
|
+
validators: deployValidators(),
|
|
24
|
+
accountExtractor: (stage: IStage): string[] => (stage.account ? [stage.account] : []),
|
|
25
|
+
configAccountExtractor: (stage: any): string[] => (stage.account ? [stage.account] : []),
|
|
26
|
+
artifactExtractor: ExpectedArtifactService.accumulateArtifacts(['manifestArtifactId', 'requiredArtifactIds']),
|
|
27
|
+
artifactRemover: ArtifactReferenceService.removeArtifactFromFields(['manifestArtifactId', 'requiredArtifactIds']),
|
|
28
|
+
});
|