From 9339d10233782c9dd99b6c5927c85e097a54e438 Mon Sep 17 00:00:00 2001 From: LP B Date: Wed, 19 Jun 2024 13:45:02 +0200 Subject: [PATCH] fix(app): properly update the app state when losing connectivity to a remote environment while browsing it (#11943) --- app/docker/__module.js | 4 +- app/kubernetes/__module.js | 63 +++++++++++++++-------- app/react/portainer/HomeView/HomeView.tsx | 13 ++++- 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/app/docker/__module.js b/app/docker/__module.js index 21cfc5149..d397807fd 100644 --- a/app/docker/__module.js +++ b/app/docker/__module.js @@ -16,7 +16,7 @@ angular.module('portainer.docker', ['portainer.app', reactModule]).config([ parent: 'endpoint', url: '/docker', 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 () => { 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) { params = { redirect: true, environmentId: endpoint.Id, environmentName: endpoint.Name, route: 'docker.dashboard' }; } else { + EndpointProvider.clean(); Notifications.error('Failed loading environment', e); } $state.go('portainer.home', params, { reload: true, inherit: false }); + return false; } async function checkEndpointStatus(endpoint) { diff --git a/app/kubernetes/__module.js b/app/kubernetes/__module.js index 68af4628b..d45e2ac4d 100644 --- a/app/kubernetes/__module.js +++ b/app/kubernetes/__module.js @@ -63,12 +63,13 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo $state, endpoint, KubernetesHealthService, - KubernetesNamespaceService, Notifications, StateManager, $http, Authentication, - UserService + UserService, + EndpointService, + EndpointProvider ) { return $async(async () => { // 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'); return; } + try { - if (endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) { - //edge - try { - await KubernetesHealthService.ping(endpoint.Id); - endpoint.Status = EnvironmentStatus.Up; - } catch (e) { - endpoint.Status = EnvironmentStatus.Down; - } + const status = await checkEndpointStatus( + endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment + ? KubernetesHealthService.ping(endpoint.Id) + : // use selfsubject access review to check if we can connect to the kubernetes environment + // because it gets a fast response, and is accessible to all users + getSelfSubjectAccessReview(endpoint.Id, 'default') + ); + + 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); - - 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) { let params = {}; if (endpoint.Type == PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) { params = { redirect: true, environmentId: endpoint.Id, environmentName: endpoint.Name, route: 'kubernetes.dashboard' }; } else { + EndpointProvider.clean(); Notifications.error('Failed loading environment', e); } $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 }); } }); }, diff --git a/app/react/portainer/HomeView/HomeView.tsx b/app/react/portainer/HomeView/HomeView.tsx index 6e5e83ad6..5a504601a 100644 --- a/app/react/portainer/HomeView/HomeView.tsx +++ b/app/react/portainer/HomeView/HomeView.tsx @@ -1,6 +1,8 @@ +import { useStore } from 'zustand'; import { useCurrentStateAndParams, useRouter } from '@uirouter/react'; import { useEffect, useState } from 'react'; +import { environmentStore } from '@/react/hooks/current-environment-store'; import { Environment } from '@/react/portainer/environments/types'; import { snapshotEndpoints } from '@/react/portainer/environments/environment.service'; import { isEdgeEnvironment } from '@/react/portainer/environments/utils'; @@ -18,6 +20,8 @@ import { LicenseNodePanel } from './LicenseNodePanel'; import { BackupFailedPanel } from './BackupFailedPanel'; export function HomeView() { + const { clear: clearStore } = useStore(environmentStore); + const { params } = useCurrentStateAndParams(); const [connectingToEdgeEndpoint, setConnectingToEdgeEndpoint] = useState( !!params.redirect @@ -40,14 +44,19 @@ export function HomeView() { endpointId: params.environmentId, }); } else { - router.stateService.go('portainer.home', {}, { inherit: false }); + clearStore(); + router.stateService.go( + 'portainer.home', + {}, + { reload: true, inherit: false } + ); } } if (params.redirect) { redirect(); } - }, [params, setConnectingToEdgeEndpoint, router]); + }, [params, setConnectingToEdgeEndpoint, router, clearStore]); return ( <>