Esempio n. 1
0
    def check(self):
        """
        Check: Runs the check script.
        """
        # 1. Load existing configuration
        state = self._load_state()
        if not self._load_state():
            raise DisallowedOperationException(
                "Called check but found no state at %s.  Call deploy first." %
                (self.state_file))
        logger.debug("Loaded state: %s", state)

        network = self.client.network.get(state["network"])
        if not network:
            raise DisallowedOperationException("Network %s not found!" %
                                               state["network"])
        service = self.client.service.get(network, state["service"])
        if not service:
            raise DisallowedOperationException("Service %s not found!" %
                                               state["service"])
        instances = self.client.service.get_instances(service)
        if not instances:
            raise DisallowedOperationException(
                "No running instances in service %s!" % (state["service"]))
        if len(instances) > 1:
            raise DisallowedOperationException(
                "More than one running instance in service %s!" %
                (state["service"]))
        public_ip = instances[0].public_ip
        # 2. Run ./check <ssh_user> <ssh_key_path>
        return subprocess.run([
            self.config.get_check_script_path(), state["ssh_username"],
            public_ip, self.private_key_path
        ],
                              check=True)
Esempio n. 2
0
 def try_run(client, cmd):
     logger.info("running '%s'", cmd)
     _, stdout, stderr = client.exec_command(cmd)
     exit_status = stdout.channel.recv_exit_status()
     if exit_status:
         raise Exception(
             "Failed to delete image build user on image: %s.  "
             "Exit code: %s." % (stderr.read(), exit_status))
     logger.debug("Stdout: %s", stdout.read())
Esempio n. 3
0
 def from_file(cls, blueprint_filename):
     """
     Loads the blueprint from a file.
     """
     logger.debug("Creating blueprint from file: %s", blueprint_filename)
     blueprint_path = os.path.dirname(blueprint_filename)
     with open(blueprint_filename, 'r') as stream:
         blueprint = stream.read()
     return cls(blueprint, blueprint_path)
Esempio n. 4
0
 def __init__(self, blueprint, blueprint_path="./"):
     logger.debug("Creating blueprint from data: %s", blueprint)
     try:
         self.blueprint = yaml.safe_load(blueprint)
     except yaml.YAMLError as exc:
         logger.error("Error parsing blueprint: %s", exc)
         raise exc
     self.blueprint_path = blueprint_path
     if not self.blueprint:
         self.blueprint = {}
Esempio n. 5
0
def load_config_for_cli(client, config):
    """
    Try to load the configuration for an existing
    """
    logger.debug("Running load_config_for_cli on: %s", config)
    config_obj = BlueprintTestConfiguration(config)
    state = get_state(config_obj)
    if not state:
        return None
    network = client.network.get(state["network_name"])
    service = client.service.get(network, state["service_name"])
    return (service, state["ssh_username"], private_key_path(config_obj))
Esempio n. 6
0
def teardown(client, config):
    """
    Destroy all services in this network, and destroy the network.
    """
    logger.debug("Running teardown on: %s", config)
    config_obj = BlueprintTestConfiguration(config)
    state = get_state(config_obj)
    if not state or "network_name" not in state:
        return
    all_services = client.service.list()
    for service in all_services:
        if service.network.name == state["network_name"]:
            client.service.destroy(service)
    network = client.network.get(state["network_name"])
    if network:
        client.network.destroy(network)
    save_state({}, config_obj)
    remove_key_pair(config_obj)
Esempio n. 7
0
def verify(client, config):
    """
    Verify that the instances are behaving as expected.
    """
    logger.debug("Running verify on: %s", config)
    config_obj = BlueprintTestConfiguration(config)
    state = get_state(config_obj)
    blueprint_tester = get_blueprint_tester(
        client, config_obj.get_config_dir(),
        config_obj.get_verify_fixture_type(),
        config_obj.get_verify_fixture_options())
    setup_info = SetupInfo(state["setup_info"]["deployment_info"],
                           state["setup_info"]["blueprint_vars"])
    network = client.network.get(state["network_name"])
    service = client.service.get(network, state["service_name"])
    blueprint_tester.verify(network, service, setup_info)
    logger.info("Verify successful!")
    return (service, state["ssh_username"], private_key_path(config_obj))
Esempio n. 8
0
def call_with_retries(function, retry_count, retry_delay):
    """
    Calls the given function with retries.  Also handles logging on each retry.
    """
    logger.debug("Calling function: %s with retry count: %s, retry_delay: %s",
                 function, retry_count, retry_delay)
    retry = 0
    while True:
        logger.info("Attempt number: %s", retry)
        retry = retry + 1
        try:
            return function()
        # pylint: disable=broad-except
        except Exception as verify_exception:
            logger.info("Verify exception: %s", verify_exception)
            time.sleep(float(retry_delay))
            if retry > int(retry_count):
                logger.info("Exceeded max retries!  Reraising last exception")
                raise
Esempio n. 9
0
 def runtime_scripts(self, template_vars):
     """
     Returns the contents of the provided runtime scripts.  Currently only supports a list with
     one script.
     """
     if not template_vars:
         template_vars = {}
     if len(self.blueprint["initialization"]) > 1:
         raise NotImplementedError("Only one initialization script currently supported")
     def handle_initialization_block(script):
         """
         Handles a single initialization block.  Factored out in case I
         want to support multiple startup scripts.
         """
         full_path = os.path.join(self.blueprint_path,
                                  script["path"])
         with open(full_path) as startup_script_file:
             startup_script = startup_script_file.read()
         template = jinja2.Template(startup_script)
         if "vars" in script:
             for name, opts in script["vars"].items():
                 if opts["required"] and name not in template_vars:
                     raise BlueprintException(
                         "Template Variable: \"%s\" must be set." %
                         (name))
         return template.render(template_vars)
     all_template_vars = [
         name
         for initialization in self.blueprint["initialization"]
         if "vars" in initialization
         for name in initialization["vars"]]
     for template_var in template_vars:
         if template_var not in all_template_vars:
             raise BlueprintException(
                 "Unrecognized Template Variable: \"%s\"." %
                 (template_var))
     rendered_runtime_scripts = handle_initialization_block(self.blueprint["initialization"][0])
     logger.debug("Rendered runtime scripts: %s", rendered_runtime_scripts)
     return rendered_runtime_scripts
Esempio n. 10
0
def get_fitting_instance(instances_client, blueprint):
    """
    Finds the cheapest instance that satisfies the requirements specified in
    the given blueprint.
    """
    # Raise exceptions for anything not supported
    if len(blueprint.disks()) > 1:
        raise NotImplementedError
    for disk in blueprint.disks():
        if parse_storage_size(disk["size"]) > parse_storage_size("8GB"):
            raise NotImplementedError
        if disk["type"] != "standard":
            raise NotImplementedError
        if disk["device_name"] != "/dev/sda1":
            raise NotImplementedError

    # Get the smallest node type that satisfies our requirements
    node_types = instances_client.node_types()
    current_node = None
    for node_type in node_types:

        # First, check if the node even satisfies our requirements
        if (node_type["cpus"] >= blueprint.cpus()
                and node_type["memory"] >= blueprint.memory()):
            logger.debug("Need cpus: %s memory: %s", blueprint.cpus(),
                         blueprint.memory())
            logger.debug("Found satisfying node: %s", node_type)

            # Set our node to this one if it's the first we've found
            if not current_node:
                current_node = node_type

            # Otherwise, if this node is smaller than the one we found before,
            # reset (this is a heuristic for "cheapest").
            elif (node_type["cpus"] <= current_node["cpus"]
                  and node_type["memory"] <= current_node["memory"]):
                current_node = node_type
    return current_node["type"]
Esempio n. 11
0
    def _save(self, mock=False):
        """
        Save: Saves the image after it is configured and checked (must run after configure/check)

        This is internal and not exposed on the command line because you should only save an image
        as part of a full build.  All the other steps here are for debugging and development.
        """
        # 1. Load existing configuration
        state = self._load_state()
        if not self._load_state():
            raise DisallowedOperationException(
                "Called save but found no state at %s.  Call deploy first." %
                (self.state_file))
        logger.debug("Loaded state: %s", state)

        network = self.client.network.get(state["network"])
        if not network:
            raise DisallowedOperationException("Network %s not found!" %
                                               state["network"])
        service = self.client.service.get(network, state["service"])
        if not service:
            raise DisallowedOperationException("Service %s not found!" %
                                               state["service"])
        instances = self.client.service.get_instances(service)
        if not instances:
            raise DisallowedOperationException(
                "No running instances in service %s!" % (state["service"]))
        if len(instances) > 1:
            raise DisallowedOperationException(
                "More than one running instance in service %s!" %
                (state["service"]))
        public_ip = instances[0].public_ip

        # 2. Remove test keys
        if not mock:
            ssh = paramiko.SSHClient()
            ssh_key = paramiko.RSAKey(file_obj=open(state["ssh_private_key"]))
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(hostname=public_ip,
                        username=state["ssh_username"],
                        pkey=ssh_key)

            # This removes all permissions from the temporary user's home directory and sets the
            # account to expire immediately.  We unfortunately can't completely delete this
            # temporary account because we are currently logged in.
            def try_run(client, cmd):
                logger.info("running '%s'", cmd)
                _, stdout, stderr = client.exec_command(cmd)
                exit_status = stdout.channel.recv_exit_status()
                if exit_status:
                    raise Exception(
                        "Failed to delete image build user on image: %s.  "
                        "Exit code: %s." % (stderr.read(), exit_status))
                logger.debug("Stdout: %s", stdout.read())

            try_run(ssh, 'cd /tmp')
            try_run(ssh, 'sudo chmod -R 000 /home/%s/' % state["ssh_username"])
            try_run(ssh,
                    'sudo usermod --expiredate 1 %s' % state["ssh_username"])
            logger.info("Deleted test user: %s", state["ssh_username"])
            ssh.close()

        # 3. Save the image with the correct name
        logger.debug("Saving service %s with name: %s", service.name,
                     self.config.get_image_name())
        image = self.client.image.create(self.config.get_image_name(), service)
        logger.info("Image saved with name: %s", self.config.get_image_name())

        return image
Esempio n. 12
0
def setup(client, config):
    """
    Create all the boilerplate to spin up the service, and the service itself.
    """
    logger.debug("Running setup to test: %s", config)
    config_obj = BlueprintTestConfiguration(config)
    state = get_state(config_obj)
    if state:
        raise DisallowedOperationException("Found non empty state file: %s" %
                                           state)
    network_name = generate_unique_name("test-network")
    service_name = generate_unique_name("test-service")
    key_pair = generate_ssh_keypair()
    state = {
        "network_name": network_name,
        "service_name": service_name,
        "public_key": key_pair.public_key,
        "private_key": key_pair.private_key
    }
    logger.debug("Saving state: %s now in case something fails", state)
    save_state(state, config_obj)
    save_key_pair(key_pair, config_obj)

    logger.debug("Creating test network: %s", network_name)
    network = client.network.create(network_name, NETWORK_BLUEPRINT)

    logger.debug("Calling the pre service setup in test fixture")
    blueprint_tester = get_blueprint_tester(
        client, config_obj.get_config_dir(),
        config_obj.get_create_fixture_type(),
        config_obj.get_create_fixture_options())
    setup_info = blueprint_tester.setup_before_tested_service(network)
    if not isinstance(setup_info, SetupInfo):
        raise DisallowedOperationException(
            "Test fixture must return cloudless.testutils.fixture.SetupInfo object!"
            "Found: %s" % setup_info)
    state["setup_info"] = {
        "deployment_info": setup_info.deployment_info,
        "blueprint_vars": setup_info.blueprint_vars,
    }
    state["ssh_username"] = "******"
    logger.debug("Saving full state: %s", state)
    save_state(state, config_obj)

    # Add SSH key to the instance using reserved variables
    if "cloudless_test_framework_ssh_key" in setup_info.blueprint_vars:
        raise DisallowedOperationException(
            "cloudless_test_framework_ssh_key is a parameter reserved by the test framework "
            "and cannot be returned by the test fixture.  Found: %s" %
            (setup_info.blueprint_vars))
    setup_info.blueprint_vars[
        "cloudless_test_framework_ssh_key"] = key_pair.public_key
    if "cloudless_test_framework_ssh_username" in setup_info.blueprint_vars:
        raise DisallowedOperationException(
            "cloudless_test_framework_ssh_username is a parameter reserved by the test "
            "framework and cannot be returned by the test fixture.  Found: %s"
            % (setup_info.blueprint_vars))
    setup_info.blueprint_vars["cloudless_test_framework_ssh_username"] = state[
        "ssh_username"]

    logger.debug("Creating services using the blueprint under test")
    service = client.service.create(network,
                                    service_name,
                                    config_obj.get_blueprint_path(),
                                    setup_info.blueprint_vars,
                                    count=config_obj.get_count())

    logger.debug("Calling the post service setup in test fixture")
    blueprint_tester.setup_after_tested_service(network, service, setup_info)

    logger.debug("Allowing SSH to test service")
    internet = CidrBlock("0.0.0.0/0")
    client.paths.add(internet, service, 22)

    logger.debug("Test service instances: %s",
                 client.service.get_instances(service))
    return (service, state["ssh_username"], private_key_path(config_obj))