aws_one_click_staging 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +7 -7
- data/aws_one_click_staging.gemspec +3 -1
- data/bin/aws_one_click_staging +25 -0
- data/config/aws_one_click_staging.yml +9 -0
- data/lib/aws_one_click_staging.rb +14 -1
- data/lib/aws_one_click_staging/aws_warrior.rb +139 -0
- data/lib/aws_one_click_staging/bucket_sync_service.rb +85 -0
- data/lib/aws_one_click_staging/version.rb +1 -1
- metadata +34 -4
- data/bin/console +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90ed89f6ad179598fb6989d7655c9524305589b7
|
4
|
+
data.tar.gz: 8a8c004ddeaf405de3670debbe1501cc70321889
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26af7c6c981ed74c0708f87b898fb2dab81a30111e814cf88b1e1ed9b8655be48533d54fce5327c833f25fccdf43bcc1969a68aa3c9f1af87a6dc1d8f1ca26ee
|
7
|
+
data.tar.gz: 6019696f745680f4057122db72c8d99cd6193a1cb0137449773de011668ad3dccf738769a4a0d142e5150c8e821ab50c6b686c65c40dbd863220c379b3dc865c
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -16,13 +16,13 @@ Next you'll need to setup the config file:
|
|
16
16
|
|
17
17
|
(~/.config/aws_one_click_staging.yml)
|
18
18
|
```
|
19
|
-
aws_access_key_id:
|
20
|
-
aws_secret_access_key:
|
19
|
+
aws_access_key_id: ""
|
20
|
+
aws_secret_access_key: ""
|
21
21
|
aws_region: 'us-west-1'
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
aws_master_username: ""
|
23
|
+
aws_master_user_password: ""
|
24
|
+
aws_production_bucket: "" # this bucket is read from
|
25
|
+
aws_staging_bucket: "" # this bucket is DELETED and written to!
|
26
26
|
```
|
27
27
|
|
28
28
|
|
@@ -31,7 +31,7 @@ db_password: ''
|
|
31
31
|
Because amazon services are kind of a mess right now, it's best to run this from on an actual Amazon server you have shell access to (it downloads the bucket files and then re-uploads them, lol).
|
32
32
|
|
33
33
|
```
|
34
|
-
aws_one_click_staging stage
|
34
|
+
aws_one_click_staging stage
|
35
35
|
```
|
36
36
|
|
37
37
|
After a while, the operation will complete and it will say 'congrats' or something and output the RDS url and bucket name for the staging clone. Plug those values into your staging server and you should be good to go.
|
@@ -14,11 +14,13 @@ Gem::Specification.new do |spec|
|
|
14
14
|
# spec.license = "MIT" # uncomment this line if MIT is the best license for your situation
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
-
spec.bindir = "
|
17
|
+
spec.bindir = "bin"
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency 'aws-sdk', '~> 2'
|
21
22
|
spec.add_dependency "aws-sdk-v1"
|
23
|
+
spec.add_dependency "thor"
|
22
24
|
|
23
25
|
spec.add_development_dependency "bundler", "~> 1.10"
|
24
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
data/bin/aws_one_click_staging
CHANGED
@@ -1,3 +1,28 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require 'thor'
|
3
4
|
require "aws_one_click_staging"
|
5
|
+
|
6
|
+
class AwsOneClickStagingRunner < Thor
|
7
|
+
|
8
|
+
default_task :start
|
9
|
+
|
10
|
+
desc "list", "Shows help"
|
11
|
+
def list
|
12
|
+
AwsOneClickStaging.list
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "stage", "Makes a copy of the RDS database and the staging server's S3 bucket. This takes a while."
|
16
|
+
def stage
|
17
|
+
AwsOneClickStaging.stage(421)
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "version", "Prints gem's version"
|
21
|
+
def version
|
22
|
+
AwsOneClickStaging::VERSION
|
23
|
+
end
|
24
|
+
map %w(-v --version) => :version
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
AwsOneClickStagingRunner.start
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# edit these values to match that of your production machine
|
2
|
+
# which will be cloned from
|
3
|
+
aws_access_key_id: ""
|
4
|
+
aws_secret_access_key: ""
|
5
|
+
aws_region: 'us-west-1'
|
6
|
+
aws_master_username: ""
|
7
|
+
aws_master_user_password: ""
|
8
|
+
aws_production_bucket: "" # this bucket is read from
|
9
|
+
aws_staging_bucket: "" # this bucket is DELETED and written to!
|
@@ -1,5 +1,18 @@
|
|
1
|
+
require 'aws_one_click_staging/bucket_sync_service'
|
2
|
+
require "aws_one_click_staging/aws_warrior"
|
1
3
|
require "aws_one_click_staging/version"
|
2
4
|
|
5
|
+
SOURCE_ROOT = File.expand_path("#{File.dirname(__FILE__)}/..")
|
6
|
+
|
3
7
|
module AwsOneClickStaging
|
4
|
-
|
8
|
+
|
9
|
+
def self.stage
|
10
|
+
warrior = AwsWarrior.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.list
|
14
|
+
warrior = AwsWarrior.new # this makes a config file if needed
|
15
|
+
puts "This is aws_one_click_staging, use stage to set things up"
|
16
|
+
end
|
17
|
+
|
5
18
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'aws-sdk'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module AwsOneClickStaging
|
6
|
+
|
7
|
+
class AwsWarrior
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# read config file
|
11
|
+
@config_dir = "#{ENV['HOME']}/.config"
|
12
|
+
@config_file = File.expand_path("#{@config_dir}/aws_one_click_staging.yml")
|
13
|
+
@config = YAML.load(read_config_file)
|
14
|
+
end
|
15
|
+
|
16
|
+
def clone_rds
|
17
|
+
setup_aws_credentials
|
18
|
+
|
19
|
+
@db_instance_id_production = "actioncenter-staging"
|
20
|
+
@db_instance_id_staging = "actioncenter-staging-test"
|
21
|
+
@db_snapshot_id = "actioncenter-snapshot-for-staging"
|
22
|
+
|
23
|
+
@c = Aws::RDS::Client.new
|
24
|
+
|
25
|
+
delete_snapshot_for_staging!
|
26
|
+
create_new_snapshot_for_staging!
|
27
|
+
|
28
|
+
delete_staging_db_instance!
|
29
|
+
spawn_new_staging_db_instance!
|
30
|
+
end
|
31
|
+
|
32
|
+
def clone_s3_bucket
|
33
|
+
setup_aws_credentials
|
34
|
+
|
35
|
+
from_creds = { aws_access_key_id: @access_key_id,
|
36
|
+
aws_secret_access_key: @secret_access_key,
|
37
|
+
bucket: @aws_production_bucket}
|
38
|
+
to_creds = { aws_access_key_id: @access_key_id,
|
39
|
+
aws_secret_access_key: @secret_access_key,
|
40
|
+
bucket: @aws_staging_bucket}
|
41
|
+
|
42
|
+
bs = BucketSyncService.new(from_creds, to_creds)
|
43
|
+
|
44
|
+
bs.perform
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def setup_aws_credentials
|
51
|
+
@config[""]
|
52
|
+
aws_region = @config["aws_region"]
|
53
|
+
@access_key_id = @config["aws_access_key_id"]
|
54
|
+
@secret_access_key = @config["aws_secret_access_key"]
|
55
|
+
@master_username = @config["aws_master_username"]
|
56
|
+
@master_user_password = @config["aws_master_user_password"]
|
57
|
+
@aws_production_bucket = @config["aws_production_bucket"]
|
58
|
+
@aws_staging_bucket = @config["aws_staging_bucket"]
|
59
|
+
|
60
|
+
Aws.config.update({ region: aws_region,
|
61
|
+
credentials: Aws::Credentials.new(@access_key_id, @secret_access_key) })
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete_snapshot_for_staging!
|
65
|
+
puts "deleting old staging db snapshot"
|
66
|
+
response = @c.delete_db_snapshot(db_snapshot_identifier: @db_snapshot_id)
|
67
|
+
|
68
|
+
sleep 1 while response.percent_progress != 100
|
69
|
+
true
|
70
|
+
rescue
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_new_snapshot_for_staging!
|
75
|
+
puts "creating new snapshot... this takes like 70 seconds..."
|
76
|
+
response = @c.create_db_snapshot({db_instance_identifier: @db_instance_id_production,
|
77
|
+
db_snapshot_identifier: @db_snapshot_id })
|
78
|
+
|
79
|
+
sleep 10 while get_fresh_db_snapshot_state.status != "available"
|
80
|
+
true
|
81
|
+
rescue
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def delete_staging_db_instance!
|
87
|
+
response = @c.delete_db_instance(db_instance_identifier: @db_instance_id_staging)
|
88
|
+
|
89
|
+
sleep 2 while response.percent_progress != 100
|
90
|
+
rescue
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
def spawn_new_staging_db_instance!
|
95
|
+
response = @c.create_db_instance(db_instance_identifier: @db_instance_id_staging,
|
96
|
+
db_instance_class: "db.t1.micro",
|
97
|
+
engine: "postgres",
|
98
|
+
master_username: @master_username,
|
99
|
+
master_user_password: @master_user_password,
|
100
|
+
allocated_storage: "10")
|
101
|
+
|
102
|
+
sleep 10 while get_fresh_db_instance_state.db_instance_status != "available"
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# we use this methods cause amazon lawl-pain
|
107
|
+
def get_fresh_db_snapshot_state
|
108
|
+
@c.describe_db_snapshots(db_snapshot_identifier: @db_snapshot_id).db_snapshots.first
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_fresh_db_instance_state
|
112
|
+
@c.describe_db_instances(db_instance_identifier: @db_instance_id_staging).db_instances.first
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def read_config_file
|
117
|
+
return if create_config_file_if_needed!
|
118
|
+
config = File.read(@config_file)
|
119
|
+
end
|
120
|
+
|
121
|
+
def create_config_file_if_needed!
|
122
|
+
return false if File.exists?(@config_file)
|
123
|
+
msg = ""
|
124
|
+
msg += "Config file not found, creating...\n\n"
|
125
|
+
|
126
|
+
# copy example config file to config file path
|
127
|
+
FileUtils.mkdir_p @config_dir
|
128
|
+
FileUtils.cp("#{SOURCE_ROOT}/config/aws_one_click_staging.yml", @config_file)
|
129
|
+
|
130
|
+
msg += "An empty config file was created for you in #{@config_file}\n"
|
131
|
+
msg += "Please populate it with the correct information and run this \n"
|
132
|
+
msg += "command again. \n"
|
133
|
+
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# props to bantic
|
2
|
+
# https://gist.github.com/bantic/4080793
|
3
|
+
require 'aws/s3' # gem name is 'aws-sdk'
|
4
|
+
|
5
|
+
class BucketSyncService
|
6
|
+
attr_reader :from_bucket, :to_bucket, :logger
|
7
|
+
attr_accessor :debug
|
8
|
+
|
9
|
+
DEFAULT_ACL = :public_read
|
10
|
+
|
11
|
+
# from_credentials and to_credentials are both hashes with these keys:
|
12
|
+
# * :aws_access_key_id
|
13
|
+
# * :aws_secret_access_key
|
14
|
+
# * :bucket
|
15
|
+
def initialize(from_credentials, to_credentials)
|
16
|
+
@from_bucket = bucket_from_credentials(from_credentials)
|
17
|
+
@to_bucket = bucket_from_credentials(to_credentials)
|
18
|
+
end
|
19
|
+
|
20
|
+
def perform(output=STDOUT)
|
21
|
+
object_counts = {sync:0, skip:0}
|
22
|
+
create_logger(output)
|
23
|
+
|
24
|
+
logger.info "Starting sync."
|
25
|
+
from_bucket.objects.each do |object|
|
26
|
+
if object_needs_syncing?(object)
|
27
|
+
sync(object)
|
28
|
+
object_counts[:sync] += 1
|
29
|
+
else
|
30
|
+
logger.debug "Skipped #{pp object}"
|
31
|
+
object_counts[:skip] += 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
logger.info "Done. Synced #{object_counts[:sync]}, " +
|
35
|
+
"skipped #{object_counts[:skip]}."
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def create_logger(output)
|
41
|
+
@logger = Logger.new(output).tap do |l|
|
42
|
+
l.level = debug ? Logger::DEBUG : Logger::INFO
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def sync(object)
|
47
|
+
logger.debug "Syncing #{pp object}"
|
48
|
+
object.copy_to( to_bucket.objects[object.key], acl:DEFAULT_ACL)
|
49
|
+
end
|
50
|
+
|
51
|
+
def pp(object)
|
52
|
+
content_length_in_kb = object.content_length / 1024
|
53
|
+
"#{object.key} #{content_length_in_kb}k " +
|
54
|
+
"#{object.last_modified.strftime("%b %d %Y %H:%M")}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def object_needs_syncing?(object)
|
58
|
+
to_object = to_bucket.objects[object.key]
|
59
|
+
return true if !to_object.exists?
|
60
|
+
|
61
|
+
return to_object.etag != object.etag
|
62
|
+
end
|
63
|
+
|
64
|
+
def bucket_from_credentials(credentials)
|
65
|
+
s3 = AWS::S3.new(access_key_id: credentials[:aws_access_key_id],
|
66
|
+
secret_access_key: credentials[:aws_secret_access_key])
|
67
|
+
|
68
|
+
bucket = s3.buckets[ credentials[:bucket] ]
|
69
|
+
if !bucket.exists?
|
70
|
+
bucket = s3.buckets.create( credentials[:bucket] )
|
71
|
+
end
|
72
|
+
bucket
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
=begin
|
79
|
+
Example usage:
|
80
|
+
from_creds = {aws_access_key_id:"XXX", aws_secret_access_key:"YYY", bucket:"first-bucket"}
|
81
|
+
to_creds = {aws_access_key_id:"ZZZ", aws_secret_access_key:"AAA", bucket:"second-bucket"}
|
82
|
+
syncer = BucketSyncService.new(from_creds, to_creds)
|
83
|
+
syncer.debug = true # log each object
|
84
|
+
syncer.perform
|
85
|
+
=end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws_one_click_staging
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TheNotary
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: aws-sdk-v1
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,6 +38,20 @@ dependencies:
|
|
24
38
|
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: bundler
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,9 +124,11 @@ files:
|
|
96
124
|
- Rakefile
|
97
125
|
- aws_one_click_staging.gemspec
|
98
126
|
- bin/aws_one_click_staging
|
99
|
-
- bin/console
|
100
127
|
- changelog
|
128
|
+
- config/aws_one_click_staging.yml
|
101
129
|
- lib/aws_one_click_staging.rb
|
130
|
+
- lib/aws_one_click_staging/aws_warrior.rb
|
131
|
+
- lib/aws_one_click_staging/bucket_sync_service.rb
|
102
132
|
- lib/aws_one_click_staging/version.rb
|
103
133
|
- rspec
|
104
134
|
homepage: https://github.com/TheNotary/aws_one_click_staging
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "aws_one_click_staging"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|