diff --git a/app/extensions/registry-management/_module.js b/app/extensions/registry-management/_module.js
index 9fdde0c5d..777ebcc55 100644
--- a/app/extensions/registry-management/_module.js
+++ b/app/extensions/registry-management/_module.js
@@ -34,8 +34,20 @@ angular.module('portainer.extensions.registrymanagement', [])
}
}
};
+ var registryRepositoryTag = {
+ name: 'portainer.registries.registry.repository.tag',
+ url: '/:tag',
+ views: {
+ 'content@': {
+ templateUrl: './views/repositories/tag/registryRepositoryTag.html',
+ controller: 'RegistryRepositoryTagController',
+ controllerAs: 'ctrl'
+ }
+ }
+ };
$stateRegistryProvider.register(registryConfiguration);
$stateRegistryProvider.register(registryRepositories);
$stateRegistryProvider.register(registryRepositoryTags);
+ $stateRegistryProvider.register(registryRepositoryTag);
}]);
diff --git a/app/extensions/registry-management/components/registries-repository-tags-datatable/registriesRepositoryTagsDatatable.html b/app/extensions/registry-management/components/registries-repository-tags-datatable/registriesRepositoryTagsDatatable.html
index 6e524d77e..15448a9e4 100644
--- a/app/extensions/registry-management/components/registries-repository-tags-datatable/registriesRepositoryTagsDatatable.html
+++ b/app/extensions/registry-management/components/registries-repository-tags-datatable/registriesRepositoryTagsDatatable.html
@@ -44,7 +44,8 @@
- {{ item.Name }}
+ {{ item.Name }}
{{ item.Os }}/{{ item.Architecture }} |
{{ item.ImageId | trimshasum }} |
diff --git a/app/extensions/registry-management/models/registryImageDetails.js b/app/extensions/registry-management/models/registryImageDetails.js
new file mode 100644
index 000000000..9a80adedc
--- /dev/null
+++ b/app/extensions/registry-management/models/registryImageDetails.js
@@ -0,0 +1,14 @@
+export function RegistryImageDetailsViewModel(data) {
+ this.Id = data.id;
+ this.Parent = data.parent;
+ this.Created = data.created;
+ this.DockerVersion = data.docker_version;
+ this.Os = data.os;
+ this.Architecture = data.architecture;
+ this.Author = data.author;
+ this.Command = data.config.Cmd;
+ this.Entrypoint = data.container_config.Entrypoint ? data.container_config.Entrypoint : '';
+ this.ExposedPorts = data.container_config.ExposedPorts ? Object.keys(data.container_config.ExposedPorts) : [];
+ this.Volumes = data.container_config.Volumes ? Object.keys(data.container_config.Volumes) : [];
+ this.Env = data.container_config.Env ? data.container_config.Env : [];
+}
diff --git a/app/extensions/registry-management/models/registryImageLayer.js b/app/extensions/registry-management/models/registryImageLayer.js
new file mode 100644
index 000000000..f8990a514
--- /dev/null
+++ b/app/extensions/registry-management/models/registryImageLayer.js
@@ -0,0 +1,8 @@
+import _ from 'lodash-es';
+
+export function RegistryImageLayerViewModel(order, data) {
+ this.Order = order;
+ this.Id = data.id;
+ this.Created = data.created;
+ this.CreatedBy = _.join(data.container_config.Cmd, ' ');
+}
\ No newline at end of file
diff --git a/app/extensions/registry-management/views/repositories/tag/registryRepositoryTag.html b/app/extensions/registry-management/views/repositories/tag/registryRepositoryTag.html
new file mode 100644
index 000000000..69316f24e
--- /dev/null
+++ b/app/extensions/registry-management/views/repositories/tag/registryRepositoryTag.html
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+ Registries >
+ {{ ctrl.registry.Name }} >
+ {{ ctrl.context.repository }} >
+ {{ ctrl.context.tag }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID |
+
+ {{ ctrl.details.Id }}
+ |
+
+
+ Parent |
+ {{ ctrl.details.Parent }} |
+
+
+ Created |
+ {{ ctrl.details.Created|getisodate }} |
+
+
+ Build |
+ Docker {{ ctrl.details.DockerVersion }} on {{ ctrl.details.Os}}, {{ ctrl.details.Architecture }} |
+
+
+ Author |
+ {{ ctrl.details.Author }} |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CMD |
+ {{ ctrl.details.Command|command }} |
+
+
+ ENTRYPOINT |
+ {{ ctrl.details.Entrypoint|command }} |
+
+
+ EXPOSE |
+
+
+ {{ port }}
+
+ |
+
+
+ VOLUME |
+
+
+ {{ volume }}
+
+ |
+
+
+ ENV |
+
+
+
+ {{ var|key: '=' }} |
+ {{ var|value: '=' }} |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Order
+
+
+
+ |
+
+
+ Layer
+
+
+
+ |
+
+
+
+
+ {{ layer.Order }}
+ |
+
+
+
+ {{ layer.CreatedBy }}
+
+
+ {{ layer.CreatedBy | truncate:130 }}
+
+
+
+
+
+
+
+
+
+ {{ layer.CreatedBy }}
+
+
+ |
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/extensions/registry-management/views/repositories/tag/registryRepositoryTagController.js b/app/extensions/registry-management/views/repositories/tag/registryRepositoryTagController.js
new file mode 100644
index 000000000..4f34b89f1
--- /dev/null
+++ b/app/extensions/registry-management/views/repositories/tag/registryRepositoryTagController.js
@@ -0,0 +1,57 @@
+import _ from 'lodash-es';
+import angular from 'angular';
+import { RegistryImageLayerViewModel } from 'Extensions/registry-management/models/registryImageLayer';
+import { RegistryImageDetailsViewModel } from 'Extensions/registry-management/models/registryImageDetails';
+
+class RegistryRepositoryTagController {
+
+ /* @ngInject */
+ constructor($transition$, $async, Notifications, RegistryService, RegistryV2Service, imagelayercommandFilter) {
+ this.$transition$ = $transition$;
+ this.$async = $async;
+ this.Notifications = Notifications;
+ this.RegistryService = RegistryService;
+ this.RegistryV2Service = RegistryV2Service;
+ this.imagelayercommandFilter = imagelayercommandFilter;
+
+ this.context = {};
+ this.onInit = this.onInit.bind(this);
+ }
+
+ toggleLayerCommand(layerId) {
+ $('#layer-command-expander'+layerId+' span').toggleClass('glyphicon-plus-sign glyphicon-minus-sign');
+ $('#layer-command-'+layerId+'-short').toggle();
+ $('#layer-command-'+layerId+'-full').toggle();
+ }
+
+ order(sortType) {
+ this.Sort.Reverse = (this.Sort.Type === sortType) ? !this.Sort.Reverse : false;
+ this.Sort.Type = sortType;
+ }
+
+ async onInit() {
+ this.context.registryId = this.$transition$.params().id;
+ this.context.repository = this.$transition$.params().repository;
+ this.context.tag = this.$transition$.params().tag;
+ this.Sort = {
+ Type: 'Order',
+ Reverse: false
+ }
+ 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);
+ 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))
+ this.details = new RegistryImageDetailsViewModel(this.tag.History[0]);
+ } catch (error) {
+ this.Notifications.error('Failure', error, 'Unable to retrieve tag')
+ }
+ }
+
+ $onInit() {
+ return this.$async(this.onInit);
+ }
+}
+export default RegistryRepositoryTagController;
+angular.module('portainer.extensions.registrymanagement').controller('RegistryRepositoryTagController', RegistryRepositoryTagController);