rbfs 0.0.2 → 0.0.3

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.
@@ -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