mirror of https://github.com/portainer/portainer
feat(state): introduce endpoint state (#529)
parent
7ebe4af77d
commit
fa4ec04c47
|
@ -160,3 +160,15 @@ func (service *EndpointService) SetActive(endpoint *portainer.Endpoint) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteActive deletes the active endpoint.
|
||||||
|
func (service *EndpointService) DeleteActive() error {
|
||||||
|
return service.store.db.Update(func(tx *bolt.Tx) error {
|
||||||
|
bucket := tx.Bucket([]byte(activeEndpointBucketName))
|
||||||
|
err := bucket.Delete(internal.Itob(activeEndpointID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -260,6 +260,7 @@ type putEndpointsRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleDeleteEndpoint handles DELETE requests on /endpoints/:id
|
// handleDeleteEndpoint handles DELETE requests on /endpoints/:id
|
||||||
|
// DELETE /endpoints/0 deletes the active endpoint
|
||||||
func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *http.Request) {
|
func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
|
@ -270,7 +271,14 @@ func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *h
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
|
var endpoint *portainer.Endpoint
|
||||||
|
if id == "0" {
|
||||||
|
endpoint, err = handler.EndpointService.GetActive()
|
||||||
|
endpointID = int(endpoint.ID)
|
||||||
|
} else {
|
||||||
|
endpoint, err = handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
|
||||||
|
}
|
||||||
|
|
||||||
if err == portainer.ErrEndpointNotFound {
|
if err == portainer.ErrEndpointNotFound {
|
||||||
Error(w, err, http.StatusNotFound, handler.Logger)
|
Error(w, err, http.StatusNotFound, handler.Logger)
|
||||||
return
|
return
|
||||||
|
@ -284,6 +292,13 @@ func (handler *EndpointHandler) handleDeleteEndpoint(w http.ResponseWriter, r *h
|
||||||
Error(w, err, http.StatusInternalServerError, handler.Logger)
|
Error(w, err, http.StatusInternalServerError, handler.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if id == "0" {
|
||||||
|
err = handler.EndpointService.DeleteActive()
|
||||||
|
if err != nil {
|
||||||
|
Error(w, err, http.StatusInternalServerError, handler.Logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if endpoint.TLS {
|
if endpoint.TLS {
|
||||||
err = handler.FileService.DeleteTLSFiles(portainer.EndpointID(endpointID))
|
err = handler.FileService.DeleteTLSFiles(portainer.EndpointID(endpointID))
|
||||||
|
|
|
@ -94,6 +94,7 @@ type (
|
||||||
DeleteEndpoint(ID EndpointID) error
|
DeleteEndpoint(ID EndpointID) error
|
||||||
GetActive() (*Endpoint, error)
|
GetActive() (*Endpoint, error)
|
||||||
SetActive(endpoint *Endpoint) error
|
SetActive(endpoint *Endpoint) error
|
||||||
|
DeleteActive() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// CryptoService represents a service for encrypting/hashing data.
|
// CryptoService represents a service for encrypting/hashing data.
|
||||||
|
|
15
app/app.js
15
app/app.js
|
@ -49,8 +49,8 @@ angular.module('portainer', [
|
||||||
.setPrefix('portainer');
|
.setPrefix('portainer');
|
||||||
|
|
||||||
jwtOptionsProvider.config({
|
jwtOptionsProvider.config({
|
||||||
tokenGetter: ['localStorageService', function(localStorageService) {
|
tokenGetter: ['LocalStorage', function(LocalStorage) {
|
||||||
return localStorageService.get('JWT');
|
return LocalStorage.getJWT();
|
||||||
}],
|
}],
|
||||||
unauthenticatedRedirector: ['$state', function($state) {
|
unauthenticatedRedirector: ['$state', function($state) {
|
||||||
$state.go('auth', {error: 'Your session has expired'});
|
$state.go('auth', {error: 'Your session has expired'});
|
||||||
|
@ -528,21 +528,18 @@ angular.module('portainer', [
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}])
|
}])
|
||||||
.run(['$rootScope', '$state', 'Authentication', 'authManager', 'EndpointMode', function ($rootScope, $state, Authentication, authManager, EndpointMode) {
|
.run(['$rootScope', '$state', 'Authentication', 'authManager', 'StateManager', function ($rootScope, $state, Authentication, authManager, StateManager) {
|
||||||
authManager.checkAuthOnRefresh();
|
authManager.checkAuthOnRefresh();
|
||||||
authManager.redirectWhenUnauthenticated();
|
authManager.redirectWhenUnauthenticated();
|
||||||
|
|
||||||
Authentication.init();
|
Authentication.init();
|
||||||
|
StateManager.init();
|
||||||
|
|
||||||
$rootScope.$state = $state;
|
$rootScope.$state = $state;
|
||||||
|
|
||||||
$rootScope.$on('tokenHasExpired', function($state) {
|
$rootScope.$on('tokenHasExpired', function($state) {
|
||||||
$state.go('auth', {error: 'Your session has expired'});
|
$state.go('auth', {error: 'Your session has expired'});
|
||||||
});
|
});
|
||||||
|
|
||||||
$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {
|
|
||||||
if (toState.name !== 'endpointInit' && (fromState.name === 'auth' || fromState.name === '' || fromState.name === 'endpointInit') && Authentication.isAuthenticated()) {
|
|
||||||
EndpointMode.determineEndpointMode();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}])
|
}])
|
||||||
// This is your docker url that the api will use to make requests
|
// This is your docker url that the api will use to make requests
|
||||||
// You need to set this to the api endpoint without the port i.e. http://192.168.1.9
|
// You need to set this to the api endpoint without the port i.e. http://192.168.1.9
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('auth', [])
|
angular.module('auth', [])
|
||||||
.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Config', 'Authentication', 'Users', 'EndpointService', 'Messages',
|
.controller('AuthenticationController', ['$scope', '$state', '$stateParams', '$window', '$timeout', '$sanitize', 'Config', 'Authentication', 'Users', 'EndpointService', 'StateManager', 'Messages',
|
||||||
function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Authentication, Users, EndpointService, Messages) {
|
function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Authentication, Users, EndpointService, StateManager, Messages) {
|
||||||
|
|
||||||
$scope.authData = {
|
$scope.authData = {
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
|
@ -60,7 +60,12 @@ function ($scope, $state, $stateParams, $window, $timeout, $sanitize, Config, Au
|
||||||
var password = $sanitize($scope.authData.password);
|
var password = $sanitize($scope.authData.password);
|
||||||
Authentication.login(username, password).then(function success() {
|
Authentication.login(username, password).then(function success() {
|
||||||
EndpointService.getActive().then(function success(data) {
|
EndpointService.getActive().then(function success(data) {
|
||||||
$state.go('dashboard');
|
StateManager.updateEndpointState(true)
|
||||||
|
.then(function success() {
|
||||||
|
$state.go('dashboard');
|
||||||
|
}, function error(err) {
|
||||||
|
Messages.error("Failure", err, 'Unable to connect to the Docker endpoint');
|
||||||
|
});
|
||||||
}, function error(err) {
|
}, function error(err) {
|
||||||
if (err.status === 404) {
|
if (err.status === 404) {
|
||||||
$state.go('endpointInit');
|
$state.go('endpointInit');
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
<span ng-show="sortType == 'IP' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
<span ng-show="sortType == 'IP' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<th ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<th ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<a ui-sref="containers" ng-click="order('Host')">
|
<a ui-sref="containers" ng-click="order('Host')">
|
||||||
Host IP
|
Host IP
|
||||||
<span ng-show="sortType == 'Host' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
<span ng-show="sortType == 'Host' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||||
|
@ -88,11 +88,11 @@
|
||||||
<tr dir-paginate="container in (state.filteredContainers = ( containers | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: pagination_count))">
|
<tr dir-paginate="container in (state.filteredContainers = ( containers | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: pagination_count))">
|
||||||
<td><input type="checkbox" ng-model="container.Checked" ng-change="selectItem(container)"/></td>
|
<td><input type="checkbox" ng-model="container.Checked" ng-change="selectItem(container)"/></td>
|
||||||
<td><span class="label label-{{ container.Status|containerstatusbadge }}">{{ container.Status }}</span></td>
|
<td><span class="label label-{{ container.Status|containerstatusbadge }}">{{ container.Status }}</span></td>
|
||||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'"><a ui-sref="container({id: container.Id})">{{ container|swarmcontainername}}</a></td>
|
<td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'"><a ui-sref="container({id: container.Id})">{{ container|swarmcontainername}}</a></td>
|
||||||
<td ng-if="endpointMode.provider !== 'DOCKER_SWARM'"><a ui-sref="container({id: container.Id})">{{ container|containername}}</a></td>
|
<td ng-if="applicationState.endpoint.mode.provider !== 'DOCKER_SWARM'"><a ui-sref="container({id: container.Id})">{{ container|containername}}</a></td>
|
||||||
<td><a ui-sref="image({id: container.Image})">{{ container.Image }}</a></td>
|
<td><a ui-sref="image({id: container.Image})">{{ container.Image }}</a></td>
|
||||||
<td ng-if="state.displayIP">{{ container.IP ? container.IP : '-' }}</td>
|
<td ng-if="state.displayIP">{{ container.IP ? container.IP : '-' }}</td>
|
||||||
<td ng-if="endpointMode.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://{{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 }}
|
||||||
|
|
|
@ -28,7 +28,7 @@ function ($scope, $filter, Container, ContainerHelper, Info, Settings, Messages,
|
||||||
if (model.IP) {
|
if (model.IP) {
|
||||||
$scope.state.displayIP = true;
|
$scope.state.displayIP = true;
|
||||||
}
|
}
|
||||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM') {
|
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') {
|
||||||
model.hostIP = $scope.swarm_hosts[_.split(container.Names[0], '/')[1]];
|
model.hostIP = $scope.swarm_hosts[_.split(container.Names[0], '/')[1]];
|
||||||
}
|
}
|
||||||
return model;
|
return model;
|
||||||
|
@ -161,7 +161,7 @@ function ($scope, $filter, Container, ContainerHelper, Info, Settings, Messages,
|
||||||
|
|
||||||
Config.$promise.then(function (c) {
|
Config.$promise.then(function (c) {
|
||||||
$scope.containersToHideLabels = c.hiddenLabels;
|
$scope.containersToHideLabels = c.hiddenLabels;
|
||||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM') {
|
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') {
|
||||||
Info.get({}, function (d) {
|
Info.get({}, function (d) {
|
||||||
$scope.swarm_hosts = retrieveSwarmHostsInfo(d);
|
$scope.swarm_hosts = retrieveSwarmHostsInfo(d);
|
||||||
update({all: Settings.displayAll ? 1 : 0});
|
update({all: Settings.displayAll ? 1 : 0});
|
||||||
|
|
|
@ -72,7 +72,7 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai
|
||||||
|
|
||||||
Network.query({}, function (d) {
|
Network.query({}, function (d) {
|
||||||
var networks = d;
|
var networks = d;
|
||||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM' || $scope.endpointMode.provider === 'DOCKER_SWARM_MODE') {
|
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') {
|
||||||
networks = d.filter(function (network) {
|
networks = d.filter(function (network) {
|
||||||
if (network.Scope === 'global') {
|
if (network.Scope === 'global') {
|
||||||
return network;
|
return network;
|
||||||
|
@ -220,7 +220,7 @@ function ($scope, $state, $stateParams, $filter, Config, Info, Container, Contai
|
||||||
var containerName = container;
|
var containerName = container;
|
||||||
if (container && typeof container === 'object') {
|
if (container && typeof container === 'object') {
|
||||||
containerName = $filter('trimcontainername')(container.Names[0]);
|
containerName = $filter('trimcontainername')(container.Names[0]);
|
||||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM') {
|
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') {
|
||||||
containerName = $filter('swarmcontainername')(container);
|
containerName = $filter('swarmcontainername')(container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,7 +269,7 @@
|
||||||
<!-- tab-network -->
|
<!-- tab-network -->
|
||||||
<div class="tab-pane" id="network">
|
<div class="tab-pane" id="network">
|
||||||
<form class="form-horizontal" style="margin-top: 15px;">
|
<form class="form-horizontal" style="margin-top: 15px;">
|
||||||
<div class="form-group" ng-if="globalNetworkCount === 0 && endpointMode.provider !== 'DOCKER_SWARM_MODE'">
|
<div class="form-group" ng-if="globalNetworkCount === 0 && applicationState.endpoint.mode.provider !== 'DOCKER_SWARM_MODE'">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<span class="small text-muted">You don't have any shared network. Head over the <a ui-sref="networks">networks view</a> to create one.</span>
|
<span class="small text-muted">You don't have any shared network. Head over the <a ui-sref="networks">networks view</a> to create one.</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -289,10 +289,10 @@
|
||||||
<div class="form-group" ng-if="config.HostConfig.NetworkMode == 'container'">
|
<div class="form-group" ng-if="config.HostConfig.NetworkMode == 'container'">
|
||||||
<label for="container_network_container" class="col-sm-1 control-label text-left">Container</label>
|
<label for="container_network_container" class="col-sm-1 control-label text-left">Container</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select ng-if="endpointMode.provider !== 'DOCKER_SWARM'" ng-options="container|containername for container in runningContainers" class="form-control" ng-model="formValues.NetworkContainer">
|
<select ng-if="applicationState.endpoint.mode.provider !== 'DOCKER_SWARM'" ng-options="container|containername for container in runningContainers" class="form-control" ng-model="formValues.NetworkContainer">
|
||||||
<option selected disabled hidden value="">Select a container</option>
|
<option selected disabled hidden value="">Select a container</option>
|
||||||
</select>
|
</select>
|
||||||
<select ng-if="endpointMode.provider === 'DOCKER_SWARM'" ng-options="container|swarmcontainername for container in runningContainers" class="form-control" ng-model="formValues.NetworkContainer">
|
<select ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'" ng-options="container|swarmcontainername for container in runningContainers" class="form-control" ng-model="formValues.NetworkContainer">
|
||||||
<option selected disabled hidden value="">Select a container</option>
|
<option selected disabled hidden value="">Select a container</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</rd-header>
|
</rd-header>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="endpointMode.provider !== 'DOCKER_SWARM'">
|
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="applicationState.endpoint.mode.provider !== 'DOCKER_SWARM'">
|
||||||
<rd-widget>
|
<rd-widget>
|
||||||
<rd-widget-header icon="fa-tachometer" title="Node info"></rd-widget-header>
|
<rd-widget-header icon="fa-tachometer" title="Node info"></rd-widget-header>
|
||||||
<rd-widget-body classes="no-padding">
|
<rd-widget-body classes="no-padding">
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<rd-widget>
|
<rd-widget>
|
||||||
<rd-widget-header icon="fa-tachometer" title="Cluster info"></rd-widget-header>
|
<rd-widget-header icon="fa-tachometer" title="Cluster info"></rd-widget-header>
|
||||||
<rd-widget-body classes="no-padding">
|
<rd-widget-body classes="no-padding">
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
<div class="col-lg-12 col-md-12 col-xs-12" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">
|
||||||
<rd-widget>
|
<rd-widget>
|
||||||
<rd-widget-header icon="fa-tachometer" title="Swarm info"></rd-widget-header>
|
<rd-widget-header icon="fa-tachometer" title="Swarm info"></rd-widget-header>
|
||||||
<rd-widget-body classes="no-padding">
|
<rd-widget-body classes="no-padding">
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
<!-- endpoin-type radio -->
|
<!-- endpoin-type radio -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label><input type="radio" name="endpointType" value="local" ng-model="formValues.endpointType">Manage the Docker instance where Portainer is running</label>
|
<label><input type="radio" name="endpointType" value="local" ng-model="formValues.endpointType" ng-click="cleanError()">Manage the Docker instance where Portainer is running</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label><input type="radio" name="endpointType" value="remote" ng-model="formValues.endpointType">Manage a remote Docker instance</label>
|
<label><input type="radio" name="endpointType" value="remote" ng-model="formValues.endpointType" ng-click="cleanError()">Manage a remote Docker instance</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- endpoint-type radio -->
|
<!-- endpoint-type radio -->
|
||||||
|
@ -41,7 +41,10 @@
|
||||||
<p class="pull-left text-danger" ng-if="state.error" style="margin: 5px;">
|
<p class="pull-left text-danger" ng-if="state.error" style="margin: 5px;">
|
||||||
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> {{ state.error }}
|
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> {{ state.error }}
|
||||||
</p>
|
</p>
|
||||||
<button type="submit" class="btn btn-primary pull-right" ng-click="createLocalEndpoint()"><i class="fa fa-plug" aria-hidden="true"></i> Connect</button>
|
<span class="pull-right">
|
||||||
|
<i id="initEndpointSpinner" class="fa fa-cog fa-spin" style="margin-right: 5px; display: none;"></i>
|
||||||
|
<button type="submit" class="btn btn-primary" ng-click="createLocalEndpoint()"><i class="fa fa-plug" aria-hidden="true"></i> Connect</button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !connect button -->
|
<!-- !connect button -->
|
||||||
|
@ -122,7 +125,10 @@
|
||||||
<p class="pull-left text-danger" ng-if="state.error" style="margin: 5px;">
|
<p class="pull-left text-danger" ng-if="state.error" style="margin: 5px;">
|
||||||
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> {{ state.error }}
|
<i class="fa fa-exclamation-circle" aria-hidden="true"></i> {{ state.error }}
|
||||||
</p>
|
</p>
|
||||||
<button type="submit" class="btn btn-primary pull-right" ng-disabled="!formValues.Name || !formValues.URL || (formValues.TLS && (!formValues.TLSCACert || !formValues.TLSCert || !formValues.TLSKey))" ng-click="createRemoteEndpoint()"><i class="fa fa-plug" aria-hidden="true"></i> Connect</button>
|
<span class="pull-right">
|
||||||
|
<i id="initEndpointSpinner" class="fa fa-cog fa-spin" style="margin-right: 5px; display: none;"></i>
|
||||||
|
<button type="submit" class="btn btn-primary" ng-disabled="!formValues.Name || !formValues.URL || (formValues.TLS && (!formValues.TLSCACert || !formValues.TLSCert || !formValues.TLSKey))" ng-click="createRemoteEndpoint()"><i class="fa fa-plug" aria-hidden="true"></i> Connect</button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- !connect button -->
|
<!-- !connect button -->
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('endpointInit', [])
|
angular.module('endpointInit', [])
|
||||||
.controller('EndpointInitController', ['$scope', '$state', 'EndpointService', 'Messages',
|
.controller('EndpointInitController', ['$scope', '$state', 'EndpointService', 'StateManager', 'Messages',
|
||||||
function ($scope, $state, EndpointService, Messages) {
|
function ($scope, $state, EndpointService, StateManager, Messages) {
|
||||||
$scope.state = {
|
$scope.state = {
|
||||||
error: '',
|
error: '',
|
||||||
uploadInProgress: false
|
uploadInProgress: false
|
||||||
|
@ -15,27 +15,39 @@ function ($scope, $state, EndpointService, Messages) {
|
||||||
TLSKey: null
|
TLSKey: null
|
||||||
};
|
};
|
||||||
|
|
||||||
EndpointService.getActive().then(function success(data) {
|
if (!_.isEmpty($scope.applicationState.endpoint)) {
|
||||||
$state.go('dashboard');
|
$state.go('dashboard');
|
||||||
}, function error(err) {
|
}
|
||||||
if (err.status !== 404) {
|
|
||||||
Messages.error("Failure", err, 'Unable to verify Docker endpoint existence');
|
$scope.cleanError = function() {
|
||||||
}
|
$scope.state.error = '';
|
||||||
});
|
};
|
||||||
|
|
||||||
$scope.createLocalEndpoint = function() {
|
$scope.createLocalEndpoint = function() {
|
||||||
|
$('#initEndpointSpinner').show();
|
||||||
$scope.state.error = '';
|
$scope.state.error = '';
|
||||||
var name = "local";
|
var name = "local";
|
||||||
var URL = "unix:///var/run/docker.sock";
|
var URL = "unix:///var/run/docker.sock";
|
||||||
var TLS = false;
|
var TLS = false;
|
||||||
EndpointService.createLocalEndpoint(name, URL, TLS, true).then(function success(data) {
|
EndpointService.createLocalEndpoint(name, URL, TLS, true).then(function success(data) {
|
||||||
$state.go('dashboard');
|
StateManager.updateEndpointState(false)
|
||||||
|
.then(function success() {
|
||||||
|
$state.go('dashboard');
|
||||||
|
}, function error(err) {
|
||||||
|
EndpointService.deleteEndpoint(0)
|
||||||
|
.then(function success() {
|
||||||
|
$('#initEndpointSpinner').hide();
|
||||||
|
$scope.state.error = 'Unable to connect to the Docker endpoint';
|
||||||
|
});
|
||||||
|
});
|
||||||
}, function error(err) {
|
}, function error(err) {
|
||||||
|
$('#initEndpointSpinner').hide();
|
||||||
$scope.state.error = 'Unable to create endpoint';
|
$scope.state.error = 'Unable to create endpoint';
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.createRemoteEndpoint = function() {
|
$scope.createRemoteEndpoint = function() {
|
||||||
|
$('#initEndpointSpinner').show();
|
||||||
$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;
|
||||||
|
@ -43,9 +55,20 @@ function ($scope, $state, EndpointService, Messages) {
|
||||||
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, TLS ? false : true).then(function success(data) {
|
EndpointService.createRemoteEndpoint(name, URL, TLS, TLSCAFile, TLSCertFile, TLSKeyFile, TLS ? false : true)
|
||||||
$state.go('dashboard');
|
.then(function success(data) {
|
||||||
|
StateManager.updateEndpointState(false)
|
||||||
|
.then(function success() {
|
||||||
|
$state.go('dashboard');
|
||||||
|
}, function error(err) {
|
||||||
|
EndpointService.deleteEndpoint(0)
|
||||||
|
.then(function success() {
|
||||||
|
$('#initEndpointSpinner').hide();
|
||||||
|
$scope.state.error = 'Unable to connect to the Docker endpoint';
|
||||||
|
});
|
||||||
|
});
|
||||||
}, function error(err) {
|
}, function error(err) {
|
||||||
|
$('#initEndpointSpinner').hide();
|
||||||
$scope.state.uploadInProgress = false;
|
$scope.state.uploadInProgress = false;
|
||||||
$scope.state.error = err.msg;
|
$scope.state.error = err.msg;
|
||||||
}, function update(evt) {
|
}, function update(evt) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('main', [])
|
angular.module('main', [])
|
||||||
.controller('MainController', ['$scope', '$cookieStore',
|
.controller('MainController', ['$scope', '$cookieStore', 'StateManager',
|
||||||
function ($scope, $cookieStore) {
|
function ($scope, $cookieStore, StateManager) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sidebar Toggle & Cookie Control
|
* Sidebar Toggle & Cookie Control
|
||||||
|
@ -10,6 +10,8 @@ function ($scope, $cookieStore) {
|
||||||
return window.innerWidth;
|
return window.innerWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.applicationState = StateManager.getState();
|
||||||
|
|
||||||
$scope.$watch($scope.getWidth, function(newValue, oldValue) {
|
$scope.$watch($scope.getWidth, function(newValue, oldValue) {
|
||||||
if (newValue >= mobileView) {
|
if (newValue >= mobileView) {
|
||||||
if (angular.isDefined($cookieStore.get('toggle'))) {
|
if (angular.isDefined($cookieStore.get('toggle'))) {
|
||||||
|
|
|
@ -23,12 +23,12 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- !name-input -->
|
<!-- !name-input -->
|
||||||
<!-- tag-note -->
|
<!-- tag-note -->
|
||||||
<div class="form-group" ng-if="endpointMode.provider === 'DOCKER_SWARM' || endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
<div class="form-group" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<span class="small text-muted">Note: The network will be created using the overlay driver and will allow containers to communicate across the hosts of your cluster.</span>
|
<span class="small text-muted">Note: The network will be created using the overlay driver and will allow containers to communicate across the hosts of your cluster.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" ng-if="endpointMode.provider === 'DOCKER_STANDALONE'">
|
<div class="form-group" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<span class="small text-muted">Note: The network will be created using the bridge driver.</span>
|
<span class="small text-muted">Note: The network will be created using the bridge driver.</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,7 @@ function ($scope, $state, Network, Config, Messages, Settings) {
|
||||||
|
|
||||||
function prepareNetworkConfiguration() {
|
function prepareNetworkConfiguration() {
|
||||||
var config = angular.copy($scope.config);
|
var config = angular.copy($scope.config);
|
||||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM' || $scope.endpointMode.provider === 'DOCKER_SWARM_MODE') {
|
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') {
|
||||||
config.Driver = 'overlay';
|
config.Driver = 'overlay';
|
||||||
// Force IPAM Driver to 'default', should not be required.
|
// Force IPAM Driver to 'default', should not be required.
|
||||||
// See: https://github.com/docker/docker/issues/25735
|
// See: https://github.com/docker/docker/issues/25735
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<li class="sidebar-list">
|
<li class="sidebar-list">
|
||||||
<a ui-sref="templates" ui-sref-active="active">App Templates <span class="menu-icon fa fa-rocket"></span></a>
|
<a ui-sref="templates" ui-sref-active="active">App Templates <span class="menu-icon fa fa-rocket"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sidebar-list" ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
<li class="sidebar-list" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">
|
||||||
<a ui-sref="services" ui-sref-active="active">Services <span class="menu-icon fa fa-list-alt"></span></a>
|
<a ui-sref="services" ui-sref-active="active">Services <span class="menu-icon fa fa-list-alt"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sidebar-list">
|
<li class="sidebar-list">
|
||||||
|
@ -37,13 +37,13 @@
|
||||||
<li class="sidebar-list">
|
<li class="sidebar-list">
|
||||||
<a ui-sref="volumes" ui-sref-active="active">Volumes <span class="menu-icon fa fa-cubes"></span></a>
|
<a ui-sref="volumes" ui-sref-active="active">Volumes <span class="menu-icon fa fa-cubes"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sidebar-list" ng-if="endpointMode.provider === 'DOCKER_STANDALONE'">
|
<li class="sidebar-list" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'">
|
||||||
<a ui-sref="events" ui-sref-active="active">Events <span class="menu-icon fa fa-history"></span></a>
|
<a ui-sref="events" ui-sref-active="active">Events <span class="menu-icon fa fa-history"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sidebar-list" ng-if="endpointMode.provider === 'DOCKER_SWARM' || (endpointMode.provider === 'DOCKER_SWARM_MODE' && endpointMode.role === 'MANAGER')">
|
<li class="sidebar-list" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || (applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER')">
|
||||||
<a ui-sref="swarm" ui-sref-active="active">Swarm <span class="menu-icon fa fa-object-group"></span></a>
|
<a ui-sref="swarm" ui-sref-active="active">Swarm <span class="menu-icon fa fa-object-group"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sidebar-list" ng-if="endpointMode.provider === 'DOCKER_STANDALONE'">
|
<li class="sidebar-list" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'">
|
||||||
<a ui-sref="docker">Docker <span class="menu-icon fa fa-th"></span></a>
|
<a ui-sref="docker">Docker <span class="menu-icon fa fa-th"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sidebar-title"><span>Portainer settings</span></li>
|
<li class="sidebar-title"><span>Portainer settings</span></li>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('sidebar', [])
|
angular.module('sidebar', [])
|
||||||
.controller('SidebarController', ['$scope', '$state', 'Settings', 'Config', 'EndpointService', 'EndpointMode', 'Messages',
|
.controller('SidebarController', ['$scope', '$state', 'Settings', 'Config', 'EndpointService', 'StateManager', 'Messages',
|
||||||
function ($scope, $state, Settings, Config, EndpointService, EndpointMode, Messages) {
|
function ($scope, $state, Settings, Config, EndpointService, StateManager, Messages) {
|
||||||
|
|
||||||
Config.$promise.then(function (c) {
|
Config.$promise.then(function (c) {
|
||||||
$scope.logo = c.logo;
|
$scope.logo = c.logo;
|
||||||
|
@ -10,8 +10,12 @@ function ($scope, $state, Settings, Config, EndpointService, EndpointMode, Messa
|
||||||
|
|
||||||
$scope.switchEndpoint = function(endpoint) {
|
$scope.switchEndpoint = function(endpoint) {
|
||||||
EndpointService.setActive(endpoint.Id).then(function success(data) {
|
EndpointService.setActive(endpoint.Id).then(function success(data) {
|
||||||
EndpointMode.determineEndpointMode();
|
StateManager.updateEndpointState(true)
|
||||||
$state.reload();
|
.then(function success() {
|
||||||
|
$state.reload();
|
||||||
|
}, function error(err) {
|
||||||
|
Messages.error("Failure", err, "Unable to connect to the Docker endpoint");
|
||||||
|
});
|
||||||
}, function error(err) {
|
}, function error(err) {
|
||||||
Messages.error("Failure", err, "Unable to switch to new endpoint");
|
Messages.error("Failure", err, "Unable to switch to new endpoint");
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Nodes</td>
|
<td>Nodes</td>
|
||||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'">{{ swarm.Nodes }}</td>
|
<td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">{{ swarm.Nodes }}</td>
|
||||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">{{ info.Swarm.Nodes }}</td>
|
<td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">{{ info.Swarm.Nodes }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<tr ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<td>Images</td>
|
<td>Images</td>
|
||||||
<td>{{ info.Images }}</td>
|
<td>{{ info.Images }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<tr ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<td>Swarm version</td>
|
<td>Swarm version</td>
|
||||||
<td>{{ docker.Version|swarmversion }}</td>
|
<td>{{ docker.Version|swarmversion }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -31,29 +31,29 @@
|
||||||
<td>Docker API version</td>
|
<td>Docker API version</td>
|
||||||
<td>{{ docker.ApiVersion }}</td>
|
<td>{{ docker.ApiVersion }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<tr ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<td>Strategy</td>
|
<td>Strategy</td>
|
||||||
<td>{{ swarm.Strategy }}</td>
|
<td>{{ swarm.Strategy }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Total CPU</td>
|
<td>Total CPU</td>
|
||||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'">{{ info.NCPU }}</td>
|
<td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">{{ info.NCPU }}</td>
|
||||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">{{ totalCPU }}</td>
|
<td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">{{ totalCPU }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Total memory</td>
|
<td>Total memory</td>
|
||||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM'">{{ info.MemTotal|humansize: 2 }}</td>
|
<td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">{{ info.MemTotal|humansize: 2 }}</td>
|
||||||
<td ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">{{ totalMemory|humansize: 2 }}</td>
|
<td ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">{{ totalMemory|humansize: 2 }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<tr ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<td>Operating system</td>
|
<td>Operating system</td>
|
||||||
<td>{{ info.OperatingSystem }}</td>
|
<td>{{ info.OperatingSystem }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<tr ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<td>Kernel version</td>
|
<td>Kernel version</td>
|
||||||
<td>{{ info.KernelVersion }}</td>
|
<td>{{ info.KernelVersion }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<tr ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<td>Go version</td>
|
<td>Go version</td>
|
||||||
<td>{{ docker.GoVersion }}</td>
|
<td>{{ docker.GoVersion }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" ng-if="endpointMode.provider === 'DOCKER_SWARM'">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<rd-widget>
|
<rd-widget>
|
||||||
<rd-widget-header icon="fa-hdd-o" title="Node status"></rd-widget-header>
|
<rd-widget-header icon="fa-hdd-o" title="Node status"></rd-widget-header>
|
||||||
<rd-widget-body classes="no-padding">
|
<rd-widget-body classes="no-padding">
|
||||||
|
@ -133,7 +133,7 @@
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">
|
||||||
<rd-widget>
|
<rd-widget>
|
||||||
<rd-widget-header icon="fa-hdd-o" title="Node status"></rd-widget-header>
|
<rd-widget-header icon="fa-hdd-o" title="Node status"></rd-widget-header>
|
||||||
<rd-widget-body classes="no-padding">
|
<rd-widget-body classes="no-padding">
|
||||||
|
|
|
@ -22,7 +22,7 @@ function ($scope, Info, Version, Node, Settings) {
|
||||||
|
|
||||||
Info.get({}, function (d) {
|
Info.get({}, function (d) {
|
||||||
$scope.info = d;
|
$scope.info = d;
|
||||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM_MODE') {
|
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') {
|
||||||
Node.query({}, function(d) {
|
Node.query({}, function(d) {
|
||||||
$scope.nodes = d;
|
$scope.nodes = d;
|
||||||
var CPU = 0, memory = 0;
|
var CPU = 0, memory = 0;
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
</rd-widget-custom-header>
|
</rd-widget-custom-header>
|
||||||
<rd-widget-body classes="padding">
|
<rd-widget-body classes="padding">
|
||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
<div class="form-group" ng-if="globalNetworkCount === 0 && endpointMode.provider === 'DOCKER_SWARM'">
|
<div class="form-group" ng-if="globalNetworkCount === 0 && applicationState.endpoint.mode.provider === 'DOCKER_SWARM'">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<span class="small text-muted">When using Swarm, we recommend deploying containers in a shared network. Looks like you don't have any shared network, head over the <a ui-sref="networks">networks view</a> to create one.</span>
|
<span class="small text-muted">When using Swarm, we recommend deploying containers in a shared network. Looks like you don't have any shared network, head over the <a ui-sref="networks">networks view</a> to create one.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" ng-if="endpointMode.provider === 'DOCKER_SWARM_MODE'">
|
<div class="form-group" ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
|
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
|
||||||
<span class="small text-muted">App templates cannot be used with swarm-mode at the moment. You can still use them to quickly deploy containers to the Docker host.</span>
|
<span class="small text-muted">App templates cannot be used with swarm-mode at the moment. You can still use them to quickly deploy containers to the Docker host.</span>
|
||||||
|
@ -41,10 +41,10 @@
|
||||||
<div ng-repeat="var in state.selectedTemplate.env" ng-if="!var.set" class="form-group">
|
<div ng-repeat="var in state.selectedTemplate.env" ng-if="!var.set" class="form-group">
|
||||||
<label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label>
|
<label for="field_{{ $index }}" class="col-sm-2 control-label text-left">{{ var.label }}</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select ng-if="endpointMode.provider !== 'DOCKER_SWARM' && var.type === 'container'" ng-options="container|containername for container in runningContainers" class="form-control" ng-model="var.value">
|
<select ng-if="applicationState.endpoint.mode.provider !== 'DOCKER_SWARM' && var.type === 'container'" ng-options="container|containername for container in runningContainers" class="form-control" ng-model="var.value">
|
||||||
<option selected disabled hidden value="">Select a container</option>
|
<option selected disabled hidden value="">Select a container</option>
|
||||||
</select>
|
</select>
|
||||||
<select ng-if="endpointMode.provider === 'DOCKER_SWARM' && var.type === 'container'" ng-options="container|swarmcontainername for container in runningContainers" class="form-control" ng-model="var.value">
|
<select ng-if="applicationState.endpoint.mode.provider === 'DOCKER_SWARM' && var.type === 'container'" ng-options="container|swarmcontainername for container in runningContainers" class="form-control" ng-model="var.value">
|
||||||
<option selected disabled hidden value="">Select a container</option>
|
<option selected disabled hidden value="">Select a container</option>
|
||||||
</select>
|
</select>
|
||||||
<input ng-if="!var.type || !var.type === 'container'" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}">
|
<input ng-if="!var.type || !var.type === 'container'" type="text" class="form-control" ng-model="var.value" id="field_{{ $index }}">
|
||||||
|
|
|
@ -115,7 +115,7 @@ function ($scope, $q, $state, $filter, $anchorScroll, Config, Info, Container, C
|
||||||
if (v.value || v.set) {
|
if (v.value || v.set) {
|
||||||
var val;
|
var val;
|
||||||
if (v.type && v.type === 'container') {
|
if (v.type && v.type === 'container') {
|
||||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM' && $scope.formValues.network.Scope === 'global') {
|
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' && $scope.formValues.network.Scope === 'global') {
|
||||||
val = $filter('swarmcontainername')(v.value);
|
val = $filter('swarmcontainername')(v.value);
|
||||||
} else {
|
} else {
|
||||||
var container = v.value;
|
var container = v.value;
|
||||||
|
@ -206,7 +206,7 @@ function ($scope, $q, $state, $filter, $anchorScroll, Config, Info, Container, C
|
||||||
var containersToHideLabels = c.hiddenLabels;
|
var containersToHideLabels = c.hiddenLabels;
|
||||||
Network.query({}, function (d) {
|
Network.query({}, function (d) {
|
||||||
var networks = d;
|
var networks = d;
|
||||||
if ($scope.endpointMode.provider === 'DOCKER_SWARM' || $scope.endpointMode.provider === 'DOCKER_SWARM_MODE') {
|
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM' || $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') {
|
||||||
networks = d.filter(function (network) {
|
networks = d.filter(function (network) {
|
||||||
if (network.Scope === 'global') {
|
if (network.Scope === 'global') {
|
||||||
return network;
|
return network;
|
||||||
|
|
|
@ -1,4 +1,35 @@
|
||||||
angular.module('portainer.helpers', [])
|
angular.module('portainer.helpers', [])
|
||||||
|
.factory('InfoHelper', [function InfoHelperFactory() {
|
||||||
|
'use strict';
|
||||||
|
return {
|
||||||
|
determineEndpointMode: function(info) {
|
||||||
|
var mode = {
|
||||||
|
provider: '',
|
||||||
|
role: ''
|
||||||
|
};
|
||||||
|
if (_.startsWith(info.ServerVersion, 'swarm')) {
|
||||||
|
mode.provider = "DOCKER_SWARM";
|
||||||
|
if (info.SystemStatus[0][1] === 'primary') {
|
||||||
|
mode.role = "PRIMARY";
|
||||||
|
} else {
|
||||||
|
mode.role = "REPLICA";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!info.Swarm || _.isEmpty(info.Swarm.NodeID)) {
|
||||||
|
mode.provider = "DOCKER_STANDALONE";
|
||||||
|
} else {
|
||||||
|
mode.provider = "DOCKER_SWARM_MODE";
|
||||||
|
if (info.Swarm.ControlAvailable) {
|
||||||
|
mode.role = "MANAGER";
|
||||||
|
} else {
|
||||||
|
mode.role = "WORKER";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
.factory('ImageHelper', [function ImageHelperFactory() {
|
.factory('ImageHelper', [function ImageHelperFactory() {
|
||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -240,44 +240,11 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize'])
|
||||||
initAdminUser: { method: 'POST', params: { username: 'admin', action: 'init' } }
|
initAdminUser: { method: 'POST', params: { username: 'admin', action: 'init' } }
|
||||||
});
|
});
|
||||||
}])
|
}])
|
||||||
.factory('EndpointMode', ['$rootScope', 'Info', function EndpointMode($rootScope, Info) {
|
.factory('Authentication', ['$q', '$rootScope', 'Auth', 'jwtHelper', 'LocalStorage', function AuthenticationFactory($q, $rootScope, Auth, jwtHelper, LocalStorage) {
|
||||||
'use strict';
|
|
||||||
return {
|
|
||||||
determineEndpointMode: function() {
|
|
||||||
Info.get({}, function(d) {
|
|
||||||
var mode = {
|
|
||||||
provider: '',
|
|
||||||
role: ''
|
|
||||||
};
|
|
||||||
if (_.startsWith(d.ServerVersion, 'swarm')) {
|
|
||||||
mode.provider = "DOCKER_SWARM";
|
|
||||||
if (d.SystemStatus[0][1] === 'primary') {
|
|
||||||
mode.role = "PRIMARY";
|
|
||||||
} else {
|
|
||||||
mode.role = "REPLICA";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!d.Swarm || _.isEmpty(d.Swarm.NodeID)) {
|
|
||||||
mode.provider = "DOCKER_STANDALONE";
|
|
||||||
} else {
|
|
||||||
mode.provider = "DOCKER_SWARM_MODE";
|
|
||||||
if (d.Swarm.ControlAvailable) {
|
|
||||||
mode.role = "MANAGER";
|
|
||||||
} else {
|
|
||||||
mode.role = "WORKER";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$rootScope.endpointMode = mode;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
.factory('Authentication', ['$q', '$rootScope', 'Auth', 'jwtHelper', 'localStorageService', function AuthenticationFactory($q, $rootScope, Auth, jwtHelper, localStorageService) {
|
|
||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
init: function() {
|
init: function() {
|
||||||
var jwt = localStorageService.get('JWT');
|
var jwt = LocalStorage.getJWT();
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
var tokenPayload = jwtHelper.decodeToken(jwt);
|
var tokenPayload = jwtHelper.decodeToken(jwt);
|
||||||
$rootScope.username = tokenPayload.username;
|
$rootScope.username = tokenPayload.username;
|
||||||
|
@ -287,7 +254,7 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize'])
|
||||||
return $q(function (resolve, reject) {
|
return $q(function (resolve, reject) {
|
||||||
Auth.login({username: username, password: password}).$promise
|
Auth.login({username: username, password: password}).$promise
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
localStorageService.set('JWT', data.jwt);
|
LocalStorage.storeJWT(data.jwt);
|
||||||
$rootScope.username = username;
|
$rootScope.username = username;
|
||||||
resolve();
|
resolve();
|
||||||
}, function() {
|
}, function() {
|
||||||
|
@ -296,10 +263,10 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize'])
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
logout: function() {
|
logout: function() {
|
||||||
localStorageService.remove('JWT');
|
LocalStorage.deleteJWT();
|
||||||
},
|
},
|
||||||
isAuthenticated: function() {
|
isAuthenticated: function() {
|
||||||
var jwt = localStorageService.get('JWT');
|
var jwt = LocalStorage.getJWT();
|
||||||
return jwt && !jwtHelper.isTokenExpired(jwt);
|
return jwt && !jwtHelper.isTokenExpired(jwt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -359,7 +326,69 @@ angular.module('portainer.services', ['ngResource', 'ngSanitize'])
|
||||||
setActiveEndpoint: { method: 'POST', params: { id: '@id', action: 'active' } }
|
setActiveEndpoint: { method: 'POST', params: { id: '@id', action: 'active' } }
|
||||||
});
|
});
|
||||||
}])
|
}])
|
||||||
.factory('EndpointService', ['$q', '$timeout', 'Endpoints', 'FileUploadService', function EndpointServiceFactory($q, $timeout, Endpoints, FileUploadService) {
|
.factory('LocalStorage', ['localStorageService', function LocalStorageFactory(localStorageService) {
|
||||||
|
'use strict';
|
||||||
|
return {
|
||||||
|
storeEndpointState: function(state) {
|
||||||
|
localStorageService.set('ENDPOINT_STATE', state);
|
||||||
|
},
|
||||||
|
getEndpointState: function() {
|
||||||
|
return localStorageService.get('ENDPOINT_STATE');
|
||||||
|
},
|
||||||
|
storeJWT: function(jwt) {
|
||||||
|
localStorageService.set('JWT', jwt);
|
||||||
|
},
|
||||||
|
getJWT: function() {
|
||||||
|
return localStorageService.get('JWT');
|
||||||
|
},
|
||||||
|
deleteJWT: function() {
|
||||||
|
localStorageService.remove('JWT');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
.factory('StateManager', ['$q', 'Info', 'InfoHelper', 'Version', 'LocalStorage', function StateManagerFactory($q, Info, InfoHelper, Version, LocalStorage) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var state = {
|
||||||
|
loading: true,
|
||||||
|
application: {},
|
||||||
|
endpoint: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: function() {
|
||||||
|
var endpointState = LocalStorage.getEndpointState();
|
||||||
|
if (endpointState) {
|
||||||
|
state.endpoint = endpointState;
|
||||||
|
}
|
||||||
|
state.loading = false;
|
||||||
|
},
|
||||||
|
updateEndpointState: function(loading) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
if (loading) {
|
||||||
|
state.loading = true;
|
||||||
|
}
|
||||||
|
$q.all([Info.get({}).$promise, Version.get({}).$promise])
|
||||||
|
.then(function success(data) {
|
||||||
|
var endpointMode = InfoHelper.determineEndpointMode(data[0]);
|
||||||
|
var endpointAPIVersion = parseFloat(data[1].ApiVersion);
|
||||||
|
state.endpoint.mode = endpointMode;
|
||||||
|
state.endpoint.apiVersion = endpointAPIVersion;
|
||||||
|
LocalStorage.storeEndpointState(state.endpoint);
|
||||||
|
state.loading = false;
|
||||||
|
deferred.resolve();
|
||||||
|
}, function error(err) {
|
||||||
|
state.loading = false;
|
||||||
|
deferred.reject({msg: 'Unable to connect to the Docker endpoint', err: err});
|
||||||
|
});
|
||||||
|
return deferred.promise;
|
||||||
|
},
|
||||||
|
getState: function() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
.factory('EndpointService', ['$q', 'Endpoints', 'FileUploadService', function EndpointServiceFactory($q, Endpoints, FileUploadService) {
|
||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
getActive: function() {
|
getActive: function() {
|
||||||
|
|
|
@ -213,7 +213,6 @@ input[type="radio"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-wrapper {
|
.page-wrapper {
|
||||||
margin-top: 25px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -398,14 +398,14 @@ module.exports = function (grunt) {
|
||||||
command: [
|
command: [
|
||||||
'docker stop portainer',
|
'docker stop portainer',
|
||||||
'docker rm portainer',
|
'docker rm portainer',
|
||||||
'docker run --privileged -d -p 9000:9000 -v /tmp/portainer:/data -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer -d /data'
|
'docker run --privileged -d -p 9000:9000 -v /tmp/portainer:/data -v /var/run/docker.sock:/var/run/docker.sock --name portainer portainer'
|
||||||
].join(';')
|
].join(';')
|
||||||
},
|
},
|
||||||
runSwarm: {
|
runSwarm: {
|
||||||
command: [
|
command: [
|
||||||
'docker stop portainer',
|
'docker stop portainer',
|
||||||
'docker rm portainer',
|
'docker rm portainer',
|
||||||
'docker run -d -p 9000:9000 -v /tmp/portainer:/data --name portainer portainer -H tcp://10.0.7.10:2375 -d /data'
|
'docker run -d -p 9000:9000 -v /tmp/portainer:/data --name portainer portainer -H tcp://10.0.7.10:2375'
|
||||||
].join(';')
|
].join(';')
|
||||||
},
|
},
|
||||||
runSwarmLocal: {
|
runSwarmLocal: {
|
||||||
|
@ -419,7 +419,7 @@ module.exports = function (grunt) {
|
||||||
command: [
|
command: [
|
||||||
'docker stop portainer',
|
'docker stop portainer',
|
||||||
'docker rm portainer',
|
'docker rm portainer',
|
||||||
'docker run -d -p 9000:9000 -v /tmp/portainer:/data -v /tmp/docker-ssl:/certs --name portainer portainer -H tcp://10.0.7.10:2376 -d /data --tlsverify'
|
'docker run -d -p 9000:9000 -v /tmp/portainer:/data -v /tmp/docker-ssl:/certs --name portainer portainer -H tcp://10.0.7.10:2376 --tlsverify'
|
||||||
].join(';')
|
].join(';')
|
||||||
},
|
},
|
||||||
cleanImages: {
|
cleanImages: {
|
||||||
|
|
27
index.html
27
index.html
|
@ -29,15 +29,36 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body ng-controller="MainController">
|
<body ng-controller="MainController">
|
||||||
<div id="page-wrapper" ng-class="{open: toggle && $state.current.name !== 'auth' && $state.current.name !== 'endpointInit', nopadding: $state.current.name === 'auth' || $state.current.name === 'endpointInit'}" ng-cloak>
|
<div id="page-wrapper" ng-class="{open: toggle && $state.current.name !== 'auth' && $state.current.name !== 'endpointInit' && $state.current.name !== 'init', nopadding: $state.current.name === 'auth' || $state.current.name === 'endpointInit' || $state.current.name === 'init' || applicationState.loading }" ng-cloak>
|
||||||
|
|
||||||
<div id="sideview" ui-view="sidebar"></div>
|
<div id="sideview" ui-view="sidebar" ng-if="!applicationState.loading"></div>
|
||||||
|
|
||||||
<div id="content-wrapper">
|
<div id="content-wrapper">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
|
|
||||||
|
<div class="page-wrapper" ng-if="applicationState.loading">
|
||||||
|
<!-- loading box -->
|
||||||
|
<div class="container simple-box">
|
||||||
|
<div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3">
|
||||||
|
<!-- loading box logo -->
|
||||||
|
<div class="row">
|
||||||
|
<img ng-if="logo" ng-src="{{ logo }}" class="simple-box-logo">
|
||||||
|
<img ng-if="!logo" src="images/logo_alt.png" class="simple-box-logo" alt="Portainer">
|
||||||
|
</div>
|
||||||
|
<!-- !loading box logo -->
|
||||||
|
<!-- panel -->
|
||||||
|
<div class="row" style="text-align: center">
|
||||||
|
Connecting to the Docker enpoint...
|
||||||
|
<i class="fa fa-cog fa-spin" style="margin-left: 5px"></i>
|
||||||
|
</div>
|
||||||
|
<!-- !panel -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- !loading box -->
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<div id="view" ui-view="content"></div>
|
<div id="view" ui-view="content" ng-if="!applicationState.loading"></div>
|
||||||
|
|
||||||
</div><!-- End Page Content -->
|
</div><!-- End Page Content -->
|
||||||
</div><!-- End Content Wrapper -->
|
</div><!-- End Content Wrapper -->
|
||||||
|
|
Loading…
Reference in New Issue