fix(app): properly update the app state when losing connectivity to a remote environment while browsing it (#11943)

pull/11972/head
LP B 2024-06-19 13:45:02 +02:00 committed by GitHub
parent e7af3296fc
commit 9339d10233
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 55 additions and 25 deletions

View File

@ -16,7 +16,7 @@ angular.module('portainer.docker', ['portainer.app', reactModule]).config([
parent: 'endpoint', parent: 'endpoint',
url: '/docker', url: '/docker',
abstract: true, abstract: true,
onEnter: /* @ngInject */ function onEnter(endpoint, $async, $state, EndpointService, Notifications, StateManager, SystemService) { onEnter: /* @ngInject */ function onEnter(endpoint, $async, $state, EndpointService, Notifications, StateManager, SystemService, EndpointProvider) {
return $async(async () => { return $async(async () => {
const dockerTypes = [PortainerEndpointTypes.DockerEnvironment, PortainerEndpointTypes.AgentOnDockerEnvironment, PortainerEndpointTypes.EdgeAgentOnDockerEnvironment]; const dockerTypes = [PortainerEndpointTypes.DockerEnvironment, PortainerEndpointTypes.AgentOnDockerEnvironment, PortainerEndpointTypes.EdgeAgentOnDockerEnvironment];
@ -44,9 +44,11 @@ angular.module('portainer.docker', ['portainer.app', reactModule]).config([
if (endpoint.Type == PortainerEndpointTypes.EdgeAgentOnDockerEnvironment) { if (endpoint.Type == PortainerEndpointTypes.EdgeAgentOnDockerEnvironment) {
params = { redirect: true, environmentId: endpoint.Id, environmentName: endpoint.Name, route: 'docker.dashboard' }; params = { redirect: true, environmentId: endpoint.Id, environmentName: endpoint.Name, route: 'docker.dashboard' };
} else { } else {
EndpointProvider.clean();
Notifications.error('Failed loading environment', e); Notifications.error('Failed loading environment', e);
} }
$state.go('portainer.home', params, { reload: true, inherit: false }); $state.go('portainer.home', params, { reload: true, inherit: false });
return false;
} }
async function checkEndpointStatus(endpoint) { async function checkEndpointStatus(endpoint) {

View File

@ -63,12 +63,13 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
$state, $state,
endpoint, endpoint,
KubernetesHealthService, KubernetesHealthService,
KubernetesNamespaceService,
Notifications, Notifications,
StateManager, StateManager,
$http, $http,
Authentication, Authentication,
UserService UserService,
EndpointService,
EndpointProvider
) { ) {
return $async(async () => { return $async(async () => {
// if the user wants to use front end cache for performance, set the angular caching settings // if the user wants to use front end cache for performance, set the angular caching settings
@ -93,39 +94,57 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
$state.go('portainer.home'); $state.go('portainer.home');
return; return;
} }
try { try {
if (endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) { const status = await checkEndpointStatus(
//edge endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
try { ? KubernetesHealthService.ping(endpoint.Id)
await KubernetesHealthService.ping(endpoint.Id); : // use selfsubject access review to check if we can connect to the kubernetes environment
endpoint.Status = EnvironmentStatus.Up; // because it gets a fast response, and is accessible to all users
} catch (e) { getSelfSubjectAccessReview(endpoint.Id, 'default')
endpoint.Status = EnvironmentStatus.Down; );
}
if (endpoint.Type !== PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
await updateEndpointStatus(endpoint, status);
}
endpoint.Status = status;
if (endpoint.Status === EnvironmentStatus.Down) {
throw new Error(
endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
? 'Unable to contact Edge agent, please ensure that the agent is properly running on the remote environment.'
: `The environment named ${endpoint.Name} is unreachable.`
);
} }
await StateManager.updateEndpointState(endpoint); await StateManager.updateEndpointState(endpoint);
if (endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment && endpoint.Status === EnvironmentStatus.Down) {
throw new Error('Unable to contact Edge agent, please ensure that the agent is properly running on the remote environment.');
}
// use selfsubject access review to check if we can connect to the kubernetes environment
// because it's gets a fast response, and is accessible to all users
try {
await getSelfSubjectAccessReview(endpoint.Id, 'default');
} catch (e) {
throw new Error(`The environment named ${endpoint.Name} is unreachable.`);
}
} catch (e) { } catch (e) {
let params = {}; let params = {};
if (endpoint.Type == PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) { if (endpoint.Type == PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
params = { redirect: true, environmentId: endpoint.Id, environmentName: endpoint.Name, route: 'kubernetes.dashboard' }; params = { redirect: true, environmentId: endpoint.Id, environmentName: endpoint.Name, route: 'kubernetes.dashboard' };
} else { } else {
EndpointProvider.clean();
Notifications.error('Failed loading environment', e); Notifications.error('Failed loading environment', e);
} }
$state.go('portainer.home', params, { reload: true, inherit: false }); $state.go('portainer.home', params, { reload: true, inherit: false });
return false;
}
async function checkEndpointStatus(promise) {
try {
await promise;
return EnvironmentStatus.Up;
} catch (e) {
return EnvironmentStatus.Down;
}
}
async function updateEndpointStatus(endpoint, status) {
if (endpoint.Status === status) {
return;
}
await EndpointService.updateEndpoint(endpoint.Id, { Status: status });
} }
}); });
}, },

View File

@ -1,6 +1,8 @@
import { useStore } from 'zustand';
import { useCurrentStateAndParams, useRouter } from '@uirouter/react'; import { useCurrentStateAndParams, useRouter } from '@uirouter/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { environmentStore } from '@/react/hooks/current-environment-store';
import { Environment } from '@/react/portainer/environments/types'; import { Environment } from '@/react/portainer/environments/types';
import { snapshotEndpoints } from '@/react/portainer/environments/environment.service'; import { snapshotEndpoints } from '@/react/portainer/environments/environment.service';
import { isEdgeEnvironment } from '@/react/portainer/environments/utils'; import { isEdgeEnvironment } from '@/react/portainer/environments/utils';
@ -18,6 +20,8 @@ import { LicenseNodePanel } from './LicenseNodePanel';
import { BackupFailedPanel } from './BackupFailedPanel'; import { BackupFailedPanel } from './BackupFailedPanel';
export function HomeView() { export function HomeView() {
const { clear: clearStore } = useStore(environmentStore);
const { params } = useCurrentStateAndParams(); const { params } = useCurrentStateAndParams();
const [connectingToEdgeEndpoint, setConnectingToEdgeEndpoint] = useState( const [connectingToEdgeEndpoint, setConnectingToEdgeEndpoint] = useState(
!!params.redirect !!params.redirect
@ -40,14 +44,19 @@ export function HomeView() {
endpointId: params.environmentId, endpointId: params.environmentId,
}); });
} else { } else {
router.stateService.go('portainer.home', {}, { inherit: false }); clearStore();
router.stateService.go(
'portainer.home',
{},
{ reload: true, inherit: false }
);
} }
} }
if (params.redirect) { if (params.redirect) {
redirect(); redirect();
} }
}, [params, setConnectingToEdgeEndpoint, router]); }, [params, setConnectingToEdgeEndpoint, router, clearStore]);
return ( return (
<> <>