mirror of https://github.com/portainer/portainer
feat(registry): registry or direct url selector
parent
a41ca1fd46
commit
e00185a160
|
@ -1,17 +1,45 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="image_name" ng-class="$ctrl.labelClass" class="control-label text-left">Image</label>
|
<div class="col-sm-12">
|
||||||
<div ng-class="$ctrl.inputClass">
|
<label for="use_registry" class="control-label text-left">
|
||||||
<input type="text" class="form-control" uib-typeahead="image for image in $ctrl.availableImages | filter:$viewValue | limitTo:5"
|
Use registry
|
||||||
ng-model="$ctrl.image" name="image_name" placeholder="e.g. myImage:myTag" required>
|
</label>
|
||||||
</div>
|
<label class="switch" style="margin-left: 20px;">
|
||||||
<label for="image_registry" class="margin-sm-top control-label text-right" ng-class="$ctrl.labelClass">
|
<input type="checkbox" ng-model="$ctrl.useRegistry"><i></i>
|
||||||
Registry
|
</label>
|
||||||
</label>
|
|
||||||
<div ng-class="$ctrl.inputClass" class="margin-sm-top">
|
|
||||||
<select ng-options="registry as registry.Name for registry in $ctrl.availableRegistries" ng-model="$ctrl.registry" id="image_registry"
|
|
||||||
class="form-control"></select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- use registry -->
|
||||||
|
<div ng-if="$ctrl.useRegistry">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="image_name" ng-class="$ctrl.labelClass" class="control-label text-left">Image</label>
|
||||||
|
<div ng-class="$ctrl.inputClass">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon" id="registry-name">{{$ctrl.registry.URL}}</span>
|
||||||
|
<input type="text" class="form-control" aria-describedby="registry-name" uib-typeahead="image for image in $ctrl.availableImages | filter:$viewValue | limitTo:5"
|
||||||
|
ng-model="$ctrl.image" name="image_name" placeholder="e.g. myImage:myTag" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="image_registry" class="margin-sm-top control-label text-right" ng-class="$ctrl.labelClass">
|
||||||
|
Registry
|
||||||
|
</label>
|
||||||
|
<div ng-class="$ctrl.inputClass" class="margin-sm-top">
|
||||||
|
<select ng-options="registry as registry.Name for registry in $ctrl.availableRegistries" ng-model="$ctrl.registry" id="image_registry"
|
||||||
|
class="form-control"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- ! use registry -->
|
||||||
|
<!-- don't use registry -->
|
||||||
|
<div ng-if="!$ctrl.useRegistry">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="image_name" ng-class="$ctrl.labelClass" class="control-label text-left">Image</label>
|
||||||
|
<div ng-class="$ctrl.inputClass">
|
||||||
|
<input type="text" class="form-control" ng-model="$ctrl.image" name="image_name" placeholder="e.g. registry:port/myImage:myTag" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- ! don't use registry -->
|
||||||
|
<!-- info message -->
|
||||||
<div class="form-group" ng-show="$ctrl.form.image_name.$invalid">
|
<div class="form-group" ng-show="$ctrl.form.image_name.$invalid">
|
||||||
<div class="col-sm-12 small text-warning">
|
<div class="col-sm-12 small text-warning">
|
||||||
<div ng-messages="$ctrl.form.image_name.$error">
|
<div ng-messages="$ctrl.form.image_name.$error">
|
||||||
|
@ -19,3 +47,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- ! info message -->
|
||||||
|
|
|
@ -1,31 +1,40 @@
|
||||||
|
import angular from 'angular';
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
|
|
||||||
angular.module('portainer.docker')
|
class porImageRegistryController {
|
||||||
.controller('porImageRegistryController', ['$q', 'RegistryService', 'DockerHubService', 'ImageService', 'Notifications',
|
/* @ngInject */
|
||||||
function ($q, RegistryService, DockerHubService, ImageService, Notifications) {
|
constructor($async, RegistryService, DockerHubService, ImageService, Notifications) {
|
||||||
var ctrl = this;
|
this.$async = $async;
|
||||||
|
this.RegistryService = RegistryService;
|
||||||
|
this.DockerHubService = DockerHubService;
|
||||||
|
this.ImageService = ImageService;
|
||||||
|
this.Notifications = Notifications;
|
||||||
|
|
||||||
function initComponent() {
|
this.onInit = this.onInit.bind(this);
|
||||||
$q.all({
|
|
||||||
registries: RegistryService.registries(),
|
|
||||||
dockerhub: DockerHubService.dockerhub(),
|
|
||||||
availableImages: ctrl.autoComplete ? ImageService.images() : []
|
|
||||||
})
|
|
||||||
.then(function success(data) {
|
|
||||||
var dockerhub = data.dockerhub;
|
|
||||||
var registries = data.registries;
|
|
||||||
ctrl.availableImages = ImageService.getUniqueTagListFromImages(data.availableImages);
|
|
||||||
ctrl.availableRegistries = [dockerhub].concat(registries);
|
|
||||||
if (!ctrl.registry.Id) {
|
|
||||||
ctrl.registry = dockerhub;
|
|
||||||
} else {
|
|
||||||
ctrl.registry = _.find(ctrl.availableRegistries, { 'Id': ctrl.registry.Id });
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function error(err) {
|
|
||||||
Notifications.error('Failure', err, 'Unable to retrieve registries');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initComponent();
|
async onInit() {
|
||||||
}]);
|
try {
|
||||||
|
const [registries, dockerhub, availableImages] = await Promise.all([this.RegistryService.registries(),
|
||||||
|
this.DockerHubService.dockerhub(),
|
||||||
|
this.autoComplete ? this.ImageService.images() : []]);
|
||||||
|
this.availableImages = this.ImageService.getUniqueTagListFromImages(availableImages);
|
||||||
|
this.availableRegistries = [dockerhub].concat(registries);
|
||||||
|
if (!this.registry.Id) {
|
||||||
|
this.registry = dockerhub;
|
||||||
|
} else {
|
||||||
|
this.registry = _.find(this.availableRegistries, { 'Id': this.registry.Id });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.Notifications.error('Failure', err, 'Unable to retrieve registries');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
this.useRegistry = true;
|
||||||
|
return this.$async(this.onInit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default porImageRegistryController;
|
||||||
|
angular.module('portainer.docker').controller('porImageRegistryController', porImageRegistryController);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import _ from 'lodash-es';
|
// import _ from 'lodash-es';
|
||||||
|
|
||||||
angular.module('portainer.docker')
|
angular.module('portainer.docker')
|
||||||
.factory('ImageHelper', [function ImageHelperFactory() {
|
.factory('ImageHelper', [function ImageHelperFactory() {
|
||||||
|
@ -12,26 +12,26 @@ angular.module('portainer.docker')
|
||||||
return tag.match(/^(?![\.\-])([a-zA-Z0-9\_\.\-])+$/g);
|
return tag.match(/^(?![\.\-])([a-zA-Z0-9\_\.\-])+$/g);
|
||||||
}
|
}
|
||||||
|
|
||||||
helper.extractImageAndRegistryFromRepository = function(repository) {
|
// helper.extractImageAndRegistryFromRepository = function(repository) {
|
||||||
var slashCount = _.countBy(repository)['/'];
|
// var slashCount = _.countBy(repository)['/'];
|
||||||
var registry = null;
|
// var registry = null;
|
||||||
var image = repository;
|
// var image = repository;
|
||||||
if (slashCount >= 1) {
|
// if (slashCount >= 1) {
|
||||||
// assume something/something[/...]
|
// // assume something/something[/...]
|
||||||
registry = repository.substr(0, repository.indexOf('/'));
|
// registry = repository.substr(0, repository.indexOf('/'));
|
||||||
// assume valid DNS name or IP (contains at least one '.')
|
// // assume valid DNS name or IP (contains at least one '.')
|
||||||
if (_.countBy(registry)['.'] > 0) {
|
// if (_.countBy(registry)['.'] > 0) {
|
||||||
image = repository.substr(repository.indexOf('/') + 1);
|
// image = repository.substr(repository.indexOf('/') + 1);
|
||||||
} else {
|
// } else {
|
||||||
registry = null;
|
// registry = null;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
registry: registry,
|
// registry: registry,
|
||||||
image: image
|
// image: image
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
helper.getImagesNamesForDownload = function(images) {
|
helper.getImagesNamesForDownload = function(images) {
|
||||||
var names = images.map(function(image) {
|
var names = images.map(function(image) {
|
||||||
|
@ -42,34 +42,12 @@ angular.module('portainer.docker')
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function extractNameAndTag(imageName, registry) {
|
|
||||||
var imageNameAndTag = imageName.split(':');
|
|
||||||
var image = imageNameAndTag[0];
|
|
||||||
var tag = imageNameAndTag[1] ? imageNameAndTag[1] : 'latest';
|
|
||||||
if (registry) {
|
|
||||||
image = registry + '/' + imageNameAndTag[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
image: image,
|
|
||||||
tag: tag
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
helper.createImageConfigForCommit = function(imageName, registry) {
|
|
||||||
var imageAndTag = extractNameAndTag(imageName, registry);
|
|
||||||
return {
|
|
||||||
repo: imageAndTag.image,
|
|
||||||
tag: imageAndTag.tag
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
helper.createImageConfigForContainer = function (imageName, registry) {
|
helper.createImageConfigForContainer = function (imageName, registry) {
|
||||||
var imageAndTag = extractNameAndTag(imageName, registry);
|
void registry;
|
||||||
|
console.log(imageName);
|
||||||
return {
|
return {
|
||||||
fromImage: imageAndTag.image,
|
fromImage: imageName
|
||||||
tag: imageAndTag.tag
|
}
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
helper.removeDigestFromRepository = function(repository) {
|
helper.removeDigestFromRepository = function(repository) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ angular.module('portainer.docker')
|
||||||
endpointId: EndpointProvider.endpointID
|
endpointId: EndpointProvider.endpointID
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
commitContainer: {method: 'POST', params: {container: '@id', repo: '@repo', tag: '@tag'}, ignoreLoadingBar: true}
|
// commitContainer: {method: 'POST', params: {container: '@id', repo: '@repo', tag: '@tag'}, ignoreLoadingBar: true}
|
||||||
|
commitContainer: {method: 'POST', params: {container: '@id', repo: '@repo'}, ignoreLoadingBar: true}
|
||||||
});
|
});
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -15,7 +15,8 @@ function ImageFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, HttpR
|
||||||
search: {method: 'GET', params: {action: 'search'}},
|
search: {method: 'GET', params: {action: 'search'}},
|
||||||
history: {method: 'GET', params: {action: 'history'}, isArray: true},
|
history: {method: 'GET', params: {action: 'history'}, isArray: true},
|
||||||
insert: {method: 'POST', params: {id: '@id', action: 'insert'}},
|
insert: {method: 'POST', params: {id: '@id', action: 'insert'}},
|
||||||
tag: {method: 'POST', params: {id: '@id', action: 'tag', force: 0, repo: '@repo', tag: '@tag'}, ignoreLoadingBar: true},
|
// tag: {method: 'POST', params: {id: '@id', action: 'tag', force: 0, repo: '@repo', tag: '@tag'}, ignoreLoadingBar: true},
|
||||||
|
tag: {method: 'POST', params: {id: '@id', action: 'tag', force: 0, repo: '@repo'}, ignoreLoadingBar: true},
|
||||||
inspect: {method: 'GET', params: {id: '@id', action: 'json'}},
|
inspect: {method: 'GET', params: {id: '@id', action: 'json'}},
|
||||||
push: {
|
push: {
|
||||||
method: 'POST', params: {action: 'push', id: '@tag'},
|
method: 'POST', params: {action: 'push', id: '@tag'},
|
||||||
|
|
|
@ -108,6 +108,7 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
|
||||||
service.createAndStartContainer = function(configuration) {
|
service.createAndStartContainer = function(configuration) {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
var container;
|
var container;
|
||||||
|
console.log(configuration);
|
||||||
service.createContainer(configuration)
|
service.createContainer(configuration)
|
||||||
.then(function success(data) {
|
.then(function success(data) {
|
||||||
container = data;
|
container = data;
|
||||||
|
|
|
@ -128,11 +128,13 @@ angular.module('portainer.docker')
|
||||||
}
|
}
|
||||||
|
|
||||||
service.pullImage = function(image, registry, ignoreErrors) {
|
service.pullImage = function(image, registry, ignoreErrors) {
|
||||||
var imageDetails = ImageHelper.extractImageAndRegistryFromRepository(image);
|
// var imageDetails = ImageHelper.extractImageAndRegistryFromRepository(image);
|
||||||
var imageConfiguration = ImageHelper.createImageConfigForContainer(imageDetails.image, registry.URL);
|
// var imageConfiguration = ImageHelper.createImageConfigForContainer(imageDetails.image, registry.URL);
|
||||||
var authenticationDetails = registry.Authentication ? RegistryService.encodedCredentials(registry) : '';
|
var authenticationDetails = registry.Authentication ? RegistryService.encodedCredentials(registry) : '';
|
||||||
HttpRequestHelper.setRegistryAuthenticationHeader(authenticationDetails);
|
HttpRequestHelper.setRegistryAuthenticationHeader(authenticationDetails);
|
||||||
|
|
||||||
|
var imageConfiguration = ImageHelper.createImageConfigForContainer(image, registry.URL);
|
||||||
|
|
||||||
if (ignoreErrors) {
|
if (ignoreErrors) {
|
||||||
return pullImageAndIgnoreErrors(imageConfiguration);
|
return pullImageAndIgnoreErrors(imageConfiguration);
|
||||||
}
|
}
|
||||||
|
@ -140,8 +142,8 @@ angular.module('portainer.docker')
|
||||||
};
|
};
|
||||||
|
|
||||||
service.tagImage = function(id, image, registry) {
|
service.tagImage = function(id, image, registry) {
|
||||||
var imageConfig = ImageHelper.createImageConfigForCommit(image, registry);
|
void registry;
|
||||||
return Image.tag({id: id, tag: imageConfig.tag, repo: imageConfig.repo}).$promise;
|
return Image.tag({id: id, repo: image}).$promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
service.downloadImages = function(images) {
|
service.downloadImages = function(images) {
|
||||||
|
|
|
@ -133,8 +133,7 @@ function ($q, $scope, $async, $state, $timeout, $transition$, $filter, Container
|
||||||
var image = config.Image;
|
var image = config.Image;
|
||||||
var registry = $scope.formValues.Registry;
|
var registry = $scope.formValues.Registry;
|
||||||
var imageConfig = ImageHelper.createImageConfigForContainer(image, registry.URL);
|
var imageConfig = ImageHelper.createImageConfigForContainer(image, registry.URL);
|
||||||
config.Image = imageConfig.fromImage + ':' + imageConfig.tag;
|
config.Image = imageConfig.fromImage ;//+ ':' + imageConfig.tag;
|
||||||
$scope.imageConfig = imageConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function preparePortBindings(config) {
|
function preparePortBindings(config) {
|
||||||
|
@ -296,6 +295,7 @@ function ($q, $scope, $async, $state, $timeout, $transition$, $filter, Container
|
||||||
config.Cmd = ContainerHelper.commandStringToArray(config.Cmd);
|
config.Cmd = ContainerHelper.commandStringToArray(config.Cmd);
|
||||||
prepareNetworkConfig(config);
|
prepareNetworkConfig(config);
|
||||||
prepareImageConfig(config);
|
prepareImageConfig(config);
|
||||||
|
console.log(config);
|
||||||
preparePortBindings(config);
|
preparePortBindings(config);
|
||||||
prepareConsole(config);
|
prepareConsole(config);
|
||||||
prepareEnvironmentVariables(config);
|
prepareEnvironmentVariables(config);
|
||||||
|
|
|
@ -150,11 +150,9 @@ function ($q, $scope, $state, $transition$, $filter, Commit, ContainerHelper, Co
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.commit = function () {
|
$scope.commit = function () {
|
||||||
var image = $scope.config.Image;
|
const image = $scope.config.Image;
|
||||||
$scope.config.Image = '';
|
$scope.config.Image = '';
|
||||||
var registry = $scope.config.Registry;
|
Commit.commitContainer({id: $transition$.params().id, repo: image}, function () {
|
||||||
var imageConfig = ImageHelper.createImageConfigForCommit(image, registry.URL);
|
|
||||||
Commit.commitContainer({id: $transition$.params().id, tag: imageConfig.tag, repo: imageConfig.repo}, function () {
|
|
||||||
update();
|
update();
|
||||||
Notifications.success('Image created', $transition$.params().id);
|
Notifications.success('Image created', $transition$.params().id);
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export function DockerHubViewModel(data) {
|
export function DockerHubViewModel(data) {
|
||||||
this.Name = 'DockerHub';
|
this.Name = 'DockerHub';
|
||||||
this.URL = '';
|
this.URL = 'docker.io';
|
||||||
|
// this.URL = '';
|
||||||
this.Authentication = data.Authentication;
|
this.Authentication = data.Authentication;
|
||||||
this.Username = data.Username;
|
this.Username = data.Username;
|
||||||
this.Password = data.Password;
|
this.Password = data.Password;
|
||||||
|
|
Loading…
Reference in New Issue