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 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
- puts "toss"
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
@@ -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
- module Toss
4
- # Your code goes here...
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
@@ -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
@@ -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
+
@@ -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
@@ -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
@@ -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
@@ -1,3 +1,4 @@
1
1
  module Toss
2
- VERSION = "0.0.4"
2
+ NAME = "Toss"
3
+ VERSION = "0.0.5"
3
4
  end
@@ -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 = "https://github.com/tsantef/toss"
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
- # specify any dependencies here; for example:
24
- # s.add_development_dependency "rspec"
25
- # s.add_runtime_dependency "rest-client"
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: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
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-02-04 00:00:00 Z
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