mirror of https://github.com/portainer/portainer
fix(registry) EE-1861 improve registry selection (#5921)
* fix(registry) EE-1861 fail to select registry with same name * fix(registry) EE-1861 show registry modal when pull and push image * fix(registry) EE-1861 cleanup code Co-authored-by: Simon Meng <simon.meng@portainer.io>pull/5931/head
parent
1ff5f25e40
commit
4f350ab6f5
|
@ -6,7 +6,7 @@
|
||||||
</label>
|
</label>
|
||||||
<div ng-class="$ctrl.inputClass">
|
<div ng-class="$ctrl.inputClass">
|
||||||
<select
|
<select
|
||||||
ng-options="registry as registry.Name for registry in $ctrl.registries track by registry.Name"
|
ng-options="registry as registry.Name for registry in $ctrl.registries track by registry.Id"
|
||||||
ng-model="$ctrl.model.Registry"
|
ng-model="$ctrl.model.Registry"
|
||||||
id="image_registry"
|
id="image_registry"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
|
|
@ -17,6 +17,8 @@ angular.module('portainer.docker').controller('ImageController', [
|
||||||
'FileSaver',
|
'FileSaver',
|
||||||
'Blob',
|
'Blob',
|
||||||
'endpoint',
|
'endpoint',
|
||||||
|
'EndpointService',
|
||||||
|
'RegistryModalService',
|
||||||
function (
|
function (
|
||||||
$async,
|
$async,
|
||||||
$q,
|
$q,
|
||||||
|
@ -32,7 +34,9 @@ angular.module('portainer.docker').controller('ImageController', [
|
||||||
ModalService,
|
ModalService,
|
||||||
FileSaver,
|
FileSaver,
|
||||||
Blob,
|
Blob,
|
||||||
endpoint
|
endpoint,
|
||||||
|
EndpointService,
|
||||||
|
RegistryModalService
|
||||||
) {
|
) {
|
||||||
$scope.endpoint = endpoint;
|
$scope.endpoint = endpoint;
|
||||||
$scope.isAdmin = Authentication.isAdmin();
|
$scope.isAdmin = Authentication.isAdmin();
|
||||||
|
@ -84,11 +88,13 @@ angular.module('portainer.docker').controller('ImageController', [
|
||||||
|
|
||||||
async function pushTag(repository) {
|
async function pushTag(repository) {
|
||||||
return $async(async () => {
|
return $async(async () => {
|
||||||
$('#uploadResourceHint').show();
|
|
||||||
try {
|
try {
|
||||||
const registryModel = await RegistryService.retrievePorRegistryModelFromRepository(repository, endpoint.Id);
|
const registryModel = await RegistryModalService.registryModal(repository, $scope.registries);
|
||||||
await ImageService.pushImage(registryModel);
|
if (registryModel) {
|
||||||
Notifications.success('Image successfully pushed', repository);
|
$('#uploadResourceHint').show();
|
||||||
|
await ImageService.pushImage(registryModel);
|
||||||
|
Notifications.success('Image successfully pushed', repository);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Notifications.error('Failure', err, 'Unable to push image to repository');
|
Notifications.error('Failure', err, 'Unable to push image to repository');
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -100,11 +106,13 @@ angular.module('portainer.docker').controller('ImageController', [
|
||||||
$scope.pullTag = pullTag;
|
$scope.pullTag = pullTag;
|
||||||
async function pullTag(repository) {
|
async function pullTag(repository) {
|
||||||
return $async(async () => {
|
return $async(async () => {
|
||||||
$('#downloadResourceHint').show();
|
|
||||||
try {
|
try {
|
||||||
const registryModel = await RegistryService.retrievePorRegistryModelFromRepository(repository, endpoint.Id);
|
const registryModel = await RegistryModalService.registryModal(repository, $scope.registries);
|
||||||
await ImageService.pullImage(registryModel);
|
if (registryModel) {
|
||||||
Notifications.success('Image successfully pulled', repository);
|
$('#downloadResourceHint').show();
|
||||||
|
await ImageService.pullImage(registryModel);
|
||||||
|
Notifications.success('Image successfully pulled', repository);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Notifications.error('Failure', err, 'Unable to pull image from repository');
|
Notifications.error('Failure', err, 'Unable to pull image from repository');
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -171,8 +179,15 @@ angular.module('portainer.docker').controller('ImageController', [
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function initView() {
|
async function initView() {
|
||||||
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
|
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$scope.registries = await RegistryService.loadRegistriesForDropdown(endpoint.Id);
|
||||||
|
} catch (err) {
|
||||||
|
this.Notifications.error('Failure', err, 'Unable to load registries');
|
||||||
|
}
|
||||||
|
|
||||||
$q.all({
|
$q.all({
|
||||||
image: ImageService.image($transition$.params().id),
|
image: ImageService.image($transition$.params().id),
|
||||||
history: ImageService.history($transition$.params().id),
|
history: ImageService.history($transition$.params().id),
|
||||||
|
|
|
@ -22,6 +22,8 @@ angular.module('portainer.app').factory('RegistryService', [
|
||||||
createRegistry,
|
createRegistry,
|
||||||
createGitlabRegistries,
|
createGitlabRegistries,
|
||||||
retrievePorRegistryModelFromRepository,
|
retrievePorRegistryModelFromRepository,
|
||||||
|
retrievePorRegistryModelFromRepositoryWithRegistries,
|
||||||
|
loadRegistriesForDropdown,
|
||||||
};
|
};
|
||||||
|
|
||||||
function registries() {
|
function registries() {
|
||||||
|
@ -114,7 +116,7 @@ angular.module('portainer.app').factory('RegistryService', [
|
||||||
// 3. only URL matched
|
// 3. only URL matched
|
||||||
// 4. pick up the first dockerhub registry
|
// 4. pick up the first dockerhub registry
|
||||||
function findBestMatchRegistry(repository, registries, registryId) {
|
function findBestMatchRegistry(repository, registries, registryId) {
|
||||||
let highMatch, lowMatch;
|
let match2, match3, match4;
|
||||||
|
|
||||||
for (const registry of registries) {
|
for (const registry of registries) {
|
||||||
if (registry.Id == registryId) {
|
if (registry.Id == registryId) {
|
||||||
|
@ -126,21 +128,21 @@ angular.module('portainer.app').factory('RegistryService', [
|
||||||
// <USERNAME>/nginx:latest
|
// <USERNAME>/nginx:latest
|
||||||
// docker.io/<USERNAME>/nginx:latest
|
// docker.io/<USERNAME>/nginx:latest
|
||||||
if (repository.startsWith(registry.Username + '/') || repository.startsWith(getURL(registry) + '/' + registry.Username + '/')) {
|
if (repository.startsWith(registry.Username + '/') || repository.startsWith(getURL(registry) + '/' + registry.Username + '/')) {
|
||||||
highMatch = registry;
|
match2 = registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to match repository examples:
|
// try to match repository examples:
|
||||||
// portainer/portainer-ee:latest
|
// portainer/portainer-ee:latest
|
||||||
// <NON-USERNAME>/portainer-ee:latest
|
// <NON-USERNAME>/portainer-ee:latest
|
||||||
lowMatch = lowMatch || registry;
|
match4 = match4 || registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.includes(repository, getURL(registry))) {
|
if (_.includes(repository, getURL(registry))) {
|
||||||
lowMatch = registry;
|
match3 = registry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return highMatch || lowMatch;
|
return match2 || match3 || match4;
|
||||||
}
|
}
|
||||||
|
|
||||||
function retrievePorRegistryModelFromRepositoryWithRegistries(repository, registries, registryId) {
|
function retrievePorRegistryModelFromRepositoryWithRegistries(repository, registries, registryId) {
|
||||||
|
@ -176,5 +178,22 @@ angular.module('portainer.app').factory('RegistryService', [
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadRegistriesForDropdown(endpointId, namespace) {
|
||||||
|
return $async(async () => {
|
||||||
|
try {
|
||||||
|
const registries = await EndpointService.registries(endpointId, namespace);
|
||||||
|
|
||||||
|
// hide default(anonymous) dockerhub registry if user has an authenticated one
|
||||||
|
if (!registries.some((registry) => registry.Type === RegistryTypes.DOCKERHUB)) {
|
||||||
|
registries.push(new DockerHubViewModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
return registries;
|
||||||
|
} catch (err) {
|
||||||
|
throw { msg: 'Unable to retrieve the registries', err: err };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -308,6 +308,17 @@ angular.module('portainer.app').factory('ModalService', [
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
service.selectRegistry = function (options) {
|
||||||
|
var box = bootbox.prompt({
|
||||||
|
title: 'Which registry do you want to use?',
|
||||||
|
inputType: 'select',
|
||||||
|
value: options.defaultValue,
|
||||||
|
inputOptions: options.options,
|
||||||
|
callback: options.callback,
|
||||||
|
});
|
||||||
|
applyBoxCSS(box);
|
||||||
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
angular.module('portainer.app').factory('RegistryModalService', ModalServiceFactory);
|
||||||
|
|
||||||
|
function ModalServiceFactory($q, ModalService, RegistryService) {
|
||||||
|
const service = {};
|
||||||
|
|
||||||
|
function registries2Options(registries) {
|
||||||
|
return registries.map((r) => ({
|
||||||
|
text: r.Name,
|
||||||
|
value: String(r.Id),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
service.registryModal = async function (repository, registries) {
|
||||||
|
const deferred = $q.defer();
|
||||||
|
|
||||||
|
const options = registries2Options(registries);
|
||||||
|
const registryModel = RegistryService.retrievePorRegistryModelFromRepositoryWithRegistries(repository, registries);
|
||||||
|
const defaultValue = String(_.get(registryModel, 'Registry.Id', '0'));
|
||||||
|
|
||||||
|
ModalService.selectRegistry({
|
||||||
|
options,
|
||||||
|
defaultValue,
|
||||||
|
callback: (registryId) => {
|
||||||
|
if (registryId) {
|
||||||
|
const registryModel = RegistryService.retrievePorRegistryModelFromRepositoryWithRegistries(repository, registries, registryId);
|
||||||
|
deferred.resolve(registryModel);
|
||||||
|
} else {
|
||||||
|
deferred.resolve(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}
|
Loading…
Reference in New Issue