pot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +15 -0
  2. data/CREDITS +32 -0
  3. data/LICENSE +20 -0
  4. data/README.md +21 -0
  5. data/bin/pot-console +9 -0
  6. data/lib/pot.rb +17 -0
  7. data/lib/pot/actor.rb +34 -0
  8. data/lib/pot/bundle.rb +66 -0
  9. data/lib/pot/config.rb +33 -0
  10. data/lib/pot/console.rb +7 -0
  11. data/lib/pot/deployment.rb +91 -0
  12. data/lib/pot/dsl.rb +39 -0
  13. data/lib/pot/installer.rb +131 -0
  14. data/lib/pot/installers/apt.rb +52 -0
  15. data/lib/pot/installers/binary.rb +46 -0
  16. data/lib/pot/installers/brew.rb +34 -0
  17. data/lib/pot/installers/deb.rb +41 -0
  18. data/lib/pot/installers/gem.rb +64 -0
  19. data/lib/pot/installers/group.rb +15 -0
  20. data/lib/pot/installers/npm.rb +19 -0
  21. data/lib/pot/installers/push_text.rb +49 -0
  22. data/lib/pot/installers/rake.rb +37 -0
  23. data/lib/pot/installers/replace_text.rb +45 -0
  24. data/lib/pot/installers/runner.rb +20 -0
  25. data/lib/pot/installers/source.rb +202 -0
  26. data/lib/pot/installers/transfer.rb +184 -0
  27. data/lib/pot/installers/user.rb +15 -0
  28. data/lib/pot/instance.rb +21 -0
  29. data/lib/pot/logger.rb +41 -0
  30. data/lib/pot/package.rb +352 -0
  31. data/lib/pot/policy.rb +74 -0
  32. data/lib/pot/role.rb +16 -0
  33. data/lib/pot/template.rb +20 -0
  34. data/lib/pot/transports/local.rb +31 -0
  35. data/lib/pot/transports/ssh.rb +81 -0
  36. data/lib/pot/verifiers/apt.rb +21 -0
  37. data/lib/pot/verifiers/brew.rb +21 -0
  38. data/lib/pot/verifiers/directory.rb +16 -0
  39. data/lib/pot/verifiers/executable.rb +53 -0
  40. data/lib/pot/verifiers/file.rb +34 -0
  41. data/lib/pot/verifiers/process.rb +21 -0
  42. data/lib/pot/verifiers/ruby.rb +25 -0
  43. data/lib/pot/verifiers/symlink.rb +30 -0
  44. data/lib/pot/verifiers/users_groups.rb +33 -0
  45. data/lib/pot/verify.rb +112 -0
  46. data/lib/pot/version.rb +13 -0
  47. metadata +118 -0
data/lib/pot/policy.rb ADDED
@@ -0,0 +1,74 @@
1
+ module Pot
2
+ # = Policies
3
+ #
4
+ # A policy defines a set of packages which are required for a certain
5
+ # role (app, database, etc.). All policies defined will be run and all
6
+ # packages required by the policy will be installed. So whereas defining
7
+ # a Pot::Package merely defines it, defining a Pot::Policy
8
+ # actually causes those packages to install.
9
+ #
10
+ # == A Basic Example
11
+ #
12
+ # policy :blog, :roles => :app do
13
+ # require :webserver
14
+ # require :database
15
+ # require :rails
16
+ # end
17
+ #
18
+ # This says that for the blog on the app role, it requires certain
19
+ # packages. The :roles option is <em>exactly</em> the same as a capistrano
20
+ # or vlad role. A role merely defines what server the commands are run
21
+ # on. This way, a single Pot script can provision an entire group
22
+ # of servers.
23
+ #
24
+ # To define a role, put in your actor specific configuration file (recipe or
25
+ # script file):
26
+ #
27
+ # role :app, "208.28.38.44"
28
+ #
29
+ # The capistrano and vlad syntax is the same for that. If you're using a
30
+ # custom actor, you may have to do it differently.
31
+ #
32
+ # == Multiple Policies
33
+ #
34
+ # You may specify as many policies as you'd like. If the packages you're
35
+ # requiring are properly defined with verification blocks, then
36
+ # no software will be installed twice, so you may require a webserver on
37
+ # multiple packages within the same role without having to wait for
38
+ # that package to install repeatedly.
39
+ class Policy #:nodoc:
40
+ REGISTER = {}
41
+
42
+ attr_reader :name,
43
+ :packages,
44
+ :roles
45
+
46
+ def initialize(name, metadata = {}, &block)
47
+ raise 'No name provided' unless name
48
+ raise 'No roles provided' unless metadata[:roles]
49
+
50
+ @name = name.to_sym
51
+ @roles = metadata[:roles]
52
+ @package_tree = Pot::Bundle.new
53
+
54
+ REGISTER[@name] = self
55
+
56
+ self.instance_eval(&block)
57
+ end
58
+
59
+ def requires(package, options = {})
60
+ @package_tree.add(package)
61
+ end
62
+
63
+ def to_s
64
+ name
65
+ end
66
+
67
+ def process(actor)
68
+ @package_tree.list do |package|
69
+ package.process(actor)
70
+ end
71
+ end
72
+
73
+ end
74
+ end
data/lib/pot/role.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Pot
2
+ class Role
3
+ REGISTER = {}
4
+
5
+ attr_reader :name, :instance
6
+
7
+ def initialize(role_name, instance_name)
8
+ @name = role_name
9
+ instance = Pot::Instance::REGISTER[instance_name]
10
+ raise "Undefined instance: #{instance_name}" unless instance
11
+ @instance = instance
12
+
13
+ REGISTER[role_name] = self
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module Pot
2
+ class Template
3
+ def initialize(package, name)
4
+ @package = package
5
+ @name = name
6
+ end
7
+
8
+ def path
9
+ default_file_path = File.join(templates_directory, 'defaults', @package.name.to_s, @name.to_s)
10
+ raise "Can't read template #{default_file_path}" unless File.readable?(default_file_path)
11
+ default_file_path
12
+ end
13
+
14
+ private
15
+
16
+ def templates_directory
17
+ File.join(Dir.pwd, 'templates')
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module Pot
2
+ module Transports
3
+ # = Local Delivery Method
4
+ #
5
+ # This actor implementation performs any given commands on your local system, as
6
+ # opposed to other implementations that generally run commands on a remote system
7
+ # via the network.
8
+ #
9
+ # This is useful if you'd like to use Pot to provision your local machine.
10
+ # To enable this actor, in your Pot script specify the :local delivery mechanism.
11
+ #
12
+ # deployment do
13
+ # delivery :local
14
+ # end
15
+ #
16
+ class Local
17
+
18
+ def execute(command)
19
+ system command
20
+ end
21
+
22
+ def transfer(source, destination, recursive = true)
23
+ if recursive
24
+ flags = "-R "
25
+ end
26
+
27
+ system "cp #{flags}#{source} #{destination}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ require 'net/ssh'
2
+ require 'net/scp'
3
+
4
+ module Pot
5
+ module Transports
6
+ class Ssh
7
+
8
+ attr_reader :instance
9
+
10
+ def initialize(instance) #:nodoc:
11
+ @instance = instance
12
+ end
13
+
14
+ def execute(command)
15
+ execute_on_connection!(command)
16
+ end
17
+
18
+ def transfer(source, destination, recursive = true)
19
+ transfer_on_connection!(source, destination, recursive)
20
+ end
21
+
22
+ private
23
+
24
+ def connection
25
+ return @connection if defined?(@connection)
26
+ options = { }
27
+ # options[:verbose] = Logger::DEBUG if Pot.config.debug?
28
+ # options[:verbose] = Logger::INFO
29
+ options[:password] = instance.password if instance.password
30
+ options[:port] = instance.port if instance.port
31
+ @connection = Net::SSH.start(instance.host, instance.user, options)
32
+ @connection
33
+ end
34
+
35
+ def transfer_on_connection!(source, destination, recursive)
36
+ scp = Net::SCP.new(connection)
37
+ scp.upload! source, destination, :recursive => recursive
38
+ end
39
+
40
+ def execute_on_connection!(command)
41
+ exit_code = nil
42
+ exit_signal = nil
43
+
44
+ connection.open_channel do |channel|
45
+ Pot.logger.debug("executing remote command: #{command}")
46
+ channel.exec(command) do |ch, success|
47
+ unless success
48
+ abort "FAILED: couldn't execute command (connection.channel.exec)"
49
+ Pot.logger.error("couldn't run remote command #{command}")
50
+ end
51
+
52
+ channel.on_data do |ch, data|
53
+ Pot.logger.debug("stdout said-->\n#{data}\n")
54
+ end
55
+
56
+ channel.on_extended_data do |ch, type, data|
57
+ next unless type == 1 # only handle stderr
58
+ Pot.logger.debug("stderr said -->\n#{data}\n")
59
+ end
60
+
61
+ channel.on_request("exit-status") do |ch, data|
62
+ exit_code = data.read_long
63
+ if exit_code == 0
64
+ Pot.logger.debug('success')
65
+ else
66
+ Pot.logger.debug('failed (%d).'%exit_code)
67
+ end
68
+ end
69
+
70
+ channel.on_request("exit-signal") do |ch, data|
71
+ exit_signal = data.read_long
72
+ Pot.logger.debug("#{command} was signaled!: #{data.read_long}")
73
+ end
74
+ end
75
+ end
76
+ connection.loop
77
+ exit_code
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,21 @@
1
+ module Pot
2
+ module Verifiers
3
+ # = Apt package Verifier
4
+ #
5
+ # Contains a verifier to check the existance of an Apt package.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # verify { has_apt 'ntp' }
10
+ #
11
+ module Apt
12
+ Pot::Verify.register(Pot::Verifiers::Apt)
13
+
14
+ # Checks to make sure the apt <tt>package</tt> exists on the remote server.
15
+ def has_apt(package)
16
+ @commands << "dpkg --status #{package} | grep \"ok installed\""
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Pot
2
+ module Verifiers
3
+ # = Brew package Verifier
4
+ #
5
+ # Contains a verifier to check the existance of a Homebrew formula.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # verify { has_brew 'ntp' }
10
+ #
11
+ module Brew
12
+ Pot::Verify.register(Pot::Verifiers::Brew)
13
+
14
+ # Checks to make sure the brew <tt>formula</tt> exists on the remote server.
15
+ def has_brew(package)
16
+ @commands << "brew list | grep #{package}"
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module Pot
2
+ module Verifiers
3
+ # = Directory Verifier
4
+ #
5
+ # Defines a verify which can be used to test the existence of a
6
+ # directory.
7
+ module Directory
8
+ Pot::Verify.register(Pot::Verifiers::Directory)
9
+
10
+ # Tests that the directory <tt>dir</tt> exists.
11
+ def has_directory(dir)
12
+ @commands << "test -d #{dir}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,53 @@
1
+ module Pot
2
+ module Verifiers
3
+ # = Executable Verifier
4
+ #
5
+ # Contains a verifier to check the existance of an executable
6
+ # script on your server.
7
+ #
8
+ # == Example Usage
9
+ #
10
+ # First, absolute path to an executable:
11
+ #
12
+ # verify { has_executable '/usr/special/secret/bin/scipt' }
13
+ #
14
+ # Second, a global executable which would be available anywhere on the
15
+ # command line:
16
+ #
17
+ # verify { has_executable 'grep' }
18
+ module Executable
19
+ Pot::Verify.register(Pot::Verifiers::Executable)
20
+
21
+ # Checks if <tt>path</tt> is an executable script. This verifier is "smart" because
22
+ # if the path contains a forward slash '/' then it assumes you're checking an
23
+ # absolute path to an executable. If no '/' is in the path, it assumes you're
24
+ # checking for a global executable that would be available anywhere on the command line.
25
+ def has_executable(path)
26
+ # Be smart: If the path includes a forward slash, we're checking
27
+ # an absolute path. Otherwise, we're checking a global executable
28
+ if path.include?('/')
29
+ @commands << "test -x #{path}"
30
+ else
31
+ @commands << "which -s #{path}"
32
+ end
33
+ end
34
+
35
+ # Same as has_executable but with checking for e certain version number.
36
+ # Last option is the parameter to append for getting the version (which
37
+ # defaults to "-v").
38
+ def has_executable_with_version(path, version, get_version = '-v')
39
+ if path.include?('/')
40
+ @commands << "[ -x #{path} -a -n \"`#{path} #{get_version} 2>&1 | egrep -e \\\"#{version}\\\"`\" ]"
41
+ else
42
+ @commands << "[ -n \"`echo \\`which #{path}\\``\" -a -n \"`\\`which #{path}\\` #{get_version} 2>&1 | egrep -e \\\"#{version}\\\"`\" ]"
43
+ end
44
+ end
45
+
46
+ # Same as has_executable but checking output of a certain command
47
+ # with grep.
48
+ def has_version_in_grep(cmd, version)
49
+ @commands << "[ -n \"`#{cmd} 2> /dev/null | egrep -e \\\"#{version}\\\"`\" ]"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,34 @@
1
+ module Pot
2
+ module Verifiers
3
+ # = File Verifier
4
+ #
5
+ # Contains a verifier to check the existance of a file.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # verify { has_file '/etc/apache2/apache2.conf' }
10
+ #
11
+ # verify { file_contains '/etc/apache2/apache2.conf', 'mod_gzip'}
12
+ #
13
+ module File
14
+ Pot::Verify.register(Pot::Verifiers::File)
15
+
16
+ # Checks to make sure <tt>path</tt> is a file on the remote server.
17
+ def has_file(path)
18
+ @commands << "test -f #{path}"
19
+ end
20
+
21
+ def file_contains(path, text)
22
+ @commands << "grep '#{text}' #{path}"
23
+ end
24
+ def user_present(username)
25
+ has_user username
26
+ end
27
+ def matches_local(localfile, remotefile, mode=nil)
28
+ raise "Couldn't find local file #{localfile}" unless ::File.exists?(localfile)
29
+ local = `md5 #{localfile}`.split.last
30
+ @commands << %{[ "X$(md5sum #{remotefile}|cut -d\\ -f 1)" = "X#{local}" ]}
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ module Pot
2
+ module Verifiers
3
+ # = Process Verifier
4
+ #
5
+ # Contains a verifier to check that a process is running.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # verify { has_process 'httpd' }
10
+ #
11
+ module Process
12
+ Pot::Verify.register(Pot::Verifiers::Process)
13
+
14
+ # Checks to make sure <tt>process</tt> is a process running
15
+ # on the remote server.
16
+ def has_process(process)
17
+ @commands << "ps -C #{process}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module Pot
2
+ module Verifiers
3
+ # = Ruby Verifiers
4
+ #
5
+ # The verifiers in this module are ruby specific.
6
+ module Ruby
7
+ Pot::Verify.register(Pot::Verifiers::Ruby)
8
+
9
+ # Checks if ruby can require the <tt>files</tt> given. <tt>rubygems</tt>
10
+ # is always included first.
11
+ def ruby_can_load(*files)
12
+ # Always include rubygems first
13
+ files = files.unshift('rubygems').collect { |x| "require '#{x}'" }
14
+
15
+ @commands << "ruby -e \"#{files.join(';')}\""
16
+ end
17
+
18
+ # Checks if a gem exists by calling "gem list" and grepping against it.
19
+ def has_gem(name, version = nil)
20
+ version = version ? "--version '#{version}'" : ''
21
+ @commands << "gem list '#{name}' --installed #{version} > /dev/null"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ module Pot
2
+ module Verifiers
3
+ # = Symlink Verifier
4
+ #
5
+ # Contains a verifier to check the existance of a symbolic link.
6
+ #
7
+ # == Example Usage
8
+ #
9
+ # First, checking for the existence of a symlink:
10
+ #
11
+ # verify { has_symlink '/usr/special/secret/pointer' }
12
+ #
13
+ # Second, checking that the symlink points to a specific place:
14
+ #
15
+ # verify { has_symlink '/usr/special/secret/pointer', '/usr/local/realfile' }
16
+ module Symlink
17
+ Pot::Verify.register(Pot::Verifiers::Symlink)
18
+
19
+ # Checks that <tt>symlink</tt> is a symbolic link. If <tt>file</tt> is
20
+ # given, it checks that <tt>symlink</tt> points to <tt>file</tt>
21
+ def has_symlink(symlink, file = nil)
22
+ if file.nil?
23
+ @commands << "test -L #{symlink}"
24
+ else
25
+ @commands << "test '#{file}' = `readlink #{symlink}`"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end