Skip to content

Commit

Permalink
Updated Backup plugin to work with Moonshot 1.x. (#195)
Browse files Browse the repository at this point in the history
  • Loading branch information
borsothy authored and askreet committed Feb 7, 2017
1 parent 494af42 commit c530574
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 29 deletions.
18 changes: 11 additions & 7 deletions docs/plugins/backup.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ When instantiating a class, you need to set the following options
in a block, where the object is provided as a block argument:

- `bucket`: the name of the S3 bucket you wish to upload the tarball (optional)
- `buckets`: a hash map containing account aliases as keys, and target buckets as values (optional)
- `files`: an array of relative path names as strings
- `buckets`: a hash map containing account aliases as keys, and target buckets as values (optional).
- `files`: an array of relative path names as strings.
- `backup_parameters`: boolean value for backing up all parameters into a YAML file (optional, defaults to `false`).
- `backup_template`: boolean value for backing up the current CloudFormation template (optional, defaults to `false`).
- `hooks`: which hooks to run the backup logic, works with all valid Moonshot hooks
- `target_name`: tarball archive name, default: `<app_name>_<timestamp>_<user>.tar.gz`
- `target_name`: tarball archive name (optional, defaults to `<app_name>_<timestamp>_<user>.tar.gz`).

You must provide either `bucket` or `buckets`, but **not both**.

If you provide either `backup_parameters` or `backup_template` you may not provide `files` additionally.

`pre_create` and `post_delete` hooks are **not** allowed to use due to certain implementation restrictions.

## Default method

If you wish to back up only the current template and parameter files, you can simply
Expand Down Expand Up @@ -71,10 +77,8 @@ parameter file after create or update.
'prod_account' => 'prod_bucket'
}

b.files = [
'cloud_formation/%{app_name}.json',
'cloud_formation/parameters/%{stack_name}.yml'
]
b.backup_template = true
b.backup_parameters = true

b.hooks = [:post_create, :post_update]
end
Expand Down
105 changes: 89 additions & 16 deletions lib/plugins/backup.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
require 'rubygems/package'
require 'zlib'
require 'yaml'

module Moonshot
module Plugins
# Moonshot plugin class for deflating and uploading files on given hooks
class Backup
class Backup # rubocop:disable Metrics/ClassLength
include Moonshot::CredsHelper

attr_accessor :bucket,
:buckets,
:files,
:hooks,
:target_name
:target_name,
:backup_parameters,
:backup_template

def initialize
yield self if block_given?
raise ArgumentError \
if @files.nil? || @files.empty? || @hooks.nil? || !(@bucket.nil? ^ @buckets.nil?)

validate_configuration
@target_name ||= '%{app_name}_%{timestamp}_%{user}.tar.gz'
end

Expand All @@ -29,10 +30,8 @@ def self.to_bucket(bucket)
raise ArgumentError if bucket.nil? || bucket.empty?
Moonshot::Plugins::Backup.new do |b|
b.bucket = bucket
b.files = [
'cloud_formation/%{app_name}.json',
'cloud_formation/parameters/%{stack_name}.yml'
]
b.backup_parameters = true
b.backup_template = true
b.hooks = [:post_create, :post_update]
end
end
Expand All @@ -44,10 +43,11 @@ def self.to_bucket(bucket)
def backup(resources)
raise ArgumentError if resources.nil?

@app_name = resources.stack.app_name
@app_name = resources.controller.config.app_name
@stack_name = resources.stack.name
@target_name = render(@target_name)
@target_bucket = define_bucket
@parameters = resources.stack.parameters

return if @target_bucket.nil?

Expand All @@ -58,7 +58,7 @@ def backup(resources)
upload(zip_out)

s.success("#{log_message} succeeded.")
rescue StandardError => e
rescue => e
s.failure("#{log_message} failed: #{e}")
ensure
tar_out.close unless tar_out.nil?
Expand Down Expand Up @@ -90,18 +90,59 @@ def respond_to?(method_name, include_private = false)
def tar(target_files)
tar_stream = StringIO.new
Gem::Package::TarWriter.new(tar_stream) do |writer|
target_files.each do |file|
file = render(file)

writer.add_file(File.basename(file), 0644) do |io|
File.open(file, 'r') { |f| io.write(f.read) }
# adding user files
unless target_files.nil? || target_files.empty?
target_files.each do |file|
file = render(file)
add_file_to_tar(writer, file)
end
end

# adding parameters
if @backup_parameters
add_str_to_tar(
writer,
render('%{stack_name}-parameters.yml'),
@parameters
)
end

# adding template file
if @backup_template
template_file_path = render('cloud_formation/%{app_name}.json')
add_file_to_tar(writer, template_file_path)
end
end
tar_stream.seek(0)
tar_stream
end

# Helper method to add a file to an inmemory tar archive.
#
# @param writer [TarWriter]
# @param file_name [String]
def add_file_to_tar(writer, file_name)
writer.add_file(File.basename(file_name), 0644) do |io|
begin
File.open(file_name, 'r') { |f| io.write(f.read) }
rescue Errno::ENOENT
warn "'#{file_name}' was not found."
end
end
end

# Helper method to add a file based on an input String as content
# to an inmemory tar archive.
#
# @param writer [TarWriter]
# @param target_filename [String]
# @param content [String]
def add_str_to_tar(writer, target_filename, content)
writer.add_file(File.basename(target_filename), 0644) do |io|
io.write(content.to_yaml)
end
end

# Create a zip archive in memory, returning the IO object pointing at the
# beginning of the zipfile.
#
Expand Down Expand Up @@ -168,6 +209,38 @@ def define_bucket
def bucket_by_account(account)
@buckets[account]
end

def validate_configuration
validate_buckets
validate_redundant_configuration
validate_targets
validate_hooks
end

def validate_buckets
raise ArgumentError, 'You must specify a target bucket.' \
if (@bucket.nil? || @bucket.empty?) \
&& (@buckets.nil? || @buckets.empty?)
end

def validate_redundant_configuration
raise ArgumentError, 'You can not specify both `bucket` and `buckets`.' \
if @bucket && @buckets
end

def validate_targets
raise ArgumentError, 'You must specify files to back up.' \
if (@files.nil? || @files.empty?) \
&& (!@backup_parameters && !@backup_template)
end

def validate_hooks
raise ArgumentError, 'You must specify a hook / hooks to run the backup on.' \
if hooks.nil? || hooks.empty?

raise ArgumentError, '`pre_create` and `post_delete` hooks are not supported.' \
if hooks.include?(:pre_create) || hooks.include?(:post_delete)
end
end
end
end
18 changes: 12 additions & 6 deletions spec/moonshot/plugins/backup_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,8 @@

it 'should set default config values' do
expect(subject.bucket).to eq test_bucket_name
expect(subject.files).to eq [
'cloud_formation/%{app_name}.json',
'cloud_formation/parameters/%{stack_name}.yml'
]
expect(subject.backup_parameters).to eq true
expect(subject.backup_template).to eq true
expect(subject.hooks).to eq [:post_create, :post_update]
end

Expand All @@ -98,8 +96,16 @@
let(:resources) do
instance_double(
Moonshot::Resources,
stack: instance_double(Moonshot::Stack, app_name: 'test_app_name', name: 'test_name'),
ilog: instance_double(Moonshot::InteractiveLoggerProxy)
stack: instance_double(
Moonshot::Stack,
name: 'test_name',
parameters: {}
),
ilog: instance_double(Moonshot::InteractiveLoggerProxy),
controller: instance_double(
Moonshot::Controller,
config: instance_double(Moonshot::ControllerConfig, app_name: 'test')
)
)
end

Expand Down

0 comments on commit c530574

Please sign in to comment.