@spinnaker/core 0.11.4 → 0.12.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/CHANGELOG.md +53 -0
- package/dist/cloudProvider/CloudProviderLogo.d.ts +0 -5
- package/dist/config/settings.d.ts +1 -0
- package/dist/core.module.d.ts +1 -1
- package/dist/index.js +40 -32
- package/dist/index.js.map +1 -1
- package/dist/managed/constraints/registry.d.ts +1 -1
- package/dist/managed/environmentBaseElements/EnvironmentsRender.d.ts +3 -2
- package/dist/managed/graphql/graphql-sdk.d.ts +35 -0
- package/dist/managed/overview/PreviewEnvironments.d.ts +8 -0
- package/dist/managed/resources/ToggleResourceManagement.d.ts +9 -0
- package/dist/managed/resources/resourceRegistry.d.ts +1 -1
- package/dist/managed/utils/defaults.d.ts +1 -1
- package/dist/managed/utils/useNotifyOnError.hook.d.ts +6 -0
- package/dist/pipeline/service/execution.service.d.ts +2 -1
- package/package.json +6 -6
- package/src/cloudProvider/CloudProviderLogo.tsx +0 -5
- package/src/cloudProvider/CloudProviderRegistry.ts +6 -65
- package/src/cloudProvider/providerSelection/ProviderSelectionService.spec.ts +96 -0
- package/src/cloudProvider/providerSelection/ProviderSelectionService.ts +6 -8
- package/src/cluster/allClusters.html +1 -0
- package/src/config/settings.ts +2 -0
- package/src/core.module.ts +1 -1
- package/src/function/function.dataSource.ts +1 -0
- package/src/instance/details/multipleInstances.controller.js +6 -0
- package/src/instance/details/multipleInstances.view.html +1 -0
- package/src/managed/config/Configuration.tsx +8 -1
- package/src/managed/config/DeliveryConfig.tsx +3 -9
- package/src/managed/config/GitIntegration.tsx +8 -1
- package/src/managed/environmentBaseElements/EnvironmentsRender.tsx +7 -1
- package/src/managed/externals/toggleResourceManagement.tsx +12 -59
- package/src/managed/graphql/graphql-sdk.ts +54 -0
- package/src/managed/graphql/schema.graphql +1 -0
- package/src/managed/overview/EnvironmentsOverview.tsx +8 -11
- package/src/managed/overview/PreviewEnvironments.tsx +47 -0
- package/src/managed/overview/Resource.tsx +76 -34
- package/src/managed/overview/artifact/ArtifactVersionTasks.tsx +3 -10
- package/src/managed/overview/artifact/Constraints.tsx +3 -11
- package/src/managed/overview/queries.graphql +8 -0
- package/src/managed/resources/ToggleResourceManagement.tsx +53 -0
- package/src/managed/utils/defaults.ts +1 -1
- package/src/managed/utils/useNotifyOnError.hook.ts +22 -0
- package/src/modal/modals.less +1 -1
- package/src/pipeline/details/SingleExecutionDetails.tsx +1 -1
- package/src/pipeline/service/execution.service.ts +7 -2
- package/src/presentation/main.less +3 -3
- package/src/serverGroup/details/multipleServerGroups.controller.js +6 -0
- package/src/serverGroup/details/multipleServerGroups.view.html +1 -0
- package/src/utils/clipboard/CopyToClipboard.tsx +1 -1
|
@@ -5,29 +5,10 @@ import { ManagedWriter } from '../ManagedWriter';
|
|
|
5
5
|
import { Application } from '../../application';
|
|
6
6
|
import { ConfirmationModalService } from '../../confirmationModal';
|
|
7
7
|
import { IManagedResource, IManagedResourceSummary, ManagedResourceStatus } from '../../domain';
|
|
8
|
+
import { ActuationWarning, MultiRegionWarning, ToggleResourceManagement } from '../resources/ToggleResourceManagement';
|
|
8
9
|
|
|
9
10
|
import './ManagedResourceStatusIndicator.less';
|
|
10
11
|
|
|
11
|
-
interface IToggleConfiguration {
|
|
12
|
-
pauseWarning?: JSX.Element;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const viewConfigurationByStatus: { [status in ManagedResourceStatus]?: IToggleConfiguration } = {
|
|
16
|
-
ACTUATING: {
|
|
17
|
-
pauseWarning: (
|
|
18
|
-
<p>
|
|
19
|
-
<div className="horizontal top sp-padding-m alert alert-warning">
|
|
20
|
-
<i className="fa fa-exclamation-triangle sp-margin-m-right sp-margin-xs-top" />
|
|
21
|
-
<span>
|
|
22
|
-
Pausing management will not interrupt the action Spinnaker is currently performing to resolve the difference
|
|
23
|
-
from desired state.
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
</p>
|
|
27
|
-
),
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
|
|
31
12
|
/***
|
|
32
13
|
* If the resource is not managed, or management is paused, this will return an immediate promise with a true value.
|
|
33
14
|
* If the resource is managed, an interstitial modal will prompt the user to pause resource management. The promise will
|
|
@@ -71,49 +52,21 @@ export const toggleResourcePause = (
|
|
|
71
52
|
|
|
72
53
|
return ConfirmationModalService.confirm({
|
|
73
54
|
header: `Really ${isPaused ? 'resume' : 'pause'} resource management?`,
|
|
74
|
-
bodyContent:
|
|
55
|
+
bodyContent: (
|
|
56
|
+
<ToggleResourceManagement
|
|
57
|
+
isActuating={resourceSummary.status === ManagedResourceStatus.ACTUATING}
|
|
58
|
+
isPaused={isPaused}
|
|
59
|
+
regions={getRegions(resourceSummary)}
|
|
60
|
+
/>
|
|
61
|
+
),
|
|
75
62
|
account: resourceSummary.locations.account,
|
|
76
63
|
buttonText: `${isPaused ? 'Resume' : 'Pause'} management`,
|
|
77
64
|
submitMethod,
|
|
78
65
|
});
|
|
79
66
|
};
|
|
80
67
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
if (isPaused) {
|
|
84
|
-
return (
|
|
85
|
-
<>
|
|
86
|
-
<p>Spinnaker will resume taking action to correct differences from the desired state.</p>
|
|
87
|
-
<MultiRegionWarning resourceSummary={resourceSummary} />
|
|
88
|
-
</>
|
|
89
|
-
);
|
|
90
|
-
} else {
|
|
91
|
-
return (
|
|
92
|
-
<>
|
|
93
|
-
<p>While a resource is paused, Spinnaker will not take action to correct differences from the desired state.</p>
|
|
94
|
-
{viewConfigurationByStatus[status]?.pauseWarning}
|
|
95
|
-
<MultiRegionWarning resourceSummary={resourceSummary} />
|
|
96
|
-
</>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const MultiRegionWarning = ({ resourceSummary }: { resourceSummary: IManagedResourceSummary }) => {
|
|
102
|
-
const { isPaused, locations } = resourceSummary;
|
|
103
|
-
const regions = locations.regions.map((r) => r.name).sort();
|
|
104
|
-
if (regions.length < 2) {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
return (
|
|
108
|
-
<div className="horizontal top sp-padding-m alert alert-warning">
|
|
109
|
-
<i className="fa fa-exclamation-triangle sp-margin-m-right sp-margin-xs-top" />
|
|
110
|
-
<span>
|
|
111
|
-
{isPaused ? 'Resuming' : 'Pausing'} management of this resource will affect the following regions:{' '}
|
|
112
|
-
<b>{regions.join(', ')}</b>.
|
|
113
|
-
</span>
|
|
114
|
-
</div>
|
|
115
|
-
);
|
|
116
|
-
};
|
|
68
|
+
const getRegions = (resourceSummary: IManagedResourceSummary) =>
|
|
69
|
+
resourceSummary.locations.regions.map((r) => r.name).sort();
|
|
117
70
|
|
|
118
71
|
const BodyText = ({ resourceSummary }: { resourceSummary: IManagedResourceSummary }) => {
|
|
119
72
|
const { status } = resourceSummary;
|
|
@@ -126,8 +79,8 @@ const BodyText = ({ resourceSummary }: { resourceSummary: IManagedResourceSummar
|
|
|
126
79
|
If you need to temporarily stop Spinnaker from managing this resource — for example, if something is wrong and
|
|
127
80
|
manual intervention is required — you can pause management and resume it later.
|
|
128
81
|
</p>
|
|
129
|
-
{
|
|
130
|
-
<MultiRegionWarning
|
|
82
|
+
{status === ManagedResourceStatus.ACTUATING && <ActuationWarning />}
|
|
83
|
+
<MultiRegionWarning isPaused regions={getRegions(resourceSummary)} />
|
|
131
84
|
</>
|
|
132
85
|
);
|
|
133
86
|
};
|
|
@@ -120,6 +120,7 @@ export interface MdConfig {
|
|
|
120
120
|
updatedAt?: Maybe<Scalars['InstantTime']>;
|
|
121
121
|
rawConfig?: Maybe<Scalars['String']>;
|
|
122
122
|
processedConfig?: Maybe<Scalars['String']>;
|
|
123
|
+
previewEnvironmentsConfigured?: Maybe<Scalars['Boolean']>;
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
export interface MdConstraint {
|
|
@@ -545,6 +546,7 @@ export type FetchApplicationQueryVariables = Exact<{
|
|
|
545
546
|
export type FetchApplicationQuery = { __typename?: 'Query' } & {
|
|
546
547
|
application?: Maybe<
|
|
547
548
|
{ __typename?: 'MdApplication' } & Pick<MdApplication, 'id' | 'name' | 'account'> & {
|
|
549
|
+
config?: Maybe<{ __typename?: 'MdConfig' } & Pick<MdConfig, 'id' | 'previewEnvironmentsConfigured'>>;
|
|
548
550
|
environments: Array<
|
|
549
551
|
{ __typename?: 'MdEnvironment' } & Pick<MdEnvironment, 'isDeleting'> & {
|
|
550
552
|
state: { __typename?: 'MdEnvironmentState' } & Pick<MdEnvironmentState, 'id'> & {
|
|
@@ -828,6 +830,12 @@ export type ImportDeliveryConfigMutationVariables = Exact<{
|
|
|
828
830
|
|
|
829
831
|
export type ImportDeliveryConfigMutation = { __typename?: 'Mutation' } & Pick<Mutation, 'importDeliveryConfig'>;
|
|
830
832
|
|
|
833
|
+
export type ToggleResourceManagementMutationVariables = Exact<{
|
|
834
|
+
payload?: Maybe<MdToggleResourceManagementPayload>;
|
|
835
|
+
}>;
|
|
836
|
+
|
|
837
|
+
export type ToggleResourceManagementMutation = { __typename?: 'Mutation' } & Pick<Mutation, 'toggleResourceManagement'>;
|
|
838
|
+
|
|
831
839
|
export const ActionDetailsFragmentDoc = gql`
|
|
832
840
|
fragment actionDetails on MdAction {
|
|
833
841
|
id
|
|
@@ -931,6 +939,10 @@ export const FetchApplicationDocument = gql`
|
|
|
931
939
|
id
|
|
932
940
|
name
|
|
933
941
|
account
|
|
942
|
+
config {
|
|
943
|
+
id
|
|
944
|
+
previewEnvironmentsConfigured
|
|
945
|
+
}
|
|
934
946
|
environments {
|
|
935
947
|
...baseEnvironmentFields
|
|
936
948
|
isDeleting
|
|
@@ -1888,3 +1900,45 @@ export type ImportDeliveryConfigMutationOptions = Apollo.BaseMutationOptions<
|
|
|
1888
1900
|
ImportDeliveryConfigMutation,
|
|
1889
1901
|
ImportDeliveryConfigMutationVariables
|
|
1890
1902
|
>;
|
|
1903
|
+
export const ToggleResourceManagementDocument = gql`
|
|
1904
|
+
mutation ToggleResourceManagement($payload: MdToggleResourceManagementPayload) {
|
|
1905
|
+
toggleResourceManagement(payload: $payload)
|
|
1906
|
+
}
|
|
1907
|
+
`;
|
|
1908
|
+
export type ToggleResourceManagementMutationFn = Apollo.MutationFunction<
|
|
1909
|
+
ToggleResourceManagementMutation,
|
|
1910
|
+
ToggleResourceManagementMutationVariables
|
|
1911
|
+
>;
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* __useToggleResourceManagementMutation__
|
|
1915
|
+
*
|
|
1916
|
+
* To run a mutation, you first call `useToggleResourceManagementMutation` within a React component and pass it any options that fit your needs.
|
|
1917
|
+
* When your component renders, `useToggleResourceManagementMutation` returns a tuple that includes:
|
|
1918
|
+
* - A mutate function that you can call at any time to execute the mutation
|
|
1919
|
+
* - An object with fields that represent the current status of the mutation's execution
|
|
1920
|
+
*
|
|
1921
|
+
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
|
1922
|
+
*
|
|
1923
|
+
* @example
|
|
1924
|
+
* const [toggleResourceManagementMutation, { data, loading, error }] = useToggleResourceManagementMutation({
|
|
1925
|
+
* variables: {
|
|
1926
|
+
* payload: // value for 'payload'
|
|
1927
|
+
* },
|
|
1928
|
+
* });
|
|
1929
|
+
*/
|
|
1930
|
+
export function useToggleResourceManagementMutation(
|
|
1931
|
+
baseOptions?: Apollo.MutationHookOptions<ToggleResourceManagementMutation, ToggleResourceManagementMutationVariables>,
|
|
1932
|
+
) {
|
|
1933
|
+
const options = { ...defaultOptions, ...baseOptions };
|
|
1934
|
+
return Apollo.useMutation<ToggleResourceManagementMutation, ToggleResourceManagementMutationVariables>(
|
|
1935
|
+
ToggleResourceManagementDocument,
|
|
1936
|
+
options,
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
export type ToggleResourceManagementMutationHookResult = ReturnType<typeof useToggleResourceManagementMutation>;
|
|
1940
|
+
export type ToggleResourceManagementMutationResult = Apollo.MutationResult<ToggleResourceManagementMutation>;
|
|
1941
|
+
export type ToggleResourceManagementMutationOptions = Apollo.BaseMutationOptions<
|
|
1942
|
+
ToggleResourceManagementMutation,
|
|
1943
|
+
ToggleResourceManagementMutationVariables
|
|
1944
|
+
>;
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { ApplicationQueryError } from '../ApplicationQueryError';
|
|
4
4
|
import { EnvironmentOverview } from './EnvironmentOverview';
|
|
5
|
+
import { PreviewEnvironments } from './PreviewEnvironments';
|
|
5
6
|
import { EnvironmentsRender, useOrderedEnvironment } from '../environmentBaseElements/EnvironmentsRender';
|
|
6
7
|
import { useFetchApplicationQuery } from '../graphql/graphql-sdk';
|
|
7
8
|
import { Messages } from '../messages/Messages';
|
|
@@ -25,11 +26,13 @@ export const EnvironmentsOverview = () => {
|
|
|
25
26
|
environments.filter((env) => !env.isPreview),
|
|
26
27
|
);
|
|
27
28
|
|
|
28
|
-
const
|
|
29
|
+
const previewEnvironments = useOrderedEnvironment(
|
|
29
30
|
wrapperRef,
|
|
30
31
|
environments.filter((env) => env.isPreview),
|
|
31
32
|
);
|
|
32
33
|
|
|
34
|
+
const previewEnvironmentsConfigured = data?.application?.config?.previewEnvironmentsConfigured;
|
|
35
|
+
|
|
33
36
|
let content;
|
|
34
37
|
if (loading && !data) {
|
|
35
38
|
content = <Spinner {...spinnerProps} message="Loading environments..." />;
|
|
@@ -46,16 +49,10 @@ export const EnvironmentsOverview = () => {
|
|
|
46
49
|
<EnvironmentOverview key={env.name} environment={env} />
|
|
47
50
|
))}
|
|
48
51
|
</EnvironmentsRender>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)}
|
|
54
|
-
<EnvironmentsRender {...previewEnvironmentsProps}>
|
|
55
|
-
{previewEnvironments.map((env) => (
|
|
56
|
-
<EnvironmentOverview key={env.name} environment={env} />
|
|
57
|
-
))}
|
|
58
|
-
</EnvironmentsRender>
|
|
52
|
+
<PreviewEnvironments
|
|
53
|
+
orderedEnvironments={previewEnvironments}
|
|
54
|
+
isConfigured={previewEnvironmentsConfigured}
|
|
55
|
+
/>
|
|
59
56
|
</>
|
|
60
57
|
) : (
|
|
61
58
|
<div className="error-message">No environments found</div>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { EnvironmentOverview } from './EnvironmentOverview';
|
|
4
|
+
import { EnvironmentsRender, OrderedEnvironments } from '../environmentBaseElements/EnvironmentsRender';
|
|
5
|
+
import { QueryEnvironment } from './types';
|
|
6
|
+
import { getDocsUrl } from '../utils/defaults';
|
|
7
|
+
import { useLogEvent } from '../utils/logging';
|
|
8
|
+
|
|
9
|
+
interface IPreviewEnvironmentsProps {
|
|
10
|
+
orderedEnvironments: OrderedEnvironments<QueryEnvironment>;
|
|
11
|
+
isConfigured?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const PreviewEnvironments = ({ orderedEnvironments, isConfigured }: IPreviewEnvironmentsProps) => {
|
|
15
|
+
const { environments: previewEnvironments, ...previewEnvironmentsProps } = orderedEnvironments;
|
|
16
|
+
const logClick = useLogEvent('PreviewEnvironments');
|
|
17
|
+
const docsLink = getDocsUrl('previewEnvironments');
|
|
18
|
+
const hasActivePreviewEnvironments = previewEnvironments.length > 0;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
{(isConfigured || hasActivePreviewEnvironments) && (
|
|
23
|
+
<h4 className="sp-margin-2xl-top sp-margin-m-bottom self-left">
|
|
24
|
+
<b>Preview Environments</b>
|
|
25
|
+
</h4>
|
|
26
|
+
)}
|
|
27
|
+
{isConfigured && !hasActivePreviewEnvironments && (
|
|
28
|
+
<div className="self-left">
|
|
29
|
+
No PRs matching your branch filter were found.
|
|
30
|
+
<br />
|
|
31
|
+
Your preview environments will automatically appear here once you create a PR from a branch matching your
|
|
32
|
+
config definition.{' '}
|
|
33
|
+
{docsLink && (
|
|
34
|
+
<a target="_blank" onClick={() => logClick({ action: 'Learn More' })} href={docsLink}>
|
|
35
|
+
Learn More
|
|
36
|
+
</a>
|
|
37
|
+
)}
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
40
|
+
<EnvironmentsRender {...previewEnvironmentsProps}>
|
|
41
|
+
{previewEnvironments.map((env) => (
|
|
42
|
+
<EnvironmentOverview key={env.name} environment={env} />
|
|
43
|
+
))}
|
|
44
|
+
</EnvironmentsRender>
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import { ResourceTask } from './ResourceTask';
|
|
4
|
+
import { ConfirmationModalService } from '../../confirmationModal/confirmationModal.service';
|
|
4
5
|
import { EnvironmentItem } from '../environmentBaseElements/EnvironmentItem';
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
FetchResourceStatusDocument,
|
|
8
|
+
MdResourceActuationState,
|
|
9
|
+
useFetchResourceStatusQuery,
|
|
10
|
+
useToggleResourceManagementMutation,
|
|
11
|
+
} from '../graphql/graphql-sdk';
|
|
6
12
|
import { Icon, Markdown, useApplicationContextSafe } from '../../presentation';
|
|
7
13
|
import { showManagedResourceHistoryModal } from '../resourceHistory/ManagedResourceHistoryModal';
|
|
8
14
|
import { showResourceDefinitionModal } from '../resources/ResourceDefinitionModal';
|
|
9
15
|
import { ResourceTitle } from '../resources/ResourceTitle';
|
|
16
|
+
import { ToggleResourceManagement } from '../resources/ToggleResourceManagement';
|
|
10
17
|
import { IResourceLinkProps, resourceManager } from '../resources/resourceRegistry';
|
|
11
18
|
import { QueryResource } from './types';
|
|
12
19
|
import { getIsDebugMode } from '../utils/debugMode';
|
|
@@ -23,22 +30,30 @@ const statusUtils: {
|
|
|
23
30
|
};
|
|
24
31
|
} = {
|
|
25
32
|
ERROR: { color: 'var(--color-status-error)', icon: 'fas fa-times', defaultReason: 'Failed to update resource' },
|
|
26
|
-
NOT_MANAGED: {
|
|
33
|
+
NOT_MANAGED: {
|
|
34
|
+
color: 'var(--color-status-warning)',
|
|
35
|
+
icon: 'fas fa-pause',
|
|
36
|
+
defaultReason: 'Resource management is paused',
|
|
37
|
+
},
|
|
27
38
|
WAITING: { icon: 'far fa-hourglass', defaultReason: 'Resource is currently locked and can not be updated' },
|
|
28
39
|
PROCESSING: { icon: 'far fa-hourglass', defaultReason: 'Resource is being updated' },
|
|
29
40
|
DELETING: { icon: 'far fa-trash-alt', defaultReason: 'Resource is being deleted' },
|
|
30
41
|
};
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
appName,
|
|
34
|
-
environmentName,
|
|
35
|
-
resourceId,
|
|
36
|
-
}: {
|
|
43
|
+
interface IStatusProps {
|
|
37
44
|
appName: string;
|
|
38
45
|
environmentName: string;
|
|
39
46
|
resourceId: string;
|
|
40
|
-
|
|
47
|
+
regions: string[];
|
|
48
|
+
account?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const Status = ({ appName, environmentName, resourceId, regions, account }: IStatusProps) => {
|
|
41
52
|
const { data: resourceStatuses, error, loading } = useFetchResourceStatusQuery({ variables: { appName } });
|
|
53
|
+
const [enableResourceManagement] = useToggleResourceManagementMutation({
|
|
54
|
+
variables: { payload: { id: resourceId, isPaused: false } },
|
|
55
|
+
refetchQueries: [{ query: FetchResourceStatusDocument, variables: { appName } }],
|
|
56
|
+
});
|
|
42
57
|
const state = resourceStatuses?.application?.environments
|
|
43
58
|
.find((env) => env.name === environmentName)
|
|
44
59
|
?.state.resources?.find((resource) => resource.id === resourceId)?.state;
|
|
@@ -51,32 +66,53 @@ const Status = ({
|
|
|
51
66
|
</div>
|
|
52
67
|
);
|
|
53
68
|
}
|
|
54
|
-
|
|
55
|
-
if (state)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
if (!state) return <Spinner className="sp-margin-xs-top" mode="circular" size="nano" color="var(--color-accent)" />;
|
|
70
|
+
if (state.status === 'UP_TO_DATE') return null;
|
|
71
|
+
const isNotManaged = state.status === 'NOT_MANAGED';
|
|
72
|
+
const reasonElem = (
|
|
73
|
+
<div>
|
|
74
|
+
{state.reason || statusUtils[state.status].defaultReason}
|
|
75
|
+
{isNotManaged ? ' (click to enable...)' : undefined}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
return (
|
|
79
|
+
<div className="resource-status">
|
|
80
|
+
<i
|
|
81
|
+
className={statusUtils[state.status].icon}
|
|
82
|
+
style={{
|
|
83
|
+
color: statusUtils[state.status].color || 'var(--color-titanium)',
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
<div>
|
|
87
|
+
{isNotManaged ? (
|
|
88
|
+
<button
|
|
89
|
+
className="as-link"
|
|
90
|
+
onClick={() => {
|
|
91
|
+
ConfirmationModalService.confirm({
|
|
92
|
+
header: `Really resume resource management?`,
|
|
93
|
+
bodyContent: <ToggleResourceManagement isPaused regions={regions} />,
|
|
94
|
+
account: account,
|
|
95
|
+
buttonText: `Resume management`,
|
|
96
|
+
submitMethod: enableResourceManagement,
|
|
97
|
+
});
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
{reasonElem}
|
|
101
|
+
</button>
|
|
102
|
+
) : (
|
|
103
|
+
reasonElem
|
|
104
|
+
)}
|
|
105
|
+
{state.event && state.event !== state.reason && <Markdown className="event" message={state.event} />}
|
|
106
|
+
{Boolean(state.tasks?.length) && (
|
|
107
|
+
<ul className="tasks-list">
|
|
108
|
+
{state.tasks?.map(({ id, name }) => (
|
|
109
|
+
<ResourceTask key={id} id={id} name={name} />
|
|
110
|
+
))}
|
|
111
|
+
</ul>
|
|
112
|
+
)}
|
|
75
113
|
</div>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return <Spinner className="sp-margin-xs-top" mode="circular" size="nano" color="var(--color-accent)" />;
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
80
116
|
};
|
|
81
117
|
|
|
82
118
|
export const Resource = ({ resource, environment }: { resource: QueryResource; environment: string }) => {
|
|
@@ -142,7 +178,13 @@ export const Resource = ({ resource, environment }: { resource: QueryResource; e
|
|
|
142
178
|
)}
|
|
143
179
|
</div>
|
|
144
180
|
<div>
|
|
145
|
-
<Status
|
|
181
|
+
<Status
|
|
182
|
+
appName={app.name}
|
|
183
|
+
environmentName={environment}
|
|
184
|
+
resourceId={resource.id}
|
|
185
|
+
account={account}
|
|
186
|
+
regions={regions}
|
|
187
|
+
/>
|
|
146
188
|
</div>
|
|
147
189
|
</EnvironmentItem>
|
|
148
190
|
);
|
|
@@ -7,7 +7,8 @@ import { Tooltip, useApplicationContextSafe } from '../../../presentation';
|
|
|
7
7
|
import { QueryArtifactVersionTask, QueryArtifactVersionTaskStatus } from '../types';
|
|
8
8
|
import { TOOLTIP_DELAY_SHOW } from '../../utils/defaults';
|
|
9
9
|
import { useLogEvent } from '../../utils/logging';
|
|
10
|
-
import {
|
|
10
|
+
import { useNotifyOnError } from '../../utils/useNotifyOnError.hook';
|
|
11
|
+
import { Spinner } from '../../../widgets';
|
|
11
12
|
|
|
12
13
|
import './ArtifactVersionTasks.less';
|
|
13
14
|
|
|
@@ -55,15 +56,7 @@ const ArtifactVersionTask = ({ type, artifact, task }: IArtifactVersionTaskProps
|
|
|
55
56
|
},
|
|
56
57
|
});
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
if (error) {
|
|
60
|
-
NotifierService.publish({
|
|
61
|
-
key: task.id,
|
|
62
|
-
content: `Failed to re-run ${type} - ${error.message}`,
|
|
63
|
-
options: { type: 'error' },
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}, [error]);
|
|
59
|
+
useNotifyOnError({ key: task.id, content: `Failed to re-run ${type}`, error });
|
|
67
60
|
|
|
68
61
|
return (
|
|
69
62
|
<div className="version-task">
|
|
@@ -10,7 +10,8 @@ import { CollapsibleSection, useApplicationContextSafe } from '../../../presenta
|
|
|
10
10
|
import { ArtifactVersionProps, QueryConstraint } from '../types';
|
|
11
11
|
import { getConstraintsStatusSummary } from './utils';
|
|
12
12
|
import { useLogEvent } from '../../utils/logging';
|
|
13
|
-
import {
|
|
13
|
+
import { useNotifyOnError } from '../../utils/useNotifyOnError.hook';
|
|
14
|
+
import { Spinner } from '../../../widgets';
|
|
14
15
|
|
|
15
16
|
import './Constraints.less';
|
|
16
17
|
|
|
@@ -31,16 +32,7 @@ const ConstraintContent = ({ constraint, versionProps }: IConstraintContentProps
|
|
|
31
32
|
],
|
|
32
33
|
});
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
if (error) {
|
|
36
|
-
NotifierService.publish({
|
|
37
|
-
action: 'create',
|
|
38
|
-
key: 'updateConstraintError',
|
|
39
|
-
content: `Failed to update constraint - ${error.message}`,
|
|
40
|
-
options: { type: 'error' },
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}, [error]);
|
|
35
|
+
useNotifyOnError({ key: 'updateConstraintError', content: `Failed to update constraint`, error });
|
|
44
36
|
|
|
45
37
|
return (
|
|
46
38
|
<dl className="constraint-content">
|
|
@@ -95,6 +95,10 @@ query fetchApplication($appName: String!, $statuses: [MdArtifactStatusInEnvironm
|
|
|
95
95
|
id
|
|
96
96
|
name
|
|
97
97
|
account
|
|
98
|
+
config {
|
|
99
|
+
id
|
|
100
|
+
previewEnvironmentsConfigured
|
|
101
|
+
}
|
|
98
102
|
environments {
|
|
99
103
|
...baseEnvironmentFields
|
|
100
104
|
isDeleting
|
|
@@ -339,3 +343,7 @@ mutation DismissNotification($payload: MdDismissNotificationPayload!) {
|
|
|
339
343
|
mutation ImportDeliveryConfig($application: String!) {
|
|
340
344
|
importDeliveryConfig(application: $application)
|
|
341
345
|
}
|
|
346
|
+
|
|
347
|
+
mutation ToggleResourceManagement($payload: MdToggleResourceManagementPayload) {
|
|
348
|
+
toggleResourceManagement(payload: $payload)
|
|
349
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface IToggleResourceManagementProps {
|
|
4
|
+
isPaused: boolean;
|
|
5
|
+
isActuating?: boolean;
|
|
6
|
+
regions: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ActuationWarning = () => (
|
|
10
|
+
<p>
|
|
11
|
+
<div className="horizontal top sp-padding-m alert alert-warning">
|
|
12
|
+
<i className="fa fa-exclamation-triangle sp-margin-m-right sp-margin-xs-top" />
|
|
13
|
+
<span>
|
|
14
|
+
Pausing management will not interrupt the action Spinnaker is currently performing to resolve the difference
|
|
15
|
+
from desired state.
|
|
16
|
+
</span>
|
|
17
|
+
</div>
|
|
18
|
+
</p>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const ToggleResourceManagement = ({ isPaused, isActuating, regions }: IToggleResourceManagementProps) => {
|
|
22
|
+
if (isPaused) {
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<p>Spinnaker will resume taking action to correct differences from the desired state.</p>
|
|
26
|
+
<MultiRegionWarning isPaused regions={regions} />
|
|
27
|
+
</>
|
|
28
|
+
);
|
|
29
|
+
} else {
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<p>While a resource is paused, Spinnaker will not take action to correct differences from the desired state.</p>
|
|
33
|
+
{isActuating && <ActuationWarning />}
|
|
34
|
+
<MultiRegionWarning isPaused regions={regions} />
|
|
35
|
+
</>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const MultiRegionWarning = ({ isPaused, regions }: Omit<IToggleResourceManagementProps, 'isActuating'>) => {
|
|
41
|
+
if (regions.length < 2) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return (
|
|
45
|
+
<div className="horizontal top sp-padding-m alert alert-warning">
|
|
46
|
+
<i className="fa fa-exclamation-triangle sp-margin-m-right sp-margin-xs-top" />
|
|
47
|
+
<span>
|
|
48
|
+
{isPaused ? 'Resuming' : 'Pausing'} management of this resource will affect the following regions:{' '}
|
|
49
|
+
<b>{regions.join(', ')}</b>.
|
|
50
|
+
</span>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
@@ -26,6 +26,6 @@ const DOCS_URLS: IManagedDeliveryURLs = {
|
|
|
26
26
|
resourceStatus: 'https://www.spinnaker.io/guides/user/managed-delivery/resource-status/',
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
export const getDocsUrl = (doc: keyof IManagedDeliveryURLs): string => {
|
|
29
|
+
export const getDocsUrl = (doc: keyof IManagedDeliveryURLs): string | undefined => {
|
|
30
30
|
return SETTINGS.managedDelivery?.urls?.[doc] || DOCS_URLS[doc];
|
|
31
31
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ApolloError } from '@apollo/client';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { NotifierService } from '../../widgets';
|
|
4
|
+
|
|
5
|
+
export const useNotifyOnError = ({
|
|
6
|
+
key,
|
|
7
|
+
content,
|
|
8
|
+
error,
|
|
9
|
+
}: {
|
|
10
|
+
key: string;
|
|
11
|
+
content?: string;
|
|
12
|
+
error: ApolloError | undefined;
|
|
13
|
+
}) => {
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
if (!error) return;
|
|
16
|
+
NotifierService.publish({
|
|
17
|
+
key,
|
|
18
|
+
content: content ? `${content} - ` : '' + error.message,
|
|
19
|
+
options: { type: 'error' },
|
|
20
|
+
});
|
|
21
|
+
}, [error]);
|
|
22
|
+
};
|
package/src/modal/modals.less
CHANGED
|
@@ -222,7 +222,7 @@ html .select2-container-multi {
|
|
|
222
222
|
display: inline-block;
|
|
223
223
|
border: 1px solid var(--color-alto);
|
|
224
224
|
background-color: var(--color-white);
|
|
225
|
-
font-family: 'Source Sans
|
|
225
|
+
font-family: 'Source Sans 3', sans-serif;
|
|
226
226
|
border-radius: 4px;
|
|
227
227
|
}
|
|
228
228
|
.select2-search-choice-close {
|
|
@@ -30,7 +30,7 @@ export interface ISingleExecutionRouterStateChange extends IStateChange {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function getAndTransformExecution(id: string, app: Application) {
|
|
33
|
-
return ReactInjector.executionService.getExecution(id).then((execution) => {
|
|
33
|
+
return ReactInjector.executionService.getExecution(id, app.pipelineConfigs?.data).then((execution) => {
|
|
34
34
|
ExecutionsTransformer.transformExecution(app, execution);
|
|
35
35
|
return execution;
|
|
36
36
|
});
|
|
@@ -97,7 +97,7 @@ export class ExecutionService {
|
|
|
97
97
|
return this.getFilteredExecutions(applicationName, statuses, limit, null, expand);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
public getExecution(executionId: string): PromiseLike<IExecution> {
|
|
100
|
+
public getExecution(executionId: string, pipelineConfigs: IPipeline[] = []): PromiseLike<IExecution> {
|
|
101
101
|
return REST('/pipelines')
|
|
102
102
|
.path(executionId)
|
|
103
103
|
.get()
|
|
@@ -106,6 +106,11 @@ export class ExecutionService {
|
|
|
106
106
|
execution.hydrated = true;
|
|
107
107
|
this.cleanExecutionForDiffing(execution);
|
|
108
108
|
if (application && name) {
|
|
109
|
+
const cached = pipelineConfigs.find((config) => config.id === execution.pipelineConfigId);
|
|
110
|
+
if (cached) {
|
|
111
|
+
execution.pipelineConfig = cached;
|
|
112
|
+
return Promise.resolve(execution);
|
|
113
|
+
}
|
|
109
114
|
return REST('/applications')
|
|
110
115
|
.path(application, 'pipelineConfigs', name)
|
|
111
116
|
.get()
|
|
@@ -402,7 +407,7 @@ export class ExecutionService {
|
|
|
402
407
|
});
|
|
403
408
|
application.executions.data.forEach((execution: IExecution) => {
|
|
404
409
|
if (execution.isActive && application.runningExecutions.data.every((e: IExecution) => e.id !== execution.id)) {
|
|
405
|
-
this.getExecution(execution.id).then((updatedExecution) => {
|
|
410
|
+
this.getExecution(execution.id, application.pipelineConfigs?.data).then((updatedExecution) => {
|
|
406
411
|
this.updateExecution(application, updatedExecution);
|
|
407
412
|
});
|
|
408
413
|
}
|