pawnee 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +22 -0
  6. data/README.md +158 -0
  7. data/Rakefile +11 -0
  8. data/bin/pawnee +10 -0
  9. data/docs/FAQ.md +33 -0
  10. data/docs/GUIDE.md +102 -0
  11. data/docs/TODO.md +10 -0
  12. data/lib/pawnee/pawnee/actions/base_model.rb +35 -0
  13. data/lib/pawnee/pawnee/actions/compile.rb +140 -0
  14. data/lib/pawnee/pawnee/actions/package.rb +111 -0
  15. data/lib/pawnee/pawnee/actions/user.rb +116 -0
  16. data/lib/pawnee/pawnee/actions.rb +29 -0
  17. data/lib/pawnee/pawnee/base.rb +265 -0
  18. data/lib/pawnee/pawnee/cli.rb +72 -0
  19. data/lib/pawnee/pawnee/invocation.rb +13 -0
  20. data/lib/pawnee/pawnee/parser/options.rb +55 -0
  21. data/lib/pawnee/pawnee/railtie.rb +23 -0
  22. data/lib/pawnee/pawnee/roles.rb +79 -0
  23. data/lib/pawnee/pawnee/setup.rb +23 -0
  24. data/lib/pawnee/pawnee/templates/newgem/Gemfile.tt +4 -0
  25. data/lib/pawnee/pawnee/templates/newgem/LICENSE.tt +22 -0
  26. data/lib/pawnee/pawnee/templates/newgem/README.md.tt +29 -0
  27. data/lib/pawnee/pawnee/templates/newgem/Rakefile.tt +2 -0
  28. data/lib/pawnee/pawnee/templates/newgem/bin/newgem.tt +3 -0
  29. data/lib/pawnee/pawnee/templates/newgem/gitignore.tt +17 -0
  30. data/lib/pawnee/pawnee/templates/newgem/lib/pawnee/newgem/base.rb.tt +27 -0
  31. data/lib/pawnee/pawnee/templates/newgem/lib/pawnee/newgem/version.rb.tt +7 -0
  32. data/lib/pawnee/pawnee/templates/newgem/lib/pawnee/newgem.rb.tt +10 -0
  33. data/lib/pawnee/pawnee/templates/newgem/newgem.gemspec.tt +18 -0
  34. data/lib/pawnee/pawnee/version.rb +3 -0
  35. data/lib/pawnee/pawnee.rb +7 -0
  36. data/pawnee.gemspec +31 -0
  37. data/spec/actions/compile_spec.rb +26 -0
  38. data/spec/actions/package_spec.rb +41 -0
  39. data/spec/actions/user_spec.rb +54 -0
  40. data/spec/base_spec.rb +167 -0
  41. data/spec/spec_helper.rb +24 -0
  42. data/spec/vagrant/.vagrant +1 -0
  43. data/spec/vagrant/Vagrantfile +99 -0
  44. data/spec/vagrant/vagrant.rb +30 -0
  45. metadata +328 -0
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ coverage
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format nested
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby-19mode # JRuby in 1.9 mode
6
+ # uncomment this line if your project needs to run something other than `rake`:
7
+ # script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pawnee.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Ryan Stout
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # Pawnee - making server provisioning easier
2
+
3
+ [NOTE: This is still a work in progress and not ready for use yet]
4
+
5
+ ## Why
6
+
7
+ You may wonder why do we need another provisioning system, [here's](https://github.com/ryanstout/pawnee/blob/master/docs/FAQ.md) my answer.
8
+
9
+ ## Goals
10
+
11
+ This system will:
12
+
13
+ 1. **Setup recipes as gems**
14
+ - Gems get prefixed so you can search for them
15
+ - Gems inherit from main gem
16
+ 2. **Gems recipes can be overridden in a rails folder**
17
+ - either by a load path solution or monkeypatching
18
+ - they have a classname that can be predicted from the gem name
19
+ - so anything that overrides that class can monkeypatch or replace parts of it
20
+ 3. **It should use bundler**
21
+ - so you just list the things you need on your server in a certain group (that only gets run on when installing)
22
+ - you could use :path => '...'
23
+ - the gems don't install the things in a bundle, just provide a task you can run to set them up
24
+ 4. **It should run gems locally or remote**
25
+ - based on the --servers option passed in
26
+ 5. **It should integrate with capistrano**
27
+ - we need a place to store server info....
28
+ - redis somewhere?
29
+ - Serverfile/Serverfile.lock
30
+ - the servers and roles should be maintained by this system
31
+ - but deploying should be a separate thing (not like rubber)
32
+ 6. **It should use thor for template stuff**
33
+ - and just overwrite it so files get copied to remote destinations if needed
34
+
35
+
36
+
37
+ ## Installation
38
+
39
+ Add any pawnee gem's you want to use to bundler:
40
+
41
+ gem 'pawnee-nginx'
42
+
43
+ You can see currently available recipe gems [here](https://rubygems.org/search?utf8=%E2%9C%93&query=pawnee)
44
+
45
+ [Or you can create your own recipy gems](https://github.com/ryanstout/pawnee/blob/master/docs/GUIDE.md)
46
+
47
+ ## Usage
48
+
49
+ ### Config File
50
+
51
+ Setup a config/pawnee.yml file. In this file you can specify any options you want to pass along with the servers:
52
+
53
+ Here's an example config with server's:
54
+
55
+ servers:
56
+ - domain: server1.com
57
+ roles: [nginx, unicorn]
58
+ - domain: server2.com
59
+ roles: [unicorn]
60
+
61
+ Then run:
62
+
63
+ bundle exec pawnee setup
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create new Pull Request
72
+
73
+
74
+
75
+
76
+ ## Pawnee Provides Helpers For:
77
+
78
+ - files
79
+ - directories
80
+ - packages
81
+ - service calls (via invoking methods in other recipes)
82
+ - compile (untar/make/make install)
83
+ - users
84
+ - cron? [not complete] (maybe leverage whenever gem)
85
+ - env [not complete] (manage adding things to .base_profile somehow - maybe leverage insert_into_file stuff?)
86
+
87
+ ## Standard Tasks
88
+
89
+ Pawnee defined some convention for task names so all gem's can provide standard ways to call things
90
+
91
+ - #setup
92
+ - #teardown
93
+ - #restart
94
+ - #stop
95
+ - #start
96
+
97
+
98
+ ## Options
99
+
100
+ In a task, recipes can access and change the self.options hash. These values will be
101
+ passed from one task to another. Gem tasks will be executed in the order they are defined
102
+ in bundler (at the moment, this may change)
103
+
104
+ ## Configuration
105
+
106
+ standard config options:
107
+ - servers
108
+ - git_repo_url
109
+ - web_root
110
+ - aws...
111
+ - s3...
112
+
113
+ ## exposed by unicorn for example
114
+ app_server_locations ['localhost:3000', 'localhost:3001'] - gets picked up on by nginx maybe?
115
+
116
+
117
+
118
+ ### ThorSsh Extensions to Thor
119
+ We make a few modifications to allow it to be used as the base to pawnee
120
+ 1) We make it so all actions can be run either locally or remotely via ssh through the thor-ssh gem
121
+ 2) We make options mutable and use that to allow the passing around of options
122
+
123
+
124
+
125
+
126
+ server connection system
127
+ - fog
128
+ - looks at fog api and tags to figure out what servers are running and their roles
129
+
130
+ core gem
131
+ - provides dsl features for setting stuff up
132
+ - packages ['...', '...'] # uses apt
133
+ - file copy in with erb
134
+ - all DSL commands can be run either locally or remote
135
+ - so file copy uploads if needed
136
+ - provides setup for defaults
137
+ - ec2/s3 credentials
138
+ - deploy_path(/mnt/[name])
139
+ - provides generator for building new gems
140
+
141
+ deployment:
142
+ - include in deploy.rb to setup servers and roles
143
+ - standard cap deploy
144
+
145
+
146
+
147
+
148
+
149
+ ### Roles
150
+ - gems define a roles they provide
151
+ - [logically, this can only be one role right?]
152
+ - multiple gems can provide the same role, but one gem can not provide multiple
153
+ - you say which roles you want to run (as --roles)
154
+ - servers say which roles they provide
155
+
156
+ 1) list of gems that run the listed roles
157
+ 2) run on each server that provides
158
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs.push "lib"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => :test
data/bin/pawnee ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+ require 'pawnee/cli'
5
+
6
+ Bundler.require(:default)
7
+
8
+ Pawnee::CLI.start(ARGV)
9
+
10
+
data/docs/FAQ.md ADDED
@@ -0,0 +1,33 @@
1
+ # Why is this better than Chef/Puppet?
2
+
3
+ Thats a tough question. I try to avoid reinventing the wheel, but it seems like the current wheel has some problems.
4
+
5
+ The main reasons is that Chef and Puppet are both huge solutions to a problem I think could be simplified with a few constraints and better choices. (chef is over 110k lines of ruby, it doesn't feel like this is a 110k lines of ruby problem)
6
+
7
+ Also, both seem to have business models that actually harm the product. For example with Chef, they charge you for a product to help set everything up. That means that they have incentives to make it difficult to setup.
8
+
9
+ Here's a few reason why Pawnee is better:
10
+
11
+ 1) Simpler, well written, documented code
12
+ 2) Leverages existing technology people know (rubygems, thor)
13
+ 3) Code reuse is built in (all recipes are gems, any part of the gem can be overridden without changing the gem - like rails engines)
14
+ 4) No need to bootstrap ruby - All actions can be run either locally (with a local ruby) or from a remote server over ssh
15
+ 5) Clean logging (yea, this is important)
16
+ 6) Tests - everything is tested using Vagrant
17
+
18
+
19
+ Also, just for good measure, here's a few reasons why its worse:
20
+
21
+ 1) Only supports ubuntu (currently - the plan is to get the kinks worked out on ubuntu first)
22
+ 2) Fewer recipes (again, currently)
23
+
24
+
25
+ ## Problems with Chef/Puppet
26
+
27
+ [Note: this is my opinion]
28
+
29
+ 1. Little to no testing of recipes (at least for Chef)
30
+ 2. Complicated recipe upgrading/code reuse patterns
31
+ 3. You need to run the code locally (should have option to run over ssh)
32
+
33
+ [chef's providers as an example: http://wiki.opscode.com/display/chef/Resources]
data/docs/GUIDE.md ADDED
@@ -0,0 +1,102 @@
1
+ # Recipe Gem Creation Guide
2
+
3
+ ## Overview
4
+
5
+ Pawnee tries to make it very easy to create recipes for setting up things on remote or local servers. Pawnee provides a gem generator to get started, simply run:
6
+
7
+ pawnee gem [gemname]
8
+
9
+ This will generate files for a gem in the current directory. The word Recipe is used to refer to these pawnee gems (since it is a standard term in the provisioning world.)
10
+
11
+ ## base.rb
12
+
13
+ The main code for your gem will go in lib/pawnee/[gemname]/base.rb This file is a recipe class that inherits from Pawnee::Base.
14
+
15
+ Pawnee uses the [thor gem](https://github.com/wycats/thor) to provide things like tasks and actions. Pawnee::Base extends thor and adds in things to do the following:
16
+
17
+ 1. allow actions to run remotely over ssh or locally.
18
+ 2. allow tasks to run multiple times (once per server).
19
+ 3. allow defaults in a config file for options
20
+ 4. allow the options hash to be changed and passed between tasks
21
+
22
+ By adding these to thor, Pawnee::Base provides a great starting point for creating recipes. It also tracks any class that inherits from it and adds it to a list of recipes so you can run:
23
+
24
+ bundle exec pawnee setup
25
+
26
+ Running the above will automatically call setup on all pawnee recipes in the current Gemfile
27
+
28
+ ## Tasks
29
+
30
+ Recipes have two default tasks, #setup and #teardown. For most situations, think of #setup as "install" and #teardown as "uninstall". To run setup now, in your gem directory you can do:
31
+
32
+ bundle
33
+ bundle exec pawnee [gemname] setup
34
+
35
+ Also, you could place the gem in another projects Gemfile (using the :path option for now) and then run something like:
36
+
37
+ bundle exec pawnee setup
38
+
39
+ Again, this will invoke the setup task on all pawnee gems in the Gemfile.
40
+
41
+ Tasks can call other tasks using the #invoke method (see thor)
42
+
43
+ ## Servers Option
44
+
45
+ We'll come back to options in a minute, but quickly we have to talk about one special option, 'servers'. 'servers' should be setup as a default option on your setup and teardown tasks.
46
+
47
+ Servers can be a few things:
48
+
49
+ 1. an array of domains (with an assumed default user of ubuntu)
50
+ 2. an array of hashs like the following:
51
+ [{'domain' => 'something.com', 'roles' => ['your gemname']}, ...]
52
+ 3. a mix of either
53
+
54
+ These options can either be passed in on the command line like so:
55
+
56
+ bundle exec pawnee yourgemname setup --servers something.com
57
+
58
+ Or they can be setup in the [config.yml file](https://github.com/ryanstout/pawnee#config-file).
59
+
60
+ ## Actions
61
+
62
+ ### Thor Actions
63
+
64
+ Since Pawnee is build on thor, we can use any of the [thor actions](https://github.com/wycats/thor/wiki/Actions) inside our class. Pawnee includes a gem called thor-ssh (which was written for pawnee) that extends thor and allows a .destination_connection to be set on any thor class. .destination_connection is automatically setup for any task if the 'servers' option is set (again, either from the command line or in the config.yml file) When destination_connection is set, any of the actions will use the local machine for the source, and the destination machine for the destination.
65
+
66
+ So running something like the following:
67
+
68
+ copy_file('templates/test.txt', '/home/ubuntu/test.txt')
69
+
70
+ Would copy the file from templates/test.txt (in the gem) to /home/ubuntu/test.txt on the remote server (or servers if multiple servers were passed in)
71
+
72
+ **note**: in the current system, if multiple servers are passed in, the task is simply run once for each server.
73
+
74
+ thor-ssh also provides two methods for running any command:
75
+
76
+ #run and #exec
77
+
78
+ #run will log the command being run, #exec will not
79
+
80
+ ### Pawnee Actions
81
+
82
+ Pawnee also provides its own set of actions for common server tasks.
83
+
84
+ install_package, remove_package, compile, create_user, delete_user
85
+
86
+ ## Options
87
+
88
+ Pawnee also uses thor's option system. This means your tasks should use thor's #desc and #method_option. ([Read more here](https://github.com/wycats/thor/wiki/Getting-Started) and [here](https://github.com/wycats/thor/wiki/Method-Options)) This allows for an easily defined set of options for any task. This also allows anyone to see what options the tasks take:
89
+
90
+ bundle exec pawnee [yourgemname] help setup
91
+
92
+ ^ would return a list of the options along with descriptions
93
+
94
+ By default pawnee makes it so any method options are scoped to the current gem. However pawnee makes it easy for gems to share options. Any gem can change the self.options hash and it will be passed (in its changed form) to the next task. This means you could easily have one gem provide things like path to another gem. (Just make sure the gem providing the paths is before the 2nd gem in the Gemfile). When sharing options, some options make since to not be scoped to the gem. In this case, you can run the #method_options inside of a global_options do block.
95
+
96
+ global_options do
97
+ method_option :deploy_path, :required => true
98
+ end
99
+
100
+ This makes it easy to have the same option be used between gems.
101
+
102
+
data/docs/TODO.md ADDED
@@ -0,0 +1,10 @@
1
+ TODO: Provide testing stubs for gems
2
+ TODO: Add a as_user('user') do .. end option
3
+ - needs to look to options for how to get to root
4
+ - have it run all commands as that user (for sftp actions we'll set the own after)
5
+ - we'll need to change exec and run to work from within a shell session
6
+ - maybe have an option to run from within a shell, or we could get them into the right place every time
7
+ - maybe we should add a system to "get you to root, then get you to another user"
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
+ TODO: Test to make sure arguments work directly as well (they probably don't right now)
@@ -0,0 +1,35 @@
1
+ require 'active_model'
2
+
3
+ module Pawnee
4
+ module Actions
5
+
6
+ class BaseModel
7
+ include ActiveModel::Dirty
8
+ attr_accessor :new_record
9
+
10
+ def new_record?
11
+ !!@new_record
12
+ end
13
+
14
+ def update_attributes(attributes)
15
+ attributes.each_pair do |key,value|
16
+ self.send(:"#{key}=", value) if self.respond_to?(:"#{key}=")
17
+ end
18
+ end
19
+
20
+ def self.change_attr_accessor(method_names)
21
+ [method_names].flatten.each do |method_name|
22
+ self.send(:define_method, :"#{method_name}") do
23
+ return instance_variable_get("@#{method_name}")
24
+ end
25
+
26
+ self.send(:define_method, :"#{method_name}=") do |val|
27
+ self.send(:"#{method_name}_will_change!") unless val == instance_variable_get("@#{method_name}")
28
+ instance_variable_set("@#{method_name}", val)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,140 @@
1
+ module Pawnee
2
+ module Actions
3
+
4
+ # Takes a tar.gz or zip file and unzips it, and runs the
5
+ # standard ./configure, make, sudo make install
6
+ #
7
+ # It attempts to raise an exception at any compilation
8
+ # failure.
9
+ #
10
+ # compile 'http://nginx.org/download/nginx-1.2.0.tar.gz'
11
+ #
12
+ # === Parameters
13
+ # url<String>:: The url to download
14
+ # temp_dir<String>:: Where the compilation should take place
15
+ # options<Hash>:: Hash of options, see below
16
+ #
17
+ # === Options
18
+ # #compile requires that you either specify a :bin_file option
19
+ # that the method can check for on the remote system, or that
20
+ # you pass a block that returns true if the app has already been
21
+ # installed
22
+ #
23
+ # :bin_file - the name of an executable that the method can check for in the path
24
+ # :configure - a string of options to pass to the ./configure command.
25
+ #
26
+ # === Block
27
+ # You can also pass a block that if it returns true, it will not
28
+ # recompile. So the general idea is return true if the exe is already
29
+ # installed.
30
+ def compile(url, temp_dir, options={})
31
+ # TODO: Add invoke/revoke support using action(...), maybe
32
+ # make things run via Thor::Group
33
+
34
+ installed = false
35
+ if options[:bin_file]
36
+ # Check if the bin file is installed
37
+ installed = exec(options[:bin_file]).strip != ''
38
+ else
39
+ raise "You must pass :bin_file or a block to compile" unless block_given?
40
+ installed = yield()
41
+ end
42
+
43
+ if installed
44
+ say_status :already_compiled, url, :blue
45
+ return true
46
+ else
47
+ # Compile and install
48
+ Compile.new(self, url, temp_dir, options)
49
+ end
50
+ end
51
+
52
+ class Compile
53
+ attr_accessor :base
54
+ attr_accessor :options
55
+
56
+ # Sets up a compile object
57
+ #
58
+ # === Parameters
59
+ # base<Thor>:: The main class
60
+ # url<String>:: The url to download from
61
+ # temp_dir<String>:: A path to a temporary directory where the compilation
62
+ # can take place
63
+ def initialize(base, url, temp_dir, options)
64
+ @base = base
65
+ @temp_dir = temp_dir
66
+ self.options = options
67
+ @file = File.basename(url)
68
+ @zip_file = @file[/[.]zip$/]
69
+ @file_path = File.join(temp_dir, @file)
70
+
71
+ # TODO: GET THE FIRST DIRECTORY INSTEAD
72
+ dir_name = @file.gsub(/[.]tar[.]gz$/, '').gsub(/[.]zip$/, '')
73
+ @extracted_path = File.join(temp_dir, dir_name)
74
+
75
+ @base.say_status 'download and compile', url
76
+
77
+ download(url)
78
+ extract
79
+ configure
80
+ make
81
+ make_install
82
+ end
83
+
84
+ # Uses get to download the file and place it in the temp path
85
+ def download(url)
86
+ base.get(url, @file_path)
87
+ end
88
+
89
+ # Extract the compressed file
90
+ def extract
91
+ base.say_status 'extract', @file
92
+ if @zip_file
93
+ base.exec("cd #{@temp_dir} ; unzip #{@file}")
94
+ else
95
+ base.exec("cd #{@temp_dir} ; tar xvfpz #{@file}")
96
+ end
97
+
98
+ # Remove the file
99
+ base.destination_files.rm_rf(@file_path)
100
+ end
101
+
102
+ # Takes a command and an action_name. It says its running the action_name, then
103
+ # runs the command and if there is non-zero exit status, it prints out an error,
104
+ # stdout, and stderr, then raises an exception
105
+ #
106
+ # === Parameters
107
+ # command<String>:: The command to be run
108
+ # action_name<String>:: An action name (used to explain what is happening)
109
+ def run_with_failure_handler(command, action_name)
110
+ base.say_status action_name.downcase, ''
111
+ stdout, stderr, exit_code, exit_status = base.exec(command, true)
112
+
113
+ if exit_code != 0
114
+ base.say_status :error, "Unable to #{action_name}, see output below", :red
115
+ puts stdout
116
+ puts stderr
117
+
118
+ raise "Unable to configure #{action_name}"
119
+ end
120
+ end
121
+
122
+ # Runs ./configure on the files
123
+ def configure
124
+ run_with_failure_handler("cd #{@extracted_path} ; ./configure #{options[:configure] || ''}", 'configure')
125
+ end
126
+
127
+ # Runs make
128
+ def make
129
+ run_with_failure_handler("cd #{@extracted_path} ; make", 'make')
130
+ end
131
+
132
+ # Runs sudo make install
133
+ def make_install
134
+ run_with_failure_handler("cd #{@extracted_path} ; sudo make install", 'make install')
135
+ end
136
+
137
+ end
138
+ end
139
+
140
+ end
@@ -0,0 +1,111 @@
1
+ module Pawnee
2
+ module Actions
3
+
4
+ # Returns true if the package is installed. If a version is
5
+ # listed, it will make sure it matches the version.
6
+ #
7
+ # package_installed? 'memcached'
8
+ # -> true
9
+ #
10
+ # package_installed? 'memcached', '1.4.7-0.1ubuntu1'
11
+ # -> false
12
+ #
13
+ # === Parameters
14
+ # package_name<String>:: The name of package
15
+ def package_installed?(package_name, version=nil)
16
+ installed_version = installed_package_version(package_name)
17
+ if version
18
+ return (installed_version == version)
19
+ else
20
+ return !!installed_version
21
+ end
22
+ end
23
+
24
+ # Returns the version of a package that is installed
25
+ #
26
+ # installed_package_version 'memcached'
27
+ # -> 1.4.7-0.1ubuntu1
28
+ #
29
+ # === Parameters
30
+ # package_name<String>:: The name of package
31
+ def installed_package_version(package_name)
32
+ packages = nil
33
+ as_root do
34
+ packages = exec("dpkg -l")
35
+ end
36
+
37
+ packages.split(/\n/).grep(/^ii /).each do |package|
38
+ _, name, version = package.split(/\s+/)
39
+
40
+ if name == package_name
41
+ return version
42
+ end
43
+ end
44
+
45
+ return nil
46
+ end
47
+
48
+ # Installs a package using the operating system's
49
+ # package management system (currentl apt-get only)
50
+ #
51
+ # install_package 'mysql-server5'
52
+ # install_package 'gcc', '4.5'
53
+ #
54
+ # === Parameters
55
+ # package_name<String>:: The name of package to be installed
56
+ # version<String>:: The version of the package
57
+ def install_package(package_name, version=nil)
58
+ if package_installed?(package_name)
59
+ say_status "package already installed", package_name
60
+ else
61
+ package_name = "#{package_name}=#{version}" if version
62
+ as_root do
63
+ exec("apt-get -y install #{package_name}")
64
+ end
65
+ say_status "installed package", package_name.gsub('=', ' ')
66
+ end
67
+ end
68
+
69
+ # Installs a set of packages, use install_package if you
70
+ # wish to specify a version
71
+ #
72
+ # install_packages 'build-essential', 'zlib1g-dev'
73
+ #
74
+ # === Parameters
75
+ # packages<Array>:: An array of strings of package names
76
+ def install_packages(*package_names)
77
+ package_names.flatten.each do |package_name|
78
+ install_package(package_name)
79
+ end
80
+ end
81
+
82
+ # Removes package_name
83
+ #
84
+ # === Parameters
85
+ # package_name<String>:: The name of package
86
+ def remove_package(package_name)
87
+ if package_installed?(package_name)
88
+ say_status "removed package", package_name
89
+ as_root do
90
+ exec("apt-get -y remove #{package_name}")
91
+ end
92
+ else
93
+ say_status "package not removed", package_name
94
+ end
95
+ end
96
+
97
+ # Removes a set of packages, use remove_package if you
98
+ # wish to specify a version
99
+ #
100
+ # remove_packages 'build-essential', 'zlib1g-dev'
101
+ #
102
+ # === Parameters
103
+ # packages<Array>:: An array of strings of package names
104
+ def remove_packages(*package_names)
105
+ package_names.flatten.each do |package_name|
106
+ remove_package(package_name)
107
+ end
108
+ end
109
+
110
+ end
111
+ end