hocho 0.1.0.beta1 → 0.2.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: 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