chake 0.19 → 0.80
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ackrc +1 -0
- data/.gitignore +2 -0
- data/.gitlab-ci.yml +21 -9
- data/.manifest +63 -0
- data/.rubocop.yml +53 -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 +124 -85
- data/README.shell.md +30 -0
- data/Rakefile +40 -13
- data/bin/chake +12 -1
- 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 +92 -168
- data/lib/chake/bootstrap/{01_debian.sh → chef/01_debian.sh} +0 -0
- data/lib/chake/bootstrap/{99_unsupported.sh → chef/99_unsupported.sh} +0 -0
- data/lib/chake/config.rb +16 -0
- data/lib/chake/config_manager.rb +93 -0
- data/lib/chake/config_manager/chef.rb +35 -0
- data/lib/chake/config_manager/itamae.rb +58 -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/man/Rakefile +27 -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 +69 -0
- data/spec/chake/config_manager/shell_spec.rb +54 -0
- data/spec/chake/config_manager_spec.rb +24 -0
- data/spec/chake/node_spec.rb +38 -15
- data/spec/spec_helper.rb +23 -19
- metadata +61 -14
- data/lib/chake/backend.rb +0 -78
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,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 "
|
25
|
-
sh
|
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 '
|
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 =
|
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
|
-
|
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',
|
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 :
|
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
|
-
|
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
|
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
|
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,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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
88
|
-
|
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(
|
94
|
-
encrypted_files.
|
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
|
-
|
103
|
-
|
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(
|
45
|
+
hash_file = File.join(Chake.tmpdir, "#{node}.#{group_name}.sha1sum")
|
110
46
|
hash_on_disk = nil
|
111
|
-
if 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(
|
127
|
-
File.open(file, 'w',
|
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 :
|
65
|
+
task bootstrap_common: :connect_common
|
137
66
|
|
138
67
|
desc 'Executed before uploading'
|
139
|
-
task :
|
68
|
+
task upload_common: :connect_common
|
140
69
|
|
141
70
|
desc 'Executed before uploading'
|
142
|
-
task :
|
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
|
-
|
76
|
+
Chake.nodes.each do |node|
|
77
|
+
node.silent = Rake.application.options.silent
|
148
78
|
|
149
79
|
hostname = node.hostname
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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}" =>
|
166
|
-
|
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| [
|
191
|
-
rsync_excludes <<
|
192
|
-
rsync_excludes <<
|
193
|
-
rsync_excludes <<
|
194
|
-
rsync_excludes <<
|
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 + [
|
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(
|
200
|
-
files = Dir.glob(
|
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',
|
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
|
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
|
-
|
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] => [
|
233
|
-
|
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] => [
|
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
|
175
|
+
node.run('sudo echo OK')
|
251
176
|
end
|
252
|
-
|
253
177
|
end
|
254
178
|
|
255
|
-
task :run_input, :command do |
|
179
|
+
task :run_input, :command do |_task, args|
|
256
180
|
$cmd_to_run = args[:command]
|
257
|
-
|
258
|
-
puts
|
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
|
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 |
|
192
|
+
task :recipe_input, :recipe do |_task, args|
|
269
193
|
$recipe_to_apply = args[:recipe]
|
270
194
|
|
271
|
-
|
195
|
+
unless $recipe_to_apply
|
272
196
|
recipes = Dir['**/*/recipes/*.rb'].map do |f|
|
273
197
|
f =~ %r{(.*/)?(.*)/recipes/(.*).rb$}
|
274
|
-
cookbook =
|
275
|
-
recipe =
|
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
|
212
|
+
puts 'I: no recipe provided, operation aborted.'
|
289
213
|
exit(1)
|
290
214
|
end
|
291
|
-
|
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
|
298
|
-
multitask :
|
221
|
+
desc 'upload to all nodes'
|
222
|
+
multitask upload: Chake.nodes.map { |node| "upload:#{node.hostname}" }
|
299
223
|
|
300
|
-
desc
|
301
|
-
multitask :
|
224
|
+
desc 'bootstrap all nodes'
|
225
|
+
multitask bootstrap: Chake.nodes.map { |node| "bootstrap:#{node.hostname}" }
|
302
226
|
|
303
|
-
desc
|
304
|
-
multitask
|
227
|
+
desc 'converge all nodes (default)'
|
228
|
+
multitask 'converge' => Chake.nodes.map { |node| "converge:#{node.hostname}" }
|
305
229
|
|
306
|
-
desc
|
307
|
-
multitask
|
230
|
+
desc 'Apply <recipe> on all nodes'
|
231
|
+
multitask 'apply', [:recipe] => Chake.nodes.map { |node| "apply:#{node.hostname}" }
|
308
232
|
|
309
|
-
desc
|
310
|
-
multitask :run, [:command] =>
|
233
|
+
desc 'run <command> on all nodes'
|
234
|
+
multitask :run, [:command] => Chake.nodes.map { |node| "run:#{node.hostname}" }
|
311
235
|
|
312
|
-
task :
|
236
|
+
task default: :converge
|
313
237
|
|
314
238
|
desc 'checks connectivity and setup on all nodes'
|
315
|
-
multitask :
|
316
|
-
puts
|
317
|
-
puts
|
318
|
-
puts
|
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(
|
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
|
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
|