def _prepare_tester_config(tester_config, global_config, more_envs={}): """ Validates the tester_config structure. Basically checks for presence of 'image' key. Then it will make sure that all keys are present, with the correct types. The environment values in global_config have precedence above all else. :param tester_config: The tester_config structure :param global_config: The global configuration from the command line :param more_envs: more key-value items to merge into the environment :return: None """ tester_template = { 'environment': {}, 'links': {}, 'commands': [], } errors = [] if 'image' not in tester_config or \ not isinstance(tester_config['image'], str): errors.append("tester_config key must contain 'image' key " "and must be a string") if 'links' not in tester_config or \ not isinstance(tester_config['links'], list): errors.append("tester_config key must contain 'links' key" " as array") if len(errors): fail("\n" + "\n".join(errors)) tmp = deep_merge(tester_template, tester_config) tmp['environment'] = deep_merge(tmp['environment'], more_envs, global_config.env) return tmp
def __init__(self, name, test_definition): self.name = name test_definition = deep_merge(default_config, test_definition) # quick shortcuts self.test_env = test_definition['environment'] self.test_meta = test_definition['meta'] self.test_commands = test_definition.get('test_commands', []) # take care of commands ... self.test_commands = _build_exec_array(self.test_commands) self.test_meta['test_before'] = \ _build_exec_array(self.test_meta.get('test_before', None)) self.test_meta['test_after'] = \ _build_exec_array(self.test_meta.get('test_after', None)) # okay. # let's keep all file references relative to the configuration # file. easy to remember. configfilepath = realpath(dirname(self.test_meta.get('_configfile', './dummy'))) # self.TEMPLATE / .TEMPLATE_NAME tmp = self.test_meta['docker_compose_template'] if not isabs(tmp): tmp = realpath(join(configfilepath, tmp)) self.template = tmp self.template_name = basename(self.template) # self.BASEDIR tmp = self.test_meta.get('test_basedir', configfilepath) if not isabs(tmp): tmp = realpath(join(configfilepath, tmp)) self.base_dir = tmp # self.SANITIZED_NAME, .TEST_DIR self.sanitized_name = resub("[^a-zA-Z0-9_]", "-", self.name) self.test_dir = dbg_tr_get_testdir(self.base_dir, self.sanitized_name) # extend SELF.TEST_ENV with TEST_DIR self.test_env['test_dir'] = self.test_dir # create SELF.COMMANDLINE self.commandline = copy.copy(default_commandline_start) for param in self.test_meta['docker_compose_params']: self.commandline.append(param) for key, val in self.test_env.items(): self.commandline.append("-e") self.commandline.append("%s=%s" % (key, val)) self.commandline.append("--rm") self.commandline.extend(copy.copy(default_commandline_end)) self.commandline.append(self.test_meta['test_service']) # create .STATE, .RESULT, .EXCEPTION, .REASON self.state = self.NOTRUN self.results = [] self.exception = None self.reason = None # log setup # NO LOGGING BEFORE HERE log_filename = join(self.base_dir, basename(self.test_dir)) + ".log" self.log = get_logger("t-%s" % self.name, filename=log_filename) # some debug output self.log.info("base commandline '%s'" % " ".join(self.commandline)) self.log.debug("test directory '%s'" % self.test_dir) self.log.debug("template path '%s'" % self.template) for key, val in self.test_env.items(): self.log.debug("env %s=%s" % (key, val))
def run(self, run_args, **kwargs): use_command = run_args \ if isinstance(run_args, list) \ else run_args.split(" ") full_command = self.base_command + use_command # call(*a1, *a2) seems only to work on python 3.5+ return tools.run_command(full_command, **tools.deep_merge(self.run_kwargs, kwargs))
def _v3_create_test_with(fileconfig, argconfig, test_name, tester_name, template_name, env_name, test_num): errors = [] # convenience vars compose_templates = fileconfig['compose_templates'] testers = fileconfig['tester_configs'] environments = fileconfig['environments'] # check references if tester_name not in testers: errors.append("CONFIG FILE: no tester definition named '{}'" .format(tester_name)) if template_name not in compose_templates: errors.append("CONFIG FILE: no template definition named '{}'" .format(tester_name)) if env_name and env_name not in environments: errors.append("CONFIG FILE: no environment definition named '{}'" .format(tester_name)) if len(errors): fail("\n" + "\n".join(errors)) # quick hack to this is a COPY of the original entry tester = deep_merge({}, fileconfig['tester_configs'][tester_name]) # take care of the tester environment conf_env = fileconfig['environments'][env_name] \ if env_name else {} tester['environment'] = deep_merge(tester['environment'], conf_env) \ if tester.get('environment') else conf_env # get template template = fileconfig['compose_templates'][template_name] # merge in the environment to the tester config tester = _prepare_tester_config(tester, argconfig) template = _prepare_docker_compose_template(template, tester['environment'], argconfig) output.output.block_open("Settings of test '{}_{}'".format(test_name, test_num)) output.output.dump("tester: {}".format(tester_name)) output.output.dump("template: {}".format(template_name)) if env_name: output.output.dump("environment: {}".format(env_name)) output.output.block_done() tr = Testrun("{}_{}".format(test_name, test_num), template, **tester) return tr
def _get_test_sets(setupdata): """Always returns a list of list of Testsets :param setupdata the full yaml setup data """ testsets = setupdata['testsets'] global_config = setupdata['global'] rv = [] for tsname, tests in sorted(testsets.items()): ts = Testset(name=tsname) rv.append(ts) # remove global settings from test set ts.set_global_config(tools.deep_merge(global_config, tests.pop("_global", {}))) for test_name, test_config in sorted(tests.items()): # the overrides have precedence above everything use_test_config = tools.deep_merge(test_config, global_overrides) ts.add_from_config(test_name, use_test_config) return rv
def _v3_create_test_with(fileconfig, argconfig, test_name, tester_name, template_name, env_name, test_num): errors = [] # convenience vars compose_templates = fileconfig['compose_templates'] testers = fileconfig['tester_configs'] environments = fileconfig['environments'] # check references if tester_name not in testers: errors.append( "CONFIG FILE: no tester definition named '{}'".format(tester_name)) if template_name not in compose_templates: errors.append("CONFIG FILE: no template definition named '{}'".format( tester_name)) if env_name and env_name not in environments: errors.append( "CONFIG FILE: no environment definition named '{}'".format( tester_name)) if len(errors): fail("\n" + "\n".join(errors)) # quick hack to this is a COPY of the original entry tester = deep_merge({}, fileconfig['tester_configs'][tester_name]) # take care of the tester environment conf_env = fileconfig['environments'][env_name] \ if env_name else {} tester['environment'] = deep_merge(tester['environment'], conf_env) \ if tester.get('environment') else conf_env # get template template = fileconfig['compose_templates'][template_name] # merge in the environment to the tester config tester = _prepare_tester_config(tester, argconfig) template = _prepare_docker_compose_template(template, tester['environment'], argconfig) output.output.block_open("Settings of test '{}_{}'".format( test_name, test_num)) output.output.dump("tester: {}".format(tester_name)) output.output.dump("template: {}".format(template_name)) if env_name: output.output.dump("environment: {}".format(env_name)) output.output.block_done() tr = Testrun("{}_{}".format(test_name, test_num), template, **tester) return tr
def _get_setupdata(): stub = tools.get_full_stub() filedata = None try: with open(config.config_file, "r") as ifile: filedata = yaml.safe_load(ifile) except IOError as e: # FileNotFoundError is python3 only. yihah. if e.errno == ENOENT: tools.fail("Could not find configuration file: %s" % config.config_file) else: tools.fail("Unspecified IO error: %s" % str(e)) logger.info("Read configuration file %s" % config.config_file) return tools.deep_merge(stub, filedata)
def test_deep_merging(self): d0 = {1: {2: 3, 3: 4}, 7: 8} d1 = {1: {4: 5, 3: 3}, 8: 9} result = {1: {2: 3, 3: 3, 4: 5}, 7: 8, 8: 9} self.assertDictEqual(deep_merge(d0, d1), result)
def test_key_priority(self): d0 = {1: 2, 2: [1, 2, 3]} d1 = {1: 3, 2: 3} result = {1: 3, 2: 3} self.assertDictEqual(deep_merge(d0, d1), result)
def add_from_config(self, name, config): test_name = "%s-%s" % (self.name, name) test_conf = deep_merge(self.global_config, config) self.tests.append(Testrun(test_name, test_conf))
def test_more_than_two_arguments(self): d0 = {1: 2, 2: 3, 3: 4} d1 = {2: 4, 3: 5, 4: 6} d2 = {3: 6, 5: 8} rs = {1: 2, 2: 4, 3: 6, 4: 6, 5: 8} self.assertDictEqual(rs, deep_merge(d0, d1, d2))
def test_more_than_two_arguments(self): d0 = {1: 2, 2: 3, 3: 4 } d1 = { 2: 4, 3: 5, 4: 6 } d2 = { 3: 6, 5: 8} rs = {1: 2, 2: 4, 3: 6, 4: 6, 5: 8} self.assertDictEqual(rs, deep_merge(d0, d1, d2))
def test_basic_function(self): d0 = {1: 2} d1 = {2: 3} result = {1: 2, 2: 3} self.assertDictEqual(deep_merge(d0, d1), result)