예제 #1
0
def test_knot_errors(cleanup_repos):
    ck.refresh_clients()

    # Test Exceptions on invalid input
    # --------------------------------
    # Assert ck.aws.CloudknotInputError on invalid name
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(name=42)

    # Assert ck.aws.CloudknotInputError on long name
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(name="a" * 56)

    # Assert ck.aws.CloudknotInputError on invalid pars input
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(func=unit_testing_func, pars=42)

    # Assert ck.aws.CloudknotInputError on redundant docker_image and func
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(func=unit_testing_func, docker_image=42)

    # Assert ck.aws.CloudknotInputError on invalid docker_image input
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(docker_image=42)

    # Assert ck.aws.CloudknotInputError on invalid retries
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(retries=0)

    # Assert ck.aws.CloudknotInputError on invalid retries
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(retries=11)

    # Assert ck.aws.CloudknotInputError on invalid memory
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(memory=0)

    # Assert ck.aws.CloudknotInputError on invalid job_def_vcpus
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(job_def_vcpus=-42)

    # Assert ck.aws.CloudknotInputError on invalid priority
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(priority=-42)

    # Assert ck.aws.CloudknotInputError on invalid min_vcpus
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(min_vcpus=-1)

    # Assert ck.aws.CloudknotInputError on invalid desired_vcpus
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(desired_vcpus=-1)

    # Assert ck.aws.CloudknotInputError on invalid max_vcpus
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(max_vcpus=-1)

    # Assert ck.aws.CloudknotInputError on invalid instance_types
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(instance_types=[42])

    # Assert ck.aws.CloudknotInputError on invalid instance_types
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(instance_types="not a valid instance")

    # Assert ck.aws.CloudknotInputError on invalid instance_types
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(instance_types=["not", "a", "valid", "instance"])

    # Assert ck.aws.CloudknotInputError on invalid image_id
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(image_id=42)

    # Assert ck.aws.CloudknotInputError on invalid ec2_key_pair
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(ec2_key_pair=42)

    # Assert ck.aws.CloudknotInputError on invalid volume_size
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(volume_size="string")

    # Assert ck.aws.CloudknotInputError on volume_size < 1
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(volume_size=0)

    # Assert ck.aws.CloudknotInputError when providing both image_id and volume_size
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(image_id="test-string", volume_size=30)
예제 #2
0
def test_knot(cleanup_repos):
    config_file = ck.config.get_config_file()
    knot = None

    ck.refresh_clients()

    try:
        ec2 = ck.aws.clients["ec2"]
        instance = ec2.run_instances(MaxCount=1, MinCount=1)["Instances"][0]
        ec2.create_image(
            BlockDeviceMappings=[{
                "DeviceName": "/dev/xvda",
                "Ebs": {
                    "DeleteOnTermination": True,
                    "VolumeSize": 30,
                    "VolumeType": "gp2",
                    "Encrypted": False,
                },
            }],
            Description="amazon linux ami 2 x86_64 ecs hvm gp2",
            Name="unit-test-ecs-optimized-ami",
            InstanceId=instance["InstanceId"],
        )

        pars = ck.Pars(name=get_testing_name(), use_default_vpc=False)
        name = get_testing_name()

        knot = ck.Knot(name=name, pars=pars, func=unit_testing_func)

        # Now remove the images and repo-uri from the docker-image
        # Forcing the next call to Knot to rebuild and re-push the image.
        config = configparser.ConfigParser()
        with ck.config.rlock:
            config.read(config_file)
            config.set("docker-image " + knot.docker_image.name, "images", "")
            config.set("docker-image " + knot.docker_image.name, "repo-uri",
                       "")
            with open(config_file, "w") as f:
                config.write(f)

        # Re-instantiate the knot so that it retrieves from config
        # with AWS resources that already exist
        knot = ck.Knot(name=name)
        knot.docker_image._clobber_script = True

        # Assert properties are as expected
        assert knot.name == name
        assert knot.knot_name == "knot " + name
        assert knot.pars.name == pars.name
        func_name = unit_testing_func.__name__.replace("_", "-")
        assert knot.docker_image.name == func_name
        assert knot.docker_repo.name == "cloudknot"
        pre = name + "-ck-"
        assert knot.job_definition.name == pre + "jd"

        # Delete the stack using boto3 to check for an error from Pars
        # on reinstantiation
        ck.aws.clients["cloudformation"].delete_stack(StackName=knot.stack_id)

        waiter = ck.aws.clients["cloudformation"].get_waiter(
            "stack_delete_complete")
        waiter.wait(StackName=knot.stack_id, WaiterConfig={"Delay": 10})

        # Confirm error on retrieving the deleted stack
        with pytest.raises(ck.aws.ResourceDoesNotExistException) as e:
            ck.Knot(name=name)

        assert e.value.resource_id == knot.stack_id

        # Confirm that the previous error deleted
        # the stack from the config file
        config_file = ck.config.get_config_file()
        config = configparser.ConfigParser()
        with ck.config.rlock:
            config.read(config_file)
            assert knot.knot_name not in config.sections()

        name = get_testing_name()
        knot = ck.Knot(name=name, func=unit_testing_func)
        knot.docker_image._clobber_script = True
        knot.clobber(clobber_pars=True, clobber_image=True, clobber_repo=True)
        assert knot.clobbered

        # Clobbering twice shouldn't be a problem
        knot.clobber()

        response = ck.aws.clients["cloudformation"].describe_stacks(
            StackName=knot.stack_id)

        status = response.get("Stacks")[0]["StackStatus"]
        assert status in ["DELETE_IN_PROGRESS", "DELETE_COMPLETE"]

        waiter = ck.aws.clients["cloudformation"].get_waiter(
            "stack_delete_complete")
        waiter.wait(StackName=knot.stack_id, WaiterConfig={"Delay": 10})

        # Confirm that clobber deleted the stack from the config file
        config_file = ck.config.get_config_file()
        config = configparser.ConfigParser()
        with ck.config.rlock:
            config.read(config_file)
            assert knot.knot_name not in config.sections()

    except Exception as e:
        try:
            if knot:
                knot.clobber(clobber_pars=True,
                             clobber_image=True,
                             clobber_repo=True)
        except Exception:
            pass

        raise e
def test_Knot(cleanup_repos):
    config_file = ck.config.get_config_file()
    knot, knot2 = None, None

    try:
        pars = ck.Pars(name=get_testing_name())

        name = get_testing_name()
        knot = ck.Knot(name=name, pars=pars, func=unit_testing_func)

        # Now remove the images and repo-uri from the docker-image
        # Forcing the next call to Knot to rebuild and re-push the image.
        config = configparser.ConfigParser()
        with ck.config.rlock:
            config.read(config_file)
            config.set('docker-image ' + knot.docker_image.name, 'images', '')
            config.set('docker-image ' + knot.docker_image.name, 'repo-uri',
                       '')
            with open(config_file, 'w') as f:
                config.write(f)

        # Re-instantiate the knot so that it retrieves from config
        # with AWS resources that already exist
        knot = ck.Knot(name=name)

        # Assert properties are as expected
        assert knot.name == name
        assert knot.knot_name == 'knot ' + name
        assert knot.pars.name == pars.name
        assert knot.docker_image.name == unit_testing_func.__name__
        assert knot.docker_repo.name == 'cloudknot'
        pre = name + '-cloudknot-'
        assert knot.job_definition.name == pre + 'job-definition'
        assert knot.job_queue.name == pre + 'job-queue'
        assert knot.compute_environment.name == pre + 'compute-environment'

        # Now remove the knot section from config file
        config = configparser.ConfigParser()
        with ck.config.rlock:
            config.read(config_file)
            config.remove_section('knot ' + name)
            with open(config_file, 'w') as f:
                config.write(f)

        # And re-instantiate by supplying resource names
        knot2 = ck.Knot(name=name,
                        pars=knot.pars,
                        docker_image=knot.docker_image,
                        job_definition_name=knot.job_definition.name,
                        compute_environment_name=knot.compute_environment.name,
                        job_queue_name=knot.job_queue.name)

        # Assert properties are as expected
        assert knot2.name == name
        assert knot2.knot_name == 'knot ' + name
        assert knot2.pars.name == pars.name
        assert knot2.docker_image.name == unit_testing_func.__name__
        assert knot2.docker_repo is None
        assert knot2.job_definition.name == pre + 'job-definition'
        assert knot2.job_queue.name == pre + 'job-queue'
        assert knot2.compute_environment.name == pre + 'compute-environment'

        knot2.clobber(clobber_pars=True, clobber_image=True)
    except Exception as e:
        try:
            if knot2:
                knot2.clobber(clobber_pars=True, clobber_image=True)
            elif knot:
                knot.clobber(clobber_pars=True, clobber_image=True)
        except Exception:
            pass

        raise e

    pars = None
    ce = None
    jd = None
    jq = None
    knot = None

    # The next tests will use the default pars, if it already exists in the
    # config file, we shouldn't delete it when we're done.
    # Otherwise, clobber it
    config = configparser.ConfigParser()
    with ck.config.rlock:
        config.read(config_file)

    clobber_pars = 'pars default' not in config.sections()

    try:
        pars = ck.Pars()

        # Make a job definition for input testing
        jd = ck.aws.JobDefinition(
            name=get_testing_name(),
            job_role=pars.batch_service_role,
            docker_image='ubuntu',
        )

        # Make a compute environment for input testing
        ce = ck.aws.ComputeEnvironment(
            name=get_testing_name(),
            batch_service_role=pars.batch_service_role,
            instance_role=pars.ecs_instance_role,
            vpc=pars.vpc,
            security_group=pars.security_group,
            spot_fleet_role=pars.spot_fleet_role,
        )

        ck.aws.wait_for_compute_environment(arn=ce.arn, name=ce.name)

        # Make a job queue for input testing
        jq = ck.aws.JobQueue(name=get_testing_name(),
                             compute_environments=ce,
                             priority=1)

        with pytest.raises(ValueError):
            knot = ck.Knot(name=get_testing_name(),
                           func=unit_testing_func,
                           job_definition_name=jd.name,
                           job_def_vcpus=42)

        with pytest.raises(ValueError):
            knot = ck.Knot(name=get_testing_name(),
                           func=unit_testing_func,
                           compute_environment_name=ce.name,
                           desired_vcpus=42)

        with pytest.raises(ValueError):
            knot = ck.Knot(name=get_testing_name(),
                           func=unit_testing_func,
                           job_queue_name=jq.name,
                           priority=42)
    finally:
        try:
            if knot:
                knot.clobber()

            for resource in [jq, ce, jd]:
                if resource:
                    resource.clobber()

            if pars and clobber_pars:
                pars.clobber()
        except Exception:
            pass

    # Test Exceptions on invalid input
    # --------------------------------
    # Assert ValueError on invalid name
    with pytest.raises(ValueError):
        ck.Knot(name=42)

    # Assert ValueError on invalid pars input
    with pytest.raises(ValueError):
        ck.Knot(func=unit_testing_func, pars=42)
예제 #4
0
        new_list_of_arg_lists.append(arg_ls)
    return new_list_of_arg_lists


##########################################################################
# This calls the function to attach the access keys to the argument list
args = attach_keys(args)

##########################################################################
# Define the :meth:`Knot` object to run your jobs on. See
# `this example <http://yeatmanlab.github.io/pyAFQ/auto_examples/cloudknot_example.html>`_ for more
# details about the arguments to the object.
knot = ck.Knot(
    name='afq-hcp-tractography-201110-0',
    func=afq_process_subject,
    base_image='python:3.8',
    image_github_installs="https://github.com/yeatmanlab/pyAFQ.git",
    pars_policies=('AmazonS3FullAccess',),
    bid_percentage=100)

##########################################################################
# This launches a process for each combination.
# Because `starmap` is `True`, each list in `args` will be unfolded
# and passed into `afq_process_subject` as arguments.
result_futures = knot.map(args, starmap=True)

##########################################################################
# The following function can be called repeatedly in a jupyter notebook
# to view the progress of jobs::
#
#     knot.view_jobs()
예제 #5
0
def test_knot_errors(cleanup_repos):
    # Test Exceptions on invalid input
    # --------------------------------
    # Assert ck.aws.CloudknotInputError on invalid name
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(name=42)

    # Assert ck.aws.CloudknotInputError on invalid pars input
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(func=unit_testing_func, pars=42)

    # Assert ck.aws.CloudknotInputError on redundant docker_image and func
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(func=unit_testing_func, docker_image=42)

    # Assert ck.aws.CloudknotInputError on invalid docker_image input
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(docker_image=42)

    # Assert ck.aws.CloudknotInputError on invalid retries
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(retries=0)

    # Assert ck.aws.CloudknotInputError on invalid retries
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(retries=11)

    # Assert ck.aws.CloudknotInputError on invalid memory
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(memory=0)

    # Assert ck.aws.CloudknotInputError on invalid job_def_vcpus
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(job_def_vcpus=-42)

    # Assert ck.aws.CloudknotInputError on invalid priority
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(priority=-42)

    # Assert ck.aws.CloudknotInputError on SPOT without bid percentage
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(resource_type="SPOT")

    # Assert ck.aws.CloudknotInputError on invalid min_vcpus
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(min_vcpus=-1)

    # Assert ck.aws.CloudknotInputError on invalid desired_vcpus
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(desired_vcpus=-1)

    # Assert ck.aws.CloudknotInputError on invalid max_vcpus
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(max_vcpus=-1)

    # Assert ck.aws.CloudknotInputError on invalid instance_types
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(instance_types=[42])

    # Assert ck.aws.CloudknotInputError on invalid instance_types
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(instance_types="not a valid instance")

    # Assert ck.aws.CloudknotInputError on invalid instance_types
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(instance_types=["not", "a", "valid", "instance"])

    # Assert ck.aws.CloudknotInputError on invalid image_id
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(image_id=42)

    # Assert ck.aws.CloudknotInputError on invalid ec2_key_pair
    with pytest.raises(ck.aws.CloudknotInputError):
        ck.Knot(ec2_key_pair=42)
예제 #6
0
def test_knot(cleanup_repos):
    config_file = ck.config.get_config_file()
    knot = None

    try:
        pars = ck.Pars(name=get_testing_name(), use_default_vpc=False)
        name = get_testing_name()

        knot = ck.Knot(name=name, pars=pars, func=unit_testing_func)

        # Now remove the images and repo-uri from the docker-image
        # Forcing the next call to Knot to rebuild and re-push the image.
        config = configparser.ConfigParser()
        with ck.config.rlock:
            config.read(config_file)
            config.set("docker-image " + knot.docker_image.name, "images", "")
            config.set("docker-image " + knot.docker_image.name, "repo-uri",
                       "")
            with open(config_file, "w") as f:
                config.write(f)

        # Re-instantiate the knot so that it retrieves from config
        # with AWS resources that already exist
        knot = ck.Knot(name=name)

        # Assert properties are as expected
        assert knot.name == name
        assert knot.knot_name == "knot " + name
        assert knot.pars.name == pars.name
        func_name = unit_testing_func.__name__.replace("_", "-")
        assert knot.docker_image.name == func_name
        assert knot.docker_repo.name == "cloudknot"
        pre = name + "-cloudknot-"
        assert knot.job_definition.name == pre + "job-definition"

        # Delete the stack using boto3 to check for an error from Pars
        # on reinstantiation
        ck.aws.clients["cloudformation"].delete_stack(StackName=knot.stack_id)

        waiter = ck.aws.clients["cloudformation"].get_waiter(
            "stack_delete_complete")
        waiter.wait(StackName=knot.stack_id, WaiterConfig={"Delay": 10})

        # Confirm error on retrieving the deleted stack
        with pytest.raises(ck.aws.ResourceDoesNotExistException) as e:
            ck.Knot(name=name)

        assert e.value.resource_id == knot.stack_id

        # Confirm that the previous error deleted
        # the stack from the config file
        config_file = ck.config.get_config_file()
        config = configparser.ConfigParser()
        with ck.config.rlock:
            config.read(config_file)
            assert knot.knot_name not in config.sections()

        name = get_testing_name()
        knot = ck.Knot(name=name, func=unit_testing_func)
        knot.clobber(clobber_pars=True, clobber_image=True, clobber_repo=True)
        assert knot.clobbered

        # Clobbering twice shouldn't be a problem
        knot.clobber()

        response = ck.aws.clients["cloudformation"].describe_stacks(
            StackName=knot.stack_id)

        status = response.get("Stacks")[0]["StackStatus"]
        assert status in ["DELETE_IN_PROGRESS", "DELETE_COMPLETE"]

        waiter = ck.aws.clients["cloudformation"].get_waiter(
            "stack_delete_complete")
        waiter.wait(StackName=knot.stack_id, WaiterConfig={"Delay": 10})

        # Confirm that clobber deleted the stack from the config file
        config_file = ck.config.get_config_file()
        config = configparser.ConfigParser()
        with ck.config.rlock:
            config.read(config_file)
            assert knot.knot_name not in config.sections()

    except Exception as e:
        try:
            if knot:
                knot.clobber(clobber_pars=True,
                             clobber_image=True,
                             clobber_repo=True)
        except Exception:
            pass

        raise e