chake 0.19 → 0.80

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.ackrc +1 -0
  3. data/.gitignore +2 -0
  4. data/.gitlab-ci.yml +21 -9
  5. data/.manifest +63 -0
  6. data/.rubocop.yml +53 -0
  7. data/.rubocop_todo.yml +40 -0
  8. data/ChangeLog.md +36 -0
  9. data/README.chef.md +70 -0
  10. data/README.itamae.md +58 -0
  11. data/README.md +124 -85
  12. data/README.shell.md +30 -0
  13. data/Rakefile +40 -13
  14. data/bin/chake +12 -1
  15. data/chake.gemspec +16 -16
  16. data/examples/test/.ssh_config +4 -0
  17. data/examples/test/Rakefile +1 -1
  18. data/examples/test/Vagrantfile +6 -0
  19. data/examples/test/config.rb +4 -4
  20. data/examples/test/cookbooks/basics/recipes/default.rb +1 -0
  21. data/examples/test/cookbooks/example/files/default/test +1 -0
  22. data/examples/test/cookbooks/example/files/{host-homer → host-lemur}/test.asc +0 -0
  23. data/lib/chake.rb +92 -168
  24. data/lib/chake/bootstrap/{01_debian.sh → chef/01_debian.sh} +0 -0
  25. data/lib/chake/bootstrap/{99_unsupported.sh → chef/99_unsupported.sh} +0 -0
  26. data/lib/chake/config.rb +16 -0
  27. data/lib/chake/config_manager.rb +93 -0
  28. data/lib/chake/config_manager/chef.rb +35 -0
  29. data/lib/chake/config_manager/itamae.rb +58 -0
  30. data/lib/chake/config_manager/shell.rb +34 -0
  31. data/lib/chake/config_manager/skel/chef/Rakefile +1 -0
  32. data/lib/chake/config_manager/skel/chef/config.rb +4 -0
  33. data/lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/default.rb +1 -0
  34. data/lib/chake/config_manager/skel/chef/nodes.yaml +3 -0
  35. data/lib/chake/config_manager/skel/itamae/Rakefile +1 -0
  36. data/lib/chake/config_manager/skel/itamae/cookbooks/basics/default.rb +1 -0
  37. data/lib/chake/config_manager/skel/itamae/nodes.yaml +3 -0
  38. data/lib/chake/config_manager/skel/itamae/roles/basic.rb +1 -0
  39. data/lib/chake/config_manager/skel/shell/Rakefile +1 -0
  40. data/lib/chake/config_manager/skel/shell/nodes.yaml +3 -0
  41. data/lib/chake/connection.rb +83 -0
  42. data/lib/chake/{backend → connection}/local.rb +2 -8
  43. data/lib/chake/{backend → connection}/ssh.rb +6 -14
  44. data/lib/chake/node.rb +49 -29
  45. data/lib/chake/readline.rb +6 -10
  46. data/lib/chake/version.rb +1 -1
  47. data/man/Rakefile +27 -14
  48. data/man/readme2man.sed +5 -5
  49. data/spec/chake/backend/local_spec.rb +5 -6
  50. data/spec/chake/backend/ssh_spec.rb +8 -10
  51. data/spec/chake/backend_spec.rb +1 -2
  52. data/spec/chake/config_manager/chef_spec.rb +38 -0
  53. data/spec/chake/config_manager/itamae_spec.rb +69 -0
  54. data/spec/chake/config_manager/shell_spec.rb +54 -0
  55. data/spec/chake/config_manager_spec.rb +24 -0
  56. data/spec/chake/node_spec.rb +38 -15
  57. data/spec/spec_helper.rb +23 -19
  58. metadata +61 -14
  59. data/lib/chake/backend.rb +0 -78
@@ -0,0 +1,30 @@
1
+ chake-shell(7) -- configure chake nodes with shell
2
+ ==================================================
3
+
4
+ ## Description
5
+
6
+ This configuration manager is a simpler wrapper for running a list of shell
7
+ commands on the nodes.
8
+
9
+ ## Configuration
10
+
11
+ The _shell_ configuration manager requires one key called `shell`, and the
12
+ value must be a list of strings representing the list of commands to run on the
13
+ node when converging.
14
+
15
+ ```yaml
16
+ host1.mycompany.com:
17
+ shell:
18
+ - echo "HELLO WORLD"
19
+ ```
20
+
21
+ ## Bootstrapping
22
+
23
+ Very little bootstrapping is required for this configuration manager, as we
24
+ hope every node you could possibly want to manage with it already has a POSIX
25
+ shell as `/bin/sh`. During bootstrapping, only the node hostname will be set
26
+ according to your chake configuration.
27
+
28
+ ## See also
29
+
30
+ * **chake(1)**
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
1
  namespace :bundler do
2
- require "bundler/gem_tasks"
2
+ require 'bundler/gem_tasks'
3
3
  end
4
4
 
5
5
  task :test do
@@ -8,6 +8,8 @@ end
8
8
 
9
9
  pkg = Gem::Specification.load('chake.gemspec')
10
10
 
11
+ task 'bundler:build' => :manifest
12
+
11
13
  task 'build:tarball' => 'bundler:build' do
12
14
  chdir 'pkg' do
13
15
  sh 'gem2tgz', "#{pkg.name}-#{pkg.version}.gem"
@@ -17,22 +19,25 @@ end
17
19
  desc 'Create Debian source package'
18
20
  task 'build:debsrc' => ['bundler:clobber', 'build:tarball'] do
19
21
  dirname = "#{pkg.name}-#{pkg.version}"
22
+ v = `git describe`.strip.tr('-', '.').sub(/^v/, '')
20
23
  chdir 'pkg' do
21
24
  sh 'gem2deb', '--no-wnpp-check', '-s', '-p', pkg.name, "#{dirname}.tar.gz"
25
+ sh "rename s/#{pkg.version}/#{v}/ *.orig.tar.gz"
22
26
  chdir dirname do
23
27
  ln 'man/Rakefile', 'debian/dh_ruby.rake'
24
- sh "sed -i -e 's/-1/-1~0/ ; s/\*.*/* Development snapshot/' debian/changelog"
25
- sh 'dpkg-buildpackage', '-S', '-us', '-uc'
28
+ sh "dch --preserve -v #{v}-1 'Development snapshot'"
29
+ sh "sed -i -e 's/#{pkg.version}/#{v}/' lib/chake/version.rb"
30
+ sh 'dpkg-buildpackage', '--diff-ignore=version.rb', '-S', '-us', '-uc'
26
31
  end
27
32
  end
28
33
  end
29
34
 
30
35
  desc 'Builds and installs Debian package'
31
- task 'deb:install' => 'build:debsrc'do
36
+ task 'deb:install' => 'build:debsrc' do
32
37
  chdir "pkg/#{pkg.name}-#{pkg.version}" do
33
- sh 'fakeroot debian/rules binary'
38
+ sh 'dpkg-buildpackage --diff-ignore=version.rb -us -uc'
39
+ sh 'debi'
34
40
  end
35
- sh 'sudo', 'dpkg', '-i', "pkg/#{pkg.name}_#{pkg.version}-1~0_all.deb"
36
41
  end
37
42
 
38
43
  desc 'Create source RPM package'
@@ -45,7 +50,7 @@ end
45
50
  file 'pkg/chake.spec' => ['chake.spec.erb', 'lib/chake/version.rb'] do |t|
46
51
  require 'erb'
47
52
  pkg = Gem::Specification.load('chake.gemspec')
48
- template = ERB.new(File.read('chake.spec.erb'))
53
+ template = ERB.new(File.read('chake.spec.erb'))
49
54
  File.open(t.name, 'w') do |f|
50
55
  f.puts(template.result(binding))
51
56
  end
@@ -57,27 +62,49 @@ task 'build:all' => ['build:debsrc', 'build:rpmsrc']
57
62
  desc 'lists changes since last release'
58
63
  task :changelog do
59
64
  last_tag = `git tag | sort -V`.split.last
60
- sh 'git', 'shortlog', last_tag + '..'
65
+ sh 'git', 'shortlog', "#{last_tag}.."
61
66
  end
62
67
 
63
68
  task :check_tag do
64
69
  last_tag = `git tag | sort -V`.split.last
65
70
  if last_tag == "v#{pkg.version}"
66
- fail "Version #{pkg.version} was already released!"
71
+ raise "Version #{pkg.version} was already released!"
67
72
  end
68
73
  end
69
74
 
70
75
  desc 'checks if the latest release is properly documented in ChangeLog.md'
71
76
  task :check_changelog do
72
77
  begin
73
- sh 'grep', '^#\s*' + pkg.version.to_s, 'ChangeLog.md'
74
- rescue
78
+ sh 'grep', "^#\\s*#{pkg.version}", 'ChangeLog.md'
79
+ rescue StandardError
75
80
  puts "Version #{pkg.version} not documented in ChangeLog.md!"
76
81
  raise
77
82
  end
78
83
  end
79
84
 
85
+ desc 'Updates manifest file'
86
+ task :manifest do
87
+ manifest = File.read('.manifest')
88
+ git = `git ls-files`
89
+ if manifest != git
90
+ File.open('.manifest', 'w') { |f| f.write(git) }
91
+ sh 'git commit .manifest -m "Update manifest"'
92
+ end
93
+ end
94
+
80
95
  desc 'Makes a release'
81
- task :release => [:check_tag, :check_changelog, :test, 'bundler:release']
96
+ task release: [:check_tag, :check_changelog, :test, 'bundler:release']
97
+
98
+ desc 'Check coding style'
99
+ task :style do
100
+ sh 'rubocop'
101
+ end
102
+
103
+ desc 'Check spelling in the source code'
104
+ task :codespell do
105
+ sh 'codespell', '--skip=.git', '--skip=coverage', '--skip=*.asc', '--skip=*.swp'
106
+ end
107
+
108
+ task default: [:test, :style, :codespell]
82
109
 
83
- task :default => :test
110
+ load './man/Rakefile'
data/bin/chake CHANGED
@@ -4,7 +4,7 @@ require 'rake'
4
4
 
5
5
  rakefiles = %w[rakefile Rakefile rakefile.rb Rakefile.rb]
6
6
 
7
- if (!rakefiles.any? { |f| File.exist?(f) }) && !ARGV.include?('-f') && !ARGV.include?('--rakefile')
7
+ if rakefiles.none? { |f| File.exist?(f) } && !ARGV.include?('-f') && !ARGV.include?('--rakefile')
8
8
  require 'tmpdir'
9
9
  require 'fileutils'
10
10
 
@@ -22,4 +22,15 @@ if (!rakefiles.any? { |f| File.exist?(f) }) && !ARGV.include?('-f') && !ARGV.inc
22
22
  end
23
23
  end
24
24
 
25
+ class Rake::Application
26
+ alias orig_thread_pool thread_pool
27
+ def thread_pool # :nodoc:
28
+ if Chake.respond_to?(:nodes)
29
+ @thread_pool ||= Rake::ThreadPool.new(Chake.nodes.size + 1)
30
+ else
31
+ orig_thread_pool
32
+ end
33
+ end
34
+ end
35
+
25
36
  Rake.application.run
@@ -1,27 +1,27 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'chake/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "chake"
6
+ spec.name = 'chake'
8
7
  spec.version = Chake::VERSION
9
- spec.authors = ["Antonio Terceiro"]
10
- spec.email = ["terceiro@softwarelivre.org"]
11
- spec.summary = %q{serverless configuration management tool for chef}
12
- spec.description = %q{chake allows one to manage a number of hosts via SSH by combining chef (solo) and rake. It doesn't require a chef server; all you need is a workstation from where you can SSH into all your hosts. chake automates copying the configuration management repository to the target host (including managing encrypted files), running chef on them, and running arbitrary commands on the hosts.}
13
- spec.homepage = "https://gitlab.com/terceiro/chake"
14
- spec.license = "MIT"
8
+ spec.authors = ['Antonio Terceiro']
9
+ spec.email = ['terceiro@softwarelivre.org']
10
+ spec.summary = 'serverless configuration management tool for chef'
11
+ spec.description = "chake allows one to manage a number of hosts via SSH by combining chef (solo) and rake. It doesn't require a chef server; all you need is a workstation from where you can SSH into all your hosts. chake automates copying the configuration management repository to the target host (including managing encrypted files), running chef on them, and running arbitrary commands on the hosts."
12
+ spec.homepage = 'https://gitlab.com/terceiro/chake'
13
+ spec.license = 'MIT'
15
14
 
16
- spec.files = `git ls-files -z`.split("\x0")
15
+ spec.files = File.read('.manifest').split("\n") + ['.manifest']
17
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
18
+ spec.require_paths = ['lib']
20
19
 
21
- spec.add_development_dependency "bundler", "~> 1.5"
22
- spec.add_development_dependency "rspec"
23
- spec.add_development_dependency "simplecov"
24
- spec.add_development_dependency "asciidoctor", '>= 1.5.5'
20
+ spec.add_development_dependency 'bundler', '~> 1.5'
21
+ spec.add_development_dependency 'ronn-ng'
22
+ spec.add_development_dependency 'rspec'
23
+ spec.add_development_dependency 'rubocop'
24
+ spec.add_development_dependency 'simplecov'
25
25
 
26
- spec.add_dependency "rake"
26
+ spec.add_dependency 'rake'
27
27
  end
@@ -0,0 +1,4 @@
1
+ Host test.local
2
+ IdentityFile .vagrant/machines/default/libvirt/private_key
3
+
4
+ # vim: ft=sshconfig
@@ -1,4 +1,4 @@
1
- $:.unshift '../../lib' # this shouldn't be needed when you have chake installed
1
+ $LOAD_PATH.unshift '../../lib' # this shouldn't be needed when you have chake installed
2
2
  require 'chake'
3
3
 
4
4
  manifest = %w[
@@ -0,0 +1,6 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ Vagrant.configure('2') do |config|
5
+ config.vm.box = 'debian/buster64'
6
+ end
@@ -1,4 +1,4 @@
1
- root = File.expand_path(File.dirname(__FILE__))
2
- file_cache_path root + '/cache'
3
- cookbook_path root + '/cookbooks'
4
- role_path root + '/config/roles'
1
+ root = __dir__
2
+ file_cache_path "#{root}/cache"
3
+ cookbook_path "#{root}/cookbooks"
4
+ role_path "#{root}/config/roles"
@@ -0,0 +1 @@
1
+ package 'openssh-server'
@@ -1,174 +1,100 @@
1
- # encoding: UTF-8
2
-
3
1
  require 'yaml'
4
2
  require 'json'
5
3
  require 'tmpdir'
6
4
 
5
+ require 'chake/config'
7
6
  require 'chake/version'
8
- require 'chake/node'
9
7
  require 'chake/readline'
10
- require 'chake/tmpdir'
11
-
12
- chef_config = ENV['CHAKE_CHEF_CONFIG'] || 'config.rb'
13
- nodes_file = ENV['CHAKE_NODES'] || 'nodes.yaml'
14
- nodes_directory = ENV['CHAKE_NODES_D'] || 'nodes.d'
15
- node_data = File.exists?(nodes_file) && YAML.load_file(nodes_file) || {}
16
- Dir.glob(File.join(nodes_directory, '*.yaml')).sort.each do |f|
17
- node_data.merge!(YAML.load_file(f))
18
- end
19
- $nodes = node_data.map { |node,data| Chake::Node.new(node, data) }.reject(&:skip?).uniq(&:hostname)
20
- $chake_tmpdir = Chake.tmpdir
21
-
22
- desc "Initializes current directory with sample structure"
23
- task :init do
24
- if File.exists?('nodes.yaml')
25
- puts '[exists] nodes.yaml'
26
- else
27
- File.open('nodes.yaml', 'w') do |f|
28
- sample_nodes = <<EOF
29
- host1.mycompany.com:
30
- run_list:
31
- - recipe[basics]
32
- EOF
33
- f.write(sample_nodes)
34
- puts "[create] nodes.yaml"
35
- end
36
- end
37
8
 
38
- if File.exist?('nodes.d')
39
- puts '[exists] nodes.d/'
40
- else
41
- FileUtils.mkdir_p 'nodes.d'
42
- puts '[ mkdir] nodes.d/'
43
- end
44
-
45
-
46
- if File.exists?('config.rb')
47
- puts '[exists] config.rb'
48
- else
49
- File.open('config.rb', 'w') do |f|
50
- f.puts "root = File.expand_path(File.dirname(__FILE__))"
51
- f.puts "file_cache_path root + '/cache'"
52
- f.puts "cookbook_path root + '/cookbooks'"
53
- f.puts "role_path root + '/config/roles'"
54
- end
55
- puts "[create] config.rb"
56
- end
57
-
58
- if !File.exist?('config/roles')
59
- FileUtils.mkdir_p 'config/roles'
60
- puts '[ mkdir] config/roles'
61
- end
62
- if !File.exist?('cookbooks/basics/recipes')
63
- FileUtils.mkdir_p 'cookbooks/basics/recipes/'
64
- puts '[ mkdir] cookbooks/basics/recipes/'
65
- end
66
- recipe = 'cookbooks/basics/recipes/default.rb'
67
- if File.exists?(recipe)
68
- puts "[exists] #{recipe}"
69
- else
70
- File.open(recipe, 'w') do |f|
71
- f.puts "package 'openssh-server'"
72
- end
73
- puts "[create] #{recipe}"
74
- end
75
- if File.exists?('Rakefile')
76
- puts '[exists] Rakefile'
77
- else
78
- File.open('Rakefile', 'w') do |f|
79
- f.puts 'require "chake"'
80
- puts '[create] Rakefile'
81
- end
9
+ desc 'Initializes current directory with sample structure'
10
+ task init: 'init:itamae'
11
+ Chake::ConfigManager.all.map do |cfgmgr|
12
+ desc "Initializes current directory for #{cfgmgr.short_name}"
13
+ task "init:#{cfgmgr.short_name}" do
14
+ cfgmgr.init
82
15
  end
83
16
  end
84
17
 
85
18
  desc 'list nodes'
86
19
  task :nodes do
87
- $nodes.each do |node|
88
- puts "%-40s %-5s\n" % [node.hostname, node.backend]
20
+ fields = %i[hostname connection config_manager]
21
+ IO.popen(['column', '-t'], mode: 'w') do |table|
22
+ table.puts(fields.join(' '))
23
+ table.puts(fields.map { |f| '-' * f.length }.join(' '))
24
+ Chake.nodes.each do |node|
25
+ table.puts fields.map { |f| node.send(f) }.join(' ')
26
+ end
89
27
  end
90
28
  end
91
29
 
92
30
  def encrypted_for(node)
93
- encrypted_files = Dir.glob("**/files/{default,host-#{node}}/*.{asc,gpg}") + Dir.glob("**/files/*.{asc,gpg}")
94
- encrypted_files.inject({}) do |hash, key|
31
+ encrypted_files = Dir.glob("**/files/{default,host-#{node}}/*.{asc,gpg}") + Dir.glob('**/files/*.{asc,gpg}')
32
+ encrypted_files.each_with_object({}) do |key, hash|
95
33
  hash[key] = key.sub(/\.(asc|gpg)$/, '')
96
- hash
97
34
  end
98
35
  end
99
36
 
100
37
  def if_files_changed(node, group_name, files)
101
- if files.empty?
102
- return
103
- end
104
- hash_io = IO.popen(['xargs', 'sha1sum'], 'w+')
38
+ return if files.empty?
39
+
40
+ hash_io = IO.popen(%w[xargs sha1sum], 'w+')
105
41
  files.sort.each { |f| hash_io.puts(f) }
106
42
  hash_io.close_write
107
43
  current_hash = hash_io.read
108
44
 
109
- hash_file = File.join($chake_tmpdir, node + '.' + group_name + '.sha1sum')
45
+ hash_file = File.join(Chake.tmpdir, "#{node}.#{group_name}.sha1sum")
110
46
  hash_on_disk = nil
111
- if File.exists?(hash_file)
112
- hash_on_disk = File.read(hash_file)
113
- end
47
+ hash_on_disk = File.read(hash_file) if File.exist?(hash_file)
114
48
 
115
- if current_hash != hash_on_disk
116
- yield
117
- end
49
+ yield if current_hash != hash_on_disk
118
50
  FileUtils.mkdir_p(File.dirname(hash_file))
119
51
  File.open(hash_file, 'w') do |f|
120
52
  f.write(current_hash)
121
53
  end
122
54
  end
123
55
 
124
-
125
56
  def write_json_file(file, data)
126
- File.chmod(0600, file) if File.exists?(file)
127
- File.open(file, 'w', 0600) do |f|
57
+ File.chmod(0o600, file) if File.exist?(file)
58
+ File.open(file, 'w', 0o600) do |f|
128
59
  f.write(JSON.pretty_generate(data))
129
60
  f.write("\n")
130
61
  end
131
62
  end
132
63
 
133
- bootstrap_steps = Dir.glob(File.expand_path('chake/bootstrap/*.sh', File.dirname(__FILE__))).sort
134
-
135
64
  desc 'Executed before bootstrapping'
136
- task :bootstrap_common => :connect_common
65
+ task bootstrap_common: :connect_common
137
66
 
138
67
  desc 'Executed before uploading'
139
- task :upload_common => :connect_common
68
+ task upload_common: :connect_common
140
69
 
141
70
  desc 'Executed before uploading'
142
- task :converge_common => :connect_common
71
+ task converge_common: :connect_common
143
72
 
144
73
  desc 'Executed before connecting to any host'
145
74
  task :connect_common
146
75
 
147
- $nodes.each do |node|
76
+ Chake.nodes.each do |node|
77
+ node.silent = Rake.application.options.silent
148
78
 
149
79
  hostname = node.hostname
150
- bootstrap_script = File.join($chake_tmpdir, 'bootstrap-' + hostname)
151
-
152
- file bootstrap_script => bootstrap_steps do |t|
153
- mkdir_p(File.dirname(bootstrap_script))
154
- File.open(t.name, 'w') do |f|
155
- f.puts '#!/bin/sh'
156
- f.puts 'set -eu'
157
- bootstrap_steps.each do |platform|
158
- f.puts(File.read(platform))
159
- end
160
- end
161
- chmod 0755, t.name
162
- end
80
+
81
+ bootstrap_script = File.join(Chake.tmpdir, "#{hostname}.bootstrap")
82
+
83
+ bootstrap_steps = node.bootstrap_steps
84
+
85
+ bootstrap_code = (["#!/bin/sh\n", "set -eu\n"] + bootstrap_steps.map { |f| File.read(f) }).join
163
86
 
164
87
  desc "bootstrap #{hostname}"
165
- task "bootstrap:#{hostname}" => [:bootstrap_common, bootstrap_script] do
166
- config = File.join($chake_tmpdir, hostname + '.json')
88
+ task "bootstrap:#{hostname}" => :bootstrap_common do
89
+ mkdir_p Chake.tmpdir unless File.directory?(Chake.tmpdir)
90
+ if node.needs_bootstrap? && (!File.exist?(bootstrap_script) || File.read(bootstrap_script) != bootstrap_code)
91
+
92
+ # create bootstrap script
93
+ File.open(bootstrap_script, 'w') do |f|
94
+ f.write(bootstrap_code)
95
+ end
96
+ chmod 0o755, bootstrap_script
167
97
 
168
- if File.exists?(config)
169
- # already bootstrapped, just overwrite
170
- write_json_file(config, node.data)
171
- else
172
98
  # copy bootstrap script over
173
99
  scp = node.scp
174
100
  target = "/tmp/.chake-bootstrap.#{Etc.getpwuid.name}"
@@ -176,28 +102,29 @@ $nodes.each do |node|
176
102
 
177
103
  # run bootstrap script
178
104
  node.run_as_root("#{target} #{hostname}")
179
-
180
- # overwrite config with current contents
181
- mkdir_p File.dirname(config)
182
- write_json_file(config, node.data)
183
105
  end
184
106
 
107
+ # overwrite config with current contents
108
+ config = File.join(Chake.tmpdir, "#{hostname}.json")
109
+ write_json_file(config, node.data)
185
110
  end
186
111
 
187
112
  desc "upload data to #{hostname}"
188
113
  task "upload:#{hostname}" => :upload_common do
114
+ next unless node.needs_upload?
115
+
189
116
  encrypted = encrypted_for(hostname)
190
- rsync_excludes = (encrypted.values + encrypted.keys).map { |f| ["--exclude", f] }.flatten
191
- rsync_excludes << "--exclude" << ".git/"
192
- rsync_excludes << "--exclude" << "cache/"
193
- rsync_excludes << "--exclude" << "nodes/"
194
- rsync_excludes << "--exclude" << "local-mode-cache/"
117
+ rsync_excludes = (encrypted.values + encrypted.keys).map { |f| ['--exclude', f] }.flatten
118
+ rsync_excludes << '--exclude' << '.git/'
119
+ rsync_excludes << '--exclude' << 'cache/'
120
+ rsync_excludes << '--exclude' << 'nodes/'
121
+ rsync_excludes << '--exclude' << 'local-mode-cache/'
195
122
 
196
- rsync = node.rsync + ["-avp"] + ENV.fetch('CHAKE_RSYNC_OPTIONS', '').split
123
+ rsync = node.rsync + ['-avp'] + ENV.fetch('CHAKE_RSYNC_OPTIONS', '').split
197
124
  rsync_logging = Rake.application.options.silent && '--quiet' || '--verbose'
198
125
 
199
- hash_files = Dir.glob(File.join($chake_tmpdir, '*.sha1sum'))
200
- files = Dir.glob("**/*").select { |f| !File.directory?(f) } - encrypted.keys - encrypted.values - hash_files
126
+ hash_files = Dir.glob(File.join(Chake.tmpdir, '*.sha1sum'))
127
+ files = Dir.glob('**/*').reject { |f| File.directory?(f) } - encrypted.keys - encrypted.values - hash_files
201
128
  if_files_changed(hostname, 'plain', files) do
202
129
  sh *rsync, '--delete', rsync_logging, *rsync_excludes, './', node.rsync_dest
203
130
  end
@@ -208,14 +135,14 @@ $nodes.each do |node|
208
135
  target = File.join(tmpdir, target_file)
209
136
  mkdir_p(File.dirname(target))
210
137
  rm_f target
211
- File.open(target, 'w', 0400) do |output|
138
+ File.open(target, 'w', 0o400) do |output|
212
139
  IO.popen(['gpg', '--quiet', '--batch', '--use-agent', '--decrypt', encrypted_file]) do |data|
213
140
  output.write(data.read)
214
141
  end
215
142
  end
216
143
  puts "#{target} (decrypted)"
217
144
  end
218
- sh *rsync, rsync_logging, tmpdir + '/', node.rsync_dest
145
+ sh *rsync, rsync_logging, "#{tmpdir}/", node.rsync_dest
219
146
  end
220
147
  end
221
148
  end
@@ -224,19 +151,17 @@ $nodes.each do |node|
224
151
 
225
152
  desc "converge #{hostname}"
226
153
  task "converge:#{hostname}" => converge_dependencies do
227
- chef_logging = Rake.application.options.silent && '-l fatal' || ''
228
- node.run_as_root "chef-solo -c #{node.path}/#{chef_config} #{chef_logging} -j #{node.path}/#{$chake_tmpdir}/#{hostname}.json"
154
+ node.converge
229
155
  end
230
156
 
231
157
  desc 'apply <recipe> on #{hostname}'
232
- task "apply:#{hostname}", [:recipe] => [:recipe_input, :connect_common] do |task, args|
233
- chef_logging = Rake.application.options.silent && '-l fatal' || ''
234
- node.run_as_root "chef-solo -c #{node.path}/#{chef_config} #{chef_logging} -j #{node.path}/#{$chake_tmpdir}/#{hostname}.json --override-runlist recipe[#{$recipe_to_apply}]"
158
+ task "apply:#{hostname}", [:recipe] => %i[recipe_input connect_common] do |_task, _args|
159
+ node.apply($recipe_to_apply)
235
160
  end
236
161
  task "apply:#{hostname}" => converge_dependencies
237
162
 
238
163
  desc "run a command on #{hostname}"
239
- task "run:#{hostname}", [:command] => [:run_input, :connect_common] do
164
+ task "run:#{hostname}", [:command] => %i[run_input connect_common] do
240
165
  node.run($cmd_to_run)
241
166
  end
242
167
 
@@ -247,34 +172,33 @@ $nodes.each do |node|
247
172
 
248
173
  desc 'checks connectivity and setup on all nodes'
249
174
  task "check:#{hostname}" => :connect_common do
250
- node.run('sudo true')
175
+ node.run('sudo echo OK')
251
176
  end
252
-
253
177
  end
254
178
 
255
- task :run_input, :command do |task,args|
179
+ task :run_input, :command do |_task, args|
256
180
  $cmd_to_run = args[:command]
257
- if !$cmd_to_run
258
- puts "# Enter command to run (use arrow keys for history):"
181
+ unless $cmd_to_run
182
+ puts '# Enter command to run (use arrow keys for history):'
259
183
  $cmd_to_run = Chake::Readline::Commands.readline
260
184
  end
261
185
  if !$cmd_to_run || $cmd_to_run.strip == ''
262
186
  puts
263
- puts "I: no command provided, operation aborted."
187
+ puts 'I: no command provided, operation aborted.'
264
188
  exit(1)
265
189
  end
266
190
  end
267
191
 
268
- task :recipe_input, :recipe do |task,args|
192
+ task :recipe_input, :recipe do |_task, args|
269
193
  $recipe_to_apply = args[:recipe]
270
194
 
271
- if !$recipe_to_apply
195
+ unless $recipe_to_apply
272
196
  recipes = Dir['**/*/recipes/*.rb'].map do |f|
273
197
  f =~ %r{(.*/)?(.*)/recipes/(.*).rb$}
274
- cookbook = $2
275
- recipe = $3
198
+ cookbook = Regexp.last_match(2)
199
+ recipe = Regexp.last_match(3)
276
200
  recipe = nil if recipe == 'default'
277
- [cookbook,recipe].compact.join('::')
201
+ [cookbook, recipe].compact.join('::')
278
202
  end.sort
279
203
  puts 'Available recipes:'
280
204
 
@@ -285,48 +209,48 @@ task :recipe_input, :recipe do |task,args|
285
209
  $recipe_to_apply = Chake::Readline::Recipes.readline
286
210
  if !$recipe_to_apply || $recipe_to_apply.empty?
287
211
  puts
288
- puts "I: no recipe provided, operation aborted."
212
+ puts 'I: no recipe provided, operation aborted.'
289
213
  exit(1)
290
214
  end
291
- if !recipes.include?($recipe_to_apply)
215
+ unless recipes.include?($recipe_to_apply)
292
216
  abort "E: no such recipe: #{$recipe_to_apply}"
293
217
  end
294
218
  end
295
219
  end
296
220
 
297
- desc "upload to all nodes"
298
- multitask :upload => $nodes.map { |node| "upload:#{node.hostname}" }
221
+ desc 'upload to all nodes'
222
+ multitask upload: Chake.nodes.map { |node| "upload:#{node.hostname}" }
299
223
 
300
- desc "bootstrap all nodes"
301
- multitask :bootstrap => $nodes.map { |node| "bootstrap:#{node.hostname}" }
224
+ desc 'bootstrap all nodes'
225
+ multitask bootstrap: Chake.nodes.map { |node| "bootstrap:#{node.hostname}" }
302
226
 
303
- desc "converge all nodes (default)"
304
- multitask "converge" => $nodes.map { |node| "converge:#{node.hostname}" }
227
+ desc 'converge all nodes (default)'
228
+ multitask 'converge' => Chake.nodes.map { |node| "converge:#{node.hostname}" }
305
229
 
306
- desc "Apply <recipe> on all nodes"
307
- multitask "apply", [:recipe] => $nodes.map { |node| "apply:#{node.hostname}" }
230
+ desc 'Apply <recipe> on all nodes'
231
+ multitask 'apply', [:recipe] => Chake.nodes.map { |node| "apply:#{node.hostname}" }
308
232
 
309
- desc "run <command> on all nodes"
310
- multitask :run, [:command] => $nodes.map { |node| "run:#{node.hostname}" }
233
+ desc 'run <command> on all nodes'
234
+ multitask :run, [:command] => Chake.nodes.map { |node| "run:#{node.hostname}" }
311
235
 
312
- task :default => :converge
236
+ task default: :converge
313
237
 
314
238
  desc 'checks connectivity and setup on all nodes'
315
- multitask :check => ($nodes.map { |node| "check:#{node.hostname}" }) do
316
- puts "✓ all hosts OK"
317
- puts " - ssh connection works"
318
- puts " - password-less sudo works"
239
+ multitask check: (Chake.nodes.map { |node| "check:#{node.hostname}" }) do
240
+ puts '✓ all hosts OK'
241
+ puts ' - ssh connection works'
242
+ puts ' - password-less sudo works'
319
243
  end
320
244
 
321
245
  desc 'runs a Ruby console in the chake environment'
322
246
  task :console do
323
247
  require 'irb'
324
- IRB.setup(eval("__FILE__"), argv: [])
248
+ IRB.setup('__FILE__', argv: [])
325
249
  workspace = IRB::WorkSpace.new(self)
326
250
 
327
251
  puts 'chake - interactive console'
328
252
  puts '---------------------------'
329
- puts 'all node data in available in $nodes'
253
+ puts 'all node data in available in Chake.nodes'
330
254
  puts
331
255
  IRB::Irb.new(workspace).run(IRB.conf)
332
256
  end