argo-cd

Форк
0
212 строк · 11.4 Кб
1
import {HelpIcon} from 'argo-ui';
2
import * as React from 'react';
3
import {ARGO_GRAY6_COLOR, DataLoader} from '../../../shared/components';
4
import {Revision} from '../../../shared/components/revision';
5
import {Timestamp} from '../../../shared/components/timestamp';
6
import * as models from '../../../shared/models';
7
import {services} from '../../../shared/services';
8
import {ApplicationSyncWindowStatusIcon, ComparisonStatusIcon, getAppDefaultSource, getAppOperationState} from '../utils';
9
import {getConditionCategory, HealthStatusIcon, OperationState, syncStatusMessage, helpTip} from '../utils';
10
import {RevisionMetadataPanel} from './revision-metadata-panel';
11

12
import './application-status-panel.scss';
13

14
interface Props {
15
    application: models.Application;
16
    showDiff?: () => any;
17
    showOperation?: () => any;
18
    showConditions?: () => any;
19
    showExtension?: (id: string) => any;
20
    showMetadataInfo?: (revision: string) => any;
21
}
22

23
interface SectionInfo {
24
    title: string;
25
    helpContent?: string;
26
}
27

28
const sectionLabel = (info: SectionInfo) => (
29
    <label style={{fontSize: '12px', fontWeight: 600, color: ARGO_GRAY6_COLOR}}>
30
        {info.title}
31
        {info.helpContent && <HelpIcon title={info.helpContent} />}
32
    </label>
33
);
34

35
const sectionHeader = (info: SectionInfo, hasMultipleSources: boolean, onClick?: () => any) => {
36
    return (
37
        <div style={{display: 'flex', alignItems: 'center', marginBottom: '0.5em'}}>
38
            {sectionLabel(info)}
39
            {onClick && (
40
                <button className='application-status-panel__more-button' onClick={onClick} disabled={hasMultipleSources}>
41
                    {hasMultipleSources && helpTip('More details are not supported for apps with multiple sources')}
42
                    <i className='fa fa-ellipsis-h' />
43
                </button>
44
            )}
45
        </div>
46
    );
47
};
48

49
export const ApplicationStatusPanel = ({application, showDiff, showOperation, showConditions, showExtension, showMetadataInfo}: Props) => {
50
    const today = new Date();
51

52
    let daysSinceLastSynchronized = 0;
53
    const history = application.status.history || [];
54
    if (history.length > 0) {
55
        const deployDate = new Date(history[history.length - 1].deployedAt);
56
        daysSinceLastSynchronized = Math.round(Math.abs((today.getTime() - deployDate.getTime()) / (24 * 60 * 60 * 1000)));
57
    }
58
    const cntByCategory = (application.status.conditions || []).reduce(
59
        (map, next) => map.set(getConditionCategory(next), (map.get(getConditionCategory(next)) || 0) + 1),
60
        new Map<string, number>()
61
    );
62
    const appOperationState = getAppOperationState(application);
63
    if (application.metadata.deletionTimestamp && !appOperationState) {
64
        showOperation = null;
65
    }
66

67
    const statusExtensions = services.extensions.getStatusPanelExtensions();
68

69
    const infos = cntByCategory.get('info');
70
    const warnings = cntByCategory.get('warning');
71
    const errors = cntByCategory.get('error');
72
    const source = getAppDefaultSource(application);
73
    const hasMultipleSources = application.spec.sources && application.spec.sources.length > 0;
74
    return (
75
        <div className='application-status-panel row'>
76
            <div className='application-status-panel__item'>
77
                <div style={{lineHeight: '19.5px', marginBottom: '0.3em'}}>{sectionLabel({title: 'APP HEALTH', helpContent: 'The health status of your app'})}</div>
78
                <div className='application-status-panel__item-value'>
79
                    <HealthStatusIcon state={application.status.health} />
80
                    &nbsp;
81
                    {application.status.health.status}
82
                </div>
83
                {application.status.health.message && <div className='application-status-panel__item-name'>{application.status.health.message}</div>}
84
            </div>
85
            <div className='application-status-panel__item'>
86
                <React.Fragment>
87
                    {sectionHeader(
88
                        {
89
                            title: 'SYNC STATUS',
90
                            helpContent: 'Whether or not the version of your app is up to date with your repo. You may wish to sync your app if it is out-of-sync.'
91
                        },
92
                        hasMultipleSources,
93
                        () => showMetadataInfo(application.status.sync ? application.status.sync.revision : '')
94
                    )}
95
                    <div className={`application-status-panel__item-value${appOperationState?.phase ? ` application-status-panel__item-value--${appOperationState.phase}` : ''}`}>
96
                        <div>
97
                            {application.status.sync.status === models.SyncStatuses.OutOfSync ? (
98
                                <a onClick={() => showDiff && showDiff()}>
99
                                    <ComparisonStatusIcon status={application.status.sync.status} label={true} />
100
                                </a>
101
                            ) : (
102
                                <ComparisonStatusIcon status={application.status.sync.status} label={true} />
103
                            )}
104
                        </div>
105
                        <div className='application-status-panel__item-value__revision show-for-large'>{syncStatusMessage(application)}</div>
106
                    </div>
107
                    <div className='application-status-panel__item-name' style={{marginBottom: '0.5em'}}>
108
                        {application.spec.syncPolicy?.automated ? 'Auto sync is enabled.' : 'Auto sync is not enabled.'}
109
                    </div>
110
                    {application.status && application.status.sync && application.status.sync.revision && !application.spec.source.chart && (
111
                        <div className='application-status-panel__item-name'>
112
                            <RevisionMetadataPanel
113
                                appName={application.metadata.name}
114
                                appNamespace={application.metadata.namespace}
115
                                type={source.chart && 'helm'}
116
                                revision={application.status.sync.revision}
117
                            />
118
                        </div>
119
                    )}
120
                </React.Fragment>
121
            </div>
122
            {appOperationState && (
123
                <div className='application-status-panel__item'>
124
                    <React.Fragment>
125
                        {sectionHeader(
126
                            {
127
                                title: 'LAST SYNC',
128
                                helpContent:
129
                                    'Whether or not your last app sync was successful. It has been ' +
130
                                    daysSinceLastSynchronized +
131
                                    ' days since last sync. Click for the status of that sync.'
132
                            },
133
                            hasMultipleSources,
134
                            () => showMetadataInfo(appOperationState.syncResult ? appOperationState.syncResult.revision : '')
135
                        )}
136
                        <div className={`application-status-panel__item-value application-status-panel__item-value--${appOperationState.phase}`}>
137
                            <a onClick={() => showOperation && showOperation()}>
138
                                <OperationState app={application} />{' '}
139
                            </a>
140
                            {appOperationState.syncResult && appOperationState.syncResult.revision && (
141
                                <div className='application-status-panel__item-value__revision show-for-large'>
142
                                    to <Revision repoUrl={source.repoURL} revision={appOperationState.syncResult.revision} />
143
                                </div>
144
                            )}
145
                        </div>
146

147
                        <div className='application-status-panel__item-name' style={{marginBottom: '0.5em'}}>
148
                            {appOperationState.phase} <Timestamp date={appOperationState.finishedAt || appOperationState.startedAt} />
149
                        </div>
150
                        {(appOperationState.syncResult && appOperationState.syncResult.revision && (
151
                            <RevisionMetadataPanel
152
                                appName={application.metadata.name}
153
                                appNamespace={application.metadata.namespace}
154
                                type={source.chart && 'helm'}
155
                                revision={appOperationState.syncResult.revision}
156
                            />
157
                        )) || <div className='application-status-panel__item-name'>{appOperationState.message}</div>}
158
                    </React.Fragment>
159
                </div>
160
            )}
161
            {application.status.conditions && (
162
                <div className={`application-status-panel__item`}>
163
                    {sectionLabel({title: 'APP CONDITIONS'})}
164
                    <div className='application-status-panel__item-value application-status-panel__conditions' onClick={() => showConditions && showConditions()}>
165
                        {infos && (
166
                            <a className='info'>
167
                                <i className='fa fa-info-circle' /> {infos} Info
168
                            </a>
169
                        )}
170
                        {warnings && (
171
                            <a className='warning'>
172
                                <i className='fa fa-exclamation-triangle' /> {warnings} Warning{warnings !== 1 && 's'}
173
                            </a>
174
                        )}
175
                        {errors && (
176
                            <a className='error'>
177
                                <i className='fa fa-exclamation-circle' /> {errors} Error{errors !== 1 && 's'}
178
                            </a>
179
                        )}
180
                    </div>
181
                </div>
182
            )}
183
            <DataLoader
184
                noLoaderOnInputChange={true}
185
                input={application}
186
                load={async app => {
187
                    return await services.applications.getApplicationSyncWindowState(app.metadata.name, app.metadata.namespace);
188
                }}>
189
                {(data: models.ApplicationSyncWindowState) => (
190
                    <React.Fragment>
191
                        {data.assignedWindows && (
192
                            <div className='application-status-panel__item' style={{position: 'relative'}}>
193
                                {sectionLabel({
194
                                    title: 'SYNC WINDOWS',
195
                                    helpContent:
196
                                        'The aggregate state of sync windows for this app. ' +
197
                                        'Red: no syncs allowed. ' +
198
                                        'Yellow: manual syncs allowed. ' +
199
                                        'Green: all syncs allowed'
200
                                })}
201
                                <div className='application-status-panel__item-value' style={{margin: 'auto 0'}}>
202
                                    <ApplicationSyncWindowStatusIcon project={application.spec.project} state={data} />
203
                                </div>
204
                            </div>
205
                        )}
206
                    </React.Fragment>
207
                )}
208
            </DataLoader>
209
            {statusExtensions && statusExtensions.map(ext => <ext.component key={ext.title} application={application} openFlyout={() => showExtension && showExtension(ext.id)} />)}
210
        </div>
211
    );
212
};
213

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.