Given a Lambda ARN, name and a list of events, schedule this as CloudWatch Events.

        'events' is a list of dictionaries, where the dict must contains the string
        of a 'function' and the string of the event 'expression', and an optional 'name' and 'description'.

        Expressions can be in rate or cron format:
            http://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html

def schedule_events(self, lambda_arn, lambda_name, events, default=True):
        """
        Given a Lambda ARN, name and a list of events, schedule this as CloudWatch Events.

        'events' is a list of dictionaries, where the dict must contains the string
        of a 'function' and the string of the event 'expression', and an optional 'name' and 'description'.

        Expressions can be in rate or cron format:
            http://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html
        """

        # The stream sources - DynamoDB, Kinesis and SQS - are working differently than the other services (pull vs push)
        # and do not require event permissions. They do require additional permissions on the Lambda roles though.
        # http://docs.aws.amazon.com/lambda/latest/dg/lambda-api-permissions-ref.html
        pull_services = ['dynamodb', 'kinesis', 'sqs']

        # XXX: Not available in Lambda yet.
        # We probably want to execute the latest code.
        # if default:
        #     lambda_arn = lambda_arn + ":$LATEST"

        self.unschedule_events(lambda_name=lambda_name, lambda_arn=lambda_arn, events=events,
                               excluded_source_services=pull_services)
        for event in events:
            function = event['function']
            expression = event.get('expression', None) # single expression
            expressions = event.get('expressions', None) # multiple expression
            kwargs = event.get('kwargs', {}) # optional dict of keyword arguments for the event
            event_source = event.get('event_source', None)
            description = event.get('description', function)

            #   - If 'cron' or 'rate' in expression, use ScheduleExpression
            #   - Else, use EventPattern
            #       - ex https://github.com/awslabs/aws-lambda-ddns-function

            if not self.credentials_arn:
                self.get_credentials_arn()

            if expression:
                expressions = [expression] # same code for single and multiple expression

            if expressions:
                for index, expression in enumerate(expressions):
                    name = self.get_scheduled_event_name(event, function, lambda_name, index)
                    # if it's possible that we truncated name, generate a unique, shortened name
                    # https://github.com/Miserlou/Zappa/issues/970
                    if len(name) >= 64:
                        rule_name = self.get_hashed_rule_name(event, function, lambda_name)
                    else:
                        rule_name = name

                    rule_response = self.events_client.put_rule(
                        Name=rule_name,
                        ScheduleExpression=expression,
                        State='ENABLED',
                        Description=description,
                        RoleArn=self.credentials_arn
                    )

                    if 'RuleArn' in rule_response:
                        logger.debug('Rule created. ARN {}'.format(rule_response['RuleArn']))

                    # Specific permissions are necessary for any trigger to work.
                    self.create_event_permission(lambda_name, 'events.amazonaws.com', rule_response['RuleArn'])

                    # Overwriting the input, supply the original values and add kwargs
                    input_template = '{"time": <time>, ' \
                                     '"detail-type": <detail-type>, ' \
                                     '"source": <source>,' \
                                     '"account": <account>, ' \
                                     '"region": <region>,' \
                                     '"detail": <detail>, ' \
                                     '"version": <version>,' \
                                     '"resources": <resources>,' \
                                     '"id": <id>,' \
                                     '"kwargs": %s' \
                                     '}' % json.dumps(kwargs)

                    # Create the CloudWatch event ARN for this function.
                    # https://github.com/Miserlou/Zappa/issues/359
                    target_response = self.events_client.put_targets(
                        Rule=rule_name,
                        Targets=[
                            {
                                'Id': 'Id' + ''.join(random.choice(string.digits) for _ in range(12)),
                                'Arn': lambda_arn,
                                'InputTransformer': {
                                    'InputPathsMap': {
                                        'time': '$.time',
                                        'detail-type': '$.detail-type',
                                        'source': '$.source',
                                        'account': '$.account',
                                        'region': '$.region',
                                        'detail': '$.detail',
                                        'version': '$.version',
                                        'resources': '$.resources',
                                        'id': '$.id'
                                    },
                                    'InputTemplate': input_template
                                }
                            }
                        ]
                    )

                    if target_response['ResponseMetadata']['HTTPStatusCode'] == 200:
                        print("Scheduled {} with expression {}!".format(rule_name, expression))
                    else:
                        print("Problem scheduling {} with expression {}.".format(rule_name, expression))

            elif event_source:
                service = self.service_from_arn(event_source['arn'])

                if service not in pull_services:
                    svc = ','.join(event['event_source']['events'])
                    self.create_event_permission(
                        lambda_name,
                        service + '.amazonaws.com',
                        event['event_source']['arn']
                    )
                else:
                    svc = service

                rule_response = add_event_source(
                    event_source,
                    lambda_arn,
                    function,
                    self.boto_session
                )

                if rule_response == 'successful':
                    print("Created {} event schedule for {}!".format(svc, function))
                elif rule_response == 'failed':
                    print("Problem creating {} event schedule for {}!".format(svc, function))
                elif rule_response == 'exists':
                    print("{} event schedule for {} already exists - Nothing to do here.".format(svc, function))
                elif rule_response == 'dryrun':
                    print("Dryrun for creating {} event schedule for {}!!".format(svc, function))
            else:
                print("Could not create event {} - Please define either an expression or an event source".format(name))