This patch add a command that supports unsetting image tags and properties Change-Id: I6f2cf45a61ff89da6664f3a34ae49fdd85d8c986 Closes-Bug:#1582968
		
			
				
	
	
		
			292 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#   Copyright 2013 Nebula Inc.
 | 
						|
#
 | 
						|
#   Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
						|
#   not use this file except in compliance with the License. You may obtain
 | 
						|
#   a copy of the License at
 | 
						|
#
 | 
						|
#        http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
#
 | 
						|
#   Unless required by applicable law or agreed to in writing, software
 | 
						|
#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
						|
#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
						|
#   License for the specific language governing permissions and limitations
 | 
						|
#   under the License.
 | 
						|
#
 | 
						|
 | 
						|
import copy
 | 
						|
import mock
 | 
						|
import random
 | 
						|
import uuid
 | 
						|
 | 
						|
from glanceclient.v2 import schemas
 | 
						|
import warlock
 | 
						|
 | 
						|
from openstackclient.common import utils as common_utils
 | 
						|
from openstackclient.tests import fakes
 | 
						|
from openstackclient.tests import utils
 | 
						|
 | 
						|
from openstackclient.tests.identity.v3 import fakes as identity_fakes
 | 
						|
 | 
						|
image_id = '0f41529e-7c12-4de8-be2d-181abb825b3c'
 | 
						|
image_name = 'graven'
 | 
						|
image_owner = 'baal'
 | 
						|
image_protected = False
 | 
						|
image_visibility = 'public'
 | 
						|
image_tags = []
 | 
						|
 | 
						|
IMAGE = {
 | 
						|
    'id': image_id,
 | 
						|
    'name': image_name,
 | 
						|
    'owner': image_owner,
 | 
						|
    'protected': image_protected,
 | 
						|
    'visibility': image_visibility,
 | 
						|
    'tags': image_tags
 | 
						|
}
 | 
						|
 | 
						|
IMAGE_columns = tuple(sorted(IMAGE))
 | 
						|
IMAGE_data = tuple((IMAGE[x] for x in sorted(IMAGE)))
 | 
						|
 | 
						|
IMAGE_SHOW = copy.copy(IMAGE)
 | 
						|
IMAGE_SHOW['tags'] = ''
 | 
						|
IMAGE_SHOW_data = tuple((IMAGE_SHOW[x] for x in sorted(IMAGE_SHOW)))
 | 
						|
 | 
						|
member_status = 'pending'
 | 
						|
MEMBER = {
 | 
						|
    'member_id': identity_fakes.project_id,
 | 
						|
    'image_id': image_id,
 | 
						|
    'status': member_status,
 | 
						|
}
 | 
						|
 | 
						|
# Just enough v2 schema to do some testing
 | 
						|
IMAGE_schema = {
 | 
						|
    "additionalProperties": {
 | 
						|
        "type": "string"
 | 
						|
    },
 | 
						|
    "name": "image",
 | 
						|
    "links": [
 | 
						|
        {
 | 
						|
            "href": "{self}",
 | 
						|
            "rel": "self"
 | 
						|
        },
 | 
						|
        {
 | 
						|
            "href": "{file}",
 | 
						|
            "rel": "enclosure"
 | 
						|
        },
 | 
						|
        {
 | 
						|
            "href": "{schema}",
 | 
						|
            "rel": "describedby"
 | 
						|
        }
 | 
						|
    ],
 | 
						|
    "properties": {
 | 
						|
        "id": {
 | 
						|
            "pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$",  # noqa
 | 
						|
            "type": "string",
 | 
						|
            "description": "An identifier for the image"
 | 
						|
        },
 | 
						|
        "name": {
 | 
						|
            "type": [
 | 
						|
                "null",
 | 
						|
                "string"
 | 
						|
            ],
 | 
						|
            "description": "Descriptive name for the image",
 | 
						|
            "maxLength": 255
 | 
						|
        },
 | 
						|
        "owner": {
 | 
						|
            "type": [
 | 
						|
                "null",
 | 
						|
                "string"
 | 
						|
            ],
 | 
						|
            "description": "Owner of the image",
 | 
						|
            "maxLength": 255
 | 
						|
        },
 | 
						|
        "protected": {
 | 
						|
            "type": "boolean",
 | 
						|
            "description": "If true, image will not be deletable."
 | 
						|
        },
 | 
						|
        "self": {
 | 
						|
            "type": "string",
 | 
						|
            "description": "(READ-ONLY)"
 | 
						|
        },
 | 
						|
        "schema": {
 | 
						|
            "type": "string",
 | 
						|
            "description": "(READ-ONLY)"
 | 
						|
        },
 | 
						|
        "size": {
 | 
						|
            "type": [
 | 
						|
                "null",
 | 
						|
                "integer"
 | 
						|
            ],
 | 
						|
            "description": "Size of image file in bytes (READ-ONLY)"
 | 
						|
        },
 | 
						|
        "status": {
 | 
						|
            "enum": [
 | 
						|
                "queued",
 | 
						|
                "saving",
 | 
						|
                "active",
 | 
						|
                "killed",
 | 
						|
                "deleted",
 | 
						|
                "pending_delete"
 | 
						|
            ],
 | 
						|
            "type": "string",
 | 
						|
            "description": "Status of the image (READ-ONLY)"
 | 
						|
        },
 | 
						|
        "tags": {
 | 
						|
            "items": {
 | 
						|
                "type": "string",
 | 
						|
                "maxLength": 255
 | 
						|
            },
 | 
						|
            "type": "array",
 | 
						|
            "description": "List of strings related to the image"
 | 
						|
        },
 | 
						|
        "visibility": {
 | 
						|
            "enum": [
 | 
						|
                "public",
 | 
						|
                "private"
 | 
						|
            ],
 | 
						|
            "type": "string",
 | 
						|
            "description": "Scope of image accessibility"
 | 
						|
        },
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
class FakeImagev2Client(object):
 | 
						|
 | 
						|
    def __init__(self, **kwargs):
 | 
						|
        self.images = mock.Mock()
 | 
						|
        self.images.resource_class = fakes.FakeResource(None, {})
 | 
						|
        self.image_members = mock.Mock()
 | 
						|
        self.image_members.resource_class = fakes.FakeResource(None, {})
 | 
						|
        self.image_tags = mock.Mock()
 | 
						|
        self.image_tags.resource_class = fakes.FakeResource(None, {})
 | 
						|
        self.auth_token = kwargs['token']
 | 
						|
        self.management_url = kwargs['endpoint']
 | 
						|
 | 
						|
 | 
						|
class TestImagev2(utils.TestCommand):
 | 
						|
 | 
						|
    def setUp(self):
 | 
						|
        super(TestImagev2, self).setUp()
 | 
						|
 | 
						|
        self.app.client_manager.image = FakeImagev2Client(
 | 
						|
            endpoint=fakes.AUTH_URL,
 | 
						|
            token=fakes.AUTH_TOKEN,
 | 
						|
        )
 | 
						|
 | 
						|
        self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client(
 | 
						|
            endpoint=fakes.AUTH_URL,
 | 
						|
            token=fakes.AUTH_TOKEN,
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class FakeImage(object):
 | 
						|
    """Fake one or more images.
 | 
						|
 | 
						|
    TODO(xiexs): Currently, only image API v2 is supported by this class.
 | 
						|
    """
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def create_one_image(attrs=None):
 | 
						|
        """Create a fake image.
 | 
						|
 | 
						|
        :param Dictionary attrs:
 | 
						|
            A dictionary with all attrbutes of image
 | 
						|
        :retrun:
 | 
						|
            A FakeResource object with id, name, owner, protected,
 | 
						|
            visibility and tags attrs
 | 
						|
        """
 | 
						|
        attrs = attrs or {}
 | 
						|
 | 
						|
        # Set default attribute
 | 
						|
        image_info = {
 | 
						|
            'id': str(uuid.uuid4()),
 | 
						|
            'name': 'image-name' + uuid.uuid4().hex,
 | 
						|
            'owner': 'image-owner' + uuid.uuid4().hex,
 | 
						|
            'protected': bool(random.choice([0, 1])),
 | 
						|
            'visibility': random.choice(['public', 'private']),
 | 
						|
            'tags': [uuid.uuid4().hex for r in range(2)],
 | 
						|
        }
 | 
						|
 | 
						|
        # Overwrite default attributes if there are some attributes set
 | 
						|
        image_info.update(attrs)
 | 
						|
 | 
						|
        # Set up the schema
 | 
						|
        model = warlock.model_factory(
 | 
						|
            IMAGE_schema,
 | 
						|
            schemas.SchemaBasedModel,
 | 
						|
        )
 | 
						|
 | 
						|
        return model(**image_info)
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def create_images(attrs=None, count=2):
 | 
						|
        """Create multiple fake images.
 | 
						|
 | 
						|
        :param Dictionary attrs:
 | 
						|
            A dictionary with all attributes of image
 | 
						|
        :param Integer count:
 | 
						|
            The number of images to be faked
 | 
						|
        :return:
 | 
						|
            A list of FakeResource objects
 | 
						|
        """
 | 
						|
        images = []
 | 
						|
        for n in range(0, count):
 | 
						|
            images.append(FakeImage.create_one_image(attrs))
 | 
						|
 | 
						|
        return images
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def get_images(images=None, count=2):
 | 
						|
        """Get an iterable MagicMock object with a list of faked images.
 | 
						|
 | 
						|
        If images list is provided, then initialize the Mock object with the
 | 
						|
        list. Otherwise create one.
 | 
						|
 | 
						|
        :param List images:
 | 
						|
            A list of FakeResource objects faking images
 | 
						|
        :param Integer count:
 | 
						|
            The number of images to be faked
 | 
						|
        :return
 | 
						|
            An iterable Mock object with side_effect set to a list of faked
 | 
						|
            images
 | 
						|
        """
 | 
						|
        if images is None:
 | 
						|
            images = FakeImage.create_images(count)
 | 
						|
 | 
						|
        return mock.MagicMock(side_effect=images)
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def get_image_columns(image=None):
 | 
						|
        """Get the image columns from a faked image object.
 | 
						|
 | 
						|
        :param image:
 | 
						|
            A FakeResource objects faking image
 | 
						|
        :return
 | 
						|
            A tuple which may include the following keys:
 | 
						|
            ('id', 'name', 'owner', 'protected', 'visibility', 'tags')
 | 
						|
        """
 | 
						|
        if image is not None:
 | 
						|
            return tuple(sorted(image))
 | 
						|
        return IMAGE_columns
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def get_image_data(image=None):
 | 
						|
        """Get the image data from a faked image object.
 | 
						|
 | 
						|
        :param image:
 | 
						|
            A FakeResource objects faking image
 | 
						|
        :return
 | 
						|
            A tuple which may include the following values:
 | 
						|
            ('image-123', 'image-foo', 'admin', False, 'public', 'bar, baz')
 | 
						|
        """
 | 
						|
        data_list = []
 | 
						|
        if image is not None:
 | 
						|
            for x in sorted(image.keys()):
 | 
						|
                if x == 'tags':
 | 
						|
                    # The 'tags' should be format_list
 | 
						|
                    data_list.append(
 | 
						|
                        common_utils.format_list(getattr(image, x)))
 | 
						|
                else:
 | 
						|
                    data_list.append(getattr(image, x))
 | 
						|
        return tuple(data_list)
 |