dollhouse 0.1.2 → 0.2.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/dollhouse +8 -24
- data/lib/dollhouse/config_loader.rb +0 -16
- data/lib/dollhouse/deployment.rb +0 -7
- data/lib/dollhouse/dollhouse.rb +17 -12
- data/lib/dollhouse/{online_server.rb → instance.rb} +21 -14
- data/lib/dollhouse/instances.rb +5 -1
- data/lib/dollhouse/manual_config.rb +1 -10
- data/lib/dollhouse/remote_server.rb +2 -2
- data/lib/dollhouse/version.rb +1 -1
- metadata +14 -30
- data/lib/dollhouse/cloud_adapter.rb +0 -21
- data/lib/dollhouse/rackspace_cloud_adapter.rb +0 -101
data/bin/dollhouse
CHANGED
@@ -6,45 +6,29 @@ require 'dollhouse'
|
|
6
6
|
require 'main'
|
7
7
|
|
8
8
|
Main do
|
9
|
-
mode '
|
10
|
-
argument('deployment') { required }
|
11
|
-
argument('prefix') { optional }
|
12
|
-
def run
|
13
|
-
Dollhouse.launch_from(Dir.pwd)
|
14
|
-
Dollhouse.initiate_deployment(params['deployment'].value.to_sym, :prefix => params['prefix'].value)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
mode 'exec' do
|
19
|
-
argument('server_name') { required }
|
20
|
-
argument('cmd') { required }
|
21
|
-
def run
|
22
|
-
Dollhouse.launch_from(Dir.pwd)
|
23
|
-
Dollhouse.instances[params['server_name'].value].instance_eval params['cmd'].value
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
mode 'destroy' do
|
9
|
+
mode 'run' do
|
28
10
|
argument('server_name') { required }
|
11
|
+
argument('task_name') { required }
|
29
12
|
def run
|
30
13
|
Dollhouse.launch_from(Dir.pwd)
|
31
|
-
Dollhouse.
|
14
|
+
Dollhouse.run(params['server_name'].value, params['task_name'].value)
|
32
15
|
end
|
33
16
|
end
|
34
17
|
|
35
18
|
mode 'list' do
|
36
19
|
def run
|
37
20
|
Dollhouse.launch_from(Dir.pwd)
|
38
|
-
|
21
|
+
puts Dollhouse.instances.list
|
39
22
|
end
|
40
23
|
end
|
41
24
|
|
42
|
-
|
25
|
+
# bit hax
|
26
|
+
mode 'exec' do
|
43
27
|
argument('server_name') { required }
|
44
|
-
argument('
|
28
|
+
argument('cmd') { required }
|
45
29
|
def run
|
46
30
|
Dollhouse.launch_from(Dir.pwd)
|
47
|
-
Dollhouse.
|
31
|
+
Dollhouse.execute(params['server_name'].value, params['cmd'].value)
|
48
32
|
end
|
49
33
|
end
|
50
34
|
|
@@ -18,22 +18,6 @@ module Dollhouse
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class ServerBuilder < Struct.new(:name)
|
21
|
-
def instance_type t
|
22
|
-
@instance_type = t
|
23
|
-
end
|
24
|
-
|
25
|
-
def os o
|
26
|
-
@os = o
|
27
|
-
end
|
28
|
-
|
29
|
-
def from_latest_snapshot snapshot_name
|
30
|
-
@snapshot = snapshot_name
|
31
|
-
end
|
32
|
-
|
33
|
-
def first_boot &blk
|
34
|
-
callbacks[:first_boot] = blk
|
35
|
-
end
|
36
|
-
|
37
21
|
def task name, &blk
|
38
22
|
callbacks[name.to_s] = blk
|
39
23
|
end
|
data/lib/dollhouse/deployment.rb
CHANGED
@@ -16,13 +16,6 @@ module Dollhouse
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def server_online cloud_name, server
|
20
|
-
online_server = OnlineServer[cloud_name, self.name, server.name, :running]
|
21
|
-
Dollhouse.instances.server_came_online online_server
|
22
|
-
online_server.bootstrap
|
23
|
-
online_server.instance_eval &server.callbacks[:first_boot]
|
24
|
-
end
|
25
|
-
|
26
19
|
def self.[](deployment)
|
27
20
|
raise "Unknown deployment #{deployment}" unless all.has_key? deployment.to_s
|
28
21
|
all[deployment.to_s]
|
data/lib/dollhouse/dollhouse.rb
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
module Dollhouse
|
2
|
-
|
3
|
-
|
4
|
-
# may need something cleverer in the future
|
5
|
-
require dir + '/config/dollhouse/config.rb'
|
6
|
-
Dir.glob(dir + '/config/dollhouse/auth.rb') { |f| require f } #optional require
|
7
|
-
Dir.glob(dir + '/config/dollhouse/deployments/*.rb') { |f| require f }
|
8
|
-
end
|
2
|
+
class << self
|
3
|
+
attr_accessor :root, :instances
|
9
4
|
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
def launch_from(dir)
|
6
|
+
self.root = dir
|
7
|
+
# may need something cleverer in the future
|
8
|
+
raise "You should be running this in your application root, or at least somewhere with a config/dollhouse subdirectory" if !File.exists?(File.join(dir, 'config', 'dollhouse'))
|
9
|
+
Dir.glob(dir + '/config/dollhouse/config.rb') { |f| require f } #optional require
|
10
|
+
Dir.glob(dir + '/config/dollhouse/auth.rb') { |f| require f } #optional require
|
11
|
+
Dir.glob(dir + '/config/dollhouse/deployments/*.rb') { |f| require f }
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
def run(server_name, task_name)
|
15
|
+
instances[server_name].run_task task_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute(server_name, cmd)
|
19
|
+
instances[server_name].instance_eval cmd
|
20
|
+
end
|
16
21
|
|
17
22
|
def instances
|
18
23
|
@instances ||= Instances.new
|
@@ -1,35 +1,31 @@
|
|
1
1
|
module Dollhouse
|
2
|
-
class
|
2
|
+
class Instance < Struct.new(:instance_name, :deployment_name, :server_name, :ip)
|
3
3
|
attr_accessor :user, :password
|
4
4
|
|
5
5
|
def bootstrap
|
6
|
-
|
6
|
+
cloud_adapter.execute(instance_name, %Q{bash -c "`wget -O- babushka.me/up/hard`"}, default_opts)
|
7
7
|
end
|
8
8
|
|
9
9
|
def babushka taskname, vars = {}
|
10
10
|
# not used yet, but this makes sense. --defaults (or headless) is the default!
|
11
11
|
if vars == :no_defaults
|
12
|
-
|
12
|
+
cloud_adapter.execute(instance_name, "babushka '#{taskname}'", default_opts)
|
13
13
|
else
|
14
14
|
if !vars.empty?
|
15
15
|
write_file(".babushka/vars/#{taskname}", {
|
16
16
|
:vars => vars.map_keys(&:to_s).map_values { |v| {:value => v} }
|
17
17
|
}.to_yaml)
|
18
18
|
end
|
19
|
-
|
19
|
+
cloud_adapter.execute(instance_name, "babushka '#{taskname}' --defaults", default_opts)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
def shell cmd, opts = {}
|
24
|
-
|
24
|
+
cloud_adapter.execute(instance_name, cmd, default_opts.merge(opts))
|
25
25
|
end
|
26
26
|
|
27
27
|
def write_file path, content, opts = {}
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
def destroy
|
32
|
-
Dollhouse.cloud_adapter.destroy(name_in_cloud)
|
28
|
+
cloud_adapter.write_file(instance_name, path, content, default_opts.merge(opts))
|
33
29
|
end
|
34
30
|
|
35
31
|
def as user, opts = {}, &blk
|
@@ -42,10 +38,6 @@ module Dollhouse
|
|
42
38
|
self.password = old_password
|
43
39
|
end
|
44
40
|
|
45
|
-
def take_snapshot name
|
46
|
-
Dollhouse.cloud_adapter.take_snapshot(name_in_cloud, name + "-" + Time.now.strftime("%Y%M%d-%H%M%S"))
|
47
|
-
end
|
48
|
-
|
49
41
|
def server
|
50
42
|
deployment.servers.find { |s| s.name == server_name }
|
51
43
|
end
|
@@ -58,6 +50,16 @@ module Dollhouse
|
|
58
50
|
instance_eval &server.callbacks[task_name]
|
59
51
|
end
|
60
52
|
|
53
|
+
def to_yaml
|
54
|
+
{instance_name => {'deployment_name' => deployment_name, 'server_name' => server_name, 'ip' => ip}}
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.from_yaml(hash)
|
58
|
+
hash.map_values_with_keys { |k,v|
|
59
|
+
Instance[k, v['deployment_name'], v['server_name'], v['ip']]
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
61
63
|
private
|
62
64
|
|
63
65
|
def default_opts
|
@@ -66,5 +68,10 @@ module Dollhouse
|
|
66
68
|
opts.merge!({:sudo_password => password}) if password
|
67
69
|
opts
|
68
70
|
end
|
71
|
+
|
72
|
+
# This could return different adapters, that connect to servers in different ways. For now we have a simple
|
73
|
+
def cloud_adapter
|
74
|
+
@cloud_adapter ||= ManualConfig.new
|
75
|
+
end
|
69
76
|
end
|
70
77
|
end
|
data/lib/dollhouse/instances.rb
CHANGED
@@ -15,12 +15,16 @@ module Dollhouse
|
|
15
15
|
|
16
16
|
def online_servers
|
17
17
|
@online_servers = if File.exists? "#{Dollhouse.root}/config/dollhouse/instances/servers.yml"
|
18
|
-
YAML::load_file("#{Dollhouse.root}/config/dollhouse/instances/servers.yml")
|
18
|
+
Instance.from_yaml(YAML::load_file("#{Dollhouse.root}/config/dollhouse/instances/servers.yml") || {})
|
19
19
|
else
|
20
20
|
{}
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
def list
|
25
|
+
online_servers.inspect
|
26
|
+
end
|
27
|
+
|
24
28
|
private
|
25
29
|
|
26
30
|
def save!
|
@@ -1,22 +1,13 @@
|
|
1
1
|
module Dollhouse
|
2
|
-
class ManualConfig
|
2
|
+
class ManualConfig
|
3
3
|
def execute(name, cmd, opts = {})
|
4
4
|
#nasty, but sudo_password isn't valid for starting a connection
|
5
5
|
sudo_password = opts.delete(:sudo_password)
|
6
6
|
ssh_conn(Dollhouse.instances[name].ip, opts[:user] || 'root', opts) do
|
7
|
-
p "Executing: #{cmd}"
|
8
7
|
exec cmd, {:sudo_password => sudo_password}
|
9
8
|
end
|
10
9
|
end
|
11
10
|
|
12
|
-
def boot_new_server(name, callback, opts)
|
13
|
-
raise "You can't, you fool!"
|
14
|
-
end
|
15
|
-
|
16
|
-
def list
|
17
|
-
Dollhouse.instances.online_servers.values.select { |i| i.status == :running }
|
18
|
-
end
|
19
|
-
|
20
11
|
def write_file(name, path, content, opts)
|
21
12
|
ssh_conn(Dollhouse.instances[name].ip, opts[:user] || 'root', opts) do
|
22
13
|
write_file(path) do |out|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Let it be known, dgoodlad is a profase.
|
1
|
+
# Let it be known, @dgoodlad is a profase.
|
2
2
|
|
3
3
|
require 'net/ssh'
|
4
4
|
require 'net/sftp'
|
@@ -48,7 +48,7 @@ module Dollhouse
|
|
48
48
|
output = ''
|
49
49
|
status_code = nil
|
50
50
|
|
51
|
-
puts "Executing
|
51
|
+
puts "## Executing: #{command}"
|
52
52
|
|
53
53
|
ch.exec(command) do |ch, success|
|
54
54
|
raise "Failed to start execution!" unless success
|
data/lib/dollhouse/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dollhouse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 62196353
|
5
|
+
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 1
|
9
8
|
- 2
|
10
|
-
|
9
|
+
- 0
|
10
|
+
- beta
|
11
|
+
- 1
|
12
|
+
version: 0.2.0.beta.1
|
11
13
|
platform: ruby
|
12
14
|
authors:
|
13
15
|
- Glen Maddern
|
@@ -15,7 +17,7 @@ autorequire:
|
|
15
17
|
bindir: bin
|
16
18
|
cert_chain: []
|
17
19
|
|
18
|
-
date: 2010-11-
|
20
|
+
date: 2010-11-12 00:00:00 +11:00
|
19
21
|
default_executable:
|
20
22
|
dependencies:
|
21
23
|
- !ruby/object:Gem::Dependency
|
@@ -98,26 +100,10 @@ dependencies:
|
|
98
100
|
version: 0.10.3
|
99
101
|
type: :runtime
|
100
102
|
version_requirements: *id005
|
101
|
-
- !ruby/object:Gem::Dependency
|
102
|
-
name: bundler
|
103
|
-
prerelease: false
|
104
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
|
-
requirements:
|
107
|
-
- - ~>
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
hash: 17
|
110
|
-
segments:
|
111
|
-
- 1
|
112
|
-
- 0
|
113
|
-
- 3
|
114
|
-
version: 1.0.3
|
115
|
-
type: :development
|
116
|
-
version_requirements: *id006
|
117
103
|
- !ruby/object:Gem::Dependency
|
118
104
|
name: cucumber
|
119
105
|
prerelease: false
|
120
|
-
requirement: &
|
106
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
121
107
|
none: false
|
122
108
|
requirements:
|
123
109
|
- - ~>
|
@@ -129,11 +115,11 @@ dependencies:
|
|
129
115
|
- 3
|
130
116
|
version: 0.9.3
|
131
117
|
type: :development
|
132
|
-
version_requirements: *
|
118
|
+
version_requirements: *id006
|
133
119
|
- !ruby/object:Gem::Dependency
|
134
120
|
name: rake
|
135
121
|
prerelease: false
|
136
|
-
requirement: &
|
122
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
137
123
|
none: false
|
138
124
|
requirements:
|
139
125
|
- - ~>
|
@@ -145,11 +131,11 @@ dependencies:
|
|
145
131
|
- 7
|
146
132
|
version: 0.8.7
|
147
133
|
type: :development
|
148
|
-
version_requirements: *
|
134
|
+
version_requirements: *id007
|
149
135
|
- !ruby/object:Gem::Dependency
|
150
136
|
name: rspec
|
151
137
|
prerelease: false
|
152
|
-
requirement: &
|
138
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
153
139
|
none: false
|
154
140
|
requirements:
|
155
141
|
- - ~>
|
@@ -161,7 +147,7 @@ dependencies:
|
|
161
147
|
- 0
|
162
148
|
version: 2.0.0
|
163
149
|
type: :development
|
164
|
-
version_requirements: *
|
150
|
+
version_requirements: *id008
|
165
151
|
description: Dollhouse is a way to deploy servers. It is designed to be used with Babushka.
|
166
152
|
email: glen.maddern@gmail.com
|
167
153
|
executables:
|
@@ -173,14 +159,12 @@ extra_rdoc_files: []
|
|
173
159
|
files:
|
174
160
|
- bin/dollhouse
|
175
161
|
- lib/core_ext/hash.rb
|
176
|
-
- lib/dollhouse/cloud_adapter.rb
|
177
162
|
- lib/dollhouse/config_loader.rb
|
178
163
|
- lib/dollhouse/deployment.rb
|
179
164
|
- lib/dollhouse/dollhouse.rb
|
165
|
+
- lib/dollhouse/instance.rb
|
180
166
|
- lib/dollhouse/instances.rb
|
181
167
|
- lib/dollhouse/manual_config.rb
|
182
|
-
- lib/dollhouse/online_server.rb
|
183
|
-
- lib/dollhouse/rackspace_cloud_adapter.rb
|
184
168
|
- lib/dollhouse/remote_server.rb
|
185
169
|
- lib/dollhouse/server.rb
|
186
170
|
- lib/dollhouse/version.rb
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Dollhouse
|
2
|
-
class CloudAdapter
|
3
|
-
def initialize
|
4
|
-
raise NotImplementedError, %Q{
|
5
|
-
Must implement the following methods:
|
6
|
-
|
7
|
-
boot_new_server(name, callback, opts)
|
8
|
-
- name is the unique id for the server in this cloud
|
9
|
-
- callback is a lambda to be fired_off when that server comes online
|
10
|
-
- generally, opts has :machine_id, :ram_size, :data_center, :backup_image_id
|
11
|
-
|
12
|
-
execute(server_name, cmd, opts)
|
13
|
-
|
14
|
-
write_file(server_name, path, content, opts)
|
15
|
-
|
16
|
-
list
|
17
|
-
|
18
|
-
} unless [:boot_new_server, :execute, :write_file, :list].all? { |m| self.class.method_defined? m }
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,101 +0,0 @@
|
|
1
|
-
module Dollhouse
|
2
|
-
class RackspaceCloudAdapter < CloudAdapter
|
3
|
-
FLAVORS = {"256mb" => 1, "512mb" => 2, "1024mb" => 3, "2048mb" => 4}
|
4
|
-
IMAGES = {"Ubuntu 10.04" => 49}
|
5
|
-
|
6
|
-
def conn
|
7
|
-
require 'fog'
|
8
|
-
@conn ||= Fog::Rackspace::Servers.new(
|
9
|
-
:rackspace_api_key => Auth::Rackspace::API_KEY,
|
10
|
-
:rackspace_username => Auth::Rackspace::USERNAME
|
11
|
-
)
|
12
|
-
end
|
13
|
-
|
14
|
-
def boot_new_server(name, callback, opts)
|
15
|
-
flavor_num = FLAVORS[opts[:instance_type]]
|
16
|
-
raise "Unknown instance_type of #{opts[:instance_type].inspect}. Permitted values are #{FLAVORS.keys.inspect}" unless flavor_num
|
17
|
-
image_num = if opts[:snapshot]
|
18
|
-
image = conn.images.select { |i| i.created_at && i.name =~ Regexp.new(Regexp.escape(opts[:snapshot])) }.sort_by { |i| i.created_at }.last
|
19
|
-
raise "Snapshot name of #{opts[:snapshot]} doesn't match any images!" unless image
|
20
|
-
puts "Booting from image: #{image.inspect}"
|
21
|
-
image.id
|
22
|
-
else
|
23
|
-
raise "Unknown os of #{opts[:os].inspect}. Permitted values are #{IMAGES.keys.inspect}" unless IMAGES[opts[:os]]
|
24
|
-
IMAGES[opts[:os]]
|
25
|
-
end
|
26
|
-
|
27
|
-
puts "Booting server #{name}"
|
28
|
-
server = conn.servers.create(:flavor_id => flavor_num, :image_id => image_num, :name => name)
|
29
|
-
|
30
|
-
puts "Server booted: #{server.inspect}"
|
31
|
-
# make this asynch soon
|
32
|
-
server.wait_for { ready? }
|
33
|
-
puts "Server #{name} online. Adding our private key"
|
34
|
-
server.public_key_path = Auth::KEYPAIR + ".pub"
|
35
|
-
server.setup
|
36
|
-
puts "Done. Ready to go!"
|
37
|
-
|
38
|
-
callback.call
|
39
|
-
end
|
40
|
-
|
41
|
-
def execute(name, cmd, opts = {})
|
42
|
-
#nasty, but sudo_password isn't valid for starting a connection
|
43
|
-
sudo_password = opts.delete(:sudo_password)
|
44
|
-
ssh_conn(name, opts) do
|
45
|
-
p "Executing: #{cmd}"
|
46
|
-
exec cmd, {:sudo_password => sudo_password}
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def write_file(name, path, content, opts = {})
|
51
|
-
ssh_conn(name, opts) do
|
52
|
-
write_file(path) do |out|
|
53
|
-
out.puts content
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def destroy name
|
59
|
-
server = get_server name
|
60
|
-
puts "Killing server #{server.inspect}"
|
61
|
-
server.destroy
|
62
|
-
puts "Done."
|
63
|
-
end
|
64
|
-
|
65
|
-
def take_snapshot name, snapshot_name
|
66
|
-
response = conn.create_image get_server(name).id, 'name' => snapshot_name
|
67
|
-
puts "Created image: #{response.body['image'].inspect}"
|
68
|
-
end
|
69
|
-
|
70
|
-
def list
|
71
|
-
conn.servers
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
def ssh_conn(name, opts, &blk)
|
77
|
-
#nasty, but sudo_password isn't valid for starting a connection
|
78
|
-
opts.delete(:sudo_password)
|
79
|
-
ssh_conns[[name, opts]].instance_eval(&blk)
|
80
|
-
end
|
81
|
-
|
82
|
-
def ssh_conns
|
83
|
-
@ssh_conns ||= Hash.new { |h, (name, opts)|
|
84
|
-
puts "Establishing connection to #{name}:"
|
85
|
-
#change this to use instances.yml, or something
|
86
|
-
server = get_server name
|
87
|
-
host = server.addresses['public'].first
|
88
|
-
user = opts[:user] || 'root'
|
89
|
-
puts "Connecting to #{host} as #{user}..."
|
90
|
-
|
91
|
-
h[[name, opts]] = RemoteServer.new(host, user, {:forward_agent => true}.merge(opts))
|
92
|
-
}
|
93
|
-
end
|
94
|
-
|
95
|
-
def get_server name
|
96
|
-
server = conn.servers.find { |s| s.name == name }
|
97
|
-
raise "Can't find server #{name}" if server.nil?
|
98
|
-
server
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|