Adds labels support to baymodels

Adds labels to the following:
  1. api baymodel attribute
  2. conductor template definitions and entrypoints
  3. a new column to the baymodel db
  4. objects. Note: Updates baymodel object version
  5. Tests

Partially-Implements: blueprint extend-api-network-attributes
Partially-Implements: blueprint extend-baymodel-net-attributes
Partially-Implements: blueprint conductor-template-net-update

Change-Id: I49f9c7df28f806cdedb3a382b1cb41bc48ff4e90
This commit is contained in:
Daneyon Hansen 2015-08-27 20:28:05 +00:00
parent c0ad7c376e
commit e01881726b
11 changed files with 115 additions and 11 deletions

View File

@ -118,6 +118,9 @@ class BayModel(base.APIBase):
registry_enabled = wsme.wsattr(types.boolean, default=False)
"""Indicates whether the docker registry is enabled"""
labels = wtypes.DictType(str, str)
"""One or more key/value pairs"""
def __init__(self, **kwargs):
self.fields = []
for field in objects.BayModel.fields:
@ -167,6 +170,7 @@ class BayModel(base.APIBase):
http_proxy='http://proxy.com:123',
https_proxy='https://proxy.com:123',
no_proxy='192.168.0.1,192.168.0.2,192.168.0.3',
labels={'key1': 'val1', 'key2': 'val2'},
created_at=datetime.datetime.utcnow(),
updated_at=datetime.datetime.utcnow())
return cls._convert_with_links(sample, 'http://localhost:9511', expand)

View File

@ -407,6 +407,8 @@ class AtomicK8sTemplateDefinition(BaseTemplateDefinition):
def get_params(self, context, baymodel, bay, **kwargs):
extra_params = kwargs.pop('extra_params', {})
label_list = ['flannel_network_cidr', 'flannel_use_vxlan',
'flannel_network_subnetlen']
scale_mgr = kwargs.pop('scale_manager', None)
if scale_mgr:
hosts = self.get_output('kube_minions')
@ -415,6 +417,9 @@ class AtomicK8sTemplateDefinition(BaseTemplateDefinition):
extra_params['discovery_url'] = self.get_discovery_url(bay)
for label in label_list:
extra_params[label] = baymodel.labels.get(label)
return super(AtomicK8sTemplateDefinition,
self).get_params(context, baymodel, bay,
extra_params=extra_params,

View File

@ -0,0 +1,30 @@
# 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 labels column to baymodel table
Revision ID: 1481f5b560dd
Revises: 3be65537a94a
Create Date: 2015-09-02 22:34:07.590142
"""
# revision identifiers, used by Alembic.
revision = '1481f5b560dd'
down_revision = '3be65537a94a'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('baymodel', sa.Column('labels',
sa.Text(), nullable=True))

View File

@ -316,6 +316,8 @@ class Connection(api.Connection):
query = query.filter_by(project_id=filters['project_id'])
if 'user_id' in filters:
query = query.filter_by(user_id=filters['user_id'])
if 'labels' in filters:
query = query.filter_by(labels=filters['labels'])
return query

View File

@ -171,6 +171,7 @@ class BayModel(Base):
https_proxy = Column(String(255))
no_proxy = Column(String(255))
registry_enabled = Column(Boolean, default=False)
labels = Column(JSONEncodedDict)
class Container(Base):

View File

@ -27,7 +27,8 @@ class BayModel(base.MagnumPersistentObject, base.MagnumObject,
# Version 1.0: Initial version
# Version 1.1: Add 'registry_enabled' field
# Version 1.2: Added 'network_driver' field
VERSION = '1.2'
# Version 1.3: Added 'labels' attribute
VERSION = '1.3'
dbapi = dbapi.get_instance()
@ -53,7 +54,8 @@ class BayModel(base.MagnumPersistentObject, base.MagnumObject,
'http_proxy': fields.StringField(nullable=True),
'https_proxy': fields.StringField(nullable=True),
'no_proxy': fields.StringField(nullable=True),
'registry_enabled': fields.BooleanField(default=False)
'registry_enabled': fields.BooleanField(default=False),
'labels': fields.DictOfStringsField(nullable=True)
}
@staticmethod

View File

@ -61,6 +61,7 @@ class TestListBayModel(api_base.FunctionalTest):
self.assertNotIn('http_proxy', response['baymodels'][0])
self.assertNotIn('https_proxy', response['baymodels'][0])
self.assertNotIn('no_proxy', response['baymodels'][0])
self.assertNotIn('labels', response['baymodels'][0])
def test_get_one(self):
baymodel = obj_utils.create_test_baymodel(self.context)
@ -79,6 +80,7 @@ class TestListBayModel(api_base.FunctionalTest):
self.assertIn('http_proxy', response)
self.assertIn('https_proxy', response)
self.assertIn('no_proxy', response)
self.assertIn('labels', response)
def test_get_one_by_name(self):
baymodel = obj_utils.create_test_baymodel(self.context)
@ -96,6 +98,7 @@ class TestListBayModel(api_base.FunctionalTest):
self.assertIn('http_proxy', response)
self.assertIn('https_proxy', response)
self.assertIn('https_proxy', response)
self.assertIn('labels', response)
def test_get_one_by_name_not_found(self):
response = self.get_json(
@ -139,7 +142,7 @@ class TestListBayModel(api_base.FunctionalTest):
for key in ("flavor_id", "master_flavor_id", "dns_nameserver",
"keypair_id", "external_network_id", "fixed_network",
"docker_volume_size", "ssh_authorized_key", "coe",
"http_proxy", "https_proxy", "no_proxy",
"http_proxy", "https_proxy", "no_proxy", "labels",
"network_driver"):
self.assertIn(key, response['baymodels'][0])
@ -157,7 +160,7 @@ class TestListBayModel(api_base.FunctionalTest):
self.assertEqual(bm_list[-1].uuid, response['baymodels'][0]['uuid'])
for key in ("flavor_id", "master_flavor_id", "dns_nameserver",
"keypair_id", "external_network_id", "fixed_network",
"network_driver", "docker_volume_size",
"network_driver", "docker_volume_size", "labels",
"ssh_authorized_key", "coe"):
self.assertIn(key, response['baymodels'][0])
self.assertIn('flavor_id', response['baymodels'][0])
@ -243,7 +246,8 @@ class TestPatch(api_base.FunctionalTest):
'KMa71G5/4EOQxuQ/sgW965OOO2Hq'
'X8vjlQUnTK0HijrbSTLxp/9kazWW'
'FrfsdB8RtZBN digambar@magnum',
coe='swarm'
coe='swarm',
labels={'key1': 'val1', 'key2': 'val2'}
)
def test_update_not_found(self):
@ -295,6 +299,8 @@ class TestPatch(api_base.FunctionalTest):
response['https_proxy'])
self.assertEqual(self.baymodel.no_proxy,
response['no_proxy'])
self.assertEqual(self.baymodel.labels,
response['labels'])
def test_remove_singular(self):
baymodel = obj_utils.create_test_baymodel(self.context,
@ -329,6 +335,8 @@ class TestPatch(api_base.FunctionalTest):
response['https_proxy'])
self.assertEqual(self.baymodel.no_proxy,
response['no_proxy'])
self.assertEqual(self.baymodel.labels,
response['labels'])
def test_remove_non_existent_property_fail(self):
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,
@ -359,6 +367,8 @@ class TestPatch(api_base.FunctionalTest):
response['docker_volume_size'])
self.assertEqual(self.baymodel.coe,
response['coe'])
self.assertEqual(self.baymodel.labels,
response['labels'])
def test_add_root_non_existent(self):
response = self.patch_json(
@ -409,6 +419,8 @@ class TestPatch(api_base.FunctionalTest):
response['https_proxy'])
self.assertEqual(self.baymodel.no_proxy,
response['no_proxy'])
self.assertEqual(self.baymodel.labels,
response['labels'])
def test_remove_uuid(self):
response = self.patch_json('/baymodels/%s' % self.baymodel.uuid,
@ -501,7 +513,7 @@ class TestPost(api_base.FunctionalTest):
"dns_nameserver", "keypair_id", "external_network_id",
"cluster_distro", "fixed_network", "apiserver_port",
"docker_volume_size", "http_proxy", "https_proxy",
"no_proxy", "network_driver"]
"no_proxy", "network_driver", "labels"]
for field in fields:
self._create_baymodel_raises_app_error(**{field: 'i' * 256})
@ -509,7 +521,7 @@ class TestPost(api_base.FunctionalTest):
fields = ["uuid", "name", "image_id", "flavor_id", "master_flavor_id",
"dns_nameserver", "keypair_id", "external_network_id",
"cluster_distro", "fixed_network", "apiserver_port",
"docker_volume_size", "ssh_authorized_key",
"docker_volume_size", "ssh_authorized_key", "labels",
"http_proxy", "https_proxy", "no_proxy", "network_driver"]
for field in fields:
self._create_baymodel_raises_app_error(**{field: ''})
@ -531,6 +543,24 @@ class TestPost(api_base.FunctionalTest):
self._create_baymodel_raises_app_error(apiserver_port=1023)
self._create_baymodel_raises_app_error(apiserver_port='not an int')
@mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
@mock.patch.object(api_baymodel.BayModelsController,
'check_keypair_exists')
def test_create_baymodel_with_labels(self, mock_keypair_exists,
mock_image_data):
with mock.patch.object(self.dbapi, 'create_baymodel',
wraps=self.dbapi.create_baymodel) as cc_mock:
mock_keypair_exists.return_value = None
mock_image_data.return_value = {'name': 'mock_name',
'os_distro': 'fedora-atomic'}
bdict = apiutils.baymodel_post_data(labels={'key1': 'val1',
'key2': 'val2'})
response = self.post_json('/baymodels', bdict)
self.assertEqual(bdict['labels'],
response.json['labels'])
cc_mock.assert_called_once_with(mock.ANY)
self.assertNotIn('id', cc_mock.call_args[0][0])
@mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
@mock.patch.object(api_baymodel.BayModelsController,
'check_keypair_exists')

View File

@ -48,6 +48,9 @@ class TestBayConductorWithK8s(base.TestCase):
'http_proxy': 'http_proxy',
'https_proxy': 'https_proxy',
'no_proxy': 'no_proxy',
'labels': {'flannel_network_cidr': '10.101.0.0/16',
'flannel_network_subnetlen': '26',
'flannel_use_vxlan': 'yes'},
}
self.bay_dict = {
'baymodel_id': 'xx-xx-xx-xx',
@ -98,6 +101,9 @@ class TestBayConductorWithK8s(base.TestCase):
'node_count': 'number_of_minions',
'master_count': 'number_of_masters',
'discovery_url': 'discovery_url',
'labels': {'flannel_network_cidr': '10.101.0.0/16',
'flannel_network_subnetlen': '26',
'flannel_use_vxlan': 'yes'},
'http_proxy': 'http_proxy',
'https_proxy': 'https_proxy',
'no_proxy': 'no_proxy',
@ -115,6 +121,9 @@ class TestBayConductorWithK8s(base.TestCase):
'fixed_network_cidr': '10.20.30.0/24',
'docker_volume_size': 20,
'discovery_url': 'https://discovery.etcd.io/test',
'flannel_network_cidr': '10.101.0.0/16',
'flannel_network_subnetlen': '26',
'flannel_use_vxlan': 'yes',
'http_proxy': 'http_proxy',
'https_proxy': 'https_proxy',
'no_proxy': 'no_proxy',
@ -163,6 +172,9 @@ class TestBayConductorWithK8s(base.TestCase):
'http_proxy': 'http_proxy',
'https_proxy': 'https_proxy',
'no_proxy': 'no_proxy',
'flannel_network_cidr': '10.101.0.0/16',
'flannel_network_subnetlen': '26',
'flannel_use_vxlan': 'yes',
}
self.assertEqual(expected, definition)
@ -205,6 +217,9 @@ class TestBayConductorWithK8s(base.TestCase):
'http_proxy': 'http_proxy',
'https_proxy': 'https_proxy',
'no_proxy': 'no_proxy',
'flannel_network_cidr': '10.101.0.0/16',
'flannel_network_subnetlen': '26',
'flannel_use_vxlan': 'yes',
}
self.assertEqual(expected, definition)
@ -286,7 +301,10 @@ class TestBayConductorWithK8s(base.TestCase):
'discovery_url': 'https://discovery.etcd.io/test',
'http_proxy': 'http_proxy',
'https_proxy': 'https_proxy',
'no_proxy': 'no_proxy'
'no_proxy': 'no_proxy',
'flannel_network_cidr': '10.101.0.0/16',
'flannel_network_subnetlen': '26',
'flannel_use_vxlan': 'yes',
}
self.assertIn('token', definition)
del definition['token']
@ -353,7 +371,10 @@ class TestBayConductorWithK8s(base.TestCase):
'discovery_url': 'https://address/token',
'http_proxy': 'http_proxy',
'https_proxy': 'https_proxy',
'no_proxy': 'no_proxy'
'no_proxy': 'no_proxy',
'flannel_network_cidr': '10.101.0.0/16',
'flannel_network_subnetlen': '26',
'flannel_use_vxlan': 'yes',
}
self.assertEqual(expected, definition)
reqget.assert_called_once_with('http://etcd/test?size=1')

View File

@ -184,6 +184,11 @@ class AtomicK8sTemplateDefinitionTestCase(base.TestCase):
removal_nodes = ['node1', 'node2']
mock_scale_manager.get_removal_nodes.return_value = removal_nodes
mock_get_discovery_url.return_value = 'fake_discovery_url'
flannel_cidr = mock_baymodel.labels.get('flannel_network_cidr')
flannel_subnet = mock_baymodel.labels.get('flannel_network_subnetlen')
flannel_vxlan = mock_baymodel.labels.get('flannel_use_vxlan')
k8s_def = tdef.AtomicK8sTemplateDefinition()
k8s_def.get_params(mock_context, mock_baymodel, mock_bay,
@ -191,7 +196,10 @@ class AtomicK8sTemplateDefinitionTestCase(base.TestCase):
expected_kwargs = {'extra_params': {
'minions_to_remove': removal_nodes,
'discovery_url': 'fake_discovery_url'}}
'discovery_url': 'fake_discovery_url',
'flannel_network_cidr': flannel_cidr,
'flannel_use_vxlan': flannel_subnet,
'flannel_network_subnetlen': flannel_vxlan}}
mock_get_params.assert_called_once_with(mock_context, mock_baymodel,
mock_bay, **expected_kwargs)

View File

@ -48,6 +48,7 @@ def get_test_baymodel(**kw):
'coe': kw.get('coe', 'swarm'),
'created_at': kw.get('created_at'),
'updated_at': kw.get('updated_at'),
'labels': kw.get('labels', {'key1': 'val1', 'key2': 'val2'}),
'http_proxy': kw.get('http_proxy', 'fake_http_proxy'),
'https_proxy': kw.get('https_proxy', 'fake_https_proxy'),
'no_proxy': kw.get('no_proxy', 'fake_no_proxy'),

View File

@ -426,7 +426,7 @@ class _TestObject(object):
object_data = {
'Bay': '1.0-35edde13ad178e9419e7ea8b6d580bcd',
'BayLock': '1.0-7d1eb08cf2070523bd210369c7a2e076',
'BayModel': '1.2-b08e888342cc84d7c709193529b92c6f',
'BayModel': '1.3-369d7b7f05720780ae4f6c5d983e8c3e',
'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2',
'Container': '1.0-e12affbba5f8a748882a3ae98aced282',
'MyObj': '1.0-b43567e512438205e32f4e95ca616697',