1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 05:45:22 +02:00

feat(registry): gitlab support (#3107)

* feat(api): gitlab registry type

* feat(registries): early support for gitlab registries

* feat(app): registry service selector

* feat(registry): gitlab support : list repositories and tags - remove features missing

* feat(registry): gitlab registry remove features

* feat(registry): gitlab switch to registry V2 API for repositories and tags

* feat(api): use development extension binary

* fix(registry): avoid 401 on gitlab retrieve to disconnect the user

* feat(registry): gitlab browse projects without extension

* style(app): code cleaning

* refactor(app): PR review changes + refactor on types

* fix(gitlab): remove gitlab info from registrymanagementconfig and force gitlab type

* style(api): go fmt

* feat(api): update APIVersion and ExtensionDefinitionsURL

* fix(api): fix invalid RM extension URL

* feat(registry): PAT scope help

* feat(registry): defaults on registry creation

* style(registry-creation): update layout and text for Gitlab registry

* feat(registry-creation): update gitlab notice
This commit is contained in:
xAt0mZ 2019-11-12 04:28:31 +01:00 committed by Anthony Lapenna
parent 03d9d6afbb
commit 198e92c734
38 changed files with 1022 additions and 160 deletions

View file

@ -30,7 +30,7 @@
<tr ng-hide="$ctrl.loading" dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
ng-class="{active: item.Checked}">
<td>
<a ui-sref="portainer.registries.registry.repository({repository: item.Name})" class="monospaced"
<a ui-sref="portainer.registries.registry.repository({repository: item.Name})"
title="{{ item.Name }}">{{ item.Name }}</a>
</td>
<td>{{ item.TagsCount }}</td>

View file

@ -1,3 +1,4 @@
import _ from 'lodash-es';
import { RepositoryTagViewModel } from '../models/repositoryTag';
angular.module('portainer.extensions.registrymanagement')
@ -7,7 +8,7 @@ angular.module('portainer.extensions.registrymanagement')
var helper = {};
function historyRawToParsed(rawHistory) {
return angular.fromJson(rawHistory[0].v1Compatibility);
return _.map(rawHistory, (item) => angular.fromJson(item.v1Compatibility));
}
helper.manifestsToTag = function (manifests) {
@ -16,7 +17,7 @@ angular.module('portainer.extensions.registrymanagement')
var history = historyRawToParsed(v1.history);
var name = v1.tag;
var os = history.os;
var os = history[0].os;
var arch = v1.architecture;
var size = v2.layers.reduce(function (a, b) {
return {
@ -26,7 +27,7 @@ angular.module('portainer.extensions.registrymanagement')
var imageId = v2.config.digest;
var imageDigest = v2.digest;
return new RepositoryTagViewModel(name, os, arch, size, imageDigest, imageId, v2);
return new RepositoryTagViewModel(name, os, arch, size, imageDigest, imageId, v2, history);
};
return helper;

View file

@ -0,0 +1,8 @@
export function RegistryGitlabProject(project) {
this.Id = project.id;
this.Description = project.description;
this.Name = project.name;
this.Namespace = project.namespace ? project.namespace.name : '';
this.PathWithNamespace = project.path_with_namespace;
this.RegistryEnabled = project.container_registry_enabled;
}

View file

@ -1,5 +1,5 @@
import _ from 'lodash-es';
export default function RegistryRepositoryViewModel(item) {
export function RegistryRepositoryViewModel(item) {
if (item.name && item.tags) {
this.Name = item.name;
this.TagsCount = _.without(item.tags, null).length;
@ -7,4 +7,9 @@ export default function RegistryRepositoryViewModel(item) {
this.Name = item;
this.TagsCount = 0;
}
}
export function RegistryRepositoryGitlabViewModel(data) {
this.Name = data.path;
this.TagsCount = data.tags.length;
}

View file

@ -0,0 +1,6 @@
export const RegistryTypes = Object.freeze({
'QUAY': 1,
'AZURE': 2,
'CUSTOM': 3,
'GITLAB': 4
})

View file

@ -1,4 +1,4 @@
export function RepositoryTagViewModel(name, os, arch, size, imageDigest, imageId, v2) {
export function RepositoryTagViewModel(name, os, arch, size, imageDigest, imageId, v2, history) {
this.Name = name;
this.Os = os || '';
this.Architecture = arch || '';
@ -6,6 +6,7 @@ export function RepositoryTagViewModel(name, os, arch, size, imageDigest, imageI
this.ImageDigest = imageDigest || '';
this.ImageId = imageId || '';
this.ManifestV2 = v2 || {};
this.History = history || [];
}
export function RepositoryShortTag(name, imageId, imageDigest, manifest) {
@ -13,4 +14,9 @@ export function RepositoryShortTag(name, imageId, imageDigest, manifest) {
this.ImageId = imageId;
this.ImageDigest = imageDigest;
this.ManifestV2 = manifest;
}
}
export function RepositoryAddTagPayload(tag, manifest) {
this.Tag = tag;
this.Manifest = manifest;
}

View file

@ -0,0 +1,33 @@
import gitlabResponseGetLink from './transform/gitlabResponseGetLink'
angular.module('portainer.extensions.registrymanagement')
.factory('Gitlab', ['$resource', 'API_ENDPOINT_REGISTRIES',
function GitlabFactory($resource, API_ENDPOINT_REGISTRIES) {
'use strict';
return function(env) {
const headers = {};
if (env) {
headers['Private-Token'] = env.token;
headers['X-Gitlab-Domain'] = env.url
}
const baseUrl = API_ENDPOINT_REGISTRIES + '/:id/proxies/gitlab/api/v4/projects';
return $resource(baseUrl, {id:'@id'},
{
projects: {
method: 'GET',
params: { membership: 'true' },
transformResponse: gitlabResponseGetLink,
headers: headers
},
repositories :{
method: 'GET',
url: baseUrl + '/:projectId/registry/repositories',
params: { tags: true },
headers: headers,
transformResponse: gitlabResponseGetLink
}
});
};
}]);

View file

@ -0,0 +1,10 @@
export default function gitlabResponseGetLink(data, headers) {
let response = {};
try {
response.data = angular.fromJson(data);
response.next = headers('X-Next-Page');
} catch (error) {
response = data;
}
return response;
}

View file

@ -0,0 +1,86 @@
import _ from 'lodash-es';
import { RegistryGitlabProject } from '../models/gitlabRegistry';
import { RegistryRepositoryGitlabViewModel } from '../models/registryRepository';
angular.module('portainer.extensions.registrymanagement')
.factory('RegistryGitlabService', ['$async', 'Gitlab',
function RegistryGitlabServiceFactory($async, Gitlab) {
'use strict';
var service = {};
/**
* PROJECTS
*/
async function _getProjectsPage(env, params, projects) {
const response = await Gitlab(env).projects(params).$promise;
projects = _.concat(projects, response.data);
if (response.next) {
params.page = response.next;
projects = await _getProjectsPage(env, params, projects);
}
return projects;
}
async function projectsAsync(url, token) {
try {
const data = await _getProjectsPage({url: url, token: token}, {page: 1}, []);
return _.map(data, (project) => new RegistryGitlabProject(project));
} catch (error) {
throw {msg: 'Unable to retrieve projects', err: error};
}
}
/**
* END PROJECTS
*/
/**
* REPOSITORIES
*/
async function _getRepositoriesPage(params, repositories) {
const response = await Gitlab().repositories(params).$promise;
repositories = _.concat(repositories, response.data);
if (response.next) {
params.page = response.next;
repositories = await _getRepositoriesPage(params, repositories);
}
return repositories;
}
async function repositoriesAsync(registry) {
try {
const params = {
id: registry.Id,
projectId: registry.Gitlab.ProjectId,
page: 1
};
const data = await _getRepositoriesPage(params, []);
return _.map(data, (r) => new RegistryRepositoryGitlabViewModel(r));
} catch (error) {
throw {msg: 'Unable to retrieve repositories', err: error};
}
}
/**
* END REPOSITORIES
*/
/**
* SERVICE FUNCTIONS DECLARATION
*/
function projects(url, token) {
return $async(projectsAsync, url, token);
}
function repositories(registry) {
return $async(repositoriesAsync, registry);
}
service.projects = projects;
service.repositories = repositories;
return service;
}
]);

View file

@ -0,0 +1,82 @@
import { RegistryTypes } from 'Extensions/registry-management/models/registryTypes';
angular.module('portainer.extensions.registrymanagement')
.factory('RegistryServiceSelector', ['$q', 'RegistryV2Service', 'RegistryGitlabService',
function RegistryServiceSelector($q, RegistryV2Service, RegistryGitlabService) {
'use strict';
const service = {};
service.ping = ping;
service.repositories = repositories;
service.getRepositoriesDetails = getRepositoriesDetails;
service.tags = tags;
service.getTagsDetails = getTagsDetails;
service.tag = tag;
service.addTag = addTag;
service.deleteManifest = deleteManifest;
service.shortTagsWithProgress = shortTagsWithProgress;
service.deleteTagsWithProgress = deleteTagsWithProgress;
service.retagWithProgress = retagWithProgress;
function ping(registry, forceNewConfig) {
let service = RegistryV2Service;
return service.ping(registry, forceNewConfig)
}
function repositories(registry) {
let service = RegistryV2Service;
if (registry.Type === RegistryTypes.GITLAB) {
service = RegistryGitlabService;
}
return service.repositories(registry);
}
function getRepositoriesDetails(registry, repositories) {
let service = RegistryV2Service;
return service.getRepositoriesDetails(registry, repositories);
}
function tags(registry, repository) {
let service = RegistryV2Service;
return service.tags(registry, repository);
}
function getTagsDetails(registry, repository, tags) {
let service = RegistryV2Service;
return service.getTagsDetails(registry, repository, tags);
}
function tag(registry, repository, tag) {
let service = RegistryV2Service;
return service.tag(registry, repository, tag);
}
function addTag(registry, repository, tag, manifest) {
let service = RegistryV2Service;
return service.addTag(registry, repository, tag, manifest);
}
function deleteManifest(registry, repository, digest) {
let service = RegistryV2Service;
return service.deleteManifest(registry, repository, digest);
}
function shortTagsWithProgress(registry, repository, tagsList) {
let service = RegistryV2Service;
return service.shortTagsWithProgress(registry, repository, tagsList);
}
function deleteTagsWithProgress(registry, repository, modifiedDigests, impactedTags) {
let service = RegistryV2Service;
return service.deleteTagsWithProgress(registry, repository, modifiedDigests, impactedTags);
}
function retagWithProgress(registry, repository, modifiedTags, modifiedDigests, impactedTags) {
let service = RegistryV2Service;
return service.retagWithProgress(registry, repository, modifiedTags, modifiedDigests, impactedTags);
}
return service;
}
]);

View file

@ -1,6 +1,7 @@
import _ from 'lodash-es';
import { RepositoryShortTag } from '../models/repositoryTag';
import RegistryRepositoryViewModel from '../models/registryRepository';
import { RepositoryAddTagPayload } from '../models/repositoryTag'
import { RegistryRepositoryViewModel } from '../models/registryRepository';
import genericAsyncGenerator from './genericAsyncGenerator';
angular.module('portainer.extensions.registrymanagement')
@ -9,12 +10,24 @@ function RegistryV2ServiceFactory($q, $async, RegistryCatalog, RegistryTags, Reg
'use strict';
var service = {};
service.ping = function(id, forceNewConfig) {
/**
* PING
*/
function ping(registry, forceNewConfig) {
const id = registry.Id;
if (forceNewConfig) {
return RegistryCatalog.pingWithForceNew({ id: id }).$promise;
}
return RegistryCatalog.ping({ id: id }).$promise;
};
}
/**
* END PING
*/
/**
* REPOSITORIES
*/
function _getCatalogPage(params, deferred, repositories) {
RegistryCatalog.get(params).$promise.then(function(data) {
@ -27,7 +40,7 @@ function RegistryV2ServiceFactory($q, $async, RegistryCatalog, RegistryTags, Reg
});
}
function getCatalog(id) {
function _getCatalog(id) {
var deferred = $q.defer();
var repositories = [];
@ -35,13 +48,12 @@ function RegistryV2ServiceFactory($q, $async, RegistryCatalog, RegistryTags, Reg
return deferred.promise;
}
service.catalog = function (id) {
var deferred = $q.defer();
function repositories(registry) {
const deferred = $q.defer();
const id = registry.Id;
getCatalog(id).then(function success(data) {
var repositories = data.map(function (repositoryName) {
return new RegistryRepositoryViewModel(repositoryName);
});
_getCatalog(id).then(function success(data) {
const repositories = _.map(data, (repositoryName) => new RegistryRepositoryViewModel(repositoryName));
deferred.resolve(repositories);
})
.catch(function error(err) {
@ -52,14 +64,37 @@ function RegistryV2ServiceFactory($q, $async, RegistryCatalog, RegistryTags, Reg
});
return deferred.promise;
};
}
service.tags = function (id, repository) {
var deferred = $q.defer();
function getRepositoriesDetails(registry, repositories) {
const deferred = $q.defer();
const promises = _.map(repositories, (repository) => tags(registry, repository.Name));
$q.all(promises)
.then(function success(data) {
var repositories = data.map(function (item) {
return new RegistryRepositoryViewModel(item);
});
repositories = _.without(repositories, undefined);
deferred.resolve(repositories);
})
.catch(function error(err) {
deferred.reject({
msg: 'Unable to retrieve repositories',
err: err
});
});
_getTagsPage({id: id, repository: repository}, deferred, {tags:[]});
return deferred.promise;
};
}
/**
* END REPOSITORIES
*/
/**
* TAGS
*/
function _getTagsPage(params, deferred, previousTags) {
RegistryTags.get(params).$promise.then(function(data) {
@ -78,45 +113,23 @@ function RegistryV2ServiceFactory($q, $async, RegistryCatalog, RegistryTags, Reg
});
}
service.getRepositoriesDetails = function (id, repositories) {
var deferred = $q.defer();
var promises = [];
for (var i = 0; i < repositories.length; i++) {
var repository = repositories[i].Name;
promises.push(service.tags(id, repository));
}
$q.all(promises)
.then(function success(data) {
var repositories = data.map(function (item) {
return new RegistryRepositoryViewModel(item);
});
repositories = _.without(repositories, undefined);
deferred.resolve(repositories);
})
.catch(function error(err) {
deferred.reject({
msg: 'Unable to retrieve repositories',
err: err
});
});
function tags(registry, repository) {
const deferred = $q.defer();
const id = registry.Id;
_getTagsPage({id: id, repository: repository}, deferred, {tags:[]});
return deferred.promise;
};
}
service.getTagsDetails = function (id, repository, tags) {
var promises = [];
for (var i = 0; i < tags.length; i++) {
var tag = tags[i].Name;
promises.push(service.tag(id, repository, tag));
}
function getTagsDetails(registry, repository, tags) {
const promises = _.map(tags, (t) => tag(registry, repository, t.Name));
return $q.all(promises);
};
}
service.tag = function (id, repository, tag) {
var deferred = $q.defer();
function tag(registry, repository, tag) {
const deferred = $q.defer();
const id = registry.Id;
var promises = {
v1: RegistryManifestsJquery.get({
@ -142,35 +155,33 @@ function RegistryV2ServiceFactory($q, $async, RegistryCatalog, RegistryTags, Reg
});
return deferred.promise;
};
}
service.addTag = function (id, repository, {tag, manifest}) {
/**
* END TAGS
*/
/**
* ADD TAG
*/
// tag: RepositoryAddTagPayload
function _addTagFromGenerator(registry, repository, tag) {
return addTag(registry, repository, tag.Tag, tag.Manifest);
}
function addTag(registry, repository, tag, manifest) {
const id = registry.Id;
delete manifest.digest;
return RegistryManifestsJquery.put({
id: id,
repository: repository,
tag: tag
}, manifest);
};
}
service.deleteManifest = function (id, repository, imageDigest) {
return RegistryManifestsJquery.delete({
id: id,
repository: repository,
tag: imageDigest
});
};
service.shortTag = function(id, repository, tag) {
return new Promise ((resolve, reject) => {
RegistryManifestsJquery.getV2({id:id, repository: repository, tag: tag})
.then((data) => resolve(new RepositoryShortTag(tag, data.config.digest, data.digest, data)))
.catch((err) => reject(err))
});
};
async function* addTagsWithProgress(id, repository, tagsList, progression = 0) {
for await (const partialResult of genericAsyncGenerator($q, tagsList, service.addTag, [id, repository])) {
async function* _addTagsWithProgress(registry, repository, tagsList, progression = 0) {
for await (const partialResult of genericAsyncGenerator($q, tagsList, _addTagFromGenerator, [registry, repository])) {
if (typeof partialResult === 'number') {
yield progression + partialResult;
} else {
@ -179,36 +190,110 @@ function RegistryV2ServiceFactory($q, $async, RegistryCatalog, RegistryTags, Reg
}
}
service.shortTagsWithProgress = async function* (id, repository, tagsList) {
yield* genericAsyncGenerator($q, tagsList, service.shortTag, [id, repository]);
/**
* END ADD TAG
*/
/**
* DELETE MANIFEST
*/
function deleteManifest(registry, repository, imageDigest) {
const id = registry.Id;
return RegistryManifestsJquery.delete({
id: id,
repository: repository,
tag: imageDigest
});
}
async function* deleteManifestsWithProgress(id, repository, manifests) {
for await (const partialResult of genericAsyncGenerator($q, manifests, service.deleteManifest, [id, repository])) {
async function* _deleteManifestsWithProgress(registry, repository, manifests) {
for await (const partialResult of genericAsyncGenerator($q, manifests, deleteManifest, [registry, repository])) {
yield partialResult;
}
}
service.retagWithProgress = async function* (id, repository, modifiedTags, modifiedDigests, impactedTags){
yield* deleteManifestsWithProgress(id, repository, modifiedDigests);
/**
* END DELETE MANIFEST
*/
/**
* SHORT TAG
*/
function _shortTagFromGenerator(id, repository, tag) {
return new Promise ((resolve, reject) => {
RegistryManifestsJquery.getV2({id:id, repository: repository, tag: tag})
.then((data) => resolve(new RepositoryShortTag(tag, data.config.digest, data.digest, data)))
.catch((err) => reject(err))
});
}
async function* shortTagsWithProgress(registry, repository, tagsList) {
const id = registry.Id;
yield* genericAsyncGenerator($q, tagsList, _shortTagFromGenerator, [id, repository]);
}
/**
* END SHORT TAG
*/
/**
* RETAG
*/
async function* retagWithProgress(registry, repository, modifiedTags, modifiedDigests, impactedTags){
yield* _deleteManifestsWithProgress(registry, repository, modifiedDigests);
const newTags = _.map(impactedTags, (item) => {
const tagFromTable = _.find(modifiedTags, { 'Name': item.Name });
const name = tagFromTable && tagFromTable.Name !== tagFromTable.NewName ? tagFromTable.NewName : item.Name;
return { tag: name, manifest: item.ManifestV2 };
return new RepositoryAddTagPayload(name, item.ManifestV2);
});
yield* addTagsWithProgress(id, repository, newTags, modifiedDigests.length);
yield* _addTagsWithProgress(registry, repository, newTags, modifiedDigests.length);
}
service.deleteTagsWithProgress = async function* (id, repository, modifiedDigests, impactedTags) {
yield* deleteManifestsWithProgress(id, repository, modifiedDigests);
/**
* END RETAG
*/
const newTags = _.map(impactedTags, (item) => {return {tag: item.Name, manifest: item.ManifestV2}})
/**
* DELETE TAGS
*/
yield* addTagsWithProgress(id, repository, newTags, modifiedDigests.length);
async function* deleteTagsWithProgress(registry, repository, modifiedDigests, impactedTags) {
yield* _deleteManifestsWithProgress(registry, repository, modifiedDigests);
const newTags = _.map(impactedTags, (item) => new RepositoryAddTagPayload(item.Name, item.ManifestV2));
yield* _addTagsWithProgress(registry, repository, newTags, modifiedDigests.length);
}
/**
* END DELETE TAGS
*/
/**
* SERVICE FUNCTIONS DECLARATION
*/
service.ping = ping;
service.repositories = repositories;
service.getRepositoriesDetails = getRepositoriesDetails;
service.tags = tags;
service.tag = tag;
service.getTagsDetails = getTagsDetails;
service.shortTagsWithProgress = shortTagsWithProgress;
service.addTag = addTag;
service.deleteManifest = deleteManifest;
service.deleteTagsWithProgress = deleteTagsWithProgress;
service.retagWithProgress = retagWithProgress;
return service;
}
]);

View file

@ -1,8 +1,9 @@
import { RegistryManagementConfigurationDefaultModel } from '../../../../portainer/models/registry';
import { RegistryTypes } from 'Extensions/registry-management/models/registryTypes';
angular.module('portainer.extensions.registrymanagement')
.controller('ConfigureRegistryController', ['$scope', '$state', '$transition$', 'RegistryService', 'RegistryV2Service', 'Notifications',
function ($scope, $state, $transition$, RegistryService, RegistryV2Service, Notifications) {
.controller('ConfigureRegistryController', ['$scope', '$state', '$transition$', 'RegistryService', 'RegistryServiceSelector', 'Notifications',
function ($scope, $state, $transition$, RegistryService, RegistryServiceSelector, Notifications) {
$scope.state = {
testInProgress: false,
@ -18,7 +19,7 @@ function ($scope, $state, $transition$, RegistryService, RegistryV2Service, Noti
RegistryService.configureRegistry($scope.registry.Id, $scope.model)
.then(function success() {
return RegistryV2Service.ping($scope.registry.Id, true);
return RegistryServiceSelector.ping($scope.registry, true);
})
.then(function success() {
Notifications.success('Success', 'Valid management configuration');
@ -50,6 +51,7 @@ function ($scope, $state, $transition$, RegistryService, RegistryV2Service, Noti
function initView() {
var registryId = $transition$.params().id;
$scope.RegistryTypes = RegistryTypes;
RegistryService.registry(registryId)
.then(function success(data) {

View file

@ -32,7 +32,7 @@
</div>
<!-- !registry-url-input -->
<!-- authentication-checkbox -->
<div class="form-group" ng-if="registry.Type === 3">
<div class="form-group" ng-if="registry.Type === RegistryTypes.CUSTOM || registry.Type === RegistryTypes.GITLAB">
<div class="col-sm-12">
<label for="registry_auth" class="control-label text-left">
Authentication
@ -65,7 +65,7 @@
</div>
<!-- !authentication-credentials -->
<!-- tls -->
<div ng-if="registry.Type === 3">
<div ng-if="registry.Type === RegistryTypes.CUSTOM || registry.Type === RegistryTypes.GITLAB">
<!-- tls-checkbox -->
<div class="form-group">
<div class="col-sm-12">

View file

@ -48,6 +48,8 @@
<td>Repository</td>
<td>
{{ repository.Name }}
</td>
<td>
<button class="btn btn-xs btn-danger" ng-if="!state.tagsRetrieval.running && state.tagsRetrieval.progression !== 0" ng-click="removeRepository()">
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Delete this repository
</button>
@ -56,10 +58,12 @@
<tr>
<td>Tags count</td>
<td>{{ repository.Tags.length }}</td>
<td></td>
</tr>
<tr ng-if="short.Images.length">
<td>Images count</td>
<td>{{ short.Images.length }}</td>
<td></td>
</tr>
</tbody>
</table>

View file

@ -2,8 +2,8 @@ import _ from 'lodash-es';
import { RepositoryTagViewModel, RepositoryShortTag } from '../../../models/repositoryTag';
angular.module('portainer.app')
.controller('RegistryRepositoryController', ['$q', '$async', '$scope', '$uibModal', '$interval', '$transition$', '$state', 'RegistryV2Service', 'RegistryService', 'ModalService', 'Notifications', 'ImageHelper',
function ($q, $async, $scope, $uibModal, $interval, $transition$, $state, RegistryV2Service, RegistryService, ModalService, Notifications, ImageHelper) {
.controller('RegistryRepositoryController', ['$q', '$async', '$scope', '$uibModal', '$interval', '$transition$', '$state', 'RegistryServiceSelector', 'RegistryService', 'ModalService', 'Notifications', 'ImageHelper',
function ($q, $async, $scope, $uibModal, $interval, $transition$, $state, RegistryServiceSelector, RegistryService, ModalService, Notifications, ImageHelper) {
$scope.state = {
actionInProgress: false,
@ -63,7 +63,7 @@ angular.module('portainer.app')
$scope.paginationAction = function (tags) {
$scope.state.loading = true;
RegistryV2Service.getTagsDetails($scope.registryId, $scope.repository.Name, tags)
RegistryServiceSelector.getTagsDetails($scope.registry, $scope.repository.Name, tags)
.then(function success(data) {
for (var i = 0; i < data.length; i++) {
var idx = _.findIndex($scope.tags, {'Name': data[i].Name});
@ -86,7 +86,7 @@ angular.module('portainer.app')
function createRetrieveAsyncGenerator() {
$scope.state.tagsRetrieval.asyncGenerator =
RegistryV2Service.shortTagsWithProgress($scope.registryId, $scope.repository.Name, $scope.repository.Tags);
RegistryServiceSelector.shortTagsWithProgress($scope.registry, $scope.repository.Name, $scope.repository.Tags);
}
function resetTagsRetrievalState() {
@ -151,7 +151,7 @@ angular.module('portainer.app')
}
const tag = $scope.short.Tags.find((item) => item.ImageId === $scope.formValues.SelectedImage);
const manifest = tag.ManifestV2;
await RegistryV2Service.addTag($scope.registryId, $scope.repository.Name, {tag: $scope.formValues.Tag, manifest: manifest})
await RegistryServiceSelector.addTag($scope.registry, $scope.repository.Name, $scope.formValues.Tag, manifest)
Notifications.success('Success', 'Tag successfully added');
$scope.short.Tags.push(new RepositoryShortTag($scope.formValues.Tag, tag.ImageId, tag.ImageDigest, tag.ManifestV2));
@ -182,7 +182,7 @@ angular.module('portainer.app')
function createRetagAsyncGenerator(modifiedTags, modifiedDigests, impactedTags) {
$scope.state.tagsRetag.asyncGenerator =
RegistryV2Service.retagWithProgress($scope.registryId, $scope.repository.Name, modifiedTags, modifiedDigests, impactedTags);
RegistryServiceSelector.retagWithProgress($scope.registry, $scope.repository.Name, modifiedTags, modifiedDigests, impactedTags);
}
async function retagActionAsync() {
@ -252,7 +252,7 @@ angular.module('portainer.app')
function createDeleteAsyncGenerator(modifiedDigests, impactedTags) {
$scope.state.tagsDelete.asyncGenerator =
RegistryV2Service.deleteTagsWithProgress($scope.registryId, $scope.repository.Name, modifiedDigests, impactedTags);
RegistryServiceSelector.deleteTagsWithProgress($scope.registry, $scope.repository.Name, modifiedDigests, impactedTags);
}
async function removeTagsAsync(selectedTags) {
@ -289,7 +289,7 @@ angular.module('portainer.app')
Notifications.success('Success', 'Tags successfully deleted');
if ($scope.short.Tags.length === 0) {
$state.go('portainer.registries.registry.repositories', {id: $scope.registryId}, {reload: true});
$state.go('portainer.registries.registry.repositories', {id: $scope.registry.Id}, {reload: true});
}
await loadRepositoryDetails();
} catch (err) {
@ -322,10 +322,10 @@ angular.module('portainer.app')
try {
const digests = _.uniqBy($scope.short.Tags, 'ImageDigest');
const promises = [];
_.map(digests, (item) => promises.push(RegistryV2Service.deleteManifest($scope.registryId, $scope.repository.Name, item.ImageDigest)));
_.map(digests, (item) => promises.push(RegistryServiceSelector.deleteManifest($scope.registry, $scope.repository.Name, item.ImageDigest)));
await Promise.all(promises);
Notifications.success('Success', 'Repository sucessfully removed');
$state.go('portainer.registries.registry.repositories', {id: $scope.registryId}, {reload: true});
$state.go('portainer.registries.registry.repositories', {id: $scope.registry.Id}, {reload: true});
} catch (err) {
Notifications.error('Failure', err, 'Unable to delete repository');
}
@ -351,9 +351,9 @@ angular.module('portainer.app')
*/
async function loadRepositoryDetails() {
try {
const registryId = $scope.registryId;
const registry = $scope.registry;
const repository = $scope.repository.Name;
const tags = await RegistryV2Service.tags(registryId, repository);
const tags = await RegistryServiceSelector.tags(registry, repository);
$scope.tags = [];
$scope.repository.Tags = [];
$scope.repository.Tags = _.sortBy(_.concat($scope.repository.Tags, _.without(tags.tags, null)));
@ -365,7 +365,7 @@ angular.module('portainer.app')
async function initView() {
try {
const registryId = $scope.registryId = $transition$.params().id;
const registryId = $transition$.params().id;
$scope.repository.Name = $transition$.params().repository;
$scope.state.loading = true;

View file

@ -1,8 +1,10 @@
import _ from 'lodash-es';
import { RegistryTypes } from 'Extensions/registry-management/models/registryTypes';
angular.module('portainer.extensions.registrymanagement')
.controller('RegistryRepositoriesController', ['$transition$', '$scope', 'RegistryService', 'RegistryV2Service', 'Notifications', 'Authentication',
function ($transition$, $scope, RegistryService, RegistryV2Service, Notifications, Authentication) {
.controller('RegistryRepositoriesController', ['$transition$', '$scope', 'RegistryService', 'RegistryServiceSelector', 'Notifications', 'Authentication',
function ($transition$, $scope, RegistryService, RegistryServiceSelector, Notifications, Authentication) {
$scope.state = {
displayInvalidConfigurationMessage: false,
@ -10,8 +12,11 @@ function ($transition$, $scope, RegistryService, RegistryV2Service, Notification
};
$scope.paginationAction = function (repositories) {
if ($scope.registry.Type === RegistryTypes.GITLAB) {
return;
}
$scope.state.loading = true;
RegistryV2Service.getRepositoriesDetails($scope.state.registryId, repositories)
RegistryServiceSelector.getRepositoriesDetails($scope.registry, repositories)
.then(function success(data) {
for (var i = 0; i < data.length; i++) {
var idx = _.findIndex($scope.repositories, {'Name': data[i].Name});
@ -30,20 +35,19 @@ function ($transition$, $scope, RegistryService, RegistryV2Service, Notification
};
function initView() {
$scope.state.registryId = $transition$.params().id;
const registryId = $transition$.params().id;
var authenticationEnabled = $scope.applicationState.application.authentication;
if (authenticationEnabled) {
$scope.isAdmin = Authentication.isAdmin();
}
RegistryService.registry($scope.state.registryId)
RegistryService.registry(registryId)
.then(function success(data) {
$scope.registry = data;
RegistryV2Service.ping($scope.state.registryId, false)
RegistryServiceSelector.ping($scope.registry, false)
.then(function success() {
return RegistryV2Service.catalog($scope.state.registryId);
return RegistryServiceSelector.repositories($scope.registry);
})
.then(function success(data) {
$scope.repositories = data;

View file

@ -6,12 +6,12 @@ import { RegistryImageDetailsViewModel } from 'Extensions/registry-management/mo
class RegistryRepositoryTagController {
/* @ngInject */
constructor($transition$, $async, Notifications, RegistryService, RegistryV2Service, imagelayercommandFilter) {
constructor($transition$, $async, Notifications, RegistryService, RegistryServiceSelector, imagelayercommandFilter) {
this.$transition$ = $transition$;
this.$async = $async;
this.Notifications = Notifications;
this.RegistryService = RegistryService;
this.RegistryV2Service = RegistryV2Service;
this.RegistryServiceSelector = RegistryServiceSelector;
this.imagelayercommandFilter = imagelayercommandFilter;
this.context = {};
@ -39,7 +39,7 @@ class RegistryRepositoryTagController {
}
try {
this.registry = await this.RegistryService.registry(this.context.registryId);
this.tag = await this.RegistryV2Service.tag(this.context.registryId, this.context.repository, this.context.tag);
this.tag = await this.RegistryServiceSelector.tag(this.registry, this.context.repository, this.context.tag);
const length = this.tag.History.length;
this.history = _.map(this.tag.History, (layer, idx) => new RegistryImageLayerViewModel(length - idx, layer));
_.forEach(this.history, (item) => item.CreatedBy = this.imagelayercommandFilter(item.CreatedBy))