pawnee 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,4 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
- coverage
18
+ coverage
19
+ spec/vagrant/.vagrant
data/docs/TODO.md CHANGED
@@ -6,10 +6,7 @@ TODO: Add a as_user('user') do .. end option
6
6
  - maybe have an option to run from within a shell, or we could get them into the right place every time
7
7
  - maybe we should add a system to "get you to root, then get you to another user"
8
8
  TODO: Need to make a clear way for pawnee gems (and recipes) to provide actions (example, git gem provides git actions)
9
- TODO: Run actions in threads (across multiple servers)
10
9
  TODO: Test to make sure arguments work directly as well (they probably don't right now)
11
- TODO: System to check for and register updates/modifications
12
- TODO: Add apt-get update to package stuff - make it only run update once per all jobs
13
10
  TODO: Make it so copied files can be overridden in a rails project
14
11
  TODO: Track modified on compile?
15
12
  TODO: Should setup self.source_root to point to the templates dir in the gem
@@ -17,6 +14,33 @@ TODO: Add --verbose option that shows the output of any outputs (bundler for exa
17
14
  - maybe show stderr by default?
18
15
  - maybe option to show on run/exec
19
16
  - show stderr when there's a non-0 exit status
17
+ TODO: Make sure it would print out any errors from bundler
18
+ TODO: Raise error on run error (with options to ignore during run, or options to always ignore (global config))
19
+ TODO: Allow ssh host strings user:pw@domain
20
+ TODO: Make thor-ssh local api compatible
21
+ TODO: Move to self namespacing - remove global_options
22
+ TODO: Work out dependency loading
23
+ TODO: Make the source_root work by default
24
+
25
+
26
+
27
+ THINK ABOUT:
28
+
29
+ ---- logging system -----
30
+
31
+ Log levels
32
+ - error - stderr
33
+ - info - actions
34
+ - debug - stdout
35
+
36
+ State Changes (and colors):
37
+ - create (green)
38
+ - update (yellow)
39
+ - destroy (red?)
40
+ - identical (blue)
41
+
42
+ Log Actions
43
+ same, identical, update, add, run
20
44
 
21
45
 
22
46
  def setup
@@ -35,7 +35,7 @@ module Pawnee
35
35
  installed = false
36
36
  if options[:bin_file]
37
37
  # Check if the bin file is installed
38
- installed = exec("which #{options[:bin_file]}").strip != ''
38
+ installed = exec("which #{options[:bin_file]}", :log_stderr => false).strip != ''
39
39
  else
40
40
  raise "You must pass :bin_file or a block to compile" unless block_given?
41
41
  installed = yield()
@@ -45,6 +45,8 @@ module Pawnee
45
45
  say_status :already_compiled, url, :blue
46
46
  return true
47
47
  else
48
+
49
+ track_modification!
48
50
  # Compile and install
49
51
  Compile.new(self, url, temp_dir, options)
50
52
  end
@@ -10,6 +10,9 @@ module Pawnee
10
10
  data, config = args.shift, args.shift
11
11
  end
12
12
 
13
+ # Get the data if its a proc
14
+ data = data.call if data.is_a?(Proc)
15
+
13
16
  if destination_files.binread(destination)[data]
14
17
  say_status :identical, destination
15
18
  # Don't run again, the text is already in place
@@ -74,8 +74,9 @@ module Pawnee
74
74
  # to prevent this from happening too much.
75
75
  def update_package_list
76
76
  # TODO: this needs to be per server
77
- unless defined?(@@packages_updated)
78
- @@packages_updated = true
77
+ unless defined?(@@packages_updated) && @@packages_updated[server]
78
+ @@packages_updated ||= {}
79
+ @@packages_updated[server] = true
79
80
  as_root do
80
81
  exec('DEBIAN_FRONTEND=noninteractive apt-get -q -y update', :no_pty => true)
81
82
  end
@@ -110,7 +110,7 @@ module Pawnee
110
110
 
111
111
  # Reject any ones we just changed, so its as if we did a find with these
112
112
  @changed_attributes = @changed_attributes.reject {|k,v| [:uid, :gid, :groups, :login].include?(k.to_sym) }
113
- @original_groups_value = @groups
113
+ @original_groups_value = @groups.dup
114
114
  else
115
115
  # No user
116
116
  @uid = nil
@@ -25,7 +25,7 @@ module Pawnee
25
25
  include Pawnee::SshConnection
26
26
  include Roles
27
27
 
28
- attr_accessor :server
28
+ attr_accessor :server, :server_options, :setup_with_connection
29
29
 
30
30
  # Creates an instance of the pawnee recipe
31
31
  #
@@ -121,6 +121,7 @@ module Pawnee
121
121
  no_tasks {
122
122
 
123
123
  # # Invoke the given task if the given args.
124
+ # TODO: This method needs some refactoring
124
125
  def invoke_task(task, *args) #:nodoc:
125
126
  current = @_invocations[self.class]
126
127
 
@@ -130,6 +131,8 @@ module Pawnee
130
131
 
131
132
  unless current.include?(task.name)
132
133
  current << task.name
134
+
135
+ puts " Run task: #{self.class.class_role.to_s} - #{task.name} ".center(80, '*')
133
136
 
134
137
  # Setup the server connections before we run the task
135
138
  servers = options[:servers] || options['servers']
@@ -142,41 +145,71 @@ module Pawnee
142
145
  # No servers, just run locally
143
146
  task.run(self, *args)
144
147
  else
148
+ first_invoke = true
149
+ # Create a list of instances that we want to run this task
150
+ instances = []
151
+
145
152
  # Run the setup task, setting up the needed connections
146
153
  servers.each do |server|
147
154
  # Only run on this server if the server supports the current recipe's
148
155
  # role.
149
156
  next unless server.is_a?(String) || server.is_a?(Net::SSH::Connection::Session) || (server['roles'] && server['roles'].include?(self.class.class_role))
150
157
 
158
+ if first_invoke
159
+ instance = self
160
+ first_invoke = false
161
+ else
162
+ # Make a clone (since we want to run self on different threads)
163
+ # TODO: Look into how deep we need to make this clone (options may be
164
+ # shared right now)
165
+ instance = self.clone
166
+ end
151
167
  # Setup the connection to the server
152
168
  if server.is_a?(Net::SSH::Connection::Session)
153
- self.destination_connection = server
154
- self.server = server.host
169
+ instance.destination_connection = server
170
+ instance.server = server.host
171
+ instance.setup_with_connection = true
155
172
  elsif server.is_a?(String)
156
173
  # Server name is a string, asume ubuntu
157
- self.destination_connection = Net::SSH.start(server, 'ubuntu')
158
- self.server = server
174
+ instance.destination_connection = Net::SSH.start(server, 'ubuntu')
175
+ instance.server = server
159
176
  else
160
177
  # Server is a hash
161
- self.destination_connection = Net::SSH.start(server['domain'], server['user'] || 'ubuntu')
162
- self.server = server['domain']
178
+ instance.destination_connection = Net::SSH.start(server['domain'], server['user'] || 'ubuntu')
179
+ instance.server = server['domain']
180
+ instance.server_options = server
163
181
  end
164
182
 
183
+ # Setup server options if not setup already
184
+ instance.server_options = instance.server_options || {}
185
+
186
+ # Add the instance to the list of instances to run on
187
+ instances << instance
188
+ end
189
+
190
+ # Take the list of instances and invoke the task on each one in a seperate thread
191
+ threads = []
192
+
193
+ instances.each do |instance|
194
+ threads << Thread.new do
195
+ # Run the task
196
+ task.run(instance, *args)
165
197
 
166
- # Run the task
167
- task.run(self, *args)
168
-
169
- # Remove the server
170
- self.server = nil
198
+ # Remove the server
199
+ instance.server = nil
171
200
 
172
- # Close the connection
173
- if self.destination_connection
174
- # Close the conection only if we created it. If it was passed in as a connection
175
- # then the creator is responsible for closing it
176
- self.destination_connection.close unless server.is_a?(Net::SSH::Connection::Session)
177
- self.destination_connection = nil
201
+ # Close the connection
202
+ if instance.destination_connection
203
+ # Close the conection only if we created it. If it was passed in as a connection
204
+ # then the creator is responsible for closing it
205
+ instance.destination_connection.close unless instance.setup_with_connection
206
+ instance.destination_connection = nil
207
+ end
178
208
  end
179
209
  end
210
+
211
+ # Wait for threads to finish
212
+ threads.each(&:join)
180
213
  end
181
214
  end
182
215
  end
@@ -184,45 +217,23 @@ module Pawnee
184
217
  # Whenever say is used, also print out the server name
185
218
  def say(*args)
186
219
  text = args[0]
187
- text = "[#{server}]:\t" + text.to_s if server
220
+ name = (server_options && server_options['name']) || server
221
+ text = "[#{name}]:\t" + text.to_s if name
188
222
  super(text, *args[1..-1])
189
223
  end
190
224
 
191
225
  def say_status(*args)
192
226
  text = args[0]
193
- text = "[#{server}]:\t" + text.to_s if server
227
+ name = (server_options && server_options['name']) || server
228
+ text = "[#{name}]:\t" + text.to_s if name
194
229
  super(text, *args[1..-1])
195
230
  end
196
231
  }
197
232
 
198
- private
199
- def self.global_options
200
- @global_options = true
201
- yield
202
- @global_options = false
203
- end
204
-
233
+ private
205
234
  def self.check_unknown_options?(config)
206
235
  false
207
236
  end
208
-
209
-
210
- # Add options to create global method_options, otherwise
211
- # they are now prefixed by the recipe name by default
212
- def self.method_option(name, options={})
213
- scope = if options[:for]
214
- find_and_refresh_task(options[:for]).options
215
- else
216
- method_options
217
- end
218
-
219
- unless @global_options
220
- prefix = self.gem_name.gsub('-', '_')
221
- name = "#{prefix}_#{name}".to_sym
222
- end
223
-
224
- build_option(name, options, scope)
225
- end
226
237
 
227
238
 
228
239
  # Inherited is called when a class inherits from Pawnee::Base, it then
@@ -11,23 +11,18 @@ module Pawnee
11
11
  namespace ''
12
12
 
13
13
  desc "setup", "calls setup for each pawnee gem in bundler"
14
- global_options do
15
- method_option :roles, :type => :array, :default => :all
16
- end
14
+ method_option :roles, :type => :array, :default => :all
17
15
  def setup
18
16
  Pawnee::Base.invoke_roles(:setup, self.options[:roles], self.options)
19
17
  end
20
18
 
21
19
 
22
20
  desc "teardown", "calls teardown for each pawnee gem in bundler"
23
- global_options do
24
- method_option :roles, :type => :array, :default => :all
25
- end
21
+ method_option :roles, :type => :array, :default => :all
26
22
  def teardown
27
23
  Pawnee::Base.invoke_roles(:setup, self.options[:roles], self.options)
28
24
  end
29
25
 
30
-
31
26
  # Create a new gem (pulled from bundler and modified - MIT LICENSE)
32
27
  desc "gem GEM", "Creates a skeleton recipie"
33
28
  method_option :bin, :type => :boolean, :default => false, :aliases => '-b', :banner => "Generate a binary for your library."
@@ -6,21 +6,12 @@ require 'pawnee/base'
6
6
  <%- end -%>
7
7
  <%- j = config[:constant_array].size -%>
8
8
  <%= ' '*j %>class Base < Pawnee::Base
9
- <%= ' '*j %> global_options do
10
- <%= ' '*j %> method_option :servers, :type => :array, :required => true
11
- <%= ' '*j %> end
9
+ <%= ' '*j %> method_option :servers, :type => :array, :required => true
12
10
  <%= ' '*j %> desc "setup", 'setup on the destination server'
13
11
  <%= ' '*j %> def setup
14
12
  <%= ' '*j %> # Add your setup code here
15
13
  <%= ' '*j %> end
16
14
  <%= ' '*j %>
17
- <%= ' '*j %> global_options do
18
- <%= ' '*j %> method_option :servers, :type => :array, :required => true
19
- <%= ' '*j %> end
20
- <%= ' '*j %> desc "teardown", 'teardown on the destination server'
21
- <%= ' '*j %> def teardown
22
- <%= ' '*j %> # Add your teardown code here
23
- <%= ' '*j %> end
24
15
  <%= ' '*j %>end
25
16
  <%- (config[:constant_array].size-1).downto(0) do |i| -%>
26
17
  <%= ' '*i %>end
@@ -1,3 +1,3 @@
1
1
  module Pawnee
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -9,16 +9,20 @@ describe "compile actions" do
9
9
  @base.destination_connection = VagrantManager.connect
10
10
  end
11
11
 
12
- it 'should install a package' do
12
+ it 'should install a package and track modification' do
13
13
  @base.as_root do
14
14
  @base.remove_file("/usr/local/bin/redis-server")
15
15
  end
16
- @base.exec('which redis-server').should == ''
16
+ @base.exec('which redis-server', :log_stderr => false).should == ''
17
+ @base.modified?.should == false
17
18
 
18
19
  @base.compile('http://redis.googlecode.com/files/redis-2.4.15.tar.gz', '/home/vagrant/redis-server/', {:skip_configure => true, :bin_file => 'redis-server'})
19
20
 
20
- @base.exec('which redis-server').should_not == ''
21
+ @base.exec('which redis-server', :log_stderr => false).should_not == ''
22
+
23
+ @base.modified?.should == true
21
24
  end
22
25
 
26
+
23
27
  end
24
28
 
@@ -10,7 +10,7 @@ describe "user actions" do
10
10
  end
11
11
 
12
12
  def user_exists?(login)
13
- _, _, exit_code, _ = @base.exec("id -u #{login}", :with_codes => true)
13
+ _, _, exit_code, _ = @base.exec("id -u #{login}", :with_codes => true, :log_stderr => false)
14
14
  return exit_code == 0
15
15
  end
16
16
 
data/spec/spec_helper.rb CHANGED
@@ -20,7 +20,7 @@ RSpec.configure do |config|
20
20
  unless ENV['TRAVIS']
21
21
  # Rollback the server
22
22
  puts "Roll back test server"
23
- # `cd spec/vagrant/ ; BUNDLE_GEMFILE=../../Gemfile bundle exec vagrant sandbox rollback`
23
+ `cd spec/vagrant/ ; BUNDLE_GEMFILE=../../Gemfile bundle exec vagrant sandbox rollback`
24
24
  end
25
25
  end
26
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pawnee
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
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-06-26 00:00:00.000000000 Z
12
+ date: 2012-07-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -293,7 +293,6 @@ files:
293
293
  - spec/base_spec.rb
294
294
  - spec/modified_spec.rb
295
295
  - spec/spec_helper.rb
296
- - spec/vagrant/.vagrant
297
296
  - spec/vagrant/Vagrantfile
298
297
  - spec/vagrant/vagrant.rb
299
298
  homepage: ''
@@ -310,7 +309,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
310
309
  version: '0'
311
310
  segments:
312
311
  - 0
313
- hash: 4043066546838291018
312
+ hash: 4195585264786359942
314
313
  required_rubygems_version: !ruby/object:Gem::Requirement
315
314
  none: false
316
315
  requirements:
@@ -319,7 +318,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
319
318
  version: '0'
320
319
  segments:
321
320
  - 0
322
- hash: 4043066546838291018
321
+ hash: 4195585264786359942
323
322
  requirements: []
324
323
  rubyforge_project:
325
324
  rubygems_version: 1.8.22
@@ -335,6 +334,5 @@ test_files:
335
334
  - spec/base_spec.rb
336
335
  - spec/modified_spec.rb
337
336
  - spec/spec_helper.rb
338
- - spec/vagrant/.vagrant
339
337
  - spec/vagrant/Vagrantfile
340
338
  - spec/vagrant/vagrant.rb
@@ -1 +0,0 @@
1
- {"active":{"default":"c90bb8aa-bba5-4bdf-98e8-5097a58829c8"}}