rbfs 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ script: 'rake spec'
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - 2.0.0
data/Rakefile CHANGED
@@ -1 +1,4 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
data/bin/rbfs CHANGED
@@ -2,9 +2,6 @@
2
2
  require 'rbfs'
3
3
  require 'rbfs/command'
4
4
 
5
- config = Rbfs::Command.config
6
- hosts = Rbfs::Hosts.new(config[:hosts])
7
- hosts.each do |host|
8
- puts "#{host} (#{host.ip})" if config[:verbose]
9
- host.sync(config)
10
- end
5
+ command = Rbfs::Command.new
6
+ success = command.sync
7
+ exit 1 unless success
@@ -1,6 +1,4 @@
1
1
  require "rbfs/version"
2
- require "rbfs/hosts"
3
2
 
4
3
  module Rbfs
5
- # Your code goes here...
6
4
  end
@@ -16,12 +16,21 @@ module Rbfs
16
16
  opts.on("-r", "--root ROOT", "Root path to sync") do |v|
17
17
  options[:root] = v
18
18
  end
19
+ opts.on("-s", "--subpath PATH", "Subpath of root to sync") do |v|
20
+ options[:subpath] = v
21
+ end
19
22
  opts.on("-v", "--[no-]verbose", "Print extra debugging info") do |v|
20
23
  options[:verbose] = v
21
24
  end
22
25
  opts.on("-d", "--dry", "Test config settings") do |v|
23
26
  options[:dry] = v
24
27
  end
28
+ opts.on("-t", "--[no-]threaded", "Run all hosts concurrently") do |v|
29
+ options[:threaded] = v
30
+ end
31
+ opts.on("--timeout TIMEOUT", "Set I/O timeout") do |v|
32
+ options[:timeout] = v
33
+ end
25
34
  end.parse!
26
35
 
27
36
  options
@@ -1,12 +1,75 @@
1
1
  require 'rbfs/args'
2
2
  require 'rbfs/config'
3
+ require "rbfs/host_parser"
4
+ require "rbfs/rsync"
5
+ require "rbfs/futures"
6
+ require "rbfs/logger"
3
7
 
4
8
  module Rbfs
5
9
  class Command
6
- def self.config
7
- args = Rbfs::Args.new.parse
8
- options = Rbfs::Config.new(args[:config]).parse
9
- options.merge(args)
10
+ attr_accessor :config
11
+
12
+ def initialize
13
+ @config = parse_config
14
+ logger.critical "No hosts file specified" unless config[:hosts]
15
+ logger.critical "Root path not specified" unless config[:root]
16
+ end
17
+
18
+ def parse_config
19
+ config = {}
20
+ cmdline_args = Rbfs::Args.new.parse
21
+ if cmdline_args[:config]
22
+ config_args = Rbfs::Config.new(cmdline_args[:config]).parse
23
+ config = config_args.merge(cmdline_args)
24
+ else
25
+ config = cmdline_args
26
+ end
27
+ config[:logger] = Logger.new(config)
28
+ config
29
+ end
30
+
31
+ def logger
32
+ @config[:logger]
33
+ end
34
+
35
+ def sync
36
+ success = true
37
+ results = sync_hosts
38
+ results.each do |host, result|
39
+ if result[:exitcode] != 0
40
+ logger.error "#{host}: #{result[:exitcode].to_i}"
41
+ else
42
+ logger.info "#{host}: #{result[:exitcode].to_i}"
43
+ end
44
+ result[:output].split("\n").each do |line|
45
+ if result[:exitcode] != 0
46
+ logger.error " | #{line}"
47
+ else
48
+ logger.info " | #{line}"
49
+ end
50
+ end
51
+ success = false if result[:exitcode] != 0
52
+ end
53
+ success
54
+ end
55
+
56
+ def sync_hosts
57
+ config[:root] = File.join(config[:root], config[:subpath]) if config[:subpath]
58
+ logger.info "Syncing #{config[:root]}..."
59
+ hosts = Rbfs::HostParser.new(File.open(config[:hosts]))
60
+ hosts.collect do |host|
61
+ [host, sync_host(host)]
62
+ end
63
+ end
64
+
65
+ def sync_host(host)
66
+ if config[:threaded]
67
+ Future.new do
68
+ Rsync.new(config, host).sync
69
+ end
70
+ else
71
+ Rsync.new(config, host).sync
72
+ end
10
73
  end
11
74
  end
12
75
  end
@@ -0,0 +1,27 @@
1
+ module Rbfs
2
+ class Future < BasicObject
3
+ def initialize(&callable)
4
+ @thread ||= ::Thread.new { callable.call }
5
+ end
6
+
7
+ def value
8
+ @thread.value
9
+ end
10
+
11
+ def inspect
12
+ if @thread.alive?
13
+ "#<Future running>"
14
+ else
15
+ value.inspect
16
+ end
17
+ end
18
+
19
+ def method_missing(method, *args)
20
+ value.send(method, *args)
21
+ end
22
+
23
+ def respond_to_missing?(method, include_private = false)
24
+ value.respond_to?(method, include_private)
25
+ end
26
+ end
27
+ end
@@ -1,9 +1,5 @@
1
- require 'rbfs/rsync'
2
-
3
1
  module Rbfs
4
2
  class Host
5
- include Rsync
6
-
7
3
  def initialize(raw)
8
4
  @raw = raw
9
5
  end
@@ -19,5 +15,9 @@ module Rbfs
19
15
  def name
20
16
  @raw.split[1]
21
17
  end
18
+
19
+ def alias
20
+ @raw.split[2]
21
+ end
22
22
  end
23
23
  end
@@ -0,0 +1,20 @@
1
+ require "rbfs/host"
2
+
3
+ module Rbfs
4
+ class HostParser
5
+ include Enumerable
6
+
7
+ def initialize(file)
8
+ @file = file
9
+ end
10
+
11
+ def each(&block)
12
+ lines = @file.read.split("\n")
13
+ lines.reject! {|line| line.strip.start_with? "#" }
14
+ lines.reject! {|line| line.strip.empty? }
15
+ lines.each do |line|
16
+ yield(Host.new(line))
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ module Rbfs
2
+ class Logger
3
+ attr_reader :config
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def info(msg)
10
+ puts "\033[0;32m#{msg}" if config[:verbose]
11
+ end
12
+
13
+ def error(msg)
14
+ puts "\033[0;31m#{msg}"
15
+ end
16
+
17
+ def critical(msg)
18
+ puts "\033[0;31m#{msg}"
19
+ exit 1
20
+ end
21
+
22
+ def notice(msg)
23
+ puts "\033[0;33m#{msg}"
24
+ end
25
+ end
26
+ end
@@ -1,10 +1,32 @@
1
1
  module Rbfs
2
- module Rsync
3
- def sync(config)
4
- command = "rsync -ave ssh --delete #{config[:root]} #{ip}:#{config[:root]}"
5
- command += " -n" if config[:dry]
6
- result = `#{command}`
7
- puts result if config[:verbose]
2
+ class Rsync
3
+ def initialize(config = {}, host = nil)
4
+ @config = config
5
+ @host = host
6
+ end
7
+
8
+ def sync
9
+ args = ["-ae", "ssh", "--delete", @config[:root], "#{@host.ip}:#{@config[:root]}"]
10
+ args << "-v" if @config[:verbose]
11
+ args << "-n" if @config[:dry]
12
+ args << "--timeout=#{@config[:timeout]}" if @config[:timeout]
13
+ output = command("rsync", args)
14
+ exitcode = $?
15
+ {:output => output, :exitcode => exitcode}
16
+ end
17
+
18
+ def command(cmd, options = [], &block)
19
+ cmd_line = "#{cmd} "
20
+ cmd_line += options.join(' ')
21
+ run_command(cmd_line, &block)
22
+ end
23
+
24
+ def run_command(cmd, &block)
25
+ if block_given?
26
+ IO.popen("#{cmd} 2>&1", &block)
27
+ else
28
+ `#{cmd} 2>&1`
29
+ end
8
30
  end
9
31
  end
10
32
  end
@@ -1,3 +1,3 @@
1
1
  module Rbfs
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -19,4 +19,5 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec"
22
23
  end
@@ -0,0 +1,30 @@
1
+ require 'rbfs/host_parser'
2
+ require 'stringio'
3
+
4
+ describe Rbfs::HostParser do
5
+ def test_case(string, size)
6
+ io = StringIO.new(string)
7
+ hp = Rbfs::HostParser.new(io)
8
+ hp.collect {|v|v}.length.should eql(size)
9
+ end
10
+
11
+ it "should handle a file with only comments" do
12
+ test_case("# TEST", 0)
13
+ end
14
+
15
+ it "should handle a file with one host" do
16
+ test_case("1.1.1.1 HOSTNAME", 1)
17
+ end
18
+
19
+ it "should handle a basic file" do
20
+ test_case("# TEST\n1.1.1.1 HOSTNAME", 1)
21
+ end
22
+
23
+ it "should handle empty lines" do
24
+ test_case("\n\n# TEST\n1.1.1.1 HOSTNAME\n\n", 1)
25
+ end
26
+
27
+ it "should handle multiple lines" do
28
+ test_case("1.1.1.1 HOSTNAME\n1.1.1.1 HOSTNAME\n1.1.1.1 HOSTNAME", 3)
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ require 'rbfs/host'
2
+
3
+ describe Rbfs::Host do
4
+ def test_example(line, ip, name=nil, alias_name=nil)
5
+ host = Rbfs::Host.new(line)
6
+ host.ip.should eql(ip)
7
+ host.name.should eql(name)
8
+ host.alias.should eql(alias_name)
9
+ end
10
+
11
+ it "should handle minimal syntax" do
12
+ test_example("1.1.1.1", "1.1.1.1")
13
+ end
14
+
15
+ it "should handle basic syntax" do
16
+ test_example("1.1.1.1 HOSTNAME", "1.1.1.1", "HOSTNAME")
17
+ end
18
+
19
+ it "should handle advanced syntax" do
20
+ test_example("1.1.1.1 HOSTNAME HOST", "1.1.1.1", "HOSTNAME", "HOST")
21
+ end
22
+
23
+ it "should support tab separation" do
24
+ test_example("1.1.1.1\tHOSTNAME", "1.1.1.1", "HOSTNAME")
25
+ end
26
+
27
+ it "should tolerate lots of tab separation" do
28
+ test_example("1.1.1.1\t\t\t\t\t\t\tHOSTNAME", "1.1.1.1", "HOSTNAME")
29
+ end
30
+
31
+ it "should tolerate lots of spaces" do
32
+ test_example("1.1.1.1 HOSTNAME", "1.1.1.1", "HOSTNAME")
33
+ end
34
+
35
+ it "should tolerate lots of aliases" do
36
+ test_example("1.1.1.1 HOSTNAME HOSTA HOSTB HOSTC HOSTD", "1.1.1.1", "HOSTNAME", "HOSTA")
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ require 'rbfs/rsync'
2
+
3
+ describe Rbfs::Rsync do
4
+ it "should run_command" do
5
+ Rbfs::Rsync.new.run_command("echo test").should eql("test\n")
6
+ end
7
+
8
+ it "should run_command block" do
9
+ Rbfs::Rsync.new.run_command("echo test") do |io|
10
+ io.read.should eql("test\n")
11
+ end
12
+ end
13
+
14
+ it "should command" do
15
+ Rbfs::Rsync.new.command("echo", ["test"]).should eql("test\n")
16
+ end
17
+
18
+ it "should command block" do
19
+ Rbfs::Rsync.new.command("echo", ["test"]) do |io|
20
+ io.read.should eql("test\n")
21
+ end
22
+ end
23
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbfs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2013-07-18 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
16
- requirement: &17891980 !ruby/object:Gem::Requirement
16
+ requirement: &15800280 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.3'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *17891980
24
+ version_requirements: *15800280
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &17891020 !ruby/object:Gem::Requirement
27
+ requirement: &15799780 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,18 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *17891020
35
+ version_requirements: *15799780
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &15799280 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *15799280
36
47
  description: Ruby File Sync
37
48
  email:
38
49
  - jbussdieker@gmail.com
@@ -42,6 +53,7 @@ extensions: []
42
53
  extra_rdoc_files: []
43
54
  files:
44
55
  - .gitignore
56
+ - .travis.yml
45
57
  - Gemfile
46
58
  - README.md
47
59
  - Rakefile
@@ -50,11 +62,16 @@ files:
50
62
  - lib/rbfs/args.rb
51
63
  - lib/rbfs/command.rb
52
64
  - lib/rbfs/config.rb
65
+ - lib/rbfs/futures.rb
53
66
  - lib/rbfs/host.rb
54
- - lib/rbfs/hosts.rb
67
+ - lib/rbfs/host_parser.rb
68
+ - lib/rbfs/logger.rb
55
69
  - lib/rbfs/rsync.rb
56
70
  - lib/rbfs/version.rb
57
71
  - rbfs.gemspec
72
+ - spec/rbfs/host_parser_spec.rb
73
+ - spec/rbfs/host_spec.rb
74
+ - spec/rbfs/rsync_spec.rb
58
75
  homepage: ''
59
76
  licenses: []
60
77
  post_install_message:
@@ -79,5 +96,8 @@ rubygems_version: 1.8.17
79
96
  signing_key:
80
97
  specification_version: 3
81
98
  summary: Ruby File Sync
82
- test_files: []
99
+ test_files:
100
+ - spec/rbfs/host_parser_spec.rb
101
+ - spec/rbfs/host_spec.rb
102
+ - spec/rbfs/rsync_spec.rb
83
103
  has_rdoc:
@@ -1,17 +0,0 @@
1
- require "rbfs/host"
2
-
3
- module Rbfs
4
- class Hosts
5
- def initialize(file_name)
6
- @file_name = file_name
7
- end
8
-
9
- def each(&block)
10
- lines = File.read(@file_name).split("\n")
11
- lines.reject! {|line| line.strip.start_with? "#"}
12
- lines.each do |line|
13
- yield(Host.new(line))
14
- end
15
- end
16
- end
17
- end