chore: import upstream snapshot with attribution
This commit is contained in:
+220
@@ -0,0 +1,220 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Laptop, Network } from 'lucide-react';
|
||||
|
||||
import { generateKey } from '@/react/portainer/environments/environment.service/edge';
|
||||
import { EdgeScriptForm } from '@/react/edge/components/EdgeScriptForm';
|
||||
import { commandsTabs } from '@/react/edge/components/EdgeScriptForm/scripts';
|
||||
import { useSettings } from '@/react/portainer/settings/queries';
|
||||
import EdgeAgentStandardIcon from '@/react/edge/components/edge-agent-standard.svg?c';
|
||||
import EdgeAgentAsyncIcon from '@/react/edge/components/edge-agent-async.svg?c';
|
||||
import { ConnectivityTestModal } from '@/react/edge/components/ConnectivityTestModal/ConnectivityTestModal';
|
||||
|
||||
import { Widget, WidgetBody, WidgetTitle } from '@@/Widget';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
import { BoxSelector } from '@@/BoxSelector';
|
||||
import { FormSection } from '@@/form-components/FormSection';
|
||||
import { CopyButton } from '@@/buttons';
|
||||
import { Button } from '@@/buttons';
|
||||
import { Link } from '@@/Link';
|
||||
import { FormControl } from '@@/form-components/FormControl';
|
||||
import { Input } from '@@/form-components/Input';
|
||||
|
||||
const commands = {
|
||||
linux: [
|
||||
commandsTabs.k8sLinux,
|
||||
commandsTabs.swarmLinux,
|
||||
commandsTabs.standaloneLinux,
|
||||
commandsTabs.podmanLinux,
|
||||
],
|
||||
win: [commandsTabs.swarmWindows, commandsTabs.standaloneWindow],
|
||||
};
|
||||
|
||||
const asyncModeOptions = [
|
||||
{
|
||||
icon: EdgeAgentStandardIcon,
|
||||
id: 'standard',
|
||||
label: 'Edge Agent Standard',
|
||||
value: false,
|
||||
iconType: 'badge',
|
||||
},
|
||||
{
|
||||
icon: EdgeAgentAsyncIcon,
|
||||
id: 'async',
|
||||
label: 'Edge Agent Async',
|
||||
value: true,
|
||||
iconType: 'badge',
|
||||
},
|
||||
] as const;
|
||||
|
||||
export function AutomaticEdgeEnvCreation() {
|
||||
const edgeKeyMutation = useGenerateKeyMutation();
|
||||
const { mutate: generateKey, reset: resetKey } = edgeKeyMutation;
|
||||
const settingsQuery = useSettings();
|
||||
const [asyncMode, setAsyncMode] = useState(false);
|
||||
|
||||
const url = settingsQuery.data?.EdgePortainerUrl;
|
||||
|
||||
const settings = settingsQuery.data;
|
||||
const edgeKey = edgeKeyMutation.data;
|
||||
const edgeComputeConfigurationOK = validateConfiguration();
|
||||
|
||||
useEffect(() => {
|
||||
if (edgeComputeConfigurationOK) {
|
||||
generateKey();
|
||||
} else {
|
||||
resetKey();
|
||||
}
|
||||
}, [generateKey, edgeComputeConfigurationOK, resetKey]);
|
||||
|
||||
if (!settingsQuery.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<WidgetTitle icon={Laptop} title="Automatic Edge Environment Creation" />
|
||||
<WidgetBody className="form-horizontal">
|
||||
{!edgeComputeConfigurationOK ? (
|
||||
<TextTip color="orange">
|
||||
In order to use this feature, please turn on Edge Compute features{' '}
|
||||
<Link
|
||||
to="portainer.settings.edgeCompute"
|
||||
data-cy="edge-disabled-portainer-edge-settings-link"
|
||||
>
|
||||
here
|
||||
</Link>{' '}
|
||||
and have Portainer API server URL and tunnel server address properly
|
||||
configured.
|
||||
</TextTip>
|
||||
) : (
|
||||
<>
|
||||
<BoxSelector
|
||||
slim
|
||||
radioName="async-mode-selector"
|
||||
value={asyncMode}
|
||||
onChange={handleChangeAsyncMode}
|
||||
options={asyncModeOptions}
|
||||
/>
|
||||
|
||||
<EdgeKeyInfo
|
||||
asyncMode={asyncMode}
|
||||
edgeKey={edgeKey}
|
||||
isLoading={edgeKeyMutation.isLoading}
|
||||
url={url}
|
||||
tunnelUrl={settings?.Edge.TunnelServerAddress}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</WidgetBody>
|
||||
</Widget>
|
||||
);
|
||||
|
||||
function handleChangeAsyncMode(asyncMode: boolean) {
|
||||
setAsyncMode(asyncMode);
|
||||
}
|
||||
|
||||
function validateConfiguration() {
|
||||
return !!(
|
||||
settings &&
|
||||
settings.EnableEdgeComputeFeatures &&
|
||||
settings.EdgePortainerUrl &&
|
||||
settings.Edge.TunnelServerAddress
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// using mutation because we want this action to run only when required
|
||||
function useGenerateKeyMutation() {
|
||||
return useMutation(generateKey);
|
||||
}
|
||||
|
||||
function EdgeKeyInfo({
|
||||
isLoading,
|
||||
edgeKey,
|
||||
url,
|
||||
tunnelUrl,
|
||||
asyncMode,
|
||||
}: {
|
||||
isLoading: boolean;
|
||||
edgeKey?: string;
|
||||
url?: string;
|
||||
tunnelUrl?: string;
|
||||
asyncMode: boolean;
|
||||
}) {
|
||||
const [isConnectivityModalOpen, setIsConnectivityModalOpen] = useState(false);
|
||||
|
||||
if (isLoading || !edgeKey) {
|
||||
return <div>Generating key for {url} ... </div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<hr />
|
||||
|
||||
<FormSection title="Edge key">
|
||||
<div className="break-words">
|
||||
<code>{edgeKey}</code>
|
||||
</div>
|
||||
|
||||
<CopyButton
|
||||
copyText={edgeKey}
|
||||
data-cy="edge-auto-create-copy-token-button"
|
||||
>
|
||||
Copy token
|
||||
</CopyButton>
|
||||
</FormSection>
|
||||
|
||||
<hr />
|
||||
|
||||
<EdgeScriptForm
|
||||
edgeInfo={{ key: edgeKey }}
|
||||
commands={commands}
|
||||
asyncMode={asyncMode}
|
||||
showMetaFields
|
||||
>
|
||||
<TextTip color="blue">
|
||||
Portainer Server URL{' '}
|
||||
{!asyncMode ? 'and tunnel server address are' : 'is'} set{' '}
|
||||
<Link
|
||||
to="portainer.settings.edgeCompute"
|
||||
data-cy="server-url-portainer-edge-settings-link"
|
||||
>
|
||||
here
|
||||
</Link>
|
||||
</TextTip>
|
||||
<FormControl label="Portainer API server URL">
|
||||
<Input value={url} readOnly data-cy="edge-auto-create-url-input" />
|
||||
</FormControl>
|
||||
|
||||
{!asyncMode && (
|
||||
<FormControl label="Portainer tunnel server address">
|
||||
<Input
|
||||
value={tunnelUrl}
|
||||
readOnly
|
||||
data-cy="edge-auto-create-tunnel-address-input"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
<Button
|
||||
color="default"
|
||||
className="!ml-0 mb-8"
|
||||
icon={Network}
|
||||
onClick={() => setIsConnectivityModalOpen(true)}
|
||||
data-cy="edge-auto-create-test-connectivity-button"
|
||||
>
|
||||
Test connectivity
|
||||
</Button>
|
||||
</EdgeScriptForm>
|
||||
|
||||
{isConnectivityModalOpen && url && (
|
||||
<ConnectivityTestModal
|
||||
portainerUrl={url}
|
||||
tunnelServerAddr={!asyncMode ? tunnelUrl : undefined}
|
||||
onDismiss={() => setIsConnectivityModalOpen(false)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { AutomaticEdgeEnvCreation } from './AutomaticEdgeEnvCreation';
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { withLimitToBE } from '@/react/hooks/useLimitToBE';
|
||||
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
|
||||
import { AutomaticEdgeEnvCreation } from './AutomaticEdgeEnvCreation';
|
||||
|
||||
export const EdgeAutoCreateScriptViewWrapper = withLimitToBE(
|
||||
EdgeAutoCreateScriptView
|
||||
);
|
||||
|
||||
function EdgeAutoCreateScriptView() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title="Automatic Edge Environment Creation"
|
||||
breadcrumbs={[
|
||||
{ label: 'Environments', link: 'portainer.endpoints' },
|
||||
'Automatic Edge Environment Creation',
|
||||
]}
|
||||
reload
|
||||
/>
|
||||
|
||||
<div className="mx-3">
|
||||
<AutomaticEdgeEnvCreation />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { EdgeAutoCreateScriptViewWrapper as EdgeAutoCreateScriptView } from './EdgeAutoCreateScriptView';
|
||||
Reference in New Issue
Block a user