Merge "Allow container memory size to be specified"

This commit is contained in:
Jenkins 2015-10-04 14:53:12 +00:00 committed by Gerrit Code Review
commit 6e5256b809
11 changed files with 128 additions and 30 deletions

View File

@ -92,6 +92,9 @@ class Container(base.APIBase):
status = wtypes.text
"""The status of container"""
memory = wtypes.text
"""Memory limit for the container. Example: 512m"""
def __init__(self, **kwargs):
self.fields = []
for field in objects.Container.fields:
@ -105,7 +108,8 @@ class Container(base.APIBase):
def _convert_with_links(container, url, expand=True):
if not expand:
container.unset_fields_except(['uuid', 'name', 'bay_uuid',
'image', 'command', 'status'])
'image', 'command', 'status',
'memory'])
container.links = [link.Link.make_link(
'self', url,
@ -129,6 +133,7 @@ class Container(base.APIBase):
image='ubuntu',
command='env',
status='Running',
memory='512m',
bay_uuid="fff114da-3bfa-4a0f-a123-c0dffad9718e",
created_at=datetime.datetime.utcnow(),
updated_at=datetime.datetime.utcnow())

View File

@ -145,7 +145,8 @@ class Handler(object):
docker.inspect_image(self._encode_utf8(container.image))
docker.create_container(image, name=name,
hostname=container_uuid,
command=container.command)
command=container.command,
mem_limit=container.memory)
container.status = fields.ContainerStatus.STOPPED
return container
except errors.APIError:

View File

@ -0,0 +1,34 @@
# Copyright 2015 Huawei Technologies Co.,LTD.
#
# 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.
"""Add memory to container
Revision ID: 33ef79969018
Revises: 2ae93c9c6191
Create Date: 2015-10-03 17:03:47.194253
"""
# revision identifiers, used by Alembic.
revision = '33ef79969018'
down_revision = '2ae93c9c6191'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('container',
sa.Column('memory', sa.String(length=255),
nullable=True))

View File

@ -402,14 +402,9 @@ class Connection(api.Connection):
if filters is None:
filters = []
if 'name' in filters:
query = query.filter_by(name=filters['name'])
if 'image' in filters:
query = query.filter_by(image=filters['image'])
if 'project_id' in filters:
query = query.filter_by(project_id=filters['project_id'])
if 'user_id' in filters:
query = query.filter_by(user_id=filters['user_id'])
for key in ['name', 'image', 'project_id', 'user_id', 'memory']:
if key in filters:
query = query.filter_by(**{key: filters[key]})
return query

View File

@ -194,6 +194,7 @@ class Container(Base):
command = Column(String(255))
bay_uuid = Column(String(36))
status = Column(String(20))
memory = Column(String(255))
class Node(Base):

View File

@ -22,7 +22,8 @@ from magnum.objects import fields as m_fields
class Container(base.MagnumPersistentObject, base.MagnumObject,
base.MagnumObjectDictCompat):
# Version 1.0: Initial version
VERSION = '1.0'
# Version 1.1: Add memory field
VERSION = '1.1'
dbapi = dbapi.get_instance()
@ -36,6 +37,7 @@ class Container(base.MagnumPersistentObject, base.MagnumObject,
'command': fields.StringField(nullable=True),
'bay_uuid': fields.StringField(nullable=True),
'status': m_fields.ContainerStatusField(nullable=True),
'memory': fields.StringField(nullable=True),
}
@staticmethod

View File

@ -44,7 +44,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = lambda x: x
params = ('{"name": "My Docker", "image": "ubuntu",'
'"command": "env",'
'"command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
response = self.app.post('/v1/containers',
params=params,
@ -63,7 +63,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = _create_side_effect
params = ('{"name": "My Docker", "image": "ubuntu",'
'"command": "env",'
'"command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
self.app.post('/v1/containers',
params=params,
@ -79,7 +79,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = lambda x: x
# Create a container with a command
params = ('{"name": "My Docker", "image": "ubuntu",'
'"command": "env",'
'"command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
response = self.app.post('/v1/containers',
params=params,
@ -97,6 +97,7 @@ class TestContainerController(api_base.FunctionalTest):
self.assertEqual('My Docker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual('Stopped', c.get('status'))
self.assertEqual('512m', c.get('memory'))
# Delete the container we created
response = self.app.delete('/v1/containers/%s' % c.get('uuid'))
self.assertEqual(response.status_int, 204)
@ -116,6 +117,43 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_show):
mock_container_create.side_effect = lambda x: x
# Create a container with a command
params = ('{"name": "My Docker", "image": "ubuntu",'
'"command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
response = self.app.post('/v1/containers',
params=params,
content_type='application/json')
self.assertEqual(response.status_int, 201)
# get all containers
container = objects.Container.list(self.context)[0]
container.status = 'Stopped'
mock_container_show.return_value = container
response = self.app.get('/v1/containers')
self.assertEqual(response.status_int, 200)
self.assertEqual(1, len(response.json))
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertEqual('My Docker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual('Stopped', c.get('status'))
self.assertEqual('512m', c.get('memory'))
# Delete the container we created
response = self.app.delete('/v1/containers/%s' % c.get('uuid'))
self.assertEqual(response.status_int, 204)
response = self.app.get('/v1/containers')
self.assertEqual(response.status_int, 200)
c = response.json['containers']
self.assertEqual(0, len(c))
self.assertTrue(mock_container_create.called)
@patch('magnum.conductor.api.API.container_show')
@patch('magnum.conductor.api.API.container_create')
def test_create_container_without_memory(self,
mock_container_create,
mock_container_show):
mock_container_create.side_effect = lambda x: x
# Create a container with a command
params = ('{"name": "My Docker", "image": "ubuntu",'
'"command": "env",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
@ -135,20 +173,12 @@ class TestContainerController(api_base.FunctionalTest):
self.assertEqual('My Docker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual('Stopped', c.get('status'))
# Delete the container we created
response = self.app.delete('/v1/containers/%s' % c.get('uuid'))
self.assertEqual(response.status_int, 204)
response = self.app.get('/v1/containers')
self.assertEqual(response.status_int, 200)
c = response.json['containers']
self.assertEqual(0, len(c))
self.assertTrue(mock_container_create.called)
self.assertIsNone(c.get('memory'))
@patch('magnum.conductor.api.API.container_create')
def test_create_container_without_name(self, mock_container_create):
# No name param
params = ('{"image": "ubuntu", "command": "env",'
params = ('{"image": "ubuntu", "command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
self.assertRaises(AppError, self.app.post, '/v1/containers',
params=params, content_type='application/json')
@ -158,7 +188,7 @@ class TestContainerController(api_base.FunctionalTest):
def test_create_container_invalid_long_name(self, mock_container_create):
# Long name
params = ('{"name": "' + 'i' * 256 + '", "image": "ubuntu",'
'"command": "env",'
'"command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
self.assertRaises(AppError, self.app.post, '/v1/containers',
params=params, content_type='application/json')
@ -232,6 +262,7 @@ class TestContainerController(api_base.FunctionalTest):
self.assertIn('status', actual_containers[0])
self.assertIn('image', actual_containers[0])
self.assertIn('command', actual_containers[0])
self.assertIn('memory', actual_containers[0])
@patch('magnum.conductor.api.API.container_show')
@patch('magnum.objects.Container.list')
@ -570,7 +601,7 @@ class TestContainerEnforcement(api_base.FunctionalTest):
def test_policy_disallow_create(self):
params = ('{"name": "' + 'i' * 256 + '", "image": "ubuntu",'
'"command": "env",'
'"command": "env", "memory": "512m",'
'"bay_uuid": "fff114da-3bfa-4a0f-a123-c0dffad9718e"}')
self._common_policy_check(

View File

@ -165,6 +165,7 @@ class TestDockerHandler(base.BaseTestCase):
mock_container.uuid = 'some-uuid'
mock_container.image = 'test_image:some_tag'
mock_container.command = None
mock_container.memory = None
container = self.conductor.container_create(
None, mock_container)
@ -177,7 +178,8 @@ class TestDockerHandler(base.BaseTestCase):
mock_container.image,
name='some-name',
hostname='some-uuid',
command=None)
command=None,
mem_limit=None)
self.assertEqual(fields.ContainerStatus.STOPPED, container.status)
def test_container_create_with_command(self):
@ -186,6 +188,7 @@ class TestDockerHandler(base.BaseTestCase):
mock_container.uuid = 'some-uuid'
mock_container.image = 'test_image:some_tag'
mock_container.command = 'env'
mock_container.memory = None
container = self.conductor.container_create(
None, mock_container)
@ -198,7 +201,31 @@ class TestDockerHandler(base.BaseTestCase):
mock_container.image,
name='some-name',
hostname='some-uuid',
command='env')
command='env',
mem_limit=None)
self.assertEqual(fields.ContainerStatus.STOPPED, container.status)
def test_container_create_with_memory(self):
mock_container = mock.MagicMock()
mock_container.name = 'some-name'
mock_container.uuid = 'some-uuid'
mock_container.image = 'test_image:some_tag'
mock_container.command = None
mock_container.memory = '512m'
container = self.conductor.container_create(
None, mock_container)
utf8_image = self.conductor._encode_utf8(mock_container.image)
self.mock_docker.pull.assert_called_once_with('test_image',
tag='some_tag')
self.mock_docker.inspect_image.assert_called_once_with(utf8_image)
self.mock_docker.create_container.assert_called_once_with(
mock_container.image,
name='some-name',
hostname='some-uuid',
command=None,
mem_limit='512m')
self.assertEqual(fields.ContainerStatus.STOPPED, container.status)
def test_encode_utf8_unicode(self):

View File

@ -220,6 +220,7 @@ def get_test_container(**kw):
'command': kw.get('command', 'fake_command'),
'bay_uuid': kw.get('bay_uuid', 'fff114da-3bfa-4a0f-a123-c0dffad9718e'),
'status': kw.get('state', 'Running'),
'memory': kw.get('memory', '512m'),
}

View File

@ -98,11 +98,12 @@ class TestContainerObject(base.DbTestCase):
autospec=True) as mock_update_container:
container = objects.Container.get_by_uuid(self.context, uuid)
container.image = 'container.img'
container.memory = '512m'
container.save()
mock_get_container.assert_called_once_with(self.context, uuid)
mock_update_container.assert_called_once_with(
uuid, {'image': 'container.img'})
uuid, {'image': 'container.img', 'memory': '512m'})
self.assertEqual(self.context, container._context)
def test_refresh(self):

View File

@ -428,7 +428,7 @@ object_data = {
'BayLock': '1.0-7d1eb08cf2070523bd210369c7a2e076',
'BayModel': '1.7-3ceb83f91310437d21f465ce522fc4e7',
'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2',
'Container': '1.0-e12affbba5f8a748882a3ae98aced282',
'Container': '1.1-968c62bc65ee08027a2cdbba95f5819c',
'MyObj': '1.0-b43567e512438205e32f4e95ca616697',
'Node': '1.0-30943e6e3387a2fae7490b57c4239a17',
'Pod': '1.1-7a31c372f163742845c10a008f47cc15',