Skip to content
This repository has been archived by the owner on Dec 7, 2022. It is now read-only.

Commit

Permalink
ref #1203 - support manifest search, copy and remove.
Browse files Browse the repository at this point in the history
  • Loading branch information
jortel committed Aug 31, 2015
1 parent 4d3a7dd commit 1b6f36f
Show file tree
Hide file tree
Showing 6 changed files with 730 additions and 44 deletions.
4 changes: 2 additions & 2 deletions common/pulp_docker/common/models.py
Expand Up @@ -17,8 +17,8 @@ def __init__(self, digest):
"""
Initialize the Blob.
:param image_id: This field will store the blob's digest.
:type image_id: basestring
:param digest: This field will store the blob's digest.
:type digest: basestring
"""
self.digest = digest

Expand Down
125 changes: 125 additions & 0 deletions extensions_admin/pulp_docker/extensions/admin/manifest.py
@@ -0,0 +1,125 @@
from gettext import gettext as _

from pulp.client.commands import options
from pulp.client.commands.criteria import DisplayUnitAssociationsCommand
from pulp.client.commands.unit import UnitCopyCommand, UnitRemoveCommand

from pulp_docker.common.models import Manifest, Blob


DESC_COPY = _('copies manifests from one repository into another')
DESC_REMOVE = _('remove manifests from a repository')
DESC_SEARCH = _('search for manifests in a repository')
FORMAT_ERR = _('The docker manifest formatter can not process %s units.')

UNIT_ID_TEMPLATE = '%(digest)s'


def get_formatter_for_type(type_id):
"""
Returns a formatter that can be used to format the unit key
of a docker manifest or blob for display purposes.
:param type_id: A unit type ID.
:type type_id: str
:return: A formatter.
:rtype: callable
:raises ValueError: when the type_id is not supported.
"""
if type_id in [Manifest.TYPE_ID, Blob.TYPE_ID]:
return lambda u: UNIT_ID_TEMPLATE % u
else:
raise ValueError(FORMAT_ERR % type_id)


class ManifestSearchCommand(DisplayUnitAssociationsCommand):
"""
Command used to search for manifest units in a repository.
"""

def __init__(self, context):
"""
:param context: A client context.
:type context: pulp.client.extensions.core.ClientContext
"""
super(ManifestSearchCommand, self).__init__(
name='search',
description=DESC_SEARCH,
method=self.run)
self.context = context
self.prompt = context.prompt

def run(self, **kwargs):
"""
Print a list of all the manifests matching the search parameters.
:param kwargs: the search parameters for finding docker manifests
:type kwargs: dict
"""
repo_id = kwargs.pop(options.OPTION_REPO_ID.keyword)
kwargs['type_ids'] = [Manifest.TYPE_ID]
reply = self.context.server.repo_unit.search(repo_id, **kwargs)
manifests = reply.response_body
self.prompt.render_document_list(manifests)


class ManifestCopyCommand(UnitCopyCommand):
"""
Command used to copy manifest units between repositories.
"""

def __init__(self, context):
"""
:param context: A client context.
:type context: pulp.client.extensions.core.ClientContext
"""
super(ManifestCopyCommand, self).__init__(
context,
name='copy',
description=DESC_COPY,
method=self.run,
type_id=Manifest.TYPE_ID)

def get_formatter_for_type(self, type_id):
"""
Returns a formatter that can be used to format the unit key
of a docker manifest or blob for display purposes.
:param type_id: A unit type ID.
:type type_id: str
:return: A formatter.
:rtype: callable
:raises ValueError: when the type_id is not supported.
"""
return get_formatter_for_type(type_id)


class ManifestRemoveCommand(UnitRemoveCommand):
"""
Command used to remove manifest units from a repository.
"""

def __init__(self, context):
"""
:param context: A client context.
:type context: pulp.client.extensions.core.ClientContext
"""
super(ManifestRemoveCommand, self).__init__(
name='remove',
description=DESC_REMOVE,
context=context,
method=self.run,
type_id=Manifest.TYPE_ID)

def get_formatter_for_type(self, type_id):
"""
Returns a formatter that can be used to format the unit key
of a docker manifest or blob for display purposes.
:param type_id: A unit type ID.
:type type_id: str
:return: A formatter.
:rtype: callable
:raises ValueError: when the type_id is not supported.
"""
return get_formatter_for_type(type_id)
22 changes: 22 additions & 0 deletions extensions_admin/pulp_docker/extensions/admin/pulp_cli.py
Expand Up @@ -10,6 +10,8 @@
from pulp_docker.extensions.admin.images import ImageCopyCommand
from pulp_docker.extensions.admin.images import ImageRemoveCommand
from pulp_docker.extensions.admin.images import ImageSearchCommand
from pulp_docker.extensions.admin.manifest import (
ManifestSearchCommand, ManifestCopyCommand, ManifestRemoveCommand)
from pulp_docker.extensions.admin.upload import UploadDockerImageCommand
from pulp_docker.extensions.admin.repo_list import ListDockerRepositoriesCommand

Expand All @@ -36,6 +38,9 @@
'exported as a tar file to the given file on the server. '
'The web server\'s user must have the permission to write the file specified.')

SECTION_MANIFEST = 'manifest'
DESC_MANIFEST = _('manifest management commands')

OPTION_EXPORT_FILE = PulpCliOption('--export-file', DESC_EXPORT_FILE, required=False)


Expand Down Expand Up @@ -88,10 +93,27 @@ def add_repo_section(context, parent_section):
repo_section.add_command(ImageCopyCommand(context))
repo_section.add_command(ImageSearchCommand(context))
repo_section.add_command(ListDockerRepositoriesCommand(context))
add_manifest_section(context, repo_section)

return repo_section


def add_manifest_section(context, parent_section):
"""
Add a manifest section to the parent section.
:param context: pulp context
:type context: pulp.client.extensions.core.ClientContext
:param parent_section: section of the CLI to which the manifest section
should be added
:type parent_section: pulp.client.extensions.extensions.PulpCliSection
"""
section = parent_section.create_subsection(SECTION_MANIFEST, DESC_MANIFEST)
section.add_command(ManifestSearchCommand(context))
section.add_command(ManifestCopyCommand(context))
section.add_command(ManifestRemoveCommand(context))


def add_sync_section(context, parent_section):
"""
add a sync section
Expand Down
95 changes: 95 additions & 0 deletions extensions_admin/test/unit/extensions/admin/test_manifest.py
@@ -0,0 +1,95 @@

from unittest import TestCase

from mock import patch, Mock

from pulp_docker.common.models import Manifest, Blob
from pulp_docker.extensions.admin.manifest import (
get_formatter_for_type, options, ManifestSearchCommand,
ManifestCopyCommand, ManifestRemoveCommand)


MODULE = 'pulp_docker.extensions.admin.manifest'


class TestGetFormatterForType(TestCase):

def test_call_with_manifest(self):
digest = '1234'
formatter = get_formatter_for_type(Manifest.TYPE_ID)
unit = dict(digest=digest)
self.assertEqual(formatter(unit), digest)

def test_call_with_blob(self):
digest = '1234'
formatter = get_formatter_for_type(Blob.TYPE_ID)
unit = dict(digest=digest)
self.assertEqual(formatter(unit), digest)

def test_call_invalid_type_id(self):
self.assertRaises(ValueError, get_formatter_for_type, '')


class TestManifestSearchCommand(TestCase):

def test_init(self):
context = Mock()
command = ManifestSearchCommand(context)
self.assertEqual(command.context, context)
self.assertEqual(command.name, 'search')
self.assertEqual(command.prompt, context.prompt)
self.assertFalse(command.description is None)
self.assertEqual(command.method, command.run)

def test_run(self):
repo_id = '1234'
context = Mock()
kwargs = {
options.OPTION_REPO_ID.keyword: repo_id
}
command = ManifestSearchCommand(context)

# test
command.run(**kwargs)

# validation
context.server.repo_unit.search.assert_called_once_with(
repo_id, type_ids=[Manifest.TYPE_ID])
context.prompt.render_document_list(
context.server.repo_unit.search.return_value.response_body)


class TestManifestCopyCommand(TestCase):

def test_init(self):
context = Mock(config={'output': {'poll_frequency_in_seconds': 10}})
command = ManifestCopyCommand(context)
self.assertEqual(command.name, 'copy')
self.assertFalse(command.description is None)
self.assertEqual(command.context, context)
self.assertEqual(command.method, command.run)

@patch(MODULE + '.get_formatter_for_type')
def test_get_formatter_for_type(self, get_formatter):
context = Mock(config={'output': {'poll_frequency_in_seconds': 10}})
command = ManifestCopyCommand(context)
formatter = command.get_formatter_for_type(Manifest.TYPE_ID)
self.assertEqual(formatter, get_formatter.return_value)


class TestManifestRemoveCommand(TestCase):

def test_init(self):
context = Mock(config={'output': {'poll_frequency_in_seconds': 10}})
command = ManifestRemoveCommand(context)
self.assertEqual(command.name, 'remove')
self.assertFalse(command.description is None)
self.assertEqual(command.context, context)
self.assertEqual(command.method, command.run)

@patch(MODULE + '.get_formatter_for_type')
def test_get_formatter_for_type(self, get_formatter):
context = Mock(config={'output': {'poll_frequency_in_seconds': 10}})
command = ManifestRemoveCommand(context)
formatter = command.get_formatter_for_type(Manifest.TYPE_ID)
self.assertEqual(formatter, get_formatter.return_value)

0 comments on commit 1b6f36f

Please sign in to comment.