Add runtime parameter to container API
This commit adds a new parameter `runtime` to container create and run APIs so that Zun can support passing multiple runtimes in future. Currently Zun only supports `runc` tool. Change-Id: I2795a4555032e52707e0344daa9dc97ad832650b Partial-Implements: blueprint support-secure-container
This commit is contained in:
parent
bd0d0de761
commit
6522a75c51
@ -47,6 +47,7 @@ Request
|
||||
- image_driver: image_driver
|
||||
- security_groups: security_groups
|
||||
- nets: nets
|
||||
- runtime: runtime
|
||||
|
||||
Request Example
|
||||
----------------
|
||||
|
@ -210,6 +210,12 @@ restart_policy:
|
||||
no, on-failure[:max-retry], always, unless-stopped.
|
||||
in: body
|
||||
type: string
|
||||
runtime:
|
||||
description: |
|
||||
The container runtime tool to create container with. Allowed values
|
||||
are runc.
|
||||
in: body
|
||||
type: string
|
||||
security_groups:
|
||||
description: |
|
||||
Security groups to be added to the container.
|
||||
|
@ -26,5 +26,6 @@
|
||||
"v6-fixed-ip": "",
|
||||
"port": "890699a9-4690-4bd6-8b70-3a9c1be77ecb"
|
||||
}
|
||||
]
|
||||
],
|
||||
"runtime": "runc"
|
||||
}
|
||||
|
@ -33,5 +33,6 @@
|
||||
"ports": null,
|
||||
"command": "/bin/sh -c 'echo hello'",
|
||||
"cpu": 2.0,
|
||||
"interactive": false
|
||||
"interactive": false,
|
||||
"runtime": "runc"
|
||||
}
|
||||
|
@ -235,6 +235,39 @@ class ContainersController(base.Controller):
|
||||
'"false", True, False, "True" and "False"')
|
||||
raise exception.InvalidValue(msg)
|
||||
|
||||
auto_remove = container_dict.pop('auto_remove', None)
|
||||
if auto_remove is not None:
|
||||
req_version = pecan.request.version
|
||||
min_version = versions.Version('', '', '', '1.3')
|
||||
if req_version >= min_version:
|
||||
try:
|
||||
container_dict['auto_remove'] = strutils.bool_from_string(
|
||||
auto_remove, strict=True)
|
||||
except ValueError:
|
||||
msg = _('Auto_remove value are true or false')
|
||||
raise exception.InvalidValue(msg)
|
||||
else:
|
||||
msg = _('Invalid param auto_remove because current request '
|
||||
'version is %(req_version)s. Auto_remove is only '
|
||||
'supported from version %(min_version)s') % \
|
||||
{'req_version': req_version,
|
||||
'min_version': min_version}
|
||||
raise exception.InvalidParam(msg)
|
||||
|
||||
runtime = container_dict.pop('runtime', None)
|
||||
if runtime is not None:
|
||||
req_version = pecan.request.version
|
||||
min_version = versions.Version('', '', '', '1.5')
|
||||
if req_version >= min_version:
|
||||
container_dict['runtime'] = runtime
|
||||
else:
|
||||
msg = _('Invalid param runtime because current request '
|
||||
'version is %(req_version)s. `runtime` is only '
|
||||
'supported from version %(min_version)s') % \
|
||||
{'req_version': req_version,
|
||||
'min_version': min_version}
|
||||
raise exception.InvalidParam(msg)
|
||||
|
||||
nets = container_dict.get('nets', [])
|
||||
requested_networks = self._build_requested_networks(context, nets)
|
||||
|
||||
@ -262,25 +295,6 @@ class ContainersController(base.Controller):
|
||||
if container_dict.get('restart_policy'):
|
||||
self._check_for_restart_policy(container_dict)
|
||||
|
||||
auto_remove = container_dict.pop('auto_remove', None)
|
||||
if auto_remove is not None:
|
||||
req_version = pecan.request.version
|
||||
min_version = versions.Version('', '', '', '1.3')
|
||||
if req_version >= min_version:
|
||||
try:
|
||||
container_dict['auto_remove'] = strutils.bool_from_string(
|
||||
auto_remove, strict=True)
|
||||
except ValueError:
|
||||
msg = _('Auto_remove value are true or false')
|
||||
raise exception.InvalidValue(msg)
|
||||
else:
|
||||
msg = _('Invalid param auto_remove because current request '
|
||||
'version is %(req_version)s. Auto_remove is only '
|
||||
'supported from version %(min_version)s') % \
|
||||
{'req_version': req_version,
|
||||
'min_version': min_version}
|
||||
raise exception.InvalidParam(msg)
|
||||
|
||||
container_dict['status'] = consts.CREATING
|
||||
extra_spec = container_dict.get('hints', None)
|
||||
new_container = objects.Container(context, **container_dict)
|
||||
|
@ -30,7 +30,8 @@ _container_properties = {
|
||||
'image_driver': parameter_types.image_driver,
|
||||
'security_groups': parameter_types.security_groups,
|
||||
'hints': parameter_types.hints,
|
||||
'nets': parameter_types.nets
|
||||
'nets': parameter_types.nets,
|
||||
'runtime': parameter_types.runtime
|
||||
}
|
||||
|
||||
container_create = {
|
||||
|
@ -40,6 +40,7 @@ _basic_keys = (
|
||||
'image_driver',
|
||||
'security_groups',
|
||||
'auto_remove',
|
||||
'runtime'
|
||||
)
|
||||
|
||||
|
||||
|
@ -37,10 +37,11 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
* 1.2 - Support user specify pre created networks
|
||||
* 1.3 - Add auto_remove to container
|
||||
* 1.4 - Support list all container host and show a container host
|
||||
* 1.5 - Add runtime to container
|
||||
"""
|
||||
|
||||
BASE_VER = '1.1'
|
||||
CURRENT_MAX_VER = '1.4'
|
||||
CURRENT_MAX_VER = '1.5'
|
||||
|
||||
|
||||
class Version(object):
|
||||
|
@ -50,3 +50,11 @@ user documentation.
|
||||
Users can use this api to list all the zun compute hosts.
|
||||
Add get host api
|
||||
Users can use this api to get details of a zun compute host.
|
||||
|
||||
1.5
|
||||
---
|
||||
|
||||
Add a new attribure 'runtime' to the request to create a container.
|
||||
Users can use this attribute to choose runtime for their containers.
|
||||
The specified runtime should be configured by admin to run with Zun.
|
||||
The default runtime for Zun is runc.
|
||||
|
@ -38,3 +38,9 @@ RESOURCE_CLASSES = (
|
||||
'NUMA_SOCKET', 'NUMA_CORE', 'NUMA_THREAD', 'NUMA_MEMORY_MB',
|
||||
'IPV4_ADDRESS'
|
||||
)
|
||||
|
||||
CONTAINER_RUNTIME = (
|
||||
RUNC,
|
||||
) = (
|
||||
'runc',
|
||||
)
|
||||
|
@ -122,6 +122,11 @@ environment = {
|
||||
},
|
||||
}
|
||||
|
||||
runtime = {
|
||||
'type': ['string', 'null'],
|
||||
'enum': ['runc', None]
|
||||
}
|
||||
|
||||
image_id = {
|
||||
'type': ['string', 'null'],
|
||||
'minLength': 2,
|
||||
|
@ -46,7 +46,10 @@ could join the namespaces of the infra container thus sharing resources inside
|
||||
the sandbox (i.e. the network interface). This is typically used to group
|
||||
a set of high-coupled containers into a unit. If set to False, infra container
|
||||
won't be created.
|
||||
""")
|
||||
"""),
|
||||
cfg.StrOpt('container_runtime', default='runc',
|
||||
help="""Define the runtime to create container with. Current
|
||||
supported values in Zun is ``runc``.""")
|
||||
]
|
||||
|
||||
|
||||
|
@ -123,7 +123,6 @@ class DockerDriver(driver.ContainerDriver):
|
||||
if network_standalone:
|
||||
self._provision_network(context, network_api,
|
||||
requested_networks)
|
||||
|
||||
kwargs = {
|
||||
'name': self.get_container_name(container),
|
||||
'command': container.command,
|
||||
@ -134,7 +133,11 @@ class DockerDriver(driver.ContainerDriver):
|
||||
'stdin_open': container.interactive,
|
||||
}
|
||||
|
||||
runtime = container.runtime if container.runtime\
|
||||
else CONF.container_runtime
|
||||
|
||||
host_config = {}
|
||||
host_config['runtime'] = runtime
|
||||
if sandbox_id:
|
||||
host_config['network_mode'] = 'container:%s' % sandbox_id
|
||||
# TODO(hongbin): Uncomment this after docker-py add support for
|
||||
|
@ -0,0 +1,33 @@
|
||||
# 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 runtime column
|
||||
|
||||
Revision ID: 945569b3669f
|
||||
Revises: a251f1f61217
|
||||
Create Date: 2017-08-04 09:10:47.810568
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '945569b3669f'
|
||||
down_revision = 'a251f1f61217'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('container',
|
||||
sa.Column('runtime', sa.String(32), nullable=True))
|
@ -155,6 +155,7 @@ class Container(Base):
|
||||
websocket_token = Column(String(255))
|
||||
security_groups = Column(JSONEncodedList)
|
||||
auto_remove = Column(Boolean, default=False)
|
||||
runtime = Column(String(20))
|
||||
|
||||
|
||||
class Image(Base):
|
||||
|
@ -38,7 +38,8 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
||||
# Version 1.16: Add websocket_url and token
|
||||
# Version 1.17: Add security_groups
|
||||
# Version 1.18: Add auto_remove
|
||||
VERSION = '1.18'
|
||||
# Version 1.19: Add runtime column
|
||||
VERSION = '1.19'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
@ -71,6 +72,7 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
||||
'websocket_url': fields.StringField(nullable=True),
|
||||
'websocket_token': fields.StringField(nullable=True),
|
||||
'security_groups': fields.ListOfStringsField(nullable=True),
|
||||
'runtime': z_fields.ContainerRuntimeField(nullable=True)
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
@ -76,3 +76,15 @@ class ResourceClass(fields.Enum):
|
||||
|
||||
class ResourceClassField(fields.AutoTypedField):
|
||||
AUTO_TYPE = ResourceClass()
|
||||
|
||||
|
||||
class ContainerRuntime(fields.Enum):
|
||||
ALL = consts.CONTAINER_RUNTIME
|
||||
|
||||
def __init__(self):
|
||||
super(ContainerRuntime, self).__init__(
|
||||
valid_values=ContainerRuntime.ALL)
|
||||
|
||||
|
||||
class ContainerRuntimeField(fields.BaseEnumField):
|
||||
AUTO_TYPE = ContainerRuntime()
|
||||
|
@ -25,7 +25,7 @@ class TestRootController(api_base.FunctionalTest):
|
||||
'default_version':
|
||||
{'id': 'v1',
|
||||
'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}],
|
||||
'max_version': '1.4',
|
||||
'max_version': '1.5',
|
||||
'min_version': '1.1',
|
||||
'status': 'CURRENT'},
|
||||
'description': 'Zun is an OpenStack project which '
|
||||
@ -33,7 +33,7 @@ class TestRootController(api_base.FunctionalTest):
|
||||
'versions': [{'id': 'v1',
|
||||
'links': [{'href': 'http://localhost/v1/',
|
||||
'rel': 'self'}],
|
||||
'max_version': '1.4',
|
||||
'max_version': '1.5',
|
||||
'min_version': '1.1',
|
||||
'status': 'CURRENT'}]}
|
||||
|
||||
|
@ -55,6 +55,50 @@ class TestContainerController(api_base.FunctionalTest):
|
||||
self.app.post('/v1/containers?run=xyz', params=params,
|
||||
content_type='application/json')
|
||||
|
||||
@patch('zun.network.neutron.NeutronAPI.get_available_network')
|
||||
@patch('zun.compute.api.API.container_run')
|
||||
@patch('zun.compute.api.API.image_search')
|
||||
def test_run_container_runtime(self, mock_search,
|
||||
mock_container_run,
|
||||
mock_neutron_get_network):
|
||||
params = ('{"name": "MyDocker", "image": "ubuntu",'
|
||||
'"command": "env", "memory": "512",'
|
||||
'"environment": {"key1": "val1", "key2": "val2"},'
|
||||
'"runtime": "runc"}')
|
||||
api_version = {"OpenStack-API-Version": "container 1.5"}
|
||||
response = self.app.post('/v1/containers?run=true',
|
||||
params=params,
|
||||
content_type='application/json',
|
||||
headers=api_version)
|
||||
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertTrue(mock_container_run.called)
|
||||
mock_neutron_get_network.assert_called_once()
|
||||
|
||||
def test_run_container_runtime_wrong_api_version(self):
|
||||
params = ('{"name": "MyDocker", "image": "ubuntu",'
|
||||
'"command": "env", "memory": "512",'
|
||||
'"environment": {"key1": "val1", "key2": "val2"},'
|
||||
'"runtime": "runc"}')
|
||||
api_version = {"OpenStack-API-Version": "container 1.4"}
|
||||
with self.assertRaisesRegex(AppError,
|
||||
"Invalid param runtime"):
|
||||
self.app.post('/v1/containers?run=true',
|
||||
params=params, content_type='application/json',
|
||||
headers=api_version)
|
||||
|
||||
def test_run_container_runtime_wrong_value(self):
|
||||
params = ('{"name": "MyDocker", "image": "ubuntu",'
|
||||
'"command": "env", "memory": "512",'
|
||||
'"environment": {"key1": "val1", "key2": "val2"},'
|
||||
'"runtime": "wrong_value"}')
|
||||
api_version = {"OpenStack-API-Version": "container 1.4"}
|
||||
with self.assertRaisesRegex(AppError,
|
||||
"Invalid input for field"):
|
||||
self.app.post('/v1/containers?run=true',
|
||||
params=params, content_type='application/json',
|
||||
headers=api_version)
|
||||
|
||||
@patch('zun.network.neutron.NeutronAPI.get_available_network')
|
||||
@patch('zun.compute.api.API.container_run')
|
||||
@patch('zun.compute.api.API.image_search')
|
||||
|
@ -30,7 +30,8 @@ CONTAINER_CREATE = {
|
||||
'environment': parameter_types.environment,
|
||||
'restart_policy': parameter_types.restart_policy,
|
||||
'image_driver': parameter_types.image_driver,
|
||||
'security_groups': parameter_types.security_groups
|
||||
'security_groups': parameter_types.security_groups,
|
||||
'runtime': parameter_types.runtime
|
||||
},
|
||||
'required': ['image'],
|
||||
'additionalProperties': False,
|
||||
@ -52,7 +53,8 @@ class TestSchemaValidations(base.BaseTestCase):
|
||||
'restart_policy': {'Name': 'no',
|
||||
'MaximumRetryCount': '0'},
|
||||
'image_driver': 'docker',
|
||||
'security_groups': ['abc']}
|
||||
'security_groups': ['abc'],
|
||||
'runtime': 'runc'}
|
||||
self.schema_validator.validate(request_to_validate)
|
||||
|
||||
def test_create_schema_with_all_parameters_none(self):
|
||||
@ -64,7 +66,8 @@ class TestSchemaValidations(base.BaseTestCase):
|
||||
'environment': None,
|
||||
'restart_policy': None,
|
||||
'image_driver': None,
|
||||
'security_groups': None
|
||||
'security_groups': None,
|
||||
'runtime': None
|
||||
}
|
||||
self.schema_validator.validate(request_to_validate)
|
||||
|
||||
@ -183,3 +186,11 @@ class TestSchemaValidations(base.BaseTestCase):
|
||||
"Invalid input for field"
|
||||
" 'pqr'"):
|
||||
self.schema_validator.validate(request_to_validate)
|
||||
|
||||
def test_create_schema_wrong_runtime(self):
|
||||
request_to_validate = {'image': 'nginx',
|
||||
'runtime': 'invalid'}
|
||||
with self.assertRaisesRegex(exception.SchemaValidationError,
|
||||
"Invalid input for field"
|
||||
" 'runtime'"):
|
||||
self.schema_validator.validate(request_to_validate)
|
||||
|
@ -99,6 +99,7 @@ class TestDockerDriver(base.DriverTestCase):
|
||||
host_config['cpu_quota'] = 100000
|
||||
host_config['cpu_period'] = 100000
|
||||
host_config['restart_policy'] = {'Name': 'no', 'MaximumRetryCount': 0}
|
||||
host_config['runtime'] = 'runc'
|
||||
self.mock_docker.create_host_config.assert_called_once_with(
|
||||
**host_config)
|
||||
|
||||
|
@ -68,6 +68,7 @@ def get_test_container(**kwargs):
|
||||
'websocket_token': '7878038e-957c-4d52-ae19-1e9561784e7b',
|
||||
'security_groups': kwargs.get('security_groups', ['default']),
|
||||
'auto_remove': kwargs.get('auto_remove', False),
|
||||
'runtime': kwargs.get('runtime', 'runc'),
|
||||
}
|
||||
|
||||
|
||||
|
@ -87,3 +87,22 @@ class TestResourceClass(test_fields.TestField):
|
||||
|
||||
def test_stringify_invalid(self):
|
||||
self.assertRaises(ValueError, self.field.stringify, 'bad_value')
|
||||
|
||||
|
||||
class TestContainerRuntime(test_fields.TestField):
|
||||
def setUp(self):
|
||||
super(TestContainerRuntime, self).setUp()
|
||||
self.field = fields.ContainerRuntime()
|
||||
self.coerce_good_values = [
|
||||
('runc', 'runc'),
|
||||
]
|
||||
self.coerce_bad_values = ['bad_value']
|
||||
self.to_primitive_values = self.coerce_good_values[0:1]
|
||||
self.from_primitive_values = self.coerce_good_values[0:1]
|
||||
|
||||
def test_stringify(self):
|
||||
self.assertEqual("'runc'",
|
||||
self.field.stringify('runc'))
|
||||
|
||||
def test_stringify_invalid(self):
|
||||
self.assertRaises(ValueError, self.field.stringify, 'bad_value')
|
||||
|
@ -344,7 +344,7 @@ class TestObject(test_base.TestCase, _TestObject):
|
||||
# For more information on object version testing, read
|
||||
# https://docs.openstack.org/zun/latest/
|
||||
object_data = {
|
||||
'Container': '1.18-e611f6b0fbc9f5f5c1164545b1630268',
|
||||
'Container': '1.19-5f8a734d3b0cc2a47f48205944672ab5',
|
||||
'Image': '1.0-0b976be24f4f6ee0d526e5c981ce0633',
|
||||
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
|
||||
'NUMANode': '1.0-cba878b70b2f8b52f1e031b41ac13b4e',
|
||||
|
Loading…
x
Reference in New Issue
Block a user