dew 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +22 -0
- data/README.md +38 -0
- data/Rakefile +26 -0
- data/bin/dew +87 -0
- data/config/cucumber.yaml +4 -0
- data/features/create-ami.feature +16 -0
- data/features/create-environments.feature +46 -0
- data/features/deploy-puge.feature +16 -0
- data/features/step_definitions/aws-steps.rb +101 -0
- data/features/step_definitions/deploy-puge-steps.rb +27 -0
- data/features/support/env.rb +38 -0
- data/features/support/hooks.rb +10 -0
- data/lib/dew.rb +7 -0
- data/lib/dew/aws_resources.yaml +122 -0
- data/lib/dew/base_command.rb +24 -0
- data/lib/dew/cloud.rb +79 -0
- data/lib/dew/commands.rb +6 -0
- data/lib/dew/commands/ami.rb +67 -0
- data/lib/dew/commands/console.rb +17 -0
- data/lib/dew/commands/console/irb_override.rb +24 -0
- data/lib/dew/commands/deploy.rb +114 -0
- data/lib/dew/commands/deploy/templates/apache.conf.erb +28 -0
- data/lib/dew/commands/deploy/templates/known_hosts +2 -0
- data/lib/dew/commands/deploy/templates/rvmrc +2 -0
- data/lib/dew/commands/environments.rb +110 -0
- data/lib/dew/commands/tidy.rb +35 -0
- data/lib/dew/controllers.rb +3 -0
- data/lib/dew/controllers/amis_controller.rb +82 -0
- data/lib/dew/controllers/deploy_controller.rb +10 -0
- data/lib/dew/controllers/environments_controller.rb +48 -0
- data/lib/dew/models.rb +7 -0
- data/lib/dew/models/account.rb +30 -0
- data/lib/dew/models/database.rb +32 -0
- data/lib/dew/models/deploy.rb +2 -0
- data/lib/dew/models/deploy/puge.rb +61 -0
- data/lib/dew/models/deploy/run.rb +19 -0
- data/lib/dew/models/environment.rb +199 -0
- data/lib/dew/models/fog_model.rb +23 -0
- data/lib/dew/models/profile.rb +60 -0
- data/lib/dew/models/server.rb +134 -0
- data/lib/dew/password.rb +7 -0
- data/lib/dew/tasks/spec.rake +14 -0
- data/lib/dew/validations.rb +8 -0
- data/lib/dew/version.rb +3 -0
- data/lib/dew/view.rb +39 -0
- data/lib/tasks/spec.rake +14 -0
- data/spec/dew/cloud_spec.rb +90 -0
- data/spec/dew/controllers/amis_controller_spec.rb +137 -0
- data/spec/dew/controllers/deploy_controller_spec.rb +38 -0
- data/spec/dew/controllers/environments_controller_spec.rb +133 -0
- data/spec/dew/models/account_spec.rb +47 -0
- data/spec/dew/models/database_spec.rb +58 -0
- data/spec/dew/models/deploy/puge_spec.rb +72 -0
- data/spec/dew/models/deploy/run_spec.rb +38 -0
- data/spec/dew/models/environment_spec.rb +374 -0
- data/spec/dew/models/fog_model_spec.rb +24 -0
- data/spec/dew/models/profile_spec.rb +85 -0
- data/spec/dew/models/server_spec.rb +190 -0
- data/spec/dew/password_spec.rb +11 -0
- data/spec/dew/spec_helper.rb +22 -0
- data/spec/dew/view_spec.rb +38 -0
- metadata +284 -0
data/lib/dew.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
---
|
2
|
+
regions:
|
3
|
+
us-east-1: US East (Virginia)
|
4
|
+
us-west-1: US West (North California)
|
5
|
+
eu-west-1: EU West (Ireland)
|
6
|
+
ap-southeast-1: Asia Pacific (Singapore)
|
7
|
+
ap-northeast-1: Asia Pacific (Tokyo)
|
8
|
+
|
9
|
+
instance_types:
|
10
|
+
m1.small:
|
11
|
+
memory: 1.7 GB
|
12
|
+
processor: 1 ECU
|
13
|
+
storage: 160 GB
|
14
|
+
platform: 32-bit
|
15
|
+
io_performance: Moderate
|
16
|
+
|
17
|
+
m1.large:
|
18
|
+
memory: 7.5 GB
|
19
|
+
processor: 4 ECUs
|
20
|
+
storage: 850 GB
|
21
|
+
platform: 64-bit
|
22
|
+
io_performance: High
|
23
|
+
|
24
|
+
m1.xlarge:
|
25
|
+
memory: 15 GB
|
26
|
+
processor: 8 ECUs
|
27
|
+
storage: 1,690 GB
|
28
|
+
platform: 64-bit
|
29
|
+
io_performance: High
|
30
|
+
|
31
|
+
t1.micro:
|
32
|
+
memory: 613MB
|
33
|
+
processor: Up to 2 ECUs
|
34
|
+
storage: EBS storage only
|
35
|
+
platform: 32-bit or 64-bit
|
36
|
+
io_performance: Low
|
37
|
+
|
38
|
+
m2.xlarge:
|
39
|
+
memory: 17.1 GB
|
40
|
+
processor: 6.5 ECUs
|
41
|
+
storage: 420 GB
|
42
|
+
platform: 64-bit
|
43
|
+
io_performance: Moderate
|
44
|
+
|
45
|
+
m2.2xlarge:
|
46
|
+
memory: 34.2 GB
|
47
|
+
processor: 13 ECUs
|
48
|
+
storage: 850 GB
|
49
|
+
platform: 64-bit
|
50
|
+
io_performance: High
|
51
|
+
|
52
|
+
m2.4xlarge:
|
53
|
+
memory: 68.4 GB
|
54
|
+
processor: 26 ECUs
|
55
|
+
storage: 1690 GB
|
56
|
+
platform: 64-bit
|
57
|
+
io_performance: High
|
58
|
+
|
59
|
+
c1.medium:
|
60
|
+
memory: 1.7 GB
|
61
|
+
processor: 5 ECUs
|
62
|
+
storage: 350 GB
|
63
|
+
platform: 32-bit
|
64
|
+
io_performance: Moderate
|
65
|
+
|
66
|
+
c1.xlarge:
|
67
|
+
memory: 7 GB
|
68
|
+
processor: 20 ECUs
|
69
|
+
storage: 1690 GB
|
70
|
+
platform: 64-bit
|
71
|
+
io_performance: High
|
72
|
+
|
73
|
+
cc1.4xlarge:
|
74
|
+
memory: 23 GB
|
75
|
+
processor: 33.5 ECUs
|
76
|
+
storage: 1690 GB
|
77
|
+
platform: 64-bit
|
78
|
+
io_performance: Very High
|
79
|
+
|
80
|
+
cg1.4xlarge:
|
81
|
+
memory: 22 GB
|
82
|
+
processor: 33.5 ECUs
|
83
|
+
storage: 1690 GB
|
84
|
+
platform: 64-bit
|
85
|
+
io_performance: Very High
|
86
|
+
|
87
|
+
db_instance_types:
|
88
|
+
db.m1.small:
|
89
|
+
memory: 1.7 GB
|
90
|
+
processor: 1 ECU
|
91
|
+
platform: 64-bit
|
92
|
+
io_performance: Moderate
|
93
|
+
|
94
|
+
db.m1.large:
|
95
|
+
memory: 7.5 GB
|
96
|
+
processor: 4 ECUs
|
97
|
+
platform: 64-bit
|
98
|
+
io_performance: High
|
99
|
+
|
100
|
+
db.m1.xlarge:
|
101
|
+
memory: 15 GB
|
102
|
+
processor: 8 ECUs
|
103
|
+
platform: 64-bit
|
104
|
+
io_performance: High
|
105
|
+
|
106
|
+
db.m2.xlarge:
|
107
|
+
memory: 17.1 GB
|
108
|
+
processor: 6.5 ECU
|
109
|
+
platform: 64-bit
|
110
|
+
io_performance: High
|
111
|
+
|
112
|
+
db.m2.2xlarge:
|
113
|
+
memory: 34 GB
|
114
|
+
processor: 13 ECUs
|
115
|
+
platform: 64-bit
|
116
|
+
io_performance: High
|
117
|
+
|
118
|
+
db.m2.4xlarge:
|
119
|
+
memory: 68 GB
|
120
|
+
processor: 26 ECUs
|
121
|
+
platform: 64-bit
|
122
|
+
io_performance: High
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'clamp'
|
2
|
+
|
3
|
+
# Monkey patch clamp to remove duplicate options from help
|
4
|
+
module Clamp::Option::Declaration
|
5
|
+
alias_method :non_uniq_documented_options, :documented_options
|
6
|
+
def documented_options
|
7
|
+
non_uniq_documented_options.uniq
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
class DewBaseCommand < Clamp::Command
|
13
|
+
def configure
|
14
|
+
$debug = debug?
|
15
|
+
Inform.level = quiet? ? :warning : (verbose? ? :debug : :info)
|
16
|
+
Cloud.region = region
|
17
|
+
Cloud.account_name = account
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute
|
21
|
+
configure
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
data/lib/dew/cloud.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
class Cloud
|
2
|
+
|
3
|
+
attr_reader :region, :account_name, :profile_name
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def connect region, account_name, profile_name = nil
|
8
|
+
@connection = new(region, account_name, profile_name)
|
9
|
+
Inform.info("Connected to AWS in %{region} using account %{account_name}", :region => region, :account_name => account_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing method, *args
|
13
|
+
@connection.send(method, *args)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def account
|
19
|
+
@account ||= Account.read(account_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def compute
|
23
|
+
@compute ||= Fog::Compute.new(fog_credentials.merge({:provider => 'AWS'}))
|
24
|
+
end
|
25
|
+
|
26
|
+
def security_groups
|
27
|
+
@security_groups ||= compute.security_groups.inject({}) { |h, g| h[g.name] = g; h }
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid_servers
|
31
|
+
@valid_servers ||= Cloud.compute.servers.select{ |s| %w{running pending}.include?(s.state) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def keypair_exists? keypair
|
35
|
+
!!compute.key_pairs.get(keypair)
|
36
|
+
end
|
37
|
+
|
38
|
+
def elb
|
39
|
+
@elb ||= Fog::AWS::ELB.new(fog_credentials)
|
40
|
+
end
|
41
|
+
|
42
|
+
def rds
|
43
|
+
@rds ||= Fog::AWS::RDS.new(fog_credentials)
|
44
|
+
end
|
45
|
+
|
46
|
+
def rds_authorized_ec2_owner_ids
|
47
|
+
# XXX - Does this belong in Fog::AWS::RDS ?
|
48
|
+
@rds_authorized_ec2_owner_ids ||= rds.security_groups.detect { |security_group|
|
49
|
+
security_group.id == 'default'
|
50
|
+
}.ec2_security_groups.select { |ec2_security_group|
|
51
|
+
ec2_security_group["EC2SecurityGroupName"] == "default" && ec2_security_group["Status"] == "authorized"
|
52
|
+
}.collect { |h|
|
53
|
+
h["EC2SecurityGroupOwnerId"]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def profile
|
58
|
+
if profile_name
|
59
|
+
@profile ||= Profile.read(profile_name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def keyfile_path(key_name)
|
64
|
+
account_dir = File.join(ENV['HOME'], '.dew','accounts')
|
65
|
+
File.join(account_dir, 'keys', account_name, region, "#{key_name}.pem")
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def initialize region, account_name, profile_name = nil
|
71
|
+
@region = region
|
72
|
+
@account_name = account_name
|
73
|
+
@profile_name = profile_name
|
74
|
+
end
|
75
|
+
|
76
|
+
def fog_credentials
|
77
|
+
{:region => region, :aws_access_key_id => account.aws_access_key_id, :aws_secret_access_key => account.aws_secret_access_key}
|
78
|
+
end
|
79
|
+
end
|
data/lib/dew/commands.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'dew/controllers/amis_controller'
|
2
|
+
|
3
|
+
class AMIsCommand < Clamp::Command
|
4
|
+
|
5
|
+
def puppet_node_filename node_name
|
6
|
+
File.join(ENV['HOME'], '.dew', 'puppet', 'manifests', 'nodes', "#{node_name}.pp")
|
7
|
+
end
|
8
|
+
|
9
|
+
def controller
|
10
|
+
@controller ||= AMIsController.new
|
11
|
+
end
|
12
|
+
|
13
|
+
default_subcommand "index", "Show AMIs" do
|
14
|
+
|
15
|
+
def execute
|
16
|
+
controller.index
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
subcommand "show", "Show an AMI" do
|
22
|
+
|
23
|
+
parameter "AMI_NAME", "Name of AMI"
|
24
|
+
|
25
|
+
def execute
|
26
|
+
controller.show(ami_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
subcommand "show", "Show an AMI" do
|
32
|
+
|
33
|
+
parameter "AMI_NAME", "Name of AMI"
|
34
|
+
|
35
|
+
def execute
|
36
|
+
controller.show(ami_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
subcommand "create", "Create a new AMI" do
|
42
|
+
|
43
|
+
parameter "PUPPET_NODE_NAME", "Puppet node (in puppet/manifests/nodes/*.pp) to run on AMI" do |puppet_node_name|
|
44
|
+
unless File.exist?(puppet_node_filename(puppet_node_name))
|
45
|
+
raise ArgumentError, "Can't find puppet/#{puppet_node_filename(puppet_node_name)}: check that puppet submodule is checked out and node exists"
|
46
|
+
end
|
47
|
+
puppet_node_name
|
48
|
+
end
|
49
|
+
parameter "AMI_NAME", "What to call the newly created AMI"
|
50
|
+
|
51
|
+
def execute
|
52
|
+
controller.create(ami_name, puppet_node_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
subcommand "destroy", "Destroy an existing AMI" do
|
58
|
+
|
59
|
+
parameter "AMI_NAME", "Name of AMI to destroy"
|
60
|
+
option ['-f', '--force'], :flag, "Don't ask for confirmation before destroying", :default => false
|
61
|
+
|
62
|
+
def execute
|
63
|
+
controller.destroy(ami_name, :force => force?)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class ConsoleCommand < Clamp::Command
|
2
|
+
def execute
|
3
|
+
load File.expand_path(File.join(File.dirname(__FILE__), 'console', 'irb_override.rb'))
|
4
|
+
ARGV.reject! { true }
|
5
|
+
puts <<-EOS
|
6
|
+
===============================================================
|
7
|
+
Objects available: -
|
8
|
+
|
9
|
+
Cloud - Cloud Handle
|
10
|
+
Cloud.compute - Access AWS EC2 (Elastic Compute Cloud) instances
|
11
|
+
Cloud.elb - Access AWS ELB (Elastic Load Balancers)
|
12
|
+
Cloud.rds - Access AWS RDS (Relational Database Service)
|
13
|
+
===============================================================
|
14
|
+
EOS
|
15
|
+
IRB.start_session(binding)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'irb'
|
2
|
+
|
3
|
+
module IRB # :nodoc:
|
4
|
+
def self.start_session(binding)
|
5
|
+
unless @__initialized
|
6
|
+
args = ARGV
|
7
|
+
ARGV.replace(ARGV.dup)
|
8
|
+
IRB.setup(nil)
|
9
|
+
ARGV.replace(args)
|
10
|
+
@__initialized = true
|
11
|
+
end
|
12
|
+
|
13
|
+
workspace = WorkSpace.new(binding)
|
14
|
+
|
15
|
+
irb = Irb.new(workspace)
|
16
|
+
|
17
|
+
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
18
|
+
@CONF[:MAIN_CONTEXT] = irb.context
|
19
|
+
|
20
|
+
catch(:IRB_EXIT) do
|
21
|
+
irb.eval_input
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'dew/controllers/amis_controller'
|
2
|
+
require 'erb' # XXX
|
3
|
+
require 'ostruct' # XXX
|
4
|
+
require 'open-uri' # XXX
|
5
|
+
require 'json' # XXX
|
6
|
+
|
7
|
+
class DeployCommand < Clamp::Command
|
8
|
+
|
9
|
+
def template filename
|
10
|
+
File.join(File.dirname(__FILE__), 'deploy', 'templates', filename)
|
11
|
+
end
|
12
|
+
|
13
|
+
subcommand 'passenger', "Deploy a Passenger-compatiable application to an environment" do
|
14
|
+
parameter "APPLICATION_NAME", "Repository and application name"
|
15
|
+
parameter "REVISION", "Git revision to deploy (tag, branch, or commit id)"
|
16
|
+
parameter "ENVIRONMENT_NAME", "Environment to deploy into"
|
17
|
+
|
18
|
+
option ['--rails-env'], 'ENVIRONMENT', "Rails environment to use", :default => 'production'
|
19
|
+
|
20
|
+
def execute
|
21
|
+
env = Environment.get(environment_name)
|
22
|
+
env.servers.each do |server|
|
23
|
+
Inform.info("Working with server %{id} of %{l} servers", :id => server.id, :l => env.servers.length)
|
24
|
+
env.remove_server_from_elb(server) if env.has_elb?
|
25
|
+
|
26
|
+
ssh = server.ssh
|
27
|
+
initial = !ssh.exist?(application_name)
|
28
|
+
|
29
|
+
Inform.info("%{app} doesn't exist - initial install", :app => application_name) if initial
|
30
|
+
|
31
|
+
Inform.info("Stopping apache") do
|
32
|
+
ssh.run "sudo apache2ctl stop"
|
33
|
+
end
|
34
|
+
|
35
|
+
Inform.info("Obtaining version %{v} of %{app}", :v => revision, :app => application_name) do
|
36
|
+
if initial
|
37
|
+
Inform.debug("Writing out ~/.ssh/known_hosts file to allow github clone")
|
38
|
+
ssh.upload template('known_hosts'), ".ssh/known_hosts"
|
39
|
+
Inform.debug("Cloning %{app} in to ~/%{app}", :app => application_name)
|
40
|
+
ssh.run "git clone git@github.com:playup/#{application_name}.git #{application_name}"
|
41
|
+
else
|
42
|
+
Inform.debug("Updating %{app} repository", :app => application_name)
|
43
|
+
ssh.run "cd #{application_name}; git fetch -q"
|
44
|
+
end
|
45
|
+
|
46
|
+
Inform.debug("Checking out version %{version}", :version => revision)
|
47
|
+
ssh.run "cd #{application_name} && git checkout -q -f origin/#{revision}"
|
48
|
+
end
|
49
|
+
|
50
|
+
cd_and_rvm = "cd #{application_name} && . /usr/local/rvm/scripts/rvm && rvm use ruby-1.9.2 && RAILS_ENV=#{rails_env} "
|
51
|
+
|
52
|
+
Inform.info("Updating/installing gems") do
|
53
|
+
ssh.run cd_and_rvm + "bundle install"
|
54
|
+
end
|
55
|
+
|
56
|
+
if ssh.exist?("#{application_name}/config/database.yml")
|
57
|
+
Inform.info("config/database.yml exists, creating and/or updating database") do
|
58
|
+
Inform.debug("Creating database") if initial
|
59
|
+
ssh.run cd_and_rvm + "rake db:create" if initial
|
60
|
+
Inform.debug("Updating database")
|
61
|
+
ssh.run cd_and_rvm + "rake db:migrate"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
Inform.info("No config/database.yml, skipping database step")
|
65
|
+
end
|
66
|
+
|
67
|
+
Inform.info("Starting application with passenger") do
|
68
|
+
if ssh.exist?('/etc/apache2/sites-enabled/000-default')
|
69
|
+
Inform.debug("Disabling default apache site")
|
70
|
+
ssh.run "sudo a2dissite default"
|
71
|
+
end
|
72
|
+
Inform.debug("Uploading passenger config")
|
73
|
+
passenger_config = ERB.new(File.read template('apache.conf.erb')).result(OpenStruct.new(
|
74
|
+
:rails_env => rails_env,
|
75
|
+
:application_name => application_name,
|
76
|
+
:working_directory => "/home/ubuntu/#{application_name}"
|
77
|
+
).instance_eval {binding})
|
78
|
+
ssh.write passenger_config, "/tmp/apache.conf"
|
79
|
+
ssh.run "sudo cp /tmp/apache.conf /etc/apache2/sites-available/#{application_name}"
|
80
|
+
ssh.run "sudo chmod 0644 /etc/apache2/sites-available/#{application_name}" # yeah, I don't know why it gets written as 0600
|
81
|
+
unless ssh.exist?('/etc/apache2/sites-enabled/#{application_name}')
|
82
|
+
Inform.debug("Enabling passenger site in apache")
|
83
|
+
ssh.run "sudo a2ensite #{application_name}"
|
84
|
+
end
|
85
|
+
Inform.debug("Restarting apache")
|
86
|
+
ssh.run "sudo apache2ctl restart"
|
87
|
+
end
|
88
|
+
|
89
|
+
status_url = "http://#{server.public_ip_address}/status"
|
90
|
+
Inform.info("Checking status URL at %{u}", :u => status_url) do
|
91
|
+
response = JSON.parse(open(status_url).read)
|
92
|
+
unless response.include?('status') && response['status'] == 'OK'
|
93
|
+
raise "Did not receive an OK status response."
|
94
|
+
end
|
95
|
+
end
|
96
|
+
env.add_server_to_elb(server) if env.has_elb?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
subcommand 'puge', "Deploy PUGE" do
|
103
|
+
parameter "TAG", "Git revision to deploy (tag, branch, or commit id)"
|
104
|
+
parameter "ENVIRONMENT_NAME", "Environment to deploy into"
|
105
|
+
parameter "RAILS_ENV", "Rails environment used for db setup"
|
106
|
+
|
107
|
+
def execute
|
108
|
+
Inform.info("Deploying PUGE tag %{tag} using RAILS_ENV %{rails_env} to environment %{environment_name}", :tag => tag, :rails_env => rails_env, :environment_name => environment_name)
|
109
|
+
|
110
|
+
DeployController.new.create('puge', environment_name, { 'tag' => tag, 'rails_env' => rails_env })
|
111
|
+
Inform.info("Deployed successfully.")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|