grifork 0.3.0 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 39cd6c990f5c21321b737f9c12c7651ff843a7a2
4
- data.tar.gz: 5a02758138e9f35fada790135e896d7bcea1a249
3
+ metadata.gz: 051978666d0eb719ebe0d1fb7396767f2849b60b
4
+ data.tar.gz: d1db36aff5d670f124ab13dadfa8288347ed1998
5
5
  SHA512:
6
- metadata.gz: 5c87db79f590d4356a7e4c6225cd8f546895e0ec3ebcbba02c45bfe6e742006d2726ff523c22a00ff19aa3d747a0416adf576c0b3bdf1ef9c65c8460f2f28c07
7
- data.tar.gz: 6dfbb038e924db9aa075c4ea212d15c41d32ec6c4ccbe4143dbcb89488a3b6c185178c69b93a74d659c851965233e0c18e560b0c4a59fc76547fc6d1a503825e
6
+ metadata.gz: 728ccefbb4558dc6caccd3a8a1504a52ad19fd8627d993046a82003545d0ee812cd66285088b96a858013eac16db4b25257207eadf1d6a94d7fd49b735be692d
7
+ data.tar.gz: b61507b73b372ba673b1d4eb9a78c85efce6c49d7a4b1534723392ceca9e80b9274c67b926490c044849f91a6753f03dda902b6b1914440a44ce7ae518f1bd57
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --private
2
+ -
3
+ CHANGELOG.md
4
+ LICENSE
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 0.4.0 (2016/10/9)
2
+
3
+ Bug Fix: (#3)
4
+
5
+ - `Grifork::Executor::Task#run` was not thread-safe
6
+
7
+ Features: (#3)
8
+
9
+ - New DSL methods:
10
+ - `#parallel` as option for [Parallel.map](https://github.com/grosser/parallel)
11
+ - `#ssh` as options for [Net::SSH](https://github.com/net-ssh/net-ssh)
12
+ - `#rsync` as options for `rsync` command
13
+ - New CLI option `-n|--dry-run`
14
+
15
+ Improve: (#3)
16
+
17
+ - Mode `:grifork` / Not to fork `grifork` task on node with no children
18
+
1
19
  ## 0.3.0 (2016/10/4)
2
20
 
3
21
  Release as a RubyGem.
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Gem Version](https://badge.fury.io/rb/grifork.svg)](https://badge.fury.io/rb/grifork)
1
2
  [![Build Status](https://travis-ci.org/key-amb/grifork.svg?branch=master)](https://travis-ci.org/key-amb/grifork)
2
3
 
3
4
  # grifork
@@ -63,10 +64,36 @@ bundle
63
64
 
64
65
  ```sh
65
66
  edit Griforkfile
66
- ./bin/grifork [--[f]ile path/to/Griforkfile]
67
+ ./bin/grifork [--[f]ile path/to/Griforkfile] [-n|--dry-run]
67
68
  ```
68
69
 
69
- See [example](example) directory for sample of Griforkfiles.
70
+ ## Griforkfile
71
+
72
+ **Griforkfile** is DSL file for _grifork_ which configures and defines the tasks
73
+ to be executed by _grifork_.
74
+
75
+ Here is a small example:
76
+
77
+ ```ruby
78
+ branches 4
79
+ log file: 'grifork.log'
80
+ hosts ['web1.internal', 'web2.internal', 'db1.internal', 'db2.internal', ...]
81
+
82
+ local do
83
+ rsync '/path/to/myapp/'
84
+ end
85
+
86
+ remote do
87
+ rsync_remote '/path/to/myapp/'
88
+ end
89
+ ```
90
+
91
+ If you run `grifork` with this _Griforkfile_, it just syncs `/path/to/myapp/` in
92
+ localhost to target `hosts` by `rsync` command.
93
+
94
+ See [example](https://github.com/key-amb/grifork/tree/master/example) directory for more examples of _Griforkfile_.
95
+
96
+ And refer to [Grifork::DSL](http://www.rubydoc.info/gems/grifork/Grifork/DSL) as API document of _Griforkfile_.
70
97
 
71
98
  # Authors
72
99
 
@@ -1,12 +1,24 @@
1
+ # Example of Grifork DSL for :grifok mode
2
+ # See Grifork::DSL for more informaion about DSL format
3
+
1
4
  # Configuration
2
5
  #mode :grifork
3
6
  branches 4
4
7
  log file: 'tmp/grifork.log', level: 'debug'
5
8
 
9
+ # Parallel mode for "parallel"
10
+ # Defaults to :in_threads
11
+ #parallel :in_processes
12
+
13
+ # Configure ssh options for #ssh & #grifork
14
+ # With private key auth:
15
+ ssh user: 'someone', keys: ['path/to/identity_file'], passphrase: 'xxx'
16
+ # With password auth:
17
+ #ssh user: 'someone', password: 'xxx'
18
+
6
19
  # Setting to exec grifork on remote
7
20
  # Implies to set mode as :grifork
8
21
  grifork do
9
- user 'someone' # ssh user to exec grifork on remote
10
22
  chdir '/path/to/your-app'
11
23
  tmpdir '/path/to/tmpdir'
12
24
  exec '/path/to/grifork'
@@ -15,10 +27,15 @@ end
15
27
  # Define hosts as array
16
28
  hosts ['web1.internal', 'web2.internal', '192.168.10.1', '192.168.10.2']
17
29
 
30
+ # Configure rsync options
31
+ # Defaults to ['-az', '--delete']
32
+ # Available full Hash options are bellow:
33
+ rsync delete: true, bwlimit: 4096, verbose: false, excludes: [], rsh: nil, dry_run: false
34
+
18
35
  # Define task run on localhost
19
36
  local do
20
37
  sh :echo, %W(LOCAL: #{src} => #{dst})
21
- ssh dst, :mkdir, %W(-p /path/to/dest), user: 'someone'
38
+ ssh dst, :mkdir, %W(-p /path/to/dest)
22
39
  rsync '/path/to/src/', '/path/to/dest/'
23
40
  end
24
41
 
@@ -27,7 +44,7 @@ end
27
44
  # different from "remote" task in :standalone mode
28
45
  remote do
29
46
  sh :echo, %W(REMOTE: #{src} => #{dst})
30
- ssh dst, :mkdir, %W(-p /path/to/dest), user: 'someone'
47
+ ssh dst, :mkdir, %W(-p /path/to/dest)
31
48
  rsync '/path/to/src/', '/path/to/dest/'
32
49
  end
33
50
 
@@ -1,15 +1,32 @@
1
- # Configuration
1
+ # Example of Grifork DSL for :standalone mode
2
+ # See Grifork::DSL for more informaion about DSL format
3
+
2
4
  #mode :standalone # Default mode
3
5
  branches 4
4
6
  log file: 'tmp/grifork.log', level: 'debug'
5
7
 
8
+ # Parallel mode for "parallel"
9
+ # Defaults to :in_threads
10
+ #parallel :in_processes
11
+
6
12
  # Define hosts as array
7
13
  hosts ['web1.internal', 'web2.internal', '192.168.10.1', '192.168.10.2']
8
14
 
15
+ # Configure ssh options for #ssh
16
+ # With private key auth:
17
+ ssh user: 'someone', keys: ['path/to/identity_file'], passphrase: 'xxx'
18
+ # With password auth:
19
+ #ssh user: 'someone', password: 'xxx'
20
+
21
+ # Configure rsync options
22
+ # Defaults to ['-az', '--delete']
23
+ # Available full Hash options are bellow:
24
+ rsync delete: true, bwlimit: 4096, verbose: false, excludes: [], rsh: nil, dry_run: false
25
+
9
26
  # Define task run on localhost
10
27
  local do
11
28
  sh :echo, %W(LOCAL: #{src} => #{dst})
12
- ssh dst, :mkdir, %W(-p /path/to/dest), user: 'someone'
29
+ ssh dst, :mkdir, %W(-p /path/to/dest)
13
30
  rsync '/path/to/src/', '/path/to/dest/'
14
31
  end
15
32
 
@@ -17,6 +34,6 @@ end
17
34
  remote do
18
35
  sh :echo, %W(REMOTE: #{src} => #{dst})
19
36
  ssh dst, :mkdir, %W(-p /path/to/dest)
20
- rsync_remote '/path/to/src/', '/path/to/dest/', user: 'someone'
37
+ rsync_remote '/path/to/src/', '/path/to/dest/'
21
38
  end
22
39
 
data/lib/grifork/cli.rb CHANGED
@@ -4,6 +4,7 @@ class Grifork::CLI
4
4
  opt.on('-f', '--file Griforkfile') { |f| @taskfile = f }
5
5
  opt.on('-o', '--override-by FILE') { |f| @override_file = f }
6
6
  opt.on('-r', '--on-remote') { @on_remote = true }
7
+ opt.on('-n', '--dry-run') { @dry_run = true }
7
8
  opt.on('-v', '--version') { @version = true }
8
9
  opt.parse!(argv)
9
10
  end
@@ -12,11 +13,17 @@ class Grifork::CLI
12
13
  exit
13
14
  end
14
15
 
15
- config = load_taskfiles.freeze
16
+ config = load_taskfiles
17
+ if @dry_run
18
+ config.dry_run = true
19
+ end
20
+ config.freeze
16
21
  Grifork.configure!(config)
22
+ logger = Grifork.logger
17
23
 
18
24
  graph = Grifork::Graph.new(config.hosts)
19
25
 
26
+ logger.info("START | mode: #{config.mode}")
20
27
  if @on_remote
21
28
  puts "Start on remote. Hosts: #{config.hosts}"
22
29
  end
@@ -31,6 +38,7 @@ class Grifork::CLI
31
38
  raise "Unexpected mode! #{config.mode}"
32
39
  end
33
40
 
41
+ logger.info("END | mode: #{config.mode}")
34
42
  if @on_remote
35
43
  puts "End on remote."
36
44
  end
@@ -1,6 +1,6 @@
1
1
  class Grifork::Config
2
2
  attr_reader :branches, :hosts, :log, :local_task, :remote_task, :grifork
3
- attr_accessor :griforkfile
3
+ attr_accessor :griforkfile, :dry_run
4
4
 
5
5
  def initialize(args)
6
6
  args.each do |key, val|
@@ -12,6 +12,31 @@ class Grifork::Config
12
12
  @mode || :standalone
13
13
  end
14
14
 
15
+ def parallel
16
+ @parallel || :in_threads
17
+ end
18
+
19
+ def dry_run?
20
+ @dry_run ? true : false
21
+ end
22
+
23
+ def ssh
24
+ @ssh || SSH.new
25
+ end
26
+
27
+ # Build +rsync+ command-line options from +@rsync+ and +@ssh+ objects
28
+ # @return [Array<String>] +rsync+ command options
29
+ # @see Rsync
30
+ # @see SSH
31
+ def rsync_opts
32
+ @rsync ||= Rsync.new
33
+ opts = @rsync.options
34
+ if opts.grep(/^(-e|--rsh)$/).size.zero?
35
+ opts.concat(['--rsh', ssh.command_for_rsync])
36
+ end
37
+ opts
38
+ end
39
+
15
40
  class Log
16
41
  attr :file, :level
17
42
 
@@ -21,6 +46,56 @@ class Grifork::Config
21
46
  end
22
47
  end
23
48
 
49
+ class SSH
50
+ attr :options
51
+ def initialize(opts = {})
52
+ @options = opts
53
+ end
54
+
55
+ # Build +ssh+ command with options
56
+ # @return [String] +ssh+ command with options
57
+ def command_for_rsync
58
+ args = []
59
+ args << "-l #{@options[:user]}" if @options[:user]
60
+ args << "-p #{@options[:port]}" if @options[:port]
61
+ if @options[:keys]
62
+ @options[:keys].each { |key| args << "-i #{key}" }
63
+ end
64
+ "ssh #{args.join(' ')}"
65
+ end
66
+ end
67
+
68
+ class Rsync
69
+ def initialize(args = %w(-az --delete))
70
+ case args
71
+ when Array
72
+ @options = args
73
+ when Hash
74
+ @delete = args[:delete] || true
75
+ @verbose = args[:verbose] || false
76
+ @bwlimit = args[:bwlimit] || nil
77
+ @excludes = args[:excludes] || []
78
+ @rsh = args[:rsh] || nil
79
+ @dry_run = args[:dry_run] || false
80
+ end
81
+ end
82
+
83
+ def options
84
+ @options ||= -> {
85
+ opts = []
86
+ opts << ( @verbose ? '-avz' : '-az' )
87
+ opts << '--dry-run' if @dry_run
88
+ opts << '--delete' if @delete
89
+ opts.concat(['--rsh', @rsh]) if @rsh
90
+ opts << "--bwlimit=#{@bwlimit}" if @bwlimit
91
+ if @excludes.size > 0
92
+ @excludes.each { |ex| opts << "--exclude=#{ex}" }
93
+ end
94
+ opts
95
+ }.call
96
+ end
97
+ end
98
+
24
99
  class Grifork
25
100
  attr :dir, :cmd, :login
26
101
 
data/lib/grifork/dsl.rb CHANGED
@@ -1,10 +1,14 @@
1
+ # DSL parser for *Griforkfile*
2
+ #
3
+ # _Griforkfile_ is interpreted in instance context by an object of this Class.
1
4
  class Grifork::DSL
2
5
  attr :config
3
6
 
4
7
  class LoadError < StandardError; end
5
8
 
6
9
  # Load DSL file to object
7
- # @param path [String]
10
+ # @return [Grifork::DSL]
11
+ # @param path [String] path of DSL file
8
12
  # @param on_remote [Boolean] whether process is invoked by remote host in :grifork mode or not
9
13
  def self.load_file(path, on_remote: false)
10
14
  content = File.binread(path)
@@ -18,6 +22,8 @@ class Grifork::DSL
18
22
  @on_remote = on_remote
19
23
  end
20
24
 
25
+ # Creates {Grifork::Config} object from holding properties
26
+ # @return [Grifork::Config]
21
27
  def to_config
22
28
  Grifork::Config.new(@config)
23
29
  end
@@ -30,6 +36,8 @@ class Grifork::DSL
30
36
  @config.merge!(other.config)
31
37
  end
32
38
 
39
+ # Grifork mode: How it works
40
+ # @param m [Symbol] +:standalone+ or +:grifork+. Defaults to +:standalone+
33
41
  def mode(m)
34
42
  unless Grifork::MODES.has_key?(m)
35
43
  raise LoadError, "Undefined mode! #{m}"
@@ -37,6 +45,9 @@ class Grifork::DSL
37
45
  config_set(:mode, m)
38
46
  end
39
47
 
48
+ # Configure grifork settings for +:grifork+ mode
49
+ # @param &command [Proc]
50
+ # @see Grifork::Config::Grifork.initialize
40
51
  def grifork(&command)
41
52
  if @config[:mode] == :standalone
42
53
  raise LoadError, "Can't configure grifork in standalone mode"
@@ -45,23 +56,74 @@ class Grifork::DSL
45
56
  config_set(:grifork, Grifork::Config::Grifork.new(&command))
46
57
  end
47
58
 
59
+ # Branches number for tree of host nodes
48
60
  def branches(num)
49
61
  config_set(:branches, num)
50
62
  end
51
63
 
64
+ # Configure logging
65
+ # @param args [Hash]
66
+ # @see Grifork::Config::Log.initialize
52
67
  def log(args)
53
68
  config_set(:log, Grifork::Config::Log.new(args))
54
69
  end
55
70
 
71
+ # Forking method to exec tasks in parallel.
72
+ # @param how [:Symbol] +:in_threads+ or +:in_processes+. Defaults to +:in_threads+
73
+ # @see https://github.com/grosser/parallel
74
+ def parallel(how)
75
+ unless %i(in_threads in_processes).include?(how)
76
+ raise LoadError, "Invalid parallel mode! #{how.inspect} / must be :in_threads or :in_processes"
77
+ end
78
+ config_set(:parallel, how)
79
+ end
80
+
81
+ # Host list as targets of tasks
82
+ # @param hosts [Array<String>] List of resolvable hostnames
56
83
  def hosts(list)
57
84
  config_set(:hosts, list)
58
85
  end
59
86
 
87
+ # Configure net-ssh options
88
+ # @params props [Hash] Options for Net::SSH
89
+ # @example
90
+ # # Password authentication
91
+ # ssh user: 'someone', password: 'xxx'
92
+ # # Private key authentication
93
+ # ssh user: 'someone', keys: ['path/to/priv_key'], passphrase: 'xxx'
94
+ # @see https://github.com/net-ssh/net-ssh
95
+ def ssh(props)
96
+ invalid_options = props.keys - Net::SSH::VALID_OPTIONS
97
+ if invalid_options.size > 0
98
+ raise LoadError, "#{invalid_options} are invalid for Net::SSH!"
99
+ end
100
+ config_set(:ssh, Grifork::Config::SSH.new(props))
101
+ end
102
+
103
+ # Configure rsync command options
104
+ # @params props [Array, Hash] rsync option parameters
105
+ # @example
106
+ # # Available full Hash options are bellow
107
+ # rsync delete: true, bwlimit: 4096, verbose: false, excludes: %w(.git* .svn*), rsh: nil, dry_run: false
108
+ # # This is the same with above by Array format:
109
+ # rsync %w(-az --delete --bwlimit=4096 --exclude=.git* --exclude=.svn*)
110
+ # # You can set more options by Array format:
111
+ # rsync %w(-azKc -e=rsh --delete --bwlimit=4096 --exclude-from=path/to/rsync.excludes)
112
+ def rsync(props)
113
+ config_set(:rsync, Grifork::Config::Rsync.new(props))
114
+ end
115
+
116
+ # Define tasks to execute at localhost
117
+ # @param &task [Proc] Codes to be executed by an object of {Grifork::Executor::Task}
60
118
  def local(&task)
61
119
  return if @on_remote
62
120
  config_set(:local_task, Grifork::Executor::Task.new(:local, &task))
63
121
  end
64
122
 
123
+ # Define tasks to execute at remote host
124
+ # @param &task [Proc] Codes to be executed by an object of {Grifork::Executor::Task}
125
+ # @note In +:standalone+ mode, the task is executed at localhost actually.
126
+ # In +:grifork+ mode, it is executed at remote hosts via +grifork+ command on remote hosts
65
127
  def remote(&task)
66
128
  if @on_remote
67
129
  config_set(:local_task, Grifork::Executor::Task.new(:local, &task))
@@ -1,26 +1,28 @@
1
1
  class Grifork::Executor::Grifork
2
2
  include Grifork::Executable
3
- attr :config
4
3
 
5
- # @param cfg [Grifork::Config::Grifork] configured by DSL#grifork
6
- def initialize(cfg)
7
- @config = cfg
4
+ # Run grifork command on remote node.
5
+ #
6
+ # 1. Create +Griforkfile+ and copy it to remote
7
+ # 2. Login remote host by +ssh+ and execute +grifork+ command
8
+ def run(node)
9
+ c = config.grifork
10
+ ssh node.host, %(test -d "#{c.workdir}" || mkdir -p "#{c.workdir}")
11
+ rsync(node.host, config.griforkfile, "#{c.workdir}/Griforkfile")
12
+ prepare_grifork_hosts_file_on_remote(node)
13
+ ssh node.host, %(cd #{c.dir} && #{c.cmd} --file #{c.workdir}/Griforkfile --override-by #{c.workdir}/Griforkfile.hosts --on-remote)
8
14
  end
9
15
 
10
- # Run grifork command on remote node:
11
- # 1. Create Griforkfile and copy it to remote
12
- # 2. ssh remote host and exec grifork
13
- def run(node)
14
- c = config
15
- ssh node.host, %(test -d "#{c.workdir}" || mkdir -p "#{c.workdir}"), user: c.login
16
- sh :rsync, ['-avzc', Grifork.config.griforkfile, "#{node.host}:#{c.workdir}/Griforkfile"]
16
+ private
17
+
18
+ def prepare_grifork_hosts_file_on_remote(node)
19
+ c = config.grifork
17
20
  hostsfile = Tempfile.create('Griforkfile.hosts')
18
21
  hostsfile.write(<<-EOS)
19
22
  hosts #{node.all_descendant_nodes.map(&:host)}
20
23
  EOS
21
24
  hostsfile.flush
22
- sh :rsync, ['-avzc', hostsfile.path, "#{node.host}:#{c.workdir}/Griforkfile.hosts"]
25
+ rsync(node.host, hostsfile.path, "#{c.workdir}/Griforkfile.hosts")
23
26
  hostsfile.close
24
- ssh node.host, %(cd #{c.dir} && #{c.cmd} --file #{c.workdir}/Griforkfile --override-by #{c.workdir}/Griforkfile.hosts --on-remote), [], user: c.login
25
27
  end
26
28
  end
@@ -1,15 +1,40 @@
1
1
  class Grifork::Executor::Task
2
2
  include Grifork::Executable
3
- attr :src, :dst
4
3
 
4
+ # Initialize with task
5
+ # @param &task [Proc] task to execute
5
6
  def initialize(type, &task)
6
7
  @type = type
7
8
  @task = task
8
9
  end
9
10
 
11
+ # Run the task
12
+ # @param src [String] Source hostname
13
+ # @param dst [String] Target hostname
10
14
  def run(src, dst)
11
- @src = src
12
- @dst = dst
15
+ Thread.current[:src] = src
16
+ Thread.current[:dst] = dst
13
17
  instance_eval(&@task)
14
18
  end
19
+
20
+ private
21
+
22
+ def src
23
+ Thread.current[:src]
24
+ end
25
+
26
+ def dst
27
+ Thread.current[:dst]
28
+ end
29
+
30
+ # Wrapper for {Grifork::Executable#rsync}
31
+ def rsync(from, to = nil)
32
+ super(dst, from, to)
33
+ end
34
+
35
+ # Wrapper for {Grifork::Executable#rsync_remote}
36
+ # @note This is for +remote+ task on +:standalone+ mode
37
+ def rsync_remote(from, to = nil)
38
+ super(src, dst, from, to)
39
+ end
15
40
  end
data/lib/grifork/graph.rb CHANGED
@@ -30,7 +30,7 @@ class Grifork::Graph
30
30
  # Launch local and remote tasks through whole graph
31
31
  def launch_tasks
32
32
  # level = 1
33
- Parallel.map(root.children, in_threads: root.children.size) do |node|
33
+ Parallel.map(root.children, config.parallel => root.children.size) do |node|
34
34
  logger.info("Run locally. localhost => #{node.host}")
35
35
  config.local_task.run(root.host, node.host)
36
36
  end
@@ -44,10 +44,14 @@ class Grifork::Graph
44
44
  logger.debug("#{root} Reached leaf. Nothing to do.")
45
45
  return
46
46
  end
47
- Parallel.map(root.children, in_processes: root.children.size) do |child|
47
+ Parallel.map(root.children, config.parallel => root.children.size) do |child|
48
48
  logger.info("Run locally. localhost => #{child.host}")
49
49
  config.local_task.run(root.host, child.host)
50
- Grifork::Executor::Grifork.new(config.grifork).run(child)
50
+ if child.children.size.zero?
51
+ logger.debug("#{child} Reached leaf. Nothing to do.")
52
+ next
53
+ end
54
+ Grifork::Executor::Grifork.new.run(child)
51
55
  end
52
56
  end
53
57
 
@@ -82,7 +86,7 @@ class Grifork::Graph
82
86
  return
83
87
  end
84
88
 
85
- Parallel.map(families, in_threads: families.size) do |family|
89
+ Parallel.map(families, config.parallel => families.size) do |family|
86
90
  parent = family[0]
87
91
  child = family[1]
88
92
  logger.info("Run remote [#{parent.level}]. #{parent.host} => #{child.host}")
@@ -1,11 +1,20 @@
1
1
  module Grifork::Executable
2
+ include Grifork::Configured
2
3
  include Grifork::Loggable
3
4
 
4
5
  class CommandFailure < StandardError; end
5
6
  class SSHCommandFailure < StandardError; end
6
7
 
8
+ # Execute shell command at localhost
9
+ # @param cmd [String] command
10
+ # @param args [Array] arguments
7
11
  def sh(cmd, args = [])
8
- logger.info("#sh start - #{cmd} #{args}")
12
+ if config.dry_run?
13
+ logger.info("[Dry-run] #sh | #{cmd} #{args}")
14
+ return
15
+ else
16
+ logger.info("#sh | #{cmd} #{args}")
17
+ end
9
18
  stat = Open3.popen3(cmd.to_s, *args) do |stdin, stdout, stderr, wait_thr|
10
19
  stdin.close
11
20
  stdout.each { |l| logger.info("#sh [out] #{l.chomp}") }
@@ -18,16 +27,23 @@ module Grifork::Executable
18
27
  end
19
28
  end
20
29
 
21
- def ssh(host, cmd, args = [], user: nil)
30
+ # Execute +ssh+ with command to execute at remote host
31
+ # @param host [String] hostname
32
+ # @param cmd [String] command
33
+ # @param args [Array] arguments
34
+ def ssh(host, cmd, args = [])
22
35
  command = "#{cmd} #{args.shelljoin}"
23
- logger.info("#ssh start - to: #{host}, command: #{cmd} #{args}")
24
- ssh_args = [host]
25
- ssh_args << user if user
26
- Net::SSH.start(*ssh_args) do |ssh|
36
+ if config.dry_run?
37
+ logger.info("[Dry-run] #ssh @#{host} #{config.ssh.options} | #{cmd} #{args}")
38
+ return
39
+ else
40
+ logger.info("#ssh @#{host} #{config.ssh.options} | #{cmd} #{args}")
41
+ end
42
+ Net::SSH.start(host, nil, config.ssh.options) do |ssh|
27
43
  channel = ssh.open_channel do |ch|
28
44
  ch.exec(command) do |ch, success|
29
45
  unless success
30
- raise SSHCommandFailure, "Failed to exec ssh command! on: #{host} command: #{cmd} #{args}"
46
+ raise SSHCommandFailure, "Failed to exec ssh command! - #{user}@#{host} | #{cmd} #{args}"
31
47
  end
32
48
 
33
49
  ch.on_data do |c, d|
@@ -43,17 +59,26 @@ module Grifork::Executable
43
59
  end
44
60
  end
45
61
 
46
- def rsync(from, to = nil)
62
+ # Shorthand for +rsync+ command.
63
+ # Sync contents to target host
64
+ # @param host [String] Target hostname
65
+ # @param from [String] Path to source file or directory
66
+ # @param to [String] Path to destination at remote host.
67
+ # If you omit this param, it will be the same with +from+ param
68
+ def rsync(host, from, to = nil)
47
69
  to ||= from
48
- sh :rsync, [*rsync_opts, from, "#{dst}:#{to}"]
70
+ sh :rsync, [*config.rsync_opts, from, "#{host}:#{to}"]
49
71
  end
50
72
 
51
- def rsync_remote(from, to = nil, user: nil)
73
+ # Shorthand for +rsync+ command run by +ssh+ to source host
74
+ # Sync contents from source host to target host
75
+ # @param src [String] Source hostname to login by +ssh+
76
+ # @param dst [String] Target hostname
77
+ # @param from [String] Path to source file or directory
78
+ # @param to [String] Path to destination at remote host.
79
+ # If you omit this param, it will be the same with +from+ param
80
+ def rsync_remote(src, dst, from, to = nil)
52
81
  to ||= from
53
- ssh src, :rsync, [*rsync_opts, from, "#{dst}:#{to}"], user: user
54
- end
55
-
56
- def rsync_opts
57
- %w(-avzc --delete)
82
+ ssh src, :rsync, [*config.rsync_opts, from, "#{dst}:#{to}"]
58
83
  end
59
84
  end
@@ -1,3 +1,3 @@
1
1
  class Grifork
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grifork
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - key-amb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-03 00:00:00.000000000 Z
11
+ date: 2016-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ssh
@@ -119,6 +119,7 @@ files:
119
119
  - ".gitignore"
120
120
  - ".rspec"
121
121
  - ".travis.yml"
122
+ - ".yardopts"
122
123
  - CHANGELOG.md
123
124
  - Gemfile
124
125
  - LICENSE