diff --git a/moto/glue/exceptions.py b/moto/glue/exceptions.py index 754680acf..30a083f2d 100644 --- a/moto/glue/exceptions.py +++ b/moto/glue/exceptions.py @@ -55,6 +55,11 @@ class CrawlerNotFoundException(EntityNotFoundException): super().__init__("Crawler %s not found." % crawler) +class JobNotFoundException(EntityNotFoundException): + def __init__(self, job): + super().__init__("Job %s not found." % job) + + class VersionNotFoundException(EntityNotFoundException): def __init__(self): super().__init__("Version not found.") diff --git a/moto/glue/models.py b/moto/glue/models.py index 5fafee7a0..9cb37fc06 100644 --- a/moto/glue/models.py +++ b/moto/glue/models.py @@ -15,6 +15,7 @@ from .exceptions import ( PartitionAlreadyExistsException, PartitionNotFoundException, VersionNotFoundException, + JobNotFoundException, ) from ..utilities.paginator import paginate @@ -198,6 +199,12 @@ class GlueBackend(BaseBackend): ) return name + def get_job(self, name): + try: + return self.jobs[name] + except KeyError: + raise JobNotFoundException(name) + @paginate(pagination_model=PAGINATION_MODEL) def list_jobs(self): return [job for _, job in self.jobs.items()] @@ -470,5 +477,29 @@ class FakeJob: def get_name(self): return self.name + def as_dict(self): + return { + "Name": self.name, + "Description": self.description, + "LogUri": self.log_uri, + "Role": self.role, + "CreatedOn": self.created_on.isoformat(), + "LastModifiedOn": self.last_modified_on.isoformat(), + "ExecutionProperty": self.execution_property, + "Command": self.command, + "DefaultArguments": self.default_arguments, + "NonOverridableArguments": self.non_overridable_arguments, + "Connections": self.connections, + "MaxRetries": self.max_retries, + "AllocatedCapacity": self.allocated_capacity, + "Timeout": self.timeout, + "MaxCapacity": self.max_capacity, + "WorkerType": self.worker_type, + "NumberOfWorkers": self.number_of_workers, + "SecurityConfiguration": self.security_configuration, + "NotificationProperty": self.notification_property, + "GlueVersion": self.glue_version, + } + glue_backend = GlueBackend() diff --git a/moto/glue/responses.py b/moto/glue/responses.py index 6ae20f85e..c07c1a753 100644 --- a/moto/glue/responses.py +++ b/moto/glue/responses.py @@ -366,6 +366,11 @@ class GlueResponse(BaseResponse): ) return json.dumps(dict(Name=name)) + def get_job(self): + name = self.parameters.get("JobName") + job = self.glue_backend.get_job(name) + return json.dumps({"Job": job.as_dict()}) + def list_jobs(self): next_token = self._get_param("NextToken") max_results = self._get_int_param("MaxResults") diff --git a/tests/test_glue/test_glue.py b/tests/test_glue/test_glue.py index 7f04ca845..b3717fab6 100644 --- a/tests/test_glue/test_glue.py +++ b/tests/test_glue/test_glue.py @@ -6,6 +6,7 @@ import boto3 import pytest import sure # noqa # pylint: disable=unused-import from botocore.exceptions import ParamValidationError +from botocore.client import ClientError from moto import mock_glue @@ -41,6 +42,72 @@ def test_list_jobs(): response.shouldnt.have.key("NextToken") +@mock_glue +def test_get_job_not_exists(): + client = create_glue_client() + name = "my_job_name" + + with pytest.raises(ClientError) as exc: + client.get_job(JobName=name) + + exc.value.response["Error"]["Code"].should.equal("EntityNotFoundException") + exc.value.response["Error"]["Message"].should.match("Job my_job_name not found") + + +@mock_glue +def test_get_job_exists(): + client = create_glue_client() + job_attributes = { + "Description": "test_description", + "LogUri": "test_log/", + "Role": "test_role", + "ExecutionProperty": {"MaxConcurrentRuns": 123}, + "Command": { + "Name": "test_command", + "ScriptLocation": "test_s3_path", + "PythonVersion": "3.6", + }, + "DefaultArguments": {"string": "string"}, + "NonOverridableArguments": {"string": "string"}, + "Connections": { + "Connections": [ + "string", + ] + }, + "MaxRetries": 123, + "AllocatedCapacity": 123, + "Timeout": 123, + "MaxCapacity": 123.0, + "WorkerType": "G.2X", + "NumberOfWorkers": 123, + "SecurityConfiguration": "test_config", + "NotificationProperty": {"NotifyDelayAfter": 123}, + "GlueVersion": "string", + } + job_name = create_test_job_w_all_attributes(client, **job_attributes) + response = client.get_job(JobName=job_name) + assert response["Job"]["Name"] == job_name + assert response["Job"]["Description"] + assert response["Job"]["LogUri"] + assert response["Job"]["Role"] + assert response["Job"]["CreatedOn"] + assert response["Job"]["LastModifiedOn"] + assert response["Job"]["ExecutionProperty"] + assert response["Job"]["Command"] + assert response["Job"]["DefaultArguments"] + assert response["Job"]["NonOverridableArguments"] + assert response["Job"]["Connections"] + assert response["Job"]["MaxRetries"] + assert response["Job"]["AllocatedCapacity"] + assert response["Job"]["Timeout"] + assert response["Job"]["MaxCapacity"] + assert response["Job"]["WorkerType"] + assert response["Job"]["NumberOfWorkers"] + assert response["Job"]["SecurityConfiguration"] + assert response["Job"]["NotificationProperty"] + assert response["Job"]["GlueVersion"] + + @mock_glue def test_list_jobs_with_max_results(): client = create_glue_client() @@ -100,6 +167,13 @@ def create_test_job(client, tags=None): Command=dict(Name="test_command"), Tags=tags or {}, ) + return job_name + + +def create_test_job_w_all_attributes(client, **job_attributes): + job_name = str(uuid4()) + client.create_job(Name=job_name, **job_attributes) + return job_name def create_test_jobs(client, number_of_jobs):