pot 0.1.0

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