#!/usr/bin/env python
import sys
import os
import abstrys.cmd_utils as util

config_file = 'aws-config.txt'
switches = []
s3 = None  # the AWS::S3 client
s3_paths_to_process = []
access_key_id = None
secret_access_key = None

USAGE = """
s3del
=====

Delete an S3 bucket or its contents.

Usage
-----

::

    {s3del} [s3_path] ...

The s3 path can be repeated, so multiple buckets, partial paths or objects can
be provided at once.

Notes
-----

Before using this script, you must provide your AWS credentials to the program,
with any one of the following methods:

* Set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables
  with your AWS credentials.

* Provide a YAML-formatted file called `{configfile}` in the current
  directory with your aws credentials specified like this::

      ---
      access_key_id:     ACCESSKEYID
      secret_access_key: SECRETACCESKEY

  Replace ACCESSKEYID and SECRETACCESSKEY with your own AWS credentials.

  You can also specify the path to the config file by passing the
  ``--aws_config <config_file_path>`` argument with the path to the config file
  specified. You can use this argument to point to a config file with any name.

* Provide the command-line arguments: ``--access_key ACCESSKEYID`` and/or
  ``--secret_key SECRETACCESSKEY``, providing your own AWS credentials in place
  of ACCESSKEYID and SECRETACCESSKEY.

""".format(configfile=config_file, s3del=__file__)

def show_help():
    cmd_utils.format_doc(USAGE)
    sys.exit()

def get_help():
    print "Use '%s -h' to get help." % __file__
    sys.exit()

def process_args(args):
    """Interpret the args and set switches"""

    # provide access to global variables.
    global config_file
    global s3_paths_to_process
    global access_key_id
    global secret_access_key
    global switches

    # First, iterate through any switches. These always precede the other
    # arguments.
    i = 0
    while i < len(args) and args[i][0] == '-':
        if args[i][1] == '-':
            # Oooh, an extended command!
            ext_cmd = args[i][1:]
            if ext_cmd == '-access_key':
                i += 1 # read the next argument.
                access_key_id = args[i]
            elif ext_cmd == '-secret_key':
                i += 1 # read the next argument.
                secret_access_key = args[i]
            elif ext_cmd == '-aws_config':
                i += 1 # read the next argument.
                config_file = args[i]
        elif args[i][1] == 'h':
            print USAGE
            sys.exit()
        else:
            # store any other switches in the switches list.
            switches += args[i][1]
        # increment the counter.
        i += 1

    # done with switches, let's move on to s3 path arguments.
    for s3_path in args[i:]:
        s3_paths_to_process.append(s3_path)


def authenticate_s3(access_key_id, secret_access_key, config_file):
    """Authenticate with AWS and return an S3 object. Returns None if it failed
    to get the S3 object."""
    # Rules:
    #
    # 1. If the access_key_id and/or secret access key is specified, then use
    #    its values in preference to all others.
    #
    # 2. If a file exists, use its values in preference to environment
    #    variables.
    #
    # 3. If any credentials are still missing, look in the environment.

    if not (access_key_id and secret_access_key):
        # Command-line arguments didn't do the trick, so look for a file.
        if os.path.exists(config_file):
            # We're expecting a YAML-formatted file like this:
            #
            #     ---
            #     access_key_id:     ACCESSKEYID
            #     secret_access_key: SECRETACCESKEY
            #
            f = open(config_file, 'r')
            config = dict(yaml.safe_load(f))
            f.close()
            # Whatever command-line args *weren't* specified, fill them in with
            # details from the file.
            if not access_key_id:
                access_key_id = config['access_key_id']
            if not secret_access_key:
                secret_access_key = config['secret_access_key']

    if not (access_key_id and secret_access_key):
        # There are still some credentials missing. Look in the environment.
        if not access_key_id:
            access_key_id = os.environ.get('AWS_ACCESS_KEY_ID')
        if not secret_access_key:
            secret_access_key = os.environ.get('AWS_SECRET_ACCESS_KEY')

    s3 = None
    if access_key_id and secret_access_key:
        s3 = boto.connect_s3(access_key_id, secret_access_key)
    return s3


def split_s3_path(s3_path):
    """Get a bucket name and object path"""
    # Split the s3 path after the *first* slash character. The bucket name
    # can't have a slash in it; anything after the first slash is considered
    # the S3 object name.
    if '/' in s3_path:
        return s3_path.split('/', 1)
    else:
        return (s3_path, None)

#
# THE SCRIPT
#

# Check dependencies... these modules might not be installed on the system.
try:
    import boto
except ImportError, e:
    print "boto is not installed. Run 'pip install boto' on the command-line"
    print "to install it first."
    sys.exit(1)

try:
    import yaml
except ImportError, e:
    print "pyyaml is not installed. Run 'pip install pyyaml' on the command-line"
    print "to install it first."
    sys.exit(1)

if len(sys.argv) > 1:
    process_args(sys.argv[1:])
else:
    print "** you must supply at least one argument!"
    get_help()
    sys.exit()

if len(s3_paths_to_process) == 0:
    print "** no buckets or objects supplied as input. Nothing to do..."
    get_help();

s3 = authenticate_s3(access_key_id, secret_access_key, config_file)
if s3 is None:
    print "** could not make a connection to Amazon S3."
    get_help()
    sys.exit()

# If we have args (that is, we're here), they're either:
# - buckets to delete
# - partial paths to delete
# - exact keys to delete

keys_to_delete = []
buckets_to_delete = []

# gather the buckets and keys to delete.
for s3_path in s3_paths_to_process:
    bucket_name, object_name = split_s3_path(s3_path)

    # we must at *least* have the bucket name.
    if s3.lookup(bucket_name) is None:
        print "** no such bucket: %s" % bucket_name
        sys.exit(1)

    bucket = s3.get_bucket(bucket_name)

    # only delete a bucket if its name is provided without any additional path
    # elements.
    if object_name is None:
        # to delete the bucket, we need to delete all of its keys first.
        for s3key in bucket.list():
            keys_to_delete.append((bucket, s3key))
        buckets_to_delete.append(bucket)
    else:
        # first try to get an exact match
        s3key = bucket.get_key(object_name)
        if s3key is None:
            for s3key in bucket.list(object_name):
                keys_to_delete.append((bucket, s3key))
        else:
            keys_to_delete.append((bucket, s3key))

if len(keys_to_delete) > 0:
    print "The following S3 objects will be deleted:"
    for s3key in keys_to_delete:
        print "  %s/%s" % (s3key[0].name, s3key[1].name)
        #print "  %s" % s3key.name

if len(buckets_to_delete) > 0:
    print "The following S3 buckets will be deleted:"
    for s3bucket in buckets_to_delete:
        print "  %s" % s3bucket.name

if 'y' not in switches and not util.confirm("Proceed with delete?"):
    print "*Canceled Delete*\nWhew, that was close!"
    sys.exit()

# OK, we're deleting stuff now!

# delete keys first
if len(keys_to_delete) > 0:
    print "Deleting keys:"
    for s3key in keys_to_delete:
        print "  %s/%s" % (s3key[0].name, s3key[1].name)
        s3key[1].delete()

if len(buckets_to_delete) > 0:
    print "Deleting buckets:"
    for s3bucket in buckets_to_delete:
        print "  %s" % s3bucket.name
        s3bucket.delete()

print "Finished!"
