PhilBot
PhilBot

Reputation: 60

AWS CodePipeline Custom Lambda Function Runs Forever and Never Returns

I have a simple AWS CodePipeline with the standard "Source" -> "Build" -> "Deploy" pipeline stages that work fine and I am trying to add my own custom final pipeline stage that is a single AWS Lambda Function. The problem is my last, custom Lambda function runs multiple times and after a very long time, errors with the following message:

enter image description here

Please see the attached screenshot for the whole pipeline:

enter image description here

When the pipeline reaches this final step, it spins for a very long time with the "Blue ( In-Progress )" status before showing an error as shown here:

enter image description here

Here is my Lambda Function code:

from __future__ import print_function
import hashlib
import time
import os
import boto3
import json
from botocore.exceptions import ClientError

def lambda_handler(event, context):
    
    # Test
    AWS_ACCESS_KEY = ASDF1234
    AWS_SECRET_KEY = ASDF1234
    SQS_TESTING_OUTPUT_STATUS_QUEUE_NAME = 'TestingOutputQueue'

    # Get the code pipeline
    code_pipeline = boto3.client('codepipeline')
    
    # Get the job_id
    for key, value in event.items():
        print(key,value)
    job_id = event['CodePipeline.job']['id']
    DATA = json.dumps(event)
    
    # Create a connection the SQS Notification service
    sqs_resource_connection = boto3.resource(
        'sqs',
        aws_access_key_id = AWS_ACCESS_KEY,
        aws_secret_access_key = AWS_SECRET_KEY,
        region_name = 'us-west-2'
    )
    
    # Get the queue handle
    print("Waiting for notification from AWS ...")
    queue = sqs_resource_connection.get_queue_by_name(QueueName = SQS_TESTING_OUTPUT_STATUS_QUEUE_NAME)
    messageContent = ""
    cnt = 1
        
    # Replace [email protected] with your "From" address.
    # This address must be verified with Amazon SES.
    SENDER = ME
    
    # Replace [email protected] with a "To" address. If your account
    # is still in the sandbox, this address must be verified.
    RECIPIENTS = [YOU]
    
    # If necessary, replace us-west-2 with the AWS Region you're using for Amazon SES.
    AWS_REGION = "us-east-1"
    
    # The subject line for the email.
    SUBJECT = "Test Case Results"
    
    # The email body for recipients with non-HTML email clients.
    BODY_TEXT = ("Test Case Results Were ...")
    
    # The HTML body of the email.
    BODY_HTML = """<html>
    <head></head>
    <body>
      <h1>Amazon SES Test (SDK for Python)</h1>
      <p>%s</p>
    </body>
    </html>
                """%(DATA)
    
    # The character encoding for the email.
    CHARSET = "UTF-8"
    
    # Create a new SES resource and specify a region.
    client = boto3.client('ses', region_name=AWS_REGION)
    
    # Try to send the email.
    try:
        # Provide the contents of the email.
        response = client.send_email(
            Destination={
                'ToAddresses': RECIPIENTS,
            },
            Message={
                'Body': {
                    'Html': {
                        'Charset': CHARSET,
                        'Data': BODY_HTML,
                    },
                    'Text': {
                        'Charset': CHARSET,
                        'Data': BODY_TEXT,
                    },
                },
                'Subject': {
                    'Charset': CHARSET,
                    'Data': SUBJECT,
                },
            },
            Source=SENDER,
            # If you are not using a configuration set, comment or delete the
            # following line
            #ConfigurationSetName=CONFIGURATION_SET,
        )
    # Display an error if something goes wrong.
    except ClientError as e:
        code_pipeline.put_third_party_job_failure_result(jobId=job_id, failureDetails={'message': message, 'type': 'JobFailed'})
        code_pipeline.put_job_failure_result(jobId=job_id, failureDetails={'message': message, 'type': 'JobFailed'})      
        print(e.response['Error']['Message'])
    else:
        code_pipeline.put_third_party_job_success_result(jobId=job_id)
        code_pipeline.put_job_success_result(jobId=job_id)
        print("Email sent! Message ID:"),
        print(response['MessageId'])
        
    print('Function complete.')   
    return "Complete."
        

How can I get the Lambda to fire once and return so the pipeline can complete properly.

Upvotes: 2

Views: 3008

Answers (1)

jweyrich
jweyrich

Reputation: 32260

You are missing an important integration between your Lambda Function and the CodePipeline service.

You MUST notify CodePipeline about the result of your custom step, whether it succeeded or not - see my examples below.

Reporting success:

function reportSuccess(job_id) {
  var codepipeline = new AWS.CodePipeline();
  var params = {
    jobId: job_id,
  };
  return codepipeline.putJobSuccessResult(params).promise();
}

Reporting failure:

function reportFailure(job_id, invoke_id, message) {
  var codepipeline = new AWS.CodePipeline();
  var params = {
    failureDetails: {
      message: message,
      type: 'JobFailed',
      externalExecutionId: invoke_id,
    },
    jobId: job_id,
  };
  return codepipeline.putJobFailureResult(params).promise();
}

The integration was designed this way because one may want to integrate with an external job worker, in which their Lambda starts that worker (example, an approval process), and that worker then takes control and decides whether the whole step succeeded or failed.

Upvotes: 5

Related Questions