Debug health check (#5587)

main
Andrey Zhavoronkov 3 years ago committed by GitHub
parent 8f71d90af4
commit 3fa6f35031
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,8 +4,8 @@ ARG http_proxy
ARG https_proxy ARG https_proxy
ARG no_proxy ARG no_proxy
ARG socks_proxy ARG socks_proxy
ARG PUBLIC_INSTANCE
ARG WA_PAGE_VIEW_HIT ARG WA_PAGE_VIEW_HIT
ARG UI_APP_CONFIG
ENV TERM=xterm \ ENV TERM=xterm \
http_proxy=${http_proxy} \ http_proxy=${http_proxy} \
@ -34,7 +34,7 @@ COPY cvat-core/ /tmp/cvat-core/
COPY cvat-canvas3d/ /tmp/cvat-canvas3d/ COPY cvat-canvas3d/ /tmp/cvat-canvas3d/
COPY cvat-canvas/ /tmp/cvat-canvas/ COPY cvat-canvas/ /tmp/cvat-canvas/
COPY cvat-ui/ /tmp/cvat-ui/ COPY cvat-ui/ /tmp/cvat-ui/
RUN yarn run build:cvat-ui RUN UI_APP_CONFIG="${UI_APP_CONFIG}" yarn run build:cvat-ui
FROM nginx:mainline-alpine FROM nginx:mainline-alpine
# Replace default.conf configuration to remove unnecessary rules # Replace default.conf configuration to remove unnecessary rules

@ -509,13 +509,32 @@ async function healthCheck(maxRetries, checkPeriod, requestTimeout, progressCall
timeout: requestTimeout, timeout: requestTimeout,
}) })
.then((response) => response.data) .then((response) => response.data)
.catch((errorData) => { .catch((error) => {
if (maxRetries > 0) { let isHealthy = true;
let data;
if (typeof error?.response?.data === 'object') {
data = error.response.data;
// Temporary workaround: ignore errors with media cache for debugging purposes only
for (const checkName in data) {
if (Object.prototype.hasOwnProperty.call(data, checkName) &&
checkName !== 'Cache backend: media' &&
data[checkName] !== 'working') {
isHealthy = false;
}
}
} else {
isHealthy = false;
}
if (!isHealthy && maxRetries > 0) {
return new Promise((resolve) => setTimeout(resolve, checkPeriod)) return new Promise((resolve) => setTimeout(resolve, checkPeriod))
.then(() => healthCheck(maxRetries - 1, checkPeriod, .then(() => healthCheck(maxRetries - 1, checkPeriod,
requestTimeout, progressCallback, attempt + 1)); requestTimeout, progressCallback, attempt + 1));
} }
throw generateError(errorData); if (isHealthy) {
return data;
}
throw generateError(error);
}); });
} }

@ -11,7 +11,7 @@ import Input from 'antd/lib/input';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react'; import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import consts from 'consts'; import config from 'config';
interface InputElementParameters { interface InputElementParameters {
clientID: number; clientID: number;
@ -51,7 +51,7 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element {
{values.map( {values.map(
(value: string): JSX.Element => ( (value: string): JSX.Element => (
<Select.Option key={value} value={value}> <Select.Option key={value} value={value}>
{value === consts.UNDEFINED_ATTRIBUTE_VALUE ? consts.NO_BREAK_SPACE : value} {value === config.UNDEFINED_ATTRIBUTE_VALUE ? config.NO_BREAK_SPACE : value}
</Select.Option> </Select.Option>
), ),
)} )}
@ -68,7 +68,7 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element {
{values.map( {values.map(
(value: string): JSX.Element => ( (value: string): JSX.Element => (
<Radio style={{ display: 'block' }} key={value} value={value}> <Radio style={{ display: 'block' }} key={value} value={value}>
{value === consts.UNDEFINED_ATTRIBUTE_VALUE ? consts.NO_BREAK_SPACE : value} {value === config.UNDEFINED_ATTRIBUTE_VALUE ? config.NO_BREAK_SPACE : value}
</Radio> </Radio>
), ),
)} )}
@ -185,7 +185,7 @@ function renderList(parameters: ListParameters): JSX.Element | null {
[key: string]: (keyEvent?: KeyboardEvent) => void; [key: string]: (keyEvent?: KeyboardEvent) => void;
} = {}; } = {};
const filteredValues = values.filter((value: string): boolean => value !== consts.UNDEFINED_ATTRIBUTE_VALUE); const filteredValues = values.filter((value: string): boolean => value !== config.UNDEFINED_ATTRIBUTE_VALUE);
filteredValues.slice(0, 10).forEach((value: string, index: number): void => { filteredValues.slice(0, 10).forEach((value: string, index: number): void => {
const key = `SET_${index}_VALUE`; const key = `SET_${index}_VALUE`;
keyMap[key] = { keyMap[key] = {

@ -21,7 +21,7 @@ import {
ReloadOutlined, ReloadOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import consts from 'consts'; import config from 'config';
import { DimensionType, CombinedState } from 'reducers'; import { DimensionType, CombinedState } from 'reducers';
import CanvasWrapperComponent from 'components/annotation-page/canvas/views/canvas2d/canvas-wrapper'; import CanvasWrapperComponent from 'components/annotation-page/canvas/views/canvas2d/canvas-wrapper';
import CanvasWrapper3DComponent, { import CanvasWrapper3DComponent, {
@ -72,7 +72,7 @@ const fitLayout = (type: DimensionType, layoutConfig: ItemLayout[]): ItemLayout[
const relatedViews = layoutConfig const relatedViews = layoutConfig
.filter((item: ItemLayout) => item.viewType === ViewType.RELATED_IMAGE); .filter((item: ItemLayout) => item.viewType === ViewType.RELATED_IMAGE);
const relatedViewsCols = relatedViews.length > 6 ? 2 : 1; const relatedViewsCols = relatedViews.length > 6 ? 2 : 1;
const height = Math.floor(consts.CANVAS_WORKSPACE_ROWS / (relatedViews.length / relatedViewsCols)); const height = Math.floor(config.CANVAS_WORKSPACE_ROWS / (relatedViews.length / relatedViewsCols));
relatedViews.forEach((view: ItemLayout, i: number) => { relatedViews.forEach((view: ItemLayout, i: number) => {
updatedLayout.push({ updatedLayout.push({
...view, ...view,
@ -83,7 +83,7 @@ const fitLayout = (type: DimensionType, layoutConfig: ItemLayout[]): ItemLayout[
}); });
}); });
let widthAvail = consts.CANVAS_WORKSPACE_COLS; let widthAvail = config.CANVAS_WORKSPACE_COLS;
if (updatedLayout.length > 0) { if (updatedLayout.length > 0) {
widthAvail -= updatedLayout[0].w * relatedViewsCols; widthAvail -= updatedLayout[0].w * relatedViewsCols;
} }
@ -96,7 +96,7 @@ const fitLayout = (type: DimensionType, layoutConfig: ItemLayout[]): ItemLayout[
x: 0, x: 0,
y: 0, y: 0,
w: widthAvail, w: widthAvail,
h: consts.CANVAS_WORKSPACE_ROWS, h: config.CANVAS_WORKSPACE_ROWS,
}); });
} else { } else {
const canvas = layoutConfig const canvas = layoutConfig
@ -113,25 +113,25 @@ const fitLayout = (type: DimensionType, layoutConfig: ItemLayout[]): ItemLayout[
x: 0, x: 0,
y: 0, y: 0,
w: widthAvail, w: widthAvail,
h: consts.CANVAS_WORKSPACE_ROWS - helpfulCanvasViewHeight, h: config.CANVAS_WORKSPACE_ROWS - helpfulCanvasViewHeight,
}, { }, {
...top, ...top,
x: 0, x: 0,
y: consts.CANVAS_WORKSPACE_ROWS, y: config.CANVAS_WORKSPACE_ROWS,
w: Math.ceil(widthAvail / 3), w: Math.ceil(widthAvail / 3),
h: helpfulCanvasViewHeight, h: helpfulCanvasViewHeight,
}, },
{ {
...side, ...side,
x: Math.ceil(widthAvail / 3), x: Math.ceil(widthAvail / 3),
y: consts.CANVAS_WORKSPACE_ROWS, y: config.CANVAS_WORKSPACE_ROWS,
w: Math.ceil(widthAvail / 3), w: Math.ceil(widthAvail / 3),
h: helpfulCanvasViewHeight, h: helpfulCanvasViewHeight,
}, },
{ {
...front, ...front,
x: Math.ceil(widthAvail / 3) * 2, x: Math.ceil(widthAvail / 3) * 2,
y: consts.CANVAS_WORKSPACE_ROWS, y: config.CANVAS_WORKSPACE_ROWS,
w: Math.floor(widthAvail / 3), w: Math.floor(widthAvail / 3),
h: helpfulCanvasViewHeight, h: helpfulCanvasViewHeight,
}); });
@ -152,8 +152,8 @@ function CanvasLayout({ type }: { type?: DimensionType }): JSX.Element {
containerHeight = window.innerHeight - container.getBoundingClientRect().bottom; containerHeight = window.innerHeight - container.getBoundingClientRect().bottom;
// https://github.com/react-grid-layout/react-grid-layout/issues/628#issuecomment-1228453084 // https://github.com/react-grid-layout/react-grid-layout/issues/628#issuecomment-1228453084
return Math.floor( return Math.floor(
(containerHeight - consts.CANVAS_WORKSPACE_MARGIN * (consts.CANVAS_WORKSPACE_ROWS)) / (containerHeight - config.CANVAS_WORKSPACE_MARGIN * (config.CANVAS_WORKSPACE_ROWS)) /
consts.CANVAS_WORKSPACE_ROWS, config.CANVAS_WORKSPACE_ROWS,
); );
} }
@ -214,11 +214,11 @@ function CanvasLayout({ type }: { type?: DimensionType }): JSX.Element {
<Layout.Content> <Layout.Content>
{ !!rowHeight && ( { !!rowHeight && (
<ReactGridLayout <ReactGridLayout
cols={consts.CANVAS_WORKSPACE_COLS} cols={config.CANVAS_WORKSPACE_COLS}
maxRows={consts.CANVAS_WORKSPACE_ROWS} maxRows={config.CANVAS_WORKSPACE_ROWS}
style={{ background: canvasBackgroundColor }} style={{ background: canvasBackgroundColor }}
containerPadding={[consts.CANVAS_WORKSPACE_PADDING, consts.CANVAS_WORKSPACE_PADDING]} containerPadding={[config.CANVAS_WORKSPACE_PADDING, config.CANVAS_WORKSPACE_PADDING]}
margin={[consts.CANVAS_WORKSPACE_MARGIN, consts.CANVAS_WORKSPACE_MARGIN]} margin={[config.CANVAS_WORKSPACE_MARGIN, config.CANVAS_WORKSPACE_MARGIN]}
className='cvat-canvas-grid-root' className='cvat-canvas-grid-root'
rowHeight={rowHeight} rowHeight={rowHeight}
layout={layout} layout={layout}

@ -13,7 +13,7 @@ import ObjectItemElementComponent from 'components/annotation-page/standard-work
import ObjectItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/object-item'; import ObjectItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/object-item';
import { ShapeType, Workspace } from 'reducers'; import { ShapeType, Workspace } from 'reducers';
import { rotatePoint } from 'utils/math'; import { rotatePoint } from 'utils/math';
import consts from 'consts'; import config from 'config';
interface Props { interface Props {
readonly: boolean; readonly: boolean;
@ -141,9 +141,9 @@ export default function CanvasContextMenu(props: Props): JSX.Element | null {
if (param.key === ReviewContextMenuKeys.OPEN_ISSUE) { if (param.key === ReviewContextMenuKeys.OPEN_ISSUE) {
onStartIssue(points); onStartIssue(points);
} else if (param.key === ReviewContextMenuKeys.QUICK_ISSUE_POSITION) { } else if (param.key === ReviewContextMenuKeys.QUICK_ISSUE_POSITION) {
openIssue(points, consts.QUICK_ISSUE_INCORRECT_POSITION_TEXT); openIssue(points, config.QUICK_ISSUE_INCORRECT_POSITION_TEXT);
} else if (param.key === ReviewContextMenuKeys.QUICK_ISSUE_ATTRIBUTE) { } else if (param.key === ReviewContextMenuKeys.QUICK_ISSUE_ATTRIBUTE) {
openIssue(points, consts.QUICK_ISSUE_INCORRECT_ATTRIBUTE_TEXT); openIssue(points, config.QUICK_ISSUE_INCORRECT_ATTRIBUTE_TEXT);
} else if ( } else if (
param.keyPath.length === 2 && param.keyPath.length === 2 &&
param.keyPath[1] === ReviewContextMenuKeys.QUICK_ISSUE_FROM_LATEST param.keyPath[1] === ReviewContextMenuKeys.QUICK_ISSUE_FROM_LATEST

@ -18,7 +18,7 @@ import { LogType } from 'cvat-logger';
import { Canvas } from 'cvat-canvas-wrapper'; import { Canvas } from 'cvat-canvas-wrapper';
import { Canvas3d } from 'cvat-canvas3d-wrapper'; import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { getCore } from 'cvat-core-wrapper'; import { getCore } from 'cvat-core-wrapper';
import consts from 'consts'; import config from 'config';
import CVATTooltip from 'components/common/cvat-tooltip'; import CVATTooltip from 'components/common/cvat-tooltip';
import FrameTags from 'components/annotation-page/tag-annotation-workspace/frame-tags'; import FrameTags from 'components/annotation-page/tag-annotation-workspace/frame-tags';
import { import {
@ -369,7 +369,7 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
canvasInstance.configure({ canvasInstance.configure({
forceDisableEditing: workspace === Workspace.REVIEW_WORKSPACE, forceDisableEditing: workspace === Workspace.REVIEW_WORKSPACE,
undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE, undefinedAttrValue: config.UNDEFINED_ATTRIBUTE_VALUE,
displayAllText: showObjectsTextAlways, displayAllText: showObjectsTextAlways,
autoborders: automaticBordering, autoborders: automaticBordering,
showProjections, showProjections,
@ -442,7 +442,7 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
prevProps.outlined !== outlined prevProps.outlined !== outlined
) { ) {
canvasInstance.configure({ canvasInstance.configure({
undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE, undefinedAttrValue: config.UNDEFINED_ATTRIBUTE_VALUE,
displayAllText: showObjectsTextAlways, displayAllText: showObjectsTextAlways,
autoborders: automaticBordering, autoborders: automaticBordering,
showProjections, showProjections,

@ -11,7 +11,7 @@ import Input from 'antd/lib/input';
import InputNumber from 'antd/lib/input-number'; import InputNumber from 'antd/lib/input-number';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import consts from 'consts'; import config from 'config';
import { clamp } from 'utils/math'; import { clamp } from 'utils/math';
interface Props { interface Props {
@ -84,7 +84,7 @@ function ItemAttributeComponent(props: Props): JSX.Element {
{attrValues.map( {attrValues.map(
(value: string): JSX.Element => ( (value: string): JSX.Element => (
<Radio key={value} value={value}> <Radio key={value} value={value}>
{value === consts.UNDEFINED_ATTRIBUTE_VALUE ? consts.NO_BREAK_SPACE : value} {value === config.UNDEFINED_ATTRIBUTE_VALUE ? config.NO_BREAK_SPACE : value}
</Radio> </Radio>
), ),
)} )}
@ -113,7 +113,7 @@ function ItemAttributeComponent(props: Props): JSX.Element {
{attrValues.map( {attrValues.map(
(value: string): JSX.Element => ( (value: string): JSX.Element => (
<Select.Option key={value} value={value}> <Select.Option key={value} value={value}>
{value === consts.UNDEFINED_ATTRIBUTE_VALUE ? consts.NO_BREAK_SPACE : value} {value === config.UNDEFINED_ATTRIBUTE_VALUE ? config.NO_BREAK_SPACE : value}
</Select.Option> </Select.Option>
), ),
)} )}

@ -9,7 +9,7 @@ import { CombinedState, ObjectType } from 'reducers';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import Modal from 'antd/lib/modal'; import Modal from 'antd/lib/modal';
import consts from 'consts'; import config from 'config';
import { removeObjectAsync, removeObject as removeObjectAction } from 'actions/annotation-actions'; import { removeObjectAsync, removeObject as removeObjectAction } from 'actions/annotation-actions';
export default function RemoveConfirmComponent(): JSX.Element | null { export default function RemoveConfirmComponent(): JSX.Element | null {
@ -48,7 +48,7 @@ export default function RemoveConfirmComponent(): JSX.Element | null {
</Text> </Text>
<div className='cvat-remove-object-confirm-wrapper'> <div className='cvat-remove-object-confirm-wrapper'>
{/* eslint-disable-next-line */} {/* eslint-disable-next-line */}
<img src={consts.OUTSIDE_PIC_URL} /> <img src={config.OUTSIDE_PIC_URL} />
</div> </div>
</> </>
); );

@ -1,9 +1,11 @@
// Copyright (C) 2021-2022 Intel Corporation // Copyright (C) 2021-2022 Intel Corporation
// Copyright (C) 2023 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import config from 'config';
import Location from './location'; import Location from './location';
import consts from '../../consts';
interface Props { interface Props {
selectedRegion: any; selectedRegion: any;
@ -22,7 +24,7 @@ export default function GCSLocation(props: Props): JSX.Element {
selectedRegion={selectedRegion} selectedRegion={selectedRegion}
onSelectRegion={onSelectRegion} onSelectRegion={onSelectRegion}
internalCommonProps={internalCommonProps} internalCommonProps={internalCommonProps}
values={consts.DEFAULT_GOOGLE_CLOUD_STORAGE_LOCATIONS} values={config.DEFAULT_GOOGLE_CLOUD_STORAGE_LOCATIONS}
name='location' name='location'
label='Location' label='Location'
href='https://cloud.google.com/storage/docs/locations#available-locations' href='https://cloud.google.com/storage/docs/locations#available-locations'

@ -12,7 +12,7 @@ import Input from 'antd/lib/input';
import Row from 'antd/lib/row'; import Row from 'antd/lib/row';
import notification from 'antd/lib/notification'; import notification from 'antd/lib/notification';
import Tooltip from 'antd/lib/tooltip'; import Tooltip from 'antd/lib/tooltip';
import consts from 'consts'; import config from 'config';
interface Props { interface Props {
form: any; form: any;
@ -24,7 +24,7 @@ export default function ManifestsManager(props: Props): JSX.Element {
const { form, manifestNames, setManifestNames } = props; const { form, manifestNames, setManifestNames } = props;
const maxManifestsCount = useRef(5); const maxManifestsCount = useRef(5);
const [limitingAddingManifestNotification, setLimitingAddingManifestNotification] = useState(false); const [limitingAddingManifestNotification, setLimitingAddingManifestNotification] = useState(false);
const { DATASET_MANIFEST_GUIDE_URL } = consts; const { DATASET_MANIFEST_GUIDE_URL } = config;
const updateManifestFields = (): void => { const updateManifestFields = (): void => {
const newManifestFormItems = manifestNames.map((name, idx) => ({ const newManifestFormItems = manifestNames.map((name, idx) => ({

@ -1,9 +1,11 @@
// Copyright (C) 2021-2022 Intel Corporation // Copyright (C) 2021-2022 Intel Corporation
// Copyright (C) 2023 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import config from 'config';
import Location from './location'; import Location from './location';
import consts from '../../consts';
interface Props { interface Props {
selectedRegion: any; selectedRegion: any;
@ -22,7 +24,7 @@ export default function S3Region(props: Props): JSX.Element {
selectedRegion={selectedRegion} selectedRegion={selectedRegion}
onSelectRegion={onSelectRegion} onSelectRegion={onSelectRegion}
internalCommonProps={internalCommonProps} internalCommonProps={internalCommonProps}
values={consts.DEFAULT_AWS_S3_REGIONS} values={config.DEFAULT_AWS_S3_REGIONS}
name='region' name='region'
label='Region' label='Region'
href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions' href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions'

@ -5,7 +5,7 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import Autocomplete from 'antd/lib/auto-complete'; import Autocomplete from 'antd/lib/auto-complete';
import consts from 'consts'; import config from 'config';
import { getCore } from 'cvat-core-wrapper'; import { getCore } from 'cvat-core-wrapper';
const core = getCore(); const core = getCore();
@ -38,7 +38,7 @@ export default function ProjectSubsetField(props: Props): JSX.Element {
setInternalSubsets( setInternalSubsets(
new Set([ new Set([
...(internalValue ? [internalValue] : []), ...(internalValue ? [internalValue] : []),
...consts.DEFAULT_PROJECT_SUBSETS, ...config.DEFAULT_PROJECT_SUBSETS,
...project.subsets, ...project.subsets,
]), ]),
); );
@ -48,7 +48,7 @@ export default function ProjectSubsetField(props: Props): JSX.Element {
setInternalSubsets( setInternalSubsets(
new Set([ new Set([
...(internalValue ? [internalValue] : []), ...(internalValue ? [internalValue] : []),
...consts.DEFAULT_PROJECT_SUBSETS, ...config.DEFAULT_PROJECT_SUBSETS,
...(projectSubsets || []), ...(projectSubsets || []),
]), ]),
); );

@ -68,7 +68,7 @@ import showPlatformNotification, {
showUnsupportedNotification, showUnsupportedNotification,
} from 'utils/platform-checker'; } from 'utils/platform-checker';
import '../styles.scss'; import '../styles.scss';
import consts from 'consts'; import appConfig from 'config';
import EmailConfirmationPage from './email-confirmation-pages/email-confirmed'; import EmailConfirmationPage from './email-confirmation-pages/email-confirmed';
import EmailVerificationSentPage from './email-confirmation-pages/email-verification-sent'; import EmailVerificationSentPage from './email-confirmation-pages/email-verification-sent';
import IncorrectEmailConfirmationPage from './email-confirmation-pages/incorrect-email-confirmation'; import IncorrectEmailConfirmationPage from './email-confirmation-pages/incorrect-email-confirmation';
@ -140,10 +140,10 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
}); });
const { const {
HEALH_CHECK_RETRIES, HEALTH_CHECK_PERIOD, HEALTH_CHECK_REQUEST_TIMEOUT, UPGRADE_GUIDE_URL, HEALTH_CHECK_RETRIES, HEALTH_CHECK_PERIOD, HEALTH_CHECK_REQUEST_TIMEOUT, SERVER_UNAVAILABLE_COMPONENT,
} = consts; } = appConfig;
core.server.healthCheck( core.server.healthCheck(
HEALH_CHECK_RETRIES, HEALTH_CHECK_RETRIES,
HEALTH_CHECK_PERIOD, HEALTH_CHECK_PERIOD,
HEALTH_CHECK_REQUEST_TIMEOUT, HEALTH_CHECK_REQUEST_TIMEOUT,
).then(() => { ).then(() => {
@ -157,26 +157,15 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
healthIinitialized: true, healthIinitialized: true,
backendIsHealthy: false, backendIsHealthy: false,
}); });
Modal.error({ Modal.error({
title: 'Cannot connect to the server', title: 'Cannot connect to the server',
className: 'cvat-modal-cannot-connect-server', className: 'cvat-modal-cannot-connect-server',
closable: false, closable: false,
content: ( content:
<Text> <Text>
Make sure the CVAT backend and all necessary services {SERVER_UNAVAILABLE_COMPONENT}
(Database, Redis and Open Policy Agent) are running and avaliable. </Text>,
If you upgraded from version 2.2.0 or earlier, manual actions may be needed, see the&nbsp;
<a
target='_blank'
rel='noopener noreferrer'
href={UPGRADE_GUIDE_URL}
>
Upgrade Guide
</a>
.
</Text>
),
}); });
}); });

@ -30,10 +30,10 @@ import {
LinkedinIcon, LinkedinIcon,
} from 'react-share'; } from 'react-share';
import consts from 'consts'; import config from 'config';
function renderContent(): JSX.Element { function renderContent(): JSX.Element {
const { GITHUB_URL, GITHUB_IMAGE_URL, DISCORD_URL } = consts; const { GITHUB_URL, GITHUB_IMAGE_URL, DISCORD_URL } = config;
return ( return (
<> <>

@ -17,7 +17,7 @@ import { FormInstance } from 'antd/lib/form';
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
import { EventDataNode } from 'rc-tree/lib/interface'; import { EventDataNode } from 'rc-tree/lib/interface';
import consts from 'consts'; import config from 'config';
import { CloudStorage } from 'reducers'; import { CloudStorage } from 'reducers';
import CloudStorageTab from './cloud-storages-tab'; import CloudStorageTab from './cloud-storages-tab';
import LocalFiles from './local-files'; import LocalFiles from './local-files';
@ -155,7 +155,7 @@ export class FileManager extends React.PureComponent<Props, State> {
})); }));
} }
const { SHARE_MOUNT_GUIDE_URL } = consts; const { SHARE_MOUNT_GUIDE_URL } = config;
const { treeData, onUploadShareFiles, onLoadData } = this.props; const { treeData, onUploadShareFiles, onLoadData } = this.props;
const { expandedKeys, files } = this.state; const { expandedKeys, files } = this.state;

@ -18,7 +18,7 @@ import { resetAfterErrorAsync } from 'actions/boundaries-actions';
import { CombinedState } from 'reducers'; import { CombinedState } from 'reducers';
import logger, { LogType } from 'cvat-logger'; import logger, { LogType } from 'cvat-logger';
import CVATTooltip from 'components/common/cvat-tooltip'; import CVATTooltip from 'components/common/cvat-tooltip';
import consts from 'consts'; import config from 'config';
interface OwnProps { interface OwnProps {
children: JSX.Element; children: JSX.Element;
@ -166,7 +166,7 @@ class GlobalErrorBoundary extends React.PureComponent<Props, State> {
</li> </li>
<li> <li>
Notify an administrator or submit the issue directly on Notify an administrator or submit the issue directly on
<a href={consts.GITHUB_URL}> GitHub. </a> <a href={config.GITHUB_URL}> GitHub. </a>
Please, provide also: Please, provide also:
<ul> <ul>
<li>Steps to reproduce the issue</li> <li>Steps to reproduce the issue</li>

@ -31,7 +31,7 @@ import Text from 'antd/lib/typography/Text';
import Select from 'antd/lib/select'; import Select from 'antd/lib/select';
import { getCore } from 'cvat-core-wrapper'; import { getCore } from 'cvat-core-wrapper';
import consts from 'consts'; import config from 'config';
import { CVATLogo } from 'icons'; import { CVATLogo } from 'icons';
import ChangePasswordDialog from 'components/change-password-modal/change-password-modal'; import ChangePasswordDialog from 'components/change-password-modal/change-password-modal';
@ -164,7 +164,7 @@ function HeaderContainer(props: Props): JSX.Element {
const { const {
CHANGELOG_URL, LICENSE_URL, GITTER_URL, GITHUB_URL, GUIDE_URL, DISCORD_URL, CHANGELOG_URL, LICENSE_URL, GITTER_URL, GITHUB_URL, GUIDE_URL, DISCORD_URL,
} = consts; } = config;
const history = useHistory(); const history = useHistory();
const location = useLocation(); const location = useLocation();

@ -17,7 +17,7 @@ import { CompactPicker } from 'react-color';
import { clamp } from 'utils/math'; import { clamp } from 'utils/math';
import { BackJumpIcon, ForwardJumpIcon } from 'icons'; import { BackJumpIcon, ForwardJumpIcon } from 'icons';
import { FrameSpeed } from 'reducers'; import { FrameSpeed } from 'reducers';
import consts from 'consts'; import config from 'config';
interface Props { interface Props {
frameStep: number; frameStep: number;
@ -134,7 +134,7 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element {
<Popover <Popover
content={( content={(
<CompactPicker <CompactPicker
colors={consts.CANVAS_BACKGROUND_COLORS} colors={config.CANVAS_BACKGROUND_COLORS}
color={canvasBackgroundColor} color={canvasBackgroundColor}
onChange={(e) => onChangeCanvasBackgroundColor(e.hex)} onChange={(e) => onChangeCanvasBackgroundColor(e.hex)}
/> />

@ -7,7 +7,7 @@ import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import CVATTooltip from 'components/common/cvat-tooltip'; import CVATTooltip from 'components/common/cvat-tooltip';
import consts from 'consts'; import config from 'config';
import { LabelOptColor } from './common'; import { LabelOptColor } from './common';
interface ConstructorViewerItemProps { interface ConstructorViewerItemProps {
@ -22,7 +22,7 @@ export default function ConstructorViewerItem(props: ConstructorViewerItemProps)
color, label, onUpdate, onDelete, color, label, onUpdate, onDelete,
} = props; } = props;
const backgroundColor = color || consts.NEW_LABEL_COLOR; const backgroundColor = color || config.NEW_LABEL_COLOR;
let textColor = '#ffffff'; let textColor = '#ffffff';
try { try {
// convert color to grayscale and from the result get better text color // convert color to grayscale and from the result get better text color

@ -19,7 +19,7 @@ import CVATTooltip from 'components/common/cvat-tooltip';
import ColorPicker from 'components/annotation-page/standard-workspace/objects-side-bar/color-picker'; import ColorPicker from 'components/annotation-page/standard-workspace/objects-side-bar/color-picker';
import { ColorizeIcon } from 'icons'; import { ColorizeIcon } from 'icons';
import patterns from 'utils/validation-patterns'; import patterns from 'utils/validation-patterns';
import consts from 'consts'; import config from 'config';
import { import {
equalArrayHead, idGenerator, LabelOptColor, SkeletonConfiguration, equalArrayHead, idGenerator, LabelOptColor, SkeletonConfiguration,
} from './common'; } from './common';
@ -544,7 +544,7 @@ export default class LabelForm extends React.Component<Props> {
<Button type='default' className='cvat-change-task-label-color-button'> <Button type='default' className='cvat-change-task-label-color-button'>
<Badge <Badge
className='cvat-change-task-label-color-badge' className='cvat-change-task-label-color-badge'
color={this.formRef.current?.getFieldValue('color') || consts.NEW_LABEL_COLOR} color={this.formRef.current?.getFieldValue('color') || config.NEW_LABEL_COLOR}
text={<Icon component={ColorizeIcon} />} text={<Icon component={ColorizeIcon} />}
/> />
</Button> </Button>

@ -16,7 +16,7 @@ import GlobalHotKeys from 'utils/mousetrap-react';
import CVATTooltip from 'components/common/cvat-tooltip'; import CVATTooltip from 'components/common/cvat-tooltip';
import ShortcutsContext from 'components/shortcuts.context'; import ShortcutsContext from 'components/shortcuts.context';
import { ShapeType } from 'cvat-core-wrapper'; import { ShapeType } from 'cvat-core-wrapper';
import consts from 'consts'; import config from 'config';
import { import {
idGenerator, LabelOptColor, SkeletonConfiguration, toSVGCoord, idGenerator, LabelOptColor, SkeletonConfiguration, toSVGCoord,
} from './common'; } from './common';
@ -429,7 +429,7 @@ export default class SkeletonConfigurator extends React.PureComponent<Props, Sta
setAttributes(circle, { setAttributes(circle, {
r: 1.5, r: 1.5,
stroke: 'black', stroke: 'black',
fill: consts.NEW_LABEL_COLOR, fill: config.NEW_LABEL_COLOR,
cx: x, cx: x,
cy: y, cy: y,
'stroke-width': 0.1, 'stroke-width': 0.1,

@ -19,7 +19,7 @@ import { Model, ModelAttribute, StringObject } from 'reducers';
import CVATTooltip from 'components/common/cvat-tooltip'; import CVATTooltip from 'components/common/cvat-tooltip';
import { Label as LabelInterface } from 'components/labels-editor/common'; import { Label as LabelInterface } from 'components/labels-editor/common';
import { clamp } from 'utils/math'; import { clamp } from 'utils/math';
import consts from 'consts'; import config from 'config';
import { DimensionType } from '../../reducers'; import { DimensionType } from '../../reducers';
interface Props { interface Props {
@ -265,7 +265,7 @@ function DetectorRunner(props: Props): JSX.Element {
.find((_label: LabelInterface): boolean => ( .find((_label: LabelInterface): boolean => (
_label.name === mapping[modelLabel].name)) as LabelInterface; _label.name === mapping[modelLabel].name)) as LabelInterface;
const color = label ? label.color : consts.NEW_LABEL_COLOR; const color = label ? label.color : config.NEW_LABEL_COLOR;
const notMatchedModelAttributes = model.attributes[modelLabel] const notMatchedModelAttributes = model.attributes[modelLabel]
.filter((_attribute: ModelAttribute): boolean => ( .filter((_attribute: ModelAttribute): boolean => (
!(_attribute.name in (mapping[modelLabel].attributes || {})) !(_attribute.name in (mapping[modelLabel].attributes || {}))
@ -292,7 +292,7 @@ function DetectorRunner(props: Props): JSX.Element {
Object.keys(mapping[modelLabel].attributes || {}) Object.keys(mapping[modelLabel].attributes || {})
.map((mappedModelAttr: string) => ( .map((mappedModelAttr: string) => (
renderMappingRow( renderMappingRow(
consts.NEW_LABEL_COLOR, config.NEW_LABEL_COLOR,
mappedModelAttr, mappedModelAttr,
mapping[modelLabel].attributes[mappedModelAttr], mapping[modelLabel].attributes[mappedModelAttr],
'Remove the mapped attribute', 'Remove the mapped attribute',

@ -7,7 +7,7 @@ import Text from 'antd/lib/typography/Text';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import Empty from 'antd/lib/empty'; import Empty from 'antd/lib/empty';
import consts from 'consts'; import config from 'config';
export default function EmptyListComponent(): JSX.Element { export default function EmptyListComponent(): JSX.Element {
return ( return (
@ -28,7 +28,7 @@ export default function EmptyListComponent(): JSX.Element {
<Row justify='center' align='middle'> <Row justify='center' align='middle'>
<Col> <Col>
<Text type='secondary'>deploy a model with </Text> <Text type='secondary'>deploy a model with </Text>
<a href={`${consts.NUCLIO_GUIDE}`}>nuclio</a> <a href={`${config.NUCLIO_GUIDE}`}>nuclio</a>
</Col> </Col>
</Row> </Row>
</div> </div>

@ -1,8 +1,10 @@
// Copyright (C) 2019-2022 Intel Corporation // Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corp // Copyright (C) 2022-2023 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react';
const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__'; const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__';
const NO_BREAK_SPACE = '\u00a0'; const NO_BREAK_SPACE = '\u00a0';
const CHANGELOG_URL = 'https://github.com/opencv/cvat/blob/develop/CHANGELOG.md'; const CHANGELOG_URL = 'https://github.com/opencv/cvat/blob/develop/CHANGELOG.md';
@ -49,6 +51,23 @@ const DEFAULT_AWS_S3_REGIONS: string[][] = [
['sa-east-1', 'South America (São Paulo)'], ['sa-east-1', 'South America (São Paulo)'],
]; ];
const SERVER_UNAVAILABLE_COMPONENT = (
<>
Make sure the CVAT backend and all necessary services
(Database, Redis and Open Policy Agent) are running and avaliable.
If you upgraded from version 2.2.0 or earlier, manual actions may be needed,
see the&nbsp;
<a
target='_blank'
rel='noopener noreferrer'
href={UPGRADE_GUIDE_URL}
>
Upgrade Guide
</a>
.
</>
);
const DEFAULT_GOOGLE_CLOUD_STORAGE_LOCATIONS: string[][] = [ const DEFAULT_GOOGLE_CLOUD_STORAGE_LOCATIONS: string[][] = [
['NORTHAMERICA-NORTHEAST1', 'Montréal'], ['NORTHAMERICA-NORTHEAST1', 'Montréal'],
['NORTHAMERICA-NORTHEAST2', 'Toronto'], ['NORTHAMERICA-NORTHEAST2', 'Toronto'],
@ -88,7 +107,7 @@ const DEFAULT_GOOGLE_CLOUD_STORAGE_LOCATIONS: string[][] = [
['NAM4', 'US-CENTRAL1 and US-EAST1'], ['NAM4', 'US-CENTRAL1 and US-EAST1'],
]; ];
const HEALH_CHECK_RETRIES = 10; const HEALTH_CHECK_RETRIES = 10;
const HEALTH_CHECK_PERIOD = 3000; // ms const HEALTH_CHECK_PERIOD = 3000; // ms
const HEALTH_CHECK_REQUEST_TIMEOUT = 5000; // ms const HEALTH_CHECK_REQUEST_TIMEOUT = 5000; // ms
@ -115,9 +134,10 @@ export default {
DEFAULT_GOOGLE_CLOUD_STORAGE_LOCATIONS, DEFAULT_GOOGLE_CLOUD_STORAGE_LOCATIONS,
OUTSIDE_PIC_URL, OUTSIDE_PIC_URL,
DATASET_MANIFEST_GUIDE_URL, DATASET_MANIFEST_GUIDE_URL,
HEALH_CHECK_RETRIES, HEALTH_CHECK_RETRIES,
HEALTH_CHECK_PERIOD, HEALTH_CHECK_PERIOD,
HEALTH_CHECK_REQUEST_TIMEOUT, HEALTH_CHECK_REQUEST_TIMEOUT,
SERVER_UNAVAILABLE_COMPONENT,
CANVAS_WORKSPACE_ROWS, CANVAS_WORKSPACE_ROWS,
CANVAS_WORKSPACE_COLS, CANVAS_WORKSPACE_COLS,
CANVAS_WORKSPACE_MARGIN, CANVAS_WORKSPACE_MARGIN,

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import consts from 'consts'; import config from 'config';
import { AnnotationActionTypes } from 'actions/annotation-actions'; import { AnnotationActionTypes } from 'actions/annotation-actions';
import { ReviewActionTypes } from 'actions/review-actions'; import { ReviewActionTypes } from 'actions/review-actions';
import { AuthActionTypes } from 'actions/auth-actions'; import { AuthActionTypes } from 'actions/auth-actions';
@ -103,12 +103,12 @@ export default function (state: ReviewState = defaultState, action: any): Review
new Set( new Set(
[...state.latestComments, issue.comments[0].message].filter( [...state.latestComments, issue.comments[0].message].filter(
(message: string): boolean => ![ (message: string): boolean => ![
consts.QUICK_ISSUE_INCORRECT_POSITION_TEXT, config.QUICK_ISSUE_INCORRECT_POSITION_TEXT,
consts.QUICK_ISSUE_INCORRECT_ATTRIBUTE_TEXT, config.QUICK_ISSUE_INCORRECT_ATTRIBUTE_TEXT,
].includes(message), ].includes(message),
), ),
), ),
).slice(-consts.LATEST_COMMENTS_SHOWN_QUICK_ISSUE), ).slice(-config.LATEST_COMMENTS_SHOWN_QUICK_ISSUE),
frameIssues, frameIssues,
issues, issues,
newIssuePosition: null, newIssuePosition: null,

@ -1,4 +1,5 @@
// Copyright (C) 2020-2022 Intel Corporation // Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2023 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@ -8,153 +9,164 @@
const path = require('path'); const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const Dotenv = require('dotenv-webpack'); const Dotenv = require('dotenv-webpack');
const CopyPlugin = require('copy-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin');
module.exports = (env) => ({
target: 'web', module.exports = (env) => {
mode: 'production', console.log()
devtool: 'source-map', const defaultAppConfig = path.join(__dirname, 'src/config.tsx');
entry: { const appConfigFile = process.env.UI_APP_CONFIG ? process.env.UI_APP_CONFIG : defaultAppConfig;
'cvat-ui': './src/index.tsx', console.log('Application config file is: ', appConfigFile);
},
output: { return {
path: path.resolve(__dirname, 'dist'), target: 'web',
filename: 'assets/[name].[contenthash].min.js', mode: 'production',
publicPath: '/', devtool: 'source-map',
}, entry: {
devServer: { 'cvat-ui': './src/index.tsx',
compress: false,
host: process.env.CVAT_UI_HOST || 'localhost',
client: {
overlay: false,
}, },
port: 3000, output: {
historyApiFallback: true, path: path.resolve(__dirname, 'dist'),
static: { filename: 'assets/[name].[contenthash].min.js',
directory: path.join(__dirname, 'dist'), publicPath: '/',
}, },
proxy: [ devServer: {
{ compress: false,
context: (param) => host: process.env.CVAT_UI_HOST || 'localhost',
param.match( client: {
/\/api\/.*|git\/.*|opencv\/.*|analytics\/.*|static\/.*|admin(?:\/(.*))?.*|documentation\/.*|django-rq(?:\/(.*))?/gm, overlay: false,
),
target: env && env.API_URL,
secure: false,
changeOrigin: true,
}, },
], port: 3000,
}, historyApiFallback: true,
resolve: { static: {
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'], directory: path.join(__dirname, 'dist'),
plugins: [new TsconfigPathsPlugin({ configFile: './tsconfig.json' })], },
fallback: { proxy: [
fs: false, {
}, context: (param) =>
}, param.match(
module: { /\/api\/.*|git\/.*|opencv\/.*|analytics\/.*|static\/.*|admin(?:\/(.*))?.*|documentation\/.*|django-rq(?:\/(.*))?/gm,
rules: [ ),
{ target: env && env.API_URL,
test: /\.(ts|tsx)$/, secure: false,
use: { changeOrigin: true,
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-optional-chaining',
[
'import',
{
libraryName: 'antd',
},
],
],
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/typescript'],
sourceType: 'unambiguous',
},
}, },
],
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
fallback: {
fs: false,
}, },
{ alias: {
test: /\.(css|scss)$/, config$: appConfigFile,
use: [ },
'style-loader', modules: [path.resolve(__dirname, 'src'), 'node_modules'],
{ },
loader: 'css-loader', module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: {
loader: 'babel-loader',
options: { options: {
importLoaders: 2, plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-optional-chaining',
[
'import',
{
libraryName: 'antd',
},
],
],
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/typescript'],
sourceType: 'unambiguous',
}, },
}, },
{ },
loader: 'postcss-loader', {
test: /\.(css|scss)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
{
loader: 'postcss-loader',
options: {
plugins: [require('postcss-preset-env')],
},
},
'sass-loader',
],
},
{
test: /\.svg$/,
exclude: /node_modules/,
use: [
'babel-loader',
{
loader: 'react-svg-loader',
options: {
svgo: {
plugins: [{ pretty: true }, { cleanupIDs: false }],
},
},
},
],
},
{
test: /3rdparty\/.*\.worker\.js$/,
use: {
loader: 'worker-loader',
options: { options: {
plugins: [require('postcss-preset-env')], publicPath: '/',
filename: 'assets/3rdparty/[name].[contenthash].js',
esModule: false,
}, },
}, },
'sass-loader', },
], {
}, test: /\.worker\.js$/,
{ exclude: /3rdparty/,
test: /\.svg$/, use: {
exclude: /node_modules/, loader: 'worker-loader',
use: [
'babel-loader',
{
loader: 'react-svg-loader',
options: { options: {
svgo: { publicPath: '/',
plugins: [{ pretty: true }, { cleanupIDs: false }], filename: 'assets/[name].[contenthash].js',
}, esModule: false,
}, },
}, },
],
},
{
test: /3rdparty\/.*\.worker\.js$/,
use: {
loader: 'worker-loader',
options: {
publicPath: '/',
filename: 'assets/3rdparty/[name].[contenthash].js',
esModule: false,
},
}, },
}, ],
{ parser: {
test: /\.worker\.js$/, javascript: {
exclude: /3rdparty/, exportsPresence: 'error',
use: {
loader: 'worker-loader',
options: {
publicPath: '/',
filename: 'assets/[name].[contenthash].js',
esModule: false,
},
}, },
}, },
],
parser: {
javascript: {
exportsPresence: 'error',
},
}, },
}, plugins: [
plugins: [ new HtmlWebpackPlugin({
new HtmlWebpackPlugin({ template: './src/index.html',
template: './src/index.html', inject: 'body',
inject: 'body', }),
}), new Dotenv({
new Dotenv({ systemvars: true,
systemvars: true, }),
}), new CopyPlugin({
new CopyPlugin({ patterns: [
patterns: [ {
{ from: '../cvat-data/src/ts/3rdparty/avc.wasm',
from: '../cvat-data/src/ts/3rdparty/avc.wasm', to: 'assets/3rdparty/',
to: 'assets/3rdparty/', },
}, ],
], }),
}), ],
], }
}); };

@ -11,7 +11,7 @@ from tempfile import NamedTemporaryFile
import cv2 import cv2
import pytz import pytz
from django.core.cache import cache from django.core.cache import caches
from django.conf import settings from django.conf import settings
from rest_framework.exceptions import ValidationError, NotFound from rest_framework.exceptions import ValidationError, NotFound
@ -31,14 +31,14 @@ from utils.dataset_manifest import ImageManifestManager
class MediaCache: class MediaCache:
def __init__(self, dimension=DimensionType.DIM_2D): def __init__(self, dimension=DimensionType.DIM_2D):
self._dimension = dimension self._dimension = dimension
self._cache = caches['media']
@staticmethod def _get_or_set_cache_item(self, key, create_function):
def _get_or_set_cache_item(key, create_function): item = self._cache.get(key)
item = cache.get(key)
if not item: if not item:
item = create_function() item = create_function()
if item[0]: if item[0]:
cache.set(key, item) self._cache.set(key, item)
return item return item

@ -3,6 +3,7 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
from django.apps import AppConfig from django.apps import AppConfig
from django.conf import settings
from health_check.plugins import plugin_dir from health_check.plugins import plugin_dir
@ -10,5 +11,8 @@ class HealthConfig(AppConfig):
name = 'cvat.apps.health' name = 'cvat.apps.health'
def ready(self): def ready(self):
from .backends import OPAHealthCheck from .backends import OPAHealthCheck, CustomCacheBackend
plugin_dir.register(OPAHealthCheck) plugin_dir.register(OPAHealthCheck)
for backend in settings.CACHES:
plugin_dir.register(CustomCacheBackend, backend=backend)

@ -3,11 +3,14 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
import requests import requests
import sqlite3
from health_check.backends import BaseHealthCheckBackend from health_check.backends import BaseHealthCheckBackend
from health_check.exceptions import HealthCheckException from health_check.exceptions import HealthCheckException
from health_check.exceptions import ServiceReturnedUnexpectedResult, ServiceUnavailable
from django.conf import settings from django.conf import settings
from django.core.cache import CacheKeyWarning, caches
class OPAHealthCheck(BaseHealthCheckBackend): class OPAHealthCheck(BaseHealthCheckBackend):
critical_service = True critical_service = True
@ -22,3 +25,27 @@ class OPAHealthCheck(BaseHealthCheckBackend):
def identifier(self): def identifier(self):
return self.__class__.__name__ return self.__class__.__name__
class CustomCacheBackend(BaseHealthCheckBackend):
def __init__(self, backend="default"):
super().__init__()
self.backend = backend
def identifier(self):
return f"Cache backend: {self.backend}"
def check_status(self):
try:
cache = caches[self.backend]
cache.set("djangohealtcheck_test", "itworks")
if not cache.get("djangohealtcheck_test") == "itworks":
raise ServiceUnavailable("Cache key does not match")
except CacheKeyWarning as e:
self.add_error(ServiceReturnedUnexpectedResult("Cache key warning"), e)
except ValueError as e:
self.add_error(ServiceReturnedUnexpectedResult("ValueError"), e)
except ConnectionError as e:
self.add_error(ServiceReturnedUnexpectedResult("Connection Error"), e)
except sqlite3.DatabaseError as e:
raise ServiceUnavailable("Cache error: {}".format(str(e)))

@ -127,7 +127,6 @@ INSTALLED_APPS = [
'dj_rest_auth.registration', 'dj_rest_auth.registration',
'health_check', 'health_check',
'health_check.db', 'health_check.db',
'health_check.cache',
'health_check.contrib.migrations', 'health_check.contrib.migrations',
'health_check.contrib.psutil', 'health_check.contrib.psutil',
'cvat.apps.iam', 'cvat.apps.iam',
@ -512,7 +511,10 @@ RESTRICTIONS = {
# http://www.grantjenks.com/docs/diskcache/tutorial.html#djangocache # http://www.grantjenks.com/docs/diskcache/tutorial.html#djangocache
CACHES = { CACHES = {
'default' : { 'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
},
'media' : {
'BACKEND' : 'diskcache.DjangoCache', 'BACKEND' : 'diskcache.DjangoCache',
'LOCATION' : CACHE_ROOT, 'LOCATION' : CACHE_ROOT,
'TIMEOUT' : None, 'TIMEOUT' : None,

@ -79,7 +79,6 @@
"style-loader": "^1.0.0", "style-loader": "^1.0.0",
"stylelint": "^13.6.1", "stylelint": "^13.6.1",
"stylelint-config-standard": "^20.0.0", "stylelint-config-standard": "^20.0.0",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "^3.7.3", "typescript": "^3.7.3",
"vfile-reporter-json": "^2.0.2", "vfile-reporter-json": "^2.0.2",
"webpack": "^5.72.1", "webpack": "^5.72.1",

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save