hocho 0.1.0.beta1 → 0.2.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: 6f40a1aecd4cf8840d456cf93fdd5523910c0187
4
- data.tar.gz: ee1d577aaae256bffa0b2f94ca66539b4db941eb
3
+ metadata.gz: 71a35bcabdd7fb28b6ddcde2093f7cdde9ddcafa
4
+ data.tar.gz: f4ac549145b6940d4bef0ade7b3338d3f89f2028
5
5
  SHA512:
6
- metadata.gz: ba38f1f5837dd7095c8f78bc06d2adcd50f7a281ca96008704816bd0d9a5e03579ffca9c0ed70cf02fd102097e8f61e2a31829d9318728922969fd0481b71c8a
7
- data.tar.gz: c5dd335f31ee64c2a51ddf7bfbabd341436efa8e43b63db30c6a67563904991692ecd4db9552d88f17f1240b4a8bc3b64a2747d6043ad1508e12d2d93bc32fbf
6
+ metadata.gz: c5e35fb5d3d3263ae7fab63fb51d846ba3ce5bb41421dd43f8731dd256081f0e712f11c192aef676140ab8801adfc558927a71574fa5b32b5f7ef29f29dd2bd7
7
+ data.tar.gz: 82ecb70f5dbb71f3be59e3d5dd9c42e9c4d733604038d8d296eb94e2795e629dc779ede9de00df8ccf28efce72827ccdb4550d82854391c1ca06b3e929f49b97
data/README.md CHANGED
@@ -4,14 +4,15 @@ Hocho is a wrapper of the provisioning tool [itamae](https://github.com/itamae-k
4
4
 
5
5
  ## Features
6
6
 
7
- - `itamae ssh` support
8
- - remote `itamae local` support on rsync+bundler
7
+ - Drivers
8
+ - `itamae ssh` support
9
+ - remote `itamae local` support on rsync+bundler
10
+ - remote `mitamae` support
9
11
  - Simple pluggable host inventory, discovery
10
12
 
11
- ## vs. other softwares
12
-
13
13
  ## Installation
14
14
 
15
+
15
16
  Add this line to your application's Gemfile:
16
17
 
17
18
  ```ruby
@@ -33,14 +34,24 @@ Or install it yourself as:
33
34
  inventory_providers:
34
35
  file:
35
36
  path: './hosts'
37
+ property_providers:
38
+ - add_default:
39
+ properties:
40
+ blah: blahblah
41
+ # preferred_driver: mitamae
42
+ # driver_options:
43
+ # mitamae:
44
+ # mitamae_prepare_script: 'wget -O /usr/local/bin/mitamae https://...'
36
45
  ```
37
46
 
38
- ```
47
+ ``` yaml
39
48
  # ./hosts/test.yml
40
49
  test.example.org:
41
50
  # ssh_options:
42
51
  # user: ...
43
52
  properties:
53
+ # preferred_driver: bundler
54
+ # preferred_driver: mitamae
44
55
  run_list:
45
56
  - roles/app/default.rb
46
57
  ```
@@ -17,9 +17,6 @@ module Hocho
17
17
  hosts = inventory.hosts
18
18
 
19
19
  if options[:verbose]
20
- hosts.each do |host|
21
- config.property_providers.each { |_| _.determine(host) }
22
- end
23
20
  case options[:format]
24
21
  when 'yaml'
25
22
  puts hosts.map(&:to_h).to_yaml
@@ -82,7 +79,7 @@ module Hocho
82
79
  private
83
80
 
84
81
  def inventory
85
- @inventory ||= Hocho::Inventory.new(config.inventory_providers)
82
+ @inventory ||= Hocho::Inventory.new(config.inventory_providers, config.property_providers)
86
83
  end
87
84
 
88
85
  def config
@@ -17,6 +17,9 @@ module Hocho
17
17
  raise NotImplementedError
18
18
  end
19
19
 
20
+ def finalize
21
+ end
22
+
20
23
  def run_list
21
24
  [*initializers, *host.run_list]
22
25
  end
@@ -3,35 +3,22 @@ require 'hocho/drivers/ssh_base'
3
3
  module Hocho
4
4
  module Drivers
5
5
  class Bundler < SshBase
6
- def initialize(host, base_dir: '.', initializers: [], itamae_options: [], bundle_without: [], bundle_path: nil, deploy_dir: nil, keep_synced_files: nil)
6
+ def initialize(host, base_dir: '.', initializers: [], itamae_options: [], bundle_without: [], bundle_path: nil, deploy_options: {})
7
7
  super host, base_dir: base_dir, initializers: initializers
8
8
 
9
9
  @itamae_options = itamae_options
10
10
  @bundle_without = bundle_without
11
11
  @bundle_path = bundle_path
12
- @deploy_dir = deploy_dir
13
- @keep_synced_files = keep_synced_files
14
- end
15
-
16
- def keep_synced_files?
17
- @keep_synced_files
12
+ @deploy_options = deploy_options
18
13
  end
19
14
 
20
15
  def run(dry_run: false)
21
- deploy
22
- bundle_install
23
- run_itamae(dry_run: dry_run)
24
- ensure
25
- cleanup
16
+ deploy(**@deploy_options) do
17
+ bundle_install
18
+ run_itamae(dry_run: dry_run)
19
+ end
26
20
  end
27
21
 
28
- def deploy
29
- ssh_cmd = ['ssh', *host.openssh_config.flat_map { |l| ['-o', "\"#{l}\""] }].join(' ')
30
- rsync_cmd = [*%w(rsync -az --copy-links --copy-unsafe-links --delete --exclude=.git), '--rsh', ssh_cmd, '.', "#{host.hostname}:#{host_basedir}"]
31
-
32
- puts "=> $ #{rsync_cmd.inspect}"
33
- system(*rsync_cmd, chdir: base_dir) or raise 'failed to rsync'
34
- end
35
22
 
36
23
  def bundle_install
37
24
  bundle_path_env = @bundle_path ? "BUNDLE_PATH=#{@bundle_path.shellescape} " : nil
@@ -46,12 +33,7 @@ module Hocho
46
33
  puts "=> #{host.name} # #{bundle_install.shelljoin}"
47
34
 
48
35
  ssh_run("bash") do |c|
49
- c.on_data do |c, data|
50
- puts "[#{host.name}] #{data}"
51
- end
52
- c.on_extended_data do |c, _, data|
53
- puts "[#{host.name}/ERR] #{data}"
54
- end
36
+ set_ssh_output_hook(c)
55
37
 
56
38
  c.send_data("cd #{host_basedir.shellescape}\n#{sudovars}\n#{sudocmd}#{bundle_install.shelljoin}\n")
57
39
  c.eof!
@@ -69,12 +51,7 @@ module Hocho
69
51
  prepare_sudo do |sh, sudovars, sudocmd|
70
52
  puts "=> #{host.name} # #{host.bundler_cmd} exec #{itamae_cmd.shelljoin}"
71
53
  ssh_run("bash") do |c|
72
- c.on_data do |c, data|
73
- puts "[#{host.name}] #{data}"
74
- end
75
- c.on_extended_data do |c, _, data|
76
- puts "[#{host.name}/ERR] #{data}"
77
- end
54
+ set_ssh_output_hook(c)
78
55
 
79
56
  c.send_data("cd #{host_basedir.shellescape}\n#{sudovars}\n#{sudocmd}#{host.bundler_cmd} exec #{itamae_cmd.shelljoin}\n")
80
57
  c.eof!
@@ -82,19 +59,6 @@ module Hocho
82
59
  end
83
60
  end
84
61
  end
85
-
86
- def cleanup
87
- return unless !keep_synced_files? || !deploy_dir
88
-
89
- cmd = "rm -rf #{host_basedir.shellescape}"
90
- puts "=> #{host.name} $ #{cmd}"
91
- ssh_run(cmd, error: false)
92
- end
93
-
94
- def host_basedir
95
- @deploy_dir || "#{host_tmpdir}/itamae"
96
- end
97
-
98
62
  end
99
63
  end
100
64
  end
@@ -0,0 +1,87 @@
1
+ require 'hocho/drivers/ssh_base'
2
+
3
+ module Hocho
4
+ module Drivers
5
+ class Mitamae < SshBase
6
+ def initialize(host, base_dir: '.', mitamae_path: 'mitamae', mitamae_prepare_script: [], mitamae_outdate_check_script: nil, initializers: [], mitamae_options: [], deploy_options: {})
7
+ super host, base_dir: base_dir, initializers: initializers
8
+
9
+ @mitamae_path = mitamae_path
10
+ @mitamae_prepare_script = mitamae_prepare_script
11
+ @mitamae_outdate_check_script = mitamae_outdate_check_script
12
+ @mitamae_options = mitamae_options
13
+ @deploy_options = deploy_options
14
+ end
15
+
16
+
17
+ def run(dry_run: false)
18
+ deploy(**@deploy_options) do
19
+ prepare_mitamae
20
+ run_mitamae(dry_run: dry_run)
21
+ end
22
+ end
23
+
24
+ def mitamae_available?
25
+ exitstatus, _ = if @mitamae_path.start_with?('/')
26
+ ssh_run("test -x #{@mitamae_path.shellescape}", error: false)
27
+ else
28
+ ssh_run("which #{@mitamae_path.shellescape} 2>/dev/null >/dev/null", error: false)
29
+ end
30
+ exitstatus == 0
31
+ end
32
+
33
+ def mitamae_outdated?
34
+ if @mitamae_outdate_check_script
35
+ exitstatus, _ = ssh_run("export HOCHO_MITAMAE_PATH=#{@mitamae_path.shellescape}; #{@mitamae_outdate_check_script}", error: false)
36
+ exitstatus == 0
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ def prepare_mitamae
43
+ return if mitamae_available? && !mitamae_outdated?
44
+ script = [*@mitamae_prepare_script].join("\n\n")
45
+ if script.empty?
46
+ raise "We have to prepare MItamae, but not mitamae_prepare_script is specified"
47
+ end
48
+ prepare_sudo do |sh, sudovars, sudocmd|
49
+ log_prefix = "=> #{host.name} # "
50
+ log_prefix_white = ' ' * log_prefix.size
51
+ puts "#{log_prefix}#{script.each_line.map{ |_| "#{log_prefix_white}#{_.chomp}" }.join("\n")}"
52
+
53
+ ssh_run("bash") do |c|
54
+ set_ssh_output_hook(c)
55
+
56
+ c.send_data("cd #{host_basedir.shellescape}\n#{sudovars}\n#{sudocmd} bash <<-'HOCHOEOS'\n#{script}HOCHOEOS\n")
57
+ c.eof!
58
+ end
59
+ end
60
+ availability, outdated = mitamae_available?, mitamae_outdated?
61
+ if !availability || outdated
62
+ status = [availability ? nil : 'unavailable', outdated ? 'outdated' : nil].compact.join(' and ')
63
+ raise "prepared MItamae, but it's still #{status}"
64
+ end
65
+ end
66
+
67
+ def run_mitamae(dry_run: false)
68
+ with_host_node_json_file do
69
+ itamae_cmd = [@mitamae_path, 'local', '-j', host_node_json_path, *@mitamae_options]
70
+ itamae_cmd.push('--dry-run') if dry_run
71
+ # itamae_cmd.push('--color') if $stdout.tty?
72
+ itamae_cmd.push(*run_list)
73
+
74
+ prepare_sudo do |sh, sudovars, sudocmd|
75
+ puts "=> #{host.name} # #{itamae_cmd.shelljoin}"
76
+ ssh_run("bash") do |c|
77
+ set_ssh_output_hook(c)
78
+
79
+ c.send_data("cd #{host_basedir.shellescape}\n#{sudovars}\n#{sudocmd} #{itamae_cmd.shelljoin}\n")
80
+ c.eof!
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -9,6 +9,47 @@ module Hocho
9
9
  host.ssh_connection
10
10
  end
11
11
 
12
+ def finalize
13
+ remove_host_tmpdir!
14
+ remove_host_shmdir!
15
+ end
16
+
17
+ def deploy(deploy_dir: nil, shm_prefix: [])
18
+ @host_basedir = deploy_dir if deploy_dir
19
+
20
+ shm_prefix = [*shm_prefix]
21
+
22
+ ssh_cmd = ['ssh', *host.openssh_config.flat_map { |l| ['-o', "\"#{l}\""] }].join(' ')
23
+ shm_exclude = shm_prefix.map{ |_| "--exclude=#{_}" }
24
+ rsync_cmd = [*%w(rsync -az --copy-links --copy-unsafe-links --delete --exclude=.git), *shm_exclude, '--rsh', ssh_cmd, '.', "#{host.hostname}:#{host_basedir}"]
25
+
26
+ puts "=> $ #{rsync_cmd.shelljoin}"
27
+ system(*rsync_cmd, chdir: base_dir) or raise 'failed to rsync'
28
+
29
+ unless shm_prefix.empty?
30
+ shm_include = shm_prefix.map{ |_| "--include=#{_.sub(%r{/\z},'')}/***" }
31
+ rsync_cmd = [*%w(rsync -az --copy-links --copy-unsafe-links --delete), *shm_include, '--exclude=*', '--rsh', ssh_cmd, '.', "#{host.hostname}:#{host_shm_basedir}"]
32
+ puts "=> $ #{rsync_cmd.shelljoin}"
33
+ system(*rsync_cmd, chdir: base_dir) or raise 'failed to rsync'
34
+ shm_prefix.each do |x|
35
+ mkdir = if %r{\A[^/].*\/.+\z} === x
36
+ %Q(mkdir -vp "$(basename #{x.shellescape})" &&)
37
+ else
38
+ nil
39
+ end
40
+ ssh_run(%Q(cd #{host_basedir} && #{mkdir} ln -sfv #{host_shm_basedir}/#{x.shellescape} ./#{x.sub(%r{/\z},'').shellescape}))
41
+ end
42
+ end
43
+
44
+ yield
45
+ ensure
46
+ if !deploy_dir || !keep_synced_files
47
+ cmd = "rm -rf #{host_basedir.shellescape}"
48
+ puts "=> #{host.name} $ #{cmd}"
49
+ ssh_run(cmd, error: false)
50
+ end
51
+ end
52
+
12
53
  private
13
54
 
14
55
  def prepare_sudo(password = host.sudo_password)
@@ -35,7 +76,8 @@ module Hocho
35
76
  end
36
77
 
37
78
  begin
38
- temp_executable = ssh.exec!('mktemp').chomp
79
+ tmpdir = host_shmdir ? "TMPDIR=#{host_shmdir.shellescape} " : nil
80
+ temp_executable = ssh.exec!("#{tmpdir}mktemp").chomp
39
81
  raise unless temp_executable.start_with?('/')
40
82
 
41
83
  ssh_run("chmod 0700 #{temp_executable.shellescape}; cat > #{temp_executable.shellescape}; chmod +x #{temp_executable.shellescape}") do |ch|
@@ -53,6 +95,69 @@ module Hocho
53
95
  end
54
96
  end
55
97
 
98
+ def set_ssh_output_hook(c)
99
+ outbuf, errbuf = [], []
100
+ check = ->(prefix,data,buf) do
101
+ has_newline = data.include?("\n")
102
+ lines = data.lines
103
+ last = lines.pop
104
+ if last[-1] == "\n"
105
+ buf << last
106
+ end
107
+ if has_newline
108
+ (buf+lines).join.each_line do |line|
109
+ puts "#{prefix} #{line}"
110
+ end
111
+ buf.replace([])
112
+ end
113
+ if last[-1] != "\n"
114
+ buf << last
115
+ end
116
+ end
117
+
118
+ c.on_data do |c, data|
119
+ check.call "[#{host.name}] ", data, outbuf
120
+ end
121
+ c.on_extended_data do |c, _, data|
122
+ check.call "[#{host.name}/ERR] ", data, errbuf
123
+ end
124
+ end
125
+
126
+ def host_basedir
127
+ @host_basedir || "#{host_tmpdir}/itamae"
128
+ end
129
+
130
+ def host_shm_basedir
131
+ host_shmdir && "#{host_shmdir}/itamae"
132
+ end
133
+
134
+ def host_shmdir
135
+ return @host_shmdir if @host_shmdir
136
+ return nil if @host_shmdir == false
137
+
138
+ shmdir = host.shmdir
139
+ unless shmdir
140
+ if ssh.exec!('uname').chomp == 'Linux'
141
+ shmdir = '/dev/shm'
142
+ mount = ssh.exec!("grep -F #{shmdir.shellescape} /proc/mounts").each_line.map{ |_| _.chomp.split(' ') }
143
+ unless mount.find { |_| _[1] == shmdir }&.first == 'tmpfs'
144
+ @host_shmdir = false
145
+ return nil
146
+ end
147
+ else
148
+ @host_shmdir = false
149
+ return nil
150
+ end
151
+ end
152
+
153
+ mktemp_cmd = "TMPDIR=#{shmdir.shellescape} #{%w(mktemp -d -t hocho-run-XXXXXXXXX).shelljoin}"
154
+
155
+ res = ssh.exec!(mktemp_cmd)
156
+ unless res.start_with?(shmdir)
157
+ raise "Failed to shm mktemp #{mktemp_cmd.inspect} -> #{res.inspect}"
158
+ end
159
+ @host_shmdir = res.chomp
160
+ end
56
161
 
57
162
  def host_tmpdir
58
163
  @host_tmpdir ||= begin
@@ -60,13 +165,28 @@ module Hocho
60
165
  mktemp_cmd.prepend("TMPDIR=#{host.tmpdir.shellescape} ") if host.tmpdir
61
166
 
62
167
  res = ssh.exec!(mktemp_cmd)
63
- unless res.start_with?('/tmp')
168
+ unless res.start_with?(host.tmpdir || '/')
64
169
  raise "Failed to mktemp #{mktemp_cmd.inspect} -> #{res.inspect}"
65
170
  end
66
171
  res.chomp
67
172
  end
68
173
  end
69
174
 
175
+ def remove_host_tmpdir!
176
+ if @host_tmpdir
177
+ host_tmpdir, @host_tmpdir = @host_tmpdir, nil
178
+ ssh.exec!("rm -rf #{host_tmpdir.shellescape}")
179
+ end
180
+ end
181
+
182
+ def remove_host_shmdir!
183
+ if @host_shmdir
184
+ host_shmdir, @host_shmdir = @host_shmdir, nil
185
+ ssh.exec!("rm -rf #{host_shmdir.shellescape}")
186
+ end
187
+ end
188
+
189
+
70
190
  def host_node_json_path
71
191
  "#{host_tmpdir}/node.json"
72
192
  end
@@ -5,28 +5,34 @@ require 'net/ssh/proxy/command'
5
5
 
6
6
  module Hocho
7
7
  class Host
8
- def initialize(name, provider: nil, properties: {}, tags: {}, ssh_options: nil, tmpdir: nil, sudo_password: nil)
8
+ def initialize(name, provider: nil, providers: nil, properties: {}, tags: {}, ssh_options: nil, tmpdir: nil, shmdir: nil, sudo_password: nil)
9
+ if provider
10
+ warn "DEPRECATION WARNING: #{caller[1]}: Hocho::Host.new(provider:) is deprecated. Use providers: instead "
11
+ end
12
+
9
13
  @name = name
10
- @provider = provider
14
+ @providers = [*provider, *providers]
11
15
  self.properties = properties
12
16
  @tags = tags
13
17
  @override_ssh_options = ssh_options
14
18
  @tmpdir = tmpdir
19
+ @shmdir = shmdir
15
20
  @sudo_password = sudo_password
16
21
  end
17
22
 
18
- attr_reader :name, :provider, :properties, :tmpdir
23
+ attr_reader :name, :providers, :properties, :tmpdir, :shmdir
19
24
  attr_writer :sudo_password
20
25
  attr_accessor :tags
21
26
 
22
27
  def to_h
23
28
  {
24
29
  name: name,
25
- provider: provider,
30
+ providers: providers,
26
31
  tags: tags.to_h,
27
32
  properties: properties.to_h,
28
33
  }.tap do |h|
29
34
  h[:tmpdir] = tmpdir if tmpdir
35
+ h[:shmdir] = shmdir if shmdir
30
36
  h[:ssh_options] = @override_ssh_options if @override_ssh_options
31
37
  end
32
38
  end
@@ -35,12 +41,23 @@ module Hocho
35
41
  @properties = Hashie::Mash.new(other)
36
42
  end
37
43
 
38
- def add_properties_from_providers(providers)
44
+ def merge!(other)
45
+ @tags.merge!(other.tags)
46
+ @tmpdir = other.tmpdir if other.tmpdir
47
+ @shmdir = other.shmdir if other.shmdir
48
+ @properties.merge(other.properties)
49
+ end
50
+
51
+ def apply_property_providers(providers)
39
52
  providers.each do |provider|
40
53
  provider.determine(self)
41
54
  end
42
55
  end
43
56
 
57
+ def ssh_name
58
+ properties[:ssh_name] || name
59
+ end
60
+
44
61
  def run_list
45
62
  properties[:run_list] || []
46
63
  end
@@ -58,70 +75,72 @@ module Hocho
58
75
  end
59
76
 
60
77
  def ssh_options
61
- (Net::SSH::Config.for(name) || {}).merge(Hocho::Utils::Symbolize.keys_of(properties[:ssh_options] || {})).merge(@override_ssh_options || {})
78
+ (Net::SSH::Config.for(ssh_name) || {}).merge(Hocho::Utils::Symbolize.keys_of(properties[:ssh_options] || {})).merge(@override_ssh_options || {})
62
79
  end
63
80
 
64
- def openssh_config
81
+ def openssh_config(separator='=')
65
82
  ssh_options.flat_map do |key, value|
66
83
  case key
67
84
  when :encryption
68
- "Ciphers #{[*value].join(?,)}"
85
+ [["Ciphers", [*value].join(?,)]]
69
86
  when :compression
70
- "Compression #{value}"
87
+ [["Compression", value]]
71
88
  when :compression_level
72
- "CompressionLevel #{value}"
89
+ [["CompressionLevel", value]]
73
90
  when :timeout
74
- "ConnectTimeout #{value}"
91
+ [["ConnectTimeout", value]]
75
92
  when :forward_agent
76
- "ForwardAgent #{value ? 'yes' : 'no'}"
93
+ [["ForwardAgent", value ? 'yes' : 'no']]
77
94
  when :keys_only
78
- "IdentitiesOnly #{value ? 'yes' : 'no'}"
95
+ [["IdentitiesOnly", value ? 'yes' : 'no']]
79
96
  when :global_known_hosts_file
80
- "GlobalKnownHostsFile #{value}"
97
+ [["GlobalKnownHostsFile", value]]
81
98
  when :auth_methods
82
99
  [].tap do |lines|
83
100
  methods = value.dup
84
101
  value.each do |val|
85
102
  case val
86
103
  when 'hostbased'
87
- lines << "HostBasedAuthentication yes"
104
+ lines << ["HostBasedAuthentication", "yes"]
88
105
  when 'password'
89
- lines << "PasswordAuthentication yes"
106
+ lines << ["PasswordAuthentication", "yes"]
90
107
  when 'publickey'
91
- lines << "PubkeyAuthentication yes"
108
+ lines << ["PubkeyAuthentication", "yes"]
92
109
  end
93
110
  end
94
111
  unless methods.empty?
95
- lines << "PreferredAuthentications #{methods.join(?,)}"
112
+ lines << ["PreferredAuthentications", methods.join(?,)]
96
113
  end
97
114
  end
98
115
  when :host_key
99
- "HostKeyAlgorithms #{[*value].join(?,)}"
116
+ [["HostKeyAlgorithms", [*value].join(?,)]]
100
117
  when :host_key_alias
101
- "HostKeyAlias #{value}"
118
+ [["HostKeyAlias", value]]
102
119
  when :host_name
103
- "HostName #{value}"
120
+ [["HostName", value]]
104
121
  when :keys
105
122
  [*value].map do |val|
106
- "IdentityFile #{val}"
123
+ ["IdentityFile", val]
107
124
  end
108
125
  when :hmac
109
- "Macs #{[*value].join(?,)}"
126
+ [["Macs", [*value].join(?,)]]
110
127
  when :port
111
- "Port #{value}"
128
+ [["Port", value]]
112
129
  when :proxy
113
130
  if value.kind_of?(Net::SSH::Proxy::Command)
114
- "ProxyCommand #{value.command_line_template}"
131
+ [["ProxyCommand", value.command_line_template]]
115
132
  else
116
- "ProxyCommand #{value}"
133
+ [["ProxyCommand", value]]
117
134
  end
118
135
  when :rekey_limit
119
- "RekeyLimit #{value}"
136
+ [["RekeyLimit", value]]
120
137
  when :user
121
- "User #{value}"
138
+ [["User", value]]
122
139
  when :user_known_hosts_file
123
- "UserKnownHostsFile #{value}"
140
+ [["UserKnownHostsFile", value]]
124
141
  end
142
+ end.compact.map do |keyval|
143
+ keyval.join(separator)
125
144
  end
126
145
  end
127
146
 
@@ -1,11 +1,23 @@
1
1
  module Hocho
2
2
  class Inventory
3
- def initialize(providers)
3
+ def initialize(providers, property_providers)
4
4
  @providers = providers
5
+ @property_providers = property_providers
5
6
  end
6
7
 
7
8
  def hosts
8
- @hosts ||= @providers.flat_map(&:hosts)
9
+ @hosts ||= @providers.inject({}) do |r,provider|
10
+ provider.hosts.each do |host|
11
+ if r.key?(host.name)
12
+ r[host.name].merge!(host)
13
+ else
14
+ r[host.name] = host
15
+ end
16
+ end
17
+ r
18
+ end.each do |name, host|
19
+ host.apply_property_providers(@property_providers)
20
+ end.values
9
21
  end
10
22
 
11
23
  def filter(filters)
@@ -27,7 +27,7 @@ module Hocho
27
27
  content.map do |name, value|
28
28
  Host.new(
29
29
  name.to_s,
30
- provider: self.class,
30
+ providers: self.class,
31
31
  properties: value[:properties],
32
32
  tags: value[:tags],
33
33
  ssh_options: value[:ssh_options]
@@ -3,7 +3,7 @@ require 'hocho/utils/finder'
3
3
  module Hocho
4
4
  module PropertyProviders
5
5
  def self.find(name)
6
- Hocho::Utils::Finder.find(self, 'hocho/inventory_providers', name)
6
+ Hocho::Utils::Finder.find(self, 'hocho/property_providers', name)
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,15 @@
1
+ require 'hocho/property_providers/base'
2
+
3
+ module Hocho
4
+ module PropertyProviders
5
+ class AddDefault < Base
6
+ def initialize(properties: {})
7
+ @properties = properties
8
+ end
9
+
10
+ def determine(host)
11
+ host.properties.replace(host.properties.reverse_merge(@properties))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -18,7 +18,10 @@ module Hocho
18
18
  def run(dry_run: false)
19
19
  puts "Running using #{best_driver_name}"
20
20
  driver_options = @driver_options[best_driver_name] || {}
21
- best_driver.new(host, base_dir: base_dir, initializers: initializers, **driver_options).run(dry_run: dry_run)
21
+ driver = best_driver.new(host, base_dir: base_dir, initializers: initializers, **driver_options)
22
+ driver.run(dry_run: dry_run)
23
+ ensure
24
+ driver.finalize if driver
22
25
  end
23
26
 
24
27
  # def check_ssh_port
@@ -1,3 +1,3 @@
1
1
  module Hocho
2
- VERSION = "0.1.0.beta1"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hocho
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.beta1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sorah (Shota Fukumori)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-29 00:00:00.000000000 Z
11
+ date: 2017-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -132,6 +132,7 @@ files:
132
132
  - lib/hocho/drivers/base.rb
133
133
  - lib/hocho/drivers/bundler.rb
134
134
  - lib/hocho/drivers/itamae_ssh.rb
135
+ - lib/hocho/drivers/mitamae.rb
135
136
  - lib/hocho/drivers/ssh_base.rb
136
137
  - lib/hocho/host.rb
137
138
  - lib/hocho/inventory.rb
@@ -139,6 +140,7 @@ files:
139
140
  - lib/hocho/inventory_providers/base.rb
140
141
  - lib/hocho/inventory_providers/file.rb
141
142
  - lib/hocho/property_providers.rb
143
+ - lib/hocho/property_providers/add_default.rb
142
144
  - lib/hocho/property_providers/base.rb
143
145
  - lib/hocho/runner.rb
144
146
  - lib/hocho/utils/finder.rb
@@ -161,12 +163,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
161
163
  version: '0'
162
164
  required_rubygems_version: !ruby/object:Gem::Requirement
163
165
  requirements:
164
- - - ">"
166
+ - - ">="
165
167
  - !ruby/object:Gem::Version
166
- version: 1.3.1
168
+ version: '0'
167
169
  requirements: []
168
170
  rubyforge_project:
169
- rubygems_version: 2.6.3
171
+ rubygems_version: 2.6.8
170
172
  signing_key:
171
173
  specification_version: 4
172
174
  summary: Server provisioning tool with itamae