123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- import sys
- import os, os.path
- import logging
- import readline
- import getpass
- from boto.s3.key import Key
- import credentials
- import config
- import s3_util
- import util
- from prompt_util import get_yes_no, get_input, clear_screen
- logger = logging.getLogger("vaporfile.website")
- def prompt_create_website(args):
- #This is the information we'll gather:
- user_config = None
- bucket_name = None
- using_own_domain = None
- sync_path = None
- directory_index = None
- error_index = None
- use_existing_bucket = False
-
- clear_screen()
- print(" Amazon S3 Website Creation Wizard ".center(80,"+"))
- print("This will guide you through the process of creating a new website on Amazon S3.")
- print("No changes to your S3 account will occur until you accept changes at the end.")
- print("You may press Ctrl-C at any point to quit without saving.")
- print("".center(80,"+"))
- print("")
- user_config = config.get_config()
- try:
- user_config["credentials"]
- except KeyError:
- print("No existing account information found.")
- if get_yes_no("Would you like to setup your Amazon AWS account? [Y/n] : ",default=True):
- credentials.prompt_save_credentials({})
- else:
- print("")
- print("Cannot proceed without Amazon account information.")
- sys.exit(1)
- user_config = config.load_config()
- conn = s3_util.get_connection()
- using_own_domain = get_yes_no("Will you be using your own domain name? [y/n] : ")
- if using_own_domain:
- print("")
- print("Amazon S3 websites hosted using your own domain require a CNAME configured")
- print("with your DNS service. Unfortunately, this means that you cannot use your root")
- print("domain with S3, you must use a subdomain.")
- print("")
- while True:
- bucket_name = get_input("What is the the fully qualified subdomain you would like to use?\n[eg. www.yourdomain.com] : ",accept_blank=False)
- print("Checking to see if {0} is available...".format(bucket_name))
- if not s3_util.test_bucket_exists(conn, bucket_name):
- break
- print("")
- #The bucket already exists.. by the user?
- if bucket_name in s3_util.get_bucket_names(conn):
- print("It looks like you've already configured an S3 bucket "
- "with that name.\n")
- print("WARNING: If you proceed, existing files in this "
- "bucket may be lost!")
- if get_yes_no("Are you sure you want to use {0}? [y/n]"\
- .format(bucket_name)):
- use_existing_bucket = True
- break
- print("")
- else:
- print("Sorry, it looks like someone else owns that bucket name. Contact Amazon support")
- print("for help if you own the domain you chose. This is an unfortunate side-effect of")
- print("Amazon choosing a globally flat namespace for buckets.")
- print("")
- else:
- print("")
- print("Without using your own domain, you will need to create a unique bucket name.")
- print("You will only be able to access your website through the bucket address")
- print("Amazon provides, which is a bit long and cumbersome.")
- print("Example: yourwebsite.s3-website-us-east-1.amazonaws.com")
- print("")
- while True:
- bucket_name = get_input("What is the bucket name you would like to use?\n[eg. yourwebsite] : ")
- print("Checking to see if {0} is available...".format(bucket_name))
- if not s3_util.test_bucket_exists(conn, bucket_name):
- break
- print("")
- print("Sorry, that bucketname is already used. Try another.")
- clear_screen()
- print(" Local Path Selection ".center(80,"+"))
- print("This tool works by synchronizing a local directory to your S3 bucket.")
- print("Each time you synchronize, this tool will upload new or changed files")
- print("as well as delete any files no longer found in the local directory.")
- print("".center(80,"+"))
- print("")
- while True:
- sync_path = get_input("What is the full local directory path you want "
- "synchronized?\n[eg. /home/{0}/website ] : ".\
- format(getpass.getuser()), accept_blank=False)
- if os.path.isdir(sync_path):
- break
- elif os.path.isfile(sync_path):
- print("Sorry, that's not a directory. Please try again.")
- else:
- if get_yes_no("This directory does not exist. Would you like to create it? (y/n)"):
- try:
- util.mkdir(sync_path)
- except OSError:
- print("Permission denied. Try again.")
- else:
- break
- print("")
- clear_screen()
- print(" Configuration Options ".center(80,"+"))
- print("")
- if get_yes_no("Would you like to use index.html as your default index file? (Y/n)", default=True):
- directory_index = "index.html"
- else:
- while True:
- print("What file would you like to serve when directories are requested?")
- directory_index = get_input("[eg. index.html] : ")
- if directory_index != "":
- break
- print("You must enter a directory index. Most users should choose index.html")
- print("")
- if get_yes_no("Would you like a special 404 handler when files aren't found? (y/N) : ", default=False):
- while True:
- print("What file would you like to serve when files aren't found?")
- error_index = get_input("[eg. 404.html] : ")
- if error_index != "":
- break
- print("")
- clear_screen()
- print(" Confirmation ".center(80,"+"))
- print("OK, we've gathered all the necessary information about your new website.")
- print("Let's review:")
- print("")
- if using_own_domain:
- print(" Your domain name:".ljust(35)+bucket_name)
- print(" Amazon S3 bucket name:".ljust(35)+bucket_name)
- print(" Local path to synchronize:".ljust(35)+sync_path)
- print(" Index file:".ljust(35)+directory_index)
- if error_index:
- print(" Error index file:".ljust(35)+error_index)
- print("")
- if get_yes_no("Would you like to save this configuration now? [y/n] : "):
- website = S3Website(
- bucket_name, sync_path, index=directory_index,
- error_doc=error_index)
- user_config["websites"][bucket_name] = website.to_config()
- website.create(use_existing_bucket=use_existing_bucket)
- if not use_existing_bucket:
- print("Amazon S3 bucket created!")
- config.save_config(user_config)
- print("Website configuration saved!")
- website_endpoint = website.get_bucket().get_website_endpoint()
- s3_endpoint = website_endpoint.replace(bucket_name+".","")
- print("Your Amazon website endpoint: {0}".format(website_endpoint))
- if using_own_domain:
- print("Your DNS service needs a CNAME record pointing {0} "
- "to {1}".format(bucket_name, s3_endpoint))
- print("")
- print("To upload your website run this command:")
- print("")
- print(" vaporfile -v upload {0}".format(bucket_name))
- print("")
- def upload_website(args):
- user_config = config.get_config()
- try:
- website = S3Website.from_config(user_config["websites"][args.WEBSITE])
- except KeyError:
- print("")
- print("Can't find a website configuration called {0}".format(args.WEBSITE))
- print("Maybe you need to create it first? Run: vaporfile create")
- return
- try:
- credentials.check_credentials(user_config)
- except credentials.VaporfileCredentialException:
- print("")
- print("Can't find credentials. You need to run: vaporfile "
- "credentials store")
- print("")
- return
- website.synchronize(delete=not args.no_delete)
- def remove_website(args):
- user_config = config.get_config()
- try:
- site = user_config["websites"][args.WEBSITE]
- except KeyError:
- print("")
- print("Unknown website configuration : {0}".format(args.WEBSITE))
- print("")
- else:
- del user_config["websites"][args.WEBSITE]
- config.save_config(user_config)
- print("")
- print("Local website configuration removed : {0}".format(args.WEBSITE))
- print("")
-
-
- def list_websites(args):
- try:
- user_config = config.load_config()
- except IOError:
- print("")
- print("Can't find a configuration. You need to run: vaporfile create")
- print("")
- return
- if len(user_config["websites"]) == 0:
- print("")
- print("No websites have been created yet. You need to run: vaporfile create")
- print("")
- return
- for name, website in user_config["websites"].items():
- print((" "+name).ljust(35)+"- "+website["localpath"])
-
-
- class S3Website(object):
- """Tool for maintaining a static website in S3"""
- def __init__(self, bucketname, localpath, index="index.html",
- error_doc="404.html",**kwargs):
- self.bucketname = bucketname
- self.localpath = localpath
- self.index = index
- self.error_doc = error_doc
- def to_config(self):
- return {"bucketname":self.bucketname,
- "localpath":self.localpath,
- "index":self.index,
- "error_doc":self.error_doc}
- @classmethod
- def from_config(cls, config_dict):
- return cls(**config_dict)
- def get_bucket(self):
- return self.get_connection().get_bucket(self.bucketname)
- def get_connection(self):
- return s3_util.get_connection()
- def create(self, use_existing_bucket=False):
- """Create the bucket for the subdomain."""
- #Check if the bucket name already exists in our account,
- #boto doesn't tell us this.
- connection = self.get_connection()
- if use_existing_bucket:
- bucket = connection.get_bucket(self.bucketname)
- else:
- if self.bucketname in s3_util.get_bucket_names(connection):
- raise Exception("Bucket '{0}' already exists in your account."\
- .format(self.bucketname))
- bucket = connection.create_bucket(self.bucketname)
- logger.info("Created new bucket : {0}".format(self.bucketname))
- #A website should be publically readable:
- bucket.set_acl("public-read")
- #Turn on website functionality:
- if self.error_doc:
- bucket.configure_website(self.index, self.error_doc)
- else:
- bucket.configure_website(self.index)
- def synchronize(self, delete=False):
- """Synchronize the localpath to S3.
- Upload new or changed files.
- Delete files that no longer exist locally."""
- bucket = self.get_bucket()
- s3_paths = s3_util.get_paths_from_keys(bucket)
- local_files = set()
- for dirpath, dirnames, filenames in os.walk(self.localpath):
- for filename in filenames:
- file_path = os.path.join(dirpath,filename)
- file_key = os.path.relpath(file_path,self.localpath)
- if os.sep == "\\":
- #Windows paths need conversion
- file_key = file_key.replace("\\","/")
- local_files.add(file_key)
- try:
- s3_key = s3_paths[file_key]
- except KeyError:
- #File is new
- s3_key = bucket.new_key(file_key)
- logger.info("Uploading new file: {0}".format(file_key))
- s3_key.set_contents_from_filename(file_path)
- s3_key.set_acl("public-read")
- else:
- #File already exists, check if it's changed.
- local_md5 = util.md5_for_file(file_path)
- if local_md5 != s3_key.etag.replace("\"",""):
- #File has changed
- logger.info("Uploading changed file: {0}".format(file_key))
- s3_key.set_contents_from_filename(file_path)
- s3_key.set_acl("public-read")
- if delete:
- #Delete all files that don't exist locally
- for name, key in s3_paths.items():
- if name not in local_files:
- #Delete it.
- logger.info("Deleting old file: {0}".format(name))
- key.delete()
-
|