Exemple #1
0
    def test_job_actually_runs_script(self):
        with CloudSdkIntegrationStub() as gcloud:
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run("echo hello")

            assert b"hello\n" in gcloud.instances[0].logs()
Exemple #2
0
    def test_attaching_raises_exception_after_timeout(self):
        self.gcloud.get_subscriber().pull.return_value.received_messages = []
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
        job.run(args=[])

        with pytest.raises(TimeoutError) as e_info:
            result = job.attach(timeout_seconds=1)
Exemple #3
0
    def test_running_a_job_creates_a_topic_path(self):
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        job.run(args=[])

        self.gcloud.get_publisher().topic_path.assert_called_with(
            TEST_JOB_CONFIG["project_id"], job.name)
Exemple #4
0
    def test_job_runs_script_from_file(self):
        with CloudSdkIntegrationStub() as gcloud:
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run_file("tests/script.sh")

            assert b"hello\nworld\n" in gcloud.instances[0].logs()
Exemple #5
0
    def test_passing_gcs_target_without_artifacts_shows_error(self):
        with CloudSdkIntegrationStub() as gcloud:
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run("", gcs_target={"/tmp/artifacts": "mybucket"})

            assert b"No artifacts found in /tmp/artifacts" in gcloud.instances[0].logs()
Exemple #6
0
    def test_running_a_job_creates_a_pubsub_topic(self, mock_uuid_call):
        mock_uuid_call.return_value = 1234
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        job.run("")

        self.gcloud.get_publisher().create_topic.assert_called_with(
            f"{TEST_JOB_CONFIG['project_id']}/clash-job-1234")
Exemple #7
0
    def test_attaching_succeeds_if_there_is_a_running_job_and_a_message(self):
        message = MagicMock()
        message.message = MagicMock(data='{"status": 0}')
        self.gcloud.get_subscriber().pull.return_value.received_messages = [message]
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
        job.run("")

        job.attach()  # throws no exception
Exemple #8
0
    def test_running_a_job_creates_an_instance_template(self):
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        job.run(args=[])

        self.gcloud.get_compute_client(
        ).instanceTemplates.return_value.insert.return_value.execute.assert_called(
        )
Exemple #9
0
    def test_running_a_job_creates_a_managed_instance_group(self):
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        job.run(args=[])

        self.gcloud.get_compute_client(
        ).instanceGroupManagers.return_value.insert.return_value.execute.assert_called(
        )
Exemple #10
0
    def test_attaching_returns_status_code(self):
        message = MagicMock()
        message.message = MagicMock(data='{"status": 127}')
        self.gcloud.get_subscriber().pull.return_value.received_messages = [message]
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
        job.run("")

        result = job.attach()

        assert result["status"] == 127
Exemple #11
0
    def test_passing_gcs_mount_invokes_gcsfuse(self):
        with CloudSdkIntegrationStub() as gcloud:
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run("", gcs_mounts={"mybucket": "/mnt/static"})

            assert (
                b"gcsfuse.--implicit-dirs.mybucket./mnt/static"
                in gcloud.instances[0].logs()
            )
Exemple #12
0
    def test_job_uses_given_env_vars(self):
        with CloudSdkIntegrationStub() as gcloud:
            script = """
            echo "$MESSAGE"
            """
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run(script, env_vars={"MESSAGE": "foobar"})

            assert b"foobar\n" in gcloud.instances[0].logs()
Exemple #13
0
    def test_job_shutdowns_machine_eventually(self):
        with CloudSdkIntegrationStub() as gcloud:
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run("echo hello")

            assert (
                b"gcloud.compute.instance-groups.managed.delete"
                in gcloud.instances[0].logs()
            )
Exemple #14
0
    def test_running_a_job_creates_a_pubsub_topic(self, mock_uuid_call):
        mock_uuid_call.return_value = 1234
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        job.run(args=[])

        self.gcloud.get_publisher().create_topic.assert_called_with(
            f"{TEST_JOB_CONFIG['project_id']}/clash-job-1234",
            message_storage_policy=MessageStoragePolicy(
                allowed_persistence_regions=["europe-west1"]),
        )
Exemple #15
0
    def test_removes_topic_if_job_creation_failed(self):
        self.gcloud.get_compute_client(
        ).instanceGroupManagers.return_value.insert.return_value.execute.side_effect = Exception(
            "Failure!")
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        with pytest.raises(Exception) as e_info:
            job.run(args=[])

        self.gcloud.get_publisher().delete_topic.assert_called_with(
            f"{TEST_JOB_CONFIG['project_id']}/{job.name}")
Exemple #16
0
    def test_on_finish_does_not_run_callback_when_job_is_still_running(self):
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
        result = {"called": False}
        job.run(args=[])

        def callback(status_code):
            result["called"] = True

        job.on_finish(callback)

        assert not result["called"]
Exemple #17
0
    def test_on_finish_acknowledges_message(self):
        message = MagicMock()
        message.data = '{ "status": 0 }'
        self.gcloud.get_subscriber().subscribe.side_effect = (
            lambda path, callback: callback(message))
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
        job.run(args=[])

        job.on_finish(lambda status_code: None)

        message.ack.assert_called()
Exemple #18
0
    def test_job_sends_pubpub_message_on_failure(self, mock_uuid_call):
        mock_uuid_call.return_value = 123
        with CloudSdkIntegrationStub() as gcloud:
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run("exit 1")

            assert (
                b'gcloud.pubsub.topics.publish.clash-job-123.--message={"status": 1}'
                in gcloud.instances[0].logs()
            )
Exemple #19
0
    def test_deletes_instance_template_after_job_is_complete(self):
        self.gcloud.get_compute_client(
        ).instanceGroups.return_value.list.return_value.execute.return_value = {
            "items": [{
                "name": "anothergroup"
            }]
        }

        with clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud) as job:
            job.run(args=[])

        self.gcloud.get_compute_client(
        ).instanceTemplates.return_value.delete.return_value.execute.assert_called(
        )
Exemple #20
0
    def test_job_runs_multiline_script(self):
        with CloudSdkIntegrationStub() as gcloud:
            script = """
            echo 'hello'
            the_world_is_flat=true
            if [ "$the_world_is_flat" = true ] ; then
                echo 'world'
            fi
            """
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run(script)

            assert b"hello\nworld\n" in gcloud.instances[0].logs()
Exemple #21
0
    def test_attaching_pulls_message(self):
        message = MagicMock()
        message.message = MagicMock(data='{"status": 0}')
        self.gcloud.get_subscriber().pull.return_value.received_messages = [message]
        self.gcloud.get_subscriber().subscription_path.side_effect = (
            lambda x, y: "mysubscription"
        )
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
        job.run("")

        result = job.attach()

        self.gcloud.get_subscriber().pull.assert_called_with(
            "mysubscription", max_messages=1, return_immediately=False, timeout=30
        )
Exemple #22
0
    def test_on_finish_runs_callback_when_job_is_complete(self):
        message = MagicMock()
        message.data = '{ "status": 0 }'
        self.gcloud.get_subscriber().subscribe.side_effect = (
            lambda path, callback: callback(message))
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
        result = {"status": -1}
        job.run(args=[])

        def callback(status_code):
            result["status"] = status_code

        job.on_finish(callback)

        assert result["status"] == 0
Exemple #23
0
    def test_attaching_acknowledges_messages(self):
        message = MagicMock(ack_id=42)
        message.message = MagicMock(data='{"status": 0}')
        self.gcloud.get_subscriber().pull.return_value.received_messages = [
            message
        ]
        self.gcloud.get_subscriber().subscription_path.side_effect = (
            lambda x, y: "mysubscription")
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
        job.run(args=[])

        result = job.attach()

        self.gcloud.get_subscriber().acknowledge.assert_called_with(
            "mysubscription", [42])
Exemple #24
0
    def test_running_a_job_creates_a_pubsub_subscription_for_status_updates(self):
        message = MagicMock()
        message.message = MagicMock(data='{"status": 0}')
        self.gcloud.get_subscriber().pull.return_value.received_messages = [message]
        self.gcloud.get_publisher().topic_path.side_effect = lambda x, y: "mytopic"
        self.gcloud.get_subscriber().topic_path.side_effect = lambda x, y: "mytopic"
        self.gcloud.get_subscriber().subscription_path.side_effect = (
            lambda x, y: "mysubscription"
        )
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        job.run("")

        self.gcloud.get_subscriber().create_subscription.assert_called_with(
            "mysubscription", "mytopic"
        )
Exemple #25
0
    def __init__(self,
                 job_config,
                 cmd=None,
                 cmd_file=None,
                 name_prefix=None,
                 env_vars={},
                 gcs_target={},
                 gcs_mounts={},
                 *args,
                 **kwargs):
        self.job = clash.Job(job_config=job_config, name_prefix=name_prefix)

        self.cmd = cmd
        self.cmd_file = cmd_file
        self.env_vars = env_vars
        self.gcs_target = gcs_target
        self.gcs_mounts = gcs_mounts

        super(ComputeEngineJobOperator, self).__init__(*args, **kwargs)
Exemple #26
0
    def test_passing_gcs_target_invokes_gsutil(self):
        with CloudSdkIntegrationStub() as gcloud:
            script = """
            touch /tmp/artifacts/foo
            touch /tmp/models/bar
            """
            job = clash.Job(gcloud=gcloud, job_config=TEST_JOB_CONFIG)

            job.run(
                script,
                gcs_target={
                    "/tmp/artifacts": "mybucket",
                    "/tmp/models": "modelsbucket",
                },
            )

            assert (
                b"gsutil.cp.-r./tmp/artifacts/foo.gs://mybucket"
                in gcloud.instances[0].logs()
            )
            assert (
                b"gsutil.cp.-r./tmp/models/bar.gs://modelsbucket"
                in gcloud.instances[0].logs()
            )
Exemple #27
0
    def test_creates_job_with_name_prefix(self, mock_uuid_call):
        mock_uuid_call.return_value = 1234

        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud, name_prefix="foo")

        assert "foo-clash-job-1234" == job.name
Exemple #28
0
    def test_on_finish_fails_if_job_is_not_running(self):
        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        with pytest.raises(ValueError) as e_info:
            job.on_finish(lambda status_code: None)
Exemple #29
0
    def test_creates_job(self, mock_uuid_call):
        mock_uuid_call.return_value = 1234

        job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)

        assert "clash-job-1234" == job.name
Exemple #30
0
 def test_attaching_fails_if_there_is_not_a_running_job(self):
     job = clash.Job(TEST_JOB_CONFIG, gcloud=self.gcloud)
     with pytest.raises(ValueError) as e_info:
         job.attach()