# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""

Script to process account-deletion events from an SQS queue.

This script polls an SQS queue for events indicating that an upstream account
has been deleted.  Any user records for such an account are marked as "retired"
so that they'll be cleaned up by our standard garbage-collection process.

Note that this is a purely optional administrative task, highly specific to
Mozilla's internal Firefox-Accounts-supported deployment.

"""

import os
import json
import logging
import optparse

import boto
import boto.ec2
import boto.sqs
import boto.sqs.message
import boto.utils

import tokenserver.scripts
from tokenserver.assignment import INodeAssignment


logger = logging.getLogger("tokenserver.scripts.process_account_deletions")


def process_account_deletions(config_file, queue_name, aws_region=None,
                              queue_wait_time=20):
    """Process account-deletion events from an SQS queue.

    This function polls the specified SQS queue for account-deletion events,
    processing each as it is found.  It polls indefinitely and does not return;
    to interrupt execution you'll need to e.g. SIGINT the process.
    """
    logger.info("Processing account deletion events from %s", queue_name)
    logger.debug("Using config file %r", config_file)
    config = tokenserver.scripts.load_configurator(config_file)
    config.begin()
    try:
        backend = config.registry.getUtility(INodeAssignment)
        # Connect to the SQS queue.
        # If no region is given, infer it from the instance metadata.
        if aws_region is None:
            logger.debug("Finding default region from instance metadata")
            aws_info = boto.utils.get_instance_metadata()
            aws_region = aws_info["placement"]["availability-zone"][:-1]
        logger.debug("Connecting to queue %r in %r", queue_name, aws_region)
        conn = boto.sqs.connect_to_region(aws_region)
        queue = conn.get_queue(queue_name)
        # We must force boto not to b64-decode the message contents, ugh.
        queue.set_message_class(boto.sqs.message.RawMessage)
        # Poll for messages indefinitely.
        while True:
            msg = queue.read(wait_time_seconds=queue_wait_time)
            if msg is None:
                continue
            email = None
            # Try very hard not to error out if there's junk in the queue.
            try:
                body = json.loads(msg.get_body())
                event = json.loads(body['Message'])
                # The queue may contain other event types; ignore them.
                if event["event"] == "delete":
                    email = event["uid"]
            except (ValueError, KeyError), e:
                logger.exception("Invalid account-delete message: %s", e)
            else:
                # Mark the user as retired.
                # Actual cleanup is done by a separate process.
                if email is not None:
                    logger.info("Processing account deletion for %r", email)
                    backend.retire_user(email)
            queue.delete_message(msg)
    except Exception:
        logger.exception("Error while processing account deletion events")
        raise
    finally:
        config.end()


def main(args=None):
    """Main entry-point for running this script.

    This function parses command-line arguments and passes them on
    to the process_account_deletions() function.
    """
    usage = "usage: %prog [options] config_file queue_name"
    parser = optparse.OptionParser(usage=usage)
    parser.add_option("", "--aws-region",
                      help="aws region in which the queue can be found")
    parser.add_option("", "--queue-wait-time", type="int", default=20,
                      help="Number of seconds to wait for jobs on the queue")
    parser.add_option("-v", "--verbose", action="count", dest="verbosity",
                      help="Control verbosity of log messages")

    opts, args = parser.parse_args(args)
    if len(args) != 2:
        parser.print_usage()
        return 1

    tokenserver.scripts.configure_script_logging(opts)

    config_file = os.path.abspath(args[0])
    queue_name = args[1]

    process_account_deletions(config_file, queue_name,
                              opts.aws_region, opts.queue_wait_time)
    return 0


if __name__ == "__main__":
    tokenserver.scripts.run_script(main)

@@ -2482,16 +2482,76 @@
  = None%0A
+            event_type = None%0A            generation = None%0A
         
@@ -2756,63 +2756,35 @@
     
-# The queue may contain other event types; ignore them.
+event_type = event%5B%22event%22%5D
 %0A   
@@ -2800,18 +2800,23 @@
     
-if
+email =
  event%5B%22
 even
@@ -2815,26 +2815,53 @@
 nt%5B%22
-event%22%5D
+uid%22%5D%0A                if event_type
  == %22
-del
+res
 et
-e
 %22:%0A 
@@ -2879,34 +2879,46 @@
         
-email = event%5B%22uid
+generation = event%5B%22generation
 %22%5D%0A     
@@ -3011,15 +3011,8 @@
 ount
--delete
  mes
@@ -3043,16 +3043,109 @@
   else:%0A
+                if email is not None:%0A                    if event_type == %22delete%22:%0A        
         
@@ -3180,16 +3180,24 @@
 etired.%0A
+        
         
@@ -3268,38 +3268,184 @@
         
-if email is not None:%0A
+        logger.info(%22Processing account deletion for %25r%22, email)%0A                        backend.retire_user(email)%0A                    elif event_type == %22reset%22:%0A    
         
@@ -3484,32 +3484,29 @@
 ing account 
-deletion
+reset
  for %25r%22, em
@@ -3526,32 +3526,43 @@
             
+    
+user = 
 backend.
 retire_user(
@@ -3549,34 +3549,193 @@
 backend.
-retire_user(email)
+get_user(%22sync-1.5%22, email)%0A                        backend.update_user(%22sync-1.5%22, user, generation)%0A                    # The queue may contain other event types; ignore them.
 %0A       

add fxa account reset event processing