dollhouse 0.1.2 → 0.2.0.beta.1
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/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
|