Allow model constraints to be passed via env vars

Allow model constraints to be applied to models that zaza
creates.
This commit is contained in:
Liam Young
2018-07-13 12:36:14 +00:00
parent 78a0f51f9c
commit ea7336a67e
3 changed files with 98 additions and 39 deletions

View File

@@ -9,54 +9,54 @@ class TestCharmLifecyclePrepare(ut_utils.BaseTestCase):
MODEL_CONFIG_DEFAULTS = lc_prepare.MODEL_DEFAULTS
def base_get_model_settings(self, env, expect):
def base_parse_option_list_string(self, env, expect):
with mock.patch.dict(lc_prepare.os.environ, env):
self.assertEqual(lc_prepare.get_model_settings(), expect)
def test_parse_option_list_string_empty_config(self):
self.assertEqual(
lc_prepare.parse_option_list_string(option_list=""),
{})
def test_parse_option_list_string_single_value(self):
self.assertEqual(
lc_prepare.parse_option_list_string(
option_list='image-stream=released'),
{'image-stream': 'released'})
def test_parse_option_list_string_multiple_values(self):
self.assertEqual(
lc_prepare.parse_option_list_string(
option_list='image-stream=released;no-proxy=jujucharms.com'),
{
'image-stream': 'released',
'no-proxy': 'jujucharms.com'})
def test_parse_option_list_string_whitespace(self):
self.assertEqual(
lc_prepare.parse_option_list_string(
option_list=' test-mode= false ; image-stream= released'),
{
'test-mode': 'false',
'image-stream': 'released'})
def test_get_model_settings_no_config(self):
expect_config = copy.deepcopy(self.MODEL_CONFIG_DEFAULTS)
self.base_get_model_settings({}, expect_config)
def test_get_model_settings_empty_config(self):
expect_config = copy.deepcopy(self.MODEL_CONFIG_DEFAULTS)
self.base_get_model_settings({'MODEL_SETTINGS': ''}, expect_config)
def test_get_model_settings_single_value(self):
expect_config = copy.deepcopy(self.MODEL_CONFIG_DEFAULTS)
expect_config.update({'virt-type': 'kvm'})
self.base_get_model_settings(
{'MODEL_SETTINGS': 'virt-type=kvm'},
expect_config)
def test_get_model_settings_multiple_values(self):
expect_config = copy.deepcopy(self.MODEL_CONFIG_DEFAULTS)
expect_config.update({
'virt-type': 'kvm',
'no-proxy': 'jujucharms.com'})
self.base_get_model_settings(
{'MODEL_SETTINGS': 'virt-type=kvm;no-proxy=jujucharms.com'},
expect_config)
self.base_parse_option_list_string({}, self.MODEL_CONFIG_DEFAULTS)
def test_get_model_settings_multiple_values_override(self):
expect_config = copy.deepcopy(self.MODEL_CONFIG_DEFAULTS)
expect_config.update({'test-mode': 'false'})
self.base_get_model_settings(
self.base_parse_option_list_string(
{'MODEL_SETTINGS': 'test-mode=false'},
expect_config)
def test_get_model_settings_whitespace(self):
expect_config = copy.deepcopy(self.MODEL_CONFIG_DEFAULTS)
expect_config.update({
'test-mode': 'false',
'virt-type': 'kvm'})
self.base_get_model_settings(
{'MODEL_SETTINGS': ' test-mode= false ; virt-type= kvm'},
expect_config)
def test_prepare(self):
self.patch_object(lc_prepare.zaza.controller, 'add_model')
self.patch_object(lc_prepare, 'get_model_settings')
self.patch_object(lc_prepare, 'get_model_constraints')
self.patch_object(lc_prepare.zaza.model, 'set_model_constraints')
self.get_model_settings.return_value = lc_prepare.MODEL_DEFAULTS
self.get_model_constraints.return_value = {'image-stream': 'released'}
lc_prepare.prepare('newmodel')
self.add_model.assert_called_once_with(
'newmodel',
@@ -69,6 +69,9 @@ class TestCharmLifecyclePrepare(ut_utils.BaseTestCase):
'enable-os-upgrade': 'false',
'automatically-retry-hooks': 'false',
'use-default-secgroup': 'true'})
self.set_model_constraints.assert_called_once_with(
constraints={'image-stream': 'released'},
model_name='newmodel')
def test_parser(self):
args = lc_prepare.parse_args(['-m', 'newmodel'])

View File

@@ -6,6 +6,7 @@ import os
import sys
import zaza.controller
import zaza.model
MODEL_DEFAULTS = {
# Model defaults from charm-test-infra
@@ -23,21 +24,51 @@ MODEL_DEFAULTS = {
}
def get_model_settings():
"""Construct settings for model from defaults and env variables.
def parse_option_list_string(option_list, delimiter=None):
"""Convert the given string to a dictionary of options.
:returns: Settings to usee for model
Each pair must be of the form 'k=v', the delimiter seperates the
pairs from each other not the key from the value.
:param option_list: A string representation of key value pairs.
:type option_list: str
:param delimiter: Delimiter to use to seperate each pair.
:type delimiter: str
:returns: A dictionary of settings.
:rtype: Dict
"""
model_settings = copy.deepcopy(MODEL_DEFAULTS)
for setting in os.environ.get('MODEL_SETTINGS', '').split(';'):
settings = {}
if delimiter is None:
delimiter = ';'
for setting in option_list.split(delimiter):
if not setting:
continue
key, value = setting.split('=')
model_settings[key.strip()] = value.strip()
settings[key.strip()] = value.strip()
return settings
def get_model_settings():
"""Construct settings for model from defaults and env variables.
:returns: Settings to use for model
:rtype: Dict
"""
model_settings = copy.deepcopy(MODEL_DEFAULTS)
model_settings.update(
parse_option_list_string(os.environ.get('MODEL_SETTINGS', '')))
return model_settings
def get_model_constraints():
"""Construct constraints for model.
:returns: Constraints to apply to model
:rtype: Dict
"""
return parse_option_list_string(os.environ.get('MODEL_CONSTRAINTS', ''))
def prepare(model_name):
"""Run all steps to prepare the environment before a functional test run.
@@ -45,6 +76,9 @@ def prepare(model_name):
:type bundle: str
"""
zaza.controller.add_model(model_name, config=get_model_settings())
zaza.model.set_model_constraints(
model_name=model_name,
constraints=get_model_constraints())
def parse_args(args):

View File

@@ -1008,3 +1008,25 @@ async def async_get_relation_id(application_name, remote_application_name,
return(rel.id)
get_relation_id = sync_wrapper(async_get_relation_id)
def set_model_constraints(constraints, model_name=None):
"""
Set constraints on a model.
Note: Subprocessing out to 'juju' is a temporary solution until
https://bit.ly/2ujbVPA lands in libjuju.
:param model_name: Name of model to operate on
:type model_name: str
:param constraints: Constraints to be applied to model
:type constraints: dict
"""
if not constraints:
return
if not model_name:
model_name = get_juju_model()
cmd = ['juju', 'set-model-constraints', '-m', model_name]
cmd.extend(['{}={}'.format(k, v) for k, v in constraints.items()])
subprocess.check_call(cmd)