mirror of
https://github.com/portainer/portainer.git
synced 2025-08-09 15:55:23 +02:00
feat(system): path to upgrade standalone to BE [EE-4071] (#8095)
This commit is contained in:
parent
756ac034ec
commit
5cbf52377d
73 changed files with 1374 additions and 421 deletions
|
@ -11,7 +11,7 @@ test('when user is using more nodes then allowed he should see message', async (
|
|||
rest.get('/api/licenses/info', (req, res, ctx) =>
|
||||
res(ctx.json({ nodes: allowed, type: LicenseType.Subscription }))
|
||||
),
|
||||
rest.get('/api/status/nodes', (req, res, ctx) =>
|
||||
rest.get('/api/system/nodes', (req, res, ctx) =>
|
||||
res(ctx.json({ nodes: used }))
|
||||
)
|
||||
);
|
||||
|
@ -32,7 +32,7 @@ test("when user is using less nodes then allowed he shouldn't see message", asyn
|
|||
rest.get('/api/licenses/info', (req, res, ctx) =>
|
||||
res(ctx.json({ nodes: allowed, type: LicenseType.Subscription }))
|
||||
),
|
||||
rest.get('/api/status/nodes', (req, res, ctx) =>
|
||||
rest.get('/api/system/nodes', (req, res, ctx) =>
|
||||
res(ctx.json({ nodes: used }))
|
||||
)
|
||||
);
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { useQuery } from 'react-query';
|
||||
|
||||
import { error as notifyError } from '@/portainer/services/notifications';
|
||||
import { LicenseType } from '@/portainer/license-management/types';
|
||||
import { useLicenseInfo } from '@/portainer/license-management/use-license.service';
|
||||
import { getNodesCount } from '@/portainer/services/api/status.service';
|
||||
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
import { InformationPanel } from '@@/InformationPanel';
|
||||
|
||||
import { useNodesCount } from '../system/useNodesCount';
|
||||
|
||||
export function LicenseNodePanel() {
|
||||
const nodesValid = useNodesValid();
|
||||
|
||||
|
@ -26,7 +24,7 @@ export function LicenseNodePanel() {
|
|||
}
|
||||
|
||||
function useNodesValid() {
|
||||
const { isLoading: isLoadingNodes, nodesCount } = useNodesCounts();
|
||||
const { isLoading: isLoadingNodes, data: nodesCount = 0 } = useNodesCount();
|
||||
|
||||
const { isLoading: isLoadingLicense, info } = useLicenseInfo();
|
||||
if (
|
||||
|
@ -40,17 +38,3 @@ function useNodesValid() {
|
|||
|
||||
return nodesCount <= info.nodes;
|
||||
}
|
||||
|
||||
function useNodesCounts() {
|
||||
const { isLoading, data } = useQuery(
|
||||
['status', 'nodes'],
|
||||
() => getNodesCount(),
|
||||
{
|
||||
onError(error) {
|
||||
notifyError('Failure', error as Error, 'Failed to get nodes count');
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return { nodesCount: data || 0, isLoading };
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useStatus } from '@/portainer/services/api/status.service';
|
||||
import { useSettings } from '@/react/portainer/settings/queries';
|
||||
import { useSystemStatus } from '@/react/portainer/system/useSystemStatus';
|
||||
|
||||
export function useAgentDetails() {
|
||||
const settingsQuery = useSettings();
|
||||
|
||||
const versionQuery = useStatus((status) => status.Version);
|
||||
const versionQuery = useSystemStatus({ select: (status) => status.Version });
|
||||
|
||||
if (!versionQuery.isSuccess || !settingsQuery.isSuccess) {
|
||||
return null;
|
||||
|
|
|
@ -2,9 +2,7 @@ import { useRouter } from '@uirouter/react';
|
|||
|
||||
import { usePublicSettings } from '@/react/portainer/settings/queries';
|
||||
|
||||
export enum FeatureFlag {
|
||||
BEUpgrade = 'beUpgrade',
|
||||
}
|
||||
export enum FeatureFlag {}
|
||||
|
||||
export function useFeatureFlag(
|
||||
flag: FeatureFlag,
|
||||
|
|
22
app/react/portainer/feature-flags/withEdition.tsx
Normal file
22
app/react/portainer/feature-flags/withEdition.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { ComponentType } from 'react';
|
||||
|
||||
export function withEdition<T>(
|
||||
WrappedComponent: ComponentType<T>,
|
||||
edition: 'BE' | 'CE'
|
||||
): ComponentType<T> {
|
||||
// Try to create a nice displayName for React Dev Tools.
|
||||
const displayName =
|
||||
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||
|
||||
function WrapperComponent(props: T) {
|
||||
if (process.env.PORTAINER_EDITION !== edition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <WrappedComponent {...props} />;
|
||||
}
|
||||
|
||||
WrapperComponent.displayName = `with${edition}Edition(${displayName})`;
|
||||
|
||||
return WrapperComponent;
|
||||
}
|
26
app/react/portainer/feature-flags/withFeatureFlag.tsx
Normal file
26
app/react/portainer/feature-flags/withFeatureFlag.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { ComponentType } from 'react';
|
||||
|
||||
import { FeatureFlag, useFeatureFlag } from './useRedirectFeatureFlag';
|
||||
|
||||
export function withFeatureFlag<T>(
|
||||
WrappedComponent: ComponentType<T>,
|
||||
flag: FeatureFlag
|
||||
): ComponentType<T> {
|
||||
// Try to create a nice displayName for React Dev Tools.
|
||||
const displayName =
|
||||
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||
|
||||
function WrapperComponent(props: T) {
|
||||
const featureFlagQuery = useFeatureFlag(flag);
|
||||
|
||||
if (!featureFlagQuery.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <WrappedComponent {...props} />;
|
||||
}
|
||||
|
||||
WrapperComponent.displayName = `with${flag}FeatureFlag(${displayName})`;
|
||||
|
||||
return WrapperComponent;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
export function buildUrl(action?: string) {
|
||||
let url = '/status';
|
||||
let url = '/system';
|
||||
|
||||
if (action) {
|
||||
url += `/${action}`;
|
3
app/react/portainer/system/query-keys.ts
Normal file
3
app/react/portainer/system/query-keys.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export const queryKeys = {
|
||||
base: () => ['system'] as const,
|
||||
};
|
|
@ -4,6 +4,9 @@ import axios, { parseAxiosError } from '@/portainer/services/axios';
|
|||
import { withError } from '@/react-tools/react-query';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
import { queryKeys } from './query-keys';
|
||||
|
||||
export const queryKey = [...queryKeys.base(), 'nodes'] as const;
|
||||
|
||||
export interface NodesCountResponse {
|
||||
nodes: number;
|
||||
|
@ -19,7 +22,7 @@ async function getNodesCount() {
|
|||
}
|
||||
|
||||
export function useNodesCount() {
|
||||
return useQuery(['status', 'nodes'], getNodesCount, {
|
||||
return useQuery(queryKey, getNodesCount, {
|
||||
...withError('Unable to retrieve nodes count'),
|
||||
});
|
||||
}
|
|
@ -4,9 +4,19 @@ import axios, { parseAxiosError } from '@/portainer/services/axios';
|
|||
import { withError } from '@/react-tools/react-query';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
import { queryKeys } from './query-keys';
|
||||
|
||||
export const queryKey = [...queryKeys.base(), 'info'] as const;
|
||||
|
||||
export type ContainerPlatform =
|
||||
| 'Docker Standalone'
|
||||
| 'Docker Swarm'
|
||||
| 'Kubernetes'
|
||||
| 'Podman'
|
||||
| 'Nomad';
|
||||
|
||||
export interface SystemInfoResponse {
|
||||
platform: string;
|
||||
platform: ContainerPlatform;
|
||||
agents: number;
|
||||
edgeAgents: number;
|
||||
edgeDevices: number;
|
||||
|
@ -14,7 +24,7 @@ export interface SystemInfoResponse {
|
|||
|
||||
async function getSystemInfo() {
|
||||
try {
|
||||
const { data } = await axios.get<SystemInfoResponse>(buildUrl('system'));
|
||||
const { data } = await axios.get<SystemInfoResponse>(buildUrl('info'));
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw parseAxiosError(error as Error);
|
||||
|
@ -22,7 +32,7 @@ async function getSystemInfo() {
|
|||
}
|
||||
|
||||
export function useSystemInfo() {
|
||||
return useQuery(['status', 'system'], getSystemInfo, {
|
||||
return useQuery(queryKey, getSystemInfo, {
|
||||
...withError('Unable to retrieve system info'),
|
||||
});
|
||||
}
|
46
app/react/portainer/system/useSystemStatus.ts
Normal file
46
app/react/portainer/system/useSystemStatus.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { useQuery } from 'react-query';
|
||||
import { RetryValue } from 'react-query/types/core/retryer';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
import { queryKeys } from './query-keys';
|
||||
|
||||
export const queryKey = [...queryKeys.base(), 'status'] as const;
|
||||
|
||||
export interface StatusResponse {
|
||||
Edition: string;
|
||||
Version: string;
|
||||
InstanceID: string;
|
||||
}
|
||||
|
||||
export async function getSystemStatus() {
|
||||
try {
|
||||
const { data } = await axios.get<StatusResponse>(buildUrl('status'));
|
||||
|
||||
data.Edition = 'Community Edition';
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw parseAxiosError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export function useSystemStatus<T = StatusResponse>({
|
||||
select,
|
||||
enabled,
|
||||
retry,
|
||||
onSuccess,
|
||||
}: {
|
||||
select?: (status: StatusResponse) => T;
|
||||
enabled?: boolean;
|
||||
retry?: RetryValue<unknown>;
|
||||
onSuccess?: (data: T) => void;
|
||||
} = {}) {
|
||||
return useQuery(queryKey, () => getSystemStatus(), {
|
||||
select,
|
||||
enabled,
|
||||
retry,
|
||||
onSuccess,
|
||||
});
|
||||
}
|
38
app/react/portainer/system/useSystemVersion.ts
Normal file
38
app/react/portainer/system/useSystemVersion.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { useQuery } from 'react-query';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
import { queryKeys } from './query-keys';
|
||||
|
||||
export const queryKey = [...queryKeys.base(), 'version'] as const;
|
||||
|
||||
export interface VersionResponse {
|
||||
// Whether portainer has an update available
|
||||
UpdateAvailable: boolean;
|
||||
// The latest version available
|
||||
LatestVersion: string;
|
||||
ServerVersion: string;
|
||||
DatabaseVersion: string;
|
||||
Build: {
|
||||
BuildNumber: string;
|
||||
ImageTag: string;
|
||||
NodejsVersion: string;
|
||||
YarnVersion: string;
|
||||
WebpackVersion: string;
|
||||
GoVersion: string;
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSystemVersion() {
|
||||
try {
|
||||
const { data } = await axios.get<VersionResponse>(buildUrl('version'));
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw parseAxiosError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export function useSystemVersion() {
|
||||
return useQuery(queryKey, () => getSystemVersion());
|
||||
}
|
20
app/react/portainer/system/useUpgradeEditionMutation.ts
Normal file
20
app/react/portainer/system/useUpgradeEditionMutation.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { useMutation } from 'react-query';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { withError } from '@/react-tools/react-query';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
|
||||
export function useUpgradeEditionMutation() {
|
||||
return useMutation(upgradeEdition, {
|
||||
...withError('Unable to upgrade edition'),
|
||||
});
|
||||
}
|
||||
|
||||
async function upgradeEdition({ license }: { license: string }) {
|
||||
try {
|
||||
await axios.post(buildUrl('upgrade'), { license });
|
||||
} catch (error) {
|
||||
throw parseAxiosError(error as Error);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue