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:
parent
77a85bd385
commit
2327d696e0
116 changed files with 1900 additions and 689 deletions
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}]);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
}]);
|
141
app/portainer/views/endpoints/create/createendpoint.html
Normal file
141
app/portainer/views/endpoints/create/createendpoint.html
Normal 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> > 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>
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
};
|
||||
}
|
||||
}]);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue