feat(endpoints) - Access exposed containers on endpoint public URL (#826)

pull/838/head
Thomas Krzero 2017-05-01 12:19:43 +02:00 committed by Anthony Lapenna
parent 3d8eec2557
commit 7c6c9284f2
13 changed files with 73 additions and 12 deletions

View File

@ -108,6 +108,7 @@ func (handler *EndpointHandler) handlePostEndpoints(w http.ResponseWriter, r *ht
endpoint := &portainer.Endpoint{ endpoint := &portainer.Endpoint{
Name: req.Name, Name: req.Name,
URL: req.URL, URL: req.URL,
PublicURL: req.PublicURL,
TLS: req.TLS, TLS: req.TLS,
AuthorizedUsers: []portainer.UserID{}, AuthorizedUsers: []portainer.UserID{},
} }
@ -136,9 +137,10 @@ func (handler *EndpointHandler) handlePostEndpoints(w http.ResponseWriter, r *ht
} }
type postEndpointsRequest struct { type postEndpointsRequest struct {
Name string `valid:"required"` Name string `valid:"required"`
URL string `valid:"required"` URL string `valid:"required"`
TLS bool PublicURL string `valid:"-"`
TLS bool
} }
type postEndpointsResponse struct { type postEndpointsResponse struct {
@ -262,6 +264,10 @@ func (handler *EndpointHandler) handlePutEndpoint(w http.ResponseWriter, r *http
endpoint.URL = req.URL endpoint.URL = req.URL
} }
if req.PublicURL != "" {
endpoint.PublicURL = req.PublicURL
}
if req.TLS { if req.TLS {
endpoint.TLS = true endpoint.TLS = true
caCertPath, _ := handler.FileService.GetPathForTLSFile(endpoint.ID, portainer.TLSFileCA) caCertPath, _ := handler.FileService.GetPathForTLSFile(endpoint.ID, portainer.TLSFileCA)
@ -296,9 +302,10 @@ func (handler *EndpointHandler) handlePutEndpoint(w http.ResponseWriter, r *http
} }
type putEndpointsRequest struct { type putEndpointsRequest struct {
Name string `valid:"-"` Name string `valid:"-"`
URL string `valid:"-"` URL string `valid:"-"`
TLS bool `valid:"-"` PublicURL string `valid:"-"`
TLS bool `valid:"-"`
} }
// handleDeleteEndpoint handles DELETE requests on /endpoints/:id // handleDeleteEndpoint handles DELETE requests on /endpoints/:id

View File

@ -72,6 +72,7 @@ type (
ID EndpointID `json:"Id"` ID EndpointID `json:"Id"`
Name string `json:"Name"` Name string `json:"Name"`
URL string `json:"URL"` URL string `json:"URL"`
PublicURL string `json:"PublicURL"`
TLS bool `json:"TLS"` TLS bool `json:"TLS"`
TLSCACertPath string `json:"TLSCACert,omitempty"` TLSCACertPath string `json:"TLSCACert,omitempty"`
TLSCertPath string `json:"TLSCert,omitempty"` TLSCertPath string `json:"TLSCert,omitempty"`

View File

@ -112,7 +112,7 @@
<td ng-if="state.displayIP">{{ container.IP ? container.IP : '-' }}</td> <td ng-if="state.displayIP">{{ container.IP ? container.IP : '-' }}</td>
<td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">{{ container.hostIP }}</td> <td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">{{ container.hostIP }}</td>
<td> <td>
<a ng-if="container.Ports.length > 0" ng-repeat="p in container.Ports" class="image-tag" ng-href="http://{{p.host}}:{{p.public}}" target="_blank"> <a ng-if="container.Ports.length > 0" ng-repeat="p in container.Ports" class="image-tag" ng-href="http://{{ PublicURL || p.host }}:{{p.public}}" target="_blank">
<i class="fa fa-external-link" aria-hidden="true"></i> {{p.public}}:{{ p.private }} <i class="fa fa-external-link" aria-hidden="true"></i> {{p.public}}:{{ p.private }}
</a> </a>
<span ng-if="container.Ports.length == 0" >-</span> <span ng-if="container.Ports.length == 0" >-</span>

View File

@ -1,6 +1,6 @@
angular.module('containers', []) angular.module('containers', [])
.controller('ContainersController', ['$q', '$scope', '$filter', 'Container', 'ContainerHelper', 'Info', 'Settings', 'Notifications', 'Config', 'Pagination', 'EntityListService', 'ModalService', 'Authentication', 'ResourceControlService', 'UserService', .controller('ContainersController', ['$q', '$scope', '$filter', 'Container', 'ContainerHelper', 'Info', 'Settings', 'Notifications', 'Config', 'Pagination', 'EntityListService', 'ModalService', 'Authentication', 'ResourceControlService', 'UserService', 'EndpointProvider',
function ($q, $scope, $filter, Container, ContainerHelper, Info, Settings, Notifications, Config, Pagination, EntityListService, ModalService, Authentication, ResourceControlService, UserService) { function ($q, $scope, $filter, Container, ContainerHelper, Info, Settings, Notifications, Config, Pagination, EntityListService, ModalService, Authentication, ResourceControlService, UserService, EndpointProvider) {
$scope.state = {}; $scope.state = {};
$scope.state.pagination_count = Pagination.getPaginationCount('containers'); $scope.state.pagination_count = Pagination.getPaginationCount('containers');
$scope.state.displayAll = Settings.displayAll; $scope.state.displayAll = Settings.displayAll;
@ -12,6 +12,7 @@ angular.module('containers', [])
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false; $scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
$scope.sortType = sortType; $scope.sortType = sortType;
}; };
$scope.PublicURL = EndpointProvider.endpointPublicURL();
$scope.changePaginationCount = function() { $scope.changePaginationCount = function() {
Pagination.setPaginationCount('containers', $scope.state.pagination_count); Pagination.setPaginationCount('containers', $scope.state.pagination_count);

View File

@ -31,6 +31,17 @@
</div> </div>
</div> </div>
<!-- !endpoint-url-input --> <!-- !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 ng-disabled="endpointType === 'local'" type="text" class="form-control" id="endpoint_public_url" ng-model="endpoint.PublicURL" placeholder="e.g. 10.0.0.10 or mydocker.mydomain.com">
</div>
</div>
<!-- !endpoint-public-url-input -->
<!-- tls-checkbox --> <!-- tls-checkbox -->
<div class="form-group" ng-if="endpointType === 'remote'"> <div class="form-group" ng-if="endpointType === 'remote'">
<div class="col-sm-12"> <div class="col-sm-12">

View File

@ -22,6 +22,7 @@ function ($scope, $state, $stateParams, $filter, EndpointService, Notifications)
var endpointParams = { var endpointParams = {
name: $scope.endpoint.Name, name: $scope.endpoint.Name,
URL: $scope.endpoint.URL, URL: $scope.endpoint.URL,
PublicURL: $scope.endpoint.PublicURL,
TLS: $scope.endpoint.TLS, TLS: $scope.endpoint.TLS,
TLSCACert: $scope.formValues.TLSCACert !== $scope.endpoint.TLSCACert ? $scope.formValues.TLSCACert : null, TLSCACert: $scope.formValues.TLSCACert !== $scope.endpoint.TLSCACert ? $scope.formValues.TLSCACert : null,
TLSCert: $scope.formValues.TLSCert !== $scope.endpoint.TLSCert ? $scope.formValues.TLSCert : null, TLSCert: $scope.formValues.TLSCert !== $scope.endpoint.TLSCert ? $scope.formValues.TLSCert : null,

View File

@ -67,12 +67,13 @@ function ($scope, $state, EndpointService, StateManager, EndpointProvider, Notif
$scope.state.error = ''; $scope.state.error = '';
var name = $scope.formValues.Name; var name = $scope.formValues.Name;
var URL = $scope.formValues.URL; var URL = $scope.formValues.URL;
var PublicURL = URL.split(':')[0];
var TLS = $scope.formValues.TLS; var TLS = $scope.formValues.TLS;
var TLSCAFile = $scope.formValues.TLSCACert; var TLSCAFile = $scope.formValues.TLSCACert;
var TLSCertFile = $scope.formValues.TLSCert; var TLSCertFile = $scope.formValues.TLSCert;
var TLSKeyFile = $scope.formValues.TLSKey; var TLSKeyFile = $scope.formValues.TLSKey;
EndpointService.createRemoteEndpoint(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile) EndpointService.createRemoteEndpoint(name, URL, PublicURL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile)
.then(function success(data) { .then(function success(data) {
var endpointID = data.Id; var endpointID = data.Id;
updateEndpointState(endpointID); updateEndpointState(endpointID);

View File

@ -46,6 +46,17 @@
</div> </div>
</div> </div>
<!-- !endpoint-url-input --> <!-- !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 -->
<!-- tls-checkbox --> <!-- tls-checkbox -->
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">

View File

@ -13,6 +13,7 @@ function ($scope, $state, EndpointService, EndpointProvider, Notifications, Pagi
$scope.formValues = { $scope.formValues = {
Name: '', Name: '',
URL: '', URL: '',
PublicURL: '',
TLS: false, TLS: false,
TLSCACert: null, TLSCACert: null,
TLSCert: null, TLSCert: null,
@ -49,11 +50,15 @@ function ($scope, $state, EndpointService, EndpointProvider, Notifications, Pagi
$scope.state.error = ''; $scope.state.error = '';
var name = $scope.formValues.Name; var name = $scope.formValues.Name;
var URL = $scope.formValues.URL; var URL = $scope.formValues.URL;
var PublicURL = $scope.formValues.PublicURL;
if (PublicURL === '') {
PublicURL = URL.split(':')[0];
}
var TLS = $scope.formValues.TLS; var TLS = $scope.formValues.TLS;
var TLSCAFile = $scope.formValues.TLSCACert; var TLSCAFile = $scope.formValues.TLSCACert;
var TLSCertFile = $scope.formValues.TLSCert; var TLSCertFile = $scope.formValues.TLSCert;
var TLSKeyFile = $scope.formValues.TLSKey; var TLSKeyFile = $scope.formValues.TLSKey;
EndpointService.createRemoteEndpoint(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, false).then(function success(data) { EndpointService.createRemoteEndpoint(name, URL, PublicURL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, false).then(function success(data) {
Notifications.success("Endpoint created", name); Notifications.success("Endpoint created", name);
$state.reload(); $state.reload();
}, function error(err) { }, function error(err) {

View File

@ -11,7 +11,9 @@ function ($scope, $state, Settings, Config, EndpointService, StateManager, Endpo
$scope.switchEndpoint = function(endpoint) { $scope.switchEndpoint = function(endpoint) {
var activeEndpointID = EndpointProvider.endpointID(); var activeEndpointID = EndpointProvider.endpointID();
var activeEndpointPublicURL = EndpointProvider.endpointPublicURL();
EndpointProvider.setEndpointID(endpoint.Id); EndpointProvider.setEndpointID(endpoint.Id);
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
StateManager.updateEndpointState(true) StateManager.updateEndpointState(true)
.then(function success() { .then(function success() {
$state.go('dashboard'); $state.go('dashboard');
@ -19,6 +21,7 @@ function ($scope, $state, Settings, Config, EndpointService, StateManager, Endpo
.catch(function error(err) { .catch(function error(err) {
Notifications.error("Failure", err, "Unable to connect to the Docker endpoint"); Notifications.error("Failure", err, "Unable to connect to the Docker endpoint");
EndpointProvider.setEndpointID(activeEndpointID); EndpointProvider.setEndpointID(activeEndpointID);
EndpointProvider.setEndpointPublicURL(activeEndpointPublicURL);
StateManager.updateEndpointState(true) StateManager.updateEndpointState(true)
.then(function success() {}); .then(function success() {});
}); });
@ -32,6 +35,7 @@ function ($scope, $state, Settings, Config, EndpointService, StateManager, Endpo
angular.forEach($scope.endpoints, function (endpoint) { angular.forEach($scope.endpoints, function (endpoint) {
if (endpoint.Id === activeEndpointID) { if (endpoint.Id === activeEndpointID) {
$scope.activeEndpoint = endpoint; $scope.activeEndpoint = endpoint;
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
} }
}); });
}) })

View File

@ -5,9 +5,13 @@ angular.module('portainer.services')
var service = {}; var service = {};
service.initialize = function() { service.initialize = function() {
var endpointID = LocalStorage.getEndpointID(); var endpointID = LocalStorage.getEndpointID();
var endpointPublicURL = LocalStorage.getEndpointPublicURL();
if (endpointID) { if (endpointID) {
endpoint.ID = endpointID; endpoint.ID = endpointID;
} }
if (endpointPublicURL) {
endpoint.PublicURL = endpointPublicURL;
}
}; };
service.clean = function() { service.clean = function() {
endpoint = {}; endpoint = {};
@ -19,5 +23,12 @@ angular.module('portainer.services')
endpoint.ID = id; endpoint.ID = id;
LocalStorage.storeEndpointID(id); LocalStorage.storeEndpointID(id);
}; };
service.endpointPublicURL = function() {
return endpoint.PublicURL;
}
service.setEndpointPublicURL = function(publicURL) {
endpoint.PublicURL = publicURL;
LocalStorage.storeEndpointPublicURL(publicURL);
}
return service; return service;
}]); }]);

View File

@ -18,6 +18,7 @@ angular.module('portainer.services')
service.updateEndpoint = function(id, endpointParams) { service.updateEndpoint = function(id, endpointParams) {
var query = { var query = {
name: endpointParams.name, name: endpointParams.name,
PublicURL: endpointParams.PublicURL,
TLS: endpointParams.TLS, TLS: endpointParams.TLS,
authorizedUsers: endpointParams.authorizedUsers authorizedUsers: endpointParams.authorizedUsers
}; };
@ -54,10 +55,11 @@ angular.module('portainer.services')
return Endpoints.create({}, endpoint).$promise; return Endpoints.create({}, endpoint).$promise;
}; };
service.createRemoteEndpoint = function(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile) { service.createRemoteEndpoint = function(name, URL, PublicURL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile) {
var endpoint = { var endpoint = {
Name: name, Name: name,
URL: 'tcp://' + URL, URL: 'tcp://' + URL,
PublicURL: PublicURL,
TLS: TLS TLS: TLS
}; };
var deferred = $q.defer(); var deferred = $q.defer();

View File

@ -8,6 +8,12 @@ angular.module('portainer.services')
getEndpointID: function() { getEndpointID: function() {
return localStorageService.get('ENDPOINT_ID'); return localStorageService.get('ENDPOINT_ID');
}, },
storeEndpointPublicURL: function(publicURL) {
localStorageService.set('ENDPOINT_PUBLIC_URL', publicURL);
},
getEndpointPublicURL: function() {
return localStorageService.get('ENDPOINT_PUBLIC_URL');
},
storeEndpointState: function(state) { storeEndpointState: function(state) {
localStorageService.set('ENDPOINT_STATE', state); localStorageService.set('ENDPOINT_STATE', state);
}, },