def test_api_drivers(): results = api.drivers() for result in results: assert isinstance(result, api.Driver) assert 'delegated' in results
def _get_modules_directories(self): """Return list of ansilbe module includes directories. Adds modules directory from molecule and its plugins. """ paths = [util.abs_path(os.path.join(self._get_plugin_directory(), "modules"))] for d in drivers(): p = d.modules_dir() if p: paths.append(p) paths.extend( [ util.abs_path( os.path.join(self._config.scenario.ephemeral_directory, "library") ), util.abs_path(os.path.join(self._config.project_directory, "library")), util.abs_path( os.path.join( os.path.expanduser("~"), ".ansible", "plugins", "modules", ) ), "/usr/share/ansible/plugins/modules", ] ) return paths
def _get_modules_directories(self) -> List[str]: """Return list of ansilbe module includes directories. Adds modules directory from molecule and its plugins. """ paths: List[Optional[str]] = [] if os.environ.get("ANSIBLE_LIBRARY"): paths = list( map(util.abs_path, os.environ["ANSIBLE_LIBRARY"].split(":"))) paths.append( util.abs_path(os.path.join(self._get_plugin_directory(), "modules"))) for d in drivers(): p = d.modules_dir() if p: paths.append(p) paths.extend([ util.abs_path( os.path.join(self._config.scenario.ephemeral_directory, "library")), util.abs_path( os.path.join(self._config.project_directory, "library")), util.abs_path( os.path.join( os.path.expanduser("~"), ".ansible", "plugins", "modules", )), "/usr/share/ansible/plugins/modules", ]) return [path for path in paths if path is not None]
def driver(self): driver_name = self._get_driver_name() driver = None driver = api.drivers(config=self)[driver_name] driver.name = driver_name return driver
def reset(ctx, scenario_name): # pragma: no cover """Reset molecule temporary folders.""" args = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) command_args = {"subcommand": subcommand} base.execute_cmdline_scenarios(scenario_name, args, command_args) for driver in drivers(): driver.reset()
def drivers(ctx, format): # pragma: no cover """List drivers.""" drivers = [[x] for x in api.drivers()] headers = ['name'] table_format = 'simple' if format == 'plain': headers = [] table_format = format _print_tabulate_data(headers, drivers, table_format)
def drivers(ctx, format): # pragma: no cover """List drivers.""" drivers = [[x] for x in api.drivers()] headers = ["name"] table_format = "simple" if format == "plain": headers = [] table_format = format _print_tabulate_data(headers, drivers, table_format)
def _version_string() -> str: v = pkg_resources.parse_version(molecule.__version__) color = "bright_yellow" if v.is_prerelease else "green" # type: ignore msg = "molecule %s\n" % _colorize(molecule.__version__, color) details = f" ansible:{ansible_version()} python:{sys.version_info[0]}.{sys.version_info[1]}" for driver in drivers(): details += f"\n {driver}:{driver.version} from {driver.module}" msg += _colorize(details, "bright_black") return msg
def execute(self): """ Execute the actions necessary to perform a `molecule init scenario` and \ returns None. :return: None """ scenario_name = self._command_args["scenario_name"] role_name = os.getcwd().split(os.sep)[-1] role_directory = util.abs_path(os.path.join(os.getcwd(), os.pardir)) msg = "Initializing new scenario {}...".format(scenario_name) LOG.info(msg) molecule_directory = config.molecule_directory( os.path.join(role_directory, role_name) ) scenario_directory = os.path.join(molecule_directory, scenario_name) if os.path.isdir(scenario_directory): msg = ( "The directory molecule/{} exists. " "Cannot create new scenario." ).format(scenario_name) util.sysexit_with_message(msg) driver_template = api.drivers()[ self._command_args["driver_name"] ].template_dir() if "driver_template" in self._command_args: self._validate_template_dir(self._command_args["driver_template"]) cli_driver_template = "{driver_template}/{driver_name}".format( **self._command_args ) if os.path.isdir(cli_driver_template): driver_template = cli_driver_template else: LOG.warning( "Driver not found in custom template directory({}), " "using the default template instead".format(cli_driver_template) ) scenario_base_directory = os.path.join(role_directory, role_name) templates = [ driver_template, api.verifiers()[self._command_args["verifier_name"]].template_dir(), ] self._process_templates("molecule", self._command_args, role_directory) for template in templates: self._process_templates( template, self._command_args, scenario_base_directory ) role_directory = os.path.join(role_directory, role_name) msg = "Initialized scenario in {} successfully.".format(scenario_directory) LOG.success(msg)
def pytest_configure(config): # We hide DeprecationWarnings thrown by driver loading because these are # outside our control and worse: they are displayed even on projects that # have no molecule tests at all as pytest_configure() is called during # collection, causing spam. with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=DeprecationWarning) config.option.molecule = {} for driver in map(str, drivers()): config.addinivalue_line( "markers", "{0}: mark test to run only when {0} is available".format( driver), ) config.option.molecule[driver] = {"available": True} # TODO(ssbarnea): extend molecule itself to allow it to report usable drivers if driver == "docker": try: import docker # validate docker connectivity # Default docker value is 60s but we want to fail faster # With parallel execution 5s proved to give errors. c = docker.from_env(timeout=10, version="auto") if not c.ping(): raise Exception("Failed to ping docker server.") except Exception as e: msg = "Molecule {} driver is not available due to: {}.".format( driver, e) if config.option.molecule_unavailable_driver: msg += " We will tag scenarios using it with '{}' marker.".format( config.option.molecule_unavailable_driver) logging.getLogger().warning(msg) config.option.molecule[driver]["available"] = False config.addinivalue_line( "markers", "molecule: mark used by all molecule scenarios") # validate selinux availability if sys.platform == "linux" and os.path.isfile("/etc/selinux/config"): try: import selinux # noqa except Exception: logging.error( "It appears that you are trying to use " "molecule with a Python interpreter that does not have the " "libselinux python bindings installed. These can only be " "installed using your distro package manager and are specific " "to each python version. Common package names: " "libselinux-python python2-libselinux python3-libselinux")
def print_version(ctx, param, value): """Print version information.""" if not value or ctx.resilient_parsing: return v = pkg_resources.parse_version(molecule.__version__) color = "bright_yellow" if v.is_prerelease else "green" msg = f"molecule [{color}]{v}[/] using python [repr.number]{sys.version_info[0]}.{sys.version_info[1]}[/] \n" msg += ( f" [repr.attrib_name]ansible[/][dim]:[/][repr.number]{ansible_version()}[/]" ) for driver in drivers(): msg += f"\n [repr.attrib_name]{str(driver)}[/][dim]:[/][repr.number]{driver.version}[/][dim] from {driver.module}[/]" console.print(msg) ctx.exit()
def execute(self): """ Execute the actions necessary to perform a `molecule init role` and returns None. :return: None """ role_name = self._command_args['role_name'] role_directory = os.getcwd() msg = 'Initializing new role {}...'.format(role_name) LOG.info(msg) if os.path.isdir(role_name): msg = ('The directory {} exists. ' 'Cannot create new role.').format( role_name ) util.sysexit_with_message(msg) try: cmd = ["ansible-galaxy", "init", "-v", "--offline", role_name] subprocess.check_output( cmd, stderr=subprocess.STDOUT, universal_newlines=True ) except Exception as e: util.sysexit_with_message( "Galaxy failed to create role: %s: %s" % (e, e.output) ) scenario_base_directory = os.path.join(role_directory, role_name) templates = [ api.drivers()[self._command_args['driver_name']].template_dir(), api.verifiers()[self._command_args['verifier_name']].template_dir(), ] for template in templates: self._process_templates( template, self._command_args, scenario_base_directory ) self._process_templates('molecule', self._command_args, role_directory) role_directory = os.path.join(role_directory, role_name) msg = 'Initialized role in {} successfully.'.format(role_directory) LOG.success(msg)
def print_version(ctx, param, value): """Print version information.""" if not value or ctx.resilient_parsing: return v = packaging.version.Version(molecule.__version__) color = "bright_yellow" if v.is_prerelease else "green" msg = f"molecule [{color}]{v}[/] using python [repr.number]{sys.version_info[0]}.{sys.version_info[1]}[/] \n" runtime = Runtime() msg += f" [repr.attrib_name]ansible[/][dim]:[/][repr.number]{runtime.version}[/]" for driver in drivers(): msg += f"\n [repr.attrib_name]{str(driver)}[/][dim]:[/][repr.number]{driver.version}[/][dim] from {driver.module}[/]" if driver.required_collections: msg += " requiring collections:" for name, version in driver.required_collections.items(): msg += f" {name}>={version}" console.print(msg) ctx.exit()
def execute(self): """ Execute the actions necessary to perform a `molecule init role` and \ returns None. :return: None """ role_name = self._command_args["role_name"] role_directory = os.getcwd() msg = "Initializing new role {}...".format(role_name) LOG.info(msg) if os.path.isdir(role_name): msg = ("The directory {} exists. " "Cannot create new role.").format( role_name ) util.sysexit_with_message(msg) cmd = ["ansible-galaxy", "init", "-v", "--offline", role_name] result = util.run_command(cmd) if result.returncode != 0: util.sysexit_with_message( "Galaxy failed to create role, returned %s" % result.returncode ) scenario_base_directory = os.path.join(role_directory, role_name) templates = [ api.drivers()[self._command_args["driver_name"]].template_dir(), api.verifiers()[self._command_args["verifier_name"]].template_dir(), ] self._process_templates("molecule", self._command_args, role_directory) for template in templates: self._process_templates( template, self._command_args, scenario_base_directory ) role_directory = os.path.join(role_directory, role_name) msg = "Initialized role in {} successfully.".format(role_directory) LOG.success(msg)
def execute(self): """ Execute the actions necessary to perform a `molecule init role` and returns None. :return: None """ role_name = self._command_args['role_name'] role_directory = os.getcwd() msg = 'Initializing new role {}...'.format(role_name) LOG.info(msg) if os.path.isdir(role_name): msg = ('The directory {} exists. ' 'Cannot create new role.').format(role_name) util.sysexit_with_message(msg) template_directory = '' if 'template' in self._command_args.keys(): template_directory = self._command_args['template'] else: template_directory = 'role' self._process_templates(template_directory, self._command_args, role_directory) scenario_base_directory = os.path.join(role_directory, role_name) templates = [ api.drivers()[self._command_args['driver_name']].template_dir(), api.verifiers()[ self._command_args['verifier_name']].template_dir(), ] for template in templates: self._process_templates(template, self._command_args, scenario_base_directory) self._process_templates('molecule', self._command_args, role_directory) role_directory = os.path.join(role_directory, role_name) msg = 'Initialized role in {} successfully.'.format(role_directory) LOG.success(msg)
def test_driver_is_detected(): """Check that driver is recognized.""" assert "containers" in [str(d) for d in api.drivers()]
def pre_validate_base_schema(env, keep_string): return { 'dependency': { 'type': 'dict', 'schema': { 'name': { 'type': 'string', 'molecule_env_var': True, 'allowed': ['galaxy', 'gilt', 'shell'], } }, }, 'driver': { 'type': 'dict', 'schema': { 'name': { 'type': 'string', 'molecule_env_var': True, 'allowed': api.drivers(), # NOTE(retr0h): Some users use an environment variable to # change the driver name. May add this coercion to rest of # config using allowed validation. 'coerce': (str, functools.partial(coerce_env, env, keep_string)), } }, }, 'lint': { 'type': 'dict', 'schema': { 'name': { 'type': 'string', 'molecule_env_var': True, 'allowed': ['yamllint'], } }, }, 'platforms': { 'type': 'list', 'schema': { 'type': 'dict', 'schema': { 'registry': { 'type': 'dict', 'schema': { 'credentials': { 'type': 'dict', 'schema': {'password': {'type': 'string'}}, } }, } }, }, }, 'provisioner': { 'type': 'dict', 'schema': { 'name': { 'type': 'string', 'molecule_env_var': True, 'allowed': ['ansible'], }, 'lint': { 'type': 'dict', 'schema': { 'name': { 'type': 'string', 'molecule_env_var': True, 'allowed': ['ansible-lint'], } }, }, }, }, 'scenario': { 'type': 'dict', 'schema': {'name': {'type': 'string', 'molecule_env_var': True}}, }, 'verifier': { 'type': 'dict', 'schema': { 'name': { 'type': 'string', 'molecule_env_var': True, 'allowed': api.verifiers(), }, 'lint': { 'type': 'dict', 'schema': { 'name': { 'type': 'string', 'molecule_env_var': True, 'allowed': [ 'flake8', 'pre-commit', 'rubocop', 'yamllint', 'ansible-lint', ], } }, }, }, }, }
def pytest_configure(config): INTERESTING_ENV_VARS = [ "ANSIBLE", "MOLECULE", "DOCKER", "PODMAN", "VAGRANT", "VIRSH", "ZUUL", ] # Add extra information that may be key for debugging failures for p in ["ansible", "molecule"]: config._metadata["Packages"][p] = pkg_resources.get_distribution(p).version # Adds interesting env vars env = "" for k, v in sorted(os.environ.items()): for a in INTERESTING_ENV_VARS: if k.startswith(a): env += f"{k}={v} " config._metadata["env"] = env # We hide DeprecationWarnings thrown by driver loading because these are # outside our control and worse: they are displayed even on projects that # have no molecule tests at all as pytest_configure() is called during # collection, causing spam. with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=DeprecationWarning) config.option.molecule = {} for driver in map(str, drivers()): config.addinivalue_line( "markers", "{0}: mark test to run only when {0} is available".format(driver), ) config.option.molecule[driver] = {"available": True} # TODO(ssbarnea): extend molecule itself to allow it to report usable drivers if driver == "docker": try: import docker # validate docker connectivity # Default docker value is 60s but we want to fail faster # With parallel execution 5s proved to give errors. c = docker.from_env(timeout=10, version="auto") if not c.ping(): raise Exception("Failed to ping docker server.") except Exception as e: msg = "Molecule {} driver is not available due to: {}.".format( driver, e ) if config.option.molecule_unavailable_driver: msg += " We will tag scenarios using it with '{}' marker.".format( config.option.molecule_unavailable_driver ) logging.getLogger().warning(msg) config.option.molecule[driver]["available"] = False config.addinivalue_line( "markers", "molecule: mark used by all molecule scenarios" ) # validate selinux availability if sys.platform == "linux" and os.path.isfile("/etc/selinux/config"): try: import selinux # noqa except Exception: logging.error( "It appears that you are trying to use " "molecule with a Python interpreter that does not have the " "libselinux python bindings installed. These can only be " "installed using your distro package manager and are specific " "to each python version. Common package names: " "libselinux-python python2-libselinux python3-libselinux" )
def execute(self): """ Execute the actions necessary to perform a `molecule init role` and \ returns None. :return: None """ namespace = None role_name = self._command_args["role_name"] role_directory = os.getcwd() # outside collections our tooling needs a namespace. if not os.path.isfile("../galaxy.yml"): name_re = re.compile(r"^[a-z][a-z0-9_]+\.[a-z][a-z0-9_]+$") if not name_re.match(role_name): util.sysexit_with_message( "Outside collections you must mention role " "namespace like: molecule init role 'acme.myrole'. Be sure " "you use only lowercase characters and underlines. See https://galaxy.ansible.com/docs/contributing/creating_role.html" ) namespace, role_name = role_name.split(".") msg = f"Initializing new role {role_name}..." LOG.info(msg) if os.path.isdir(role_name): msg = f"The directory {role_name} exists. Cannot create new role." util.sysexit_with_message(msg) cmd = ["ansible-galaxy", "init", "-v", "--offline", role_name] result = util.run_command(cmd) if result.returncode != 0: util.sysexit_with_message( f"Galaxy failed to create role, returned {result.returncode!s}" ) if namespace: # we need to inject namespace info into meta/main.yml cmd = [ "ansible", "localhost", "-o", # one line output "-m", "lineinfile", "-a", f'path={role_name}/meta/main.yml line=" namespace: {namespace}" insertafter=" author: your name"', ] util.run_command(cmd, check=True) scenario_base_directory = os.path.join(role_directory, role_name) templates = [ api.drivers()[self._command_args["driver_name"]].template_dir(), api.verifiers()[ self._command_args["verifier_name"]].template_dir(), ] self._process_templates("molecule", { **self._command_args, "role_name": role_name }, role_directory) for template in templates: self._process_templates(template, self._command_args, scenario_base_directory) role_directory = os.path.join(role_directory, role_name) msg = f"Initialized role in {role_directory} successfully." LOG.info(msg)
def pytest_configure(config): """Pytest hook for loading our specific configuration.""" interesting_env_vars = [ "ANSIBLE", "MOLECULE", "DOCKER", "PODMAN", "VAGRANT", "VIRSH", "ZUUL", ] # Add extra information that may be key for debugging failures for package in ["molecule"]: config._metadata["Packages"][package] = pkg_resources.get_distribution( package ).version if "Tools" not in config._metadata: config._metadata["Tools"] = {} config._metadata["Tools"]["ansible"] = str(ansible_version()) # Adds interesting env vars env = "" for key, value in sorted(os.environ.items()): for var_name in interesting_env_vars: if key.startswith(var_name): env += f"{key}={value} " config._metadata["env"] = env # We hide DeprecationWarnings thrown by driver loading because these are # outside our control and worse: they are displayed even on projects that # have no molecule tests at all as pytest_configure() is called during # collection, causing spam. with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=DeprecationWarning) config.option.molecule = {} for driver in map(str, drivers()): config.addinivalue_line( "markers", "{0}: mark test to run only when {0} is available".format(driver), ) config.option.molecule[driver] = {"available": True} config.addinivalue_line( "markers", "no_driver: mark used for scenarios that do not contain driver info", ) config.addinivalue_line( "markers", "molecule: mark used by all molecule scenarios" ) # validate selinux availability if sys.platform == "linux" and os.path.isfile("/etc/selinux/config"): try: import selinux # noqa pylint: disable=unused-import,import-error,import-outside-toplevel except ImportError: logging.error( "It appears that you are trying to use " "molecule with a Python interpreter that does not have the " "libselinux python bindings installed. These can only be " "installed using your distro package manager and are specific " "to each python version. Common package names: " "libselinux-python python2-libselinux python3-libselinux" )
def test_driver_is_detected(): driver_name = __name__.split(".")[0].split("_")[-1] assert driver_name in [str(d) for d in api.drivers()]
def test_api_molecule_drivers_as_attributes(): results = api.drivers() assert hasattr(results, 'delegated') assert isinstance(results.delegated, api.Driver)
def test_driver_is_detected(): assert "lxd" in [str(d) for d in api.drivers()]
:return: None """ @base.click_command_ex() @click.pass_context @click.option( "--scenario-name", "-s", default=base.MOLECULE_DEFAULT_SCENARIO_NAME, help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})", ) @click.option( "--driver-name", "-d", type=click.Choice([str(s) for s in drivers()]), help=f"Name of driver to use. ({DEFAULT_DRIVER})", ) @click.option( "--all/--no-all", "__all", default=False, help="Test all scenarios. Default is False.", ) @click.option( "--destroy", type=click.Choice(["always", "never"]), default="always", help=("The destroy strategy used at the conclusion of a " "Molecule run (always)."), ) @click.option(
def pre_validate_base_schema(env: MutableMapping, keep_string: str): """Pre-validate base schema.""" return { "dependency": { "type": "dict", "schema": { "name": { "type": "string", "molecule_env_var": True, "allowed": ["galaxy", "shell"], } }, }, "driver": { "type": "dict", "schema": { "name": { "type": "string", "molecule_env_var": True, "allowed": api.drivers(), # NOTE(retr0h): Some users use an environment variable to # change the driver name. May add this coercion to rest of # config using allowed validation. "coerce": (str, functools.partial(coerce_env, env, keep_string)), } }, }, "lint": {"type": "string"}, "platforms": { "type": "list", "schema": { "type": "dict", "schema": { "registry": { "type": "dict", "schema": { "credentials": { "type": "dict", "schema": {"password": {"type": "string"}}, } }, } }, }, }, "provisioner": { "type": "dict", "schema": { "name": { "type": "string", "molecule_env_var": True, "allowed": ["ansible"], }, }, }, "scenario": {"type": "dict", "schema": {"name": {"molecule_env_var": True}}}, "verifier": { "type": "dict", "schema": { "name": { "type": "string", "molecule_env_var": True, "allowed": api.verifiers(), }, }, }, }
def test_driver_is_detected(DRIVER): """Asserts that molecule recognizes the driver.""" assert DRIVER in [str(d) for d in api.drivers()]
util.sysexit_with_message(msg) return value @click.command() @click.pass_context @click.option( '--dependency-name', type=click.Choice(['galaxy']), default='galaxy', help='Name of dependency to initialize. (galaxy)', ) @click.option( '--driver-name', '-d', type=click.Choice(api.drivers()), default='docker', help='Name of driver to initialize. (docker)', ) @click.option( '--lint-name', type=click.Choice(['yamllint']), default='yamllint', help='Name of lint to initialize. (ansible-lint)', ) @click.option( '--provisioner-name', type=click.Choice(['ansible']), default='ansible', help='Name of provisioner to initialize. (ansible)', )