mirror of
https://github.com/portainer/portainer.git
synced 2025-08-04 21:35:23 +02:00
refactor(edge): move edge deploy script to react [EE-2689] (#6747)
This commit is contained in:
parent
328ce2f995
commit
85a7b7e0fc
38 changed files with 1079 additions and 342 deletions
75
app/edge/components/EdgeScriptForm/EdgePropertiesForm.tsx
Normal file
75
app/edge/components/EdgeScriptForm/EdgePropertiesForm.tsx
Normal file
|
@ -0,0 +1,75 @@
|
|||
import { FormControl } from '@/portainer/components/form-components/FormControl';
|
||||
import { Input } from '@/portainer/components/form-components/Input';
|
||||
import { FormSectionTitle } from '@/portainer/components/form-components/FormSectionTitle';
|
||||
import { SwitchField } from '@/portainer/components/form-components/SwitchField';
|
||||
|
||||
import { OsSelector } from './OsSelector';
|
||||
import { EdgeProperties } from './types';
|
||||
|
||||
interface Props {
|
||||
setFieldValue<T>(key: string, value: T): void;
|
||||
values: EdgeProperties;
|
||||
hideIdGetter: boolean;
|
||||
}
|
||||
|
||||
export function EdgePropertiesForm({
|
||||
setFieldValue,
|
||||
values,
|
||||
hideIdGetter,
|
||||
}: Props) {
|
||||
return (
|
||||
<form className="form-horizontal">
|
||||
<FormSectionTitle>Edge script settings</FormSectionTitle>
|
||||
|
||||
<OsSelector
|
||||
value={values.os}
|
||||
onChange={(os) => setFieldValue('os', os)}
|
||||
/>
|
||||
|
||||
{!hideIdGetter && (
|
||||
<FormControl
|
||||
label="Edge ID Generator"
|
||||
tooltip="A bash script one liner that will generate the edge id"
|
||||
inputId="edge-id-generator-input"
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
name="edgeIdGenerator"
|
||||
value={values.edgeIdGenerator}
|
||||
id="edge-id-generator-input"
|
||||
onChange={(e) => setFieldValue(e.target.name, e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
<div className="form-group">
|
||||
<div className="col-sm-12">
|
||||
<SwitchField
|
||||
checked={values.allowSelfSignedCertificates}
|
||||
label="Allow self-signed certificates"
|
||||
tooltip="When allowing self-signed certificates the edge agent will ignore the domain validation when connecting to Portainer via HTTPS"
|
||||
onChange={(checked) =>
|
||||
setFieldValue('allowSelfSignedCertificates', checked)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{values.platform !== 'k8s' && (
|
||||
<FormControl
|
||||
label="Environment variables"
|
||||
tooltip="Comma separated list of environment variables that will be sourced from the host where the agent is deployed."
|
||||
inputId="env-vars-input"
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
name="envVars"
|
||||
value={values.envVars}
|
||||
id="env-vars-input"
|
||||
onChange={(e) => setFieldValue(e.target.name, e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
60
app/edge/components/EdgeScriptForm/EdgeScriptForm.tsx
Normal file
60
app/edge/components/EdgeScriptForm/EdgeScriptForm.tsx
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { useState } from 'react';
|
||||
|
||||
import { useStatus } from '@/portainer/services/api/status.service';
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { useSettings } from '@/portainer/settings/settings.service';
|
||||
|
||||
import { EdgePropertiesForm } from './EdgePropertiesForm';
|
||||
import { Scripts } from './Scripts';
|
||||
import { EdgeProperties } from './types';
|
||||
|
||||
interface Props {
|
||||
edgeKey: string;
|
||||
edgeId?: string;
|
||||
}
|
||||
|
||||
export function EdgeScriptForm({ edgeKey, edgeId }: Props) {
|
||||
const [edgeProperties, setEdgeProperties] = useState<EdgeProperties>({
|
||||
allowSelfSignedCertificates: true,
|
||||
envVars: '',
|
||||
edgeIdGenerator: '',
|
||||
os: 'linux',
|
||||
platform: 'swarm',
|
||||
});
|
||||
|
||||
const settingsQuery = useSettings((settings) => settings.AgentSecret);
|
||||
|
||||
const versionQuery = useStatus((status) => status.Version);
|
||||
|
||||
if (!versionQuery.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const agentVersion = versionQuery.data;
|
||||
const agentSecret = settingsQuery.data;
|
||||
|
||||
return (
|
||||
<>
|
||||
<EdgePropertiesForm
|
||||
setFieldValue={(key, value) =>
|
||||
setEdgeProperties({ ...edgeProperties, [key]: value })
|
||||
}
|
||||
values={edgeProperties}
|
||||
hideIdGetter={edgeId !== undefined}
|
||||
/>
|
||||
|
||||
<Scripts
|
||||
values={edgeProperties}
|
||||
agentVersion={agentVersion}
|
||||
edgeKey={edgeKey}
|
||||
onPlatformChange={(platform) =>
|
||||
setEdgeProperties({ ...edgeProperties, platform })
|
||||
}
|
||||
edgeId={edgeId}
|
||||
agentSecret={agentSecret}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const EdgeScriptFormAngular = r2a(EdgeScriptForm, ['edgeKey', 'edgeId']);
|
45
app/edge/components/EdgeScriptForm/OsSelector.tsx
Normal file
45
app/edge/components/EdgeScriptForm/OsSelector.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { ButtonSelector } from '@/portainer/components/form-components/ButtonSelector/ButtonSelector';
|
||||
|
||||
import { OS } from './types';
|
||||
|
||||
interface Props {
|
||||
value: OS;
|
||||
onChange(value: OS): void;
|
||||
}
|
||||
|
||||
export function OsSelector({ onChange, value }: Props) {
|
||||
return (
|
||||
<div className="form-group">
|
||||
<div className="col-sm-12">
|
||||
<ButtonSelector
|
||||
size="small"
|
||||
value={value}
|
||||
onChange={(os: OS) => onChange(os)}
|
||||
options={[
|
||||
{
|
||||
value: 'linux',
|
||||
label: (
|
||||
<>
|
||||
<i className="fab fa-linux space-right" aria-hidden="true" />
|
||||
Linux
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'win',
|
||||
label: (
|
||||
<>
|
||||
<i
|
||||
className="fab fa-windows space-right"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Windows
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
293
app/edge/components/EdgeScriptForm/Scripts.tsx
Normal file
293
app/edge/components/EdgeScriptForm/Scripts.tsx
Normal file
|
@ -0,0 +1,293 @@
|
|||
import { useEffect } from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { Code } from '@/portainer/components/Code';
|
||||
import { CopyButton } from '@/portainer/components/Button/CopyButton';
|
||||
import { NavTabs } from '@/portainer/components/NavTabs/NavTabs';
|
||||
import { getAgentShortVersion } from '@/portainer/views/endpoints/helpers';
|
||||
|
||||
import { EdgeProperties, Platform } from './types';
|
||||
|
||||
const commandsByOs = {
|
||||
linux: [
|
||||
{
|
||||
id: 'swarm',
|
||||
label: 'Docker Swarm',
|
||||
command: buildLinuxSwarmCommand,
|
||||
},
|
||||
{
|
||||
id: 'standalone',
|
||||
label: 'Docker Standalone',
|
||||
command: buildLinuxStandaloneCommand,
|
||||
},
|
||||
{
|
||||
id: 'k8s',
|
||||
label: 'Kubernetes',
|
||||
command: buildKubernetesCommand,
|
||||
},
|
||||
],
|
||||
win: [
|
||||
{
|
||||
id: 'swarm',
|
||||
label: 'Docker Swarm',
|
||||
command: buildWindowsSwarmCommand,
|
||||
},
|
||||
{
|
||||
id: 'standalone',
|
||||
label: 'Docker Standalone',
|
||||
command: buildWindowsStandaloneCommand,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
interface Props {
|
||||
values: EdgeProperties;
|
||||
edgeKey: string;
|
||||
agentVersion: string;
|
||||
edgeId?: string;
|
||||
agentSecret?: string;
|
||||
onPlatformChange(platform: Platform): void;
|
||||
}
|
||||
|
||||
export function Scripts({
|
||||
agentVersion,
|
||||
values,
|
||||
edgeKey,
|
||||
edgeId,
|
||||
agentSecret,
|
||||
onPlatformChange,
|
||||
}: Props) {
|
||||
const {
|
||||
os,
|
||||
allowSelfSignedCertificates,
|
||||
edgeIdGenerator,
|
||||
envVars,
|
||||
platform,
|
||||
} = values;
|
||||
|
||||
useEffect(() => {
|
||||
if (!commandsByOs[os].find((p) => p.id === platform)) {
|
||||
onPlatformChange('standalone');
|
||||
}
|
||||
}, [os, platform, onPlatformChange]);
|
||||
|
||||
const options = commandsByOs[os].map((c) => {
|
||||
const cmd = c.command(
|
||||
agentVersion,
|
||||
edgeIdGenerator,
|
||||
edgeKey,
|
||||
allowSelfSignedCertificates,
|
||||
envVars,
|
||||
edgeId,
|
||||
agentSecret
|
||||
);
|
||||
|
||||
return {
|
||||
id: c.id,
|
||||
label: c.label,
|
||||
children: (
|
||||
<>
|
||||
<Code>{cmd}</Code>
|
||||
<CopyButton copyText={cmd}>Copy</CopyButton>
|
||||
</>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
<NavTabs
|
||||
selectedId={platform}
|
||||
options={options}
|
||||
onSelect={(id: Platform) => onPlatformChange(id)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function buildDockerEnvVars(envVars: string, defaultVars: string[]) {
|
||||
const vars = defaultVars.concat(
|
||||
envVars.split(',').filter((s) => s.length > 0)
|
||||
);
|
||||
|
||||
return vars.map((s) => `-e ${s}`).join(' \\\n ');
|
||||
}
|
||||
|
||||
function buildLinuxStandaloneCommand(
|
||||
agentVersion: string,
|
||||
edgeIdScript: string,
|
||||
edgeKey: string,
|
||||
allowSelfSignedCerts: boolean,
|
||||
envVars: string,
|
||||
edgeId?: string,
|
||||
agentSecret?: string
|
||||
) {
|
||||
const env = buildDockerEnvVars(
|
||||
envVars,
|
||||
buildDefaultEnvVars(
|
||||
edgeKey,
|
||||
allowSelfSignedCerts,
|
||||
!edgeIdScript ? edgeId : undefined,
|
||||
agentSecret
|
||||
)
|
||||
);
|
||||
|
||||
return `${edgeIdScript ? `PORTAINER_EDGE_ID=$(${edgeIdScript}) \n\n` : ''}
|
||||
docker run -d \\
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \\
|
||||
-v /var/lib/docker/volumes:/var/lib/docker/volumes \\
|
||||
-v /:/host \\
|
||||
-v portainer_agent_data:/data \\
|
||||
--restart always \\
|
||||
${env} \\
|
||||
--name portainer_edge_agent \\
|
||||
portainer/agent:${agentVersion}
|
||||
`;
|
||||
}
|
||||
|
||||
function buildWindowsStandaloneCommand(
|
||||
agentVersion: string,
|
||||
edgeIdScript: string,
|
||||
edgeKey: string,
|
||||
allowSelfSignedCerts: boolean,
|
||||
envVars: string,
|
||||
edgeId?: string,
|
||||
agentSecret?: string
|
||||
) {
|
||||
const env = buildDockerEnvVars(
|
||||
envVars,
|
||||
buildDefaultEnvVars(
|
||||
edgeKey,
|
||||
allowSelfSignedCerts,
|
||||
edgeIdScript ? '$Env:PORTAINER_EDGE_ID' : edgeId,
|
||||
agentSecret
|
||||
)
|
||||
);
|
||||
|
||||
return `${
|
||||
edgeIdScript ? `$Env:PORTAINER_EDGE_ID = "@(${edgeIdScript})" \n\n` : ''
|
||||
}
|
||||
docker run -d \\
|
||||
--mount type=npipe,src=\\\\.\\pipe\\docker_engine,dst=\\\\.\\pipe\\docker_engine \\
|
||||
--mount type=bind,src=C:\\ProgramData\\docker\\volumes,dst=C:\\ProgramData\\docker\\volumes \\
|
||||
--mount type=volume,src=portainer_agent_data,dst=C:\\data \\
|
||||
--restart always \\
|
||||
${env} \\
|
||||
--name portainer_edge_agent \\
|
||||
portainer/agent:${agentVersion}
|
||||
`;
|
||||
}
|
||||
|
||||
function buildLinuxSwarmCommand(
|
||||
agentVersion: string,
|
||||
edgeIdScript: string,
|
||||
edgeKey: string,
|
||||
allowSelfSignedCerts: boolean,
|
||||
envVars: string,
|
||||
edgeId?: string,
|
||||
agentSecret?: string
|
||||
) {
|
||||
const env = buildDockerEnvVars(envVars, [
|
||||
...buildDefaultEnvVars(
|
||||
edgeKey,
|
||||
allowSelfSignedCerts,
|
||||
!edgeIdScript ? edgeId : undefined,
|
||||
agentSecret
|
||||
),
|
||||
'AGENT_CLUSTER_ADDR=tasks.portainer_edge_agent',
|
||||
]);
|
||||
|
||||
return `${edgeIdScript ? `PORTAINER_EDGE_ID=$(${edgeIdScript}) \n\n` : ''}
|
||||
docker network create \\
|
||||
--driver overlay \\
|
||||
portainer_agent_network;
|
||||
|
||||
docker service create \\
|
||||
--name portainer_edge_agent \\
|
||||
--network portainer_agent_network \\
|
||||
${env} \\
|
||||
--mode global \\
|
||||
--constraint 'node.platform.os == linux' \\
|
||||
--mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \\
|
||||
--mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes \\
|
||||
--mount type=bind,src=//,dst=/host \\
|
||||
--mount type=volume,src=portainer_agent_data,dst=/data \\
|
||||
portainer/agent:${agentVersion}
|
||||
`;
|
||||
}
|
||||
|
||||
function buildWindowsSwarmCommand(
|
||||
agentVersion: string,
|
||||
edgeIdScript: string,
|
||||
edgeKey: string,
|
||||
allowSelfSignedCerts: boolean,
|
||||
envVars: string,
|
||||
edgeId?: string,
|
||||
agentSecret?: string
|
||||
) {
|
||||
const env = buildDockerEnvVars(envVars, [
|
||||
...buildDefaultEnvVars(
|
||||
edgeKey,
|
||||
allowSelfSignedCerts,
|
||||
edgeIdScript ? '$Env:PORTAINER_EDGE_ID' : edgeId,
|
||||
agentSecret
|
||||
),
|
||||
'AGENT_CLUSTER_ADDR=tasks.portainer_edge_agent',
|
||||
]);
|
||||
|
||||
return `${
|
||||
edgeIdScript ? `$Env:PORTAINER_EDGE_ID = "@(${edgeIdScript})" \n\n` : ''
|
||||
}
|
||||
docker network create \\
|
||||
--driver overlay \\
|
||||
portainer_agent_network;
|
||||
|
||||
docker service create \\
|
||||
--name portainer_edge_agent \\
|
||||
--network portainer_agent_network \\
|
||||
${env} \\
|
||||
--mode global \\
|
||||
--constraint 'node.platform.os == windows' \\
|
||||
--mount type=npipe,src=\\\\.\\pipe\\docker_engine,dst=\\\\.\\pipe\\docker_engine \\
|
||||
--mount type=bind,src=C:\\ProgramData\\docker\\volumes,dst=C:\\ProgramData\\docker\\volumes \\
|
||||
--mount type=volume,src=portainer_agent_data,dst=C:\\data \\
|
||||
portainer/agent:${agentVersion}
|
||||
`;
|
||||
}
|
||||
|
||||
function buildKubernetesCommand(
|
||||
agentVersion: string,
|
||||
edgeIdScript: string,
|
||||
edgeKey: string,
|
||||
allowSelfSignedCerts: boolean,
|
||||
_envVars: string,
|
||||
edgeId?: string,
|
||||
agentSecret = ''
|
||||
) {
|
||||
const agentShortVersion = getAgentShortVersion(agentVersion);
|
||||
const idEnvVar = edgeIdScript
|
||||
? `PORTAINER_EDGE_ID=$(${edgeIdScript}) \n\n`
|
||||
: '';
|
||||
|
||||
return `${idEnvVar}curl https://downloads.portainer.io/portainer-ee${agentShortVersion}-edge-agent-setup.sh |
|
||||
bash -s -- "${
|
||||
!edgeIdScript && edgeId ? edgeId : '$PORTAINER_EDGE_ID'
|
||||
}" "${edgeKey}" "${allowSelfSignedCerts ? '1' : '0'}" "${agentSecret}"`;
|
||||
}
|
||||
|
||||
function buildDefaultEnvVars(
|
||||
edgeKey: string,
|
||||
allowSelfSignedCerts: boolean,
|
||||
edgeId = '$PORTAINER_EDGE_ID',
|
||||
agentSecret = ''
|
||||
) {
|
||||
return _.compact([
|
||||
'EDGE=1',
|
||||
`EDGE_ID=${edgeId}`,
|
||||
`EDGE_KEY=${edgeKey}`,
|
||||
`EDGE_INSECURE_POLL=${allowSelfSignedCerts ? 1 : 0}`,
|
||||
agentSecret ? `AGENT_SECRET=${agentSecret}` : ``,
|
||||
]);
|
||||
}
|
1
app/edge/components/EdgeScriptForm/index.ts
Normal file
1
app/edge/components/EdgeScriptForm/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { EdgeScriptForm, EdgeScriptFormAngular } from './EdgeScriptForm';
|
10
app/edge/components/EdgeScriptForm/types.ts
Normal file
10
app/edge/components/EdgeScriptForm/types.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
export type Platform = 'standalone' | 'swarm' | 'k8s';
|
||||
export type OS = 'win' | 'linux';
|
||||
|
||||
export interface EdgeProperties {
|
||||
os: OS;
|
||||
allowSelfSignedCertificates: boolean;
|
||||
envVars: string;
|
||||
edgeIdGenerator: string;
|
||||
platform: Platform;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue