hubcap 0.0.3 → 0.0.4

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/.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.