Skip to content

Commit

Permalink
fix(System): timestamp providers maintenance [YTFRONT-4452]
Browse files Browse the repository at this point in the history
  • Loading branch information
SimbiozizV committed Dec 6, 2024
1 parent ef28721 commit fa837cd
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 132 deletions.
77 changes: 44 additions & 33 deletions packages/ui/src/ui/pages/system/Masters/ChangeMaintenanceButton.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,54 @@
import React, {FC, useCallback, useState} from 'react';
import React, {FC, useState} from 'react';
import {Button, Icon} from '@gravity-ui/uikit';
import {changeMasterMaintenance} from '../../../store/actions/system/masters';
import {changeSchedulerMaintenance} from '../../../store/actions/system/schedulers';
import {useDispatch} from 'react-redux';
import PencilIcon from '@gravity-ui/icons/svgs/pencil.svg';
import {FormApi, YTDFDialog, makeErrorFields} from '../../../components/Dialog';
import hammer from '../../../common/hammer';
import {useToggle} from 'react-use';

type Props = {
className?: string;
address: string;
path: string | null;
title?: string;
host?: string;
container?: string;
maintenance: boolean;
maintenanceMessage: string;
type: 'master' | 'scheduler' | 'agent';
onMaintenanceChange: (data: {
path: string;
message: string;
maintenance: boolean;
}) => Promise<void>;
};

type FormData = {maintenance: string; message: string};
type FormData = {host: string; container: string; maintenance: string; message: string};

export const ChangeMaintenanceButton: FC<Props> = ({
className,
path,
title,
host,
container,
maintenance,
maintenanceMessage,
type,
address,
onMaintenanceChange,
}) => {
const [error, setError] = useState<Error | undefined>(undefined);
const [visible, toggleVisible] = useToggle(false);
const dispatch = useDispatch();

const handleMaintenanceChange = useCallback(
async (form: FormApi<FormData>) => {
const {values} = form.getState();
const action = type === 'master' ? changeMasterMaintenance : changeSchedulerMaintenance;
try {
await dispatch(
action({
address,
message: values.message,
maintenance: values.maintenance === 'enabled',
type,
}),
);
setError(undefined);
} catch (e) {
setError(e as Error);
throw e as Error;
}
},
[address, dispatch, type],
);
if (!path) return;

const handleMaintenanceChange = async (form: FormApi<FormData>) => {
const {values} = form.getState();
try {
await onMaintenanceChange({
path,
message: values.message,
maintenance: values.maintenance === 'enabled',
});
} catch (e) {
setError(e as Error);
throw e as Error;
}
};

const handleOnClose = () => {
toggleVisible();
Expand All @@ -61,13 +60,25 @@ export const ChangeMaintenanceButton: FC<Props> = ({
<YTDFDialog<FormData>
pristineSubmittable
headerProps={{
title: `Edit ${hammer.format['Address'](address)}`,
title,
}}
initialValues={{
host,
container,
maintenance: maintenance ? 'enabled' : 'disabled',
message: maintenanceMessage,
}}
fields={[
{
name: 'host',
type: 'plain',
caption: 'Host',
},
{
name: 'container',
type: 'plain',
caption: 'Container',
},
{
name: 'maintenance',
type: 'radio',
Expand Down
31 changes: 24 additions & 7 deletions packages/ui/src/ui/pages/system/Masters/Instance.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, {FC} from 'react';
import React, {FC, useCallback} from 'react';
import hammer from '../../../common/hammer';
import NodeQuad, {NodeQuadTheme} from '../NodeQuad/NodeQuad';
import {Tooltip} from '../../../components/Tooltip/Tooltip';
import Icon from '../../../components/Icon/Icon';
import {useDispatch} from 'react-redux';
import ReadOnlyIcon from '../../../assets/img/svg/read-only-icon.svg';
import WarmUpIcon from '../../../assets/img/svg/warmup-icon.svg';
import {makeShortSystemAddress} from '../helpers/makeShortSystemAddress';
Expand All @@ -12,6 +13,8 @@ import {ChangeMaintenanceButton} from './ChangeMaintenanceButton';
import block from 'bem-cn-lite';
import {MasterInstance} from '../../../store/selectors/system/masters';
import './MasterGroup.scss';
import {changeMasterMaintenance} from '../../../store/actions/system/masters';
import {CypressNode} from '../../../../shared/yt-types';

const b = block('master-group');

Expand Down Expand Up @@ -39,8 +42,12 @@ type Props = {
};

export const Instance: FC<Props> = ({instance, hostType, allowVoting, allowService}) => {
const dispatch = useDispatch();
const {state, $address, $physicalAddress} = instance;
const attributes = instance.$attributes as Record<string, any>;
const attributes = instance.$attributes as CypressNode<
{read_only: boolean; voting: boolean; warming_up: boolean},
unknown
>['$attributes'];
const address = hostType === 'host' ? $address : $physicalAddress;
const maintenance = instance.getMaintenance();
const maintenanceMessage = maintenance ? instance.getMaintenanceMessage() || 'Maintenance' : '';
Expand All @@ -50,6 +57,15 @@ export const Instance: FC<Props> = ({instance, hostType, allowVoting, allowServi
const theme = denyVoting && state === 'following' ? 'nonvoting' : instanceStateToTheme[state!];
const addressWithoutPort = hammer.format['Address'](address);

const handleOnMaintenanceChange = useCallback(
async (data: {path: string; maintenance: boolean; message: string}) => {
await dispatch(changeMasterMaintenance(data));
},
[dispatch],
);

const shortName = makeShortSystemAddress(addressWithoutPort) || addressWithoutPort;

return (
<>
<div className={b('availability')}>
Expand Down Expand Up @@ -77,9 +93,7 @@ export const Instance: FC<Props> = ({instance, hostType, allowVoting, allowServi
</div>
<div className={b('host')}>
<Tooltip content={addressWithoutPort}>
<div className={b('host-name')}>
{makeShortSystemAddress(addressWithoutPort) || addressWithoutPort}
</div>
<div className={b('host-name')}>{shortName}</div>
</Tooltip>
<Flex gap={1}>
<span className={b('host-btn')}>
Expand All @@ -88,10 +102,13 @@ export const Instance: FC<Props> = ({instance, hostType, allowVoting, allowServi
{allowService && (
<ChangeMaintenanceButton
className={b('host-btn')}
address={address}
path={instance.getPath()}
title={`Edit ${addressWithoutPort}`}
host={$physicalAddress}
container={$address}
maintenance={maintenance}
maintenanceMessage={maintenanceMessage}
type="master"
onMaintenanceChange={handleOnMaintenanceChange}
/>
)}
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useCallback} from 'react';

import hammer from '../../../../common/hammer';
import block from 'bem-cn-lite';
Expand All @@ -11,16 +11,29 @@ import NodeQuad from '../../NodeQuad/NodeQuad';
import '../Schedulers.scss';
import {ChangeMaintenanceButton} from '../../Masters/ChangeMaintenanceButton';
import {makeShortSystemAddress} from '../../helpers/makeShortSystemAddress';
import {useDispatch} from 'react-redux';
import {changeSchedulerMaintenance} from '../../../../store/actions/system/schedulers';

const b = block('system');

export type SchedulerProps = {
host?: string;
host: string;
address: string;
physicalHost: string;
state: 'active' | 'connected' | 'standby' | 'disconnected' | 'offline';
maintenanceMessage?: React.ReactNode;
type: 'scheduler' | 'agent';
type: 'schedulers' | 'agents';
};
export default function Scheduler({host, state, maintenanceMessage, type}: SchedulerProps) {

export default function Scheduler({
host,
physicalHost,
address,
state,
maintenanceMessage,
type,
}: SchedulerProps) {
const dispatch = useDispatch();
const theme = (
{
active: 'online',
Expand All @@ -31,7 +44,15 @@ export default function Scheduler({host, state, maintenanceMessage, type}: Sched
} as const
)[state];

const address = hammer.format['Address'](host);
const formatedAddress = hammer.format['Address'](address);
const path = `//sys/${type === 'schedulers' ? 'scheduler' : 'controller_agents'}/instances/${host}`;

const handleOnMaintenanceChange = useCallback(
async (data: {path: string; message: string; maintenance: boolean}) => {
await dispatch(changeSchedulerMaintenance(data));
},
[dispatch],
);

return (
<div className={b('scheduler')}>
Expand All @@ -44,21 +65,24 @@ export default function Scheduler({host, state, maintenanceMessage, type}: Sched
</Tooltip>
)}
</div>
<div title={address} className={b('scheduler-host')}>
<Tooltip content={address}>
<div title={formatedAddress} className={b('scheduler-host')}>
<Tooltip content={formatedAddress}>
<div className={b('scheduler-host-address')}>
{makeShortSystemAddress(address) || address}
{makeShortSystemAddress(formatedAddress) || formatedAddress}
</div>
</Tooltip>
<div className={b('scheduler-host-btn')}>
{host && <ClipboardButton view="flat-secondary" text={host} />}
</div>
<ChangeMaintenanceButton
className={b('scheduler-host-btn')}
address={host!}
path={path}
title={`Edit ${address}`}
host={physicalHost}
container={host}
maintenance={Boolean(maintenanceMessage)}
maintenanceMessage={maintenanceMessage as string}
type={type}
onMaintenanceChange={handleOnMaintenanceChange}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,22 @@ class SchedulersAndAgents extends Component {
};

renderHosts(name, connectedHosts) {
return map_(connectedHosts, ({host, state, maintenanceMessage}, index) => {
return (
<Scheduler
key={host ?? index}
host={host}
state={state}
maintenanceMessage={maintenanceMessage}
type={name}
/>
);
});
return map_(
connectedHosts,
({host, physicalHost, address, state, maintenanceMessage}, index) => {
return (
<Scheduler
key={host ?? index}
host={host}
physicalHost={physicalHost}
address={address}
state={state}
maintenanceMessage={maintenanceMessage}
type={name}
/>
);
},
);
}

renderSection(name, heading) {
Expand Down
39 changes: 10 additions & 29 deletions packages/ui/src/ui/store/actions/system/masters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ import {
MasterAlert,
MasterDataItemInfo,
MastersConfigResponse,
MastersGroup,
MastersStateAction,
ResponseItemsGroup,
} from '../../reducers/system/masters';
import {ThunkAction} from 'redux-thunk';
import type {RootState} from '../../reducers';
import {MasterInstance} from '../../selectors/system/masters';
import {getMastersHostType} from '../../selectors/settings';
import {VisibleHostType} from '../../../constants/system/masters';
import {ValueOf} from '../../../../@types/types';

export const FETCH_MASTER_CONFIG = createActionTypes('MASTER_CONFIG');
Expand Down Expand Up @@ -324,7 +321,7 @@ function loadHydra(
requests: BatchSubRequest[],
masterInfo: MasterDataItemInfo[],
type: 'primary' | 'providers' | 'secondary',
masterEntry: ResponseItemsGroup,
masterEntry: MastersGroup,
) {
const {addresses, cellTag} = masterEntry;
const hydraPath = '/orchid/monitoring/hydra';
Expand Down Expand Up @@ -432,11 +429,12 @@ export function loadMasters() {
};
}

const getPathByMasterType = (type: string): string | null => {
export const getPathByMasterType = (type: string, cellTag?: string): string | null => {
switch (type) {
case 'primary':
return '//sys/primary_masters';
case 'secondary':
return '//sys/cluster_masters';
return `//sys/secondary_masters/${cellTag}`;
case 'providers':
return '//sys/timestamp_providers';
case 'queue_agent':
Expand Down Expand Up @@ -480,37 +478,20 @@ export const changeMaintenance = ({

export const changeMasterMaintenance =
({
address,
path,
maintenance,
message,
}: {
address: string;
path: string;
maintenance: boolean;
message: string;
}): ThunkAction<any, RootState, unknown, MastersStateAction> =>
async (dispatch, getSate) => {
const state = getSate();
const hostType = getMastersHostType(state);
const {primary, secondary, queueAgents, providers} = state.system.masters;
const instances: MasterInstance[] = [
...primary.instances,
...secondary.reduce<MasterInstance[]>((acc, item) => [...acc, ...item.instances], []),
...queueAgents.instances,
...providers.instances,
];

const master = instances.find((i) => {
const {host, physicalHost} = i.toObject();
const addressByType = hostType === VisibleHostType.host ? host : physicalHost;
return addressByType === address;
});
if (!master) throw new Error('Cant find master by address');

const path = getPathByMasterType(master.getType());
async (dispatch, getState) => {
const state = getState();
if (!path) throw new Error('Cant take path by master type');

const result = await changeMaintenance({
path: `${path}/${master.toObject().host}`,
path,
login: state.global.login,
maintenance,
message,
Expand Down
Loading

0 comments on commit fa837cd

Please sign in to comment.