grifork 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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