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.
- checksums.yaml +15 -0
- data/CREDITS +32 -0
- data/LICENSE +20 -0
- data/README.md +21 -0
- data/bin/pot-console +9 -0
- data/lib/pot.rb +17 -0
- data/lib/pot/actor.rb +34 -0
- data/lib/pot/bundle.rb +66 -0
- data/lib/pot/config.rb +33 -0
- data/lib/pot/console.rb +7 -0
- data/lib/pot/deployment.rb +91 -0
- data/lib/pot/dsl.rb +39 -0
- data/lib/pot/installer.rb +131 -0
- data/lib/pot/installers/apt.rb +52 -0
- data/lib/pot/installers/binary.rb +46 -0
- data/lib/pot/installers/brew.rb +34 -0
- data/lib/pot/installers/deb.rb +41 -0
- data/lib/pot/installers/gem.rb +64 -0
- data/lib/pot/installers/group.rb +15 -0
- data/lib/pot/installers/npm.rb +19 -0
- data/lib/pot/installers/push_text.rb +49 -0
- data/lib/pot/installers/rake.rb +37 -0
- data/lib/pot/installers/replace_text.rb +45 -0
- data/lib/pot/installers/runner.rb +20 -0
- data/lib/pot/installers/source.rb +202 -0
- data/lib/pot/installers/transfer.rb +184 -0
- data/lib/pot/installers/user.rb +15 -0
- data/lib/pot/instance.rb +21 -0
- data/lib/pot/logger.rb +41 -0
- data/lib/pot/package.rb +352 -0
- data/lib/pot/policy.rb +74 -0
- data/lib/pot/role.rb +16 -0
- data/lib/pot/template.rb +20 -0
- data/lib/pot/transports/local.rb +31 -0
- data/lib/pot/transports/ssh.rb +81 -0
- data/lib/pot/verifiers/apt.rb +21 -0
- data/lib/pot/verifiers/brew.rb +21 -0
- data/lib/pot/verifiers/directory.rb +16 -0
- data/lib/pot/verifiers/executable.rb +53 -0
- data/lib/pot/verifiers/file.rb +34 -0
- data/lib/pot/verifiers/process.rb +21 -0
- data/lib/pot/verifiers/ruby.rb +25 -0
- data/lib/pot/verifiers/symlink.rb +30 -0
- data/lib/pot/verifiers/users_groups.rb +33 -0
- data/lib/pot/verify.rb +112 -0
- data/lib/pot/version.rb +13 -0
- 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
|
data/lib/pot/template.rb
ADDED
@@ -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
|