def _load_config(self, skip_molecule_file=False): config = self._get_config() molecule_file = None if not skip_molecule_file: molecule_file = self._load_molecule_file(config) # if molecule file has a molecule section, merge that into our config as # an override with the highest precedence if 'molecule' in molecule_file: config = utilities.merge_dicts(config, molecule_file['molecule']) # merge virtualbox provider options from molecule file with our defaults for provider in molecule_file['vagrant']['providers']: if provider['type'] in config['providers']: if 'options' in provider: config['providers'][provider['type']]['options'] = utilities.merge_dicts( config['providers'][provider['type']]['options'], provider['options']) # append molecule_dir to filenames so they're easier to use later config['state_file'] = '/'.join([config['molecule_dir'], config['state_file']]) config['vagrantfile_file'] = '/'.join([config['molecule_dir'], config['vagrantfile_file']]) config['rakefile_file'] = '/'.join([config['molecule_dir'], config['rakefile_file']]) config['ansible']['config_file'] = '/'.join([config['molecule_dir'], config['ansible']['config_file']]) config['ansible']['inventory_file'] = '/'.join([config['molecule_dir'], config['ansible']['inventory_file']]) return config, molecule_file
def test_merge_deep_deep_01(self): expected = { "users": {"remy": {"age": 21, "email": "*****@*****.**", "office": "Austin", "position": "python master"}} } with testtools.ExpectedException(LookupError): actual = utilities.merge_dicts(self.deep_dict_a, self.deep_dict_b, raise_conflicts=True) self.assertEqual(expected, actual)
def merge_molecule_file(self, molecule_file=None): """ Looks for a molecule file in the local path and merges it into our config :param molecule_file: path and name of molecule file to look for :return: None """ if molecule_file is None: molecule_file = self.config['molecule']['molecule_file'] if not os.path.isfile(molecule_file): error = '\n{}Unable to find {}. Exiting.{}' utilities.logger.error(error.format(colorama.Fore.RED, self.config[ 'molecule']['molecule_file'], colorama.Fore.RESET)) sys.exit(1) with open(molecule_file, 'r') as env: try: molecule_yml = yaml.load(env) except Exception as e: error = "\n{}{} isn't properly formatted: {}{}" utilities.logger.error(error.format( colorama.Fore.RED, molecule_file, e, colorama.Fore.RESET)) sys.exit(1) interim = utilities.merge_dicts(self.config, molecule_yml) self.config = interim
def merge_molecule_file(self, molecule_file=None): """ Looks for a molecule file in the local path and merges it into our config :param molecule_file: path and name of molecule file to look for :return: None """ if molecule_file is None: molecule_file = self.config['molecule']['molecule_file'] if not os.path.isfile(molecule_file): error = '\n{}Unable to find {}. Exiting.{}' print( error.format(Fore.RED, self.config['molecule']['molecule_file'], Fore.RESET)) sys.exit(1) with open(molecule_file, 'r') as env: try: molecule_yml = yaml.safe_load(env) except Exception as e: error = "\n{}{} isn't properly formatted: {}{}" print(error.format(Fore.RED, molecule_file, e, Fore.RESET)) sys.exit(1) interim = utilities.merge_dicts(self.config, molecule_yml) self.config = interim
def test_merge_deep_deep_02(self): expected = { "users": {"remy": {"age": 21, "email": "*****@*****.**", "office": "San Jose", "position": "python master"}} } actual = utilities.merge_dicts(self.deep_dict_b, self.deep_dict_a) self.assertEqual(expected, actual)
def test_merge_simple_deep_01(self): expected = { "name": "remy", "city": "Berkeley", "age": 21, "users": {"remy": {"email": "*****@*****.**", "office": "San Jose", "age": 21}}, } actual = utilities.merge_dicts(self.deep_dict_a, self.simple_dict_a) self.assertEqual(expected, actual)
def test_merge_deep_deep_00(self): expected = { "users": {"remy": {"age": 21, "email": "*****@*****.**", "office": "Austin", "position": "python master"}} } actual = utilities.merge_dicts(self.deep_dict_a, self.deep_dict_b) self.assertEqual(expected, actual)
def test_merge_deep_deep_00(deep_dict_a, deep_dict_b): expected = { "users": {"remy": {"age": 21, "email": "*****@*****.**", "office": "Austin", "position": "python master"}} } actual = utilities.merge_dicts(deep_dict_a, deep_dict_b) assert expected == actual
def test_merge_simple_deep_01(self): expected = { "name": "remy", "city": "Berkeley", "age": 21, "users": {"remy": {"email": "*****@*****.**", "office": "San Jose", "age": 21}} } actual = utilities.merge_dicts(self.deep_dict_a, self.simple_dict_a) self.assertEqual(expected, actual)
def test_merge_dicts(): # Example taken from python-anyconfig/anyconfig/__init__.py a = {'b': [{'c': 0}, {'c': 2}], 'd': {'e': 'aaa', 'f': 3}} b = {'a': 1, 'b': [{'c': 3}], 'd': {'e': 'bbb'}} expected = {'a': 1, 'b': [{'c': 0}, {'c': 2}, {'c': 3}], 'd': {'e': "bbb", 'f': 3}} result = utilities.merge_dicts(a, b) assert expected == result
def _get_config(self): merged_config = Molecule.CONFIG_DEFAULTS.copy() # merge defaults with a config file if found for path in Molecule.CONFIG_PATHS: if path and os.path.isfile(path): with open(path, 'r') as stream: merged_config = utilities.merge_dicts(merged_config, yaml.load(stream)) return merged_config return Molecule.CONFIG_DEFAULTS
def test_merge_simple_deep_01(deep_dict_a, simple_dict_a): expected = { "name": "remy", "city": "Berkeley", "age": 21, "users": {"remy": {"email": "*****@*****.**", "office": "San Jose", "age": 21}} } actual = utilities.merge_dicts(deep_dict_a, simple_dict_a) assert expected == actual
def test_merge_deep_deep_01(deep_dict_a, deep_dict_b): expected = { "users": {"remy": {"age": 21, "email": "*****@*****.**", "office": "Austin", "position": "python master"}} } with pytest.raises(LookupError): actual = utilities.merge_dicts(deep_dict_a, deep_dict_b, raise_conflicts=True) assert expected == actual
def merge_molecule_config_files(self, paths=CONFIG_PATHS): """ Looks for a molecule config file in paths and merges it with current config if found Only the first file that's found will be merged in. :param paths: list of places to look for config files :return: Path of file that was merged into config, if found, otherwise None """ # merge defaults with a config file if found for path in paths: if path and os.path.isfile(path): with open(path, 'r') as stream: self.config = utilities.merge_dicts(self.config, yaml.safe_load(stream)) return path return
def merge_molecule_config_files(self, paths=CONFIG_PATHS): """ Looks for a molecule config file in paths and merges it with current config if found Only the first file that's found will be merged in. :param paths: list of places to look for config files :return: Path of file that was merged into config, if found, otherwise None """ # merge defaults with a config file if found for path in paths: if path and os.path.isfile(path): with open(path, 'r') as stream: self.config = utilities.merge_dicts( self.config, yaml.safe_load(stream)) return path return
def _create_playbook_args(self): merged_args = self._config['ansible'].copy() # don't pass these to molecule-playbook CLI env_args = ['raw_ssh_args', 'host_key_checking', 'config_file', 'raw_env_vars'] # args that molecule-playbook doesn't accept as --arg=value special_args = ['playbook', 'verbose'] # merge defaults with molecule.yml values if 'ansible' in self._molecule_file: merged_args = utilities.merge_dicts(merged_args, self._molecule_file['ansible']) # set raw environment variables if any are found if 'raw_env_vars' in self._molecule_file['ansible']: for key, value in self._molecule_file['ansible']['raw_env_vars'].iteritems(): self._env[key] = value self._env['PYTHONUNBUFFERED'] = '1' self._env['ANSIBLE_FORCE_COLOR'] = 'true' self._env['ANSIBLE_HOST_KEY_CHECKING'] = str(merged_args['host_key_checking']).lower() self._env['ANSIBLE_SSH_ARGS'] = ' '.join(merged_args['raw_ssh_args']) self._env['ANSIBLE_CONFIG'] = merged_args['config_file'] kwargs = {} args = [] # pull in values passed to molecule CLI if '--tags' in self._args: merged_args['tags'] = self._args['--tags'] # pass supported --arg=value args for arg, value in merged_args.iteritems(): # don't pass False arguments to ansible-playbook if value and arg not in (env_args + special_args): kwargs[arg] = value # verbose is weird -vvvv if merged_args['verbose']: args.append('-' + merged_args['verbose']) kwargs['_env'] = self._env kwargs['_out'] = self._print_line kwargs['_err'] = self._print_line return merged_args['playbook'], args, kwargs
def test_merge_simple_simple_01(self): expected = {"name": "remy", "city": "Berkeley", "age": 21} actual = utilities.merge_dicts(self.simple_dict_b, self.simple_dict_a) self.assertEqual(expected, actual)
def test_merge_simple_simple_00(self): expected = {"name": "remy", "city": "Austin", "age": 21} actual = utilities.merge_dicts(self.simple_dict_a, self.simple_dict_b) self.assertEqual(expected, actual)
def test_merge_simple_simple_01(simple_dict_b, simple_dict_a): expected = {"name": "remy", "city": "Berkeley", "age": 21} actual = utilities.merge_dicts(simple_dict_b, simple_dict_a) assert expected == actual
def molecule_docker_config(molecule_section_data, docker_section_data, ansible_section_data): return reduce( lambda x, y: utilities.merge_dicts(x, y), [molecule_section_data, docker_section_data, ansible_section_data])
def molecule_openstack_config(molecule_section_data, openstack_section_data, ansible_section_data): return reduce( lambda x, y: utilities.merge_dicts(x, y), [molecule_section_data, openstack_section_data, ansible_section_data])
def execute(self, exit=True): if self.static: self.disabled('verify') serverspec_dir = self.molecule._config.config['molecule'][ 'serverspec_dir'] testinfra_dir = self.molecule._config.config['molecule'][ 'testinfra_dir'] rakefile = self.molecule._config.config['molecule']['rakefile_file'] ignore_paths = self.molecule._config.config['molecule']['ignore_paths'] # whitespace & trailing newline check validators.check_trailing_cruft(ignore_paths=ignore_paths, exit=exit) self.molecule._write_ssh_config() # testinfra's Ansible calls get same env vars as ansible-playbook ansible = ansible_playbook.AnsiblePlaybook( self.molecule._config.config['ansible'], _env=self.molecule._env) debug = self.molecule._args.get('--debug', False) testinfra_kwargs = utilities.merge_dicts( self.molecule._provisioner.testinfra_args, self.molecule._config.config['testinfra']) testinfra_kwargs['env'] = ansible.env testinfra_kwargs['env']['PYTHONDONTWRITEBYTECODE'] = '1' testinfra_kwargs['debug'] = debug testinfra_kwargs['sudo'] = self.molecule._args.get('--sudo', False) serverspec_kwargs = self.molecule._provisioner.serverspec_args serverspec_kwargs['debug'] = debug try: # testinfra tests = '{}/test_*.py'.format(testinfra_dir) tests_glob = glob.glob(tests) if len(tests_glob) > 0: msg = 'Executing testinfra tests found in {}/.' utilities.print_info(msg.format(testinfra_dir)) validators.testinfra(tests_glob, **testinfra_kwargs) else: msg = 'No testinfra tests found in {}/.\n' utilities.logger.warning(msg.format(testinfra_dir)) # serverspec / rubocop if os.path.isdir(serverspec_dir): msg = 'Executing rubocop on *.rb files found in {}/.' utilities.print_info(msg.format(serverspec_dir)) validators.rubocop(serverspec_dir, **serverspec_kwargs) msg = 'Executing serverspec tests found in {}/.' utilities.print_info(msg.format(serverspec_dir)) validators.rake(rakefile, **serverspec_kwargs) else: msg = 'No serverspec tests found in {}/.\n' utilities.logger.warning(msg.format(serverspec_dir)) except sh.ErrorReturnCode as e: utilities.logger.error('ERROR: {}'.format(e)) if exit: utilities.sysexit(e.exit_code) return e.exit_code, e.stdout return None, None
def molecule_invalid_provisioner_config( molecule_section_data, invalid_section_data, ansible_section_data): return reduce( lambda x, y: utilities.merge_dicts(x, y), [molecule_section_data, invalid_section_data, ansible_section_data])
def execute(self, exit=True): if self.static: self.disabled('verify') serverspec_dir = self.molecule._config.config['molecule'][ 'serverspec_dir'] testinfra_dir = self.molecule._config.config['molecule'][ 'testinfra_dir'] rakefile = self.molecule._config.config['molecule']['rakefile_file'] ignore_paths = self.molecule._config.config['molecule']['ignore_paths'] # whitespace & trailing newline check validators.check_trailing_cruft(ignore_paths=ignore_paths, exit=exit) # no serverspec or testinfra if not os.path.isdir(serverspec_dir) and not os.path.isdir( testinfra_dir): msg = '{}Skipping tests, could not find {}/ or {}/.{}' utilities.logger.warning(msg.format(colorama.Fore.YELLOW, serverspec_dir, testinfra_dir, colorama.Fore.RESET)) return None, None self.molecule._write_ssh_config() # testinfra's Ansible calls get same env vars as ansible-playbook ansible = AnsiblePlaybook(self.molecule._config.config['ansible'], _env=self.molecule._env) testinfra_kwargs = utilities.merge_dicts( self.molecule._provisioner.testinfra_args, self.molecule._config.config['testinfra']) serverspec_kwargs = self.molecule._provisioner.serverspec_args testinfra_kwargs['env'] = ansible.env testinfra_kwargs['env']['PYTHONDONTWRITEBYTECODE'] = '1' testinfra_kwargs['debug'] = True if self.molecule._args.get( '--debug') else False testinfra_kwargs['sudo'] = True if self.molecule._args.get( '--sudo') else False serverspec_kwargs['env'] = testinfra_kwargs['env'] serverspec_kwargs['debug'] = testinfra_kwargs['debug'] try: # testinfra if len(glob.glob1(testinfra_dir, "test_*.py")) > 0: msg = '\n{}Executing testinfra tests found in {}/.{}' print(msg.format(colorama.Fore.MAGENTA, testinfra_dir, colorama.Fore.RESET)) validators.testinfra(testinfra_dir, **testinfra_kwargs) print() else: msg = '{}No testinfra tests found in {}/.\n{}' utilities.logger.warning(msg.format( colorama.Fore.YELLOW, testinfra_dir, colorama.Fore.RESET)) # serverspec / rubocop if os.path.isdir(serverspec_dir): msg = '{}Executing rubocop on *.rb files found in {}/.{}' print(msg.format(colorama.Fore.MAGENTA, serverspec_dir, colorama.Fore.RESET)) validators.rubocop(serverspec_dir, **serverspec_kwargs) print() msg = '{}Executing serverspec tests found in {}/.{}' print(msg.format(colorama.Fore.MAGENTA, serverspec_dir, colorama.Fore.RESET)) validators.rake(rakefile, **serverspec_kwargs) print() else: msg = '{}No serverspec tests found in {}/.\n{}' utilities.logger.warning(msg.format( colorama.Fore.YELLOW, serverspec_dir, colorama.Fore.RESET)) except sh.ErrorReturnCode as e: utilities.logger.error('ERROR: {}'.format(e)) if exit: sys.exit(e.exit_code) return e.exit_code, e.stdout return None, None
def test_merge_simple_simple_00(simple_dict_a, simple_dict_b): expected = {"name": "remy", "city": "Austin", "age": 21} actual = utilities.merge_dicts(simple_dict_a, simple_dict_b) expected == actual