buechner.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #!/usr/bin/env python
  2. import os
  3. import sys
  4. from datetime import datetime
  5. from boto.s3.connection import S3Connection
  6. from boto.s3.key import Key
  7. # Relative path from directory of buechner.py. Default to src/static,
  8. # but script will attempt to pull from BUECHNER_STATIC_RELPATH env var.
  9. try:
  10. STATIC_DIR_REL = os.environ['BUECHNER_STATIC_RELPATH']
  11. except KeyError:
  12. STATIC_DIR_REL = os.path.join(
  13. 'src',
  14. 'static')
  15. def init_s3_interface(s3_bucket, access_key_id, secret_access_key):
  16. """
  17. Initialize the interface.
  18. Arguments are all strings: bucket name, key id, and secret key,
  19. respectively. Returns a list of boto.s3.connection.S3Connection,
  20. boto.s3.bucket.Bucket.
  21. """
  22. conn = S3Connection(access_key_id, secret_access_key)
  23. bucket = conn.get_bucket(s3_bucket)
  24. return conn, bucket
  25. def get_keys_from_directory(basedir):
  26. """
  27. Return dict of paths -> mtimes of files found recursively under `basedir`.
  28. Paths are relative to `basedir` with no leading slashes. Mtimes are
  29. datetime.datetime objects in UTC. Will not follow directory symlinks.
  30. Note: this will probably only work right on Unix, unless os.path.getmtime
  31. gives UTC on other platforms too.
  32. """
  33. results = []
  34. # Fill up results with base, namelist
  35. os.path.walk(
  36. basedir,
  37. (lambda x, y, z: results.append([y, z])),
  38. None)
  39. files = dict()
  40. for base, names in results:
  41. for name in names:
  42. fullpath = os.path.join(base, name)
  43. # only care about files
  44. if os.path.isfile(fullpath):
  45. mtime = datetime.utcfromtimestamp(os.path.getmtime(fullpath))
  46. relative_path = fullpath.replace(
  47. basedir, '').lstrip(os.path.sep)
  48. files[relative_path] = mtime
  49. return files
  50. def upload_new_files(staticdir, bucket):
  51. """
  52. Upload newer files recursively under `staticdir` to `bucket`.
  53. This assumes that the directory `staticdir` represents the root of
  54. the S3 bucket. `bucket` should be an instance of boto.s3.bucket.Bucket.
  55. Return a list of the files uploaded, with paths relative to `staticdir`.
  56. """
  57. allkeys = bucket.list()
  58. local_files_mtimes = get_keys_from_directory(staticdir)
  59. # `fmt` should be ISO 8601, but the time zone isn't parsed right when
  60. # given as %Z, so we hack it off below. Hopefully it's always Zulu time
  61. fmt = '%Y-%m-%dT%H:%M:%S.%f'
  62. # This is a dict of key_name -> [key_obj, key.last_modified]
  63. remote_files_mtimes_keys = dict(
  64. (
  65. k.name,
  66. [
  67. k,
  68. datetime.strptime(
  69. k.last_modified[:-1], # strip off Z at end
  70. fmt)
  71. ]
  72. ) for k in allkeys)
  73. uploaded_files = []
  74. for filepath, local_mtime in local_files_mtimes.iteritems():
  75. if filepath in remote_files_mtimes_keys:
  76. the_key, remote_mtime = remote_files_mtimes_keys[filepath]
  77. # Skip file if local is older
  78. if remote_mtime > local_mtime:
  79. continue
  80. else:
  81. the_key = Key(bucket)
  82. the_key.key = filepath
  83. uploaded_files.append(filepath)
  84. the_key.set_contents_from_filename(os.path.join(staticdir, filepath))
  85. the_key.set_acl('public-read')
  86. return uploaded_files
  87. if __name__ == '__main__':
  88. # If no AWS keys are found in environment, try to import the config file
  89. try:
  90. AWS_S3_BUCKET = os.environ['AWS_S3_BUCKET']
  91. AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
  92. AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
  93. print("Using environment config. Loading bucket '%s'" % (
  94. AWS_S3_BUCKET))
  95. except KeyError:
  96. print(
  97. 'Failed to find all environment variables, attempting to load '
  98. 'aws_config.py...')
  99. try:
  100. import aws_config
  101. AWS_S3_BUCKET = aws_config.AWS_S3_BUCKET
  102. AWS_ACCESS_KEY_ID = aws_config.AWS_ACCESS_KEY_ID
  103. AWS_SECRET_ACCESS_KEY = aws_config.AWS_SECRET_ACCESS_KEY
  104. print("Using aws_config.py config. Loading bucket '%s'" % (
  105. AWS_S3_BUCKET))
  106. except (ImportError, NameError, AttributeError) as e:
  107. print('Failed to locate AWS config, check environment or config')
  108. print("Error was: '%s'" % e)
  109. sys.exit(1)
  110. staticdir = os.path.join(
  111. os.path.dirname(os.path.realpath(__file__)),
  112. STATIC_DIR_REL)
  113. print("Will upload new files from '%s' to bucket '%s'." % (
  114. staticdir,
  115. AWS_S3_BUCKET))
  116. if raw_input('Continue? [Y]').strip().lower() != 'y':
  117. print("Exiting...")
  118. sys.exit(1)
  119. print("Preparing upload...")
  120. conn, bucket = init_s3_interface(
  121. AWS_S3_BUCKET,
  122. AWS_ACCESS_KEY_ID,
  123. AWS_SECRET_ACCESS_KEY)
  124. uploaded = upload_new_files(staticdir, bucket)
  125. for filename in uploaded:
  126. print("Uploaded '%s' to S3" % filename)
  127. print("Setting public read ACL on bucket '%s'..." % AWS_S3_BUCKET)
  128. bucket.set_acl('public-read')
  129. print("Complete!")