Repository navigation
[AIRFLOW-2310]: Add AWS Glue Job Compatibility to Airflow - #4068
oelesinsc24 wants to merge 11 commits into
Conversation
…b and not create glue job
|
@Fokko, can you please help look at this? Some Kubernetes tests seem to be failing and they are not related to my commits, precisely, I have not had anything to do with them |
Fokko
left a comment
There was a problem hiding this comment.
@oelesinsc24 Thanks for the PR, please check the comments.
|
@Fokko , thanks for the review. Will the changes be merged any time soon? |
|
@oelesinsc24 Travis is still not happy: |
|
Hi, Any update on this PR. very interesting hook |
|
Is there any further update on when this PR will be actioned on closed ? |
As far as I know, the PR is approved. Awaiting merge. @Fokko, can you help see to this? Thanks |
ashb
left a comment
There was a problem hiding this comment.
Code looks good.
Only question is about the split of the Operator and the sensor: is there ever a case where you want a glue job to "fire-and-forget" or would you the AwsGlueJobOperator to handle the polling internally, without the need for a sensor task?
|
@ashb, great question. But I think there may be cases for both. However, I would recommend submitting a glue job and then using the sensor to poll for updates. |
Could you explain your thinking? If this is what 90% of people will want, then does it make more sense having it be the default and built as a single operator? |
|
This is great, when are you guys planning to merge this ? |
I guess if 90% of people want this then we can add it. However, in my experience with AWS Glue, it makes sense to submit the job and then poll for it |
|
Do you ever do anything else than Submit the job and the immediately start a sensor to poll for it? |
|
Like stated earlier. My preference would be to have the operator itself poll for it. If you think about atomicity, if the job fails, you will need to clear both the sensor and the operator. This makes life more complicated and adds very little value. Also, your dags will be twice as big. Please fix the flake8 issues: |
Now I get the point and I agree as well. I will add the necessary changes. @Fokko , thanks for the clarification as well. |
|
Any time @oelesinsc24. Let me know when you're ready, so we can get this in! 👍 |
|
How would this work? I assume the Glue endpoint has to be setup a priori with the code that it would run? |
| .format(job_name)) | ||
| time.sleep(6) | ||
|
|
||
| def get_glue_job(self): |
There was a problem hiding this comment.
this code can only create a job but I want to run exists job by name
so suggest to use
def get_or_create_glue_job(self):
try:
glue_client = self.get_conn()
glue_client.get_job(JobName=self.job_name)
except ClientError as client_err:
if client_err.response['Error']['Code'] == \
'EntityNotFoundException':
return self._create_glue_job()
raise client_err
def _create_glue_job(self):
glue_client = self.get_conn()
if self.s3_bucket is None:
raise AirflowException(
'Could not initialize glue job, '
'error: Specify Parameter `s3_bucket`'
)
self.log.info("Now creating and running AWS Glue Job")
s3_log_path = "s3://{bucket_name}/{logs_path}{job_name}"\
.format(bucket_name=self.s3_bucket,
logs_path=self.S3_GLUE_LOGS,
job_name=self.job_name)
execution_role = self.get_iam_execution_role()
script_location = self._check_script_location()
create_job_response = glue_client.create_job(
Name=self.job_name,
Description=self.desc,
LogUri=s3_log_path,
Role=execution_role['Role']['RoleName'],
ExecutionProperty={"MaxConcurrentRuns": self.concurrent_run_limit},
Command={"Name": "glueetl", "ScriptLocation": script_location},
MaxRetries=self.retry_limit,
AllocatedCapacity=self.num_of_dpus
)
return create_job_response
and then in initialize_job do
glue_client = self.get_conn()
try:
self.get_or_create_glue_job()
job_run = glue_client.start_job_run(
JobName=self.job_name,
Arguments=script_arguments
)
self.log.info("Run job with id: {}".format(job_run['JobRunId']))
return self.job_completion(self.job_name, job_run['JobRunId'])
except Exception as general_error:
raise AirflowException(
'Failed to run aws glue job, error: {error}'.format(
error=str(general_error)
)
)
| ) | ||
| job_run_state = job_status['JobRun']['JobRunState'] | ||
|
|
||
| if job_run_state in {'FAILED', 'STOPPED', 'SUCCEEDED'}: |
There was a problem hiding this comment.
use constants for the states, then we can you them in tests
| :param aws_conn_id: aws connection id | ||
| :type aws_conn_id: str | ||
| """ | ||
| template_fields = () |
There was a problem hiding this comment.
please, add script_args argument to allow templating
template_fields = ('script_args',)
There was a problem hiding this comment.
+1 , IMO it may be useful to template the job_name as well.
| RunId=run_id, | ||
| PredecessorsIncluded=True | ||
| ) | ||
| job_run_state = job_status['JobRun']['JobRunState'] |
There was a problem hiding this comment.
collect an error message for logging
error_message = job_status['JobRun']['ErrorMessage'] \
if 'ErrorMessage' in job_status['JobRun'] else None
if failed or stopped or completed:
self.log.info("Exiting Job {} Run State: {}"
.format(run_id, job_run_state))
return {'JobRunState': job_run_state, 'JobRunId': run_id,
'ErrorMessage': error_message}
then in AWSGlueJobOperator
glue_job_run = glue_job.initialize_job(self.script_args)
error_message = 'Error message: {}'.format(glue_job_run['ErrorMessage'])\
if glue_job_run['ErrorMessage'] else ''
self.log.info('AWS Glue Job: {job_name} status: {job_status}. '
'Run Id: {run_id}{error_message}'
.format(run_id=glue_job_run['JobRunId'],
job_name=self.job_name,
job_status=glue_job_run['JobRunState'],
error_message=error_message)
)
if glue_job_run['JobRunState'] != SUCCEEDED:
raise AirflowException('AWS Glue job failed with error: {}'
.format(glue_job_run['ErrorMessage']))
| get_job_response = glue_client.get_job( | ||
| JobName=self.job_name | ||
| )['Job'] | ||
| self.logger.info("Found AWS Glue Job: {job_name}".format(job_name=self.job_name)) |
There was a problem hiding this comment.
| self.logger.info("Found AWS Glue Job: {job_name}".format(job_name=self.job_name)) | |
| self.logger.info("Found AWS Glue Job: %s", self.job_name) |
Please avoid formatting string. You should pass variables to logger to avoid creating string objects in memory. When the logger level is too low, they will never be created in memory.
| ) | ||
| ) | ||
|
|
||
| def job_completion(self, job_name=None, run_id=None): |
There was a problem hiding this comment.
Could you correct the name of this method to include a verb? GoogleCloudBaseHook use a name
_wait_for_operation_to_complete.
There was a problem hiding this comment.
- See also https://github.com/apache/airflow/blob/master/airflow/contrib/operators/awsbatch_operator.py#L125
- python doesn't have java-interfaces, but that seems be what this requires; is there a general Airflow operator ABC, document or recommendation on all these operator method names?
|
Could you complete the integration.rst file? |
|
I'm looking forward to this PR |
|
@oelesinsc24 @Fokko @ashb Any update on this PR? Also, I agree that the operator should follow the status of the job, there is really no need for that sensor. |
zhongjiajie
left a comment
There was a problem hiding this comment.
My 2 cent, most of them because AIP-3 According to https://issues.apache.org/jira/browse/AIRFLOW-4196
| self.job_name = job_name | ||
| self.desc = desc | ||
| self.aws_conn_id = aws_conn_id | ||
| super(AwsGlueJobHook, self).__init__(self.aws_conn_id, *args, **kwargs) |
There was a problem hiding this comment.
| super(AwsGlueJobHook, self).__init__(self.aws_conn_id, *args, **kwargs) | |
| super().__init__(self.aws_conn_id, *args, **kwargs) |
| aws_conn_id='aws_default', | ||
| *args, **kwargs | ||
| ): | ||
| super(AWSGlueJobOperator, self).__init__(*args, **kwargs) |
There was a problem hiding this comment.
| super(AWSGlueJobOperator, self).__init__(*args, **kwargs) | |
| super().__init__(*args, **kwargs) |
There was a problem hiding this comment.
According to https://issues.apache.org/jira/browse/AIRFLOW-4196
| aws_conn_id='aws_default', | ||
| *args, | ||
| **kwargs): | ||
| super(AwsGlueJobSensor, self).__init__(*args, **kwargs) |
There was a problem hiding this comment.
| super(AwsGlueJobSensor, self).__init__(*args, **kwargs) | |
| super().__init__(*args, **kwargs) |
There was a problem hiding this comment.
According to https://issues.apache.org/jira/browse/AIRFLOW-4196
| .. autoclass:: airflow.contrib.operators.adls_to_gcs.AdlsToGoogleCloudStorageOperator | ||
| .. autoclass:: airflow.contrib.operators.aws_athena_operator.AWSAthenaOperator | ||
| .. autoclass:: airflow.contrib.operators.awsbatch_operator.AWSBatchOperator | ||
| .. autoclass:: airflow.contrib.operators.aws_glue_job_operator.AWSGlueJobOperator |
There was a problem hiding this comment.
We don't need code.rst anymore
| # KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations | ||
| # under the License. | ||
| from __future__ import unicode_literals |
There was a problem hiding this comment.
| from __future__ import unicode_literals |
There was a problem hiding this comment.
don't need __future__ anymore
There was a problem hiding this comment.
According to https://issues.apache.org/jira/browse/AIRFLOW-4196
|
I'd be very interested in getting this merged -- please do let me know how I can be of help here. |
|
I'm also interested in getting this PR approved -- I can spend some time getting the operator to poll for the status of the job itself. |
Yes you can help. Apologies, I've been really busy for some time now. |
Please do, all hands needed to get this approved and available to all |
|
|
The github profile is private, here's my public: https://github.com/OELEsin. Good you asked about the status. I have not had much time to contribute to it. This is why I asked if anyone was willing to continue from where I stopped |
|
Looks like there is a good amount of demand for this one. If @oelesinsc24 is busy, does anyone want to take this forward, create a new PR? |
|
Hi,I wanted to work on it, I cloned this repo (https://github.com/OElesin/incubator-airflow) and tried the glue sensor/hook/operator and they weren't working properly so i have fixed and have submiited this pull request(#6007) |
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Make sure you have checked all steps below.
Jira
Description
Tests
Added tests.contrib.test_aws_glue_job_hook.py.
However, moto the test class for boto3 does not support AWS Glue Job mock currently
Commits
Documentation
Code Quality
flake8