feat(edge/stacks): info for old agent status [EE-5792] (#10012)

pull/10054/head
Chaim Lev-Ari 2023-08-14 16:04:20 +03:00 committed by GitHub
parent f5cc245c63
commit faa1387110
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 35 deletions

View File

@ -50,7 +50,7 @@ func (store *Store) MigrateData() error {
if err != nil {
err = errors.Wrap(err, "failed to migrate database")
log.Warn().Msg("migration failed, restoring database to previous version")
log.Warn().Err(err).Msg("migration failed, restoring database to previous version")
err = store.restoreWithOptions(&BackupOptions{BackupPath: backupPath})
if err != nil {
return errors.Wrap(err, "failed to restore database")

View File

@ -115,10 +115,16 @@ func (m *Migrator) updateEdgeStackStatusForDB100() error {
}
if environmentStatus.Details.Ok {
statusArray = append(statusArray, portainer.EdgeStackDeploymentStatus{
Type: portainer.EdgeStackStatusRunning,
Time: time.Now().Unix(),
})
statusArray = append(statusArray,
portainer.EdgeStackDeploymentStatus{
Type: portainer.EdgeStackStatusDeploymentReceived,
Time: time.Now().Unix(),
},
portainer.EdgeStackDeploymentStatus{
Type: portainer.EdgeStackStatusRunning,
Time: time.Now().Unix(),
},
)
}
if environmentStatus.Details.ImagesPulled {

View File

@ -1,4 +1,4 @@
import { semverCompare } from './utils';
import { semverCompare } from './semver-utils';
describe('semverCompare', () => {
test('sort array', () => {

View File

@ -0,0 +1,27 @@
/**
* Compares two semver strings.
*
* returns:
* - `-1` if `a < b`
* - `0` if `a == b`
* - `1` if `a > b`
*/
export function semverCompare(a: string, b: string) {
if (a.startsWith(`${b}-`)) {
return -1;
}
if (b.startsWith(`${a}-`)) {
return 1;
}
return a.localeCompare(b, undefined, {
numeric: true,
sensitivity: 'case',
caseFirst: 'upper',
});
}
export function isVersionSmaller(a: string, b: string) {
return semverCompare(a, b) < 0;
}

View File

@ -5,9 +5,14 @@ import {
type Icon as IconType,
Loader2,
XCircle,
MinusCircle,
} from 'lucide-react';
import { useEnvironmentList } from '@/react/portainer/environments/queries';
import { isVersionSmaller } from '@/react/common/semver-utils';
import { Icon, IconMode } from '@@/Icon';
import { Tooltip } from '@@/Tip/Tooltip';
import { DeploymentStatus, EdgeStack, StatusType } from '../../types';
@ -15,28 +20,51 @@ export function EdgeStackStatus({ edgeStack }: { edgeStack: EdgeStack }) {
const status = Object.values(edgeStack.Status);
const lastStatus = _.compact(status.map((s) => _.last(s.Status)));
const { icon, label, mode, spin } = getStatus(
const environmentsQuery = useEnvironmentList({ edgeStackId: edgeStack.Id });
if (environmentsQuery.isLoading) {
return null;
}
const hasOldVersion = environmentsQuery.environments.some((env) =>
isVersionSmaller(env.Agent.Version, '2.19.0')
);
const { icon, label, mode, spin, tooltip } = getStatus(
edgeStack.NumDeployments,
lastStatus
lastStatus,
hasOldVersion
);
return (
<div className="mx-auto inline-flex items-center gap-2">
{icon && <Icon icon={icon} spin={spin} mode={mode} />}
{label}
{tooltip && <Tooltip message={tooltip} />}
</div>
);
}
function getStatus(
numDeployments: number,
envStatus: Array<DeploymentStatus>
envStatus: Array<DeploymentStatus>,
hasOldVersion: boolean
): {
label: string;
icon?: IconType;
spin?: boolean;
mode?: IconMode;
tooltip?: string;
} {
if (!numDeployments || hasOldVersion) {
return {
label: 'Unavailable',
icon: MinusCircle,
mode: 'secondary',
tooltip: getUnavailableTooltip(),
};
}
if (envStatus.length < numDeployments) {
return {
label: 'Deploying',
@ -56,7 +84,11 @@ function getStatus(
};
}
const allRunning = envStatus.every((s) => s.Type === StatusType.Running);
const allRunning = envStatus.every(
(s) =>
s.Type === StatusType.Running ||
(s.Type === StatusType.DeploymentReceived && hasOldVersion)
);
if (allRunning) {
return {
@ -84,4 +116,16 @@ function getStatus(
spin: true,
mode: 'primary',
};
function getUnavailableTooltip() {
if (!numDeployments) {
return 'Your edge stack is currently unavailable due to the absence of an available environment in your edge group';
}
if (hasOldVersion) {
return 'Please note that the new status feature for the Edge stack is only available for Edge Agent versions 2.19.0 and above. To access the status of your edge stack, it is essential to upgrade your Edge Agent to a corresponding version that is compatible with your Portainer server.';
}
return '';
}
}

View File

@ -45,13 +45,19 @@ export const columns = _.compact([
(item) => item.aggregatedStatus[StatusType.ImagesPulled] || 0,
{
header: 'Images pre-pulled',
cell: ({ getValue, row }) => (
<DeploymentCounter
count={getValue()}
type={StatusType.ImagesPulled}
total={row.original.NumDeployments}
/>
),
cell: ({ getValue, row: { original: item } }) => {
if (!item.PrePullImage) {
return <div className="text-center">-</div>;
}
return (
<DeploymentCounter
count={getValue()}
type={StatusType.ImagesPulled}
total={item.NumDeployments}
/>
);
},
enableSorting: false,
enableHiding: false,
meta: {

View File

@ -1,12 +1,12 @@
import _ from 'lodash';
import { Environment } from '@/react/portainer/environments/types';
import { semverCompare } from '@/react/common/semver-utils';
import { TextTip } from '@@/Tip/TextTip';
import { VersionSelect } from './VersionSelect';
import { ScheduledTimeField } from './ScheduledTimeField';
import { semverCompare } from './utils';
interface Props {
environments: Environment[];

View File

@ -1,18 +1,4 @@
export function semverCompare(a: string, b: string) {
if (a.startsWith(`${b}-`)) {
return -1;
}
if (b.startsWith(`${a}-`)) {
return 1;
}
return a.localeCompare(b, undefined, {
numeric: true,
sensitivity: 'case',
caseFirst: 'upper',
});
}
import { semverCompare } from '@/react/common/semver-utils';
export function compareVersion(
currentVersion: string,

View File

@ -2,8 +2,7 @@ import { useQuery } from 'react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { withError } from '@/react-tools/react-query';
import { semverCompare } from '../common/utils';
import { semverCompare } from '@/react/common/semver-utils';
import { queryKeys } from './query-keys';
import { buildUrl } from './urls';