Convert type of 'command' from string to list

Zun is currently using a string representation of 'command',
but Docker is returning 'Cmd' in string representation. This
requires a conversion of commands between list and string,
which is complicated and error-prone (it might causes bugs like
https://review.openstack.org/#/c/551795/).
It is better to switch from string to list

Co-Authored-By: Hongbin Lu <hongbin034@gmail.com>

Change-Id: Ie0df504a67cc20907d35e3513fd80d3f6be687fd
Closes-Bug: #1755019
This commit is contained in:
rajat29 2018-06-15 14:50:48 +05:30 committed by Hongbin Lu
parent e1e9ac23f4
commit b03fbe6dfd
17 changed files with 156 additions and 64 deletions

View File

@ -13,11 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import shlex
from neutronclient.common import exceptions as n_exc
from oslo_log import log as logging
from oslo_utils import strutils
from oslo_utils import uuidutils
import pecan
import six
from zun.api.controllers import base
from zun.api.controllers import link
@ -269,12 +272,34 @@ class ContainersController(base.Controller):
name = name_gen.generate()
return name + '-container'
@base.Controller.api_version("1.1", "1.19")
@pecan.expose('json')
@api_utils.enforce_content_types(['application/json'])
@exception.wrap_pecan_controller_exception
@validation.validate_query_param(pecan.request, schema.query_param_create)
@validation.validated(schema.legacy_container_create)
def post(self, run=False, **container_dict):
# NOTE(hongbin): We convert the representation of 'command' from
# string to list. For example:
# '"nginx" "-g" "daemon off;"' -> ["nginx", "-g", "daemon off;"]
command = container_dict.pop('command', None)
if command is not None:
if isinstance(command, six.string_types):
command = shlex.split(command)
container_dict['command'] = command
return self._do_post(run, **container_dict)
@base.Controller.api_version("1.20") # noqa
@pecan.expose('json')
@api_utils.enforce_content_types(['application/json'])
@exception.wrap_pecan_controller_exception
@validation.validate_query_param(pecan.request, schema.query_param_create)
@validation.validated(schema.container_create)
def post(self, run=False, **container_dict):
return self._do_post(run, **container_dict)
def _do_post(self, run=False, **container_dict):
"""Create or run a new container.
:param run: if true, starts the container

View File

@ -14,7 +14,7 @@ import copy
from zun.api.controllers.v1.schemas import parameter_types
_container_properties = {
_legacy_container_properties = {
'name': parameter_types.container_name,
'image': parameter_types.image_name,
'command': parameter_types.command,
@ -39,6 +39,16 @@ _container_properties = {
'auto_heal': parameter_types.boolean,
}
legacy_container_create = {
'type': 'object',
'properties': _legacy_container_properties,
'required': ['image'],
'additionalProperties': False
}
_container_properties = copy.deepcopy(_legacy_container_properties)
_container_properties['command'] = parameter_types.command_list
container_create = {
'type': 'object',
'properties': _container_properties,

View File

@ -81,6 +81,10 @@ command = {
'type': ['string', 'null']
}
command_list = {
'type': ['array', 'null']
}
auto_remove = {
'type': ['boolean', 'null']
}

View File

@ -52,10 +52,11 @@ REST_API_VERSION_HISTORY = """REST API Version History:
* 1.17 - Add support for detaching ports
* 1.18 - Modify the response of network list
* 1.19 - Intoduce container resize API
* 1.20 - Convert type of 'command' from string to list
"""
BASE_VER = '1.1'
CURRENT_MAX_VER = '1.19'
CURRENT_MAX_VER = '1.20'
class Version(object):

View File

@ -167,3 +167,8 @@ user documentation.
Introduce an API endpoint for resizing a container, such as changing the
CPU or memory of the container.
1.20
----
Convert type of 'command' from string to list

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
from oslo_utils import uuidutils
import pecan
@ -96,6 +97,7 @@ def enforce_content_types(valid_content_types):
def content_types_decorator(fn):
@functools.wraps(fn)
def content_types_enforcer(inst, *args, **kwargs):
_do_enforce_content_types(pecan.request, valid_content_types)
return fn(inst, *args, **kwargs)

View File

@ -639,18 +639,7 @@ class DockerDriver(driver.ContainerDriver):
def _populate_command(self, container, config):
command_list = config.get('Cmd')
command_str = None
if command_list:
# NOTE(hongbin): We convert the representation of the command
# from list to string. For example:
# * list: ["nginx", "-g", "daemon off;"]
# * string: '"nginx" "-g" "daemon off;"'
# In the string representation, we quote each command's token
# to avoid potential ambiguity (without quoting, the string
# representation will be 'nginx -g daemon off;' so we don't
# how the original command arguments were tokenized).
command_str = ' '.join('"%s"' % x for x in command_list)
container.command = command_str
container.command = command_list
def _populate_hostname_and_ports(self, container, config):
# populate hostname only when container.hostname wasn't set

View File

@ -0,0 +1,55 @@
# 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.
"""Convert type of 'command' from string to list
Revision ID: 3e80bbfd8da7
Revises: 26896d5f9053
Create Date: 2018-06-20 11:21:38.077673
"""
import json
import shlex
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '3e80bbfd8da7'
down_revision = '26896d5f9053'
branch_labels = None
depends_on = None
TABLE_MODEL = sa.Table(
'container', sa.MetaData(),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('command', sa.Text()))
def upgrade():
op.alter_column('container', 'command', type_=sa.Text())
# Convert 'command' from string to json-encoded list
session = sa.orm.Session(bind=op.get_bind())
with session.begin(subtransactions=True):
for row in session.query(TABLE_MODEL):
if row[1]:
command = shlex.split(row[1])
command = json.dumps(command)
session.execute(
TABLE_MODEL.update().values(
command=command).where(
TABLE_MODEL.c.id == row[0]))
session.commit()

View File

@ -142,7 +142,7 @@ class Container(Base):
name = Column(String(255))
image = Column(String(255))
cpu = Column(Float)
command = Column(String(255))
command = Column(JSONEncodedList)
memory = Column(String(255))
status = Column(String(20))
status_reason = Column(Text, nullable=True)

View File

@ -62,7 +62,7 @@ class Container(base.ZunPersistentObject, base.ZunObject):
# Version 1.30: Add capsule_id attribute
# Version 1.31: Add 'started_at' attribute
# Version 1.32: Add 'exec_instances' attribute
VERSION = '1.32'
VERSION = '1.33'
fields = {
'id': fields.IntegerField(),
@ -74,7 +74,7 @@ class Container(base.ZunPersistentObject, base.ZunObject):
'image': fields.StringField(nullable=True),
'cpu': fields.FloatField(nullable=True),
'memory': fields.StringField(nullable=True),
'command': fields.StringField(nullable=True),
'command': fields.ListOfStringsField(nullable=True),
'status': z_fields.ContainerStatusField(nullable=True),
'status_reason': fields.StringField(nullable=True),
'task_state': z_fields.TaskStateField(nullable=True),

View File

@ -26,7 +26,7 @@ from zun.tests.unit.db import base
PATH_PREFIX = '/v1'
CURRENT_VERSION = "container 1.19"
CURRENT_VERSION = "container 1.20"
class FunctionalTest(base.DbTestCase):

View File

@ -28,7 +28,7 @@ class TestRootController(api_base.FunctionalTest):
'default_version':
{'id': 'v1',
'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}],
'max_version': '1.19',
'max_version': '1.20',
'min_version': '1.1',
'status': 'CURRENT'},
'description': 'Zun is an OpenStack project which '
@ -37,7 +37,7 @@ class TestRootController(api_base.FunctionalTest):
'versions': [{'id': 'v1',
'links': [{'href': 'http://localhost/v1/',
'rel': 'self'}],
'max_version': '1.19',
'max_version': '1.20',
'min_version': '1.1',
'status': 'CURRENT'}]}

View File

@ -33,7 +33,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = lambda x, y, **z: y
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
response = self.post('/v1/containers?run=true',
@ -50,7 +50,7 @@ class TestContainerController(api_base.FunctionalTest):
def test_run_container_wrong_run_value(self, mock_search,
mock_container_create):
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
with self.assertRaisesRegex(AppError,
"Invalid input for query parameters"):
@ -60,7 +60,7 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.compute.api.API.container_create')
def test_run_container_wrong_memory_value(self, mock_container_create):
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "3",'
'"command": ["env"], "memory": "3",'
'"environment": {"key1": "val1", "key2": "val2"}}')
with self.assertRaisesRegex(AppError,
"Invalid input for query parameters"):
@ -70,7 +70,7 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.compute.api.API.container_create')
def test_run_container_wrong_cpu_value(self, mock_container_create):
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512", "cpu": "100"'
'"command": ["env"], "memory": "512", "cpu": "100"'
'"environment": {"key1": "val1", "key2": "val2"}}')
with self.assertRaisesRegex(AppError,
"Invalid input for query parameters"):
@ -80,7 +80,7 @@ class TestContainerController(api_base.FunctionalTest):
@patch('zun.compute.api.API.container_create')
def test_run_container_wrong_disk_value(self, mock_container_create):
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512", "disk": "0"'
'"command": ["env"], "memory": "512", "disk": "0"'
'"environment": {"key1": "val1", "key2": "val2"}}')
with self.assertRaisesRegex(AppError,
"Invalid input for query parameters"):
@ -101,7 +101,7 @@ class TestContainerController(api_base.FunctionalTest):
def test_run_container_runtime_wrong_value(self):
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"},'
'"runtime": 1234}')
with self.assertRaisesRegex(AppError,
@ -130,7 +130,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = lambda x, y, **z: y
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
response = self.post('/v1/containers?run=false',
params=params,
@ -146,7 +146,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create):
mock_container_create.side_effect = exception.InvalidValue
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
self.assertRaises(AppError, self.post, '/v1/containers?run=wrong',
params=params, content_type='application/json')
@ -159,7 +159,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_neutron_get_network):
mock_container_create.side_effect = lambda x, y, **z: y
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
response = self.post('/v1/containers/',
params=params,
@ -181,7 +181,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = lambda x, y, **z: y
mock_can.return_value = True
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
response = self.post('/v1/containers/',
params=params,
@ -197,7 +197,7 @@ class TestContainerController(api_base.FunctionalTest):
def test_create_container_image_not_specified(self, mock_container_create):
params = ('{"name": "MyDocker",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
with self.assertRaisesRegex(AppError,
"is a required property"):
@ -219,7 +219,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = _create_side_effect
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
self.post('/v1/containers/',
params=params,
@ -235,7 +235,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = lambda x, y, **z: y
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"}}')
response = self.post('/v1/containers/',
params=params,
@ -267,7 +267,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_search_volume.return_value = fake_volume
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"},'
'"runtime": "runc", "hostname": "testhost",'
'"disk": 20, "restart_policy": {"Name": "no"},'
@ -283,7 +283,7 @@ class TestContainerController(api_base.FunctionalTest):
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertEqual('MyDocker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual(["env"], c.get('command'))
self.assertEqual('512', c.get('memory'))
self.assertEqual({"key1": "val1", "key2": "val2"},
c.get('environment'))
@ -341,7 +341,7 @@ class TestContainerController(api_base.FunctionalTest):
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertIsNotNone(c.get('name'))
self.assertIsNone(c.get('command'))
self.assertIsNone(None, c.get('command'))
self.assertEqual('2048', c.get('memory'))
self.assertEqual(1.0, c.get('cpu'))
# TODO(kiennt): Uncomment it when bug [1] be resolved.
@ -370,7 +370,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_neutron_get_network.return_value = fake_network
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env",'
'"command": ["env"],'
'"availability_zone": "test-az"}')
response = self.post('/v1/containers/',
params=params,
@ -382,7 +382,7 @@ class TestContainerController(api_base.FunctionalTest):
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertEqual('MyDocker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual(["env"], c.get('command'))
self.assertEqual('2048', c.get('memory'))
self.assertEqual(1.0, c.get('cpu'))
# TODO(kiennt): Uncomment it when bug [1] be resolved.
@ -409,7 +409,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_neutron_get_network.return_value = fake_network
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"restart_policy": {"Name": "no",'
'"MaximumRetryCount": "0"}}')
response = self.post('/v1/containers/',
@ -422,7 +422,7 @@ class TestContainerController(api_base.FunctionalTest):
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertEqual('MyDocker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual(["env"], c.get('command'))
self.assertEqual('512', c.get('memory'))
self.assertEqual({"Name": "no", "MaximumRetryCount": "0"},
c.get('restart_policy'))
@ -446,7 +446,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_neutron_get_network.return_value = fake_network
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"restart_policy": {"Name": "no",'
'"MaximumRetryCount": "6"}}')
response = self.post('/v1/containers/',
@ -459,7 +459,7 @@ class TestContainerController(api_base.FunctionalTest):
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertEqual('MyDocker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual(["env"], c.get('command'))
self.assertEqual('512', c.get('memory'))
self.assertEqual({"Name": "no", "MaximumRetryCount": "0"},
c.get('restart_policy'))
@ -483,7 +483,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_neutron_get_network.return_value = fake_network
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"restart_policy": {"Name": "unless-stopped",'
'"MaximumRetryCount": "0"}}')
response = self.post('/v1/containers/',
@ -496,7 +496,7 @@ class TestContainerController(api_base.FunctionalTest):
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertEqual('MyDocker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual(["env"], c.get('command'))
self.assertEqual('512', c.get('memory'))
self.assertEqual({"Name": "unless-stopped", "MaximumRetryCount": "0"},
c.get('restart_policy'))
@ -528,7 +528,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_show_port.return_value = {'port': fake_port}
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"},'
'"nets": [{"port": "testport"}]}')
response = self.post('/v1/containers/',
@ -541,7 +541,7 @@ class TestContainerController(api_base.FunctionalTest):
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertEqual('MyDocker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual(["env"], c.get('command'))
self.assertEqual('512', c.get('memory'))
self.assertEqual({"key1": "val1", "key2": "val2"},
c.get('environment'))
@ -584,7 +584,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_get_network.return_value = fake_public_network
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"},'
'"nets": [{"network": "testpublicnet"}]}')
response = self.post('/v1/containers/',
@ -619,13 +619,13 @@ class TestContainerController(api_base.FunctionalTest):
mock_get_network.return_value = fake_network
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"},'
'"nets": [{"network": "fakenetid", "v4-fixed-ip": '
'"10.0.0.10"}]}')
response = self.app.post('/v1/containers/',
params=params,
content_type='application/json')
response = self.post('/v1/containers/',
params=params,
content_type='application/json')
fake_admin_authorize = True
mock_authorize.return_value = fake_admin_authorize
self.assertEqual(202, response.status_int)
@ -648,7 +648,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_create_volume.return_value = fake_volume
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"mounts": [{"destination": "d", '
'"size": "5"}]}')
response = self.post('/v1/containers/',
@ -665,7 +665,7 @@ class TestContainerController(api_base.FunctionalTest):
c = response.json['containers'][0]
self.assertIsNotNone(c.get('uuid'))
self.assertEqual('MyDocker', c.get('name'))
self.assertEqual('env', c.get('command'))
self.assertEqual(["env"], c.get('command'))
self.assertEqual('Creating', c.get('status'))
self.assertEqual('512', c.get('memory'))
self.assertIn('host', c)
@ -692,7 +692,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = lambda x, y, **z: y
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"restart_policy": {"Name": "always",'
'"MaximumRetryCount": "1"}}')
with self.assertRaisesRegex(
@ -709,7 +709,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create):
# Long name
params = ('{"name": "' + 'i' * 256 + '", "image": "ubuntu",'
'"command": "env", "memory": "512"}')
'"command": ["env"], "memory": "512"}')
self.assertRaises(AppError, self.post, '/v1/containers/',
params=params, content_type='application/json')
self.assertTrue(mock_container_create.not_called)
@ -1476,7 +1476,7 @@ class TestContainerController(api_base.FunctionalTest):
mock_container_create.side_effect = lambda x, y, **z: y
# Create a container with a command
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512",'
'"command": ["env"], "memory": "512",'
'"environment": {"key1": "val1", "key2": "val2"},'
'"image_driver": "glance"}')
response = self.post('/v1/containers/',
@ -2049,7 +2049,7 @@ class TestContainerEnforcement(api_base.FunctionalTest):
def test_policy_disallow_create(self):
params = ('{"name": "MyDocker", "image": "ubuntu",'
'"command": "env", "memory": "512"}')
'"command": ["env"], "memory": "512"}')
self._common_policy_check(
'container:create', self.post, '/v1/containers/',

View File

@ -21,7 +21,7 @@ CONTAINER_CREATE = {
'properties': {
'name': parameter_types.container_name,
'image': parameter_types.image_name,
'command': parameter_types.command,
'command': parameter_types.command_list,
'cpu': parameter_types.cpu,
'memory': parameter_types.memory,
'workdir': parameter_types.workdir,
@ -55,7 +55,8 @@ class TestSchemaValidations(base.BaseTestCase):
def test_create_schema_with_all_valid_parameters(self):
request_to_validate = {'name': 'test1', 'image': 'nginx',
'command': '/bin/sh', 'cpu': 1.0,
'command': ["/bin/sh"],
'cpu': 1.0,
'memory': '5', 'workdir': '/workdir',
'image_pull_policy': 'never',
'labels': {'abc': 12, 'bcd': 'xyz'},

View File

@ -138,7 +138,7 @@ class TestDockerDriver(base.DriverTestCase):
kwargs = {
'name': '%sea8e2a25-2901-438d-8157-de7ffd68d051' %
consts.NAME_PREFIX,
'command': 'fake_command',
'command': ['fake_command'],
'environment': {'key1': 'val1', 'key2': 'val2'},
'working_dir': '/home/ubuntu',
'labels': {'key1': 'val1', 'key2': 'val2'},
@ -204,7 +204,7 @@ class TestDockerDriver(base.DriverTestCase):
kwargs = {
'name': '%sea8e2a25-2901-438d-8157-de7ffd68d051' %
consts.NAME_PREFIX,
'command': 'fake_command',
'command': ['fake_command'],
'environment': {'key1': 'val1', 'key2': 'val2'},
'working_dir': '/home/ubuntu',
'labels': {'key1': 'val1', 'key2': 'val2'},
@ -389,7 +389,7 @@ class TestDockerDriver(base.DriverTestCase):
self.driver.show(self.context, mock_container)
self.mock_docker.inspect_container.assert_called_once_with(
mock_container.container_id)
self.assertEqual('"fake_command"', mock_container.command)
self.assertEqual(['fake_command'], mock_container.command)
def test_show_without_command(self):
self.mock_docker.inspect_container = mock.Mock(

View File

@ -54,7 +54,7 @@ def get_test_container(**kwargs):
'image': kwargs.get('image', 'ubuntu'),
'created_at': kwargs.get('created_at'),
'updated_at': kwargs.get('updated_at'),
'command': kwargs.get('command', 'fake_command'),
'command': kwargs.get('command', ['fake_command']),
'status': kwargs.get('status', 'Running'),
'status_reason': kwargs.get('status_reason', 'Created Successfully'),
'task_state': kwargs.get('task_state', None),

View File

@ -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.32-9e9a594ca58e978fb7b580292692a1a7',
'Container': '1.33-5eac0a995f25329ca566fdddde45c759',
'VolumeMapping': '1.1-50df6202f7846a136a91444c38eba841',
'Image': '1.1-330e6205c80b99b59717e1cfc6a79935',
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',