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.
- checksums.yaml +4 -4
- data/.ackrc +2 -0
- data/.gitignore +22 -0
- data/.gitlab-ci.yml +24 -0
- data/.manifest +65 -0
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +40 -0
- data/ChangeLog.md +36 -0
- data/README.chef.md +70 -0
- data/README.itamae.md +58 -0
- data/README.md +118 -85
- data/README.shell.md +30 -0
- data/Rakefile +36 -10
- data/bin/chake +2 -2
- data/chake.gemspec +16 -16
- data/examples/test/.ssh_config +4 -0
- data/examples/test/Rakefile +1 -1
- data/examples/test/Vagrantfile +6 -0
- data/examples/test/config.rb +4 -4
- data/examples/test/cookbooks/basics/recipes/default.rb +1 -0
- data/examples/test/cookbooks/example/files/default/test +1 -0
- data/examples/test/cookbooks/example/files/{host-homer → host-lemur}/test.asc +0 -0
- data/lib/chake.rb +110 -154
- data/lib/chake/bootstrap/chef/01_installed.sh +4 -0
- data/lib/chake/bootstrap/{01_debian.sh → chef/02_debian.sh} +0 -0
- data/lib/chake/bootstrap/{99_unsupported.sh → chef/99_unsupported.sh} +0 -0
- data/lib/chake/config.rb +2 -7
- data/lib/chake/config_manager.rb +89 -0
- data/lib/chake/config_manager/chef.rb +35 -0
- data/lib/chake/config_manager/itamae.rb +57 -0
- data/lib/chake/config_manager/shell.rb +34 -0
- data/lib/chake/config_manager/skel/chef/Rakefile +1 -0
- data/lib/chake/config_manager/skel/chef/config.rb +4 -0
- data/lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/default.rb +1 -0
- data/lib/chake/config_manager/skel/chef/nodes.yaml +3 -0
- data/lib/chake/config_manager/skel/itamae/Rakefile +1 -0
- data/lib/chake/config_manager/skel/itamae/cookbooks/basics/default.rb +1 -0
- data/lib/chake/config_manager/skel/itamae/nodes.yaml +3 -0
- data/lib/chake/config_manager/skel/itamae/roles/basic.rb +1 -0
- data/lib/chake/config_manager/skel/shell/Rakefile +1 -0
- data/lib/chake/config_manager/skel/shell/nodes.yaml +3 -0
- data/lib/chake/connection.rb +83 -0
- data/lib/chake/{backend → connection}/local.rb +2 -8
- data/lib/chake/{backend → connection}/ssh.rb +6 -14
- data/lib/chake/node.rb +49 -29
- data/lib/chake/readline.rb +6 -10
- data/lib/chake/version.rb +1 -1
- data/lib/chake/wipe.rb +18 -0
- data/man/.gitignore +2 -0
- data/man/Rakefile +28 -14
- data/man/readme2man.sed +5 -5
- data/spec/chake/backend/local_spec.rb +5 -6
- data/spec/chake/backend/ssh_spec.rb +8 -10
- data/spec/chake/backend_spec.rb +1 -2
- data/spec/chake/config_manager/chef_spec.rb +38 -0
- data/spec/chake/config_manager/itamae_spec.rb +87 -0
- data/spec/chake/config_manager/shell_spec.rb +54 -0
- data/spec/chake/config_manager_spec.rb +23 -0
- data/spec/chake/node_spec.rb +38 -15
- data/spec/spec_helper.rb +37 -17
- metadata +65 -39
- data/coverage/assets/0.10.0/application.css +0 -799
- data/coverage/assets/0.10.0/application.js +0 -34783
- data/coverage/assets/0.10.0/colorbox/border.png +0 -0
- data/coverage/assets/0.10.0/colorbox/controls.png +0 -0
- data/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
- data/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.10.0/favicon_green.png +0 -0
- data/coverage/assets/0.10.0/favicon_red.png +0 -0
- data/coverage/assets/0.10.0/favicon_yellow.png +0 -0
- data/coverage/assets/0.10.0/loading.gif +0 -0
- data/coverage/assets/0.10.0/magnify.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/index.html +0 -2326
- data/lib/chake/backend.rb +0 -80
data/README.shell.md
ADDED
|
@@ -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
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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',
|
|
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 :
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
data/chake.gemspec
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
|
|
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 =
|
|
6
|
+
spec.name = 'chake'
|
|
8
7
|
spec.version = Chake::VERSION
|
|
9
|
-
spec.authors = [
|
|
10
|
-
spec.email = [
|
|
11
|
-
spec.summary =
|
|
12
|
-
spec.description =
|
|
13
|
-
spec.homepage =
|
|
14
|
-
spec.license =
|
|
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 =
|
|
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 = [
|
|
18
|
+
spec.require_paths = ['lib']
|
|
20
19
|
|
|
21
|
-
spec.add_development_dependency
|
|
22
|
-
spec.add_development_dependency
|
|
23
|
-
spec.add_development_dependency
|
|
24
|
-
spec.add_development_dependency
|
|
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
|
|
26
|
+
spec.add_dependency 'rake'
|
|
27
27
|
end
|
data/examples/test/Rakefile
CHANGED
data/examples/test/config.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
root =
|
|
2
|
-
file_cache_path root
|
|
3
|
-
cookbook_path root
|
|
4
|
-
role_path root
|
|
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'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test
|
|
File without changes
|
data/lib/chake.rb
CHANGED
|
@@ -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/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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(
|
|
85
|
-
encrypted_files.
|
|
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
|
|
92
|
-
if
|
|
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
|
-
|
|
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
|
|
64
|
+
hash_file = File.join(Chake.tmpdir, "#{node}.#{group_name}.sha1sum")
|
|
101
65
|
hash_on_disk = nil
|
|
102
|
-
if 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(
|
|
118
|
-
File.open(file, 'w',
|
|
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 :
|
|
84
|
+
task bootstrap_common: :connect_common
|
|
128
85
|
|
|
129
86
|
desc 'Executed before uploading'
|
|
130
|
-
task :
|
|
87
|
+
task upload_common: :connect_common
|
|
131
88
|
|
|
132
89
|
desc 'Executed before uploading'
|
|
133
|
-
task :
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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}" =>
|
|
157
|
-
|
|
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| [
|
|
182
|
-
rsync_excludes <<
|
|
183
|
-
rsync_excludes <<
|
|
184
|
-
rsync_excludes <<
|
|
185
|
-
rsync_excludes <<
|
|
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 + [
|
|
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(
|
|
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',
|
|
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
|
|
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
|
-
|
|
219
|
-
|
|
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] => [
|
|
224
|
-
|
|
225
|
-
|
|
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] => [
|
|
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 |
|
|
202
|
+
task :run_input, :command do |_task, args|
|
|
247
203
|
$cmd_to_run = args[:command]
|
|
248
|
-
|
|
249
|
-
puts
|
|
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
|
|
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 |
|
|
215
|
+
task :recipe_input, :recipe do |_task, args|
|
|
260
216
|
$recipe_to_apply = args[:recipe]
|
|
261
217
|
|
|
262
|
-
|
|
218
|
+
unless $recipe_to_apply
|
|
263
219
|
recipes = Dir['**/*/recipes/*.rb'].map do |f|
|
|
264
220
|
f =~ %r{(.*/)?(.*)/recipes/(.*).rb$}
|
|
265
|
-
cookbook =
|
|
266
|
-
recipe =
|
|
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
|
|
235
|
+
puts 'I: no recipe provided, operation aborted.'
|
|
280
236
|
exit(1)
|
|
281
237
|
end
|
|
282
|
-
|
|
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
|
|
289
|
-
multitask :
|
|
244
|
+
desc 'upload to all nodes'
|
|
245
|
+
multitask upload: Chake.nodes.map { |node| "upload:#{node.hostname}" }
|
|
290
246
|
|
|
291
|
-
desc
|
|
292
|
-
multitask :
|
|
247
|
+
desc 'bootstrap all nodes'
|
|
248
|
+
multitask bootstrap: Chake.nodes.map { |node| "bootstrap:#{node.hostname}" }
|
|
293
249
|
|
|
294
|
-
desc
|
|
295
|
-
multitask
|
|
250
|
+
desc 'converge all nodes (default)'
|
|
251
|
+
multitask 'converge' => Chake.nodes.map { |node| "converge:#{node.hostname}" }
|
|
296
252
|
|
|
297
|
-
desc
|
|
298
|
-
multitask
|
|
253
|
+
desc 'Apply <recipe> on all nodes'
|
|
254
|
+
multitask 'apply', [:recipe] => Chake.nodes.map { |node| "apply:#{node.hostname}" }
|
|
299
255
|
|
|
300
|
-
desc
|
|
256
|
+
desc 'run <command> on all nodes'
|
|
301
257
|
multitask :run, [:command] => Chake.nodes.map { |node| "run:#{node.hostname}" }
|
|
302
258
|
|
|
303
|
-
task :
|
|
259
|
+
task default: :converge
|
|
304
260
|
|
|
305
261
|
desc 'checks connectivity and setup on all nodes'
|
|
306
|
-
multitask :
|
|
307
|
-
puts
|
|
308
|
-
puts
|
|
309
|
-
puts
|
|
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(
|
|
271
|
+
IRB.setup('__FILE__', argv: [])
|
|
316
272
|
workspace = IRB::WorkSpace.new(self)
|
|
317
273
|
|
|
318
274
|
puts 'chake - interactive console'
|