mirror of https://github.com/portainer/portainer
feat(edge): create edge device with wizard [EE-3096] (#7029)
parent
d574a71cb1
commit
762c664948
|
@ -21,6 +21,11 @@ interface Props {
|
|||
showWaitingRoomLink: boolean;
|
||||
}
|
||||
|
||||
enum DeployType {
|
||||
FDO = 'FDO',
|
||||
MANUAL = 'MANUAL',
|
||||
}
|
||||
|
||||
export function EdgeDevicesDatatableActions({
|
||||
selectedItems,
|
||||
isOpenAMTEnabled,
|
||||
|
@ -105,38 +110,37 @@ export function EdgeDevicesDatatableActions({
|
|||
}
|
||||
|
||||
async function onAddNewDeviceClick() {
|
||||
if (!isFDOEnabled) {
|
||||
router.stateService.go('portainer.endpoints.newEdgeDevice');
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await promptAsync({
|
||||
title: 'How would you like to add an Edge Device?',
|
||||
inputType: 'radio',
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Provision bare-metal using Intel FDO',
|
||||
value: 'FDO',
|
||||
},
|
||||
{
|
||||
text: 'Deploy agent manually',
|
||||
value: 'MANUAL',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Confirm',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
});
|
||||
const result = isFDOEnabled
|
||||
? await promptAsync({
|
||||
title: 'How would you like to add an Edge Device?',
|
||||
inputType: 'radio',
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Provision bare-metal using Intel FDO',
|
||||
value: DeployType.FDO,
|
||||
},
|
||||
{
|
||||
text: 'Deploy agent manually',
|
||||
value: DeployType.MANUAL,
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Confirm',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
})
|
||||
: DeployType.MANUAL;
|
||||
|
||||
switch (result) {
|
||||
case 'FDO':
|
||||
case DeployType.FDO:
|
||||
router.stateService.go('portainer.endpoints.importDevice');
|
||||
break;
|
||||
case 'MANUAL':
|
||||
router.stateService.go('portainer.endpoints.newEdgeDevice');
|
||||
case DeployType.MANUAL:
|
||||
router.stateService.go('portainer.wizard.endpoints', {
|
||||
edgeDevice: true,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -188,20 +188,6 @@ angular
|
|||
},
|
||||
};
|
||||
|
||||
var edgeDeviceCreation = {
|
||||
name: 'portainer.endpoints.newEdgeDevice',
|
||||
url: '/newEdgeDevice',
|
||||
params: {
|
||||
isEdgeDevice: true,
|
||||
},
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/endpoints/create/createendpoint.html',
|
||||
controller: 'CreateEndpointController',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var deviceImport = {
|
||||
name: 'portainer.endpoints.importDevice',
|
||||
url: '/device',
|
||||
|
@ -471,7 +457,6 @@ angular
|
|||
$stateRegistryProvider.register(endpoint);
|
||||
$stateRegistryProvider.register(endpointAccess);
|
||||
$stateRegistryProvider.register(endpointKVM);
|
||||
$stateRegistryProvider.register(edgeDeviceCreation);
|
||||
$stateRegistryProvider.register(deviceImport);
|
||||
$stateRegistryProvider.register(addFDOProfile);
|
||||
$stateRegistryProvider.register(editFDOProfile);
|
||||
|
|
|
@ -29,6 +29,19 @@ function config($stateRegistryProvider: StateRegistry) {
|
|||
},
|
||||
});
|
||||
|
||||
$stateRegistryProvider.register({
|
||||
name: 'portainer.wizard.endpoints',
|
||||
url: '/endpoints?edgeDevice',
|
||||
views: {
|
||||
'content@': {
|
||||
component: 'wizardEnvironmentTypeSelectView',
|
||||
},
|
||||
},
|
||||
params: {
|
||||
localEndpointId: 0,
|
||||
},
|
||||
});
|
||||
|
||||
$stateRegistryProvider.register({
|
||||
name: 'portainer.wizard.endpoints.create',
|
||||
url: '/create?envType',
|
||||
|
@ -41,17 +54,4 @@ function config($stateRegistryProvider: StateRegistry) {
|
|||
envType: '',
|
||||
},
|
||||
});
|
||||
|
||||
$stateRegistryProvider.register({
|
||||
name: 'portainer.wizard.endpoints',
|
||||
url: '/endpoints',
|
||||
views: {
|
||||
'content@': {
|
||||
component: 'wizardEnvironmentTypeSelectView',
|
||||
},
|
||||
},
|
||||
params: {
|
||||
localEndpointId: 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,357 +0,0 @@
|
|||
import { PortainerEndpointCreationTypes, PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
|
||||
import { getAgentShortVersion } from 'Portainer/views/endpoints/helpers';
|
||||
import { baseHref } from '@/portainer/helpers/pathHelper';
|
||||
import { EndpointSecurityFormData } from '../../../components/endpointSecurity/porEndpointSecurityModel';
|
||||
|
||||
angular
|
||||
.module('portainer.app')
|
||||
.controller(
|
||||
'CreateEndpointController',
|
||||
function CreateEndpointController(
|
||||
$async,
|
||||
$analytics,
|
||||
$q,
|
||||
$scope,
|
||||
$state,
|
||||
$filter,
|
||||
clipboard,
|
||||
EndpointService,
|
||||
GroupService,
|
||||
SettingsService,
|
||||
Notifications,
|
||||
Authentication,
|
||||
StateManager
|
||||
) {
|
||||
$scope.onChangeCheckInInterval = onChangeCheckInInterval;
|
||||
$scope.setFieldValue = setFieldValue;
|
||||
|
||||
$scope.state = {
|
||||
EnvironmentType: $state.params.isEdgeDevice ? 'edge_agent' : 'agent',
|
||||
PlatformType: 'linux',
|
||||
actionInProgress: false,
|
||||
deploymentTab: 0,
|
||||
allowCreateTag: Authentication.isAdmin(),
|
||||
isEdgeDevice: $state.params.isEdgeDevice,
|
||||
};
|
||||
|
||||
const agentVersion = StateManager.getState().application.version;
|
||||
const agentShortVersion = getAgentShortVersion(agentVersion);
|
||||
$scope.agentSecret = '';
|
||||
|
||||
$scope.deployCommands = {
|
||||
kubeLoadBalancer: `curl -L https://downloads.portainer.io/ce${agentShortVersion}/portainer-agent-k8s-lb.yaml -o portainer-agent-k8s.yaml; kubectl apply -f portainer-agent-k8s.yaml`,
|
||||
kubeNodePort: `curl -L https://downloads.portainer.io/ce${agentShortVersion}/portainer-agent-k8s-nodeport.yaml -o portainer-agent-k8s.yaml; kubectl apply -f portainer-agent-k8s.yaml`,
|
||||
agentLinux: agentLinuxSwarmCommand,
|
||||
agentWindows: agentWindowsSwarmCommand,
|
||||
};
|
||||
|
||||
$scope.formValues = {
|
||||
Name: '',
|
||||
URL: '',
|
||||
PublicURL: '',
|
||||
GroupId: 1,
|
||||
SecurityFormData: new EndpointSecurityFormData(),
|
||||
AzureApplicationId: '',
|
||||
AzureTenantId: '',
|
||||
AzureAuthenticationKey: '',
|
||||
TagIds: [],
|
||||
CheckinInterval: 0,
|
||||
};
|
||||
|
||||
$scope.copyAgentCommand = function () {
|
||||
let command = '';
|
||||
if ($scope.state.deploymentTab === 2 && $scope.state.PlatformType === 'linux') {
|
||||
command = $scope.deployCommands.agentLinux($scope.agentSecret);
|
||||
} else if ($scope.state.deploymentTab === 2 && $scope.state.PlatformType === 'windows') {
|
||||
command = $scope.deployCommands.agentWindows($scope.agentSecret);
|
||||
} else if ($scope.state.deploymentTab === 1) {
|
||||
command = $scope.deployCommands.kubeNodePort;
|
||||
} else {
|
||||
command = $scope.deployCommands.kubeLoadBalancer;
|
||||
}
|
||||
clipboard.copyText(command.trim());
|
||||
$('#copyNotification').show().fadeOut(2500);
|
||||
};
|
||||
|
||||
$scope.setDefaultPortainerInstanceURL = function () {
|
||||
let url;
|
||||
|
||||
if (window.location.origin.startsWith('http')) {
|
||||
const path = baseHref() !== '/' ? path : '';
|
||||
url = `${window.location.origin}${path}`;
|
||||
} else {
|
||||
url = baseHref().replace(/\/$/, '');
|
||||
}
|
||||
|
||||
$scope.formValues.URL = url;
|
||||
};
|
||||
|
||||
$scope.resetEndpointURL = function () {
|
||||
$scope.formValues.URL = '';
|
||||
};
|
||||
|
||||
$scope.onChangeTags = function onChangeTags(value) {
|
||||
return $scope.$evalAsync(() => {
|
||||
$scope.formValues.TagIds = value;
|
||||
});
|
||||
};
|
||||
|
||||
function onChangeCheckInInterval(value) {
|
||||
setFieldValue('EdgeCheckinInterval', value);
|
||||
}
|
||||
|
||||
function setFieldValue(name, value) {
|
||||
return $scope.$evalAsync(() => {
|
||||
$scope.formValues = {
|
||||
...$scope.formValues,
|
||||
[name]: value,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
$scope.addDockerEndpoint = function () {
|
||||
var name = $scope.formValues.Name;
|
||||
var URL = $filter('stripprotocol')($scope.formValues.URL);
|
||||
var publicURL = $scope.formValues.PublicURL;
|
||||
var groupId = $scope.formValues.GroupId;
|
||||
var tagIds = $scope.formValues.TagIds;
|
||||
|
||||
if ($scope.formValues.ConnectSocket) {
|
||||
URL = $scope.formValues.SocketPath;
|
||||
$scope.state.actionInProgress = true;
|
||||
EndpointService.createLocalEndpoint(name, URL, publicURL, groupId, tagIds)
|
||||
.then(function success() {
|
||||
Notifications.success('Environment created', name);
|
||||
$state.go('portainer.endpoints', {}, { reload: true });
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to create environment');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
} else {
|
||||
if (publicURL === '') {
|
||||
publicURL = URL.split(':')[0];
|
||||
}
|
||||
|
||||
var securityData = $scope.formValues.SecurityFormData;
|
||||
var TLS = securityData.TLS;
|
||||
var TLSMode = securityData.TLSMode;
|
||||
var TLSSkipVerify = TLS && (TLSMode === 'tls_client_noca' || TLSMode === 'tls_only');
|
||||
var TLSSkipClientVerify = TLS && (TLSMode === 'tls_ca' || TLSMode === 'tls_only');
|
||||
var TLSCAFile = TLSSkipVerify ? null : securityData.TLSCACert;
|
||||
var TLSCertFile = TLSSkipClientVerify ? null : securityData.TLSCert;
|
||||
var TLSKeyFile = TLSSkipClientVerify ? null : securityData.TLSKey;
|
||||
|
||||
addEndpoint(
|
||||
name,
|
||||
PortainerEndpointCreationTypes.LocalDockerEnvironment,
|
||||
URL,
|
||||
publicURL,
|
||||
groupId,
|
||||
tagIds,
|
||||
TLS,
|
||||
TLSSkipVerify,
|
||||
TLSSkipClientVerify,
|
||||
TLSCAFile,
|
||||
TLSCertFile,
|
||||
TLSKeyFile
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.addKubernetesEndpoint = function () {
|
||||
var name = $scope.formValues.Name;
|
||||
var tagIds = $scope.formValues.TagIds;
|
||||
$scope.state.actionInProgress = true;
|
||||
EndpointService.createLocalKubernetesEndpoint(name, tagIds)
|
||||
.then(function success(result) {
|
||||
Notifications.success('Environment created', name);
|
||||
$state.go('kubernetes.cluster.setup', { endpoinId: result.Id });
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to create environment');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addAgentEndpoint = addAgentEndpoint;
|
||||
async function addAgentEndpoint() {
|
||||
return $async(async () => {
|
||||
const name = $scope.formValues.Name;
|
||||
const URL = $scope.formValues.URL;
|
||||
const publicURL = $scope.formValues.PublicURL === '' ? URL.split(':')[0] : $scope.formValues.PublicURL;
|
||||
const groupId = $scope.formValues.GroupId;
|
||||
const tagIds = $scope.formValues.TagIds;
|
||||
|
||||
const endpoint = await addEndpoint(name, PortainerEndpointCreationTypes.AgentEnvironment, URL, publicURL, groupId, tagIds, true, true, true, null, null, null);
|
||||
$analytics.eventTrack('portainer-endpoint-creation', { category: 'portainer', metadata: { type: 'agent', platform: platformLabel(endpoint.Type) } });
|
||||
});
|
||||
|
||||
function platformLabel(type) {
|
||||
switch (type) {
|
||||
case PortainerEndpointTypes.DockerEnvironment:
|
||||
case PortainerEndpointTypes.AgentOnDockerEnvironment:
|
||||
case PortainerEndpointTypes.EdgeAgentOnDockerEnvironment:
|
||||
return 'docker';
|
||||
case PortainerEndpointTypes.KubernetesLocalEnvironment:
|
||||
case PortainerEndpointTypes.AgentOnKubernetesEnvironment:
|
||||
case PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment:
|
||||
return 'kubernetes';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.addEdgeAgentEndpoint = function () {
|
||||
var name = $scope.formValues.Name;
|
||||
var groupId = $scope.formValues.GroupId;
|
||||
var tagIds = $scope.formValues.TagIds;
|
||||
var URL = $scope.formValues.URL;
|
||||
|
||||
addEndpoint(name, PortainerEndpointCreationTypes.EdgeAgentEnvironment, URL, '', groupId, tagIds, false, false, false, null, null, null, $scope.formValues.CheckinInterval);
|
||||
};
|
||||
|
||||
$scope.addAzureEndpoint = function () {
|
||||
var name = $scope.formValues.Name;
|
||||
var applicationId = $scope.formValues.AzureApplicationId;
|
||||
var tenantId = $scope.formValues.AzureTenantId;
|
||||
var authenticationKey = $scope.formValues.AzureAuthenticationKey;
|
||||
var groupId = $scope.formValues.GroupId;
|
||||
var tagIds = $scope.formValues.TagIds;
|
||||
|
||||
createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds);
|
||||
};
|
||||
|
||||
function createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds) {
|
||||
$scope.state.actionInProgress = true;
|
||||
EndpointService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds)
|
||||
.then(function success() {
|
||||
Notifications.success('Environment created', name);
|
||||
$state.go('portainer.endpoints', {}, { reload: true });
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to create environment');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
async function addEndpoint(
|
||||
name,
|
||||
creationType,
|
||||
URL,
|
||||
PublicURL,
|
||||
groupId,
|
||||
tagIds,
|
||||
TLS,
|
||||
TLSSkipVerify,
|
||||
TLSSkipClientVerify,
|
||||
TLSCAFile,
|
||||
TLSCertFile,
|
||||
TLSKeyFile,
|
||||
CheckinInterval
|
||||
) {
|
||||
return $async(async () => {
|
||||
$scope.state.actionInProgress = true;
|
||||
try {
|
||||
const endpoint = await EndpointService.createRemoteEndpoint(
|
||||
name,
|
||||
creationType,
|
||||
URL,
|
||||
PublicURL,
|
||||
groupId,
|
||||
tagIds,
|
||||
TLS,
|
||||
TLSSkipVerify,
|
||||
TLSSkipClientVerify,
|
||||
TLSCAFile,
|
||||
TLSCertFile,
|
||||
TLSKeyFile,
|
||||
CheckinInterval,
|
||||
$scope.state.isEdgeDevice
|
||||
);
|
||||
|
||||
Notifications.success('Environment created', name);
|
||||
switch (endpoint.Type) {
|
||||
case PortainerEndpointTypes.EdgeAgentOnDockerEnvironment:
|
||||
case PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment:
|
||||
$state.go('portainer.endpoints.endpoint', { id: endpoint.Id });
|
||||
break;
|
||||
case PortainerEndpointTypes.AgentOnKubernetesEnvironment:
|
||||
$state.go('kubernetes.cluster.setup', { endpoinId: endpoint.Id });
|
||||
break;
|
||||
default:
|
||||
$state.go('portainer.endpoints', {}, { reload: true });
|
||||
break;
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
} catch (err) {
|
||||
Notifications.error('Failure', err, 'Unable to create environment');
|
||||
} finally {
|
||||
$scope.state.actionInProgress = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$q.all({
|
||||
groups: GroupService.groups(),
|
||||
settings: SettingsService.settings(),
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.groups = data.groups;
|
||||
|
||||
const settings = data.settings;
|
||||
|
||||
$scope.agentSecret = settings.AgentSecret;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to load groups');
|
||||
});
|
||||
}
|
||||
|
||||
function agentLinuxSwarmCommand(agentSecret) {
|
||||
let secret = agentSecret == '' ? '' : `\\\n -e AGENT_SECRET=${agentSecret} `;
|
||||
return `
|
||||
docker network create \\
|
||||
--driver overlay \\
|
||||
portainer_agent_network
|
||||
|
||||
docker service create \\
|
||||
--name portainer_agent \\
|
||||
--network portainer_agent_network \\
|
||||
-p 9001:9001/tcp ${secret}\\
|
||||
--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 \\
|
||||
portainer/agent:${agentVersion}
|
||||
`;
|
||||
}
|
||||
|
||||
function agentWindowsSwarmCommand(agentSecret) {
|
||||
let secret = agentSecret == '' ? '' : `\\\n -e AGENT_SECRET=${agentSecret} `;
|
||||
return `
|
||||
docker network create \\
|
||||
--driver overlay \\
|
||||
portainer_agent_network && \\
|
||||
docker service create \\
|
||||
--name portainer_agent \\
|
||||
--network portainer_agent_network \\
|
||||
-p 9001:9001/tcp ${secret}\\
|
||||
--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 \\
|
||||
portainer/agent:${agentVersion}
|
||||
`;
|
||||
}
|
||||
|
||||
initView();
|
||||
}
|
||||
);
|
|
@ -1,521 +0,0 @@
|
|||
<page-header title="'Create environment'" breadcrumbs="[{label:'Environments', link:'portainer.endpoints'}, 'Add environment']"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" name="endpointCreationForm">
|
||||
<div class="col-sm-12 form-section-title"> Environment type </div>
|
||||
<div class="form-group"></div>
|
||||
<div class="form-group" style="margin-bottom: 0">
|
||||
<div class="boxselector_wrapper">
|
||||
<div ng-show="!state.isEdgeDevice" ng-click="resetEndpointURL()">
|
||||
<input type="radio" id="agent_endpoint" ng-model="state.EnvironmentType" value="agent" />
|
||||
<label for="agent_endpoint" data-cy="endpointCreate-agentSelectButton">
|
||||
<div class="boxselector_header">
|
||||
<i class="fa fa-bolt" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
Agent
|
||||
</div>
|
||||
<p>Portainer agent</p>
|
||||
</label>
|
||||
</div>
|
||||
<div ng-click="setDefaultPortainerInstanceURL()">
|
||||
<input type="radio" id="edge_agent_endpoint" ng-model="state.EnvironmentType" value="edge_agent" />
|
||||
<label for="edge_agent_endpoint" data-cy="endpointCreate-edgeAgentSelectButton">
|
||||
<div class="boxselector_header vertical-center">
|
||||
<pr-icon icon="'cloud'" feather="true"></pr-icon>
|
||||
Edge Agent
|
||||
</div>
|
||||
<p>Portainer Edge agent</p>
|
||||
</label>
|
||||
</div>
|
||||
<div ng-show="!state.isEdgeDevice" ng-click="resetEndpointURL()">
|
||||
<input type="radio" id="docker_endpoint" ng-model="state.EnvironmentType" value="docker" />
|
||||
<label for="docker_endpoint" data-cy="endpointCreate-dockerSelectButton">
|
||||
<div class="boxselector_header">
|
||||
<i class="fab fa-docker" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
Docker
|
||||
</div>
|
||||
<p>Directly connect to the Docker API</p>
|
||||
</label>
|
||||
</div>
|
||||
<div ng-show="!state.isEdgeDevice" ng-click="resetEndpointURL()">
|
||||
<input type="radio" id="kubernetes_endpoint" ng-model="state.EnvironmentType" value="kubernetes" />
|
||||
<label for="kubernetes_endpoint" data-cy="endpointCreate-kubeSelectButton">
|
||||
<div class="boxselector_header">
|
||||
<i class="fas fa-dharmachakra" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
Kubernetes
|
||||
</div>
|
||||
<p>Local Kubernetes environment</p>
|
||||
</label>
|
||||
</div>
|
||||
<div ng-show="!state.isEdgeDevice">
|
||||
<input type="radio" id="azure_endpoint" ng-model="state.EnvironmentType" value="azure" />
|
||||
<label for="azure_endpoint" data-cy="endpointCreate-azureSelectButton">
|
||||
<div class="boxselector_header">
|
||||
<i class="fab fa-microsoft" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
Azure
|
||||
</div>
|
||||
<p>Connect to Microsoft Azure ACI</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="state.EnvironmentType === 'docker'">
|
||||
<div class="col-sm-12 form-section-title"> Important notice </div>
|
||||
<div class="form-group">
|
||||
<p class="col-sm-12 text-muted small">
|
||||
You can connect Portainer to a Docker environment via socket or via TCP. You can find more information about how to expose the Docker API over TCP
|
||||
<a href="https://docs.docker.com/engine/security/https/"> in the Docker documentation</a>.
|
||||
</p>
|
||||
|
||||
<p class="col-sm-12 text-muted small">
|
||||
When using the socket, ensure that you have started the Portainer container with the following Docker flag
|
||||
<code> -v "/var/run/docker.sock:/var/run/docker.sock" </code>
|
||||
(on Linux) or
|
||||
<code> -v \.\pipe\docker_engine:\.\pipe\docker_engine </code>
|
||||
(on Windows).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="state.EnvironmentType === 'agent'">
|
||||
<div class="col-sm-12 form-section-title"> Information </div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
Ensure that you have deployed the Portainer agent in your cluster first. Refer to the platform related command below to deploy it.
|
||||
<div class="input-group input-group-sm" style="margin-top: 10px; margin-bottom: 10px">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<label class="btn btn-primary" ng-model="state.PlatformType" uib-btn-radio="'linux'"><i class="fab fa-linux" style="margin-right: 2px"></i> Linux</label>
|
||||
<label class="btn btn-primary" ng-model="state.PlatformType" uib-btn-radio="'windows'"><i class="fab fa-windows" style="margin-right: 2px"></i> Windows</label>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 10px">
|
||||
<uib-tabset active="state.deploymentTab">
|
||||
<uib-tab index="0" ng-if="state.PlatformType === 'linux'" heading="Kubernetes via load balancer">
|
||||
<p ng-if="agentSecret != ''" style="margin-top: 16px; margin-bottom: 16px">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
Note that the environment variable AGENT_SECRET will need to be set to <code>{{ agentSecret }}</code
|
||||
>. Please update the manifest that will be downloaded from the following script.
|
||||
</p>
|
||||
<code style="display: block; padding: 16px 45px">{{ deployCommands.kubeLoadBalancer }}</code>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab index="1" ng-if="state.PlatformType === 'linux'" heading="Kubernetes via node port">
|
||||
<p ng-if="agentSecret != ''" style="margin-top: 16px; margin-bottom: 16px">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
Note that the environment variable AGENT_SECRET will need to be set to <code>{{ agentSecret }}</code
|
||||
>. Please update the manifest that will be downloaded from the following script.
|
||||
</p>
|
||||
<code style="display: block; padding: 16px 45px">{{ deployCommands.kubeNodePort }}</code>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab index="2" heading="Docker Swarm">
|
||||
<code ng-if="state.PlatformType === 'linux'" style="display: block; white-space: pre-wrap; padding: 16px 45px">{{
|
||||
deployCommands.agentLinux(agentSecret)
|
||||
}}</code>
|
||||
<code ng-if="state.PlatformType === 'windows'" style="display: block; white-space: pre-wrap; padding: 16px 45px">{{
|
||||
deployCommands.agentWindows(agentSecret)
|
||||
}}</code>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
<div style="margin-top: 10px">
|
||||
<span class="btn btn-primary btn-sm space-left" ng-click="copyAgentCommand()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy command</span>
|
||||
<span>
|
||||
<i id="copyNotification" class="fa fa-check green-icon" aria-hidden="true" style="margin-left: 7px; display: none"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="state.EnvironmentType === 'edge_agent'">
|
||||
<div class="col-sm-12 form-section-title"> Information </div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
<p>
|
||||
Allows you to create an environment that can be registered with an Edge agent. The Edge agent will initiate the communications with the Portainer instance. All
|
||||
the required information on how to connect an Edge agent to this environment will be available after environment creation.
|
||||
</p>
|
||||
<p> You can read more about the Edge agent in the userguide available <a href="https://downloads.portainer.io/edge_agent_guide.pdf">here.</a> </p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="state.EnvironmentType === 'kubernetes'">
|
||||
<div class="col-sm-12 form-section-title"> Important notice </div>
|
||||
<div class="form-group">
|
||||
<p class="col-sm-12 text-muted small"> This will allow you to manage the Kubernetes environment where Portainer is running. </p>
|
||||
|
||||
<p class="col-sm-12 text-muted small">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
In order to manage a remote Kubernetes environment, please use the Agent or Edge agent options.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="state.EnvironmentType === 'azure'">
|
||||
<div class="col-sm-12 form-section-title"> Information </div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-muted"> <i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px"></i> This feature is experimental. </p>
|
||||
<p class="text-primary"> Connect to Microsoft Azure to manage Azure Container Instances (ACI). </p>
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
Have a look at
|
||||
<a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal" target="_blank"
|
||||
>the Azure documentation</a
|
||||
>
|
||||
to retrieve the credentials required below.
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title"> Environment details </div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="container_name" class="col-sm-3 col-lg-2 control-label text-left">Name</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="container_name"
|
||||
ng-model="formValues.Name"
|
||||
placeholder="e.g. docker-prod01 / kubernetes-cluster01"
|
||||
required
|
||||
auto-focus
|
||||
data-cy="endpointCreate-nameInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="endpointCreationForm.container_name.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="endpointCreationForm.container_name.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- connect-via-socket-input -->
|
||||
<div ng-if="state.EnvironmentType === 'docker'">
|
||||
<div class="form-group" style="padding-left: 15px">
|
||||
<label for="connect_socket" class="col-sm_12 control-label text-left"> Connect via socket </label>
|
||||
<label class="switch" style="margin-left: 20px">
|
||||
<input type="checkbox" ng-model="formValues.ConnectSocket" /><i data-cy="endpointCreate-connectSocketSwitch"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="state.EnvironmentType === 'docker' && formValues.ConnectSocket">
|
||||
<div class="form-group" style="padding-left: 15px">
|
||||
<label for="override_socket" class="col-sm_12 control-label text-left"> Override default socket path </label>
|
||||
<label class="switch" style="margin-left: 20px">
|
||||
<input type="checkbox" ng-model="formValues.OverrideSocket" /><i data-cy="endpointCreate-overrideSocketSwitch"></i>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div ng-if="formValues.OverrideSocket">
|
||||
<div class="form-group">
|
||||
<label for="socket_path" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Socket path
|
||||
<portainer-tooltip message="'Path to the Docker socket. Remember to bind-mount the socket, see the important notice above for more information.'">
|
||||
</portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="socket_path"
|
||||
ng-model="formValues.SocketPath"
|
||||
placeholder="e.g. /var/run/docker.sock (on Linux) or //./pipe/docker_engine (on Windows)"
|
||||
required
|
||||
data-cy="endpointCreate-socketPathInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="endpointCreationForm.socket_path.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="endpointCreationForm.socket_path.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !connect-via-socket-input -->
|
||||
<!-- endpoint-url-input -->
|
||||
<div ng-if="(state.EnvironmentType === 'docker' && !formValues.ConnectSocket) || state.EnvironmentType === 'agent'">
|
||||
<div class="form-group">
|
||||
<label for="endpoint_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Environment URL
|
||||
<portainer-tooltip
|
||||
message="'URL or IP address of a Docker host. The Docker API must be exposed over a TCP port. Please refer to the Docker documentation to configure it.'"
|
||||
>
|
||||
</portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
ng-if="state.EnvironmentType === 'docker'"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="endpoint_url"
|
||||
ng-model="formValues.URL"
|
||||
placeholder="e.g. 10.0.0.10:2375 or mydocker.mydomain.com:2375"
|
||||
required
|
||||
data-cy="endpointCreate-endpointUrlDockerInput"
|
||||
/>
|
||||
<input
|
||||
ng-if="state.EnvironmentType === 'agent'"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="endpoint_url"
|
||||
ng-model="formValues.URL"
|
||||
placeholder="e.g. 10.0.0.10:9001 or tasks.portainer_agent:9001"
|
||||
required
|
||||
data-cy="endpointCreate-endpointUrlAgentInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="endpointCreationForm.endpoint_url.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="endpointCreationForm.endpoint_url.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-url-input -->
|
||||
<!-- portainer-instance-input -->
|
||||
<div ng-if="state.EnvironmentType === 'edge_agent'">
|
||||
<div class="form-group">
|
||||
<label for="endpoint_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Portainer server URL
|
||||
<portainer-tooltip message="'URL of the Portainer instance that the agent will use to initiate the communications.'"></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="endpoint_url"
|
||||
ng-model="formValues.URL"
|
||||
placeholder="e.g. 10.0.0.10:9443 or portainer.mydomain.com"
|
||||
required
|
||||
data-cy="endpointCreate-portainerServerUrlInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="endpointCreationForm.endpoint_url.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="endpointCreationForm.endpoint_url.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !portainer-instance-input -->
|
||||
|
||||
<div>
|
||||
<div class="col-sm-12 form-section-title"> Check-in Intervals </div>
|
||||
<edge-checkin-interval-field value="formValues.EdgeCheckinInterval" on-change="(onChangeCheckInInterval)"></edge-checkin-interval-field>
|
||||
</div>
|
||||
</div>
|
||||
<!-- endpoint-public-url-input -->
|
||||
<div ng-if="state.EnvironmentType === 'docker' || state.EnvironmentType === 'agent'">
|
||||
<div class="form-group">
|
||||
<label for="endpoint_public_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Public IP
|
||||
<portainer-tooltip message="'URL or IP address where exposed containers will be reachable. This field is optional and will default to the environment URL.'">
|
||||
</portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="endpoint_public_url"
|
||||
ng-model="formValues.PublicURL"
|
||||
placeholder="e.g. 10.0.0.10 or mydocker.mydomain.com"
|
||||
data-cy="endpointCreate-publicIpInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-public-url-input -->
|
||||
<!-- azure-details -->
|
||||
<div ng-if="state.EnvironmentType === 'azure'">
|
||||
<!-- applicationId-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_appid" class="col-sm-3 col-lg-2 control-label text-left">Application ID</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="azure_credential_appid"
|
||||
ng-model="formValues.AzureApplicationId"
|
||||
placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
required
|
||||
data-cy="endpointCreate-azureAppIdInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="endpointCreationForm.azure_credential_appid.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="endpointCreationForm.azure_credential_appid.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !applicationId-input -->
|
||||
<!-- tenantId-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_tenantid" class="col-sm-3 col-lg-2 control-label text-left">Tenant ID</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="azure_credential_tenantid"
|
||||
ng-model="formValues.AzureTenantId"
|
||||
placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
required
|
||||
data-cy="endpointCreate-azureTenantIdInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="endpointCreationForm.azure_credential_tenantid.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="endpointCreationForm.azure_credential_tenantid.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !tenantId-input -->
|
||||
<!-- authenticationkey-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_authkey" class="col-sm-3 col-lg-2 control-label text-left">Authentication key</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="azure_credential_authkey"
|
||||
ng-model="formValues.AzureAuthenticationKey"
|
||||
placeholder="cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="
|
||||
required
|
||||
data-cy="endpointCreate-azureAuthKeyInput"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="endpointCreationForm.azure_credential_authkey.$invalid">
|
||||
<div class="col-sm-12 small text-warning">
|
||||
<div ng-messages="endpointCreationForm.azure_credential_authkey.$error">
|
||||
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !authenticationkey-input -->
|
||||
</div>
|
||||
<!-- !azure-details -->
|
||||
<!-- endpoint-security -->
|
||||
<por-endpoint-security ng-if="state.EnvironmentType === 'docker' && !formValues.ConnectSocket" form-data="formValues.SecurityFormData"></por-endpoint-security>
|
||||
<!-- !endpoint-security -->
|
||||
<div class="col-sm-12 form-section-title"> Metadata </div>
|
||||
<!-- group -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_group" class="col-sm-3 col-lg-2 control-label text-left"> Group </label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<select
|
||||
ng-options="group.Id as group.Name for group in groups"
|
||||
ng-model="formValues.GroupId"
|
||||
id="endpoint_group"
|
||||
class="form-control"
|
||||
data-cy="endpointCreate-endpointGroup"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !group -->
|
||||
|
||||
<tag-selector ng-if="formValues" value="formValues.TagIds" allow-create="state.allowCreateTag" on-change="(onChangeTags)"> </tag-selector>
|
||||
|
||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
ng-if="state.EnvironmentType === 'docker'"
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !endpointCreationForm.$valid || (formValues.TLS && ((formValues.TLSVerify && !formValues.TLSCACert) || (formValues.TLSClientCert && (!formValues.TLSCert || !formValues.TLSKey))))"
|
||||
ng-click="addDockerEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
data-cy="endpointCreate-createDockerEndpoint"
|
||||
analytics-on
|
||||
analytics-category="portainer"
|
||||
analytics-event="portainer-endpoint-creation"
|
||||
analytics-properties="{ metadata: { type: 'docker-api' } }"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add environment</span>
|
||||
<span ng-show="state.actionInProgress">Creating environment...</span>
|
||||
</button>
|
||||
<button
|
||||
ng-if="state.EnvironmentType === 'agent'"
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !endpointCreationForm.$valid"
|
||||
ng-click="addAgentEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
data-cy="endpointCreate-createAgentEndpoint"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add environment</span>
|
||||
<span ng-show="state.actionInProgress">Creating environment...</span>
|
||||
</button>
|
||||
<button
|
||||
ng-if="state.EnvironmentType === 'edge_agent'"
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !endpointCreationForm.$valid"
|
||||
ng-click="addEdgeAgentEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
data-cy="endpointCreate-createEdgeAgentEndpoint"
|
||||
analytics-on
|
||||
analytics-category="portainer"
|
||||
analytics-event="portainer-endpoint-creation"
|
||||
analytics-properties="{ metadata: { type: 'edge-agent' } }"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add environment</span>
|
||||
<span ng-show="state.actionInProgress">Creating environment...</span>
|
||||
</button>
|
||||
<button
|
||||
ng-if="state.EnvironmentType === 'kubernetes'"
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !endpointCreationForm.$valid"
|
||||
ng-click="addKubernetesEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
analytics-on
|
||||
analytics-category="portainer"
|
||||
analytics-event="portainer-endpoint-creation"
|
||||
analytics-properties="{ metadata: { type: 'kubernetes-api' } }"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add environment</span>
|
||||
<span ng-show="state.actionInProgress">Creating environment...</span>
|
||||
</button>
|
||||
<button
|
||||
ng-if="state.EnvironmentType === 'azure'"
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !endpointCreationForm.$valid"
|
||||
ng-click="addAzureEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
data-cy="endpointCreate-createAzureEndpoint"
|
||||
analytics-on
|
||||
analytics-category="portainer"
|
||||
analytics-event="portainer-endpoint-creation"
|
||||
analytics-properties="{ metadata: { type: 'azure-api' } }"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><pr-icon icon="'plus'" feather="true"></pr-icon> Add environment</span>
|
||||
<span ng-show="state.actionInProgress">Creating environment...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
|
@ -8,6 +8,8 @@ import { Button } from '@@/buttons';
|
|||
import { PageHeader } from '@@/PageHeader';
|
||||
import { Widget, WidgetBody, WidgetTitle } from '@@/Widget';
|
||||
|
||||
import { useCreateEdgeDeviceParam } from '../hooks/useCreateEdgeDeviceParam';
|
||||
|
||||
import {
|
||||
EnvironmentSelector,
|
||||
EnvironmentSelectorValue,
|
||||
|
@ -15,6 +17,8 @@ import {
|
|||
import { environmentTypes } from './environment-types';
|
||||
|
||||
export function EnvironmentTypeSelectView() {
|
||||
const createEdgeDevice = useCreateEdgeDeviceParam();
|
||||
|
||||
const [types, setTypes] = useState<EnvironmentSelectorValue[]>([]);
|
||||
const { trackEvent } = useAnalytics();
|
||||
const router = useRouter();
|
||||
|
@ -31,7 +35,11 @@ export function EnvironmentTypeSelectView() {
|
|||
<Widget>
|
||||
<WidgetTitle icon="fa-magic" title="Environment Wizard" />
|
||||
<WidgetBody>
|
||||
<EnvironmentSelector value={types} onChange={setTypes} />
|
||||
<EnvironmentSelector
|
||||
value={types}
|
||||
onChange={setTypes}
|
||||
createEdgeDevice={createEdgeDevice}
|
||||
/>
|
||||
<Button
|
||||
disabled={types.length === 0}
|
||||
onClick={() => startWizard()}
|
||||
|
@ -63,6 +71,7 @@ export function EnvironmentTypeSelectView() {
|
|||
|
||||
router.stateService.go('portainer.wizard.endpoints.create', {
|
||||
envType: types,
|
||||
...(createEdgeDevice ? { edgeDevice: createEdgeDevice } : {}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,16 @@ export type EnvironmentSelectorValue = typeof environmentTypes[number]['id'];
|
|||
interface Props {
|
||||
value: EnvironmentSelectorValue[];
|
||||
onChange(value: EnvironmentSelectorValue[]): void;
|
||||
createEdgeDevice?: boolean;
|
||||
}
|
||||
|
||||
export function EnvironmentSelector({ value, onChange }: Props) {
|
||||
const hasEdge: EnvironmentSelectorValue[] = ['docker', 'kubernetes'];
|
||||
|
||||
export function EnvironmentSelector({
|
||||
value,
|
||||
onChange,
|
||||
createEdgeDevice,
|
||||
}: Props) {
|
||||
return (
|
||||
<div className="row">
|
||||
<FormSection title="Select your environment(s)">
|
||||
|
@ -20,17 +27,19 @@ export function EnvironmentSelector({ value, onChange }: Props) {
|
|||
apply.
|
||||
</p>
|
||||
<div className="flex gap-4 flex-wrap">
|
||||
{environmentTypes.map((eType) => (
|
||||
<Option
|
||||
key={eType.id}
|
||||
featureId={eType.featureId}
|
||||
title={eType.title}
|
||||
description={eType.description}
|
||||
icon={eType.icon}
|
||||
active={value.includes(eType.id)}
|
||||
onClick={() => handleClick(eType.id)}
|
||||
/>
|
||||
))}
|
||||
{filterEdgeDevicesIfNeed(environmentTypes, createEdgeDevice).map(
|
||||
(eType) => (
|
||||
<Option
|
||||
key={eType.id}
|
||||
featureId={eType.featureId}
|
||||
title={eType.title}
|
||||
description={eType.description}
|
||||
icon={eType.icon}
|
||||
active={value.includes(eType.id)}
|
||||
onClick={() => handleClick(eType.id)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</FormSection>
|
||||
</div>
|
||||
|
@ -45,3 +54,14 @@ export function EnvironmentSelector({ value, onChange }: Props) {
|
|||
onChange([...value, eType]);
|
||||
}
|
||||
}
|
||||
|
||||
function filterEdgeDevicesIfNeed(
|
||||
types: typeof environmentTypes,
|
||||
createEdgeDevice?: boolean
|
||||
) {
|
||||
if (!createEdgeDevice) {
|
||||
return types;
|
||||
}
|
||||
|
||||
return types.filter((eType) => hasEdge.includes(eType.id));
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { BoxSelector, type BoxSelectorOption } from '@@/BoxSelector';
|
|||
|
||||
import { AnalyticsStateKey } from '../types';
|
||||
import { EdgeAgentTab } from '../shared/EdgeAgentTab';
|
||||
import { useFilterEdgeOptionsIfNeeded } from '../useOnlyEdgeOptions';
|
||||
|
||||
import { AgentTab } from './AgentTab';
|
||||
import { APITab } from './APITab';
|
||||
|
@ -16,7 +17,9 @@ interface Props {
|
|||
onCreate(environment: Environment, analytics: AnalyticsStateKey): void;
|
||||
}
|
||||
|
||||
const options: BoxSelectorOption<'agent' | 'api' | 'socket' | 'edgeAgent'>[] = [
|
||||
const defaultOptions: BoxSelectorOption<
|
||||
'agent' | 'api' | 'socket' | 'edgeAgent'
|
||||
>[] = [
|
||||
{
|
||||
id: 'agent',
|
||||
icon: 'fa fa-bolt',
|
||||
|
@ -49,6 +52,8 @@ const options: BoxSelectorOption<'agent' | 'api' | 'socket' | 'edgeAgent'>[] = [
|
|||
];
|
||||
|
||||
export function WizardDocker({ onCreate }: Props) {
|
||||
const options = useFilterEdgeOptionsIfNeeded(defaultOptions, 'edgeAgent');
|
||||
|
||||
const [creationType, setCreationType] = useState(options[0].value);
|
||||
|
||||
const tab = getTab(creationType);
|
||||
|
|
|
@ -13,6 +13,7 @@ import { BEFeatureIndicator } from '@@/BEFeatureIndicator';
|
|||
|
||||
import { AnalyticsStateKey } from '../types';
|
||||
import { EdgeAgentTab } from '../shared/EdgeAgentTab';
|
||||
import { useFilterEdgeOptionsIfNeeded } from '../useOnlyEdgeOptions';
|
||||
|
||||
import { AgentPanel } from './AgentPanel';
|
||||
import { KubeConfigTeaserForm } from './KubeConfigTeaserForm';
|
||||
|
@ -21,11 +22,7 @@ interface Props {
|
|||
onCreate(environment: Environment, analytics: AnalyticsStateKey): void;
|
||||
}
|
||||
|
||||
const options: BoxSelectorOption<
|
||||
| EnvironmentCreationTypes.AgentEnvironment
|
||||
| EnvironmentCreationTypes.EdgeAgentEnvironment
|
||||
| EnvironmentCreationTypes.KubeConfigEnvironment
|
||||
>[] = [
|
||||
const defaultOptions: BoxSelectorOption<EnvironmentCreationTypes>[] = [
|
||||
{
|
||||
id: 'agent_endpoint',
|
||||
icon: 'fa fa-bolt',
|
||||
|
@ -52,6 +49,11 @@ const options: BoxSelectorOption<
|
|||
];
|
||||
|
||||
export function WizardKubernetes({ onCreate }: Props) {
|
||||
const options = useFilterEdgeOptionsIfNeeded(
|
||||
defaultOptions,
|
||||
EnvironmentCreationTypes.EdgeAgentEnvironment
|
||||
);
|
||||
|
||||
const [creationType, setCreationType] = useState(options[0].value);
|
||||
|
||||
const tab = getTab(creationType);
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import { EnvironmentCreationTypes } from '@/portainer/environments/types';
|
||||
|
||||
import { BoxSelectorOption } from '@@/BoxSelector';
|
||||
|
||||
import { useCreateEdgeDeviceParam } from '../hooks/useCreateEdgeDeviceParam';
|
||||
|
||||
export function useFilterEdgeOptionsIfNeeded<T = EnvironmentCreationTypes>(
|
||||
options: BoxSelectorOption<T>[],
|
||||
edgeValue: T
|
||||
) {
|
||||
const createEdgeDevice = useCreateEdgeDeviceParam();
|
||||
|
||||
if (!createEdgeDevice) {
|
||||
return options;
|
||||
}
|
||||
|
||||
return options.filter((option) => option.value === edgeValue);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||
|
||||
export function useCreateEdgeDeviceParam() {
|
||||
const {
|
||||
params: { edgeDevice: edgeDeviceParam },
|
||||
} = useCurrentStateAndParams();
|
||||
|
||||
const createEdgeDevice = edgeDeviceParam ? edgeDeviceParam === 'true' : false;
|
||||
|
||||
return createEdgeDevice;
|
||||
}
|
Loading…
Reference in New Issue