Apply node maintenance mode to servers (#4421)

This commit is contained in:
Boy132 2022-11-07 00:02:30 +01:00 committed by GitHub
parent 4032481a4f
commit 032e4f2e31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 29 additions and 5 deletions

View file

@ -17,6 +17,8 @@ class ServerStateConflictException extends ConflictHttpException
$message = 'This server is currently in an unsupported state, please try again later.'; $message = 'This server is currently in an unsupported state, please try again later.';
if ($server->isSuspended()) { if ($server->isSuspended()) {
$message = 'This server is currently suspended and the functionality requested is unavailable.'; $message = 'This server is currently suspended and the functionality requested is unavailable.';
} elseif ($server->node->isUnderMaintenance()) {
$message = 'The node of this server is currently under maintenance and the functionality requested is unavailable.';
} elseif (!$server->isInstalled()) { } elseif (!$server->isInstalled()) {
$message = 'This server has not yet completed its installation process, please try again later.'; $message = 'This server has not yet completed its installation process, please try again later.';
} elseif ($server->status === Server::STATUS_RESTORING_BACKUP) { } elseif ($server->status === Server::STATUS_RESTORING_BACKUP) {

View file

@ -53,7 +53,7 @@ class AuthenticateServerAccess
// Still allow users to get information about their server if it is installing or // Still allow users to get information about their server if it is installing or
// being transferred. // being transferred.
if (!$request->routeIs('api:client:server.view')) { if (!$request->routeIs('api:client:server.view')) {
if ($server->isSuspended() && !$request->routeIs('api:client:server.resources')) { if (($server->isSuspended() || $server->node->isUnderMaintenance()) && !$request->routeIs('api:client:server.resources')) {
throw $exception; throw $exception;
} }
if (!$user->root_admin || !$request->routeIs($this->except)) { if (!$user->root_admin || !$request->routeIs($this->except)) {

View file

@ -24,6 +24,7 @@ class StoreNodeRequest extends ApplicationApiRequest
'fqdn', 'fqdn',
'scheme', 'scheme',
'behind_proxy', 'behind_proxy',
'maintenance_mode',
'memory', 'memory',
'memory_overallocate', 'memory_overallocate',
'disk', 'disk',

View file

@ -186,6 +186,11 @@ class Node extends Model
); );
} }
public function isUnderMaintenance(): bool
{
return $this->maintenance_mode;
}
public function mounts(): HasManyThrough public function mounts(): HasManyThrough
{ {
return $this->hasManyThrough(Mount::class, MountNode::class, 'node_id', 'id', 'id', 'mount_id'); return $this->hasManyThrough(Mount::class, MountNode::class, 'node_id', 'id', 'id', 'mount_id');

View file

@ -354,6 +354,7 @@ class Server extends Model
{ {
if ( if (
$this->isSuspended() || $this->isSuspended() ||
$this->node->isUnderMaintenance() ||
!$this->isInstalled() || !$this->isInstalled() ||
$this->status === self::STATUS_RESTORING_BACKUP || $this->status === self::STATUS_RESTORING_BACKUP ||
!is_null($this->transfer) !is_null($this->transfer)

View file

@ -43,6 +43,7 @@ class ServerTransformer extends BaseClientTransformer
'uuid' => $server->uuid, 'uuid' => $server->uuid,
'name' => $server->name, 'name' => $server->name,
'node' => $server->node->name, 'node' => $server->node->name,
'is_node_under_maintenance' => $server->node->isUnderMaintenance(),
'sftp_details' => [ 'sftp_details' => [
'ip' => $server->node->fqdn, 'ip' => $server->node->fqdn,
'port' => $server->node->daemonSFTP, 'port' => $server->node->daemonSFTP,

View file

@ -17,6 +17,7 @@ export interface Server {
uuid: string; uuid: string;
name: string; name: string;
node: string; node: string;
isNodeUnderMaintenance: boolean;
status: ServerStatus; status: ServerStatus;
sftpDetails: { sftpDetails: {
ip: string; ip: string;
@ -50,6 +51,7 @@ export const rawDataToServerObject = ({ attributes: data }: FractalResponseData)
uuid: data.uuid, uuid: data.uuid,
name: data.name, name: data.name,
node: data.node, node: data.node,
isNodeUnderMaintenance: data.is_node_under_maintenance,
status: data.status, status: data.status,
invocation: data.invocation, invocation: data.invocation,
dockerImage: data.docker_image, dockerImage: data.docker_image,

View file

@ -8,6 +8,9 @@ import ServerRestoreSvg from '@/assets/images/server_restore.svg';
export default () => { export default () => {
const status = ServerContext.useStoreState((state) => state.server.data?.status || null); const status = ServerContext.useStoreState((state) => state.server.data?.status || null);
const isTransferring = ServerContext.useStoreState((state) => state.server.data?.isTransferring || false); const isTransferring = ServerContext.useStoreState((state) => state.server.data?.isTransferring || false);
const isNodeUnderMaintenance = ServerContext.useStoreState(
(state) => state.server.data?.isNodeUnderMaintenance || false
);
return status === 'installing' || status === 'install_failed' ? ( return status === 'installing' || status === 'install_failed' ? (
<ScreenBlock <ScreenBlock
@ -21,6 +24,12 @@ export default () => {
image={ServerErrorSvg} image={ServerErrorSvg}
message={'This server is suspended and cannot be accessed.'} message={'This server is suspended and cannot be accessed.'}
/> />
) : isNodeUnderMaintenance ? (
<ScreenBlock
title={'Node under Maintenance'}
image={ServerErrorSvg}
message={'The node of this server is currently under maintenance.'}
/>
) : ( ) : (
<ScreenBlock <ScreenBlock
title={isTransferring ? 'Transferring' : 'Restoring from Backup'} title={isTransferring ? 'Transferring' : 'Restoring from Backup'}

View file

@ -19,12 +19,15 @@ const ServerConsoleContainer = () => {
const isInstalling = ServerContext.useStoreState((state) => state.server.isInstalling); const isInstalling = ServerContext.useStoreState((state) => state.server.isInstalling);
const isTransferring = ServerContext.useStoreState((state) => state.server.data!.isTransferring); const isTransferring = ServerContext.useStoreState((state) => state.server.data!.isTransferring);
const eggFeatures = ServerContext.useStoreState((state) => state.server.data!.eggFeatures, isEqual); const eggFeatures = ServerContext.useStoreState((state) => state.server.data!.eggFeatures, isEqual);
const isNodeUnderMaintenance = ServerContext.useStoreState((state) => state.server.data!.isNodeUnderMaintenance);
return ( return (
<ServerContentBlock title={'Console'}> <ServerContentBlock title={'Console'}>
{(isInstalling || isTransferring) && ( {(isNodeUnderMaintenance || isInstalling || isTransferring) && (
<Alert type={'warning'} className={'mb-4'}> <Alert type={'warning'} className={'mb-4'}>
{isInstalling {isNodeUnderMaintenance
? 'The node of this server is currently under maintenance and all actions are unavailable.'
: isInstalling
? 'This server is currently running its installation process and most actions are unavailable.' ? 'This server is currently running its installation process and most actions are unavailable.'
: 'This server is currently being transferred to another node and all actions are unavailable.'} : 'This server is currently being transferred to another node and all actions are unavailable.'}
</Alert> </Alert>

View file

@ -30,7 +30,7 @@ const server: ServerDataStore = {
return false; return false;
} }
return state.data.status !== null || state.data.isTransferring; return state.data.status !== null || state.data.isTransferring || state.data.isNodeUnderMaintenance;
}), }),
isInstalling: computed((state) => { isInstalling: computed((state) => {

View file

@ -93,7 +93,7 @@
<div class="row"> <div class="row">
@if($node->maintenance_mode) @if($node->maintenance_mode)
<div class="col-sm-12"> <div class="col-sm-12">
<div class="info-box bg-grey"> <div class="info-box bg-orange">
<span class="info-box-icon"><i class="ion ion-wrench"></i></span> <span class="info-box-icon"><i class="ion ion-wrench"></i></span>
<div class="info-box-content" style="padding: 23px 10px 0;"> <div class="info-box-content" style="padding: 23px 10px 0;">
<span class="info-box-text">This node is under</span> <span class="info-box-text">This node is under</span>