chake 0.21 → 0.81.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) 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 +37 -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 +111 -153
  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.11.0/application.css +0 -809
  63. data/coverage/assets/0.11.0/application.js +0 -43679
  64. data/coverage/assets/0.11.0/colorbox/border.png +0 -0
  65. data/coverage/assets/0.11.0/colorbox/controls.png +0 -0
  66. data/coverage/assets/0.11.0/colorbox/loading.gif +0 -0
  67. data/coverage/assets/0.11.0/colorbox/loading_background.png +0 -0
  68. data/coverage/assets/0.11.0/favicon_green.png +0 -0
  69. data/coverage/assets/0.11.0/favicon_red.png +0 -0
  70. data/coverage/assets/0.11.0/favicon_yellow.png +0 -0
  71. data/coverage/assets/0.11.0/loading.gif +0 -0
  72. data/coverage/assets/0.11.0/magnify.png +0 -0
  73. data/coverage/assets/0.11.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  74. data/coverage/assets/0.11.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  75. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  76. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  77. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  78. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  79. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  80. data/coverage/assets/0.11.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  81. data/coverage/assets/0.11.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  82. data/coverage/assets/0.11.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  83. data/coverage/assets/0.11.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  84. data/coverage/assets/0.11.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  85. data/coverage/assets/0.11.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  86. data/coverage/index.html +0 -4158
  87. data/lib/chake/backend.rb +0 -80
  88. data/tags +0 -72
@@ -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,117 @@ 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
21
+ fields = %i[hostname connection config_manager]
22
+ lengths = fields.map do |f|
23
+ [f.length, Chake.nodes.map { |n| n.send(f).to_s.length }.max].max
24
+ end
25
+ columns = lengths.map { |l| "%-#{l}s"}.join(" ")
26
+ puts(columns % fields)
27
+ puts(columns % lengths.map { |l| '-' * l })
78
28
  Chake.nodes.each do |node|
79
- puts "%-40s %-5s\n" % [node.hostname, node.backend]
29
+ puts(columns % fields.map { |f| node.send(f) })
80
30
  end
81
31
  end
82
32
 
83
33
  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|
34
+ encrypted_files = Dir.glob("**/files/{default,host-#{node}}/*.{asc,gpg}") + Dir.glob('**/files/*.{asc,gpg}')
35
+ encrypted_files.each_with_object({}) do |key, hash|
86
36
  hash[key] = key.sub(/\.(asc|gpg)$/, '')
87
- hash
88
37
  end
89
38
  end
90
39
 
91
- def if_files_changed(node, group_name, files)
92
- if files.empty?
93
- return
40
+ def maybe_decrypt(node)
41
+ if node.needs_upload?
42
+ return yield
43
+ end
44
+
45
+ files = encrypted_for(node.hostname)
46
+ files.each do |encrypted, target|
47
+ sh "gpg --use-agent --quiet --decrypt --output #{target} #{encrypted}"
48
+ end
49
+ begin
50
+ yield
51
+ ensure
52
+ files.each do |_, target|
53
+ Chake::Wipe.instance.wipe(target)
54
+ end
94
55
  end
95
- hash_io = IO.popen(['xargs', 'sha1sum'], 'w+')
56
+ end
57
+
58
+ def if_files_changed(node, group_name, files)
59
+ return if files.empty?
60
+
61
+ hash_io = IO.popen(%w[xargs sha1sum], 'w+')
96
62
  files.sort.each { |f| hash_io.puts(f) }
97
63
  hash_io.close_write
98
64
  current_hash = hash_io.read
99
65
 
100
- hash_file = File.join(Chake.tmpdir, node + '.' + group_name + '.sha1sum')
66
+ hash_file = File.join(Chake.tmpdir, "#{node}.#{group_name}.sha1sum")
101
67
  hash_on_disk = nil
102
- if File.exists?(hash_file)
103
- hash_on_disk = File.read(hash_file)
104
- end
68
+ hash_on_disk = File.read(hash_file) if File.exist?(hash_file)
105
69
 
106
- if current_hash != hash_on_disk
107
- yield
108
- end
70
+ yield if current_hash != hash_on_disk
109
71
  FileUtils.mkdir_p(File.dirname(hash_file))
110
72
  File.open(hash_file, 'w') do |f|
111
73
  f.write(current_hash)
112
74
  end
113
75
  end
114
76
 
115
-
116
77
  def write_json_file(file, data)
117
- File.chmod(0600, file) if File.exists?(file)
118
- File.open(file, 'w', 0600) do |f|
78
+ File.chmod(0o600, file) if File.exist?(file)
79
+ File.open(file, 'w', 0o600) do |f|
119
80
  f.write(JSON.pretty_generate(data))
120
81
  f.write("\n")
121
82
  end
122
83
  end
123
84
 
124
- bootstrap_steps = Dir.glob(File.expand_path('chake/bootstrap/*.sh', File.dirname(__FILE__))).sort
125
-
126
85
  desc 'Executed before bootstrapping'
127
- task :bootstrap_common => :connect_common
86
+ task bootstrap_common: :connect_common
128
87
 
129
88
  desc 'Executed before uploading'
130
- task :upload_common => :connect_common
89
+ task upload_common: :connect_common
131
90
 
132
91
  desc 'Executed before uploading'
133
- task :converge_common => :connect_common
92
+ task converge_common: :connect_common
134
93
 
135
94
  desc 'Executed before connecting to any host'
136
95
  task :connect_common
137
96
 
138
97
  Chake.nodes.each do |node|
98
+ node.silent = Rake.application.options.silent
139
99
 
140
100
  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
101
+
102
+ bootstrap_script = File.join(Chake.tmpdir, "#{hostname}.bootstrap")
103
+
104
+ bootstrap_steps = node.bootstrap_steps
105
+
106
+ bootstrap_code = (["#!/bin/sh\n", "set -eu\n"] + bootstrap_steps.map { |f| File.read(f) }).join
154
107
 
155
108
  desc "bootstrap #{hostname}"
156
- task "bootstrap:#{hostname}" => [:bootstrap_common, bootstrap_script] do
157
- config = File.join(Chake.tmpdir, hostname + '.json')
109
+ task "bootstrap:#{hostname}" => :bootstrap_common do
110
+ mkdir_p Chake.tmpdir unless File.directory?(Chake.tmpdir)
111
+ if !File.exist?(bootstrap_script) || File.read(bootstrap_script) != bootstrap_code
112
+
113
+ # create bootstrap script
114
+ File.open(bootstrap_script, 'w') do |f|
115
+ f.write(bootstrap_code)
116
+ end
117
+ chmod 0o755, bootstrap_script
158
118
 
159
- if File.exists?(config)
160
- # already bootstrapped, just overwrite
161
- write_json_file(config, node.data)
162
- else
163
119
  # copy bootstrap script over
164
120
  scp = node.scp
165
121
  target = "/tmp/.chake-bootstrap.#{Etc.getpwuid.name}"
@@ -167,28 +123,29 @@ Chake.nodes.each do |node|
167
123
 
168
124
  # run bootstrap script
169
125
  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
126
  end
175
127
 
128
+ # overwrite config with current contents
129
+ config = File.join(Chake.tmpdir, "#{hostname}.json")
130
+ write_json_file(config, node.data)
176
131
  end
177
132
 
178
133
  desc "upload data to #{hostname}"
179
134
  task "upload:#{hostname}" => :upload_common do
135
+ next unless node.needs_upload?
136
+
180
137
  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/"
138
+ rsync_excludes = (encrypted.values + encrypted.keys).map { |f| ['--exclude', f] }.flatten
139
+ rsync_excludes << '--exclude' << '.git/'
140
+ rsync_excludes << '--exclude' << 'cache/'
141
+ rsync_excludes << '--exclude' << 'nodes/'
142
+ rsync_excludes << '--exclude' << 'local-mode-cache/'
186
143
 
187
- rsync = node.rsync + ["-avp"] + ENV.fetch('CHAKE_RSYNC_OPTIONS', '').split
144
+ rsync = node.rsync + ['-avp'] + ENV.fetch('CHAKE_RSYNC_OPTIONS', '').split
188
145
  rsync_logging = Rake.application.options.silent && '--quiet' || '--verbose'
189
146
 
190
147
  hash_files = Dir.glob(File.join(Chake.tmpdir, '*.sha1sum'))
191
- files = Dir.glob("**/*").select { |f| !File.directory?(f) } - encrypted.keys - encrypted.values - hash_files
148
+ files = Dir.glob('**/*').reject { |f| File.directory?(f) } - encrypted.keys - encrypted.values - hash_files
192
149
  if_files_changed(hostname, 'plain', files) do
193
150
  sh *rsync, '--delete', rsync_logging, *rsync_excludes, './', node.rsync_dest
194
151
  end
@@ -199,14 +156,14 @@ Chake.nodes.each do |node|
199
156
  target = File.join(tmpdir, target_file)
200
157
  mkdir_p(File.dirname(target))
201
158
  rm_f target
202
- File.open(target, 'w', 0400) do |output|
159
+ File.open(target, 'w', 0o400) do |output|
203
160
  IO.popen(['gpg', '--quiet', '--batch', '--use-agent', '--decrypt', encrypted_file]) do |data|
204
161
  output.write(data.read)
205
162
  end
206
163
  end
207
164
  puts "#{target} (decrypted)"
208
165
  end
209
- sh *rsync, rsync_logging, tmpdir + '/', node.rsync_dest
166
+ sh *rsync, rsync_logging, "#{tmpdir}/", node.rsync_dest
210
167
  end
211
168
  end
212
169
  end
@@ -215,19 +172,21 @@ Chake.nodes.each do |node|
215
172
 
216
173
  desc "converge #{hostname}"
217
174
  task "converge:#{hostname}" => converge_dependencies do
218
- chef_logging = Rake.application.options.silent && '-l fatal' || ''
219
- node.run_as_root "rm -f #{node.path}/nodes/*.json && chef-solo -c #{node.path}/#{Chake.chef_config} #{chef_logging} -j #{node.path}/#{Chake.tmpdir}/#{hostname}.json"
175
+ maybe_decrypt(node) do
176
+ node.converge
177
+ end
220
178
  end
221
179
 
222
180
  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 "rm -f #{node.path}/nodes/*.json && chef-solo -c #{node.path}/#{Chake.chef_config} #{chef_logging} -j #{node.path}/#{Chake.tmpdir}/#{hostname}.json --override-runlist recipe[#{$recipe_to_apply}]"
181
+ task "apply:#{hostname}", [:recipe] => %i[recipe_input connect_common] do |_task, _args|
182
+ maybe_decrypt(node) do
183
+ node.apply($recipe_to_apply)
184
+ end
226
185
  end
227
186
  task "apply:#{hostname}" => converge_dependencies
228
187
 
229
188
  desc "run a command on #{hostname}"
230
- task "run:#{hostname}", [:command] => [:run_input, :connect_common] do
189
+ task "run:#{hostname}", [:command] => %i[run_input connect_common] do
231
190
  node.run($cmd_to_run)
232
191
  end
233
192
 
@@ -240,32 +199,31 @@ Chake.nodes.each do |node|
240
199
  task "check:#{hostname}" => :connect_common do
241
200
  node.run('sudo echo OK')
242
201
  end
243
-
244
202
  end
245
203
 
246
- task :run_input, :command do |task,args|
204
+ task :run_input, :command do |_task, args|
247
205
  $cmd_to_run = args[:command]
248
- if !$cmd_to_run
249
- puts "# Enter command to run (use arrow keys for history):"
206
+ unless $cmd_to_run
207
+ puts '# Enter command to run (use arrow keys for history):'
250
208
  $cmd_to_run = Chake::Readline::Commands.readline
251
209
  end
252
210
  if !$cmd_to_run || $cmd_to_run.strip == ''
253
211
  puts
254
- puts "I: no command provided, operation aborted."
212
+ puts 'I: no command provided, operation aborted.'
255
213
  exit(1)
256
214
  end
257
215
  end
258
216
 
259
- task :recipe_input, :recipe do |task,args|
217
+ task :recipe_input, :recipe do |_task, args|
260
218
  $recipe_to_apply = args[:recipe]
261
219
 
262
- if !$recipe_to_apply
220
+ unless $recipe_to_apply
263
221
  recipes = Dir['**/*/recipes/*.rb'].map do |f|
264
222
  f =~ %r{(.*/)?(.*)/recipes/(.*).rb$}
265
- cookbook = $2
266
- recipe = $3
223
+ cookbook = Regexp.last_match(2)
224
+ recipe = Regexp.last_match(3)
267
225
  recipe = nil if recipe == 'default'
268
- [cookbook,recipe].compact.join('::')
226
+ [cookbook, recipe].compact.join('::')
269
227
  end.sort
270
228
  puts 'Available recipes:'
271
229
 
@@ -276,43 +234,43 @@ task :recipe_input, :recipe do |task,args|
276
234
  $recipe_to_apply = Chake::Readline::Recipes.readline
277
235
  if !$recipe_to_apply || $recipe_to_apply.empty?
278
236
  puts
279
- puts "I: no recipe provided, operation aborted."
237
+ puts 'I: no recipe provided, operation aborted.'
280
238
  exit(1)
281
239
  end
282
- if !recipes.include?($recipe_to_apply)
240
+ unless recipes.include?($recipe_to_apply)
283
241
  abort "E: no such recipe: #{$recipe_to_apply}"
284
242
  end
285
243
  end
286
244
  end
287
245
 
288
- desc "upload to all nodes"
289
- multitask :upload => Chake.nodes.map { |node| "upload:#{node.hostname}" }
246
+ desc 'upload to all nodes'
247
+ multitask upload: Chake.nodes.map { |node| "upload:#{node.hostname}" }
290
248
 
291
- desc "bootstrap all nodes"
292
- multitask :bootstrap => Chake.nodes.map { |node| "bootstrap:#{node.hostname}" }
249
+ desc 'bootstrap all nodes'
250
+ multitask bootstrap: Chake.nodes.map { |node| "bootstrap:#{node.hostname}" }
293
251
 
294
- desc "converge all nodes (default)"
295
- multitask "converge" => Chake.nodes.map { |node| "converge:#{node.hostname}" }
252
+ desc 'converge all nodes (default)'
253
+ multitask 'converge' => Chake.nodes.map { |node| "converge:#{node.hostname}" }
296
254
 
297
- desc "Apply <recipe> on all nodes"
298
- multitask "apply", [:recipe] => Chake.nodes.map { |node| "apply:#{node.hostname}" }
255
+ desc 'Apply <recipe> on all nodes'
256
+ multitask 'apply', [:recipe] => Chake.nodes.map { |node| "apply:#{node.hostname}" }
299
257
 
300
- desc "run <command> on all nodes"
258
+ desc 'run <command> on all nodes'
301
259
  multitask :run, [:command] => Chake.nodes.map { |node| "run:#{node.hostname}" }
302
260
 
303
- task :default => :converge
261
+ task default: :converge
304
262
 
305
263
  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"
264
+ multitask check: (Chake.nodes.map { |node| "check:#{node.hostname}" }) do
265
+ puts '✓ all hosts OK'
266
+ puts ' - ssh connection works'
267
+ puts ' - password-less sudo works'
310
268
  end
311
269
 
312
270
  desc 'runs a Ruby console in the chake environment'
313
271
  task :console do
314
272
  require 'irb'
315
- IRB.setup(eval("__FILE__"), argv: [])
273
+ IRB.setup('__FILE__', argv: [])
316
274
  workspace = IRB::WorkSpace.new(self)
317
275
 
318
276
  puts 'chake - interactive console'