pot 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|