Commit 6c9f605142aeea3581e3743bc3a423de24f3b33a

Authored by nollieheel
1 parent 14c62b36

Bump to v0.3.0. Enable multiple instances of script. Enable encryption. Add time…

…stam to stored backups.
@@ -8,16 +8,16 @@ driver: @@ -8,16 +8,16 @@ driver:
8 subnet_id: subnet-d530d8b1 8 subnet_id: subnet-d530d8b1
9 instance_type: t2.micro 9 instance_type: t2.micro
10 associate_public_ip: true 10 associate_public_ip: true
11 - require_chef_omnibus: true 11 + require_chef_omnibus: 12.12.15
12 shared_credentials_profile: earth 12 shared_credentials_profile: earth
13 13
14 provisioner: 14 provisioner:
15 - name: chef_solo 15 + name: chef_zero
16 16
17 platforms: 17 platforms:
18 - name: ubuntu-14.04 18 - name: ubuntu-14.04
19 driver: 19 driver:
20 - image_id: ami-50946030 20 + image_id: ami-548e2d34
21 transport: 21 transport:
22 username: ubuntu 22 username: ubuntu
23 ssh_key: ~/.ssh/cfe_stg_20160222.pem 23 ssh_key: ~/.ssh/cfe_stg_20160222.pem
1 -# 0.2.1 1 +## 0.3.0 - 2016-11-25
  2 +### Changed
  3 +- 'dirs' attribute has been replaced with 'backups' which gives the option of creating multiple instances of the script, each having a different filename.
  4 +- Maximum number of backup copies can now be specified for each file/directory to be backed up.
  5 +- Attributes for cron entries have been simplified.
  6 +- Definition for 'aws_tar_extract' now includes options for decrypting encrypted tarballs. The contents of the private key file must be provided in this case.
2 7
3 -Include tar cookbook in default recipe. 8 +### Added
  9 +- An option whether or not to use encryption for each file/directory backup is provided. If encryption is used, proper encryption keys must be provided beforehand as node attributes.
  10 +- Backup copies stored in S3 are now stored in their own subfolders (prefixes) along with a text file indicating the timestamp for when that backup was originally uploaded.
4 11
5 -# 0.2.0 12 +## 0.2.1
  13 +- Include tar cookbook in default recipe.
6 14
7 -Add definition aws_tar_extract to abstract reloading of backups. 15 +## 0.2.0
  16 +- Add definition aws_tar_extract to abstract reloading of backups.
8 17
9 -# 0.1.0  
10 -  
11 -Initial release of backup-file2s3 18 +## 0.1.0
  19 +- Initial release of backup-file2s3
1 # backup-file2s3-cookbook 1 # backup-file2s3-cookbook
2 2
3 -Installs a script that backs up one or more directories into an S3 bucket. Also sets up the cronjob to regularly run said script. 3 +Installs script/s that backs up one or more files/directories into an S3 bucket. Also sets up the cronjob to regularly run said script/s.
4 4
5 -There is also a definition `aws_tar_extract` to recover those backup files and unpack them. 5 +There is an option to encrypt the backups with OpenSSL. If encryption is used, the contents of the public PEM key must be provided.
  6 +
  7 +There is also a definition `aws_tar_extract` to recover those backup files and unpack them. This definition can also decrypt the tarballs using private key contents in a node attribute (default), or explicitly passed to the definition.
6 8
7 ## Supported Platforms 9 ## Supported Platforms
8 10
@@ -30,21 +32,27 @@ Ubuntu 14.04 @@ -30,21 +32,27 @@ Ubuntu 14.04
30 <td><tt>'us-east-1'</tt></td> 32 <td><tt>'us-east-1'</tt></td>
31 </tr> 33 </tr>
32 <tr> 34 <tr>
33 - <td><tt>['backup-file2s3']['max_backups']</tt></td>  
34 - <td>Integer</td>  
35 - <td>Number of old backup tarballs to retain in S3.</td>  
36 - <td><tt>30</tt></td>  
37 - </tr>  
38 - <tr>  
39 - <td><tt>['backup-file2s3']['dirs']</tt></td> 35 + <td><tt>['backup-file2s3']['backups']</tt></td>
40 <td>Array</td> 36 <td>Array</td>
41 - <td>An array of directories to be backed up.</td> 37 + <td>An array of hashes that details the script names and directories (or files) that are to be backed up. This is also where you specify whether or not you want to encrypt the backup. Please see the default attribs file for more details.</td>
42 <td><tt>[]</tt></td> 38 <td><tt>[]</tt></td>
43 </tr> 39 </tr>
44 <tr> 40 <tr>
45 - <td><tt>['backup-file2s3']['cron']['min']</tt></td> 41 + <td><tt>['backup-file2s3']['cron']['sched']</tt></td>
  42 + <td>String</td>
  43 + <td>Crontab syntax for scheduling how often the backup script will run.</td>
  44 + <td><tt>'0 0 * * *'</tt></td>
  45 + </tr>
  46 + <tr>
  47 + <td><tt>['backup-file2s3']['encrypt']['pub_key']</tt></td>
  48 + <td>String</td>
  49 + <td>If encryption is used by any of the paths under 'backups', then this attribute should contain the public key used to encrypt.</td>
  50 + <td><tt>nil</tt></td>
  51 + </tr>
  52 + <tr>
  53 + <td><tt>['backup-file2s3']['encrypt']['priv_key']</tt></td>
46 <td>String</td> 54 <td>String</td>
47 - <td>Related cron attributes are: `hour`, `day`, `mon`, `wday`, each specifying a corresponding crontab value. This cron job will determine how often the backup script is run.</td> 55 + <td>If an encrypted tarball is fetched using 'aws_tar_extract', then this attribute should contain the default private key to decrypt. Although the private key can also be explicitly passed to the 'aws_tar_extract' definition.</td>
48 <td><tt>nil</tt></td> 56 <td><tt>nil</tt></td>
49 </tr> 57 </tr>
50 </table> 58 </table>
@@ -18,28 +18,36 @@ @@ -18,28 +18,36 @@
18 # limitations under the License. 18 # limitations under the License.
19 # 19 #
20 20
21 -default['backup-file2s3']['bucket'] = 'bucketname'  
22 -default['backup-file2s3']['region'] = 'us-east-1'  
23 -default['backup-file2s3']['max_backups'] = 30 21 +default['backup-file2s3']['bucket'] = 'bucketname'
  22 +default['backup-file2s3']['region'] = 'us-east-1'
24 23
25 -# The array of file directories to be backed up to S3  
26 -default['backup-file2s3']['dirs'] = [] 24 +# Contains hashes with details of file/directories to be backed up to S3.
  25 +default['backup-file2s3']['backups'] = [
  26 +# {
  27 +# :script_name => 'default', # default: 'default', but only
  28 +# # one default can exist at a time
  29 +# :enable => true # default: true
  30 +# :paths => [
  31 +# {
  32 +# :path => '/my/dir',
  33 +# :bak_filename => 'dir', # default: basename of path
  34 +# :bak_encrypted => false, # default: false
  35 +# :bak_maxcopies => 30 # default: 30
  36 +# }
  37 +# ]
  38 +# }
  39 +]
27 40
28 -default['backup-file2s3']['script_dir'] = '/etc/backup_file_to_s3'  
29 -default['backup-file2s3']['log_dir'] = '/var/log/backup_file_to_s3'  
30 -default['backup-file2s3']['tmp_dir'] = '/tmp/backup_files' 41 +# Encryption keys (string contents of the actual keys, if used)
  42 +default['backup-file2s3']['encrypt']['pub_key'] = nil
  43 +default['backup-file2s3']['encrypt']['priv_key'] = nil
31 44
32 -default['backup-file2s3']['aws_bin'] = value_for_platform(  
33 - 'ubuntu' => { 'default' => '/usr/local/bin/aws' },  
34 - 'default' => '/usr/local/bin/aws' # haven't tested on other platforms yet  
35 -) 45 +default['backup-file2s3']['script_dir'] = '/etc/backup_file_to_s3'
  46 +default['backup-file2s3']['log_dir'] = '/var/log/backup_file_to_s3'
  47 +default['backup-file2s3']['tmp_dir'] = '/tmp/backup_files'
36 48
37 # Basic options for cron 49 # Basic options for cron
38 -default['backup-file2s3']['cron']['min'] = '0'  
39 -default['backup-file2s3']['cron']['hour'] = '0'  
40 -default['backup-file2s3']['cron']['day'] = '*'  
41 -default['backup-file2s3']['cron']['mon'] = '*'  
42 -default['backup-file2s3']['cron']['wday'] = '*' 50 +default['backup-file2s3']['cron']['sched'] = '0 0 * * *'
43 default['backup-file2s3']['cron']['mailto'] = "''" 51 default['backup-file2s3']['cron']['mailto'] = "''"
44 52
45 # Basic options for logrotate 53 # Basic options for logrotate
@@ -51,3 +59,10 @@ default['backup-file2s3']['logrotate']['options'] = %w{ @@ -51,3 +59,10 @@ default['backup-file2s3']['logrotate']['options'] = %w{
51 compress 59 compress
52 notifempty 60 notifempty
53 } 61 }
  62 +
  63 +# Constants
  64 +default['backup-file2s3']['tar_bin'] = '/bin/tar'
  65 +default['backup-file2s3']['aws_bin'] = value_for_platform(
  66 + 'ubuntu' => { 'default' => '/usr/local/bin/aws' },
  67 + 'default' => '/usr/local/bin/aws' # haven't tested on other platforms yet
  68 +)
@@ -19,45 +19,79 @@ @@ -19,45 +19,79 @@
19 # 19 #
20 20
21 # Gets a tarball from AWS S3, then unpack it into a directory. 21 # Gets a tarball from AWS S3, then unpack it into a directory.
22 -# Parameters (all mandatory): 22 +# Parameters:
23 # :name | :file => The name of the backup tarball, without the extension 23 # :name | :file => The name of the backup tarball, without the extension
24 # :region => AWS region 24 # :region => AWS region
25 # :bucket => AWS bucket 25 # :bucket => AWS bucket
26 # :target_dir => Where the tarball is to be unpacked. If not 26 # :target_dir => Where the tarball is to be unpacked. If not
27 # exists, it will be created 27 # exists, it will be created
28 # :creates => A file path used for idempotency 28 # :creates => A file path used for idempotency
  29 +# :encrypted => Boolean. Whether these backup files are encrypted.
  30 +# :priv_key => String. Contents of private key, if used.
29 31
30 define :aws_tar_extract, 32 define :aws_tar_extract,
31 :file => nil, # default is params[:name] 33 :file => nil, # default is params[:name]
32 - :region => 'us-east-1', 34 + :region => nil,
33 :bucket => nil, 35 :bucket => nil,
34 :target_dir => nil, 36 :target_dir => nil,
35 - :creates => nil do  
36 - file = params[:file] || params[:name] 37 + :creates => nil,
  38 + :encrypted => false,
  39 + :priv_key => nil do
37 40
38 - tmp_dir = ::File.join(Chef::Config[:file_cache_path], 'backups')  
39 - filetgz = "#{tmp_dir}/#{file}.tar.gz" 41 + fname = params[:file] || params[:name]
  42 + region = params[:region] || node['backup-file2s3']['region']
  43 + bucket = params[:bucket] || node['backup-file2s3']['bucket']
  44 + priv_key = params[:priv_key] || node['backup-file2s3']['encrypt']['priv_key']
  45 + tmp_dir = ::File.join(Chef::Config[:file_cache_path], 'f2s3_backups')
40 46
41 include_recipe 'awscli' 47 include_recipe 'awscli'
42 include_recipe 'tar' 48 include_recipe 'tar'
43 49
44 - [ tmp_dir, params[:target_dir] ].each do |ndir|  
45 - directory ndir do  
46 - recursive true 50 + unless ::File.exist?(params[:creates])
  51 +
  52 + directory(tmp_dir) { recursive true }
  53 + directory(params[:target_dir]) { recursive true }
  54 +
  55 + file_priv_key = "#{tmp_dir}/priv.key"
  56 + fname_tgz = "#{tmp_dir}/#{fname}.tar.gz"
  57 + fname_path = "#{tmp_dir}/#{fname}.tar.gz"
  58 +
  59 + if params[:encrypted]
  60 + fname_path << '.enc'
  61 +
  62 + file file_priv_key do
  63 + content priv_key
  64 + mode 0600
  65 + sensitive true
  66 + end
  67 +
  68 + execute "decrypt_#{fname}" do
  69 + command "openssl smime -decrypt -binary -inkey #{file_priv_key} "\
  70 + "-in #{fname_path} -out #{fname_tgz} -inform DEM"
  71 + notifies :delete, "file[#{fname_path}]"
  72 + notifies :delete, "file[#{file_priv_key}]"
  73 + action :nothing
  74 + end
  75 +
  76 + file(fname_path) { action :nothing }
47 end 77 end
48 - end  
49 78
50 - unless ::File.exist?(params[:creates])  
51 - awscli_s3_file filetgz do  
52 - region params[:region]  
53 - bucket params[:bucket]  
54 - key "#{file}.tar.gz" 79 + awscli_s3_file fname_path do
  80 + region region
  81 + bucket bucket
  82 + key "#{fname}/#{::File.basename(fname_path)}"
  83 + if params[:encrypted]
  84 + notifies :run, "execute[decrypt_#{fname}]", :immediately
  85 + end
55 end 86 end
56 87
57 - tar_extract filetgz do 88 + tar_extract fname_tgz do
58 action :extract_local 89 action :extract_local
59 target_dir params[:target_dir] 90 target_dir params[:target_dir]
60 creates params[:creates] 91 creates params[:creates]
  92 + notifies :delete, "file[#{fname_tgz}]"
61 end 93 end
  94 +
  95 + file(fname_tgz) { action :nothing }
62 end 96 end
63 end 97 end
@@ -4,7 +4,7 @@ maintainer_email 'sysadmin@chromedia.com' @@ -4,7 +4,7 @@ maintainer_email 'sysadmin@chromedia.com'
4 license 'Apache License' 4 license 'Apache License'
5 description 'Creates a script to backup directories into an S3 bucket.' 5 description 'Creates a script to backup directories into an S3 bucket.'
6 long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) 6 long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
7 -version '0.2.1' 7 +version '0.3.0'
8 8
9 depends 'awscli', '~> 1.0.1' 9 depends 'awscli', '~> 1.0.1'
10 depends 'cron', '~> 1.7.4' 10 depends 'cron', '~> 1.7.4'
@@ -22,40 +22,70 @@ include_recipe 'awscli' @@ -22,40 +22,70 @@ include_recipe 'awscli'
22 include_recipe 'tar' 22 include_recipe 'tar'
23 23
24 attribs = node['backup-file2s3'] 24 attribs = node['backup-file2s3']
25 -scr_dir = attribs['script_dir']  
26 -sname = 'backup_file_to_s3' 25 +pub_key_file = "#{attribs['script_dir']}/pub.key"
27 26
28 -template "#{scr_dir}/#{sname}" do  
29 - mode 0644  
30 - only_if "test -d #{scr_dir} || mkdir -p #{scr_dir}"  
31 - variables(  
32 - :aws_bin => attribs['aws_bin'],  
33 - :log_dir => attribs['log_dir'],  
34 - :tmp_dir => attribs['tmp_dir'],  
35 - :bucket => attribs['bucket'],  
36 - :region => attribs['region'] || 'us-east-1',  
37 - :max_backups => attribs['max_backups'] || 30,  
38 - :dirs => attribs['dirs']  
39 - ) 27 +directory attribs['script_dir'] { recursive true }
  28 +directory attribs['log_dir'] { recursive true }
  29 +
  30 +is_any_enc = attribs['backups'].any? do |back|
  31 + back[:paths].any? do |path|
  32 + path[:bak_encrypted]
  33 + end
40 end 34 end
  35 +if !attribs['encrypt']['pub_key'] && is_any_enc
  36 + Chef::Application.fatal!('No encryption public key contents supplied')
  37 +end
  38 +
  39 +file pub_key_file do
  40 + content attribs['encrypt']['pub_key']
  41 + mode 0600
  42 + owner 'root'
  43 + group 'root'
  44 + sensitive true
  45 + only_if { attribs['encrypt']['pub_key'] }
  46 +end
  47 +
  48 +attribs['backups'].each do |back|
  49 + snam = back[:script_name] || 'default'
  50 + sname = "#{snam.gsub(' ', '-')}_backup2s3"
  51 + enable = back.has_key?(:enable) ? back[:enable] : true
  52 +
  53 + template "#{attribs['script_dir']}/#{sname}" do
  54 + mode 0644
  55 + source 'backup_file_to_s3.erb'
  56 + variables(
  57 + :aws_bin => attribs['aws_bin'],
  58 + :tar_bin => attribs['tar_bin'],
  59 + :tmp_dir => attribs['tmp_dir'],
  60 + :bucket => attribs['bucket'],
  61 + :region => attribs['region'],
  62 + :pub_key_file => pub_key_file,
  63 + :paths => back[:paths]
  64 + )
  65 + action( enable ? :create : :delete )
  66 + end
  67 +
  68 + sched = attribs['cron']['sched'].split(' ')
  69 + cron_d sname do
  70 + command "bash #{attribs['script_dir']}/#{sname} >> "\
  71 + "#{attribs['log_dir']}/#{sname}.log 2>&1"
  72 + minute sched[0]
  73 + hour sched[1]
  74 + day sched[2]
  75 + month sched[3]
  76 + weekday sched[4]
  77 + mailto attribs['cron']['mailto']
  78 + path '/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin'
41 79
42 -cra = attribs['cron']  
43 -cron_d sname do  
44 - command "bash #{scr_dir}/#{sname}"  
45 - minute cra['min']  
46 - hour cra['hour']  
47 - day cra['day']  
48 - month cra['mon']  
49 - weekday cra['wday']  
50 - mailto cra['mailto']  
51 - path '/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin' 80 + action( enable ? :create : :delete )
  81 + end
52 end 82 end
53 83
54 package 'logrotate' 84 package 'logrotate'
55 85
56 loa = attribs['logrotate'] 86 loa = attribs['logrotate']
57 -template "#{loa['conf_dir']}/#{sname}" do  
58 - source "#{sname}_logrotate.erb" 87 +template "#{loa['conf_dir']}/backup_file_to_s3" do
  88 + source 'backup_file_to_s3_logrotate.erb'
59 only_if "test -d #{loa['conf_dir']} || mkdir -p #{loa['conf_dir']}" 89 only_if "test -d #{loa['conf_dir']} || mkdir -p #{loa['conf_dir']}"
60 variables( 90 variables(
61 :log_dir => attribs['log_dir'], 91 :log_dir => attribs['log_dir'],
@@ -2,59 +2,66 @@ @@ -2,59 +2,66 @@
2 # 2 #
3 # Generated by Chef 3 # Generated by Chef
4 # 4 #
5 -# Back up directories into an S3 bucket 5 +# Back up directories/files into an S3 bucket
6 6
7 set -e 7 set -e
8 8
9 suffix=.backup_file_to_s3 9 suffix=.backup_file_to_s3
10 -[ -f /tmp/*"$suffix" ] && exit 200  
11 -tmp_file=$( mktemp --suffix "$suffix" )  
12 -  
13 -log_dir=<%= @log_dir %>  
14 -if [[ ! -d "$log_dir" ]] ; then  
15 - mkdir -p "$log_dir" 10 +if [ -f /tmp/*"$suffix" ] ; then
  11 + ( >&2 echo "[ERROR] Another operation might still be in progress" )
  12 + exit 200
16 fi 13 fi
17 -  
18 -aws_cmd=<%= @aws_bin %>  
19 -  
20 -exec 3>&1 4>&2  
21 -trap 'exec 2>&4 1>&3' 0 1 2 3  
22 -exec 1>>"${log_dir}/backup_file_to_s3.log" 2>&1 14 +tmp_file=$( mktemp --suffix "$suffix" )
23 15
24 bucket=<%= @bucket %> 16 bucket=<%= @bucket %>
25 region=<%= @region %> 17 region=<%= @region %>
26 -max_backups=<%= @max_backups %>  
27 bak_dir=<%= @tmp_dir %> 18 bak_dir=<%= @tmp_dir %>
28 19
29 -# Array of directories to be backed up.  
30 -#  
31 -# Example:  
32 -# declare -a tar_dirs=(  
33 -# "/path/to/dira"  
34 -# "/another/path/to/dirb"  
35 -# )  
36 -#  
37 -# Tarball names will be the basename of each path given.  
38 -declare -a tar_dirs=(  
39 -<% @dirs.each do |dirx| -%>  
40 - "<%= dirx %>"  
41 -<% end -%>  
42 -) 20 +aws_bin=<%= @aws_bin %>
  21 +tar_bin=<%= @tar_bin || '/bin/tar' %>
43 22
44 -if [[ ! -d "$bak_dir" ]] ; then  
45 - echo "$(date) : Missing backup directory. Creating."  
46 - mkdir -p "$bak_dir"  
47 -fi 23 +pub_key_file=<%= @pub_key_file %>
  24 +
  25 +# Create the tarball.
  26 +# Args:
  27 +# $1 = full path to be backed up, e.g. '/var/src/mydir'
  28 +# $2 = desired filename, sans '.tar.gz' extension, e.g. 'myfilename'
  29 +tar_dir() {
  30 + bname=$( basename "$1" )
  31 + dname=$( dirname "$1" )
  32 +
  33 + echo "$(date) : Creating tarball from ${1}"
  34 + "$tar_bin" -C "${dname}" -czf "${bak_dir}/${2}.tar.gz" "$bname"
  35 +}
  36 +
  37 +# Encrypt a tarball using OpenSSL and a given public key.
  38 +# The original file will be replaced by a new one with the same filename,
  39 +# suffixed with '.enc'.
  40 +#
  41 +# Given file should be located in $bak_dir.
  42 +# Args:
  43 +# $1 = filename, sans '.tar.gz' extension, e.g. 'myfilename'
  44 +encrypt_tarball() {
  45 + echo "$(date) : Encrypt file ${1}.tar.gz"
  46 + openssl smime -encrypt -binary -text -aes256 -in "${bak_dir}/${1}.tar.gz" \
  47 + -out "${bak_dir}/${1}.tar.gz.enc" -outform DER "${pub_key_file}"
  48 + rm "${bak_dir}/${1}.tar.gz"
  49 +}
48 50
49 -# Rotate the current backups in S3  
50 -# $1 = directory to be tarred 51 +# Rotate the number suffixes of the current backups in S3
  52 +# Args:
  53 +# $1 = filename, sans '.tar.gz' extension, e.g. 'myfilename'
  54 +# $2 = max number of backup copies to store at a time
51 increment_backup_names() { 55 increment_backup_names() {
52 - fname=$( basename "$1" )  
53 - bak_keyname="${fname}.tar.gz" 56 + bak_keyname=$1
  57 + max_backups=$2
54 58
55 - baks=$( "$aws_cmd" --output text --region "$region" \  
56 - s3api list-objects --bucket "$bucket" \  
57 - | grep '^CONTENTS' | cut -f3 | grep "^${bak_keyname}" || echo "" ) 59 + # Backups will stored inside subdirectories (prefixes)
  60 + # in S3, so only look at 'PRE' objects.
  61 + baks=$( "$aws_bin" --output text --region "$region" \
  62 + s3 ls "s3://${bucket}/" | grep '^\s*PRE' | \
  63 + sed -e 's/^ *PRE //' -e 's/\/$//' | \
  64 + grep "^${bak_keyname}" || echo "" )
58 65
59 echo "$(date) : Backup rotation for ${bak_keyname}" 66 echo "$(date) : Backup rotation for ${bak_keyname}"
60 start=$((max_backups - 1)) 67 start=$((max_backups - 1))
@@ -63,48 +70,70 @@ increment_backup_names() { @@ -63,48 +70,70 @@ increment_backup_names() {
63 if echo "$baks" | grep "^${bak_keyname}\\.${x}\$" ; then 70 if echo "$baks" | grep "^${bak_keyname}\\.${x}\$" ; then
64 newx=$((x + 1)) 71 newx=$((x + 1))
65 if [[ $newx -lt $max_backups ]] ; then 72 if [[ $newx -lt $max_backups ]] ; then
66 - "$aws_cmd" --region "$region" \  
67 - s3 cp "s3://${bucket}/${bak_keyname}.${x}" \ 73 + "$aws_bin" --region "$region" \
  74 + s3 mv --recursive "s3://${bucket}/${bak_keyname}.${x}" \
68 "s3://${bucket}/${bak_keyname}.${newx}" 75 "s3://${bucket}/${bak_keyname}.${newx}"
  76 + else
  77 + "$aws_bin" --region "$region" \
  78 + s3 rm --recursive "s3://${bucket}/${bak_keyname}.${x}"
69 fi 79 fi
70 fi 80 fi
71 done 81 done
72 82
73 if echo "$baks" | grep "^${bak_keyname}\$" ; then 83 if echo "$baks" | grep "^${bak_keyname}\$" ; then
74 - "$aws_cmd" --region "$region" \  
75 - s3 cp "s3://${bucket}/${bak_keyname}" \ 84 + "$aws_bin" --region "$region" \
  85 + s3 mv --recursive "s3://${bucket}/${bak_keyname}" \
76 "s3://${bucket}/${bak_keyname}.1" 86 "s3://${bucket}/${bak_keyname}.1"
77 fi 87 fi
78 } 88 }
79 89
80 -# Tar up the directory  
81 -# $1 = directory to be tarred  
82 -tar_dir() {  
83 - fname=$( basename "$1" )  
84 - parent=$( dirname "$1" )  
85 - echo "$(date) : Tar up ${1}" 90 +# Upload the tarball to the S3 bucket. It will be uploaded
  91 +# to this location: ${bucket}/basename/myfile.tar.gz.enc
  92 +#
  93 +# A timestamp file will also be uploaded to this location:
  94 +# ${bucket}/basename/YYYY-MM-DDThh:mm:ss.txt
  95 +# Args:
  96 +# $1 = filename, sans '.tar.gz' extension, e.g. 'myfilename'
  97 +# $2 = if file is encrypted or not (boolean)
  98 +upload_to_s3() {
  99 + bak_keyname=$1
  100 + if [ "$2" = true ] ; then
  101 + fname="${1}.tar.gz.enc"
  102 + else
  103 + fname="${1}.tar.gz"
  104 + fi
86 105
87 - tar -C "$parent" -czf "${bak_dir}/${fname}.tar.gz" "$fname"  
88 -} 106 + echo "$(date) : Upload ${fname} to S3 bucket ${bucket}"
89 107
90 -# $1 = directory to be tarred  
91 -upload_to_s3() {  
92 - fname=$( basename "$1" )  
93 - echo "$(date) : Upload ${fname}.tar.gz to S3 bucket ${bucket}" 108 + stamp=$( date +"%FT%T )
  109 + echo "Uploaded: ${stamp}" > "${bak_dir}/${stamp}.txt"
94 110
95 - "$aws_cmd" --region "$region" \  
96 - s3 mv "${bak_dir}/${fname}.tar.gz" "s3://${bucket}/${fname}.tar.gz" 111 + "$aws_bin" --region "$region" \
  112 + s3 mv "${bak_dir}/${fname}" \
  113 + "s3://${bucket}/${bak_keyname}/${fname}"
  114 + "$aws_bin" --region "$region" \
  115 + s3 mv "${bak_dir}/${stamp}.txt" \
  116 + "s3://${bucket}/${bak_keyname}/${stamp}.txt"
97 } 117 }
98 118
99 -for dirx in "${tar_dirs[@]}" ; do  
100 - if [[ -d "$dirx" ]] ; then  
101 - increment_backup_names "$dirx"  
102 - tar_dir "$dirx"  
103 - upload_to_s3 "$dirx"  
104 - else  
105 - echo "$(date) : WARNING : Directory ${dirx} does not exist. Skipping."  
106 - fi  
107 -done 119 +if [[ ! -d "$bak_dir" ]] ; then
  120 + mkdir -p "$bak_dir"
  121 +fi
  122 +
  123 +<% @paths.each do |path| -%>
  124 +<% bname = path[:bak_filename] || ::File.basename(path[:path]) -%>
  125 +<% is_enc = path.has_key?(:bak_encrypted) ? path[:bak_encrypted] : false -%>
  126 +if [[ -d <%= path[:path] %> || -f <%= path[:path] %> ]] ; then
  127 + increment_backup_names <%= bname %> <%= path[:bak_maxcopies] || 30 %>
  128 + tar_dir <%= path[:path] %> <%= bname %>
  129 +<% if is_enc -%>
  130 + encrypt_tarball <%= bname %>
  131 +<% end -%>
  132 + upload_to_s3 <%= bname %> <%= is_enc %>
  133 +else
  134 + >&2 echo "$(date) [WARNING] Path <%= path[:path] %> does not exist"
  135 +fi
108 136
  137 +<% end -%>
109 rm "$tmp_file" 138 rm "$tmp_file"
110 echo "$(date) : Done" 139 echo "$(date) : Done"