mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 13:55:21 +02:00
feat(registries): add registry management (#930)
This commit is contained in:
parent
9360f24d89
commit
08c5a5a4f6
75 changed files with 2317 additions and 621 deletions
|
@ -17,11 +17,11 @@
|
|||
<!-- !access-control-switch -->
|
||||
<!-- restricted-access -->
|
||||
<div class="form-group" ng-if="formValues.enableAccessControl" style="margin-bottom: 0">
|
||||
<div class="ownership_wrapper">
|
||||
<div class="boxselector_wrapper">
|
||||
<div ng-if="isAdmin">
|
||||
<input type="radio" id="access_administrators" ng-model="formValues.Ownership" ng-click="synchronizeFormData()" value="administrators">
|
||||
<label for="access_administrators">
|
||||
<div class="ownership_header">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'administrators' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Administrators
|
||||
</div>
|
||||
|
@ -31,7 +31,7 @@
|
|||
<div ng-if="isAdmin">
|
||||
<input type="radio" id="access_restricted" ng-model="formValues.Ownership" ng-click="synchronizeFormData()" value="restricted">
|
||||
<label for="access_restricted">
|
||||
<div class="ownership_header">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Restricted
|
||||
</div>
|
||||
|
@ -43,7 +43,7 @@
|
|||
<div ng-if="!isAdmin">
|
||||
<input type="radio" id="access_private" ng-model="formValues.Ownership" ng-click="synchronizeFormData()" value="private">
|
||||
<label for="access_private">
|
||||
<div class="ownership_header">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'private' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Private
|
||||
</div>
|
||||
|
@ -55,7 +55,7 @@
|
|||
<div ng-if="!isAdmin && availableTeams.length > 0">
|
||||
<input type="radio" id="access_restricted" ng-model="formValues.Ownership" ng-click="synchronizeFormData()" value="restricted">
|
||||
<label for="access_restricted">
|
||||
<div class="ownership_header">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Restricted
|
||||
</div>
|
||||
|
|
|
@ -63,11 +63,11 @@
|
|||
<!-- edit-ownership-choices -->
|
||||
<tr ng-if="state.editOwnership">
|
||||
<td colspan="2">
|
||||
<div class="ownership_wrapper">
|
||||
<div class="boxselector_wrapper">
|
||||
<div ng-if="isAdmin">
|
||||
<input type="radio" id="access_administrators" ng-model="formValues.Ownership" value="administrators">
|
||||
<label for="access_administrators">
|
||||
<div class="ownership_header">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'administrators' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Administrators
|
||||
</div>
|
||||
|
@ -77,7 +77,7 @@
|
|||
<div ng-if="isAdmin">
|
||||
<input type="radio" id="access_restricted" ng-model="formValues.Ownership" value="restricted">
|
||||
<label for="access_restricted">
|
||||
<div class="ownership_header">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Restricted
|
||||
</div>
|
||||
|
@ -89,7 +89,7 @@
|
|||
<div ng-if="!isAdmin && state.canChangeOwnershipToTeam && availableTeams.length > 0">
|
||||
<input type="radio" id="access_restricted" ng-model="formValues.Ownership" value="restricted">
|
||||
<label for="access_restricted">
|
||||
<div class="ownership_header">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'restricted' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Restricted
|
||||
</div>
|
||||
|
@ -104,7 +104,7 @@
|
|||
<div>
|
||||
<input type="radio" id="access_public" ng-model="formValues.Ownership" value="public">
|
||||
<label for="access_public">
|
||||
<div class="ownership_header">
|
||||
<div class="boxselector_header">
|
||||
<i ng-class="'public' | ownershipicon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Public
|
||||
</div>
|
||||
|
|
|
@ -134,21 +134,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- !tag-description -->
|
||||
<!-- name-and-registry-inputs -->
|
||||
<!-- image-and-registry -->
|
||||
<div class="form-group">
|
||||
<label for="image_name" class="col-sm-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-11 col-md-6">
|
||||
<input type="text" class="form-control" ng-model="config.Image" id="image_name" placeholder="e.g. myImage:myTag">
|
||||
</div>
|
||||
<label for="image_registry" class="col-sm-2 margin-sm-top control-label text-left">
|
||||
Registry
|
||||
<portainer-tooltip position="bottom" message="A registry to pull the image from. Leave empty to use the official Docker registry."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-10 col-md-3 margin-sm-top">
|
||||
<input type="text" class="form-control" ng-model="config.Registry" id="image_registry" placeholder="optional">
|
||||
</div>
|
||||
<por-image-registry image="config.Image" registry="config.Registry"></por-image-registry>
|
||||
</div>
|
||||
<!-- !name-and-registry-inputs -->
|
||||
<!-- !image-and-registry -->
|
||||
<!-- tag-note -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('containers', [])
|
||||
.controller('ContainersController', ['$q', '$scope', '$filter', 'Container', 'ContainerService', 'ContainerHelper', 'Info', 'Notifications', 'Pagination', 'EntityListService', 'ModalService', 'ResourceControlService', 'EndpointProvider',
|
||||
function ($q, $scope, $filter, Container, ContainerService, ContainerHelper, Info, Notifications, Pagination, EntityListService, ModalService, ResourceControlService, EndpointProvider) {
|
||||
.controller('ContainersController', ['$q', '$scope', '$filter', 'Container', 'ContainerService', 'ContainerHelper', 'SystemService', 'Notifications', 'Pagination', 'EntityListService', 'ModalService', 'ResourceControlService', 'EndpointProvider',
|
||||
function ($q, $scope, $filter, Container, ContainerService, ContainerHelper, SystemService, Notifications, Pagination, EntityListService, ModalService, ResourceControlService, EndpointProvider) {
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('containers');
|
||||
$scope.state.displayAll = true;
|
||||
|
@ -202,15 +202,18 @@ angular.module('containers', [])
|
|||
return swarm_hosts;
|
||||
}
|
||||
|
||||
function initView(){
|
||||
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') {
|
||||
Info.get({}, function (d) {
|
||||
function initView() {
|
||||
var provider = $scope.applicationState.endpoint.mode.provider;
|
||||
$q.when(provider !== 'DOCKER_SWARM' || SystemService.info())
|
||||
.then(function success(data) {
|
||||
if (provider === 'DOCKER_SWARM') {
|
||||
$scope.swarm_hosts = retrieveSwarmHostsInfo(d);
|
||||
update({all: $scope.state.displayAll ? 1 : 0});
|
||||
});
|
||||
} else {
|
||||
}
|
||||
update({all: $scope.state.displayAll ? 1 : 0});
|
||||
}
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve cluster information');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
|
||||
// See app/components/templates/templatesController.js as a reference.
|
||||
angular.module('createContainer', [])
|
||||
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Info', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'Network', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($q, $scope, $state, $stateParams, $filter, Info, Container, ContainerHelper, Image, ImageHelper, Volume, Network, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, ControllerDataPipeline, FormValidator) {
|
||||
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'Network', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, Network, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, ControllerDataPipeline, FormValidator) {
|
||||
|
||||
$scope.formValues = {
|
||||
alwaysPull: true,
|
||||
|
@ -94,7 +94,7 @@ function ($q, $scope, $state, $stateParams, $filter, Info, Container, ContainerH
|
|||
function prepareImageConfig(config) {
|
||||
var image = config.Image;
|
||||
var registry = $scope.formValues.Registry;
|
||||
var imageConfig = ImageHelper.createImageConfigForContainer(image, registry);
|
||||
var imageConfig = ImageHelper.createImageConfigForContainer(image, registry.URL);
|
||||
config.Image = imageConfig.fromImage + ':' + imageConfig.tag;
|
||||
$scope.imageConfig = imageConfig;
|
||||
}
|
||||
|
|
|
@ -21,21 +21,11 @@
|
|||
<div class="col-sm-12 form-section-title">
|
||||
Image configuration
|
||||
</div>
|
||||
<!-- image-and-registry-inputs -->
|
||||
<!-- image-and-registry -->
|
||||
<div class="form-group">
|
||||
<label for="container_image" class="col-sm-1 control-label text-left">Image</label>
|
||||
<div class="col-sm-11 col-md-6">
|
||||
<input type="text" class="form-control" ng-model="config.Image" id="container_image" placeholder="e.g. ubuntu:trusty">
|
||||
</div>
|
||||
<label for="image_registry" class="col-sm-2 margin-sm-top control-label text-left">
|
||||
Registry
|
||||
<portainer-tooltip position="bottom" message="A registry to pull the image from. Leave empty to use the official Docker registry."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-10 col-md-3 margin-sm-top">
|
||||
<input type="text" class="form-control" ng-model="formValues.Registry" id="image_registry" placeholder="e.g. myregistry.mydomain">
|
||||
</div>
|
||||
<por-image-registry image="config.Image" registry="formValues.Registry"></por-image-registry>
|
||||
</div>
|
||||
<!-- !image-and-registry-inputs -->
|
||||
<!-- !image-and-registry -->
|
||||
<!-- always-pull -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
|
|
49
app/components/createRegistry/createRegistryController.js
Normal file
49
app/components/createRegistry/createRegistryController.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
angular.module('createRegistry', [])
|
||||
.controller('CreateRegistryController', ['$scope', '$state', 'RegistryService', 'Notifications',
|
||||
function ($scope, $state, RegistryService, Notifications) {
|
||||
|
||||
$scope.state = {
|
||||
RegistryType: 'quay'
|
||||
};
|
||||
|
||||
$scope.formValues = {
|
||||
Name: 'Quay',
|
||||
URL: 'quay.io',
|
||||
Authentication: true,
|
||||
Username: '',
|
||||
Password: ''
|
||||
};
|
||||
|
||||
$scope.selectQuayRegistry = function() {
|
||||
$scope.formValues.Name = 'Quay';
|
||||
$scope.formValues.URL = 'quay.io';
|
||||
$scope.formValues.Authentication = true;
|
||||
};
|
||||
|
||||
$scope.selectCustomRegistry = function() {
|
||||
$scope.formValues.Name = '';
|
||||
$scope.formValues.URL = '';
|
||||
$scope.formValues.Authentication = false;
|
||||
};
|
||||
|
||||
$scope.addRegistry = function() {
|
||||
$('#createRegistrySpinner').show();
|
||||
var registryName = $scope.formValues.Name;
|
||||
var registryURL = $scope.formValues.URL.replace(/^https?\:\/\//i, '');
|
||||
var authentication = $scope.formValues.Authentication;
|
||||
var username = $scope.formValues.Username;
|
||||
var password = $scope.formValues.Password;
|
||||
|
||||
RegistryService.createRegistry(registryName, registryURL, authentication, username, password)
|
||||
.then(function success(data) {
|
||||
Notifications.success('Registry successfully created');
|
||||
$state.go('registries');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to create registry');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#createRegistrySpinner').hide();
|
||||
});
|
||||
};
|
||||
}]);
|
117
app/components/createRegistry/createregistry.html
Normal file
117
app/components/createRegistry/createregistry.html
Normal file
|
@ -0,0 +1,117 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="Create registry">
|
||||
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="display:none"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="registries">Registries</a> > Add registry
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Registry provider
|
||||
</div>
|
||||
<div class="form-group"></div>
|
||||
<div class="form-group" style="margin-bottom: 0">
|
||||
<div class="boxselector_wrapper">
|
||||
<div ng-click="selectQuayRegistry()">
|
||||
<input type="radio" id="registry_quay" ng-model="state.RegistryType" value="quay">
|
||||
<label for="registry_quay">
|
||||
<div class="boxselector_header">
|
||||
<i class="fa fa-database" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Quay.io
|
||||
</div>
|
||||
<p>Quay container registry</p>
|
||||
</label>
|
||||
</div>
|
||||
<div ng-click="selectCustomRegistry()">
|
||||
<input type="radio" id="registry_custom" ng-model="state.RegistryType" value="custom">
|
||||
<label for="registry_custom">
|
||||
<div class="boxselector_header">
|
||||
<i class="fa fa-database" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Custom registry
|
||||
</div>
|
||||
<p>Define your own registry</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title" ng-if="state.RegistryType === 'custom'">
|
||||
Important notice
|
||||
</div>
|
||||
<div class="form-group" ng-if="state.RegistryType === 'custom'">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
Docker requires you to connect to a <a href="https://docs.docker.com/registry/deploying/#running-a-domain-registry" target="_blank">secure registry</a>.
|
||||
You can find more information about how to connect to an insecure registry <a href="https://docs.docker.com/registry/insecure/" target="_blank">in the Docker documentation</a>.
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Registry details
|
||||
</div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group" ng-if="state.RegistryType === 'custom'">
|
||||
<label for="registry_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="registry_name" ng-model="formValues.Name" placeholder="e.g. my-registry">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- registry-url-input -->
|
||||
<div class="form-group" ng-if="state.RegistryType === 'custom'">
|
||||
<label for="registry_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Registry URL
|
||||
<portainer-tooltip position="bottom" message="URL or IP address of a Docker registry. Any protocol will be stripped."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="registry_url" ng-model="formValues.URL" placeholder="e.g. 10.0.0.10:5000 or myregistry.domain.tld">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !registry-url-input -->
|
||||
<!-- authentication-checkbox -->
|
||||
<div class="form-group" ng-if="state.RegistryType === 'custom'">
|
||||
<div class="col-sm-12">
|
||||
<label for="registry_auth" class="control-label text-left">
|
||||
Authentication
|
||||
<portainer-tooltip position="bottom" message="Enable this option if you need to specify credentials to connect to this registry."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;">
|
||||
<input type="checkbox" ng-model="formValues.Authentication"><i></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !authentication-checkbox -->
|
||||
<!-- authentication-credentials -->
|
||||
<div ng-if="formValues.Authentication || state.RegistryType === 'quay'">
|
||||
<!-- credentials-user -->
|
||||
<div class="form-group">
|
||||
<label for="credentials_username" class="col-sm-3 col-lg-2 control-label text-left">Username</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="credentials_username" ng-model="formValues.Username">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !credentials-user -->
|
||||
<!-- credentials-password -->
|
||||
<div class="form-group">
|
||||
<label for="credentials_password" class="col-sm-3 col-lg-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="password" class="form-control" id="credentials_password" ng-model="formValues.Password">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !credentials-password -->
|
||||
</div>
|
||||
<!-- !authentication-credentials -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Name || !formValues.URL || (formValues.Authentication && (!formValues.Username || !formValues.Password))" ng-click="addRegistry()">Add registry</button>
|
||||
<i id="createRegistrySpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
|
@ -1,13 +1,13 @@
|
|||
// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
|
||||
// See app/components/templates/templatesController.js as a reference.
|
||||
angular.module('createService', [])
|
||||
.controller('CreateServiceController', ['$q', '$scope', '$state', 'Service', 'ServiceHelper', 'SecretHelper', 'SecretService', 'VolumeService', 'NetworkService', 'ImageHelper', 'Authentication', 'ResourceControlService', 'Notifications', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($q, $scope, $state, Service, ServiceHelper, SecretHelper, SecretService, VolumeService, NetworkService, ImageHelper, Authentication, ResourceControlService, Notifications, ControllerDataPipeline, FormValidator) {
|
||||
.controller('CreateServiceController', ['$q', '$scope', '$state', 'Service', 'ServiceHelper', 'SecretHelper', 'SecretService', 'VolumeService', 'NetworkService', 'ImageHelper', 'Authentication', 'ResourceControlService', 'Notifications', 'ControllerDataPipeline', 'FormValidator', 'RegistryService', 'HttpRequestHelper',
|
||||
function ($q, $scope, $state, Service, ServiceHelper, SecretHelper, SecretService, VolumeService, NetworkService, ImageHelper, Authentication, ResourceControlService, Notifications, ControllerDataPipeline, FormValidator, RegistryService, HttpRequestHelper) {
|
||||
|
||||
$scope.formValues = {
|
||||
Name: '',
|
||||
Image: '',
|
||||
Registry: '',
|
||||
Registry: {},
|
||||
Mode: 'replicated',
|
||||
Replicas: 1,
|
||||
Command: '',
|
||||
|
@ -105,7 +105,7 @@ function ($q, $scope, $state, Service, ServiceHelper, SecretHelper, SecretServic
|
|||
};
|
||||
|
||||
function prepareImageConfig(config, input) {
|
||||
var imageConfig = ImageHelper.createImageConfigForContainer(input.Image, input.Registry);
|
||||
var imageConfig = ImageHelper.createImageConfigForContainer(input.Image, input.Registry.URL);
|
||||
config.TaskTemplate.ContainerSpec.Image = imageConfig.fromImage + ':' + imageConfig.tag;
|
||||
}
|
||||
|
||||
|
@ -257,6 +257,10 @@ function ($q, $scope, $state, Service, ServiceHelper, SecretHelper, SecretServic
|
|||
}
|
||||
|
||||
function createNewService(config, accessControlData) {
|
||||
|
||||
var registry = $scope.formValues.Registry;
|
||||
var authenticationDetails = registry.Authentication ? RegistryService.encodedCredentials(registry) : '';
|
||||
HttpRequestHelper.setRegistryAuthenticationHeader(authenticationDetails);
|
||||
Service.create(config).$promise
|
||||
.then(function success(data) {
|
||||
var serviceIdentifier = data.ID;
|
||||
|
|
|
@ -23,21 +23,11 @@
|
|||
<div class="col-sm-12 form-section-title">
|
||||
Image configuration
|
||||
</div>
|
||||
<!-- image-and-registry-inputs -->
|
||||
<!-- image-and-registry -->
|
||||
<div class="form-group">
|
||||
<label for="service_image" class="col-sm-1 control-label text-left">Image</label>
|
||||
<div class="col-sm-11 col-md-6">
|
||||
<input type="text" class="form-control" ng-model="formValues.Image" id="service_image" placeholder="e.g. nginx:latest">
|
||||
</div>
|
||||
<label for="image_registry" class="col-sm-2 margin-sm-top control-label text-left">
|
||||
Registry
|
||||
<portainer-tooltip position="bottom" message="A registry to pull the image from. Leave empty to use the official Docker registry."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-10 col-md-3 margin-sm-top">
|
||||
<input type="text" class="form-control" ng-model="formValues.Registry" id="image_registry" placeholder="e.g. myregistry.mydomain">
|
||||
</div>
|
||||
<por-image-registry image="formValues.Image" registry="formValues.Registry"></por-image-registry>
|
||||
</div>
|
||||
<!-- !image-and-registry-inputs -->
|
||||
<!-- !image-and-registry -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Scheduling
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('createVolume', [])
|
||||
.controller('CreateVolumeController', ['$scope', '$state', 'VolumeService', 'InfoService', 'ResourceControlService', 'Authentication', 'Notifications', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($scope, $state, VolumeService, InfoService, ResourceControlService, Authentication, Notifications, ControllerDataPipeline, FormValidator) {
|
||||
.controller('CreateVolumeController', ['$scope', '$state', 'VolumeService', 'SystemService', 'ResourceControlService', 'Authentication', 'Notifications', 'ControllerDataPipeline', 'FormValidator',
|
||||
function ($scope, $state, VolumeService, SystemService, ResourceControlService, Authentication, Notifications, ControllerDataPipeline, FormValidator) {
|
||||
|
||||
$scope.formValues = {
|
||||
Driver: 'local',
|
||||
|
@ -69,7 +69,7 @@ function ($scope, $state, VolumeService, InfoService, ResourceControlService, Au
|
|||
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
InfoService.getVolumePlugins()
|
||||
SystemService.getVolumePlugins()
|
||||
.then(function success(data) {
|
||||
$scope.availableVolumeDrivers = data;
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('dashboard', [])
|
||||
.controller('DashboardController', ['$scope', '$q', 'Container', 'ContainerHelper', 'Image', 'Network', 'Volume', 'Info', 'Notifications',
|
||||
function ($scope, $q, Container, ContainerHelper, Image, Network, Volume, Info, Notifications) {
|
||||
.controller('DashboardController', ['$scope', '$q', 'Container', 'ContainerHelper', 'Image', 'Network', 'Volume', 'SystemService', 'Notifications',
|
||||
function ($scope, $q, Container, ContainerHelper, Image, Network, Volume, SystemService, Notifications) {
|
||||
|
||||
$scope.containerData = {
|
||||
total: 0
|
||||
|
@ -68,7 +68,7 @@ function ($scope, $q, Container, ContainerHelper, Image, Network, Volume, Info,
|
|||
Image.query({}).$promise,
|
||||
Volume.query({}).$promise,
|
||||
Network.query({}).$promise,
|
||||
Info.get({}).$promise
|
||||
SystemService.info()
|
||||
]).then(function (d) {
|
||||
prepareContainerData(d[0]);
|
||||
prepareImageData(d[1]);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<rd-header-content>Docker</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row" ng-if="state.loaded">
|
||||
<div class="row" ng-if="info && version">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-code" title="Engine version"></rd-widget-header>
|
||||
|
@ -50,7 +50,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="state.loaded">
|
||||
<div class="row" ng-if="info && version">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-th" title="Engine status"></rd-widget-header>
|
||||
|
@ -92,7 +92,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="state.loaded && info.Plugins">
|
||||
<div class="row" ng-if="info && info.Plugins">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-plug" title="Engine plugins"></rd-widget-header>
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
angular.module('docker', [])
|
||||
.controller('DockerController', ['$scope', 'Info', 'Version', 'Notifications',
|
||||
function ($scope, Info, Version, Notifications) {
|
||||
$scope.state = {
|
||||
loaded: false
|
||||
};
|
||||
.controller('DockerController', ['$q', '$scope', 'SystemService', 'Notifications',
|
||||
function ($q, $scope, SystemService, Notifications) {
|
||||
$scope.info = {};
|
||||
$scope.version = {};
|
||||
|
||||
Info.get({}, function (infoData) {
|
||||
$scope.info = infoData;
|
||||
Version.get({}, function (versionData) {
|
||||
$scope.version = versionData;
|
||||
$scope.state.loaded = true;
|
||||
$('#loadingViewSpinner').hide();
|
||||
}, function (e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve engine details');
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
$q.all({
|
||||
version: SystemService.version(),
|
||||
info: SystemService.info()
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.version = data.version;
|
||||
$scope.info = data.info;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve engine details');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}, function (e) {
|
||||
Notifications.error('Failure', e, 'Unable to retrieve engine information');
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
||||
|
|
|
@ -41,137 +41,5 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="endpoint">
|
||||
<div class="col-sm-6">
|
||||
<rd-widget>
|
||||
<rd-widget-header classes="col-sm-12 col-md-6 nopadding" icon="fa-users" title="Users and groups">
|
||||
<div class="pull-md-right pull-lg-right">
|
||||
Items per page:
|
||||
<select ng-model="state.pagination_count_accesses" ng-change="changePaginationCountAccesses()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-taskbar classes="col-sm-12 nopadding">
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<button class="btn btn-primary btn-sm" ng-click="authorizeAllAccesses()" ng-disabled="accesses.length === 0 || filteredUsers.length === 0"><i class="fa fa-user-plus space-right" aria-hidden="true"></i>Authorize all</button>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<input type="text" id="filter" ng-model="state.filterUsers" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})" ng-click="orderAccesses('Name')">
|
||||
Name
|
||||
<span ng-show="sortTypeAccesses == 'Name' && !sortReverseAccesses" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortTypeAccesses == 'Name' && sortReverseAccesses" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})" ng-click="orderAccesses('Type')">
|
||||
Type
|
||||
<span ng-show="sortTypeAccesses == 'Type' && !sortReverseAccesses" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortTypeAccesses == 'Type' && sortReverseAccesses" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-click="authorizeAccess(user)" class="interactive" dir-paginate="user in accesses | filter:state.filterUsers | orderBy:sortTypeAccesses:sortReverseAccesses | itemsPerPage: state.pagination_count_accesses">
|
||||
<td>{{ user.Name }}</td>
|
||||
<td>
|
||||
<i class="fa" ng-class="user.Type === 'user' ? 'fa-user' : 'fa-users'" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
{{ user.Type }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!accesses">
|
||||
<td colspan="2" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="accesses.length === 0 || (accesses | filter:state.filterUsers | orderBy:sortTypeAccesses:sortReverseAccesses | itemsPerPage: state.pagination_count_accesses).length === 0">
|
||||
<td colspan="2" class="text-center text-muted">No user or team available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="accesses" class="pull-left pagination-controls">
|
||||
<dir-pagination-controls></dir-pagination-controls>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<rd-widget>
|
||||
<rd-widget-header classes="col-sm-12 col-md-6 nopadding" icon="fa-users" title="Authorized users and groups">
|
||||
<div class="pull-md-right pull-lg-right">
|
||||
Items per page:
|
||||
<select ng-model="state.pagination_count_authorizedAccesses" ng-change="changePaginationCountAuthorizedAccesses()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-taskbar classes="col-sm-12 nopadding">
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<button class="btn btn-primary btn-sm" ng-click="unauthorizeAllAccesses()" ng-disabled="authorizedAccesses.length === 0 || filteredAuthorizedUsers.length === 0"><i class="fa fa-user-times space-right" aria-hidden="true"></i>Deny all</button>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<input type="text" id="filter" ng-model="state.filterAuthorizedUsers" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})" ng-click="orderAuthorizedAccesses('Name')">
|
||||
Name
|
||||
<span ng-show="sortTypeAuthorizedAccesses == 'Name' && !sortReverseAuthorizedAccesses" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortTypeAuthorizedAccesses == 'Name' && sortReverseAuthorizedAccesses" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="endpoint.access({id: endpoint.Id})" ng-click="orderAuthorizedAccesses('Type')">
|
||||
Type
|
||||
<span ng-show="sortTypeAuthorizedAccesses == 'Type' && !sortReverseAuthorizedAccesses" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortTypeAuthorizedAccesses == 'Type' && sortReverseAuthorizedAccesses" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-click="unauthorizeAccess(user)" class="interactive" pagination-id="table_authaccess" dir-paginate="user in authorizedAccesses | filter:state.filterAuthorizedUsers | orderBy:sortTypeAuthorizedAccesses:sortReverseAuthorizedAccesses | itemsPerPage: state.pagination_count_authorizedAccesses">
|
||||
<td>{{ user.Name }}</td>
|
||||
<td>
|
||||
<i class="fa" ng-class="user.Type === 'user' ? 'fa-user' : 'fa-users'" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
{{ user.Type }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!authorizedAccesses">
|
||||
<td colspan="2" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="authorizedAccesses.length === 0 || (authorizedAccesses | filter:state.filterAuthorizedUsers | orderBy:sortTypeAuthorizedAccesses:sortReverseAuthorizedAccesses | itemsPerPage: state.pagination_count_authorizedAccesses).length === 0">
|
||||
<td colspan="2" class="text-center text-muted">No authorized user or team.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="authorizedAccesses" class="pull-left pagination-controls">
|
||||
<dir-pagination-controls pagination-id="table_authaccess"></dir-pagination-controls>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
<por-access-management ng-if="endpoint" access-controlled-entity="endpoint" update-access="updateAccess(userAccesses, teamAccesses)">
|
||||
</por-access-management>
|
||||
|
|
|
@ -1,177 +1,18 @@
|
|||
angular.module('endpointAccess', [])
|
||||
.controller('EndpointAccessController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'EndpointService', 'UserService', 'TeamService', 'Pagination', 'Notifications',
|
||||
function ($q, $scope, $state, $stateParams, $filter, EndpointService, UserService, TeamService, Pagination, Notifications) {
|
||||
.controller('EndpointAccessController', ['$scope', '$stateParams', 'EndpointService', 'Notifications',
|
||||
function ($scope, $stateParams, EndpointService, Notifications) {
|
||||
|
||||
$scope.state = {
|
||||
pagination_count_accesses: Pagination.getPaginationCount('endpoint_access_accesses'),
|
||||
pagination_count_authorizedAccesses: Pagination.getPaginationCount('endpoint_access_authorizedAccesses')
|
||||
};
|
||||
|
||||
$scope.sortTypeAccesses = 'Type';
|
||||
$scope.sortReverseAccesses = false;
|
||||
|
||||
$scope.orderAccesses = function(sortType) {
|
||||
$scope.sortReverseAccesses = ($scope.sortTypeAccesses === sortType) ? !$scope.sortReverseAccesses : false;
|
||||
$scope.sortTypeAccesses = sortType;
|
||||
};
|
||||
|
||||
$scope.changePaginationCountAccesses = function() {
|
||||
Pagination.setPaginationCount('endpoint_access_accesses', $scope.state.pagination_count_accesses);
|
||||
};
|
||||
|
||||
$scope.sortTypeAuthorizedAccesses = 'Type';
|
||||
$scope.sortReverseAuthorizedAccesses = false;
|
||||
|
||||
$scope.orderAuthorizedAccesses = function(sortType) {
|
||||
$scope.sortReverseAuthorizedAccesses = ($scope.sortTypeAuthorizedAccesses === sortType) ? !$scope.sortReverseAuthorizedAccesses : false;
|
||||
$scope.sortTypeAuthorizedAccesses = sortType;
|
||||
};
|
||||
|
||||
$scope.changePaginationCountAuthorizedAccesses = function() {
|
||||
Pagination.setPaginationCount('endpoint_access_authorizedAccesses', $scope.state.pagination_count_authorizedAccesses);
|
||||
};
|
||||
|
||||
$scope.authorizeAllAccesses = function() {
|
||||
var authorizedUsers = [];
|
||||
var authorizedTeams = [];
|
||||
angular.forEach($scope.authorizedAccesses, function (a) {
|
||||
if (a.Type === 'user') {
|
||||
authorizedUsers.push(a.Id);
|
||||
} else if (a.Type === 'team') {
|
||||
authorizedTeams.push(a.Id);
|
||||
}
|
||||
});
|
||||
angular.forEach($scope.accesses, function (a) {
|
||||
if (a.Type === 'user') {
|
||||
authorizedUsers.push(a.Id);
|
||||
} else if (a.Type === 'team') {
|
||||
authorizedTeams.push(a.Id);
|
||||
}
|
||||
});
|
||||
|
||||
EndpointService.updateAccess($stateParams.id, authorizedUsers, authorizedTeams)
|
||||
.then(function success(data) {
|
||||
$scope.authorizedAccesses = $scope.authorizedAccesses.concat($scope.accesses);
|
||||
$scope.accesses = [];
|
||||
Notifications.success('Endpoint accesses successfully updated');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update endpoint accesses');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.unauthorizeAllAccesses = function() {
|
||||
EndpointService.updateAccess($stateParams.id, [], [])
|
||||
.then(function success(data) {
|
||||
$scope.accesses = $scope.accesses.concat($scope.authorizedAccesses);
|
||||
$scope.authorizedAccesses = [];
|
||||
Notifications.success('Endpoint accesses successfully updated');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update endpoint accesses');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.authorizeAccess = function(access) {
|
||||
var authorizedUsers = [];
|
||||
var authorizedTeams = [];
|
||||
angular.forEach($scope.authorizedAccesses, function (a) {
|
||||
if (a.Type === 'user') {
|
||||
authorizedUsers.push(a.Id);
|
||||
} else if (a.Type === 'team') {
|
||||
authorizedTeams.push(a.Id);
|
||||
}
|
||||
});
|
||||
|
||||
if (access.Type === 'user') {
|
||||
authorizedUsers.push(access.Id);
|
||||
} else if (access.Type === 'team') {
|
||||
authorizedTeams.push(access.Id);
|
||||
}
|
||||
|
||||
EndpointService.updateAccess($stateParams.id, authorizedUsers, authorizedTeams)
|
||||
.then(function success(data) {
|
||||
removeAccessFromArray(access, $scope.accesses);
|
||||
$scope.authorizedAccesses.push(access);
|
||||
Notifications.success('Endpoint accesses successfully updated', access.Name);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update endpoint accesses');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.unauthorizeAccess = function(access) {
|
||||
var authorizedUsers = [];
|
||||
var authorizedTeams = [];
|
||||
angular.forEach($scope.authorizedAccesses, function (a) {
|
||||
if (a.Type === 'user') {
|
||||
authorizedUsers.push(a.Id);
|
||||
} else if (a.Type === 'team') {
|
||||
authorizedTeams.push(a.Id);
|
||||
}
|
||||
});
|
||||
|
||||
if (access.Type === 'user') {
|
||||
_.remove(authorizedUsers, function(n) {
|
||||
return n === access.Id;
|
||||
});
|
||||
} else if (access.Type === 'team') {
|
||||
_.remove(authorizedTeams, function(n) {
|
||||
return n === access.Id;
|
||||
});
|
||||
}
|
||||
|
||||
EndpointService.updateAccess($stateParams.id, authorizedUsers, authorizedTeams)
|
||||
.then(function success(data) {
|
||||
removeAccessFromArray(access, $scope.authorizedAccesses);
|
||||
$scope.accesses.push(access);
|
||||
Notifications.success('Endpoint accesses successfully updated', access.Name);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update endpoint accesses');
|
||||
});
|
||||
$scope.updateAccess = function(authorizedUsers, authorizedTeams) {
|
||||
return EndpointService.updateAccess($stateParams.id, authorizedUsers, authorizedTeams);
|
||||
};
|
||||
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
$q.all({
|
||||
endpoint: EndpointService.endpoint($stateParams.id),
|
||||
users: UserService.users(false),
|
||||
teams: TeamService.teams()
|
||||
})
|
||||
EndpointService.endpoint($stateParams.id)
|
||||
.then(function success(data) {
|
||||
$scope.endpoint = data.endpoint;
|
||||
$scope.accesses = [];
|
||||
var users = data.users.map(function (user) {
|
||||
return new EndpointAccessUserViewModel(user);
|
||||
});
|
||||
var teams = data.teams.map(function (team) {
|
||||
return new EndpointAccessTeamViewModel(team);
|
||||
});
|
||||
$scope.accesses = $scope.accesses.concat(users, teams);
|
||||
$scope.authorizedAccesses = [];
|
||||
angular.forEach($scope.endpoint.AuthorizedUsers, function(userID) {
|
||||
for (var i = 0, l = $scope.accesses.length; i < l; i++) {
|
||||
if ($scope.accesses[i].Type === 'user' && $scope.accesses[i].Id === userID) {
|
||||
$scope.authorizedAccesses.push($scope.accesses[i]);
|
||||
$scope.accesses.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
angular.forEach($scope.endpoint.AuthorizedTeams, function(teamID) {
|
||||
for (var i = 0, l = $scope.accesses.length; i < l; i++) {
|
||||
if ($scope.accesses[i].Type === 'team' && $scope.accesses[i].Id === teamID) {
|
||||
$scope.authorizedAccesses.push($scope.accesses[i]);
|
||||
$scope.accesses.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
$scope.endpoint = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.accesses = [];
|
||||
$scope.authorizedAccesses = [];
|
||||
Notifications.error('Failure', err, 'Unable to retrieve endpoint details');
|
||||
})
|
||||
.finally(function final(){
|
||||
|
@ -179,14 +20,5 @@ function ($q, $scope, $state, $stateParams, $filter, EndpointService, UserServic
|
|||
});
|
||||
}
|
||||
|
||||
function removeAccessFromArray(access, accesses) {
|
||||
for (var i = 0, l = accesses.length; i < l; i++) {
|
||||
if (access.Type === accesses[i].Type && access.Id === accesses[i].Id) {
|
||||
accesses.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('events', [])
|
||||
.controller('EventsController', ['$scope', 'Notifications', 'Events', 'Pagination',
|
||||
function ($scope, Notifications, Events, Pagination) {
|
||||
.controller('EventsController', ['$scope', 'Notifications', 'SystemService', 'Pagination',
|
||||
function ($scope, Notifications, SystemService, Pagination) {
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('events');
|
||||
$scope.sortType = 'Time';
|
||||
|
@ -15,18 +15,22 @@ function ($scope, Notifications, Events, Pagination) {
|
|||
Pagination.setPaginationCount('events', $scope.state.pagination_count);
|
||||
};
|
||||
|
||||
var from = moment().subtract(24, 'hour').unix();
|
||||
var to = moment().unix();
|
||||
function initView() {
|
||||
var from = moment().subtract(24, 'hour').unix();
|
||||
var to = moment().unix();
|
||||
|
||||
Events.query({since: from, until: to},
|
||||
function(d) {
|
||||
$scope.events = d.map(function (item) {
|
||||
return new EventViewModel(item);
|
||||
$('#loadEventsSpinner').show();
|
||||
SystemService.events(from, to)
|
||||
.then(function success(data) {
|
||||
$scope.events = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to load events');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadEventsSpinner').hide();
|
||||
});
|
||||
$('#loadEventsSpinner').hide();
|
||||
},
|
||||
function (e) {
|
||||
$('#loadEventsSpinner').hide();
|
||||
Notifications.error('Failure', e, 'Unable to load events');
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
||||
|
|
|
@ -54,21 +54,11 @@
|
|||
<rd-widget-header icon="fa-tag" title="Tag the image"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- name-and-registry-inputs -->
|
||||
<!-- image-and-registry -->
|
||||
<div class="form-group">
|
||||
<label for="image_name" class="col-sm-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-11 col-md-6">
|
||||
<input type="text" class="form-control" ng-model="config.Image" id="image_name" placeholder="e.g. myImage:myTag">
|
||||
</div>
|
||||
<label for="image_registry" class="col-sm-2 margin-sm-top control-label text-left">
|
||||
Registry
|
||||
<portainer-tooltip position="bottom" message="A registry to pull the image from. Leave empty to use the official Docker registry."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-10 col-md-3 margin-sm-top">
|
||||
<input type="text" class="form-control" ng-model="config.Registry" id="image_registry" placeholder="e.g. myregistry.mydomain">
|
||||
</div>
|
||||
<por-image-registry image="formValues.Image" registry="formValues.Registry"></por-image-registry>
|
||||
</div>
|
||||
<!-- !name-and-registry-inputs -->
|
||||
<!-- !image-and-registry -->
|
||||
<!-- tag-note -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
|
@ -78,7 +68,7 @@
|
|||
<!-- !tag-note -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!config.Image" ng-click="tagImage()">Tag</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Image" ng-click="tagImage()">Tag</button>
|
||||
<i id="pullImageSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
angular.module('image', [])
|
||||
.controller('ImageController', ['$scope', '$stateParams', '$state', 'ImageService', 'Notifications',
|
||||
function ($scope, $stateParams, $state, ImageService, Notifications) {
|
||||
$scope.config = {
|
||||
.controller('ImageController', ['$scope', '$stateParams', '$state', '$timeout', 'ImageService', 'RegistryService', 'Notifications',
|
||||
function ($scope, $stateParams, $state, $timeout, ImageService, RegistryService, Notifications) {
|
||||
$scope.formValues = {
|
||||
Image: '',
|
||||
Registry: ''
|
||||
};
|
||||
|
||||
$scope.tagImage = function() {
|
||||
$('#loadingViewSpinner').show();
|
||||
var image = $scope.config.Image;
|
||||
var registry = $scope.config.Registry;
|
||||
var image = $scope.formValues.Image;
|
||||
var registry = $scope.formValues.Registry;
|
||||
|
||||
ImageService.tagImage($stateParams.id, image, registry)
|
||||
ImageService.tagImage($stateParams.id, image, registry.URL)
|
||||
.then(function success(data) {
|
||||
Notifications.success('Image successfully tagged');
|
||||
$state.go('image', {id: $stateParams.id}, {reload: true});
|
||||
|
@ -24,28 +24,35 @@ function ($scope, $stateParams, $state, ImageService, Notifications) {
|
|||
});
|
||||
};
|
||||
|
||||
$scope.pushTag = function(tag) {
|
||||
$scope.pushTag = function(repository) {
|
||||
$('#loadingViewSpinner').show();
|
||||
ImageService.pushImage(tag)
|
||||
.then(function success() {
|
||||
Notifications.success('Image successfully pushed');
|
||||
RegistryService.retrieveRegistryFromRepository(repository)
|
||||
.then(function success(data) {
|
||||
var registry = data;
|
||||
return ImageService.pushImage(repository, registry);
|
||||
})
|
||||
.then(function success(data) {
|
||||
Notifications.success('Image successfully pushed', repository);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to push image tag');
|
||||
Notifications.error('Failure', err, 'Unable to push image to repository');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.pullTag = function(tag) {
|
||||
$scope.pullTag = function(repository) {
|
||||
$('#loadingViewSpinner').show();
|
||||
|
||||
ImageService.pullTag(tag)
|
||||
RegistryService.retrieveRegistryFromRepository(repository)
|
||||
.then(function success(data) {
|
||||
Notifications.success('Image successfully pulled', tag);
|
||||
var registry = data;
|
||||
return ImageService.pullImage(repository, registry);
|
||||
})
|
||||
.catch(function error(err){
|
||||
.then(function success(data) {
|
||||
Notifications.success('Image successfully pulled', repository);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to pull image');
|
||||
})
|
||||
.finally(function final() {
|
||||
|
@ -53,15 +60,15 @@ function ($scope, $stateParams, $state, ImageService, Notifications) {
|
|||
});
|
||||
};
|
||||
|
||||
$scope.removeTag = function(id) {
|
||||
$scope.removeTag = function(repository) {
|
||||
$('#loadingViewSpinner').show();
|
||||
ImageService.deleteImage(id, false)
|
||||
ImageService.deleteImage(repository, false)
|
||||
.then(function success() {
|
||||
if ($scope.image.RepoTags.length === 1) {
|
||||
Notifications.success('Image successfully deleted', id);
|
||||
Notifications.success('Image successfully deleted', repository);
|
||||
$state.go('images', {}, {reload: true});
|
||||
} else {
|
||||
Notifications.success('Tag successfully deleted', id);
|
||||
Notifications.success('Tag successfully deleted', repository);
|
||||
$state.go('image', {id: $stateParams.id}, {reload: true});
|
||||
}
|
||||
})
|
||||
|
|
|
@ -15,21 +15,11 @@
|
|||
</rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- name-and-registry-inputs -->
|
||||
<!-- image-and-registry -->
|
||||
<div class="form-group">
|
||||
<label for="image_name" class="col-sm-1 control-label text-left">Name</label>
|
||||
<div class="col-sm-11 col-md-6">
|
||||
<input type="text" class="form-control" ng-model="config.Image" id="image_name" placeholder="e.g. ubuntu:trusty">
|
||||
</div>
|
||||
<label for="image_registry" class="col-sm-2 margin-sm-top control-label text-left">
|
||||
Registry
|
||||
<portainer-tooltip position="bottom" message="A registry to pull the image from. Leave empty to use the official Docker registry."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-10 col-md-3 margin-sm-top">
|
||||
<input type="text" class="form-control" ng-model="config.Registry" id="image_registry" placeholder="e.g. myregistry.mydomain">
|
||||
</div>
|
||||
<por-image-registry image="formValues.Image" registry="formValues.Registry"></por-image-registry>
|
||||
</div>
|
||||
<!-- !name-and-registry-inputs -->
|
||||
<!-- !image-and-registry -->
|
||||
<!-- tag-note -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
|
@ -39,7 +29,7 @@
|
|||
<!-- !tag-note -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!config.Image" ng-click="pullImage()">Pull</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!formValues.Image" ng-click="pullImage()">Pull</button>
|
||||
<i id="pullImageSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,7 +7,7 @@ function ($scope, $state, ImageService, Notifications, Pagination, ModalService)
|
|||
$scope.sortReverse = true;
|
||||
$scope.state.selectedItemCount = 0;
|
||||
|
||||
$scope.config = {
|
||||
$scope.formValues = {
|
||||
Image: '',
|
||||
Registry: ''
|
||||
};
|
||||
|
@ -40,10 +40,11 @@ function ($scope, $state, ImageService, Notifications, Pagination, ModalService)
|
|||
|
||||
$scope.pullImage = function() {
|
||||
$('#pullImageSpinner').show();
|
||||
var image = $scope.config.Image;
|
||||
var registry = $scope.config.Registry;
|
||||
var image = $scope.formValues.Image;
|
||||
var registry = $scope.formValues.Registry;
|
||||
ImageService.pullImage(image, registry)
|
||||
.then(function success(data) {
|
||||
Notifications.success('Image successfully pulled', image);
|
||||
$state.reload();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
|
|
150
app/components/registries/registries.html
Normal file
150
app/components/registries/registries.html
Normal file
|
@ -0,0 +1,150 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="Registries">
|
||||
<a data-toggle="tooltip" title="Refresh" ui-sref="registries" ui-sref-opts="{reload: true}">
|
||||
<i class="fa fa-refresh" aria-hidden="true"></i>
|
||||
</a>
|
||||
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>Registry management</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row" ng-if="dockerhub">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-database" title="DockerHub">
|
||||
</rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- note -->
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
The DockerHub registry can be used by any user. You can specify the credentials that will be used to push & pull images here.
|
||||
</span>
|
||||
</div>
|
||||
<!-- !note -->
|
||||
<!-- authentication-checkbox -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label for="registry_auth" class="control-label text-left">
|
||||
Authentication
|
||||
<portainer-tooltip position="bottom" message="Enable this option if you need to specify credentials to connect to push/pull private images."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;">
|
||||
<input type="checkbox" ng-model="dockerhub.Authentication"><i></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !authentication-checkbox -->
|
||||
<!-- authentication-credentials -->
|
||||
<div ng-if="dockerhub.Authentication">
|
||||
<!-- credentials-user -->
|
||||
<div class="form-group">
|
||||
<label for="hub_username" class="col-sm-3 col-lg-2 control-label text-left">Username</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="hub_username" ng-model="dockerhub.Username">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !credentials-user -->
|
||||
<!-- credentials-password -->
|
||||
<div class="form-group">
|
||||
<label for="hub_password" class="col-sm-3 col-lg-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="password" class="form-control" id="hub_password" ng-model="dockerhub.Password">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !credentials-password -->
|
||||
</div>
|
||||
<!-- !authentication-credentials -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="dockerhub.Authentication && (!dockerhub.Username || !dockerhub.Password)" ng-click="updateDockerHub()">Update</button>
|
||||
<i id="updateDockerhubSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-database" title="Available registries">
|
||||
<div class="pull-right">
|
||||
Items per page:
|
||||
<select ng-model="state.pagination_count" ng-change="changePaginationCount()">
|
||||
<option value="0">All</option>
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</rd-widget-header>
|
||||
<rd-widget-taskbar classes="col-lg-12">
|
||||
<div class="pull-left">
|
||||
<button type="button" class="btn btn-danger" ng-click="removeAction()" ng-disabled="!state.selectedItemCount"><i class="fa fa-trash space-right" aria-hidden="true"></i>Remove</button>
|
||||
<a class="btn btn-primary" type="button" ui-sref="actions.create.registry"><i class="fa fa-plus space-right" aria-hidden="true"></i>Add registry</a>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<input type="text" id="filter" ng-model="state.filter" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input type="checkbox" ng-model="allSelected" ng-change="selectItems(allSelected)" />
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="registries" ng-click="order('Name')">
|
||||
Name
|
||||
<span ng-show="sortType == 'Name' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'Name' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a ui-sref="registries" ng-click="order('URL')">
|
||||
URL
|
||||
<span ng-show="sortType == 'URL' && !sortReverse" class="glyphicon glyphicon-chevron-down"></span>
|
||||
<span ng-show="sortType == 'URL' && sortReverse" class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr dir-paginate="registry in (state.filteredRegistries = (registries | filter:state.filter | orderBy:sortType:sortReverse | itemsPerPage: state.pagination_count))">
|
||||
<td><input type="checkbox" ng-model="registry.Checked" ng-change="selectItem(registry)" /></td>
|
||||
<td>
|
||||
<a ui-sref="registry({id: registry.Id})">{{ registry.Name }}</a>
|
||||
<span ng-if="registry.Authentication" style="margin-left: 5px;">
|
||||
<i class="fa fa-shield" aria-hidden="true" tooltip-placement="bottom" tooltip-class="portainer-tooltip" uib-tooltip="Authentication is enabled for this registry."></i>
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ registry.URL }}</td>
|
||||
<td>
|
||||
<span ng-if="applicationState.application.authentication">
|
||||
<a ui-sref="registry.access({id: registry.Id})"><i class="fa fa-users" aria-hidden="true" style="margin-left: 7px;"></i> Manage access</a>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="!registries">
|
||||
<td colspan="3" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-if="registries.length == 0">
|
||||
<td colspan="3" class="text-center text-muted">No registries available.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="registries" class="pull-left pagination-controls">
|
||||
<dir-pagination-controls></dir-pagination-controls>
|
||||
</div>
|
||||
</div>
|
||||
</rd-widget-body>
|
||||
<rd-widget>
|
||||
</div>
|
||||
</div>
|
113
app/components/registries/registriesController.js
Normal file
113
app/components/registries/registriesController.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
angular.module('registries', [])
|
||||
.controller('RegistriesController', ['$q', '$scope', '$state', 'RegistryService', 'DockerHubService', 'ModalService', 'Notifications', 'Pagination',
|
||||
function ($q, $scope, $state, RegistryService, DockerHubService, ModalService, Notifications, Pagination) {
|
||||
|
||||
$scope.state = {
|
||||
selectedItemCount: 0,
|
||||
pagination_count: Pagination.getPaginationCount('registries')
|
||||
};
|
||||
$scope.sortType = 'Name';
|
||||
$scope.sortReverse = true;
|
||||
|
||||
$scope.updateDockerHub = function() {
|
||||
$('#updateDockerhubSpinner').show();
|
||||
var dockerhub = $scope.dockerhub;
|
||||
DockerHubService.update(dockerhub)
|
||||
.then(function success(data) {
|
||||
Notifications.success('DockerHub registry updated');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update DockerHub details');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#updateDockerhubSpinner').hide();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.order = function(sortType) {
|
||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||
$scope.sortType = sortType;
|
||||
};
|
||||
|
||||
$scope.changePaginationCount = function() {
|
||||
Pagination.setPaginationCount('endpoints', $scope.state.pagination_count);
|
||||
};
|
||||
|
||||
$scope.selectItems = function (allSelected) {
|
||||
angular.forEach($scope.state.filteredRegistries, function (registry) {
|
||||
if (registry.Checked !== allSelected) {
|
||||
registry.Checked = allSelected;
|
||||
$scope.selectItem(registry);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.selectItem = function (item) {
|
||||
if (item.Checked) {
|
||||
$scope.state.selectedItemCount++;
|
||||
} else {
|
||||
$scope.state.selectedItemCount--;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.removeAction = function() {
|
||||
ModalService.confirmDeletion(
|
||||
'Do you want to remove the selected registries?',
|
||||
function onConfirm(confirmed) {
|
||||
if(!confirmed) { return; }
|
||||
removeRegistries();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function removeRegistries() {
|
||||
$('#loadingViewSpinner').show();
|
||||
var counter = 0;
|
||||
var complete = function () {
|
||||
counter = counter - 1;
|
||||
if (counter === 0) {
|
||||
$('#loadingViewSpinner').hide();
|
||||
}
|
||||
};
|
||||
|
||||
var registries = $scope.registries;
|
||||
angular.forEach(registries, function (registry) {
|
||||
if (registry.Checked) {
|
||||
counter = counter + 1;
|
||||
RegistryService.deleteRegistry(registry.Id)
|
||||
.then(function success(data) {
|
||||
var index = registries.indexOf(registry);
|
||||
registries.splice(index, 1);
|
||||
Notifications.success('Registry deleted', registry.Name);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to remove registry');
|
||||
})
|
||||
.finally(function final() {
|
||||
complete();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
$q.all({
|
||||
registries: RegistryService.registries(),
|
||||
dockerhub: DockerHubService.dockerhub()
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.registries = data.registries;
|
||||
$scope.dockerhub = data.dockerhub;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
$scope.registries = [];
|
||||
Notifications.error('Failure', err, 'Unable to retrieve registries');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
78
app/components/registry/registry.html
Normal file
78
app/components/registry/registry.html
Normal file
|
@ -0,0 +1,78 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="Registry details">
|
||||
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="registries">Registries</a> > <a ui-sref="registry({id: registry.Id})">{{ registry.Name }}</a>
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="registry_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="registry_name" ng-model="registry.Name" placeholder="e.g. my-registry">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- registry-url-input -->
|
||||
<div class="form-group">
|
||||
<label for="registry_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Registry URL
|
||||
<portainer-tooltip position="bottom" message="URL or IP address of a Docker registry."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="registry_url" ng-model="registry.URL" placeholder="e.g. 10.0.0.10:5000 or myregistry.domain.tld">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !registry-url-input -->
|
||||
<!-- authentication-checkbox -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label for="registry_auth" class="control-label text-left">
|
||||
Authentication
|
||||
<portainer-tooltip position="bottom" message="Enable this option if you need to specify credentials to connect to this registry."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;">
|
||||
<input type="checkbox" ng-model="registry.Authentication"><i></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !authentication-checkbox -->
|
||||
<!-- authentication-credentials -->
|
||||
<div ng-if="registry.Authentication">
|
||||
<!-- credentials-user -->
|
||||
<div class="form-group">
|
||||
<label for="credentials_username" class="col-sm-3 col-lg-2 control-label text-left">Username</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="credentials_username" ng-model="registry.Username">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !credentials-user -->
|
||||
<!-- credentials-password -->
|
||||
<div class="form-group">
|
||||
<label for="credentials_password" class="col-sm-3 col-lg-2 control-label text-left">Password</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="password" class="form-control" id="credentials_password" ng-model="registry.Password">
|
||||
</div>
|
||||
</div>
|
||||
<!-- !credentials-password -->
|
||||
</div>
|
||||
<!-- !authentication-credentials -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="!registry.Name || !registry.URL || (registry.Authentication && (!registry.Username || !registry.Password))" ng-click="updateRegistry()">Update registry</button>
|
||||
<a type="button" class="btn btn-default btn-sm" ui-sref="registries">Cancel</a>
|
||||
<i id="updateRegistrySpinner" class="fa fa-cog fa-spin" style="margin-left: 5px; display: none;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
37
app/components/registry/registryController.js
Normal file
37
app/components/registry/registryController.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
angular.module('registry', [])
|
||||
.controller('RegistryController', ['$scope', '$state', '$stateParams', '$filter', 'RegistryService', 'Notifications',
|
||||
function ($scope, $state, $stateParams, $filter, RegistryService, Notifications) {
|
||||
|
||||
$scope.updateRegistry = function() {
|
||||
$('#updateRegistrySpinner').show();
|
||||
var registry = $scope.registry;
|
||||
RegistryService.updateRegistry(registry)
|
||||
.then(function success(data) {
|
||||
Notifications.success('Registry successfully updated');
|
||||
$state.go('registries');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to update registry');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#updateRegistrySpinner').hide();
|
||||
});
|
||||
};
|
||||
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
var registryID = $stateParams.id;
|
||||
RegistryService.registry(registryID)
|
||||
.then(function success(data) {
|
||||
$scope.registry = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve registry details');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
45
app/components/registryAccess/registryAccess.html
Normal file
45
app/components/registryAccess/registryAccess.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
<rd-header>
|
||||
<rd-header-title title="Registry access">
|
||||
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>
|
||||
<a ui-sref="registries">Registries</a> > <a ui-sref="registry({id: registry.Id})">{{ registry.Name }}</a> > Access management
|
||||
</rd-header-content>
|
||||
</rd-header>
|
||||
|
||||
<div class="row" ng-if="registry">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-plug" title="Registry"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>
|
||||
{{ registry.Name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>
|
||||
{{ registry.URL }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<span class="small text-muted">
|
||||
You can select which user or team can access this registry by moving them to the authorized accesses table. Simply click
|
||||
on a user or team entry to move it from one table to the other.
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<por-access-management ng-if="registry" access-controlled-entity="registry" update-access="updateAccess(userAccesses, teamAccesses)">
|
||||
</por-access-management>
|
24
app/components/registryAccess/registryAccessController.js
Normal file
24
app/components/registryAccess/registryAccessController.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
angular.module('registryAccess', [])
|
||||
.controller('RegistryAccessController', ['$scope', '$stateParams', 'RegistryService', 'Notifications',
|
||||
function ($scope, $stateParams, RegistryService, Notifications) {
|
||||
|
||||
$scope.updateAccess = function(authorizedUsers, authorizedTeams) {
|
||||
return RegistryService.updateAccess($stateParams.id, authorizedUsers, authorizedTeams);
|
||||
};
|
||||
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
RegistryService.registry($stateParams.id)
|
||||
.then(function success(data) {
|
||||
$scope.registry = data;
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve registry details');
|
||||
})
|
||||
.finally(function final(){
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
|
@ -64,6 +64,9 @@
|
|||
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin">
|
||||
<a ui-sref="endpoints" ui-sref-active="active">Endpoints <span class="menu-icon fa fa-plug"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin">
|
||||
<a ui-sref="registries" ui-sref-active="active">Registries <span class="menu-icon fa fa-database"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-list" ng-if="!applicationState.application.authentication || isAdmin">
|
||||
<a ui-sref="settings" ui-sref-active="active">Settings <span class="menu-icon fa fa-cogs"></span></a>
|
||||
</li>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<a data-toggle="tooltip" title="Refresh" ui-sref="swarm" ui-sref-opts="{reload: true}">
|
||||
<i class="fa fa-refresh" aria-hidden="true"></i>
|
||||
</a>
|
||||
<i id="loadingViewSpinner" class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
|
||||
</rd-header-title>
|
||||
<rd-header-content>Swarm</rd-header-content>
|
||||
</rd-header>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('swarm', [])
|
||||
.controller('SwarmController', ['$scope', 'Info', 'Version', 'Node', 'Pagination',
|
||||
function ($scope, Info, Version, Node, Pagination) {
|
||||
.controller('SwarmController', ['$q', '$scope', 'SystemService', 'NodeService', 'Pagination', 'Notifications',
|
||||
function ($q, $scope, SystemService, NodeService, Pagination, Notifications) {
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('swarm_nodes');
|
||||
$scope.sortType = 'Spec.Role';
|
||||
|
@ -20,30 +20,6 @@ function ($scope, Info, Version, Node, Pagination) {
|
|||
Pagination.setPaginationCount('swarm_nodes', $scope.state.pagination_count);
|
||||
};
|
||||
|
||||
Version.get({}, function (d) {
|
||||
$scope.docker = d;
|
||||
});
|
||||
|
||||
Info.get({}, function (d) {
|
||||
$scope.info = d;
|
||||
if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') {
|
||||
Node.query({}, function(d) {
|
||||
$scope.nodes = d.map(function (node) {
|
||||
return new NodeViewModel(node);
|
||||
});
|
||||
var CPU = 0, memory = 0;
|
||||
angular.forEach(d, function(node) {
|
||||
CPU += node.Description.Resources.NanoCPUs;
|
||||
memory += node.Description.Resources.MemoryBytes;
|
||||
});
|
||||
$scope.totalCPU = CPU / 1000000000;
|
||||
$scope.totalMemory = memory;
|
||||
});
|
||||
} else {
|
||||
extractSwarmInfo(d);
|
||||
}
|
||||
});
|
||||
|
||||
function extractSwarmInfo(info) {
|
||||
// Swarm info is available in SystemStatus object
|
||||
var systemStatus = info.SystemStatus;
|
||||
|
@ -84,4 +60,43 @@ function ($scope, Info, Version, Node, Pagination) {
|
|||
node.version = info[offset + 8][1];
|
||||
$scope.swarm.Status.push(node);
|
||||
}
|
||||
|
||||
function processTotalCPUAndMemory(nodes) {
|
||||
var CPU = 0, memory = 0;
|
||||
angular.forEach(nodes, function(node) {
|
||||
CPU += node.CPUs;
|
||||
memory += node.Memory;
|
||||
});
|
||||
$scope.totalCPU = CPU / 1000000000;
|
||||
$scope.totalMemory = memory;
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$('#loadingViewSpinner').show();
|
||||
var provider = $scope.applicationState.endpoint.mode.provider;
|
||||
$q.all({
|
||||
version: SystemService.version(),
|
||||
info: SystemService.info(),
|
||||
nodes: provider !== 'DOCKER_SWARM_MODE' || NodeService.nodes()
|
||||
})
|
||||
.then(function success(data) {
|
||||
$scope.docker = data.version;
|
||||
$scope.info = data.info;
|
||||
if (provider === 'DOCKER_SWARM_MODE') {
|
||||
var nodes = data.nodes;
|
||||
processTotalCPUAndMemory(nodes);
|
||||
$scope.nodes = nodes;
|
||||
} else {
|
||||
extractSwarmInfo(data.info);
|
||||
}
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve cluster details');
|
||||
})
|
||||
.finally(function final() {
|
||||
$('#loadingViewSpinner').hide();
|
||||
});
|
||||
}
|
||||
|
||||
initView();
|
||||
}]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue