Esempio n. 1
0
def test_run2(client):
    """
    Tests that a docker executor raises HostUnavailableError
    when host is not available when running a container and
    deletes host and port metadata from execution
    """

    with client.application.app_context():
        task, job, execution = JobExecutionFixture.new_defaults()
        _, pool_mock, client_mock = PoolFixture.new_defaults(r"test-.+")

        client_mock.containers.run.side_effect = requests.exceptions.ConnectionError(
            "failed")

        exe = Executor(app=client.application, pool=pool_mock)

        msg = "Connection to host host:1234 failed with error: failed"
        with expect.error_to_happen(HostUnavailableError, message=msg):
            exe.run(
                task,
                job,
                execution,
                "mock-image",
                "latest",
                "command",
                blacklisted_hosts=set(),
            )

        expect(execution.metadata).not_to_include("docker_host")
        expect(execution.metadata).not_to_include("docker_port")
Esempio n. 2
0
def test_run3(client):
    """
    Tests that a docker executor raises RuntimeError if no
    docker_host and docker_port available in execution
    """

    with client.application.app_context():
        task, job, execution = JobExecutionFixture.new_defaults()
        _, pool_mock, _ = PoolFixture.new_defaults(r"test-.+")

        exe = Executor(app=client.application, pool=pool_mock)

        del execution.metadata["docker_host"]
        del execution.metadata["docker_port"]

        msg = "Can't run job without docker_host and docker_port in execution metadata."
        with expect.error_to_happen(RuntimeError, message=msg):
            exe.run(
                task,
                job,
                execution,
                "mock-image",
                "latest",
                "command",
                blacklisted_hosts=set(),
            )

        expect(execution.metadata).not_to_include("docker_host")
        expect(execution.metadata).not_to_include("docker_port")
Esempio n. 3
0
def test_run1(client):
    """Tests that a docker executor can run containers"""

    with client.application.app_context():
        task, job, execution = JobExecutionFixture.new_defaults()
        _, pool_mock, client_mock = PoolFixture.new_defaults(r"test-.+")

        client_mock.containers.run.return_value = MagicMock(id="job_id")

        exe = Executor(app=client.application, pool=pool_mock)
        exe.run(
            task,
            job,
            execution,
            "mock-image",
            "latest",
            "command",
            blacklisted_hosts=set(),
        )

        expect(execution.metadata).to_include("container_id")
        expect(client_mock.containers.run.call_count).to_equal(1)
        client_mock.containers.run.assert_called_with(
            image=f"mock-image:latest",
            environment={},
            command="command",
            detach=True,
            name=f"fastlane-job-{execution.execution_id}",
            extra_hosts={})
Esempio n. 4
0
def test_mark_as_done1(client):
    """
    Tests marking a container as done renames the container
    """

    with client.application.app_context():
        containers = [
            ContainerFixture.new(
                container_id="fastlane-job-123",
                name="fastlane-job-123",
                stdout="stdout",
                stderr="stderr",
            )
        ]
        _, pool_mock, _ = PoolFixture.new_defaults(r"test.+",
                                                   max_running=1,
                                                   containers=containers)

        task, job, execution = JobExecutionFixture.new_defaults(
            container_id="fastlane-job-123")
        executor = Executor(client.application, pool_mock)

        executor.mark_as_done(task, job, execution)

        new_name = f"defunct-fastlane-job-123"
        containers[0].rename.assert_called_with(new_name)
Esempio n. 5
0
def test_get_running2(client):
    """
    Tests getting running containers when some hosts are unavailable
    """

    with client.application.app_context():
        match = re.compile(r"test-.+")
        client_mock = ClientFixture.new([
            ContainerFixture.new(name="fastlane-job-123",
                                 container_id="fastlane-job-123")
        ])
        faulty_client = ClientFixture.new([
            ContainerFixture.new(name="fastlane-job-456",
                                 container_id="fastlane-job-456")
        ])
        faulty_client.containers.list.side_effect = RuntimeError("failed")

        pool_mock = PoolFixture.new(
            clients={
                "host:1234": ("host", 1234, client_mock),
                "host:4567": ("host", 4567, faulty_client),
            },
            clients_per_regex=[(match, [("host", 1234, client_mock),
                                        ("host", 4567, faulty_client)])],
            max_running={match: 2},
        )

        executor = Executor(client.application, pool_mock)
        result = executor.get_running_containers()

        expect(result).to_include("available")
        available = result["available"]
        expect(available).to_be_like([{
            "host": "host",
            "port": 1234,
            "available": True,
            "blacklisted": False,
            "circuit": "closed",
            "error": None,
        }])

        expect(result).to_include("unavailable")
        unavailable = result["unavailable"]

        expect(unavailable).to_be_like([{
            "host": "host",
            "port": 4567,
            "available": False,
            "blacklisted": False,
            "circuit": "closed",
            "error": "failed",
        }])

        expect(result).to_include("running")
        running = result["running"]
        expect(running).to_length(1)
        host, port, container_id = running[0]
        expect(host).to_equal("host")
        expect(port).to_equal(1234)
        expect(container_id).to_equal("fastlane-job-123")
Esempio n. 6
0
def test_mark_as_done2(client):
    """
    Tests marking a container as done raises HostUnavailableError
    when docker host is not available
    """

    with client.application.app_context():
        containers = [
            ContainerFixture.new(
                container_id="fastlane-job-123",
                name="fastlane-job-123",
                stdout="stdout",
                stderr="stderr",
            )
        ]
        _, pool_mock, _ = PoolFixture.new_defaults(r"test.+",
                                                   max_running=1,
                                                   containers=containers)

        task, job, execution = JobExecutionFixture.new_defaults(
            container_id="fastlane-job-123")
        executor = Executor(client.application, pool_mock)

        containers[0].rename.side_effect = requests.exceptions.ConnectionError(
            "failed")

        message = "Connection to host host:1234 failed with error: failed"
        with expect.error_to_happen(HostUnavailableError, message=message):
            executor.mark_as_done(task, job, execution)
Esempio n. 7
0
def test_remove_done1(client):
    """
    Tests removing all defunct containers
    """

    with client.application.app_context():
        containers = [
            ContainerFixture.new(
                container_id="fastlane-job-123",
                name="defunct-fastlane-job-123",
                stdout="stdout",
                stderr="stderr",
            )
        ]
        _, pool_mock, _ = PoolFixture.new_defaults(r"test.+",
                                                   max_running=1,
                                                   containers=containers)

        JobExecutionFixture.new_defaults(container_id="fastlane-job-123")
        executor = Executor(client.application, pool_mock)
        result = executor.remove_done()

        containers[0].remove.assert_called()

        expect(result).to_length(1)
        expect(result[0]).to_be_like({
            "host": "host:1234",
            "name": "defunct-fastlane-job-123",
            "id": "fastlane-job-123",
            "image": "ubuntu:latest",
        })
Esempio n. 8
0
def test_circuit5(client):
    """
    Tests that when getting streaming logs with a
    docker host that's not accessible, the circuit is open and
    a HostUnavailableError is raised
    """

    with client.application.app_context():
        client.application.config["DOCKER_CIRCUIT_BREAKER_MAX_FAILS"] = 1

        task, job, execution = JobExecutionFixture.new_defaults(
            docker_host="localhost",
            docker_port=4567,
            container_id=str(uuid4()))
        pool = DockerPool(([None, ["localhost:4567"], 2], ))
        executor = Executor(client.application, pool)

        expect(executor.get_circuit("localhost:4567").current_state).to_equal(
            "closed")

        with expect.error_to_happen(HostUnavailableError):
            for _ in executor.get_streaming_logs(task, job, execution):
                expect.not_to_be_here()

        expect(executor.get_circuit("localhost:4567").current_state).to_equal(
            "open")
Esempio n. 9
0
def verify_get_result(client, status, exit_code, stdout, stderr, custom_error,
                      started_at, finished_at):
    app = client.application

    with app.app_context():
        container_mock = ContainerFixture.new_with_status(
            container_id="fastlane-job-123",
            name="fastlane-job-123",
            status=status,
            exit_code=exit_code,
            started_at=started_at,
            finished_at=finished_at,
            custom_error=custom_error,
            stdout=stdout,
            stderr=stderr,
        )

        _, pool_mock, _ = PoolFixture.new_defaults(r"test[-].+",
                                                   max_running=1,
                                                   containers=[container_mock])

        executor = Executor(app, pool_mock)

        _, job, execution = JobExecutionFixture.new_defaults(
            container_id="fastlane-job-123")

        result = executor.get_result(job.task, job, execution)
        expect(result.status).to_equal(STATUS.get(status))
        expect(result.exit_code).to_equal(exit_code)

        if stdout is None:
            expect(result.log).to_be_empty()
        else:
            expect(result.log).to_equal(stdout)

        if stderr is not None and custom_error != "":
            expect(
                result.error).to_equal(f"{custom_error}\n\nstderr:\n{stderr}")
        else:
            if stderr is not None:
                expect(result.error).to_equal(stderr)
            else:
                expect(result.error).to_equal(custom_error)

        parsed_started_at = parse(started_at)
        expect(result.started_at).to_equal(parsed_started_at)

        if finished_at is not None:
            parsed_finished_at = parse(finished_at)
        else:
            parsed_finished_at = finished_at
        expect(result.finished_at).to_equal(parsed_finished_at)
Esempio n. 10
0
def test_pool3(client):
    """
    Tests that when getting docker hosts, hosts with half-open circuits are returned
    """

    with client.application.app_context():
        pool = DockerPool(([None, ["localhost:1234"], 2], ))
        executor = Executor(client.application, pool)
        executor.get_circuit("localhost:1234").half_open()

        host, port, client = pool.get_client(executor, "test-123")
        expect(host).to_equal("localhost")
        expect(port).to_equal(1234)
Esempio n. 11
0
def test_validate_max2(client):
    """
    Tests validating max current executions works even if no hosts match task_id
    """

    app = client.application

    with app.app_context():
        pool_mock = PoolFixture.new()
        executor = Executor(app, pool_mock)

        result = executor.validate_max_running_executions("test123")
        expect(result).to_be_true()
Esempio n. 12
0
def test_pool5(client):
    """
    Tests getting client with specific host and port
    """

    with client.application.app_context():
        executor = Executor(client.application)
        pool = executor.pool
        expect(pool.clients).to_include("localhost:2375")

        host, port, client = pool.get_client(executor,
                                             "test-123",
                                             host="localhost",
                                             port=2375)
        expect(host).to_equal("localhost")
        expect(port).to_equal(2375)
        expect(client).to_be_instance_of(docker.client.DockerClient)

        host, port, client = pool.get_client(executor,
                                             "test-123",
                                             host="localhost",
                                             port=4000)
        expect(host).to_equal("localhost")
        expect(port).to_equal(4000)
        expect(client).to_be_null()
Esempio n. 13
0
def test_pull1(client):
    """Tests that a docker executor can pull images"""

    with client.application.app_context():
        task, job, execution = JobExecutionFixture.new_defaults()
        _, pool_mock, client_mock = PoolFixture.new_defaults(r"test-.+")

        exe = Executor(app=client.application, pool=pool_mock)
        exe.update_image(task,
                         job,
                         execution,
                         "mock-image",
                         "latest",
                         blacklisted_hosts=set())

        expect(client_mock.images.pull.call_count).to_equal(1)
        client_mock.images.pull.assert_called_with("mock-image", tag="latest")
Esempio n. 14
0
def test_validate_max1(client):
    """
    Tests validating max current executions for a docker host
    """

    app = client.application

    with app.app_context():
        containers = [ContainerFixture.new(name="fastlane-job-123")]
        _, pool_mock, _ = PoolFixture.new_defaults(r"test.+",
                                                   max_running=1,
                                                   containers=containers)

        executor = Executor(app, pool_mock)

        result = executor.validate_max_running_executions("test123")
        expect(result).to_be_true()
Esempio n. 15
0
def test_get_blacklisted_hosts1(client):
    """
    Tests getting the blacklisted hosts
    """

    app = client.application
    with app.app_context():
        _, pool_mock, _ = PoolFixture.new_defaults(r"test-.+")

        executor = Executor(client.application, pool_mock)
        hosts = executor.get_blacklisted_hosts()
        expect(hosts).to_be_empty()

        redis = app.redis
        redis.sadd(BLACKLIST_KEY, "localhost:5678")

        hosts = executor.get_blacklisted_hosts()
        expect(hosts).to_length(1)
        expect(list(hosts)[0]).to_equal("localhost:5678")
Esempio n. 16
0
def test_stop2(client):
    """
    Tests stopping a job stops fails if container is not in metadata
    """

    app = client.application

    with app.app_context():
        _, pool_mock, _ = PoolFixture.new_defaults(r"test[-].+",
                                                   max_running=1,
                                                   containers=[])

        task, job, execution = JobExecutionFixture.new_defaults()
        del execution.metadata["container_id"]

        executor = Executor(app, pool_mock)

        result = executor.stop_job(task, job, execution)
        expect(result).to_be_false()
Esempio n. 17
0
def test_get_result3(client):
    app = client.application

    with app.app_context():
        container_mock = ContainerFixture.new_with_status(
            container_id="fastlane-job-123", name="fastlane-job-123")

        _, pool_mock, client_mock = PoolFixture.new_defaults(
            r"test[-].+", max_running=1, containers=[container_mock])
        client_mock.containers.get.side_effect = requests.exceptions.ConnectionError(
            "failed")

        executor = Executor(app, pool_mock)

        _, job, execution = JobExecutionFixture.new_defaults(
            container_id="fastlane-job-123")

        msg = "Connection to host host:1234 failed with error: failed"
        with expect.error_to_happen(HostUnavailableError, message=msg):
            executor.get_result(job.task, job, execution)
Esempio n. 18
0
def test_get_running3(client):
    """
    Tests getting running containers when some hosts are blacklisted
    """

    with client.application.app_context():
        match = re.compile(r"test-.+")
        client_mock = ClientFixture.new([
            ContainerFixture.new(name="fastlane-job-123",
                                 container_id="fastlane-job-123")
        ])

        pool_mock = PoolFixture.new(
            clients={"host:1234": ("host", 1234, client_mock)},
            clients_per_regex=[(match, [("host", 1234, client_mock)])],
            max_running={match: 2},
        )

        executor = Executor(client.application, pool_mock)
        result = executor.get_running_containers(
            blacklisted_hosts=set(["host:1234"]))

        expect(result).to_include("available")
        available = result["available"]
        expect(available).to_be_empty()

        expect(result).to_include("unavailable")
        unavailable = result["unavailable"]

        expect(unavailable).to_be_like([{
            "host": "host",
            "port": 1234,
            "available": False,
            "blacklisted": True,
            "circuit": "closed",
            "error": "server is blacklisted",
        }])

        expect(result).to_include("running")
        running = result["running"]
        expect(running).to_length(0)
Esempio n. 19
0
def test_validate_max3(client):
    """
    Tests validating max current executions returns False
    if max concurrent containers already running
    """

    app = client.application

    with app.app_context():
        containers = [
            ContainerFixture.new(name="fastlane-job-123"),
            ContainerFixture.new(name="fastlane-job-456"),
        ]

        _, pool_mock, _ = PoolFixture.new_defaults(r"test.+",
                                                   max_running=1,
                                                   containers=containers)
        executor = Executor(app, pool_mock)

        result = executor.validate_max_running_executions("test123")
        expect(result).to_be_false()
Esempio n. 20
0
def test_stop1(client):
    """
    Tests stopping a job stops the container in docker
    """

    app = client.application

    with app.app_context():
        container_mock = ContainerFixture.new_with_status(
            name="fastlane-job-1234", container_id="fastlane-job-1234")
        _, pool_mock, _ = PoolFixture.new_defaults(r"test[-].+",
                                                   max_running=1,
                                                   containers=[container_mock])

        task, job, execution = JobExecutionFixture.new_defaults(
            container_id="fastlane-job-1234")

        executor = Executor(app, pool_mock)

        result = executor.stop_job(task, job, execution)
        container_mock.stop.assert_called()
        expect(result).to_be_true()
Esempio n. 21
0
def test_pool6(client):
    """
    Tests getting client when no farms match raises
    """

    with client.application.app_context():
        pool = DockerPool(
            ([re.compile(r"test-.+"), ["localhost:1234", "localhost:4567"],
              2], ))
        executor = Executor(client.application, pool=pool)

        message = "Failed to find a docker host for task id qwe-123."
        with expect.error_to_happen(NoAvailableHostsError, message=message):
            pool.get_client(executor, "qwe-123")
Esempio n. 22
0
def test_pool2(client):
    """
    Tests that when getting docker hosts, hosts in the blacklist are not returned
    """

    with client.application.app_context():
        pool = DockerPool(([None, ["localhost:1234", "localhost:4567"], 2], ))
        executor = Executor(client.application, pool)

        host, port, client = pool.get_client(executor,
                                             "test-123",
                                             blacklist=set(["localhost:4567"]))
        expect(host).to_equal("localhost")
        expect(port).to_equal(1234)
Esempio n. 23
0
def test_get_running1(client):
    """
    Tests getting running containers
    """

    with client.application.app_context():
        containers = [
            ContainerFixture.new(name="fastlane-job-123",
                                 container_id="fastlane-job-123")
        ]
        _, pool_mock, _ = PoolFixture.new_defaults(r"test.+",
                                                   max_running=1,
                                                   containers=containers)

        executor = Executor(client.application, pool_mock)
        result = executor.get_running_containers()

        expect(result).to_include("available")
        available = result["available"]
        expect(available).to_length(1)
        expect(available[0]).to_equal({
            "host": "host",
            "port": 1234,
            "available": True,
            "blacklisted": False,
            "circuit": "closed",
            "error": None,
        })

        expect(result).to_include("running")
        running = result["running"]
        expect(running).to_length(1)
        host, port, container_id = running[0]
        expect(host).to_equal("host")
        expect(port).to_equal(1234)
        expect(container_id).to_equal("fastlane-job-123")
Esempio n. 24
0
def test_pool4(client):
    """
    Tests that when creating docker executor, the pool is configured properly
    """

    with client.application.app_context():
        executor = Executor(client.application)
        pool = executor.pool
        expect(pool.clients).to_include("localhost:2375")

        host, port, client = pool.clients["localhost:2375"]
        expect(host).to_equal("localhost")
        expect(port).to_equal(2375)
        expect(client).to_be_instance_of(docker.client.DockerClient)

        expect(pool.max_running).to_equal({None: 2})
Esempio n. 25
0
def test_circuit1(client):
    """
    Tests that when updating with a docker host that's not accessible,
    the circuit is open and a HostUnavailableError is raised
    """

    with client.application.app_context():
        client.application.config["DOCKER_CIRCUIT_BREAKER_MAX_FAILS"] = 2

        task, job, execution = JobExecutionFixture.new_defaults(
            docker_host="localhost", docker_port=4567)
        pool = DockerPool(([None, ["localhost:4567"], 2], ))
        executor = Executor(client.application, pool)

        expect(executor.get_circuit("localhost:4567").current_state).to_equal(
            "closed")

        with expect.error_to_happen(HostUnavailableError):
            executor.update_image(task, job, execution, "ubuntu", "latest")

        expect(executor.get_circuit("localhost:4567").current_state).to_equal(
            "open")