pawnee 0.1.4 → 0.1.5

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
@@ -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"}}