hubcap 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  .screenrc
2
+ Capfile
data/README.md CHANGED
@@ -17,23 +17,25 @@ derive from decentralization and pushing changes on-demand.)
17
17
 
18
18
  Here's a really simple infrastructure configuration file:
19
19
 
20
- group('us') {
21
- server('app.example.com') {
22
- role(:app)
23
- cap_attribute(:primary => true)
24
- }
25
- server('db.example.com') {
26
- role(:db)
27
- cap_attribute(:no_release => true)
28
- }
29
- }
30
-
31
- group('au') {
32
- server('example.com.au') {
33
- role(:app, :db)
34
- cap_attribute(:primary => true)
35
- }
36
- }
20
+ ```ruby
21
+ group('us') {
22
+ server('app.example.com') {
23
+ role(:app)
24
+ cap_attribute(:primary => true)
25
+ }
26
+ server('db.example.com') {
27
+ role(:db)
28
+ cap_attribute(:no_release => true)
29
+ }
30
+ }
31
+
32
+ group('au') {
33
+ server('example.com.au') {
34
+ role(:app, :db)
35
+ cap_attribute(:primary => true)
36
+ }
37
+ }
38
+ ```
37
39
 
38
40
  Using this config, you could tell Capistrano to deploy to all servers, servers
39
41
  in one group, or just a single server.
@@ -42,54 +44,55 @@ Here's a more advanced example - an application that can be deployed to a set
42
44
  of *staging* servers or a larger set of *production* servers. It has special
43
45
  parameters that Puppet will use.
44
46
 
45
- # An application called 'readme' that uses Cap's default deployment recipe.
46
- application('readme', :recipes => 'deploy') {
47
- # Set a capistrano variable.
48
- cap_set('repository', 'git@github.com:joseph/readme.git')
49
-
50
- # Declare that all servers will have the 'baseline' puppet class.
51
- role(:puppet => 'baseline')
52
-
53
- group('staging') {
54
- # Puppet gets a $::exception_subject_prefix variable on these servers.
55
- param('exception_subject_prefix' => '[STAGING] ')
56
- # For simple staging, just one server that does everything.
57
- server('readme.stage') {
58
- role(:cap => [:web, :app, :db], :puppet => ['proxy', 'app', 'db'])
59
- }
47
+ ```ruby
48
+ # An application called 'readme' that uses Cap's default deployment recipe.
49
+ application('readme', :recipes => 'deploy') {
50
+ # Set a capistrano variable.
51
+ cap_set('repository', 'git@github.com:joseph/readme.git')
52
+
53
+ # Declare that all servers will have the 'baseline' puppet class.
54
+ role(:puppet => 'baseline')
55
+
56
+ group('staging') {
57
+ # Puppet gets a $::exception_subject_prefix variable on these servers.
58
+ param('exception_subject_prefix' => '[STAGING] ')
59
+ # For simple staging, just one server that does everything.
60
+ server('readme.stage') {
61
+ role(:cap => [:web, :app, :db], :puppet => ['proxy', 'app', 'db'])
62
+ }
63
+ }
64
+
65
+ group('production') {
66
+ # Puppet gets these top-scope variables on servers in this group.
67
+ param(
68
+ 'exception_subject_prefix' => '[PRODUCTION] ',
69
+ 'env' => {
70
+ 'FORCE_SSL' => true,
71
+ 'S3_KEY' => 'AKIAKJRK23943202JK',
72
+ 'S3_SECRET' => 'KDJkaddsalkjfkawjri32jkjaklvjgakljkj'
60
73
  }
74
+ )
61
75
 
62
- group('production') {
63
- # Puppet gets these top-scope variables on servers in this group.
64
- param(
65
- 'exception_subject_prefix' => '[PRODUCTION] ',
66
- 'env' => {
67
- 'FORCE_SSL' => true,
68
- 'S3_KEY' => 'AKIAKJRK23943202JK',
69
- 'S3_SECRET' => 'KDJkaddsalkjfkawjri32jkjaklvjgakljkj'
70
- }
71
- )
72
-
73
- group('proxy') {
74
- # Servers will have the :web role and the 'proxy' puppet class.
75
- role(:cap => :web, :puppet => 'proxy')
76
- server('proxy-1', :address => '10.10.10.5')
77
- }
78
-
79
- group('app') {
80
- # Servers will have the :app role and the 'app' puppet class.
81
- role(:app)
82
- server('app-1', :address => '10.10.10.10')
83
- server('app-2', :address => '10.10.10.11')
84
- }
85
-
86
- group('db') {
87
- role(:db)
88
- server('db-1', :address => '10.10.10.50')
89
- }
90
- }
76
+ group('proxy') {
77
+ # Servers will have the :web role and the 'proxy' puppet class.
78
+ role(:cap => :web, :puppet => 'proxy')
79
+ server('proxy-1', :address => '10.10.10.5')
91
80
  }
92
81
 
82
+ group('app') {
83
+ # Servers will have the :app role and the 'app' puppet class.
84
+ role(:app)
85
+ server('app-1', :address => '10.10.10.10')
86
+ server('app-2', :address => '10.10.10.11')
87
+ }
88
+
89
+ group('db') {
90
+ role(:db)
91
+ server('db-1', :address => '10.10.10.50')
92
+ }
93
+ }
94
+ }
95
+ ```
93
96
 
94
97
  Save this as `example.rb` in a `hub` subdirectory of the location of your
95
98
  `Capfile`.
@@ -167,8 +170,10 @@ http://docs.puppetlabs.com/guides/external_nodes.html
167
170
  If you'd rather run `cap` than `hubcap`, you can load your hub configuration
168
171
  directly in your `Capfile`. Add this to the end of the file:
169
172
 
170
- require('hubcap')
171
- Hubcap.load('', 'hub').configure_capistrano(self)
173
+ ```ruby
174
+ require('hubcap')
175
+ Hubcap.load('', 'hub').configure_capistrano(self)
176
+ ```
172
177
 
173
178
  The two arguments to `Hubcap.load` are the filter (where `''` means no filter),
174
179
  and the path to the hub configuration. This will load `*.rb` in the `hub`
@@ -178,15 +183,17 @@ arguments -- whole directories or specific files.
178
183
  If you want to simulate the behaviour of the `hubcap` script, you could do it
179
184
  with something like this in your `Capfile`.
180
185
 
181
- # Load servers and sets from node config. Any recipes loaded after this
182
- # point will be available only in application mode.
183
- if (target = ENV['TO']) && !ENV['TO'].empty?
184
- target = '' if target == 'ALL'
185
- require('hubcap')
186
- Hubcap.load(target, 'hub').configure_capistrano(self)
187
- else
188
- warn('NB: No servers specified. Target a Hubcap group with TO.')
189
- end
186
+ ```ruby
187
+ # Load servers and sets from node config. Any recipes loaded after this
188
+ # point will be available only in application mode.
189
+ if (target = ENV['TO']) && !ENV['TO'].empty?
190
+ target = '' if target == 'ALL'
191
+ require('hubcap')
192
+ Hubcap.load(target, 'hub').configure_capistrano(self)
193
+ else
194
+ warn('NB: No servers specified. Target a Hubcap group with TO.')
195
+ end
196
+ ```
190
197
 
191
198
  In this set-up, you'd run `cap` like this:
192
199
 
data/lib/hubcap/group.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  class Hubcap::Group
2
2
 
3
+ IP_PATTERN = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/
4
+
3
5
  attr_reader(:name, :parent, :children)
4
6
 
5
7
  # Supply the parent group, the name of this new group and a block of code to
@@ -198,7 +200,7 @@ class Hubcap::Group
198
200
  # the IP of a hostname just for a child-group, you can (but: caveat emptor).
199
201
  #
200
202
  def host(hash)
201
- @hosts = hash
203
+ @hosts.update(hash)
202
204
  end
203
205
 
204
206
 
@@ -232,13 +234,32 @@ class Hubcap::Group
232
234
  #
233
235
  def resolv(*hnames)
234
236
  if hnames.size == 1
235
- hosts[hnames.first] || Resolv.getaddress(hnames.first)
237
+ result = lookup(hnames.first)
238
+ result.match(IP_PATTERN) ? result : Resolv.getaddress(result)
236
239
  else
237
240
  hnames.collect { |hname| resolv(hname) }
238
241
  end
239
242
  end
240
243
 
241
244
 
245
+ # Dereferences the host name using the @hosts table. Follows references.
246
+ # Returns the first value that looks like an IP address OR that it can't
247
+ # find in the table. (So, if there's no match, you just get the name back.
248
+ #
249
+ def lookup(hname, history = [])
250
+ return hname if hname.match(IP_PATTERN)
251
+ result = hosts[hname]
252
+ if !result
253
+ return hname
254
+ elsif history.include?(result)
255
+ raise(Hubcap::HostCircularReference, history.to_s)
256
+ else
257
+ history << hname
258
+ lookup(result, history)
259
+ end
260
+ end
261
+
262
+
242
263
  # Instantiate an application as a child of this group.
243
264
  #
244
265
  def application(name, options = {}, &blk)
@@ -301,5 +322,6 @@ class Hubcap::Group
301
322
 
302
323
  class Hubcap::GroupWithoutParent < StandardError; end
303
324
  class Hubcap::InvalidParamKeyType < StandardError; end
325
+ class Hubcap::HostCircularReference < StandardError; end
304
326
 
305
327
  end
@@ -55,7 +55,7 @@ Capistrano::Configuration.instance(:must_exist).load do
55
55
  'echo "Ruby 1.9 verified";',
56
56
  'else',
57
57
  "#{apt_cmd} update;",
58
- "#{apt_cmd} install ruby1.9.1 ruby1.9.1-dev git-core;",
58
+ "#{apt_cmd} install ruby1.9.1 ruby1.9.1-dev git-core build-essential;",
59
59
  'fi'
60
60
  ].join(' '))
61
61
 
@@ -18,4 +18,11 @@ Capistrano::Configuration.instance(:must_exist).load do
18
18
 
19
19
  end
20
20
 
21
+
22
+ task(:ssh) do
23
+ host = hubcap.servers.first.address
24
+ puts("SSH connect to: #{user}@#{host}")
25
+ system("ssh -A #{user}@#{host}")
26
+ end
27
+
21
28
  end
data/lib/hubcap/server.rb CHANGED
@@ -4,8 +4,14 @@ class Hubcap::Server < Hubcap::Group
4
4
 
5
5
 
6
6
  def initialize(parent, name, options = {}, &blk)
7
- @address = options[:address] || name
8
7
  super(parent, name, &blk)
8
+ # If name is an IP, or is not in hosts hash, use name as address
9
+ # Otherwise, dereference it from the hash and assign it
10
+ unless @address = options[:address]
11
+ hist = history.join('.')
12
+ @address = lookup(hist)
13
+ @address = lookup(name) if @address == hist
14
+ end
9
15
  end
10
16
 
11
17
 
@@ -1,5 +1,5 @@
1
1
  module Hubcap
2
2
 
3
- VERSION = '0.0.3'
3
+ VERSION = '0.0.4'
4
4
 
5
5
  end
@@ -66,4 +66,104 @@ class Hubcap::TestServer < Test::Unit::TestCase
66
66
  assert_equal([1, 2], hash['parameters'].values.sort)
67
67
  end
68
68
 
69
+
70
+ def test_host_lookup_single_level
71
+ hub = Hubcap.hub {
72
+ host('test' => '0.0.0.0')
73
+ server('test')
74
+ }
75
+ assert_equal('0.0.0.0', hub.servers.first.address)
76
+ end
77
+
78
+
79
+ def test_host_lookup_multiple_levels
80
+ hub = Hubcap.hub {
81
+ host('g1.t1' => '1.1.1.1')
82
+ host('g1.a1.t2' => '2.2.2.2')
83
+ group('g1') {
84
+ server('t1')
85
+ application('a1') {
86
+ server('t2')
87
+ }
88
+ }
89
+ }
90
+ assert_equal('1.1.1.1', hub.servers[0].address)
91
+ assert_equal('2.2.2.2', hub.servers[1].address)
92
+ end
93
+
94
+
95
+ def test_host_lookup_where_name_is_ip
96
+ hub = Hubcap.hub {
97
+ host(
98
+ 'foo' => '255.255.255.255',
99
+ '1.1.1.1' => '255.255.255.254'
100
+ )
101
+ server('1.1.1.1')
102
+ }
103
+ assert_equal('1.1.1.1', hub.servers.first.address)
104
+ end
105
+
106
+
107
+ def test_host_lookup_where_name_is_not_in_hosts
108
+ hub = Hubcap.hub {
109
+ host(
110
+ 'foo.bar.com' => '255.255.255.255',
111
+ 'baz.bar.com' => '255.255.255.254'
112
+ )
113
+ server('garply.bar.com')
114
+ }
115
+ assert_equal('garply.bar.com', hub.servers.first.address)
116
+ end
117
+
118
+
119
+ def test_host_lookup_dereferencing
120
+ hub = Hubcap.hub {
121
+ host('some.other.thing' => '1.1.1.1')
122
+ group('g1') {
123
+ host('g1.t1' => 'some.other.thing')
124
+ server('t1')
125
+ }
126
+ }
127
+ assert_equal('1.1.1.1', hub.servers.first.address)
128
+ end
129
+
130
+
131
+ def test_host_lookup_handle_circular_reference
132
+ assert_raises(Hubcap::HostCircularReference) {
133
+ hub = Hubcap.hub {
134
+ host('some.other.thing' => 'g1.t1')
135
+ group('g1') {
136
+ host('g1.t1' => 'some.other.thing')
137
+ server('t1')
138
+ }
139
+ }
140
+ }
141
+ end
142
+
143
+
144
+ def test_host_lookup_where_hosts_is_overridden_in_subgroup
145
+ hub = Hubcap.hub {
146
+ host('g1.t1' => '2.2.2.2')
147
+ group('g1') {
148
+ host('g1.t1' => '1.1.1.1')
149
+ server('t1')
150
+ }
151
+ }
152
+ assert_equal('1.1.1.1', hub.servers[0].address)
153
+ end
154
+
155
+
156
+ def test_resolv
157
+ hub = Hubcap.hub {
158
+ host('g1.t1' => '2.2.2.2')
159
+ group('g1') {
160
+ host('g1.t1' => '1.1.1.1')
161
+ server('t1')
162
+ }
163
+ server('t2', :address => resolv('g1.t1'))
164
+ }
165
+ assert_equal('1.1.1.1', hub.servers[0].address)
166
+ assert_equal('2.2.2.2', hub.servers[1].address)
167
+ end
168
+
69
169
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hubcap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-27 00:00:00.000000000 Z
12
+ date: 2012-09-29 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Create a hub for your server configuration. Use it with Capistrano, Puppet
15
15
  and others.