toss 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/toss +50 -1
- data/lib/toss.rb +34 -2
- data/lib/toss/color.rb +86 -0
- data/lib/toss/meta.rb +34 -0
- data/lib/toss/options.rb +50 -0
- data/lib/toss/server.rb +78 -0
- data/lib/toss/ssh.rb +65 -0
- data/lib/toss/version.rb +2 -1
- data/toss.gemspec +5 -4
- metadata +56 -6
data/bin/toss
CHANGED
@@ -1,3 +1,52 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
2
4
|
|
3
|
-
|
5
|
+
require 'rubygems'
|
6
|
+
require 'toss'
|
7
|
+
|
8
|
+
puts "#{Toss::NAME} #{Toss::VERSION}"
|
9
|
+
|
10
|
+
$options = Toss::Options.load
|
11
|
+
|
12
|
+
# set environment
|
13
|
+
set :env, $options[:environment]
|
14
|
+
|
15
|
+
# load preset
|
16
|
+
$options[:pre_set].each do |name, value|
|
17
|
+
set name, value
|
18
|
+
end
|
19
|
+
|
20
|
+
# load recipe
|
21
|
+
if $options[:recipes].count > 0
|
22
|
+
$options[:recipes].each do |file|
|
23
|
+
require file
|
24
|
+
end
|
25
|
+
else
|
26
|
+
require "#{Dir.pwd}/Tossfile.rb"
|
27
|
+
end
|
28
|
+
|
29
|
+
# load set
|
30
|
+
$options[:set].each do |name, value|
|
31
|
+
set name, value
|
32
|
+
end
|
33
|
+
|
34
|
+
if ARGV.count.to_i > 0
|
35
|
+
@steps = []
|
36
|
+
ARGV.each do |func|
|
37
|
+
step func
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# run steps
|
42
|
+
steps && steps.each_with_index do |toss_step, index|
|
43
|
+
@step_index = index
|
44
|
+
puts "== " + cyan(cap_words(toss_step[0].to_s.gsub("_", " "))) + " =="
|
45
|
+
if method(toss_step[0]).call == false
|
46
|
+
rollback_steps
|
47
|
+
exit(1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# close any open ssh sessions
|
52
|
+
close_sessions
|
data/lib/toss.rb
CHANGED
@@ -1,5 +1,37 @@
|
|
1
|
+
require "toss/options"
|
1
2
|
require "toss/version"
|
3
|
+
require "toss/meta"
|
4
|
+
require "toss/ssh"
|
5
|
+
require "toss/color"
|
6
|
+
require "toss/server"
|
2
7
|
|
3
|
-
|
4
|
-
|
8
|
+
include Toss::Meta
|
9
|
+
include Toss::Ssh
|
10
|
+
|
11
|
+
@step_index ||= 0
|
12
|
+
|
13
|
+
def steps
|
14
|
+
@steps ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def step(step_name, options={})
|
18
|
+
steps << [step_name, options]
|
5
19
|
end
|
20
|
+
|
21
|
+
def rollback_steps
|
22
|
+
@step_index.downto(0) do |index|
|
23
|
+
m = method(steps[index][0])
|
24
|
+
if m.arity == -1
|
25
|
+
puts "== " + red("Rolling Back") + " " + bwhite(cap_words(steps[index][0].to_s.gsub("_", " "))) + " =="
|
26
|
+
|
27
|
+
if m.call(true) == false
|
28
|
+
puts "here"
|
29
|
+
exit(1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def cap_words(str)
|
36
|
+
str.split(" ").each{|word| word.capitalize!}.join(" ")
|
37
|
+
end
|
data/lib/toss/color.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
def colorize(str, beginColor, endColor = 0)
|
2
|
+
return str unless STDOUT.isatty
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/
|
6
|
+
rescue LoadError
|
7
|
+
raise 'You must gem install win32console to use colorize on Windows'
|
8
|
+
end
|
9
|
+
|
10
|
+
"\e[#{beginColor}m#{str}\e[#{endColor}m"
|
11
|
+
end
|
12
|
+
|
13
|
+
#30 Black
|
14
|
+
def black(str)
|
15
|
+
colorize(str, "30")
|
16
|
+
end
|
17
|
+
|
18
|
+
#31 Red
|
19
|
+
def red(str)
|
20
|
+
colorize(str, "31")
|
21
|
+
end
|
22
|
+
|
23
|
+
#31 Bright Red
|
24
|
+
def bred(str)
|
25
|
+
colorize(str, "1;31")
|
26
|
+
end
|
27
|
+
|
28
|
+
#32 Green
|
29
|
+
def green(str)
|
30
|
+
colorize(str, "32")
|
31
|
+
end
|
32
|
+
|
33
|
+
#32 Bright Green
|
34
|
+
def bgreen(str)
|
35
|
+
colorize(str, "1;32")
|
36
|
+
end
|
37
|
+
|
38
|
+
#33 Yellow
|
39
|
+
def yellow(str)
|
40
|
+
colorize(str, "33")
|
41
|
+
end
|
42
|
+
|
43
|
+
#33 Bright Yellow
|
44
|
+
def byellow(str)
|
45
|
+
colorize(str, "1;33")
|
46
|
+
end
|
47
|
+
|
48
|
+
#34 Blue
|
49
|
+
def blue(str)
|
50
|
+
colorize(str, "34")
|
51
|
+
end
|
52
|
+
|
53
|
+
#34 Bright Blue
|
54
|
+
def bblue(str)
|
55
|
+
colorize(str, "1;34")
|
56
|
+
end
|
57
|
+
|
58
|
+
#35 Magenta
|
59
|
+
def magenta(str)
|
60
|
+
colorize(str, "35")
|
61
|
+
end
|
62
|
+
|
63
|
+
#35 Bright Magenta
|
64
|
+
def bmagenta(str)
|
65
|
+
colorize(str, "1;35")
|
66
|
+
end
|
67
|
+
|
68
|
+
#36 Cyan
|
69
|
+
def cyan(str)
|
70
|
+
colorize(str, "36")
|
71
|
+
end
|
72
|
+
|
73
|
+
#36 Bright Cyan
|
74
|
+
def bcyan(str)
|
75
|
+
colorize(str, "1;36")
|
76
|
+
end
|
77
|
+
|
78
|
+
#37 White
|
79
|
+
def white(str)
|
80
|
+
colorize(str, "37")
|
81
|
+
end
|
82
|
+
|
83
|
+
#37 Bright White
|
84
|
+
def bwhite(str)
|
85
|
+
colorize(str, "1;37")
|
86
|
+
end
|
data/lib/toss/meta.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Toss
|
2
|
+
module Meta
|
3
|
+
module ClassMethods
|
4
|
+
end
|
5
|
+
|
6
|
+
module InstanceMethods
|
7
|
+
def set(name, value)
|
8
|
+
create_accessor(name)
|
9
|
+
Object.send("#{name}=", value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_method(name, &block)
|
13
|
+
Object.send(:define_method, name, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_accessor(name)
|
17
|
+
create_method("#{name}=") do |value|
|
18
|
+
$globals = $globals || {}
|
19
|
+
$globals[name] = value
|
20
|
+
end
|
21
|
+
create_method(name.to_sym) do
|
22
|
+
$globals = $globals || {}
|
23
|
+
$globals[name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.included(receiver)
|
29
|
+
receiver.extend ClassMethods
|
30
|
+
receiver.send :include, InstanceMethods
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/lib/toss/options.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Toss
|
4
|
+
module Options
|
5
|
+
|
6
|
+
def self.load
|
7
|
+
options = { :recipes => [], :pre_set => {}, :set => {}, :verbose => false }
|
8
|
+
|
9
|
+
optparse = OptionParser.new do |opts|
|
10
|
+
opts.on('-e', '--environment ENVIRONMENT', 'Environment to deploy as') do |environment|
|
11
|
+
options[:environment] = environment
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on('-f', '--file FILE', 'A recipe file to run. Option may be used more than once.') do |file|
|
15
|
+
options[:recipes] << value
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on("-S", "--set-before NAME=VALUE", "Set a variable before the recipes are loaded.") do |name_value|
|
19
|
+
name, value = name_value.split("=", 2)
|
20
|
+
options[:pre_set][name.to_sym] = value
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-s", "--set NAME=VALUE", "Set a variable after the recipes are loaded.") do |name_value|
|
24
|
+
name, value = name_value.split("=", 2)
|
25
|
+
options[:set][name.to_sym] = value
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on('-v', '--verbose', 'Show more information about the process') do
|
29
|
+
options[:verbose] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-h', '--help', 'Display this screen') do
|
33
|
+
puts opts
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
optparse.parse!
|
40
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
41
|
+
puts $!.to_s
|
42
|
+
puts optparse
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
options
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/toss/server.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
def server(properties)
|
2
|
+
return Server.new(properties)
|
3
|
+
end
|
4
|
+
|
5
|
+
def with(obj, &block)
|
6
|
+
obj.instance_eval &block
|
7
|
+
end
|
8
|
+
|
9
|
+
class Server
|
10
|
+
|
11
|
+
attr_accessor :host, :user, :password, :type
|
12
|
+
attr_accessor :working_directory
|
13
|
+
attr_accessor :ssh
|
14
|
+
|
15
|
+
def initialize(properties)
|
16
|
+
@host = properties[:host]
|
17
|
+
@user = properties[:user]
|
18
|
+
@password = properties[:password]
|
19
|
+
@type = properties[:type]
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(command='', &block)
|
23
|
+
if block_given?
|
24
|
+
self.instance_eval &block
|
25
|
+
else
|
26
|
+
result = exec(command, false)
|
27
|
+
return result.first
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def exec(command, quiet=false)
|
32
|
+
ssh = open_sessions[host] || start_session(self)
|
33
|
+
# capture all stderr and stdout output from a remote process
|
34
|
+
|
35
|
+
if $options[:verbose]
|
36
|
+
puts "#{bwhite host} executing: #{command}"
|
37
|
+
end
|
38
|
+
|
39
|
+
if !working_directory.nil? && working_directory != ""
|
40
|
+
command = "cd #{working_directory} && " + command
|
41
|
+
end
|
42
|
+
|
43
|
+
stdout_data, stderr_data, exit_code, exit_signal = ssh_exec! ssh, command
|
44
|
+
|
45
|
+
if !quiet
|
46
|
+
puts "#{bwhite host} said: #{red stderr_data.strip!}" if !stderr_data.nil? && stderr_data != ""
|
47
|
+
puts "#{bwhite host} said: #{stdout_data.strip!}" if !stdout_data.nil? && stdout_data != ""
|
48
|
+
end
|
49
|
+
|
50
|
+
return exit_code, stdout_data, stderr_data, exit_signal
|
51
|
+
end
|
52
|
+
|
53
|
+
def exists?(path)
|
54
|
+
exit_code = run("[ -e #{path} ] && exit 0 || exit 1")
|
55
|
+
return exit_code == 0
|
56
|
+
end
|
57
|
+
|
58
|
+
def cd(path)
|
59
|
+
if $options[:verbose]
|
60
|
+
puts "#{bwhite host} cd to: #{path}"
|
61
|
+
end
|
62
|
+
if block_given?
|
63
|
+
previous_path = @working_directory
|
64
|
+
@working_directory = path
|
65
|
+
yield
|
66
|
+
@working_directory = previous_path
|
67
|
+
else
|
68
|
+
@working_directory = path
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def delete(path)
|
73
|
+
if exists? "#{path}"
|
74
|
+
return run "unlink #{path}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
data/lib/toss/ssh.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require "net/ssh"
|
2
|
+
require "net/scp"
|
3
|
+
|
4
|
+
module Toss
|
5
|
+
module Ssh
|
6
|
+
module ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
def open_sessions
|
11
|
+
@open_sessions ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def start_session(server)
|
15
|
+
puts "Starting ssh session to #{server.host}"
|
16
|
+
open_sessions[server.host] = Net::SSH.start(server.host, server.user, :password => server.password)
|
17
|
+
open_sessions[server.host]
|
18
|
+
end
|
19
|
+
|
20
|
+
def close_sessions
|
21
|
+
open_sessions.each.each do |target_host,session|
|
22
|
+
puts "Closing ssh session for #{target_host}"
|
23
|
+
session.close
|
24
|
+
open_sessions.delete(target_host)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def ssh_exec!(ssh, command)
|
29
|
+
stdout_data = ""
|
30
|
+
stderr_data = ""
|
31
|
+
exit_code = nil
|
32
|
+
exit_signal = nil
|
33
|
+
ssh.open_channel do |channel|
|
34
|
+
channel.exec(command) do |ch, success|
|
35
|
+
unless success
|
36
|
+
abort "FAILED: couldn't execute command (ssh.channel.exec)"
|
37
|
+
end
|
38
|
+
channel.on_data do |ch,data|
|
39
|
+
stdout_data+=data
|
40
|
+
end
|
41
|
+
|
42
|
+
channel.on_extended_data do |ch,type,data|
|
43
|
+
stderr_data+=data
|
44
|
+
end
|
45
|
+
|
46
|
+
channel.on_request("exit-status") do |ch,data|
|
47
|
+
exit_code = data.read_long
|
48
|
+
end
|
49
|
+
|
50
|
+
channel.on_request("exit-signal") do |ch, data|
|
51
|
+
exit_signal = data.read_long
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
ssh.loop
|
56
|
+
[stdout_data, stderr_data, exit_code, exit_signal]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.included(receiver)
|
61
|
+
receiver.extend ClassMethods
|
62
|
+
receiver.send :include, InstanceMethods
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/toss/version.rb
CHANGED
data/toss.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Tim Santeford"]
|
10
10
|
s.email = ["tsantef@gmail.com"]
|
11
|
-
s.homepage =
|
11
|
+
s.homepage = %q{https://github.com/tsantef/toss}
|
12
12
|
s.default_executable = %q{toss}
|
13
13
|
s.summary = %q{toss - Simple deployment}
|
14
14
|
s.description = %q{toss - Simple deployment}
|
@@ -20,7 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
s.add_dependency(%q<net-ssh>, [">= 2.0.14"])
|
24
|
+
s.add_dependency(%q<net-scp>, [">= 1.0.0"])
|
25
|
+
s.add_dependency(%q<highline>, [">= 0"])
|
26
|
+
|
26
27
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tim Santeford
|
@@ -15,9 +15,54 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
19
|
-
dependencies:
|
20
|
-
|
18
|
+
date: 2012-03-06 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: net-ssh
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 19
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 0
|
32
|
+
- 14
|
33
|
+
version: 2.0.14
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: net-scp
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 23
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 0
|
48
|
+
- 0
|
49
|
+
version: 1.0.0
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: highline
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :runtime
|
65
|
+
version_requirements: *id003
|
21
66
|
description: toss - Simple deployment
|
22
67
|
email:
|
23
68
|
- tsantef@gmail.com
|
@@ -34,6 +79,11 @@ files:
|
|
34
79
|
- Rakefile
|
35
80
|
- bin/toss
|
36
81
|
- lib/toss.rb
|
82
|
+
- lib/toss/color.rb
|
83
|
+
- lib/toss/meta.rb
|
84
|
+
- lib/toss/options.rb
|
85
|
+
- lib/toss/server.rb
|
86
|
+
- lib/toss/ssh.rb
|
37
87
|
- lib/toss/version.rb
|
38
88
|
- toss.gemspec
|
39
89
|
homepage: https://github.com/tsantef/toss
|