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 CHANGED
@@ -6,45 +6,29 @@ require 'dollhouse'
6
6
  require 'main'
7
7
 
8
8
  Main do
9
- mode 'deploy' do
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.cloud_adapter.destroy params['server_name'].value
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
- p Dollhouse.cloud_adapter.list
21
+ puts Dollhouse.instances.list
39
22
  end
40
23
  end
41
24
 
42
- mode 'run' do
25
+ # bit hax
26
+ mode 'exec' do
43
27
  argument('server_name') { required }
44
- argument('task_name') { required }
28
+ argument('cmd') { required }
45
29
  def run
46
30
  Dollhouse.launch_from(Dir.pwd)
47
- Dollhouse.instances[params['server_name'].value].run_task(params['task_name'].value)
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
@@ -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]
@@ -1,18 +1,23 @@
1
1
  module Dollhouse
2
- def self.launch_from(dir)
3
- self.root = dir
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
- def self.initiate_deployment(deployment, opts = {})
11
- Deployment[deployment].initiate(opts)
12
- end
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
- class << self
15
- attr_accessor :root, :cloud_adapter, :instances
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 OnlineServer < Struct.new(:name_in_cloud, :deployment_name, :server_name, :status, :ip)
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
- Dollhouse.cloud_adapter.execute(name_in_cloud, %Q{bash -c "`wget -O- babushka.me/up/hard`"}, default_opts)
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
- Dollhouse.cloud_adapter.execute(name_in_cloud, "babushka '#{taskname}'", default_opts)
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
- Dollhouse.cloud_adapter.execute(name_in_cloud, "babushka '#{taskname}' --defaults", default_opts)
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
- Dollhouse.cloud_adapter.execute(name_in_cloud, cmd, default_opts.merge(opts))
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
- Dollhouse.cloud_adapter.write_file(name_in_cloud, path, content, default_opts.merge(opts))
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
@@ -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") or {}
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 < CloudAdapter
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:\n#{command}"
51
+ puts "## Executing: #{command}"
52
52
 
53
53
  ch.exec(command) do |ch, success|
54
54
  raise "Failed to start execution!" unless success
@@ -1,3 +1,3 @@
1
1
  module Dollhouse
2
- VERSION = "0.1.2" unless defined?(Dollhouse::VERSION)
2
+ VERSION = "0.2.0.beta.1" unless defined?(Dollhouse::VERSION)
3
3
  end
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: 31
5
- prerelease: false
4
+ hash: 62196353
5
+ prerelease: true
6
6
  segments:
7
7
  - 0
8
- - 1
9
8
  - 2
10
- version: 0.1.2
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-08 00:00:00 +11:00
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: &id007 !ruby/object:Gem::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: *id007
118
+ version_requirements: *id006
133
119
  - !ruby/object:Gem::Dependency
134
120
  name: rake
135
121
  prerelease: false
136
- requirement: &id008 !ruby/object:Gem::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: *id008
134
+ version_requirements: *id007
149
135
  - !ruby/object:Gem::Dependency
150
136
  name: rspec
151
137
  prerelease: false
152
- requirement: &id009 !ruby/object:Gem::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: *id009
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