kondate 0.2.1 → 0.3.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: 412540d569ef8a803833d569dc21818df84581db
4
- data.tar.gz: 3ed435ac4a9113b348453a19029ab47bb1f56550
3
+ metadata.gz: cee86599c07e6b8ab7d455f0547e1332be08ce04
4
+ data.tar.gz: 644a9d48e73a01679e55bec6fae04d8d074b2cb2
5
5
  SHA512:
6
- metadata.gz: f137334098009c2d3ba28cd6a3f05fcad3638a5b2c3e8ae245f6126976c2cdd2a5817aed3c7e15fff0fbadc4e969a7776accb180d0c9ab201f9ce0efca844334
7
- data.tar.gz: 4492126051228c4e52d86350bd5c023eadfdaa1db84160f9098feecc950f5e8ae3111bd8ab27792f950204b68e779506569ce2d05781bba28c18a21f3ee19d81
6
+ metadata.gz: 330c1056afd6e8ee5723d61f1bacd39bb65357b37f5a44c37be195b16ea5775d3110f23661e06fb5f0386abab07e3f99baefd6f9b97f913cfa598fc67203c6bf
7
+ data.tar.gz: f832e67244d4326b7cbc381f66062a4ca0f27b9755683a24ea7c879f585608404ce2bd4a3b7c938a2baa7fd23455fa6513dc419c75efef092d439a17a73438e7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 0.3.0 (2016-11-18)
2
+
3
+ Enhancements:
4
+
5
+ * Add itamae-role and serverspec-role subcommands to run for multiple hosts in parallel
6
+
1
7
  # 0.2.1 (2016-11-14)
2
8
 
3
9
  Fixes:
data/README.md CHANGED
@@ -50,12 +50,12 @@ $ bundle exec kondate serverspec <host>
50
50
  ├── bootstrap.rb # itamae bootstrap
51
51
  ├── hosts.yml # manages hostnames and its roles
52
52
  ├── properties # manages run_lists and attributes
53
- │   ├── nodes # host specific properties
53
+ │   ├── nodes # host specific properties (deprecated)
54
54
  │   ├── roles # role properties
55
55
  │   └── environments # environment properties
56
56
  ├── secrets # manages secrets attributes such as passwords
57
57
  │   └── properties
58
- │   ├── nodes
58
+ │   ├── nodes (deprecated)
59
59
  │   ├── roles
60
60
  │   └── environments
61
61
  ├── recipes # itamae recipes
@@ -76,13 +76,13 @@ The default .kondate.conf looks like below:
76
76
 
77
77
  ```
78
78
  middlware_recipes_dir: recipes/middleware
79
- roles_recipes_dir: recipes/roles
79
+ roles_recipes_dir: recipes/roles (deprecated)
80
80
  middleware_recipes_serverspec_dir: spec/middleware
81
81
  roles_recipes_serverspec_dir: spec/roles
82
- nodes_properties_dir: properties/nodes
82
+ nodes_properties_dir: properties/nodes (deprecated)
83
83
  roles_properties_dir: properties/roles
84
84
  environments_properties_dir: properties/environments
85
- secret_nodes_properties_dir: secrets/properties/nodes
85
+ secret_nodes_properties_dir: secrets/properties/nodes (deprecated)
86
86
  secret_roles_properties_dir: secrets/properties/roles
87
87
  secret_environments_properties_dir: secrets/properties/environments
88
88
  plugin_dir: lib
@@ -174,7 +174,7 @@ Secret properties are places to write confidential attributes.
174
174
  ```
175
175
  ├── secrets # manages secrets attributes such as passwords
176
176
  │   └── properties
177
- │   ├── nodes
177
+ │   ├── nodes (deprecated)
178
178
  │   ├── roles
179
179
  │   └── environments
180
180
  ```
@@ -258,7 +258,7 @@ See [templates/spec/spec_helper.rb](./lib/kondate/templates/spec/spec_helper.rb)
258
258
 
259
259
  ## Host Plugin
260
260
 
261
- The default reads `hosts.yml` to resolve roles of a host, but
261
+ The default reads `hosts.yml` to resolve roles of a host, but
262
262
  you may want to resolve roles from AWS EC2 `roles` tag, or
263
263
  you may want to resolve roles from your own host resolver API application.
264
264
 
@@ -299,6 +299,15 @@ module Kondate
299
299
  super
300
300
  raise ConfigError.new('file: path is not configured') unless config.path
301
301
  @path = config.path
302
+
303
+ @roles_of_hosts = YAML.load_file(@path)
304
+ @hosts_of_roles = {}
305
+ @roles_of_hosts.each do |host, roles|
306
+ roles.each do |role|
307
+ @hosts_of_roles[role] ||= []
308
+ @hosts_of_roles[role] << host
309
+ end
310
+ end
302
311
  end
303
312
 
304
313
  # @param [String] host hostname
@@ -310,7 +319,15 @@ module Kondate
310
319
  # @param [String] host hostname
311
320
  # @return [Array] array of roles
312
321
  def get_roles(host)
313
- YAML.load_file(@path)[host]
322
+ @roles_of_hosts[host]
323
+ end
324
+
325
+ # @param [String] role role
326
+ # @return [Array] array of hosts
327
+ #
328
+ # Available from kondate >= 0.3.0
329
+ def get_hosts(role)
330
+ @hosts_of_roles[role]
314
331
  end
315
332
 
316
333
  # Optional
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ CMD = File.join(File.dirname(__FILE__), 'kondate')
4
+ if ARGV.size == 0 || ARGV[0] == "-h" || ARGV[0] == "--help"
5
+ exec CMD, 'help', 'itamae-role'
6
+ else
7
+ exec CMD, 'itamae-role', *ARGV
8
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ CMD = File.join(File.dirname(__FILE__), 'kondate')
4
+ if ARGV.size == 0 || ARGV[0] == "-h" || ARGV[0] == "--help"
5
+ exec CMD, 'help', 'serverspec-role'
6
+ else
7
+ exec CMD, 'serverspec-role', *ARGV
8
+ end
data/kondate.gemspec CHANGED
@@ -23,8 +23,10 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency 'serverspec'
24
24
  spec.add_dependency 'thor'
25
25
  spec.add_dependency 'highline'
26
- spec.add_dependency "rake"
26
+ spec.add_dependency 'facter'
27
+ spec.add_dependency 'parallel'
27
28
 
28
29
  spec.add_development_dependency "bundler"
29
30
  spec.add_development_dependency "test-unit"
31
+ spec.add_development_dependency "rake"
30
32
  end
data/lib/kondate/cli.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  require 'thor'
2
2
  require 'yaml'
3
3
  require 'net/ssh'
4
- require 'rspec/core/rake_task'
5
4
  require "highline/import"
6
5
  require_relative '../kondate'
7
6
  require 'fileutils'
8
7
  require 'shellwords'
9
8
  require 'find'
9
+ require 'facter'
10
+ require 'parallel'
10
11
 
11
12
  module Kondate
12
13
  class CLI < Thor
@@ -55,11 +56,83 @@ module Kondate
55
56
  option :profile, :type => :string, :default => nil, :desc => "[EXPERIMENTAL] Save profiling data", :banner => "PATH"
56
57
  option :recipe_graph, :type => :string, :default => nil, :desc => "[EXPERIMENTAL] Write recipe dependency graph in DOT", :banner => "PATH"
57
58
  def itamae(host)
58
- builder, property_files = build_property_files(host)
59
+ property_files = build_property_files(host)
60
+ if proceed?(property_files)
61
+ exit(-1) unless do_itamae(host, property_files)
62
+ end
63
+ end
64
+
65
+ desc "itamae-role <role>", "Execute itamae for multiple hosts in the role"
66
+ option :role, :type => :array, :default => []
67
+ option :recipe, :type => :array, :default => []
68
+ option :debug, :aliases => ["-d"], :type => :boolean, :default => false
69
+ option :confirm, :type => :boolean, :default => true
70
+ option :vagrant, :type => :boolean, :default => false
71
+ option :profile, :type => :string, :default => nil, :desc => "[EXPERIMENTAL] Save profiling data", :banner => "PATH"
72
+ option :recipe_graph, :type => :string, :default => nil, :desc => "[EXPERIMENTAL] Write recipe dependency graph in DOT", :banner => "PATH"
73
+ option :parallel, :aliases => ["-p"], :type => :numeric, :default => Facter['processorcount'].value.to_i
74
+ def itamae_role(role)
75
+ $stdout.puts "Number of parallels is #{@options[:parallel]}"
76
+ hosts = Kondate::Config.host_plugin.get_hosts(role)
77
+ if hosts.nil? or hosts.empty?
78
+ $stderr.puts 'No host'
79
+ exit(1)
80
+ end
81
+ $stdout.puts "Target hosts are [#{hosts.join(", ")}]"
82
+
83
+ property_files_of_hosts, summarized_property_files, hosts_of_roles = build_property_files_of_hosts(hosts)
84
+ if proceed?(summarized_property_files, hosts_of_roles)
85
+ successes = Parallel.map(hosts, in_processes: @options[:parallel]) do |host|
86
+ do_itamae(host, property_files_of_hosts[host])
87
+ end
88
+ exit(-1) unless successes.all?
89
+ end
90
+ end
59
91
 
92
+ desc "serverspec <host>", "Execute serverspec"
93
+ option :role, :type => :array, :default => []
94
+ option :recipe, :type => :array, :default => []
95
+ option :debug, :aliases => ["-d"], :type => :boolean, :default => false
96
+ option :confirm, :type => :boolean, :default => true
97
+ option :vagrant, :type => :boolean, :default => false
98
+ def serverspec(host)
99
+ property_files = build_property_files(host)
100
+ if proceed?(property_files)
101
+ exit(-1) unless do_serverspec(host, property_files)
102
+ end
103
+ end
104
+
105
+ desc "serverspec-role <role>", "Execute serverspec for multiple hosts in the role"
106
+ option :role, :type => :array, :default => []
107
+ option :recipe, :type => :array, :default => []
108
+ option :debug, :aliases => ["-d"], :type => :boolean, :default => false
109
+ option :confirm, :type => :boolean, :default => true
110
+ option :vagrant, :type => :boolean, :default => false
111
+ option :parallel, :aliases => ["-p"], :type => :numeric, :default => Facter['processorcount'].value.to_i
112
+ def serverspec_role(role)
113
+ $stdout.puts "Number of parallels is #{@options[:parallel]}"
114
+ hosts = Kondate::Config.host_plugin.get_hosts(role)
115
+ if hosts.nil? or hosts.empty?
116
+ $stderr.puts 'No host'
117
+ exit(1)
118
+ end
119
+ $stdout.puts "Target hosts are [#{hosts.join(", ")}]"
120
+
121
+ property_files_of_hosts, summarized_property_files, hosts_of_roles = build_property_files_of_hosts(hosts)
122
+ if proceed?(summarized_property_files, hosts_of_roles)
123
+ successes = Parallel.map(hosts, in_processes: @options[:parallel]) do |host|
124
+ do_serverspec(host, property_files_of_hosts[host])
125
+ end
126
+ exit(-1) unless successes.all?
127
+ end
128
+ end
129
+
130
+ private
131
+
132
+ def do_itamae(host, property_files)
133
+ ENV['RUBYOPT'] = "-I #{Config.plugin_dir} -r bundler/setup -r ext/itamae/kondate"
60
134
  property_files.each do |role, property_file|
61
- ENV['TARGET_HOST'] = host
62
- ENV['RUBYOPT'] = "-I #{Config.plugin_dir} -r bundler/setup -r ext/itamae/kondate"
135
+ next if property_file.nil?
63
136
  command = "bundle exec itamae ssh"
64
137
  command << " -h #{host}"
65
138
 
@@ -81,70 +154,97 @@ module Kondate
81
154
  command << " --recipe-graph=#{@options[:recipe_graph]}" if @options[:recipe_graph]
82
155
  command << " bootstrap.rb"
83
156
  $stdout.puts command
84
- exit(-1) unless system(command)
157
+ return false unless system(command)
85
158
  end
159
+ true
86
160
  end
87
161
 
88
- desc "serverspec <host>", "Execute serverspec"
89
- option :role, :type => :array, :default => []
90
- option :recipe, :type => :array, :default => []
91
- option :debug, :aliases => ["-d"], :type => :boolean, :default => false
92
- option :confirm, :type => :boolean, :default => true
93
- option :vagrant, :type => :boolean, :default => false
94
- def serverspec(host)
95
- builder, property_files = build_property_files(host)
96
-
162
+ def do_serverspec(host, property_files)
97
163
  ENV['RUBYOPT'] = "-I #{Config.plugin_dir} -r bundler/setup -r ext/serverspec/kondate"
98
164
  ENV['TARGET_VAGRANT'] = '1' if @options[:vagrant]
99
165
  property_files.each do |role, property_file|
100
- RSpec::Core::RakeTask.new([host, role].join(':'), :recipe) do |t, args|
101
- ENV['TARGET_HOST'] = host
102
-
103
- ENV['TARGET_NODE_FILE'] = property_file
104
- recipes = YAML.load_file(property_file)['attributes'].keys.map {|recipe|
105
- File.join(Config.middleware_recipes_serverspec_dir, recipe)
106
- }.compact
107
- recipes << File.join(Config.roles_recipes_serverspec_dir, role)
108
- t.pattern = '{' + recipes.join(',') + '}_spec.rb'
109
- end
110
-
111
- Rake::Task["#{host}:#{role}"].invoke(@options[:recipe])
166
+ next if property_file.nil?
167
+ recipes = YAML.load_file(property_file)['attributes'].keys.map {|recipe|
168
+ File.join(Config.middleware_recipes_serverspec_dir, recipe)
169
+ }.compact
170
+ recipes << File.join(Config.roles_recipes_serverspec_dir, role)
171
+ spec_files = recipes.map {|recipe| "#{recipe}_spec.rb"}.select! {|spec| File.exist?(spec) }
172
+
173
+ command = "TARGET_HOST=#{host.shellescape} TARGET_NODE_FILE=#{property_file.shellescape} bundle exec rspec"
174
+ command << " #{spec_files.map{|f| f.shellescape }.join(' ')}"
175
+ $stdout.puts command
176
+ return false unless system(command)
112
177
  end
178
+ true
113
179
  end
114
180
 
115
- private
181
+ def proceed?(property_files, hosts_of_roles = {})
182
+ print_property_files(property_files, hosts_of_roles)
183
+ if property_files.values.compact.empty?
184
+ $stderr.puts "Nothing to run"
185
+ false
186
+ elsif @options[:confirm]
187
+ prompt = ask "Proceed? (y/n):"
188
+ prompt == 'y'
189
+ else
190
+ true
191
+ end
192
+ end
116
193
 
117
- def build_property_files(host)
118
- builder = PropertyBuilder.new(host)
119
- roles = builder.filter_roles(@options[:role])
194
+ def print_property_files(property_files, hosts_of_roles = {})
195
+ roles = property_files.keys
120
196
  if roles.nil? or roles.empty?
121
197
  $stderr.puts 'No role'
122
- exit(1)
198
+ return
123
199
  end
124
- $stdout.puts "roles: [#{roles.join(', ')}]"
200
+ $stdout.puts "Show property files for roles: [#{roles.join(", ")}]"
201
+
202
+ property_files.each do |role, property_file|
203
+ hosts = hosts_of_roles[role]
204
+ if hosts.nil? # itamae
205
+ $stdout.print "Show property file for role: #{role}"
206
+ else # itamae_role
207
+ $stdout.print "Show representative property file for role: #{role}"
208
+ $stdout.print " [#{hosts.join(", ")}]"
209
+ end
210
+
211
+ if property_file
212
+ $stdout.puts
213
+ $stdout.puts mask_secrets(File.read(property_file))
214
+ else
215
+ $stdout.puts " (does not exist, skipped)"
216
+ end
217
+ end
218
+ end
219
+
220
+ # @return [Hash] key value pairs whoses keys are roles and values are path (or nil)
221
+ def build_property_files(host)
222
+ builder = PropertyBuilder.new(host)
223
+ roles = builder.filter_roles(@options[:role])
125
224
 
126
225
  property_files = {}
127
226
  roles.each do |role|
128
227
  if path = builder.install(role, @options[:recipe])
129
228
  property_files[role] = path
130
- $stdout.puts "# #{role}"
131
- $stdout.puts mask_secrets(File.read(path))
132
229
  else
133
- $stdout.puts "# #{role} (no attribute, skipped)"
230
+ property_files[role] = nil
134
231
  end
135
232
  end
136
233
 
137
- if property_files.empty?
138
- $stderr.puts "Nothing to run"
139
- exit(1)
140
- end
234
+ property_files
235
+ end
141
236
 
142
- if @options[:confirm]
143
- prompt = ask "Proceed? (y/n):"
144
- exit(0) unless prompt == 'y'
237
+ def build_property_files_of_hosts(hosts)
238
+ summarized_property_files = {}
239
+ property_files_of_hosts = {}
240
+ hosts_of_roles = {}
241
+ hosts.each do |host|
242
+ property_files = build_property_files(host)
243
+ property_files_of_hosts[host] = property_files
244
+ property_files.each {|role, path| summarized_property_files[role] ||= path }
245
+ property_files.each {|role, path| (hosts_of_roles[role] ||= []) << host }
145
246
  end
146
-
147
- [builder, property_files]
247
+ [property_files_of_hosts, summarized_property_files, hosts_of_roles]
148
248
  end
149
249
 
150
250
  def mask_secrets(str)
@@ -20,6 +20,14 @@ module Kondate
20
20
  raise NotImplementedError
21
21
  end
22
22
 
23
+ # @param [String] role role
24
+ # @return [Array] array of hosts
25
+ #
26
+ # Available from kondate >= 0.3.0
27
+ def get_hosts(role)
28
+ raise NotImplementedError
29
+ end
30
+
23
31
  # @param [String] host hostname
24
32
  # @return [Hash] arbitrary host information
25
33
  # def get_hostinfo(host)
@@ -12,6 +12,15 @@ module Kondate
12
12
  super
13
13
  raise ConfigError.new('file: path is not configured') unless config.path
14
14
  @path = config.path
15
+
16
+ @roles_of_hosts = YAML.load_file(@path)
17
+ @hosts_of_roles = {}
18
+ @roles_of_hosts.each do |host, roles|
19
+ roles.each do |role|
20
+ @hosts_of_roles[role] ||= []
21
+ @hosts_of_roles[role] << host
22
+ end
23
+ end
15
24
  end
16
25
 
17
26
  # @param [String] host hostname
@@ -23,7 +32,15 @@ module Kondate
23
32
  # @param [String] host hostname
24
33
  # @return [Array] array of roles
25
34
  def get_roles(host)
26
- YAML.load_file(@path)[host]
35
+ @roles_of_hosts[host]
36
+ end
37
+
38
+ # @param [String] role role
39
+ # @return [Array] array of hosts
40
+ #
41
+ # Available from kondate >= 0.3.0
42
+ def get_hosts(role)
43
+ @hosts_of_roles[role]
27
44
  end
28
45
  end
29
46
  end
@@ -136,9 +136,10 @@ module Kondate
136
136
  if property['attributes'].empty?
137
137
  nil
138
138
  else
139
- Tempfile.open("kondate_") do |fp|
140
- YAML.dump(property, fp)
141
- end.path
139
+ fp = Tempfile.open("kondate_")
140
+ YAML.dump(property, fp)
141
+ fp.close
142
+ fp.path # should be removed when process finishes
142
143
  end
143
144
  end
144
145
  end
@@ -1,3 +1,3 @@
1
1
  module Kondate
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kondate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sonots
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-14 00:00:00.000000000 Z
11
+ date: 2016-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: itamae
@@ -67,7 +67,21 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rake
70
+ name: facter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: parallel
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - ">="
@@ -108,13 +122,29 @@ dependencies:
108
122
  - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
111
139
  description: Kondate is yet another nodes management framework for Itamae/Serverspec.
112
140
  email:
113
141
  - sonots@gmail.com
114
142
  executables:
115
143
  - itamae-kondate
144
+ - itamae-kondate-role
116
145
  - kondate
117
146
  - serverspec-kondate
147
+ - serverspec-kondate-role
118
148
  extensions: []
119
149
  extra_rdoc_files: []
120
150
  files:
@@ -130,8 +160,10 @@ files:
130
160
  - bin/console
131
161
  - bin/setup
132
162
  - exe/itamae-kondate
163
+ - exe/itamae-kondate-role
133
164
  - exe/kondate
134
165
  - exe/serverspec-kondate
166
+ - exe/serverspec-kondate-role
135
167
  - kondate.gemspec
136
168
  - lib/ext/itamae/attributes.rb
137
169
  - lib/ext/itamae/kondate.rb
@@ -183,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
215
  version: '0'
184
216
  requirements: []
185
217
  rubyforge_project:
186
- rubygems_version: 2.5.1
218
+ rubygems_version: 2.5.2
187
219
  signing_key:
188
220
  specification_version: 4
189
221
  summary: Kondate is yet another nodes management framework for Itamae/Serverspec.