mirror of https://github.com/portainer/portainer
feat(endpoints) - Access exposed containers on endpoint public URL (#826)
parent
3d8eec2557
commit
7c6c9284f2
|
@ -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
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -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;
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue