kondate 0.2.1 → 0.3.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: 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.