1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-08 23:35:31 +02:00

feat(agent): add agent support (#1828)

This commit is contained in:
Anthony Lapenna 2018-05-06 09:15:57 +02:00 committed by GitHub
parent 77a85bd385
commit 2327d696e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
116 changed files with 1900 additions and 689 deletions

View file

@ -121,6 +121,17 @@ angular.module('portainer.app', [])
}
};
var endpointCreation = {
name: 'portainer.endpoints.new',
url: '/new',
views: {
'content@': {
templateUrl: 'app/portainer/views/endpoints/create/createendpoint.html',
controller: 'CreateEndpointController'
}
}
};
var endpointAccess = {
name: 'portainer.endpoints.endpoint.access',
url: '/access',
@ -297,6 +308,7 @@ angular.module('portainer.app', [])
$stateRegistryProvider.register(endpoints);
$stateRegistryProvider.register(endpoint);
$stateRegistryProvider.register(endpointAccess);
$stateRegistryProvider.register(endpointCreation);
$stateRegistryProvider.register(groups);
$stateRegistryProvider.register(group);
$stateRegistryProvider.register(groupAccess);

View file

@ -16,6 +16,9 @@
ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeAction($ctrl.state.selectedItems)">
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
</button>
<button type="button" class="btn btn-sm btn-primary" ui-sref="portainer.endpoints.new">
<i class="fa fa-plus space-right" aria-hidden="true"></i>Add endpoint
</button>
</div>
<div class="searchBar" ng-if="$ctrl.state.displayTextFilter">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>

View file

@ -99,7 +99,7 @@
></group-association-table>
</div>
</div>
<div class="col-sm-12">
<div class="col-sm-12" ng-if="$ctrl.associatedEndpoints.length === 0">
<span class="text-muted small">All the endpoints are assigned to a group.</span>
</div>
</div>

View file

@ -4,6 +4,7 @@ angular.module('portainer.app')
var service = {};
var headers = {};
headers.agentTargetQueue = [];
service.registryAuthenticationHeader = function() {
return headers.registryAuthentication;
@ -13,5 +14,32 @@ angular.module('portainer.app')
headers.registryAuthentication = headerValue;
};
// Due to the fact that async HTTP requests are decorated using an interceptor
// we need to store and retrieve the headers using a first-in-first-out (FIFO) data structure.
// Otherwise, sequential HTTP requests might end up using the same header value (incorrect in the case
// of starting multiple containers on different nodes for example).
// To prevent having to use the HttpRequestHelper.setPortainerAgentTargetHeader before EACH request,
// we re-use the latest available header in the data structure (handy in thee case of multiple requests affecting
// the same node in the same view).
service.portainerAgentTargetHeader = function() {
if (headers.agentTargetQueue.length === 0) {
return headers.agentTargetLastValue;
} else if (headers.agentTargetQueue.length === 1) {
headers.agentTargetLastValue = headers.agentTargetQueue[0];
}
return headers.agentTargetQueue.shift();
};
service.setPortainerAgentTargetHeader = function(headerValue) {
if (headerValue) {
headers.agentTargetQueue.push(headerValue);
}
};
service.resetAgentTargetQueue = function() {
headers.agentTargetQueue = [];
delete headers.agentTargetLastValue;
};
return service;
}]);

View file

@ -122,18 +122,18 @@ function StateManagerFactory($q, SystemService, InfoHelper, LocalStorage, Settin
return extensions;
}
manager.updateEndpointState = function(loading, extensions) {
manager.updateEndpointState = function(loading, type, extensions) {
var deferred = $q.defer();
if (loading) {
state.loading = true;
}
$q.all({
info: SystemService.info(),
version: SystemService.version()
version: SystemService.version(),
info: SystemService.info()
})
.then(function success(data) {
var endpointMode = InfoHelper.determineEndpointMode(data.info);
var endpointMode = InfoHelper.determineEndpointMode(data.info, type);
var endpointAPIVersion = parseFloat(data.version.ApiVersion);
state.endpoint.mode = endpointMode;
state.endpoint.apiVersion = endpointAPIVersion;

View file

@ -22,7 +22,7 @@ function ($scope, $state, $transition$, $window, $timeout, $sanitize, Authentica
ExtensionManager.initEndpointExtensions(endpoint.Id)
.then(function success(data) {
var extensions = data;
return StateManager.updateEndpointState(true, extensions);
return StateManager.updateEndpointState(true, endpoint.Type, extensions);
})
.then(function success(data) {
$state.go('docker.dashboard');

View file

@ -0,0 +1,71 @@
angular.module('portainer.app')
.controller('CreateEndpointController', ['$scope', '$state', '$filter', 'EndpointService', 'GroupService', 'Notifications',
function ($scope, $state, $filter, EndpointService, GroupService, Notifications) {
$scope.state = {
EnvironmentType: 'docker',
actionInProgress: false
};
$scope.formValues = {
Name: '',
URL: '',
PublicURL: '',
GroupId: 1,
SecurityFormData: new EndpointSecurityFormData()
};
$scope.addDockerEndpoint = function() {
var name = $scope.formValues.Name;
var URL = $filter('stripprotocol')($scope.formValues.URL);
var publicURL = $scope.formValues.PublicURL === '' ? URL.split(':')[0] : $scope.formValues.PublicURL;
var groupId = $scope.formValues.GroupId;
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, URL, publicURL, groupId, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile);
};
$scope.addAgentEndpoint = function() {
var name = $scope.formValues.Name;
var URL = $filter('stripprotocol')($scope.formValues.URL);
var publicURL = $scope.formValues.PublicURL === '' ? URL.split(':')[0] : $scope.formValues.PublicURL;
var groupId = $scope.formValues.GroupId;
addEndpoint(name, URL, publicURL, groupId, true, true, true, null, null, null);
};
function addEndpoint(name, URL, PublicURL, groupId, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) {
$scope.state.actionInProgress = true;
EndpointService.createRemoteEndpoint(name, URL, PublicURL, groupId, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile)
.then(function success() {
Notifications.success('Endpoint created', name);
$state.go('portainer.endpoints', {}, {reload: true});
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to create endpoint');
})
.finally(function final() {
$scope.state.actionInProgress = false;
});
}
function initView() {
GroupService.groups()
.then(function success(data) {
$scope.groups = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to load groups');
});
}
initView();
}]);

View file

@ -0,0 +1,141 @@
<rd-header>
<rd-header-title title="Create endpoint"></rd-header-title>
<rd-header-content>
<a ui-sref="portainer.endpoints">Endpoints</a> &gt; Add endpoint
</rd-header-content>
</rd-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>
<input type="radio" id="docker_endpoint" ng-model="state.EnvironmentType" value="docker">
<label for="docker_endpoint">
<div class="boxselector_header">
<i class="fab fa-docker" aria-hidden="true" style="margin-right: 2px;"></i>
Docker
</div>
<p>Docker environment</p>
</label>
</div>
<div>
<input type="radio" id="agent_endpoint" ng-model="state.EnvironmentType" value="agent">
<label for="agent_endpoint">
<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>
</div>
<div ng-if="state.EnvironmentType === 'docker'">
<div class="col-sm-12 form-section-title" >
Important notice
</div>
<div class="form-group">
<span class="col-sm-12 text-muted small">
The Docker API must be exposed over TCP. You can find more information about how to expose the Docker API over TCP <a href="https://docs.docker.com/engine/security/https/" target="_blank">in the Docker documentation</a>.
</span>
</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">
If you have started Portainer in the same overlay network as the agent, you can use <code>tasks.AGENT_SERVICE_NAME:AGENT_SERVICE_PORT</code> as the
endpoint URL format.
</span>
</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" required auto-focus>
</div>
</div>
<div class="form-group" ng-show="endpointCreationForm.container_name.$invalid">
<div class="col-sm-12 small text-danger">
<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 -->
<!-- endpoint-url-input -->
<div class="form-group">
<label for="endpoint_url" class="col-sm-3 col-lg-2 control-label text-left">
Endpoint URL
<portainer-tooltip position="bottom" 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>
<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>
</div>
</div>
<div class="form-group" ng-show="endpointCreationForm.endpoint_url.$invalid">
<div class="col-sm-12 small text-danger">
<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>
<!-- !endpoint-url-input -->
<!-- endpoint-public-url-input -->
<div class="form-group">
<label for="endpoint_public_url" class="col-sm-3 col-lg-2 control-label text-left">
Public IP
<portainer-tooltip position="bottom" message="URL or IP address where exposed containers will be reachable. This field is optional and will default to the endpoint 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">
</div>
</div>
<!-- !endpoint-public-url-input -->
<!-- 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"></select>
</div>
</div>
<!-- !group -->
<!-- endpoint-security -->
<por-endpoint-security ng-if="state.EnvironmentType === 'docker'" form-data="formValues.SecurityFormData"></por-endpoint-security>
<!-- !endpoint-security -->
<!-- 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">
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add endpoint</span>
<span ng-show="state.actionInProgress">Creating endpoint...</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">
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add endpoint</span>
<span ng-show="state.actionInProgress">Creating endpoint...</span>
</button>
</div>
</div>
<!-- !actions -->
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>

View file

@ -8,7 +8,7 @@
</rd-header>
<div class="row" ng-if="!applicationState.application.endpointManagement">
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header icon="fa-exclamation-triangle" title="Endpoint management is not available">
</rd-widget-header>
@ -22,72 +22,6 @@
</div>
</div>
<div class="row" ng-if="applicationState.application.endpointManagement">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-plus" title="Add a new endpoint">
</rd-widget-header>
<rd-widget-body>
<form class="form-horizontal">
<!-- 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" id="container_name" ng-model="formValues.Name" placeholder="e.g. docker-prod01">
</div>
</div>
<!-- !name-input -->
<!-- endpoint-url-input -->
<div class="form-group">
<label for="endpoint_url" class="col-sm-3 col-lg-2 control-label text-left">
Endpoint URL
<portainer-tooltip position="bottom" 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 type="text" class="form-control" id="endpoint_url" ng-model="formValues.URL" placeholder="e.g. 10.0.0.10:2375 or mydocker.mydomain.com:2375">
</div>
</div>
<!-- !endpoint-url-input -->
<!-- endpoint-public-url-input -->
<div class="form-group">
<label for="endpoint_public_url" class="col-sm-3 col-lg-2 control-label text-left">
Public IP
<portainer-tooltip position="bottom" message="URL or IP address where exposed containers will be reachable. This field is optional and will default to the endpoint 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">
</div>
</div>
<!-- !endpoint-public-url-input -->
<!-- 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"></select>
</div>
</div>
<!-- !group -->
<!-- endpoint-security -->
<por-endpoint-security form-data="formValues.SecurityFormData"></por-endpoint-security>
<!-- !endpoint-security -->
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Name || !formValues.URL || (formValues.TLS && ((formValues.TLSVerify && !formValues.TLSCACert) || (formValues.TLSClientCert && (!formValues.TLSCert || !formValues.TLSKey))))" ng-click="addEndpoint()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add endpoint</span>
<span ng-show="state.actionInProgress">Creating endpoint...</span>
</button>
</div>
</div>
<!-- !actions -->
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<endpoints-datatable

View file

@ -1,50 +1,6 @@
angular.module('portainer.app')
.controller('EndpointsController', ['$q', '$scope', '$state', '$filter', 'EndpointService', 'GroupService', 'EndpointHelper', 'Notifications',
function ($q, $scope, $state, $filter, EndpointService, GroupService, EndpointHelper, Notifications) {
$scope.state = {
uploadInProgress: false,
actionInProgress: false
};
$scope.formValues = {
Name: '',
URL: '',
PublicURL: '',
GroupId: 1,
SecurityFormData: new EndpointSecurityFormData()
};
$scope.addEndpoint = function() {
var name = $scope.formValues.Name;
var URL = $filter('stripprotocol')($scope.formValues.URL);
var PublicURL = $scope.formValues.PublicURL;
if (PublicURL === '') {
PublicURL = URL.split(':')[0];
}
var groupId = $scope.formValues.GroupId;
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;
$scope.state.actionInProgress = true;
EndpointService.createRemoteEndpoint(name, URL, PublicURL, groupId, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile)
.then(function success() {
Notifications.success('Endpoint created', name);
$state.reload();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to create endpoint');
})
.finally(function final() {
$scope.state.actionInProgress = false;
});
};
.controller('EndpointsController', ['$q', '$scope', '$state', 'EndpointService', 'GroupService', 'EndpointHelper', 'Notifications',
function ($q, $scope, $state, EndpointService, GroupService, EndpointHelper, Notifications) {
$scope.removeAction = function (selectedItems) {
var actionCount = selectedItems.length;

View file

@ -36,7 +36,7 @@ function ($scope, $state, $sanitize, Notifications, Authentication, StateManager
ExtensionManager.initEndpointExtensions(endpointID)
.then(function success(data) {
var extensions = data;
return StateManager.updateEndpointState(false, extensions);
return StateManager.updateEndpointState(false, endpoint.Type, extensions);
})
.then(function success() {
$state.go('docker.dashboard');

View file

@ -1,7 +1,7 @@
<div class="page-wrapper">
<!-- simple box -->
<div class="container simple-box">
<div class="col-md-8 col-md-offset-2 col-sm-10 col-sm-offset-1">
<div class="col-sm-10 col-sm-offset-1">
<!-- simple box logo -->
<div class="row">
<img ng-if="logo" ng-src="{{ logo }}" class="simple-box-logo">
@ -29,36 +29,49 @@
<input type="radio" id="local_endpoint" ng-model="formValues.EndpointType" value="local">
<label for="local_endpoint">
<div class="boxselector_header">
<i class="fa fa-bolt" aria-hidden="true" style="margin-right: 2px;"></i>
<i class="fab fa-docker" aria-hidden="true" style="margin-right: 2px;"></i>
Local
</div>
<p>Manage the Docker environment where Portainer is running</p>
<p>Manage the local Docker environment</p>
</label>
</div>
<div>
<input type="radio" id="remote_endpoint" ng-model="formValues.EndpointType" value="remote">
<label for="remote_endpoint">
<div class="boxselector_header">
<i class="fa fa-plug" aria-hidden="true" style="margin-right: 2px;"></i>
<i class="fab fa-docker" aria-hidden="true" style="margin-right: 2px;"></i>
Remote
</div>
<p>Manage a remote Docker environment</p>
</label>
</div>
<div>
<input type="radio" id="agent_endpoint" ng-model="formValues.EndpointType" value="agent">
<label for="agent_endpoint">
<div class="boxselector_header">
<i class="fa fa-bolt" aria-hidden="true" style="margin-right: 2px;"></i>
Agent
</div>
<p>Connect to a Portainer agent</p>
</label>
</div>
</div>
</div>
<!-- !endpoint-type -->
<!-- local-endpoint -->
<div ng-if="formValues.EndpointType === 'local'">
<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-exclamation-triangle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
This feature is not yet available for <u>native Docker Windows containers</u>.
</p>
<p class="text-primary">
Please ensure that you have started the Portainer container with the following Docker flag <code>-v "/var/run/docker.sock:/var/run/docker.sock"</code> in order to connect to the local Docker environment.
Manage the Docker environment where Portainer is running using the Unix filesystem socket.
</p>
<p class="text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Ensure that you have started the Portainer container with the following Docker flag: <code>-v "/var/run/docker.sock:/var/run/docker.sock"</code>.
</p>
</span>
</div>
@ -75,8 +88,80 @@
<!-- !actions -->
</div>
<!-- !local-endpoint -->
<!-- agent-endpoint -->
<div ng-if="formValues.EndpointType === 'agent'">
<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-primary">
Connect directly to a Portainer agent running inside a Swarm cluster.
</p>
<p class="text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
If you have started Portainer in the same overlay network as the agent, you can use <code>tasks.AGENT_SERVICE_NAME:AGENT_SERVICE_PORT</code> as the
endpoint URL format.
</p>
</span>
</div>
</div>
<div class="col-sm-12 form-section-title">
Environment
</div>
<!-- name-input -->
<div class="form-group">
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
<div class="col-sm-8 col-lg-9">
<input type="text" class="form-control" id="endpoint_name" ng-model="formValues.Name" placeholder="e.g. docker-prod01">
</div>
</div>
<!-- !name-input -->
<!-- endpoint-url-input -->
<div class="form-group">
<label for="endpoint_url" class="col-sm-4 col-lg-3 control-label text-left">
Agent URL
<portainer-tooltip position="bottom" message="URL or IP address of a Portainer agent."></portainer-tooltip>
</label>
<div class="col-sm-8 col-lg-9">
<input type="text" class="form-control" id="endpoint_url" ng-model="formValues.URL" placeholder="e.g. 10.0.0.10:9001 or tasks.portainer_agent:9001">
</div>
</div>
<!-- !endpoint-url-input -->
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Name || !formValues.URL" ng-click="createAgentEndpoint()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
<span ng-show="state.actionInProgress">Connecting...</span>
</button>
</div>
</div>
<!-- !actions -->
</div>
<!-- !agent-endpoint -->
<!-- remote-endpoint -->
<div ng-if="formValues.EndpointType === 'remote'">
<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-primary">
Connect Portainer to a remote Docker environment using the Docker API over TCP.
</p>
<p class="text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
The Docker API must be exposed over TCP. You can find more information about how to expose the Docker API over TCP <a href="https://docs.docker.com/engine/security/https/" target="_blank">in the Docker documentation</a>.
</p>
</span>
</div>
</div>
<div class="col-sm-12 form-section-title">
Environment
</div>
<!-- name-input -->
<div class="form-group">
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
@ -89,7 +174,7 @@
<div class="form-group">
<label for="endpoint_url" class="col-sm-4 col-lg-3 control-label text-left">
Endpoint URL
<portainer-tooltip position="bottom" 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>
<portainer-tooltip position="bottom" message="URL or IP address of a Docker host with API exposed over TCP."></portainer-tooltip>
</label>
<div class="col-sm-8 col-lg-9">
<input type="text" class="form-control" id="endpoint_url" ng-model="formValues.URL" placeholder="e.g. 10.0.0.10:2375 or mydocker.mydomain.com:2375">

View file

@ -28,18 +28,18 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
$scope.createLocalEndpoint = function() {
var name = 'local';
var URL = 'unix:///var/run/docker.sock';
var endpointID = 1;
var endpoint;
$scope.state.actionInProgress = true;
EndpointService.createLocalEndpoint()
.then(function success(data) {
endpointID = data.Id;
EndpointProvider.setEndpointID(endpointID);
return ExtensionManager.initEndpointExtensions(endpointID);
endpoint = data;
EndpointProvider.setEndpointID(endpoint.Id);
return ExtensionManager.initEndpointExtensions(endpoint.Id);
})
.then(function success(data) {
var extensions = data;
return StateManager.updateEndpointState(false, extensions);
return StateManager.updateEndpointState(false, endpoint.Type, extensions);
})
.then(function success(data) {
$state.go('docker.dashboard');
@ -52,6 +52,14 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
});
};
$scope.createAgentEndpoint = function() {
var name = $scope.formValues.Name;
var URL = $scope.formValues.URL;
var PublicURL = URL.split(':')[0];
createRemoteEndpoint(name, URL, PublicURL, true, true, true, null, null, null);
};
$scope.createRemoteEndpoint = function() {
var name = $scope.formValues.Name;
var URL = $scope.formValues.URL;
@ -62,18 +70,22 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
var TLSCAFile = TLSSkipVerify ? null : $scope.formValues.TLSCACert;
var TLSCertFile = TLSSKipClientVerify ? null : $scope.formValues.TLSCert;
var TLSKeyFile = TLSSKipClientVerify ? null : $scope.formValues.TLSKey;
var endpointID = 1;
createRemoteEndpoint(name, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile);
};
function createRemoteEndpoint(name, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) {
var endpoint;
$scope.state.actionInProgress = true;
EndpointService.createRemoteEndpoint(name, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile)
EndpointService.createRemoteEndpoint(name, URL, PublicURL, 1, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile)
.then(function success(data) {
endpointID = data.Id;
EndpointProvider.setEndpointID(endpointID);
return ExtensionManager.initEndpointExtensions(endpointID);
endpoint = data;
EndpointProvider.setEndpointID(endpoint.Id);
return ExtensionManager.initEndpointExtensions(endpoint.Id);
})
.then(function success(data) {
var extensions = data;
return StateManager.updateEndpointState(false, extensions);
return StateManager.updateEndpointState(false, endpoint.Type, extensions);
})
.then(function success(data) {
$state.go('docker.dashboard');
@ -84,5 +96,5 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
.finally(function final() {
$scope.state.actionInProgress = false;
});
};
}
}]);

View file

@ -3,28 +3,40 @@ angular.module('portainer.app')
function ($q, $scope, $state, EndpointService, GroupService, StateManager, EndpointProvider, Notifications, Authentication, UserService, ExtensionManager) {
$scope.switchEndpoint = function(endpoint) {
var activeEndpointID = EndpointProvider.endpointID();
var activeEndpointPublicURL = EndpointProvider.endpointPublicURL();
EndpointProvider.setEndpointID(endpoint.Id);
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
ExtensionManager.initEndpointExtensions(endpoint.Id)
.then(function success(data) {
var extensions = data;
return StateManager.updateEndpointState(true, extensions);
return StateManager.updateEndpointState(true, endpoint.Type, extensions);
})
.then(function success() {
$scope.currentEndpoint = endpoint;
$state.go('docker.dashboard');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to connect to the Docker endpoint');
EndpointProvider.setEndpointID(activeEndpointID);
EndpointProvider.setEndpointPublicURL(activeEndpointPublicURL);
StateManager.updateEndpointState(true)
.then(function success() {});
var currentEndpoint = $scope.currentEndpoint;
EndpointProvider.setEndpointID(currentEndpoint.Id);
EndpointProvider.setEndpointPublicURL(currentEndpoint.PublicURL);
return StateManager.updateEndpointState(true, currentEndpoint.Type, currentEndpoint.Extensions);
});
};
function setActiveEndpoint(endpoints) {
var activeEndpointID = EndpointProvider.endpointID();
for (var i = 0; i < endpoints.length; i++) {
var endpoint = endpoints[i];
if (endpoint.Id === activeEndpointID) {
$scope.activeEndpoint = endpoint;
$scope.currentEndpoint = endpoint;
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
break;
}
}
}
function checkPermissions(memberships) {
var isLeader = false;
angular.forEach(memberships, function(membership) {
@ -49,14 +61,7 @@ function ($q, $scope, $state, EndpointService, GroupService, StateManager, Endpo
$scope.groups = data.groups;
$scope.endpoints = endpoints;
var activeEndpointID = EndpointProvider.endpointID();
for (var i = 0; i < endpoints.length; i++) {
var endpoint = endpoints[i];
if (endpoint.Id === activeEndpointID) {
$scope.activeEndpoint = endpoint;
break;
}
}
setActiveEndpoint(endpoints);
if (StateManager.getState().application.authentication) {
var userDetails = Authentication.getUserDetails();