chake 0.20 → 0.81

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.ackrc +2 -0
  3. data/.gitignore +22 -0
  4. data/.gitlab-ci.yml +24 -0
  5. data/.manifest +65 -0
  6. data/.rubocop.yml +55 -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 +118 -85
  12. data/README.shell.md +30 -0
  13. data/Rakefile +36 -10
  14. data/bin/chake +2 -2
  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 +110 -154
  24. data/lib/chake/bootstrap/chef/01_installed.sh +4 -0
  25. data/lib/chake/bootstrap/{01_debian.sh → chef/02_debian.sh} +0 -0
  26. data/lib/chake/bootstrap/{99_unsupported.sh → chef/99_unsupported.sh} +0 -0
  27. data/lib/chake/config.rb +2 -7
  28. data/lib/chake/config_manager.rb +89 -0
  29. data/lib/chake/config_manager/chef.rb +35 -0
  30. data/lib/chake/config_manager/itamae.rb +57 -0
  31. data/lib/chake/config_manager/shell.rb +34 -0
  32. data/lib/chake/config_manager/skel/chef/Rakefile +1 -0
  33. data/lib/chake/config_manager/skel/chef/config.rb +4 -0
  34. data/lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/default.rb +1 -0
  35. data/lib/chake/config_manager/skel/chef/nodes.yaml +3 -0
  36. data/lib/chake/config_manager/skel/itamae/Rakefile +1 -0
  37. data/lib/chake/config_manager/skel/itamae/cookbooks/basics/default.rb +1 -0
  38. data/lib/chake/config_manager/skel/itamae/nodes.yaml +3 -0
  39. data/lib/chake/config_manager/skel/itamae/roles/basic.rb +1 -0
  40. data/lib/chake/config_manager/skel/shell/Rakefile +1 -0
  41. data/lib/chake/config_manager/skel/shell/nodes.yaml +3 -0
  42. data/lib/chake/connection.rb +83 -0
  43. data/lib/chake/{backend → connection}/local.rb +2 -8
  44. data/lib/chake/{backend → connection}/ssh.rb +6 -14
  45. data/lib/chake/node.rb +49 -29
  46. data/lib/chake/readline.rb +6 -10
  47. data/lib/chake/version.rb +1 -1
  48. data/lib/chake/wipe.rb +18 -0
  49. data/man/.gitignore +2 -0
  50. data/man/Rakefile +28 -14
  51. data/man/readme2man.sed +5 -5
  52. data/spec/chake/backend/local_spec.rb +5 -6
  53. data/spec/chake/backend/ssh_spec.rb +8 -10
  54. data/spec/chake/backend_spec.rb +1 -2
  55. data/spec/chake/config_manager/chef_spec.rb +38 -0
  56. data/spec/chake/config_manager/itamae_spec.rb +87 -0
  57. data/spec/chake/config_manager/shell_spec.rb +54 -0
  58. data/spec/chake/config_manager_spec.rb +23 -0
  59. data/spec/chake/node_spec.rb +38 -15
  60. data/spec/spec_helper.rb +37 -17
  61. metadata +65 -39
  62. data/coverage/assets/0.10.0/application.css +0 -799
  63. data/coverage/assets/0.10.0/application.js +0 -34783
  64. data/coverage/assets/0.10.0/colorbox/border.png +0 -0
  65. data/coverage/assets/0.10.0/colorbox/controls.png +0 -0
  66. data/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
  67. data/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
  68. data/coverage/assets/0.10.0/favicon_green.png +0 -0
  69. data/coverage/assets/0.10.0/favicon_red.png +0 -0
  70. data/coverage/assets/0.10.0/favicon_yellow.png +0 -0
  71. data/coverage/assets/0.10.0/loading.gif +0 -0
  72. data/coverage/assets/0.10.0/magnify.png +0 -0
  73. data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  74. data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  75. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  76. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  77. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  78. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  79. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  80. data/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  81. data/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  82. data/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  83. data/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  84. data/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  85. data/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  86. data/coverage/index.html +0 -2326
  87. data/lib/chake/backend.rb +0 -80
@@ -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,7 +19,7 @@ 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}"
20
- v = `git describe`.strip.gsub('-', '.').sub(/^v/, '')
22
+ v = `git describe`.strip.tr('-', '.').sub(/^v/, '')
21
23
  chdir 'pkg' do
22
24
  sh 'gem2deb', '--no-wnpp-check', '-s', '-p', pkg.name, "#{dirname}.tar.gz"
23
25
  sh "rename s/#{pkg.version}/#{v}/ *.orig.tar.gz"
@@ -31,7 +33,7 @@ task 'build:debsrc' => ['bundler:clobber', 'build:tarball'] do
31
33
  end
32
34
 
33
35
  desc 'Builds and installs Debian package'
34
- task 'deb:install' => 'build:debsrc'do
36
+ task 'deb:install' => 'build:debsrc' do
35
37
  chdir "pkg/#{pkg.name}-#{pkg.version}" do
36
38
  sh 'dpkg-buildpackage --diff-ignore=version.rb -us -uc'
37
39
  sh 'debi'
@@ -48,7 +50,7 @@ end
48
50
  file 'pkg/chake.spec' => ['chake.spec.erb', 'lib/chake/version.rb'] do |t|
49
51
  require 'erb'
50
52
  pkg = Gem::Specification.load('chake.gemspec')
51
- template = ERB.new(File.read('chake.spec.erb'))
53
+ template = ERB.new(File.read('chake.spec.erb'))
52
54
  File.open(t.name, 'w') do |f|
53
55
  f.puts(template.result(binding))
54
56
  end
@@ -60,27 +62,51 @@ task 'build:all' => ['build:debsrc', 'build:rpmsrc']
60
62
  desc 'lists changes since last release'
61
63
  task :changelog do
62
64
  last_tag = `git tag | sort -V`.split.last
63
- sh 'git', 'shortlog', last_tag + '..'
65
+ sh 'git', 'shortlog', "#{last_tag}.."
64
66
  end
65
67
 
66
68
  task :check_tag do
67
69
  last_tag = `git tag | sort -V`.split.last
68
70
  if last_tag == "v#{pkg.version}"
69
- fail "Version #{pkg.version} was already released!"
71
+ raise "Version #{pkg.version} was already released!"
70
72
  end
71
73
  end
72
74
 
73
75
  desc 'checks if the latest release is properly documented in ChangeLog.md'
74
76
  task :check_changelog do
75
77
  begin
76
- sh 'grep', '^#\s*' + pkg.version.to_s, 'ChangeLog.md'
77
- rescue
78
+ sh 'grep', "^#\\s*#{pkg.version}", 'ChangeLog.md'
79
+ rescue StandardError
78
80
  puts "Version #{pkg.version} not documented in ChangeLog.md!"
79
81
  raise
80
82
  end
81
83
  end
82
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
+
83
95
  desc 'Makes a release'
84
- 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]
109
+
110
+ task clean: 'bundler:clobber'
85
111
 
86
- task :default => :test
112
+ 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
 
@@ -24,7 +24,7 @@ end
24
24
 
25
25
  class Rake::Application
26
26
  alias orig_thread_pool thread_pool
27
- def thread_pool # :nodoc:
27
+ def thread_pool # :nodoc:
28
28
  if Chake.respond_to?(:nodes)
29
29
  @thread_pool ||= Rake::ThreadPool.new(Chake.nodes.size + 1)
30
30
  else
@@ -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 = Dir['**/*'] - Dir['pkg/**/*']
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,5 +1,3 @@
1
- # encoding: UTF-8
2
-
3
1
  require 'yaml'
4
2
  require 'json'
5
3
  require 'tmpdir'
@@ -7,159 +5,115 @@ require 'tmpdir'
7
5
  require 'chake/config'
8
6
  require 'chake/version'
9
7
  require 'chake/readline'
10
- require 'chake/tmpdir'
11
-
12
-
13
- desc "Initializes current directory with sample structure"
14
- task :init do
15
- if File.exists?('nodes.yaml')
16
- puts '[exists] nodes.yaml'
17
- else
18
- File.open('nodes.yaml', 'w') do |f|
19
- sample_nodes = <<EOF
20
- host1.mycompany.com:
21
- run_list:
22
- - recipe[basics]
23
- EOF
24
- f.write(sample_nodes)
25
- puts "[create] nodes.yaml"
26
- end
27
- end
28
-
29
- if File.exist?('nodes.d')
30
- puts '[exists] nodes.d/'
31
- else
32
- FileUtils.mkdir_p 'nodes.d'
33
- puts '[ mkdir] nodes.d/'
34
- end
35
-
36
-
37
- if File.exists?('config.rb')
38
- puts '[exists] config.rb'
39
- else
40
- File.open('config.rb', 'w') do |f|
41
- f.puts "root = File.expand_path(File.dirname(__FILE__))"
42
- f.puts "file_cache_path root + '/cache'"
43
- f.puts "cookbook_path root + '/cookbooks'"
44
- f.puts "role_path root + '/config/roles'"
45
- end
46
- puts "[create] config.rb"
47
- end
48
-
49
- if !File.exist?('config/roles')
50
- FileUtils.mkdir_p 'config/roles'
51
- puts '[ mkdir] config/roles'
52
- end
53
- if !File.exist?('cookbooks/basics/recipes')
54
- FileUtils.mkdir_p 'cookbooks/basics/recipes/'
55
- puts '[ mkdir] cookbooks/basics/recipes/'
56
- end
57
- recipe = 'cookbooks/basics/recipes/default.rb'
58
- if File.exists?(recipe)
59
- puts "[exists] #{recipe}"
60
- else
61
- File.open(recipe, 'w') do |f|
62
- f.puts "package 'openssh-server'"
63
- end
64
- puts "[create] #{recipe}"
65
- end
66
- if File.exists?('Rakefile')
67
- puts '[exists] Rakefile'
68
- else
69
- File.open('Rakefile', 'w') do |f|
70
- f.puts 'require "chake"'
71
- puts '[create] Rakefile'
72
- end
8
+ require 'chake/wipe'
9
+
10
+ desc 'Initializes current directory with sample structure'
11
+ task init: 'init:itamae'
12
+ Chake::ConfigManager.all.map do |cfgmgr|
13
+ desc "Initializes current directory for #{cfgmgr.short_name}"
14
+ task "init:#{cfgmgr.short_name}" do
15
+ cfgmgr.init
73
16
  end
74
17
  end
75
18
 
76
19
  desc 'list nodes'
77
20
  task :nodes do
78
- Chake.nodes.each do |node|
79
- puts "%-40s %-5s\n" % [node.hostname, node.backend]
21
+ fields = %i[hostname connection config_manager]
22
+ IO.popen(['column', '-t'], mode: 'w') do |table|
23
+ table.puts(fields.join(' '))
24
+ table.puts(fields.map { |f| '-' * f.length }.join(' '))
25
+ Chake.nodes.each do |node|
26
+ table.puts fields.map { |f| node.send(f) }.join(' ')
27
+ end
80
28
  end
81
29
  end
82
30
 
83
31
  def encrypted_for(node)
84
- encrypted_files = Dir.glob("**/files/{default,host-#{node}}/*.{asc,gpg}") + Dir.glob("**/files/*.{asc,gpg}")
85
- encrypted_files.inject({}) do |hash, key|
32
+ encrypted_files = Dir.glob("**/files/{default,host-#{node}}/*.{asc,gpg}") + Dir.glob('**/files/*.{asc,gpg}')
33
+ encrypted_files.each_with_object({}) do |key, hash|
86
34
  hash[key] = key.sub(/\.(asc|gpg)$/, '')
87
- hash
88
35
  end
89
36
  end
90
37
 
91
- def if_files_changed(node, group_name, files)
92
- if files.empty?
93
- return
38
+ def maybe_decrypt(node)
39
+ if node.needs_upload?
40
+ return yield
41
+ end
42
+
43
+ files = encrypted_for(node.hostname)
44
+ files.each do |encrypted, target|
45
+ sh "gpg --use-agent --quiet --decrypt --output #{target} #{encrypted}"
46
+ end
47
+ begin
48
+ yield
49
+ ensure
50
+ files.each do |_, target|
51
+ Chake::Wipe.instance.wipe(target)
52
+ end
94
53
  end
95
- hash_io = IO.popen(['xargs', 'sha1sum'], 'w+')
54
+ end
55
+
56
+ def if_files_changed(node, group_name, files)
57
+ return if files.empty?
58
+
59
+ hash_io = IO.popen(%w[xargs sha1sum], 'w+')
96
60
  files.sort.each { |f| hash_io.puts(f) }
97
61
  hash_io.close_write
98
62
  current_hash = hash_io.read
99
63
 
100
- hash_file = File.join(Chake.tmpdir, node + '.' + group_name + '.sha1sum')
64
+ hash_file = File.join(Chake.tmpdir, "#{node}.#{group_name}.sha1sum")
101
65
  hash_on_disk = nil
102
- if File.exists?(hash_file)
103
- hash_on_disk = File.read(hash_file)
104
- end
66
+ hash_on_disk = File.read(hash_file) if File.exist?(hash_file)
105
67
 
106
- if current_hash != hash_on_disk
107
- yield
108
- end
68
+ yield if current_hash != hash_on_disk
109
69
  FileUtils.mkdir_p(File.dirname(hash_file))
110
70
  File.open(hash_file, 'w') do |f|
111
71
  f.write(current_hash)
112
72
  end
113
73
  end
114
74
 
115
-
116
75
  def write_json_file(file, data)
117
- File.chmod(0600, file) if File.exists?(file)
118
- File.open(file, 'w', 0600) do |f|
76
+ File.chmod(0o600, file) if File.exist?(file)
77
+ File.open(file, 'w', 0o600) do |f|
119
78
  f.write(JSON.pretty_generate(data))
120
79
  f.write("\n")
121
80
  end
122
81
  end
123
82
 
124
- bootstrap_steps = Dir.glob(File.expand_path('chake/bootstrap/*.sh', File.dirname(__FILE__))).sort
125
-
126
83
  desc 'Executed before bootstrapping'
127
- task :bootstrap_common => :connect_common
84
+ task bootstrap_common: :connect_common
128
85
 
129
86
  desc 'Executed before uploading'
130
- task :upload_common => :connect_common
87
+ task upload_common: :connect_common
131
88
 
132
89
  desc 'Executed before uploading'
133
- task :converge_common => :connect_common
90
+ task converge_common: :connect_common
134
91
 
135
92
  desc 'Executed before connecting to any host'
136
93
  task :connect_common
137
94
 
138
95
  Chake.nodes.each do |node|
96
+ node.silent = Rake.application.options.silent
139
97
 
140
98
  hostname = node.hostname
141
- bootstrap_script = File.join(Chake.tmpdir, 'bootstrap-' + hostname)
142
-
143
- file bootstrap_script => bootstrap_steps do |t|
144
- mkdir_p(File.dirname(bootstrap_script))
145
- File.open(t.name, 'w') do |f|
146
- f.puts '#!/bin/sh'
147
- f.puts 'set -eu'
148
- bootstrap_steps.each do |platform|
149
- f.puts(File.read(platform))
150
- end
151
- end
152
- chmod 0755, t.name
153
- end
99
+
100
+ bootstrap_script = File.join(Chake.tmpdir, "#{hostname}.bootstrap")
101
+
102
+ bootstrap_steps = node.bootstrap_steps
103
+
104
+ bootstrap_code = (["#!/bin/sh\n", "set -eu\n"] + bootstrap_steps.map { |f| File.read(f) }).join
154
105
 
155
106
  desc "bootstrap #{hostname}"
156
- task "bootstrap:#{hostname}" => [:bootstrap_common, bootstrap_script] do
157
- config = File.join(Chake.tmpdir, hostname + '.json')
107
+ task "bootstrap:#{hostname}" => :bootstrap_common do
108
+ mkdir_p Chake.tmpdir unless File.directory?(Chake.tmpdir)
109
+ if !File.exist?(bootstrap_script) || File.read(bootstrap_script) != bootstrap_code
110
+
111
+ # create bootstrap script
112
+ File.open(bootstrap_script, 'w') do |f|
113
+ f.write(bootstrap_code)
114
+ end
115
+ chmod 0o755, bootstrap_script
158
116
 
159
- if File.exists?(config)
160
- # already bootstrapped, just overwrite
161
- write_json_file(config, node.data)
162
- else
163
117
  # copy bootstrap script over
164
118
  scp = node.scp
165
119
  target = "/tmp/.chake-bootstrap.#{Etc.getpwuid.name}"
@@ -167,28 +121,29 @@ Chake.nodes.each do |node|
167
121
 
168
122
  # run bootstrap script
169
123
  node.run_as_root("#{target} #{hostname}")
170
-
171
- # overwrite config with current contents
172
- mkdir_p File.dirname(config)
173
- write_json_file(config, node.data)
174
124
  end
175
125
 
126
+ # overwrite config with current contents
127
+ config = File.join(Chake.tmpdir, "#{hostname}.json")
128
+ write_json_file(config, node.data)
176
129
  end
177
130
 
178
131
  desc "upload data to #{hostname}"
179
132
  task "upload:#{hostname}" => :upload_common do
133
+ next unless node.needs_upload?
134
+
180
135
  encrypted = encrypted_for(hostname)
181
- rsync_excludes = (encrypted.values + encrypted.keys).map { |f| ["--exclude", f] }.flatten
182
- rsync_excludes << "--exclude" << ".git/"
183
- rsync_excludes << "--exclude" << "cache/"
184
- rsync_excludes << "--exclude" << "nodes/"
185
- rsync_excludes << "--exclude" << "local-mode-cache/"
136
+ rsync_excludes = (encrypted.values + encrypted.keys).map { |f| ['--exclude', f] }.flatten
137
+ rsync_excludes << '--exclude' << '.git/'
138
+ rsync_excludes << '--exclude' << 'cache/'
139
+ rsync_excludes << '--exclude' << 'nodes/'
140
+ rsync_excludes << '--exclude' << 'local-mode-cache/'
186
141
 
187
- rsync = node.rsync + ["-avp"] + ENV.fetch('CHAKE_RSYNC_OPTIONS', '').split
142
+ rsync = node.rsync + ['-avp'] + ENV.fetch('CHAKE_RSYNC_OPTIONS', '').split
188
143
  rsync_logging = Rake.application.options.silent && '--quiet' || '--verbose'
189
144
 
190
145
  hash_files = Dir.glob(File.join(Chake.tmpdir, '*.sha1sum'))
191
- files = Dir.glob("**/*").select { |f| !File.directory?(f) } - encrypted.keys - encrypted.values - hash_files
146
+ files = Dir.glob('**/*').reject { |f| File.directory?(f) } - encrypted.keys - encrypted.values - hash_files
192
147
  if_files_changed(hostname, 'plain', files) do
193
148
  sh *rsync, '--delete', rsync_logging, *rsync_excludes, './', node.rsync_dest
194
149
  end
@@ -199,14 +154,14 @@ Chake.nodes.each do |node|
199
154
  target = File.join(tmpdir, target_file)
200
155
  mkdir_p(File.dirname(target))
201
156
  rm_f target
202
- File.open(target, 'w', 0400) do |output|
157
+ File.open(target, 'w', 0o400) do |output|
203
158
  IO.popen(['gpg', '--quiet', '--batch', '--use-agent', '--decrypt', encrypted_file]) do |data|
204
159
  output.write(data.read)
205
160
  end
206
161
  end
207
162
  puts "#{target} (decrypted)"
208
163
  end
209
- sh *rsync, rsync_logging, tmpdir + '/', node.rsync_dest
164
+ sh *rsync, rsync_logging, "#{tmpdir}/", node.rsync_dest
210
165
  end
211
166
  end
212
167
  end
@@ -215,19 +170,21 @@ Chake.nodes.each do |node|
215
170
 
216
171
  desc "converge #{hostname}"
217
172
  task "converge:#{hostname}" => converge_dependencies do
218
- chef_logging = Rake.application.options.silent && '-l fatal' || ''
219
- node.run_as_root "chef-solo -c #{node.path}/#{Chake.chef_config} #{chef_logging} -j #{node.path}/#{Chake.tmpdir}/#{hostname}.json"
173
+ maybe_decrypt(node) do
174
+ node.converge
175
+ end
220
176
  end
221
177
 
222
178
  desc 'apply <recipe> on #{hostname}'
223
- task "apply:#{hostname}", [:recipe] => [:recipe_input, :connect_common] do |task, args|
224
- chef_logging = Rake.application.options.silent && '-l fatal' || ''
225
- node.run_as_root "chef-solo -c #{node.path}/#{Chake.chef_config} #{chef_logging} -j #{node.path}/#{Chake.tmpdir}/#{hostname}.json --override-runlist recipe[#{$recipe_to_apply}]"
179
+ task "apply:#{hostname}", [:recipe] => %i[recipe_input connect_common] do |_task, _args|
180
+ maybe_decrypt(node) do
181
+ node.apply($recipe_to_apply)
182
+ end
226
183
  end
227
184
  task "apply:#{hostname}" => converge_dependencies
228
185
 
229
186
  desc "run a command on #{hostname}"
230
- task "run:#{hostname}", [:command] => [:run_input, :connect_common] do
187
+ task "run:#{hostname}", [:command] => %i[run_input connect_common] do
231
188
  node.run($cmd_to_run)
232
189
  end
233
190
 
@@ -240,32 +197,31 @@ Chake.nodes.each do |node|
240
197
  task "check:#{hostname}" => :connect_common do
241
198
  node.run('sudo echo OK')
242
199
  end
243
-
244
200
  end
245
201
 
246
- task :run_input, :command do |task,args|
202
+ task :run_input, :command do |_task, args|
247
203
  $cmd_to_run = args[:command]
248
- if !$cmd_to_run
249
- puts "# Enter command to run (use arrow keys for history):"
204
+ unless $cmd_to_run
205
+ puts '# Enter command to run (use arrow keys for history):'
250
206
  $cmd_to_run = Chake::Readline::Commands.readline
251
207
  end
252
208
  if !$cmd_to_run || $cmd_to_run.strip == ''
253
209
  puts
254
- puts "I: no command provided, operation aborted."
210
+ puts 'I: no command provided, operation aborted.'
255
211
  exit(1)
256
212
  end
257
213
  end
258
214
 
259
- task :recipe_input, :recipe do |task,args|
215
+ task :recipe_input, :recipe do |_task, args|
260
216
  $recipe_to_apply = args[:recipe]
261
217
 
262
- if !$recipe_to_apply
218
+ unless $recipe_to_apply
263
219
  recipes = Dir['**/*/recipes/*.rb'].map do |f|
264
220
  f =~ %r{(.*/)?(.*)/recipes/(.*).rb$}
265
- cookbook = $2
266
- recipe = $3
221
+ cookbook = Regexp.last_match(2)
222
+ recipe = Regexp.last_match(3)
267
223
  recipe = nil if recipe == 'default'
268
- [cookbook,recipe].compact.join('::')
224
+ [cookbook, recipe].compact.join('::')
269
225
  end.sort
270
226
  puts 'Available recipes:'
271
227
 
@@ -276,43 +232,43 @@ task :recipe_input, :recipe do |task,args|
276
232
  $recipe_to_apply = Chake::Readline::Recipes.readline
277
233
  if !$recipe_to_apply || $recipe_to_apply.empty?
278
234
  puts
279
- puts "I: no recipe provided, operation aborted."
235
+ puts 'I: no recipe provided, operation aborted.'
280
236
  exit(1)
281
237
  end
282
- if !recipes.include?($recipe_to_apply)
238
+ unless recipes.include?($recipe_to_apply)
283
239
  abort "E: no such recipe: #{$recipe_to_apply}"
284
240
  end
285
241
  end
286
242
  end
287
243
 
288
- desc "upload to all nodes"
289
- multitask :upload => Chake.nodes.map { |node| "upload:#{node.hostname}" }
244
+ desc 'upload to all nodes'
245
+ multitask upload: Chake.nodes.map { |node| "upload:#{node.hostname}" }
290
246
 
291
- desc "bootstrap all nodes"
292
- multitask :bootstrap => Chake.nodes.map { |node| "bootstrap:#{node.hostname}" }
247
+ desc 'bootstrap all nodes'
248
+ multitask bootstrap: Chake.nodes.map { |node| "bootstrap:#{node.hostname}" }
293
249
 
294
- desc "converge all nodes (default)"
295
- multitask "converge" => Chake.nodes.map { |node| "converge:#{node.hostname}" }
250
+ desc 'converge all nodes (default)'
251
+ multitask 'converge' => Chake.nodes.map { |node| "converge:#{node.hostname}" }
296
252
 
297
- desc "Apply <recipe> on all nodes"
298
- multitask "apply", [:recipe] => Chake.nodes.map { |node| "apply:#{node.hostname}" }
253
+ desc 'Apply <recipe> on all nodes'
254
+ multitask 'apply', [:recipe] => Chake.nodes.map { |node| "apply:#{node.hostname}" }
299
255
 
300
- desc "run <command> on all nodes"
256
+ desc 'run <command> on all nodes'
301
257
  multitask :run, [:command] => Chake.nodes.map { |node| "run:#{node.hostname}" }
302
258
 
303
- task :default => :converge
259
+ task default: :converge
304
260
 
305
261
  desc 'checks connectivity and setup on all nodes'
306
- multitask :check => (Chake.nodes.map { |node| "check:#{node.hostname}" }) do
307
- puts "✓ all hosts OK"
308
- puts " - ssh connection works"
309
- puts " - password-less sudo works"
262
+ multitask check: (Chake.nodes.map { |node| "check:#{node.hostname}" }) do
263
+ puts '✓ all hosts OK'
264
+ puts ' - ssh connection works'
265
+ puts ' - password-less sudo works'
310
266
  end
311
267
 
312
268
  desc 'runs a Ruby console in the chake environment'
313
269
  task :console do
314
270
  require 'irb'
315
- IRB.setup(eval("__FILE__"), argv: [])
271
+ IRB.setup('__FILE__', argv: [])
316
272
  workspace = IRB::WorkSpace.new(self)
317
273
 
318
274
  puts 'chake - interactive console'