Example #1
0
def test_kubernetes_discovery(cfgdir, mocker):
    """
    Testing Kubernetes initializaion.

    Is fabric creating Stub by default?
    Does Kubernetes return []?
    """
    k8s = os.path.join(
        os.path.dirname(__file__),
        "..",
        "fixture",
        "k8s.cloud.yaml",
    )
    k8s_data = os.path.join(
        os.path.dirname(__file__),
        "..",
        "fixture",
        "k8s_discovery_data.yaml",
    )
    k8s_pods = []
    with open(k8s_data, "r") as data:
        k8s_pods = yaml.safe_load(data)
    cloud = CloudSelect(cfgdir)
    profile = cloud.configuration_read()
    profile = cloud.merge(profile, cloud.configuration_read(k8s))
    args = cloud.parse_args([])
    cloud.fabric(profile, args)
    assert Container.discovery().__class__.__name__ == "Kubernetes"
    mocker.patch.object(Kubernetes, "get_pods", return_value=k8s_pods)
    representation, instances = Container.discovery().run()
    assert len(instances) == 2
    assert representation == [36, 37, 21, 7, 7, 11]
Example #2
0
def test_options_errors(caplog, cfgdir):
    """Test error messages when options block is incorrect."""
    caplog.set_level(logging.INFO)
    configuration = os.path.join(
        os.path.dirname(__file__),
        "..",
        "fixture",
        "metadata.json",
    )

    logging.Logger.manager.loggerDict.clear()
    cloud = CloudSelect(cfgdir)
    configuration = cloud.configuration_read()
    args = cloud.parse_args([])
    configuration["group"] = {"type": "simple"}
    cloud.fabric(configuration, args)
    assert isinstance(Container.group(), Simple)

    caplog.clear()
    Container.options("option")
    assert len(caplog.records) == 1
    assert (caplog.records[0].getMessage() ==
            "option: 'options' block not found in {'type': 'simple'}")

    caplog.clear()
    configuration["group"]["options"] = 123
    Container.options("option")
    assert len(caplog.records) == 1
    assert (caplog.records[0].msg ==
            "%s: 'options' block should be list of dictionaries in %s")
Example #3
0
 def get_option(selected):
     """Get option block for report."""
     # get first instance
     # assume that all instances match the same group/pattern
     instance = next(iter(selected), None)
     if instance:
         return Container.options("option", instance.metadata)
     return Container.options("option")
Example #4
0
 def options(self, name, metadata=None):
     """Get plugin/block options."""
     group = Container.group()
     base = copy.deepcopy(Container.config().get(name, {}))
     override = group.run(name, metadata)
     if override is None:
         return base
     if override == {}:
         return {}
     return self.merge(base, override)
Example #5
0
def test_bastion_initialization(cfgdir):
    """Assert plugin initialization."""
    cloud = CloudSelect(cfgdir)
    configuration = cloud.configuration_read()
    args = cloud.parse_args([])
    configuration["pathfinder"] = {"type": "bastion"}
    cloud.fabric(configuration, args)

    assert isinstance(Container.pathfinder(), Bastion)

    result = Container.pathfinder().run(INSTANCE, [INSTANCE])
    assert result.jumphost is None
Example #6
0
    def profile_process(self):
        """Run selection process for the specific profile."""
        discovery = Container.discovery()
        pathfinder = Container.pathfinder()
        profile_name = Container.args().profile
        report = Container.report()

        self.logger.debug("Process profile '%s'", profile_name)
        representation_maximum_field_length, instances = discovery.run()
        if not instances:
            sys.exit("Error: No instances found")
        selected = self.fzf_select(representation_maximum_field_length, instances)
        selected = [pathfinder.run(i, instances) for i in selected]
        return report.run(selected)
Example #7
0
def test_stub_pathfinder(cfgdir):
    """
    Testing Stub initializaion.

    Is fabric creating Stub by default?
    Does Stub return {}?
    Is Stub singleton?
    """
    cloud = CloudSelect(cfgdir)
    # Read shared part
    profile = cloud.configuration_read()
    args = cloud.parse_args([])
    cloud.fabric(profile, args)
    assert Container.pathfinder().__class__.__name__ == "Stub"
    assert Container.pathfinder().run(INSTANCE, [INSTANCE]) == INSTANCE
    assert Container.pathfinder() == Container.pathfinder()
Example #8
0
def test_stub_discovery(cfgdir):
    """
    Testing Stub initializaion.

    Is fabric creating Stub by default?
    Does Stub return []?
    Is Stub singleton?
    """
    cloud = CloudSelect(cfgdir)
    # Read shared part
    profile = cloud.configuration_read()
    args = cloud.parse_args([])
    cloud.fabric(profile, args)
    assert Container.discovery().__class__.__name__ == "Stub"
    assert Container.discovery().run() == []
    assert Container.discovery() == Container.discovery()
Example #9
0
    def instances(self):
        """Collect Hetzner instances."""
        # Array with maximum field length for each element in representation
        fields_length = []
        for i in self.find():
            instance_id = i["id"]
            metadata = i
            config = Container.options("discovery", metadata)
            ip = self.get_ip(i)
            key = self.get_key(config)
            port = self.get_port(config)
            user = self.get_user(config)

            representation = [instance_id, ip]
            self.enrich_representation(representation, metadata)

            # Update maximum field length
            for idx, value in enumerate(representation):
                if idx >= len(fields_length):
                    fields_length.append(len(value))
                else:
                    fields_length[idx] = max(fields_length[idx], len(value))

            instance = CloudInstance(
                instance_id,
                ip,
                None,
                metadata,
                representation,
                key,
                user,
                port,
            )
            yield instance
        yield fields_length
Example #10
0
def test_stub_group(tmpdir):
    """
    Testing Stub initializaion.

    Is fabric creating Stub by default?
    Does Stub return {}?
    Is Stub singleton?
    """
    cloud = CloudSelect(tmpdir)
    # Read shared part
    profile = cloud.configuration_read()
    args = cloud.parse_args([])
    cloud.fabric(profile, args)
    assert Container.group().__class__.__name__ == "Stub"
    assert Container.group().run("aws", METADATA) is None
    assert Container.group() == Container.group()
Example #11
0
 def run(self, instance, instances):
     """Enrich instance with jumphost if any."""
     if not instance:
         raise ValueError("instance must be something, not None")
     arguments = Container.options("pathfinder", instance.metadata)
     if arguments:
         jumphost = None
         if "host" in arguments:
             jumphost = CloudInstance(
                 -1,
                 arguments["host"],
                 None,
                 {},
                 [],
                 arguments.get("key"),
                 arguments.get("user"),
                 arguments.get("port", 22),
             )
         elif "metadata" in arguments and ":" in arguments["metadata"]:
             key, pattern = arguments["metadata"].split(":", 1)
             for i in instances:
                 point = self.find_jumphost(key, pattern, i)
                 if point:
                     if point == instance:
                         break  # We don't like to add bastion for itself
                     jumphost = point
                     break
         if jumphost:
             return attr.evolve(instance, jumphost=jumphost)
     return instance
Example #12
0
def test_bastion_behaviour(cfgdir):
    """Assert bastion returning correct result."""
    cloud = CloudSelect(cfgdir)
    configuration = cloud.configuration_read()
    args = cloud.parse_args([])
    configuration["pathfinder"] = {
        "type": "bastion",
        "host": "my-bastion-hostname"
    }
    cloud.fabric(configuration, args)

    assert isinstance(Container.pathfinder(), Bastion)

    result = Container.pathfinder().run(INSTANCE, [INSTANCE])

    assert isinstance(result.jumphost, CloudInstance)
    assert result.jumphost.host == "my-bastion-hostname"
Example #13
0
    def complete(self, cline, cpoint):
        """List profiles for shell completion."""
        configpath = Container.configpath()
        extensions = Container.extensions()

        prefix = cline[0:cpoint].partition(" ")[-1]
        self.logger.debug(
            "Complete line %s, point %s, prefix %s", cline, cpoint, prefix,
        )
        profiles = []
        for profile in os.listdir(configpath):
            for extension in extensions:
                if profile.endswith(".{}".format(extension)):
                    name = profile.replace(".{}".format(extension), "")
                    if name.startswith(prefix):
                        profiles.append(name)
        print("\n".join(sorted(set(profiles))))
Example #14
0
    def profile_list(self):
        """List available profiles."""
        configpath = Container.configpath()
        extensions = Container.extensions()

        self.logger.debug("List all available profiles from %s", configpath)
        profiles = []
        print("CloudSelect profiles:")

        for profile in os.listdir(configpath):
            for extension in extensions:
                if profile.endswith(".{}".format(extension)):
                    profiles.append(
                        "- {}".format(profile.replace(".{}".format(extension), "")),
                    )
        if profiles:
            print("\n".join(sorted(set(profiles))))
        else:
            print("- NO PROFILES -")
Example #15
0
 def reporter_list():
     """List available reporters."""
     empty = True
     print("CloudSelect reporters:")
     for reporter in sorted(
         Container.config().get("plugin", {}).get("report", {}).keys(),
     ):
         empty = False
         print("- {}".format(reporter))
     if empty:
         print("- NO REPORTERS -")
Example #16
0
    def fabric(self, configuration, args):
        """Load discovery, group, pathfinder, report plugins."""
        if args.verbose:
            if args.verbose == 1:
                configuration.get("log", {}).get("root",
                                                 {})["level"] = logging.INFO
            elif args.verbose > 1:
                configuration.get("log", {}).get("root",
                                                 {})["level"] = logging.DEBUG
        dictConfig(configuration.get("log", {}))
        self.log = logging.getLogger("cloudselect.CloudSelect")
        self.log.debug("Logging is initialized")
        self.log.debug(
            "Configuration:\n%s",
            json.dumps(configuration,
                       sort_keys=True,
                       indent=4,
                       separators=(",", ": ")),
        )
        Container.args = providers.Object(args)
        Container.config = providers.Configuration(name="config",
                                                   default=configuration)
        Container.configpath = providers.Object(self.configpath)
        Container.extensions = providers.Object(self.extensions)
        Container.options = providers.Callable(self.options)
        Container.selector = providers.Singleton(Selector)

        Container.discovery = self.fabric_load_plugin(
            configuration,
            "discovery",
            DiscoveryServiceProvider,
            DiscoveryStub,
        )
        Container.group = self.fabric_load_plugin(
            configuration,
            "group",
            GroupServiceProvider,
            GroupStub,
        )
        Container.pathfinder = self.fabric_load_plugin(
            configuration,
            "pathfinder",
            PathFinderServiceProvider,
            PathFinderStub,
        )
        Container.report = self.fabric_load_plugin(
            configuration,
            "report",
            ReportServiceProvider,
            ReportStub,
            args.reporter,
        )

        return Container.selector()
Example #17
0
    def select(self):
        """Entry point. Select instances."""
        args = Container.args()
        configpath = Container.configpath()
        extensions = Container.extensions()

        if args.edit is None or args.edit:
            if args.edit is None:
                configuration = os.path.join(configpath, "{}".format(extensions[0]))
                return self.edit(configuration)
            for extension in extensions:
                profile = os.path.join(configpath, "{}.{}".format(args.edit, extension))
                if os.path.isfile(profile):
                    return self.edit(profile)
            print("Profile '{}' does not exist".format(args.edit), file=sys.stderr)
            return self.logger.error("Profile '%s' does not exist", args.edit)
        if args.reporter is None:
            return self.reporter_list()
        if not args.profile:
            return self.profile_list()
        return self.profile_process()
Example #18
0
def test_select_single(cfgdir):
    """Testing that Selector automaticaly select the only one available instance."""
    profile = os.path.join(os.path.dirname(__file__), "fixture", "single.cloud.json")
    cloud = CloudSelect(cfgdir)
    args = cloud.parse_args([profile])
    configuration = cloud.configuration_read()
    configuration = cloud.merge(configuration, cloud.configuration_read(args.profile))
    assert args.profile == profile
    selector = cloud.fabric(configuration, args)
    assert isinstance(Container.discovery(), Local)
    report = selector.select()
    assert len(report["instances"]) == 1
    assert report["instances"][0]["host"] == "my.cloud.instance"
Example #19
0
def test_options_regex(cfgdir):
    """Test regex against metadata mock."""
    metadata = os.path.join(os.path.dirname(__file__), "..", "fixture",
                            "metadata.json")

    cloud = CloudSelect(cfgdir)
    configuration = cloud.configuration_read()
    args = cloud.parse_args([])
    configuration["group"] = {
        "type": "simple",
        "options": [{
            "match": "Tags.Name:my-app.abc",
            "option": {}
        }],
    }
    configuration["option"] = {"ssh": "-t"}
    cloud.fabric(configuration, args)
    assert isinstance(Container.group(), Simple)

    with open(metadata) as json_file:
        data = json.load(json_file)
        assert Container.options("option") == {"ssh": "-t"}
        assert Container.options("option", data) == {}
Example #20
0
def test_select_empty(cfgdir):
    """Testing that Selector exits if there is no any instances."""
    profile = os.path.join(os.path.dirname(__file__), "fixture", "empty.cloud.json")
    cloud = CloudSelect(cfgdir)
    args = cloud.parse_args([profile])
    configuration = cloud.configuration_read()
    configuration = cloud.merge(configuration, cloud.configuration_read(args.profile))
    assert args.profile == profile
    selector = cloud.fabric(configuration, args)
    assert isinstance(Container.discovery(), Local)
    with pytest.raises(SystemExit) as pytest_wrapped_e:
        selector.select()
    assert pytest_wrapped_e.type == SystemExit
    assert pytest_wrapped_e.value.code == "Error: No instances found"
Example #21
0
    def get_ip(self, instance, config):
        """Get instance IP."""
        profile_name = Container.args().profile
        region = instance["Placement"]["AvailabilityZone"][:-1]

        ip = self.get_option(config.get("ip", {}), None, profile_name, region)
        if ip == "public":
            return instance.get("PublicIpAddress", "")
        if ip == "private":
            return instance.get("PrivateIpAddress", "")
        if ip == "public_private":
            return instance.get("PublicIpAddress", instance.get("PrivateIpAddress", ""))
        if ip == "private_public":
            return instance.get("PrivateIpAddress", instance.get("PublicIpAddress", ""))

        return instance.get("PublicIpAddress", instance.get("PrivateIpAddress", ""))
Example #22
0
 def config():
     """Return selector configuration."""
     return Container.config().get("option", {})
Example #23
0
 def config():
     """Return discovery configuration."""
     return Container.config().get("discovery", {})
Example #24
0
 def config():
     """Return group configuration."""
     return Container.config().get("group", {})
Example #25
0
def test_options(cfgdir):
    """
    Test options behaviour of simple plugin.

    It should returns {} if there is no options.
    It should returns shared dictionary if there is no any matched filters.
    It should returns clarified dictionary if there is matched filter.
    """
    cloud = CloudSelect(cfgdir)
    configuration = cloud.configuration_read()
    args = cloud.parse_args([])
    configuration["group"] = {"type": "simple"}
    cloud.fabric(configuration, args)

    assert isinstance(Container.group(), Simple)

    assert Container.options("test") == {}
    assert Container.options("plugin") == configuration["plugin"]
    assert Container.options("log") == configuration["log"]

    assert Container.options("option") == {}
    options_a = {"ssh": "-t", "ssh_command": "sudo -i"}
    options_b = {"ssh": "-t", "ssh_command": "su"}
    options_c = {"ssh": "-t", "ssh_command": None}
    configuration["option"] = options_a
    assert Container.options("option") == options_a

    configuration["group"]["options"] = [{"match": "x:y", "option": options_a}]
    assert Container.options("option") == options_a
    assert Container.options("option", {"a": {"b": "c123"}}) == options_a

    configuration["group"]["options"].append({
        "match": "a.b:c123",
        "option": options_b
    })
    assert Container.options("option") == options_a
    assert Container.options("option", {"a": {"b": "c123"}}) == options_b

    configuration["group"]["options"].append(
        {
            "match": "a.b:c(.*3|111)",
            "option": options_c
        }, )
    assert Container.options("option") == options_a
    assert Container.options("option", {"a": {"b": "c1nnnn3"}}) == options_c
    assert Container.options("option", {"a": {"b": "c111111"}}) == options_c
    assert Container.options("option", {"a": {"b": "c112111"}}) == options_a
Example #26
0
 def get_key(self, host):
     """Get key for ssh host."""
     self.logger.debug("Search SSH key for %s", host)
     config = Container.options("discovery", host)
     return (config.get("key") or config.get("key", {}).get(host)
             or config.get("key", {}).get("*"))
Example #27
0
 def get_user(self, host):
     """Get user for SSH host."""
     self.logger.debug("Search user for %s", host)
     config = Container.options("discovery", host)
     return (config.get("user") or config.get("user", {}).get(host)
             or config.get("user", {}).get("*"))