Add registry_id to container
Partial-Bug: #1702830 Change-Id: I9de5e9786a6cddce9e31b1de7b71ceaa553b1fd9
This commit is contained in:
parent
c88568d05b
commit
f608300050
@ -75,7 +75,7 @@ class ContainerCollection(collection.Collection):
|
||||
context = pecan.request.context
|
||||
collection = ContainerCollection()
|
||||
collection.containers = \
|
||||
[view.format_container(context, url, p.as_dict())
|
||||
[view.format_container(context, url, p)
|
||||
for p in rpc_containers]
|
||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||
return collection
|
||||
@ -267,7 +267,7 @@ class ContainersController(base.Controller):
|
||||
raise exception.ServerNotUsable
|
||||
|
||||
return view.format_container(context, pecan.request.host_url,
|
||||
container.as_dict())
|
||||
container)
|
||||
|
||||
def _generate_name_for_container(self):
|
||||
"""Generate a random name like: zeta-22-container."""
|
||||
@ -413,6 +413,12 @@ class ContainersController(base.Controller):
|
||||
exposed_ports = utils.build_exposed_ports(exposed_ports)
|
||||
container_dict['exposed_ports'] = exposed_ports
|
||||
|
||||
registry = container_dict.pop('registry', None)
|
||||
if registry:
|
||||
api_utils.version_check('registry', '1.31')
|
||||
registry = utils.get_registry(registry)
|
||||
container_dict['registry_id'] = registry.id
|
||||
|
||||
container_dict['status'] = consts.CREATING
|
||||
extra_spec = {}
|
||||
extra_spec['hints'] = container_dict.get('hints', None)
|
||||
@ -435,7 +441,7 @@ class ContainersController(base.Controller):
|
||||
new_container.uuid)
|
||||
pecan.response.status = 202
|
||||
return view.format_container(context, pecan.request.host_url,
|
||||
new_container.as_dict())
|
||||
new_container)
|
||||
|
||||
def _check_container_quotas(self, context, container_delta_dict,
|
||||
update_container=False):
|
||||
@ -706,7 +712,7 @@ class ContainersController(base.Controller):
|
||||
compute_api = pecan.request.compute_api
|
||||
container = compute_api.container_update(context, container, patch)
|
||||
return view.format_container(context, pecan.request.host_url,
|
||||
container.as_dict())
|
||||
container)
|
||||
|
||||
@base.Controller.api_version("1.1", "1.13")
|
||||
@pecan.expose('json')
|
||||
@ -727,7 +733,7 @@ class ContainersController(base.Controller):
|
||||
context = pecan.request.context
|
||||
container.save(context)
|
||||
return view.format_container(context, pecan.request.host_url,
|
||||
container.as_dict())
|
||||
container)
|
||||
|
||||
@base.Controller.api_version("1.19")
|
||||
@pecan.expose('json')
|
||||
@ -752,7 +758,7 @@ class ContainersController(base.Controller):
|
||||
compute_api.resize_container(context, container, kwargs)
|
||||
pecan.response.status = 202
|
||||
return view.format_container(context, pecan.request.host_url,
|
||||
container.as_dict())
|
||||
container)
|
||||
|
||||
@pecan.expose('json')
|
||||
@exception.wrap_pecan_controller_exception
|
||||
|
@ -39,14 +39,6 @@ RESOURCE_NAME = 'registry'
|
||||
COLLECTION_NAME = 'registries'
|
||||
|
||||
|
||||
def _get_registry(registry_ident):
|
||||
registry = api_utils.get_resource('Registry', registry_ident)
|
||||
if not registry:
|
||||
raise exception.RegistryNotFound(registry=registry_ident)
|
||||
|
||||
return registry
|
||||
|
||||
|
||||
def check_policy_on_registry(registry, action):
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, action, registry, action=action)
|
||||
@ -160,7 +152,7 @@ class RegistryController(base.Controller):
|
||||
context = pecan.request.context
|
||||
if context.is_admin:
|
||||
context.all_projects = True
|
||||
registry = _get_registry(registry_ident)
|
||||
registry = utils.get_registry(registry_ident)
|
||||
policy_action = policies.REGISTRY % 'get_one'
|
||||
check_policy_on_registry(registry.as_dict(), policy_action)
|
||||
return RegistryItem.render_response(registry)
|
||||
@ -181,7 +173,7 @@ class RegistryController(base.Controller):
|
||||
# Set the HTTP Location Header
|
||||
pecan.response.location = link.build_url(COLLECTION_NAME,
|
||||
new_registry.uuid)
|
||||
pecan.response.status = 202
|
||||
pecan.response.status = 201
|
||||
return RegistryItem.render_response(new_registry)
|
||||
|
||||
@pecan.expose('json')
|
||||
@ -193,7 +185,7 @@ class RegistryController(base.Controller):
|
||||
:param registry_ident: UUID or name of a registry.
|
||||
:param registry_dict: a json document to apply to this registry.
|
||||
"""
|
||||
registry = _get_registry(registry_ident)
|
||||
registry = utils.get_registry(registry_ident)
|
||||
context = pecan.request.context
|
||||
policy_action = policies.REGISTRY % 'update'
|
||||
check_policy_on_registry(registry.as_dict(), policy_action)
|
||||
@ -220,7 +212,7 @@ class RegistryController(base.Controller):
|
||||
context = pecan.request.context
|
||||
if context.is_admin:
|
||||
context.all_projects = True
|
||||
registry = _get_registry(registry_ident)
|
||||
registry = utils.get_registry(registry_ident)
|
||||
policy_action = policies.REGISTRY % 'delete'
|
||||
check_policy_on_registry(registry.as_dict(), policy_action)
|
||||
registry.destroy(context)
|
||||
|
@ -41,6 +41,7 @@ _legacy_container_properties = {
|
||||
'privileged': parameter_types.boolean,
|
||||
'healthcheck': parameter_types.healthcheck,
|
||||
'exposed_ports': parameter_types.exposed_ports,
|
||||
'registry': parameter_types.container_registry,
|
||||
}
|
||||
|
||||
legacy_container_create = {
|
||||
|
@ -572,3 +572,10 @@ registry_password = {
|
||||
'type': ['string'],
|
||||
'minLength': 1,
|
||||
}
|
||||
|
||||
container_registry = {
|
||||
'type': ['string'],
|
||||
'minLength': 2,
|
||||
'maxLength': 255,
|
||||
'pattern': '^[a-zA-Z0-9][a-zA-Z0-9_.-]+$'
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ def format_capsule(url, capsule, context):
|
||||
bookmark=True)])
|
||||
elif key == 'containers':
|
||||
containers = []
|
||||
for c in value:
|
||||
for c in capsule.containers:
|
||||
container = containers_view.format_container(
|
||||
context, None, c)
|
||||
containers.append(container)
|
||||
|
@ -49,6 +49,7 @@ _basic_keys = (
|
||||
'privileged',
|
||||
'healthcheck',
|
||||
'cpu_policy',
|
||||
'registry_id',
|
||||
)
|
||||
|
||||
|
||||
@ -69,8 +70,14 @@ def format_container(context, url, container):
|
||||
'bookmark', url,
|
||||
'containers', value,
|
||||
bookmark=True)])
|
||||
elif key == 'registry_id':
|
||||
if value:
|
||||
# the value is an internal id so replace it with the
|
||||
# user-facing uuid
|
||||
value = container.registry.uuid
|
||||
yield ('registry_id', value)
|
||||
else:
|
||||
yield (key, value)
|
||||
|
||||
return dict(itertools.chain.from_iterable(
|
||||
transform(k, v) for k, v in container.items()))
|
||||
transform(k, v) for k, v in container.as_dict().items()))
|
||||
|
@ -63,10 +63,11 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
* 1.28 - Add support cpuset
|
||||
* 1.29 - Add enable_cpu_pinning to compute_node
|
||||
* 1.30 - Introduce API resource for representing private registry
|
||||
* 1.31 - Add 'registry_id' to containers
|
||||
"""
|
||||
|
||||
BASE_VER = '1.1'
|
||||
CURRENT_MAX_VER = '1.30'
|
||||
CURRENT_MAX_VER = '1.31'
|
||||
|
||||
|
||||
class Version(object):
|
||||
|
@ -54,7 +54,7 @@ user documentation.
|
||||
1.5
|
||||
---
|
||||
|
||||
Add a new attribure 'runtime' to the request to create a container.
|
||||
Add a new attribute '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.
|
||||
@ -235,3 +235,9 @@ user documentation.
|
||||
----
|
||||
|
||||
Introduce API endpoint for create/read/update/delete private registry.
|
||||
|
||||
1.31
|
||||
----
|
||||
|
||||
Add 'registry_id' to container resource.
|
||||
This attribute indicate the registry from which the container pulls images.
|
||||
|
@ -488,6 +488,14 @@ def get_image(image_id):
|
||||
return image
|
||||
|
||||
|
||||
def get_registry(registry_id):
|
||||
registry = api_utils.get_resource('Registry', registry_id)
|
||||
if not registry:
|
||||
raise exception.RegistryNotFound(registry=registry_id)
|
||||
|
||||
return registry
|
||||
|
||||
|
||||
def check_for_restart_policy(container_dict):
|
||||
"""Check for restart policy input
|
||||
|
||||
|
@ -1179,6 +1179,18 @@ def create_registry(context, values):
|
||||
return _get_dbdriver_instance().create_registry(context, values)
|
||||
|
||||
|
||||
@profiler.trace("db")
|
||||
def get_registry_by_id(context, registry_id):
|
||||
"""Return a registry.
|
||||
|
||||
:param context: The security context
|
||||
:param registry_id: The id of a registry.
|
||||
:returns: A registry.
|
||||
"""
|
||||
return _get_dbdriver_instance().get_registry_by_id(
|
||||
context, registry_id)
|
||||
|
||||
|
||||
@profiler.trace("db")
|
||||
def get_registry_by_uuid(context, registry_uuid):
|
||||
"""Return a registry.
|
||||
|
@ -0,0 +1,36 @@
|
||||
# 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 registry_id to container
|
||||
|
||||
Revision ID: 1bc34e18180b
|
||||
Revises: 5ffc1cabe6b4
|
||||
Create Date: 2019-01-06 21:45:57.505152
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1bc34e18180b'
|
||||
down_revision = '5ffc1cabe6b4'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('container',
|
||||
sa.Column('registry_id', sa.Integer(),
|
||||
nullable=True))
|
||||
op.create_foreign_key(
|
||||
None, 'container', 'registry', ['registry_id'], ['id'])
|
@ -1426,6 +1426,17 @@ class Connection(object):
|
||||
ref.update(values)
|
||||
return ref
|
||||
|
||||
def get_registry_by_id(self, context, registry_id):
|
||||
query = model_query(models.Registry)
|
||||
query = self._add_project_filters(context, query)
|
||||
query = query.filter_by(id=registry_id)
|
||||
try:
|
||||
result = query.one()
|
||||
result['password'] = crypt.decrypt(result['password'])
|
||||
return result
|
||||
except NoResultFound:
|
||||
raise exception.RegistryNotFound(registry=registry_id)
|
||||
|
||||
def get_registry_by_uuid(self, context, registry_uuid):
|
||||
query = model_query(models.Registry)
|
||||
query = self._add_project_filters(context, query)
|
||||
@ -1457,6 +1468,11 @@ class Connection(object):
|
||||
with session.begin():
|
||||
query = model_query(models.Registry, session=session)
|
||||
query = add_identity_filter(query, registry_uuid)
|
||||
count = query.delete()
|
||||
if count != 1:
|
||||
raise exception.RegistryNotFound(registry=registry_uuid)
|
||||
try:
|
||||
count = query.delete()
|
||||
if count != 1:
|
||||
raise exception.RegistryNotFound(registry=registry_uuid)
|
||||
except db_exc.DBReferenceError:
|
||||
raise exception.Conflict('Failed to delete registry '
|
||||
'%(registry)s since it is in use.',
|
||||
registry=registry_uuid)
|
||||
|
@ -175,6 +175,9 @@ class Container(Base):
|
||||
privileged = Column(Boolean, default=False)
|
||||
healthcheck = Column(JSONEncodedDict)
|
||||
exposed_ports = Column(JSONEncodedDict)
|
||||
registry_id = Column(Integer,
|
||||
ForeignKey('registry.id'),
|
||||
nullable=True)
|
||||
|
||||
|
||||
class VolumeMapping(Base):
|
||||
|
@ -20,12 +20,13 @@ from zun.objects import base
|
||||
from zun.objects import exec_instance as exec_inst
|
||||
from zun.objects import fields as z_fields
|
||||
from zun.objects import pci_device
|
||||
from zun.objects import registry
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CONTAINER_OPTIONAL_ATTRS = ["pci_devices", "exec_instances"]
|
||||
CONTAINER_OPTIONAL_ATTRS = ["pci_devices", "exec_instances", "registry"]
|
||||
|
||||
|
||||
@base.ZunObjectRegistry.register
|
||||
@ -96,7 +97,8 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
||||
# Version 1.36: Add 'get_count' method
|
||||
# Version 1.37: Add 'exposed_ports' attribute
|
||||
# Version 1.38: Add 'cpuset' attribute
|
||||
VERSION = '1.37'
|
||||
# Version 1.39: Add 'register' and 'registry_id' attributes
|
||||
VERSION = '1.39'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
@ -143,13 +145,15 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
||||
nullable=True),
|
||||
'privileged': fields.BooleanField(nullable=True),
|
||||
'healthcheck': z_fields.JsonField(nullable=True),
|
||||
'registry_id': fields.IntegerField(nullable=True),
|
||||
'registry': fields.ObjectField("Registry", nullable=True),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(container, db_container):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in container.fields:
|
||||
if field in ['pci_devices', 'exec_instances']:
|
||||
if field in ['pci_devices', 'exec_instances', 'registry']:
|
||||
continue
|
||||
if field == 'cpuset':
|
||||
container.cpuset = Cpuset._from_dict(
|
||||
@ -349,6 +353,9 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
||||
if attrname == 'exec_instances':
|
||||
self._load_exec_instances()
|
||||
|
||||
if attrname == 'registry':
|
||||
self._load_registry()
|
||||
|
||||
self.obj_reset_changes([attrname])
|
||||
|
||||
def _load_pci_devices(self):
|
||||
@ -359,6 +366,12 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
||||
self.exec_instances = exec_inst.ExecInstance.list_by_container_id(
|
||||
self._context, self.id)
|
||||
|
||||
def _load_registry(self):
|
||||
self.registry = None
|
||||
if self.registry_id:
|
||||
self.registry = registry.Registry.get_by_id(
|
||||
self._context, self.registry_id)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_count(cls, context, project_id, flag):
|
||||
"""Get the counts of Container objects in the database.
|
||||
|
@ -51,6 +51,18 @@ class Registry(base.ZunPersistentObject, base.ZunObject):
|
||||
return [Registry._from_db_object(cls(context), obj)
|
||||
for obj in db_objects]
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, id):
|
||||
"""Find a registry based on id and return a :class:`Registry` object.
|
||||
|
||||
:param id: the id of a registry.
|
||||
:param context: Security context
|
||||
:returns: a :class:`Registry` object.
|
||||
"""
|
||||
db_registry = dbapi.get_registry_by_id(context, id)
|
||||
registry = Registry._from_db_object(cls(context), db_registry)
|
||||
return registry
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
"""Find a registry based on uuid and return a :class:`Registry` object.
|
||||
|
@ -26,7 +26,7 @@ from zun.tests.unit.db import base
|
||||
|
||||
|
||||
PATH_PREFIX = '/v1'
|
||||
CURRENT_VERSION = "container 1.30"
|
||||
CURRENT_VERSION = "container 1.31"
|
||||
|
||||
|
||||
class FunctionalTest(base.DbTestCase):
|
||||
|
@ -28,7 +28,7 @@ class TestRootController(api_base.FunctionalTest):
|
||||
'default_version':
|
||||
{'id': 'v1',
|
||||
'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}],
|
||||
'max_version': '1.30',
|
||||
'max_version': '1.31',
|
||||
'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.30',
|
||||
'max_version': '1.31',
|
||||
'min_version': '1.1',
|
||||
'status': 'CURRENT'}]}
|
||||
|
||||
|
@ -31,7 +31,7 @@ class TestRegistryController(api_base.FunctionalTest):
|
||||
params=params,
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertEqual(201, response.status_int)
|
||||
self.assertEqual(1, len(response.json))
|
||||
r = response.json['registry']
|
||||
self.assertIsNotNone(r.get('uuid'))
|
||||
@ -57,7 +57,7 @@ class TestRegistryController(api_base.FunctionalTest):
|
||||
params=params,
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertEqual(201, response.status_int)
|
||||
self.assertEqual(1, len(response.json))
|
||||
r = response.json['registry']
|
||||
self.assertIsNotNone(r.get('uuid'))
|
||||
@ -82,7 +82,7 @@ class TestRegistryController(api_base.FunctionalTest):
|
||||
response = self.post('/v1/registries/',
|
||||
params=params,
|
||||
content_type='application/json')
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertEqual(201, response.status_int)
|
||||
response = self.get('/v1/registries/')
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual(2, len(response.json))
|
||||
@ -175,7 +175,7 @@ class TestRegistryController(api_base.FunctionalTest):
|
||||
response = self.post('/v1/registries/',
|
||||
params=params,
|
||||
content_type='application/json')
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertEqual(201, response.status_int)
|
||||
# get by uuid
|
||||
registry_uuid = response.json['registry']['uuid']
|
||||
response = self.get('/v1/registries/%s/' % registry_uuid)
|
||||
@ -218,7 +218,7 @@ class TestRegistryController(api_base.FunctionalTest):
|
||||
response = self.post('/v1/registries/',
|
||||
params=params,
|
||||
content_type='application/json')
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertEqual(201, response.status_int)
|
||||
registry_uuid = response.json['registry']['uuid']
|
||||
params = {'registry': {'name': 'new-name', 'domain': 'new-domain',
|
||||
'username': 'new-username', 'password': 'new-pass'}}
|
||||
@ -242,7 +242,7 @@ class TestRegistryController(api_base.FunctionalTest):
|
||||
response = self.post('/v1/registries/',
|
||||
params=params,
|
||||
content_type='application/json')
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertEqual(201, response.status_int)
|
||||
registry_uuid = response.json['registry']['uuid']
|
||||
response = self.delete('/v1/registries/%s/' % registry_uuid)
|
||||
self.assertEqual(204, response.status_int)
|
||||
|
@ -120,6 +120,7 @@ def get_test_container(**kwargs):
|
||||
'exposed_ports': kwargs.get('exposed_ports', {"80/tcp": {}}),
|
||||
'cpu_policy': kwargs.get('cpu_policy', None),
|
||||
'cpuset': kwargs.get('cpuset', None),
|
||||
'registry_id': kwargs.get('registry_id', None),
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.37-193d8cd6635760882a27142760931af9',
|
||||
'Container': '1.39-6a7bc5bcd85277c30982c1106f10c336',
|
||||
'Cpuset': '1.0-06c4e6335683c18b87e2e54080f8c341',
|
||||
'Volume': '1.0-4ec18c39ea49f898cc354f9ca178dfb7',
|
||||
'VolumeMapping': '1.5-57febc66526185a75a744637e7a387c7',
|
||||
@ -368,7 +368,7 @@ object_data = {
|
||||
'ContainerActionEvent': '1.0-2974d0a6f5d4821fd4e223a88c10181a',
|
||||
'Network': '1.1-26e8d37a54e5fc905ede657744a221d9',
|
||||
'ExecInstance': '1.0-59464e7b96db847c0abb1e96d3cec30a',
|
||||
'Registry': '1.0-21ed56234497120755c60deba7c9e1dc',
|
||||
'Registry': '1.0-36c2053fbc30e0021630e657dd1699c9',
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user