chake-0.91/ 0000755 0000041 0000041 00000000000 14537556746 012574 5 ustar www-data www-data chake-0.91/README.md 0000644 0000041 0000041 00000030315 14537556746 014055 0 ustar www-data www-data chake(1) -- serverless configuration management tool
========================================
## SYNOPSIS
`chake` init
`chake` [rake arguments]
## Description
chake is a tool that helps you manage multiple hosts without the need for a
central server. Configuration is managed in a local directory, which should
(but doesn't need to) be under version control with **git(1)** or any other
version control system.
Configuration is deployed to managed hosts remotely, either by invoking a
configuration management tool that will connect to them, or by first uploading
the necessary configuration and them remotely running a tool on the hosts.
## Supported configuration managers.
chake supports the following configuration management tools:
* **itamae**: configuration is applied by running the itamae command line tool
on the management host; no configuration needs to be uploaded to the managed
hosts. See chake-itamae(7) for details.
* **shell**: the local repository is copied to the host, and the shell commands
specified in the node configuration is executed from the directory where that
copy is. See chake-shell(7) for details.
* **chef**: the local repository is copied to the host, and **chef-solo** is
executed remotely on the managed host. See chake-chef(7) for details.
Beyond applying configuration management recipes on the hosts, chake also
provides useful tools to manage multiple hosts, such as listing nodes, running
commands against all of them simultaneously, logging in to interactive
shells, and others.
## creating the repository
$ chake init[:configmanager]
This will create an initial directory structure. Some of the files are specific
to your your chosen **configmanager**, which can be one of [SUPPORTED
CONFIGURATION MANAGERS]. The following files, though, will be common to any
usage of chake:
* `nodes.yaml`: where you will list the hosts you will be managing, and what
recipes to apply to each of them.
* `nodes.d`: a directory with multiple files in the same format as nodes.yaml.
All files matching `*.yaml` in it will be added to the list of nodes.
* `Rakefile`: Contains just the `require 'chake'` line. You can augment it with
other tasks specific to your intrastructure.
If you omit _configmanager_, `itamae` will be used by default.
After the repository is created, you can call either `chake` or `rake`, as they
are completely equivalent.
## Managing nodes
Just after you created your repository, the contents of `nodes.yaml` is the
following:
```yaml
host1.mycompany.com:
itamae:
- roles/basic.rb
```
The exact contents depends on the chosen configuration management tool.
You can list your hosts with `rake nodes`:
```
$ rake nodes
host1.mycompany.com ssh
```
To add more nodes, just append to `nodes.yaml`:
```yaml
host1.mycompany.com:
itamae:
- roles/basic.rb
host2.mycompany.com:
itamae:
- roles/basic.rb
```
And chake now knows about your new node:
```
$ rake nodes
host1.mycompany.com ssh
host2.mycompany.com ssh
```
## Preparings nodes to be managed
Nodes have very few initial requirements to be managed with `chake`:
- The node must be accessible via SSH.
- The user you connect to the node must either be `root`, or be allowed to run
`sudo` (in which case `sudo` must be installed).
**A note on password prompts:** every time chake calls ssh on a node, you may
be required to type in your password; every time chake calls sudo on the node,
you may be require to type in your password. For managing one or two nodes this
is probably fine, but for larger numbers of nodes it is not practical. To avoid
password prompts, you can:
- Configure SSH key-based authentication. This is more secure than using passwords.
While you are at it, you also probably want disable password authentication
completely, and only allow key-based authentication
- Configure passwordless `sudo` access for the user you use to connect to your
nodes.
## Checking connectivity and initial host setup
To check whether hosts are correctly configured, you can use the `check` task:
```
$ rake check
```
That will run the the `sudo true` command on each host. If that pass without
you having to type any passwords, it means that:
* you have SSH access to each host; and
* the user you are connecting as has password-less sudo correctly setup.
## Applying configuration
Note that by default all tasks that apply to all hosts will run in parallel,
using rake's support for multitasks. If for some reason you need to prevent
that, you can pass `-j1` (or --jobs=1`) in the rake invocation. Note that by
default rake will only run N+4 tasks in parallel, where N is the number of
cores on the machine you are running it. If you have more than N+4 hosts and
want all of them to be handled in parallel, you might want to pass `-j` (or
`--jobs`), without any number, as the last argument; with that rake will have
no limit on the number of tasks to perform in parallel.
To apply the configuration to all nodes, run
```
$ rake converge
```
To apply the configuration to a single node, run
```
$ rake converge:$NODE
```
To apply a single recipe on all nodes, run
```
$ rake apply[myrecipe]
```
What `recipe` is depends on the configuration manager.
To apply a single recipe on a specific node, run
```
$ rake apply:$NODE[myrecipe]
```
If you don't inform a recipe in the command line, you will be prompted for one.
To run a shell command on all nodes, run
```
$ rake run
```
The above will prompt you for a command, then execute it on all nodes.
To pass the command to run in the command line, use the following syntax:
```
$ rake run[command]
```
If the `command` you want to run contains spaces, or other characters that are
special do the shell, you have to quote them, for example:
```
$ rake run["cat /etc/hostname"]
```
To run a shell command on a specific node, run
```
$ rake run:$NODE[command]
```
As before, if you run just `rake run:$NODE`, you will be prompted for the
command.
To list all existing tasks, run:
```
$ rake -T
```
## Writing configuration management code
As chake supports different configuration management tools, the specifics of
configuration management code depends on the the tool you choose. See the
corresponding documentation.
## The node bootstrapping process
Some of the configuration management tools require some software to be
installed on the managed hosts. When that's the case, chake acts on a node for
the first time, it has to bootstrap it. The bootstrapping process includes
doing the following:
- installing and configuring the needed software
- setting up the hostname
## Node URLs
The keys in the hash that is represented in `nodes.yaml` is a node URL. All
components of the URL but the hostname are optional, so just listing hostnames
is the simplest form of specifying your nodes. Here are all the components of
the node URLs:
```
[connection://][username@]hostname[:port][/path]
```
* `connection`: what to use to connect to the host. `ssh` or `local` (default: `ssh`)
* `username`: user name to connect with (default: the username on your local workstation)
* `hostname`: the hostname to connect to (default: _none_)
* `port`: port number to connect to (default: 22)
* `/path`: where to store the cookbooks at the node (default: `/var/tmp/chef.$USERNAME`)
## Extra features
### Hooks
You can define rake tasks that will be executed before bootstrapping nodes,
before uploading configuration management content to nodes, and before
converging. To do this, you just need to enhance the corresponding tasks:
* `bootstrap_common`: executed before bootstrapping nodes (even if nodes have
already been bootstrapped)
* `upload_common`: executed before uploading content to the node
* `converge_common`: executed before converging (i.e. running chef)
* `connect_common`: executed before doing any action that connects to any of
the hosts. This can be used for example to generate a ssh configuration file
based on the contents of the nodes definition files.
Example:
```ruby
task :bootstrap_common do
sh './scripts/pre-bootstrap-checks'
end
```
### Encrypted files
`chake` supports encrypted files matching either `\*.gpg` or `\*.asc`. There are
two ways of specicying per-host encrypted files:
1. listing them in the `encrypted` attribute in the node configuration file.
Example:
```yaml
host1.mycompany.com:
itamae:
- roles/basic.rb
encrypted:
- foo.txt.asc
```
2. (deprecated) any files matching
`\*\*/files/{default,host-#{node}}/\*.{asc,gpg}` and
`\*\*/files/\*.{asc,gpg}`, **if** `encrypted` is not defined in the node
configuration.
They will be decrypted with GnuPG before being sent to the node (for the
configuration management tools that required files to be sent), without the
`\*.asc` or `\*.gpg` extension. You can use them to store passwords and other
sensitive information (SSL keys, etc) in the repository together with the rest
of the configuration.
For configuration managers that don't require uploading files to the managed
node, this decryption will happen right before converging or applying single
recipes, and the decrypted files will be wiped right after that.
If you use this feature, make sure that you have the `wipe` program installed.
This way chake will be able to delete the decrypted files in a slightly more
secure way, after being done with them.
### repository-local SSH configuration
If you need special SSH configuration parameters, you can create a file called
`.ssh_config` (or whatever file name you have in the `$CHAKE_SSH_CONFIG`
environment variable, see below for details) in at the root of your repository,
and chake will use it when calling `ssh`.
### Logging in to a host
To easily login to one of your host, just run `rake login:$HOSTNAME`. This will
automatically use the repository-local SSH configuration as above so you don't
have to type `-F .ssh_config` all the time.
### Running all SSH invocations with some prefix command
Some times, you will also want or need to prefix your SSH invocations with some
prefix command in order to e.g. tunnel it through some central exit node. You
can do this by setting `$CHAKE_SSH_PREFIX` on your environment. Example:
```bash
CHAKE_SSH_PREFIX=tsocks rake converge
```
The above will make all SSH invocations to all hosts be called as `tsocks ssh
[...]`
### Converging local host
If you want to manage your local workstation with chake, you can declare a
local node using the "local" connection type, like this (in `nodes.yaml`):
```yaml
local://thunderbolt:
itamae:
- role/workstation.rb
```
To apply the configuration to the local host, you can use the conventional
`rake converge:thunderbolt`, or the special target `rake local`.
When converging all nodes, `chake` will skip nodes that are declared with the
`local://` connection and whose hostname does not match the hostname in the
declaration. For example:
```yaml
local://desktop:
itamae:
- role/workstation.rb
local://laptop:
itamae:
- role/workstation.rb
```
When you run `rake converge` on `desktop`, `laptop` will be skipped, and
vice-versa.
### Accessing node data from your own tasks
It's often useful to be able to run arbitrary commands against the data you
have about nodes. You can use the `Chake.nodes` for that. For example, if you
want to geolocate each of yours hosts:
```ruby
task :geolocate do
Chake.nodes.each do |node|
puts "#{node.hostname}: %s" % `geoiplookup #{node.hostname}`.strip
end
end
```
## Environment variables
* `$CHAKE_SSH_CONFIG`:
Local SSH configuration file. Defaults to `.ssh_config`.
* `$CHAKE_SSH_PREFIX`:
Command to prefix SSH (and rsync over SSH) calls with.
* `$CHAKE_RSYNC_OPTIONS`:
extra options to pass to `rsync`. Useful to e.g. exclude large files from
being upload to each server.
* `$CHAKE_NODES`:
File containing the list of servers to be managed. Default: `nodes.yaml`.
* `$CHAKE_NODES_D`:
Directory containing node definition files servers to be managed. Default: `nodes.d`.
* `$CHAKE_TMPDIR`:
Directory used to store temporary cache files. Default: `tmp/chake`.
* `$CHAKE_CHEF_CONFIG`:
Chef configuration file, relative to the root of the repository. Default: `config.rb`.
## See also
* **rake(1)**
* **chake-itamae(7)**, https://itamae.kitchen/
* **chake-shell(7)**
* **chake-chef(7)**, **chef-solo(1)**, https://docs.chef.io/
chake-0.91/bin/ 0000755 0000041 0000041 00000000000 14537556746 013344 5 ustar www-data www-data chake-0.91/bin/chake 0000755 0000041 0000041 00000001415 14537556746 014346 0 ustar www-data www-data #!/usr/bin/env ruby
require 'rake'
rakefiles = %w[rakefile Rakefile rakefile.rb Rakefile.rb]
if rakefiles.none? { |f| File.exist?(f) } && !ARGV.include?('-f') && !ARGV.include?('--rakefile')
require 'tmpdir'
require 'fileutils'
# syntethize a Rakefile
tmpdir = Dir.mktmpdir
rakefile = File.join(tmpdir, 'Rakefile')
File.open(rakefile, 'w') do |f|
f.puts 'require "chake"'
end
ARGV.unshift << '--rakefile' << rakefile
# cleanup after finishing
at_exit do
FileUtils.rm_rf tmpdir
end
end
class Rake::Application
alias orig_thread_pool thread_pool
def thread_pool # :nodoc:
if Chake.respond_to?(:nodes)
@thread_pool ||= Rake::ThreadPool.new(Chake.nodes.size + 1)
else
orig_thread_pool
end
end
end
Rake.application.run
chake-0.91/.manifest 0000644 0000041 0000041 00000004224 14537556746 014405 0 ustar www-data www-data .ackrc
.gitignore
.gitlab-ci.yml
.manifest
.rubocop.yml
.rubocop_todo.yml
ChangeLog.md
Gemfile
LICENSE.txt
README.chef.md
README.itamae-remote.md
README.itamae.md
README.md
README.shell.md
Rakefile
activate.sh
bin/chake
chake.gemspec
chake.spec.erb
examples/test/.ssh_config
examples/test/Rakefile
examples/test/Vagrantfile
examples/test/config.rb
examples/test/cookbooks/basics/recipes/default.rb
examples/test/cookbooks/example/files/default/test
examples/test/cookbooks/example/files/host-lemur/test.asc
examples/test/cookbooks/example/recipes/default.rb
lib/chake.rb
lib/chake/bootstrap/00_set_hostname.sh
lib/chake/bootstrap/chef/01_installed.sh
lib/chake/bootstrap/chef/02_debian.sh
lib/chake/bootstrap/chef/99_unsupported.sh
lib/chake/bootstrap/itamae-remote/01_installed.sh
lib/chake/bootstrap/itamae-remote/02_debian.sh
lib/chake/bootstrap/itamae-remote/99_unsupported.sh
lib/chake/config.rb
lib/chake/config_manager.rb
lib/chake/config_manager/chef.rb
lib/chake/config_manager/itamae.rb
lib/chake/config_manager/itamae_remote.rb
lib/chake/config_manager/shell.rb
lib/chake/config_manager/skel/chef/Rakefile
lib/chake/config_manager/skel/chef/config.rb
lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/default.rb
lib/chake/config_manager/skel/chef/nodes.yaml
lib/chake/config_manager/skel/itamae/Rakefile
lib/chake/config_manager/skel/itamae/cookbooks/basics/default.rb
lib/chake/config_manager/skel/itamae/nodes.yaml
lib/chake/config_manager/skel/itamae/roles/basic.rb
lib/chake/config_manager/skel/shell/Rakefile
lib/chake/config_manager/skel/shell/nodes.yaml
lib/chake/connection.rb
lib/chake/connection/local.rb
lib/chake/connection/ssh.rb
lib/chake/node.rb
lib/chake/readline.rb
lib/chake/tmpdir.rb
lib/chake/version.rb
lib/chake/wipe.rb
lib/chake/yaml.rb
man/.gitignore
man/Rakefile
man/readme2man.sed
spec/chake/backend/local_spec.rb
spec/chake/backend/ssh_spec.rb
spec/chake/backend_spec.rb
spec/chake/config_manager/chef_spec.rb
spec/chake/config_manager/itamae_remote_spec.rb
spec/chake/config_manager/itamae_spec.rb
spec/chake/config_manager/shell_spec.rb
spec/chake/config_manager_spec.rb
spec/chake/node_spec.rb
spec/integration_tests_spec.rb
spec/spec_helper.rb
chake-0.91/spec/ 0000755 0000041 0000041 00000000000 14537556746 013526 5 ustar www-data www-data chake-0.91/spec/integration_tests_spec.rb 0000644 0000041 0000041 00000002067 14537556746 020637 0 ustar www-data www-data require 'fileutils'
require 'pathname'
require 'tmpdir'
describe 'Chake' do
include FileUtils
def sh(*args)
cmd = Shellwords.join(args)
lib = [Pathname.new(__FILE__).parent.parent / 'lib', ENV.fetch('RUBYLIB', nil)].compact.join(':')
path = [Pathname.new(__FILE__).parent.parent / 'bin', ENV.fetch('PATH', nil)].join(':')
env = {
'RUBYLIB' => lib,
'PATH' => path
}
unless system(env, *args, out: ['.out', 'w'], err: ['.err', 'w'])
out = File.read('.out')
err = File.read('.err')
raise "Command [#{cmd}] failed with exit status #{$CHILD_STATUS} (PATH = #{path}, RUBYLIB = #{lib}).\nstdout:\n#{out}\nstderr:\n#{err}"
end
rm_f '.log'
end
def chake(*args)
cmd = [Gem.ruby, '-S', 'chake'] + args
sh(*cmd)
end
def rake(*args)
cmd = [Gem.ruby, '-S', 'rake'] + args
sh(*cmd)
end
def project
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
yield dir
end
end
end
it 'loads node information' do
project do
chake 'init'
rake 'nodes'
end
end
end
chake-0.91/spec/chake/ 0000755 0000041 0000041 00000000000 14537556746 014601 5 ustar www-data www-data chake-0.91/spec/chake/config_manager_spec.rb 0000644 0000041 0000041 00000001177 14537556746 021105 0 ustar www-data www-data require 'pathname'
require 'chake/node'
require 'chake/config_manager'
describe Chake::ConfigManager do
subject { Chake::ConfigManager.new(Chake::Node.new('ssh://user@hostname.tld')) }
it 'provides a path' do
allow(subject).to receive(:name).and_return('xyz')
expect(subject.path).to eq('/var/tmp/xyz.user')
end
it 'provides bootstrap scripts' do
bootstrap_steps = subject.bootstrap_steps
expect(bootstrap_steps).to_not be_empty
bootstrap_steps.each do |path|
expect(Pathname(path)).to exist
end
end
it 'requires uploading by default' do
expect(subject.needs_upload?).to eq(true)
end
end
chake-0.91/spec/chake/config_manager/ 0000755 0000041 0000041 00000000000 14537556746 017540 5 ustar www-data www-data chake-0.91/spec/chake/config_manager/itamae_remote_spec.rb 0000644 0000041 0000041 00000002206 14537556746 023712 0 ustar www-data www-data require 'spec_helper'
require 'chake/node'
require 'chake/config_manager/itamae_remote'
describe Chake::ConfigManager::ItamaeRemote do
let(:node) do
Chake::Node.new('somehost').tap do |n|
n.silent = true
n.data['itamae-remote'] = ['foo.rb', 'bar.rb']
end
end
let(:cfg) { Chake::ConfigManager.get(node) }
it 'is detected correctly' do
expect(cfg).to be_a(Chake::ConfigManager::ItamaeRemote)
end
it 'requires uploading' do
expect(cfg.needs_upload?).to eq(true)
end
it 'calls itamae remotely to converge' do
expect(node).to receive(:run_as_root).with(
a_string_matching(%r{itamae.*#{node.path}/foo.rb.*#{node.path}/bar.rb})
)
cfg.converge
end
it 'calls itamae remotely to apply' do
expect(node).to receive(:run_as_root).with(
a_string_matching(%r{itamae.*#{node.path}/doit.rb})
)
cfg.apply('doit.rb')
end
it 'handles silent mode' do
node.silent = true
expect(node).to receive(:run_as_root).with(
a_string_matching(/--log-level\\=warn/)
)
cfg.converge
end
it 'has a name with dashes' do
expect(cfg.name).to eq('itamae-remote')
end
end
chake-0.91/spec/chake/config_manager/chef_spec.rb 0000644 0000041 0000041 00000002147 14537556746 022010 0 ustar www-data www-data require 'chake/node'
require 'chake/config_manager/chef'
describe Chake::ConfigManager::Chef do
let(:node) do
Chake::Node.new('foobar')
end
subject do
Chake::ConfigManager::Chef.new(node)
end
it 'provides a name' do
expect(subject.name).to eq('chef')
end
it 'calls chef-solo on converge' do
expect(subject).to receive(:logging).and_return('-l debug')
expect(node).to receive(:run_as_root).with(%r{chef-solo -c #{node.path}/config.rb -l debug -j #{node.path}/#{Chake.tmpdir}/foobar.json})
subject.converge
end
it 'calls chef-solo on apply' do
expect(subject).to receive(:logging).and_return('-l debug')
expect(node).to receive(:run_as_root).with(%r{chef-solo -c #{node.path}/config.rb -l debug -j #{node.path}/#{Chake.tmpdir}/foobar.json --override-runlist recipe\[myrecipe\]})
subject.apply('myrecipe')
end
context 'logging' do
it 'logs when requested' do
expect(subject.send(:logging)).to eq('')
end
it 'only show fatal errrrs when requested' do
node.silent = true
expect(subject.send(:logging)).to eq('-l fatal')
end
end
end
chake-0.91/spec/chake/config_manager/itamae_spec.rb 0000644 0000041 0000041 00000004437 14537556746 022347 0 ustar www-data www-data require 'spec_helper'
require 'chake/node'
require 'chake/config_manager/itamae'
describe Chake::ConfigManager::Itamae do
let(:hostname) { 'foobar' }
let(:node) do
Chake::Node.new(hostname).tap do |n|
n.silent = true
n.data['itamae'] = ['foo.rb', 'bar.rb']
end
end
let(:cfg) { Chake::ConfigManager::Itamae.new(node) }
let(:output) { StringIO.new("line1\nline2\n") }
it 'does not require uploading' do
expect(cfg.needs_upload?).to eq(false)
end
it 'calls itamae when converging' do
expect(IO).to receive(:popen).with(
array_including('itamae', 'foo.rb', 'bar.rb'),
'r',
err: %i[child out]
).and_return(output)
cfg.converge
end
it 'calls itamae when applying' do
expect(IO).to receive(:popen).with(
array_including('itamae', 'foobarbaz.rb'),
'r',
err: %i[child out]
).and_return(output)
cfg.apply('foobarbaz.rb')
end
context 'for ssh hosts' do
let(:hostname) { 'ssh://theusernanme@thehostname' }
it 'calls itamae ssh subcommand' do
expect(IO).to receive(:popen).with(
array_including('itamae', 'ssh', '--host=thehostname', '--user=theusernanme'),
anything,
err: anything
).and_return(output)
cfg.converge
end
end
context 'for local hosts' do
let(:hostname) { 'local://localhostname' }
it 'calls itamae with local subcommand' do
expect(IO).to receive(:popen).with(
array_including('itamae', 'local', /--node-json=.*/, 'foo.rb', 'bar.rb'),
anything,
err: anything
).and_return(output)
cfg.converge
end
end
it 'throws an error for unsupported connection' do
allow(node).to receive(:connection).and_return(Object.new)
expect { cfg.converge }.to raise_error(NotImplementedError)
end
it 'handles silent mode' do
expect(IO).to receive(:popen).with(
array_including('--log-level=warn'),
anything,
err: anything
).and_return(output)
cfg.converge
end
RSpec::Matchers.define_negated_matcher :array_excluding, :include
it 'handles non-silent mode' do
node.silent = false
expect(IO).to receive(:popen).with(
array_excluding('--log-level=warn'),
anything,
err: anything
).and_return(output)
silence($stdout) { cfg.converge }
end
end
chake-0.91/spec/chake/config_manager/shell_spec.rb 0000644 0000041 0000041 00000003253 14537556746 022211 0 ustar www-data www-data require 'chake/node'
require 'chake/config_manager'
require 'chake/config_manager/shell'
describe Chake::ConfigManager::Shell do |_c|
let(:node) do
Chake::Node.new('foobar').tap do |n|
allow(n).to receive(:path).and_return(nil)
end
end
it 'accepts node with explicit config_manager in data' do
node.data['config_manager'] = 'shell'
expect(Chake::ConfigManager.get(node)).to be_a(Chake::ConfigManager::Shell)
end
it 'accepts node with `shell` in data' do
node.data['shell'] = ['date']
expect(Chake::ConfigManager.get(node)).to be_a(Chake::ConfigManager::Shell)
end
let(:subject) { Chake::ConfigManager::Shell.new(node) }
it 'calls all shell commands on converge' do
node.data['shell'] = %w[date true]
expect(node).to receive(:run_as_root).with("sh -xec 'date && true'")
subject.converge
end
it 'changes to node path to run commands' do
node.data['shell'] = %w[true]
allow(node).to receive(:path).and_return('/foo')
expect(node).to receive(:run_as_root).with("sh -xec 'cd /foo && true'")
subject.converge
end
it 'calls given shell command on apply' do
node.data['shell'] = %w[date true]
expect(node).to receive(:run_as_root).with("sh -xec 'reboot'")
subject.apply('reboot')
end
it 'hides output on converge in silent mode' do
node.data['shell'] = ['date']
node.silent = true
expect(node).to receive(:run_as_root).with("sh -ec 'date' >/dev/null")
subject.converge
end
it 'hides output on apply in silent mode' do
node.data['shell'] = ['date']
node.silent = true
expect(node).to receive(:run_as_root).with("sh -ec 'reboot' >/dev/null")
subject.apply('reboot')
end
end
chake-0.91/spec/chake/node_spec.rb 0000644 0000041 0000041 00000005403 14537556746 017067 0 ustar www-data www-data require 'chake/node'
describe Chake::Node do
before do
ent = double
allow(ent).to receive(:name).and_return('jonhdoe')
allow(Etc).to receive(:getpwuid).and_return(ent)
end
let(:simple) { Chake::Node.new('hostname') }
it('has a name') { expect(simple.hostname).to eq('hostname') }
it('uses ssh by default') { expect(simple.connection).to be_an_instance_of(Chake::Connection::Ssh) }
it('user current username by default') {
expect(simple.username).to eq('jonhdoe')
}
it('writes to specified path') {
node = Chake::Node.new('ssh://host.tld/path/to/config')
expect(node.path).to eq('/path/to/config')
}
let(:with_username) { Chake::Node.new('username@hostname') }
it('accepts username') { expect(with_username.username).to eq('username') }
it('uses ssh') { expect(with_username.connection).to be_an_instance_of(Chake::Connection::Ssh) }
let(:with_connection) { Chake::Node.new('local://hostname') }
it('accepts connection as URI scheme') { expect(with_connection.connection).to be_an_instance_of(Chake::Connection::Local) }
it('wont accept any connection') do
expect { Chake::Node.new('foobar://bazqux').connection }.to raise_error(ArgumentError)
end
let(:with_data) { Chake::Node.new('local://localhost', 'run_list' => ['recipe[common]']) }
it('takes data') do
expect(with_data.data).to be_a(Hash)
end
let(:with_port) { Chake::Node.new('ssh://foo.bar.com:2222') }
it('accepts a port specification') do
expect(with_port.port).to eq(2222)
end
let(:with_port_but_no_scheme) { Chake::Node.new('foo.bar.com:2222') }
it('accepts a port specification without a scheme') do
expect(with_port_but_no_scheme.port).to eq(2222)
expect(with_port_but_no_scheme.connection.to_s).to eq('ssh')
end
%i[run run_as_root rsync_dest].each do |method|
it("delegates #{method} to connection") do
node = simple
connection = double
args = Object.new
allow(node).to receive(:connection).and_return(connection)
expect(connection).to receive(method).with(args)
node.send(method, args)
end
end
it 'delegates converge to config_manager' do
node = simple
expect(node.config_manager).to receive(:converge)
node.converge
end
it 'delegates apply to config_manager' do
node = simple
expect(node.config_manager).to receive(:apply).with('myrecipe')
node.apply('myrecipe')
end
it 'falls back to writing to path specified by config manager' do
expect(simple.path).to eq(simple.config_manager.path)
end
it 'calculates max node name length' do
Chake::Node.max_node_name_length = 0
Chake::Node.new('foobar')
expect(Chake::Node.max_node_name_length).to eq(6)
Chake::Node.new('foobarbaz')
expect(Chake::Node.max_node_name_length).to eq(9)
end
end
chake-0.91/spec/chake/backend_spec.rb 0000644 0000041 0000041 00000000033 14537556746 017523 0 ustar www-data www-data require 'chake/connection'
chake-0.91/spec/chake/backend/ 0000755 0000041 0000041 00000000000 14537556746 016170 5 ustar www-data www-data chake-0.91/spec/chake/backend/local_spec.rb 0000644 0000041 0000041 00000001031 14537556746 020614 0 ustar www-data www-data require 'spec_helper'
describe Chake::Connection::Local do
include_examples 'Chake::Connection', Chake::Connection::Local
let(:node) { Chake::Node.new('local://myusername@myhost/srv/chake') }
it('runs commands with sh -c') { expect(connection.command_runner).to eq(['sh', '-c']) }
it('rsyncs locally') { expect(connection.rsync_dest).to eq('/srv/chake/') }
it('skips if hostname is not the local hostname') do
allow(Socket).to receive(:gethostname).and_return('otherhost')
expect(node.skip?).to eq(true)
end
end
chake-0.91/spec/chake/backend/ssh_spec.rb 0000644 0000041 0000041 00000002162 14537556746 020325 0 ustar www-data www-data require 'spec_helper'
describe Chake::Connection::Ssh do
include_examples 'Chake::Connection', Chake::Connection::Ssh
let(:node) { Chake::Node.new('ssh://myuser@myhost/srv/chake') }
it('runs commands with ssh') { expect(connection.command_runner).to eq(['ssh', 'myuser@myhost']) }
it('rsyncs over ssh') { expect(connection.rsync_dest).to eq('myuser@myhost:/srv/chake/') }
it 'uses no remote username if none was passed' do
node = Chake::Node.new('theserver')
expect(node.username).to eq(Etc.getpwuid.name)
expect(node.remote_username).to be_nil
end
it 'uses username is passwd' do
expect(node.username).to eq('myuser')
expect(node.remote_username).to eq('myuser')
end
context 'with a custom port' do
let(:node) { Chake::Node.new('ssh://myhost:2222') }
it 'uses port with ssh' do
expect(connection.command_runner).to eq(['ssh', '-p', '2222', 'myhost'])
end
it 'uses port with scp' do
expect(connection.scp).to eq(['scp', '-P', '2222'])
end
it 'uses port with rsync' do
expect(connection.send(:rsync_ssh)).to eq(['-e', 'ssh -p 2222'])
end
end
end
chake-0.91/spec/spec_helper.rb 0000644 0000041 0000041 00000003210 14537556746 016340 0 ustar www-data www-data begin
require 'simplecov'
SimpleCov.start do
minimum_coverage 35.3
track_files 'lib/**/*.rb'
add_filter %r{^/spec/}
add_filter %r{^/lib/chake/config_manager/skel/}
end
rescue LoadError
puts "W: simplecov not installed, we won't have a coverage report"
end
require 'chake/node'
require 'chake/connection'
require 'rspec/version'
if RSpec::Version::STRING < '2.14'
puts 'Skipping tests, need RSpec >= 2.14'
exit
end
shared_examples 'Chake::Connection' do |connection_class|
let(:connection) { connection_class.new(node) }
it('runs commands') do
io = StringIO.new("line 1\n line 2\n")
expect(IO).to receive(:popen).with(connection.command_runner + ['/bin/sh'], 'w+', Hash).and_return(io)
expect(io).to receive(:write).with('something').ordered
expect(io).to receive(:close_write).ordered
expect(node).to receive(:log).with('$ something')
expect(node).to receive(:log).with('line 1')
expect(node).to receive(:log).with(' line 2')
connection.run('something')
end
it('runs as root') do
expect(connection).to receive(:run).with('sudo something')
connection.run_as_root('something')
end
it('does not use sudo if already root') do
allow(connection.node).to receive(:remote_username).and_return('root')
expect(connection).to receive(:run).with('something')
connection.run_as_root('something')
end
end
module Helpers
def silence(stream)
orig_stream = stream.clone
begin
File.open('/dev/null', 'w') do |f|
stream.reopen(f)
yield
end
ensure
stream.reopen(orig_stream)
end
end
end
RSpec.configure do |c|
c.include Helpers
end
chake-0.91/.rubocop.yml 0000644 0000041 0000041 00000001463 14537556746 015052 0 ustar www-data www-data inherit_from: .rubocop_todo.yml
AllCops:
NewCops: enable
Exclude:
- pkg/**/*
Layout/LineLength:
Enabled: false
Metrics/AbcSize:
Enabled: false
Metrics/BlockLength:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/PerceivedComplexity:
Enabled: false
Style/Documentation:
Enabled: false
Style/FormatString:
EnforcedStyle: percent
Style/FrozenStringLiteralComment:
Enabled: false
Style/GlobalVars:
Exclude:
- lib/chake.rb
Style/GuardClause:
Enabled: false
Style/HashEachMethods:
Enabled: false
Style/HashTransformKeys:
Enabled: false
Style/HashTransformValues:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
Style/SymbolArray:
Enabled: false
Gemspec/RequiredRubyVersion:
Enabled: false
Gemspec/DeprecatedAttributeAssignment:
Enabled: false
chake-0.91/README.chef.md 0000644 0000041 0000041 00000003414 14537556746 014761 0 ustar www-data www-data chake-chef(7) -- configure chake nodes with chef-solo
=====================================================
## Description
This configuration manager will allow you to manage nodes by running
**chef-solo(1)** on each remote node.
When `chake init` runs, the following chef-specific files will be created:
A brief explanation of the created files that are specific to **chef**:
* `config.rb`: contains the chef-solo configuration. You can modify it, but
usually you won't need to.
* `config/roles`: directory is where you can put your role definitions.
* `cookbooks`: directory where you will store your cookbooks. A sample cookbook
called "basics" is created, but feel free to remove it and add actual
cookbooks.
## Configuration
Nodes can be configured to be managed with chef by having a `run_list` key in
their configuration:
```yaml
host1.mycompany.com:
run_list:
- role[server]
- recipe[service1]
service1:
option1: "here we go"
```
Any extra configuration under `host1.mycompany.com` will be saved to a JSON file
and given to the chef-solo --node-json option in the command line. For example,
the above configuration will produce a JSON file that looks like this:
```json
{
"run_list": [
"role[server]",
"recipe[service1]"
]
,
"service1": {
"option1": "here we go"
}
}
```
Inside Chef recipes, you can access those values by using the `node` object.
For example:
```ruby
template "/etc/service1.conf.d/option1.conf" do
variables option1: node["option1"]
end
```
## Bootstrapping
The bootstrap process for _chef_ involves getting chef-solo installed. The
node hostname will also be set based on the hostname informed in the
configuration file.
## See also
* **chake(1)**
*
*
chake-0.91/chake.spec.erb 0000644 0000041 0000041 00000001504 14537556746 015272 0 ustar www-data www-data %define gem_name <%= pkg.name %>
Summary: <%= pkg.summary %>
Name: <%= pkg.name %>
Version: <%= pkg.version %>
Release: 1
Source0: %{name}-%{version}.tar.gz
License: <%= pkg.license %>
Group: Development/Tools
Prefix: %{_prefix}
Vendor: <%= pkg.authors.first %> <<%= pkg.email.first %>>
Url: <%= pkg.homepage %>
BuildArch: noarch
BuildRequires: ruby, rubygems-devel, rubygem-rake, rubygem-bundler
Requires: ruby, rubygem-rake
%description
<%= pkg.description %>
%prep
%setup -n %{name}-%{version}
%build
%{__rm} -rf %{buildroot}
sed -i -e 's#spec.files\s*=.*#spec.files = Dir.glob("**/*")#' %{name}.gemspec
rake bundler:build
%gem_install -n pkg/%{name}-%{version}.gem
%install
cp -a usr %{buildroot}/usr
%clean
rm -rf $RPM_BUILD_ROOT
%files
%{gem_instdir}/
%exclude %{gem_cache}
%{gem_spec}
%{_bindir}/chake
%doc %{gem_docdir}
chake-0.91/.gitignore 0000644 0000041 0000041 00000000330 14537556746 014560 0 ustar www-data www-data *.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
/examples/test/nodes.yaml
/examples/test/.tmp
.*.html
*.1
*.7
chake-0.91/README.itamae-remote.md 0000644 0000041 0000041 00000003206 14537556746 016604 0 ustar www-data www-data chake-itamae-remote(7) -- configure chake nodes with a remote itamae
====================================================================
## Description
This configuration manager will run **itamae(1)** on each remote node. This is
different from the _itamae_ configuration manager, which runs itamae on your
workstation (the host running chake). _itamae-remote_ will run itamae
individually on each node, which is one order of magnitude faster. This
requires itamae to be installed on each node, and that will be taken care of
automatically by the bootstrapping process.
## Configuration
The _itamae-remote_ configuration manager requires one key called
`itamae-remote`, and the value must be a list of strings representing the list
of recipes to apply to the node when converging.
```yaml
host1.mycompany.com:
itamae-remote:
- cookbooks/basic/default.rb
- roles/server.rb
service1:
option1: "here we go"
```
Any extra configuration under `host1.mycompany.com` will be saved to a JSON file
and given to the itamae --node-json option in the command line. For example,
the above configuration will produce a JSON file that looks like this:
```json
{
"itamae": [
"cookbooks/basic.rb",
"roles/server.rb"
]
,
"service1": {
"option1": "here we go"
}
}
```
Inside itamae recipes, you can access those values by using the `node` object.
For example:
```ruby
template "/etc/service1.conf.d/option1.conf" do
variables option1: node["option1"]
end
```
## Bootstrapping
The bootstrapping process will make sure itamae is installed. The node hostname
will be set according to your chake configuration.
## See also
* **chake(1)**
chake-0.91/man/ 0000755 0000041 0000041 00000000000 14537556746 013347 5 ustar www-data www-data chake-0.91/man/.gitignore 0000644 0000041 0000041 00000000024 14537556746 015333 0 ustar www-data www-data chake.1*
chake.adoc
chake-0.91/man/Rakefile 0000644 0000041 0000041 00000002136 14537556746 015016 0 ustar www-data www-data MAIN_MANPAGE = 'man/chake.1'.freeze
OTHER_MANPAGES = %w[
man/chake-chef.7
man/chake-itamae.7
man/chake-itamae-remote.7
man/chake-shell.7
].freeze
MANPAGES = [MAIN_MANPAGE] + OTHER_MANPAGES
task default: :man
task man: MANPAGES
MANPAGES.each do |man|
source = "README#{man.pathmap('%n').sub(/^chake/, '').sub('-', '.')}.md"
file man => [source, 'man/readme2man.sed'] do
sh "sed -f man/readme2man.sed #{source} > #{man}.ronn || (rm -f #{man}.ronn; false)"
sh "ronn --roff #{man}.ronn"
sh "rm -f #{man}.ronn"
sh 'sed', '-i', '-e', 's/\\\\\'/\'/g', man
end
end
task install: MANPAGES do
prefix = ENV['PREFIX'] || (File.exist?('debian/rules') && '/usr') || '/usr/local'
man1 = File.join(*[ENV.fetch('DESTDIR', nil), prefix, 'share/man/man1'].compact)
man7 = File.join(*[ENV.fetch('DESTDIR', nil), prefix, 'share/man/man7'].compact)
target = { '.1' => man1, '.7' => man7 }
sh 'install', '-d', '-m', '0755', man1
sh 'install', '-d', '-m', '0755', man7
MANPAGES.each do |m|
sh 'install', '-m', '0644', m, target[m.pathmap('%x')]
end
end
task :clean do
rm_f MANPAGES
end
chake-0.91/man/readme2man.sed 0000644 0000041 0000041 00000000225 14537556746 016056 0 ustar www-data www-data # Capitalize section titles
s/^\(##\+\)\(.*\)/\1 \U\2/
# Turn fenced code blocks into 4-space indented blocks
/^```/,/```/ s/^/ /
/^ ```.*/ d
chake-0.91/activate.sh 0000644 0000041 0000041 00000000143 14537556746 014726 0 ustar www-data www-data base=$(dirname $BASH_SOURCE)
export RUBYLIB=${base}/lib:${RUBYLIB}
export PATH=${base}/bin:${PATH}
chake-0.91/examples/ 0000755 0000041 0000041 00000000000 14537556746 014412 5 ustar www-data www-data chake-0.91/examples/test/ 0000755 0000041 0000041 00000000000 14537556746 015371 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/ 0000755 0000041 0000041 00000000000 14537556746 017362 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/basics/ 0000755 0000041 0000041 00000000000 14537556746 020626 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/basics/recipes/ 0000755 0000041 0000041 00000000000 14537556746 022260 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/basics/recipes/default.rb 0000644 0000041 0000041 00000000031 14537556746 024223 0 ustar www-data www-data package 'openssh-server'
chake-0.91/examples/test/cookbooks/example/ 0000755 0000041 0000041 00000000000 14537556746 021015 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/example/recipes/ 0000755 0000041 0000041 00000000000 14537556746 022447 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/example/recipes/default.rb 0000644 0000041 0000041 00000000403 14537556746 024415 0 ustar www-data www-data file '/tmp/chake.test' do
content "It works on #{node[:fqdn]}!\n"
mode '0644'
owner 'root'
group 'root'
end
cookbook_file '/tmp/chake.test.unencrypted' do
source 'test'
mode '0600'
owner 'root'
group 'root'
end
chake-0.91/examples/test/cookbooks/example/files/ 0000755 0000041 0000041 00000000000 14537556746 022117 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/example/files/default/ 0000755 0000041 0000041 00000000000 14537556746 023543 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/example/files/default/test 0000644 0000041 0000041 00000000005 14537556746 024440 0 ustar www-data www-data test
chake-0.91/examples/test/cookbooks/example/files/host-lemur/ 0000755 0000041 0000041 00000000000 14537556746 024216 5 ustar www-data www-data chake-0.91/examples/test/cookbooks/example/files/host-lemur/test.asc 0000644 0000041 0000041 00000001564 14537556746 025673 0 ustar www-data www-data -----BEGIN PGP MESSAGE-----
Version: GnuPG v1
hQIMA5A8ZkAWdYz7AQ//dgQhKXuGS0dY04TXa1cXEtOHYC64LLnoo+UZT/KgnkzI
/IAgLFbAV0Fd9DGLK857qv9fWf/FB5b6loYLPOVEys0mb5aQrqPPFeKwTTXxTJdk
Ts2NvSVfNZx95Igotm/vkKW6/dbGlDfQSOVQMzhvmKOMMpRP+ixqn+G/nwE/0wQG
QXc3UPAe9gBFlI9GWLTafhftwrxXiYeNF8N03W9BUk1OiQqJUmDRK9ZjVe/vbEiZ
iQ689MnF5l+/6gptU0j77QIqk5vEItkl7RISxOS8PDFI+926NV3fZUrRAlu2eDIy
HNMGkcKTjGoPNrj/UzzmqjP3uNQtKoK559cul7uY44QmsEINoFP3HoAOHeVxm1pG
6IG5EY6znwVEYTIVPK21NAPFpfk9yAB53sv6GrtOaYp8FFp+lLHJlPQcOOP51UV9
GqiAuec7Lgr3iy1yaXIBHFlLG0lmZ1OI41zwqh+Z8EF1NC0gFPhGuOJqBGmBbOxy
Wt1IFx1JUHi0f277us9pFbQlcUb8tgFZ0J69epBrod+xWaZQKFwLWsCTN66fKJfp
rqBzRBcNDJwsKOd54v/Cmrws8bwnfB8iNZYuEQdEt9u5TGIZFl4R9k+/pydAQouP
Z95g4vh2ST2ZEeblnbCc2TFY/7j2O+aXyBzX/4+/bM0kZbFjxCNuefU/v3ZnDwXS
QAH+NPQ31IiqMmoETUqHzo1UI+hV9em81Llt2bsbgWULp84LEmzczbjAcMi2EWNt
SzPyJzOLrqEvEYg2O/+bGho=
=Y2iZ
-----END PGP MESSAGE-----
chake-0.91/examples/test/Rakefile 0000644 0000041 0000041 00000000663 14537556746 017043 0 ustar www-data www-data $LOAD_PATH.unshift '../../lib' # this shouldn't be needed when you have chake installed
require 'chake'
manifest = %w[
Rakefile
cookbooks
cookbooks/example
cookbooks/example/recipes
cookbooks/example/recipes/default.rb
cookbooks/example/files
cookbooks/example/files/host-homer
cookbooks/example/files/host-homer/test.asc
config.rb
]
desc 'removes everything'
task :clean do
rm_rf Dir.glob('**/*') - manifest
end
chake-0.91/examples/test/Vagrantfile 0000644 0000041 0000041 00000000165 14537556746 017560 0 ustar www-data www-data # -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure('2') do |config|
config.vm.box = 'debian/buster64'
end
chake-0.91/examples/test/config.rb 0000644 0000041 0000041 00000000200 14537556746 017153 0 ustar www-data www-data root = __dir__
file_cache_path "#{root}/cache"
cookbook_path "#{root}/cookbooks"
role_path "#{root}/config/roles"
chake-0.91/examples/test/.ssh_config 0000644 0000041 0000041 00000000142 14537556746 017511 0 ustar www-data www-data Host test.local
IdentityFile .vagrant/machines/default/libvirt/private_key
# vim: ft=sshconfig
chake-0.91/.rubocop_todo.yml 0000644 0000041 0000041 00000002161 14537556746 016073 0 ustar www-data www-data # This configuration was generated by
# `rubocop --auto-gen-config`
# on 2020-03-25 17:43:43 -0300 using RuboCop version 0.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent
Layout/HeredocIndentation:
Exclude:
- 'lib/chake.rb'
# Offense count: 3
Lint/AmbiguousOperator:
Exclude:
- 'lib/chake.rb'
# Offense count: 1
Lint/InterpolationCheck:
Exclude:
- 'lib/chake.rb'
# Offense count: 1
# Configuration parameters: Blacklist.
# Blacklist: END, (?-mix:EO[A-Z]{1})
Naming/HeredocDelimiterNaming:
Exclude:
- 'lib/chake.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle.
# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
Exclude:
- 'bin/chake'
chake-0.91/README.itamae.md 0000644 0000041 0000041 00000002624 14537556746 015316 0 ustar www-data www-data chake-itamae(7) -- configure chake nodes with itamae
====================================================
## Description
This configuration manager will run **itamae(1)** against your nodes.
## Configuration
The _itamae_ configuration manager requires one key called `itamae`, and the
value must be a list of strings representing the list of recipes to apply to
the node when converging.
```yaml
host1.mycompany.com:
itamae:
- cookbooks/basic/default.rb
- roles/server.rb
service1:
option1: "here we go"
```
Any extra configuration under `host1.mycompany.com` will be saved to a JSON file
and given to the itamae --node-json option in the command line. For example,
the above configuration will produce a JSON file that looks like this:
```json
{
"itamae": [
"cookbooks/basic.rb",
"roles/server.rb"
]
,
"service1": {
"option1": "here we go"
}
}
```
Inside itamae recipes, you can access those values by using the `node` object.
For example:
```ruby
template "/etc/service1.conf.d/option1.conf" do
variables option1: node["option1"]
end
```
## Bootstrapping
Very little bootstrapping is required for this configuration manager, as itamae
requires no setup on the node site since the Ruby code in the recipes is
interpreted locally and not on the nodes. During bootstrapping, only the node
hostname will be set according to your chake configuration.
## See also
* **chake(1)**
chake-0.91/Rakefile 0000644 0000041 0000041 00000005702 14537556746 014245 0 ustar www-data www-data namespace :bundler do
require 'bundler/gem_tasks'
end
task :test do
sh 'rspec', '--color'
end
pkg = Gem::Specification.load('chake.gemspec')
task 'bundler:build' => :manifest
task 'build:tarball' => 'bundler:build' do
chdir 'pkg' do
sh 'gem2tgz', "#{pkg.name}-#{pkg.version}.gem"
end
end
desc 'Create Debian source package'
task 'build:debsrc' => ['bundler:clobber', 'build:tarball'] do
dirname = "#{pkg.name}-#{pkg.version}"
v = `git describe`.strip.tr('-', '.').sub(/^v/, '')
chdir 'pkg' do
sh 'gem2deb', '--no-wnpp-check', '-s', '-p', pkg.name, "#{dirname}.tar.gz"
sh "rename s/#{pkg.version}/#{v}/ *.orig.tar.gz"
chdir dirname do
ln 'man/Rakefile', 'debian/dh_ruby.rake'
sh "dch --preserve -v #{v}-1 'Development snapshot'"
sh "sed -i -e 's/#{pkg.version}/#{v}/' lib/chake/version.rb"
sh 'dpkg-buildpackage', '--diff-ignore=version.rb', '-S', '-us', '-uc'
end
end
end
desc 'Builds and installs Debian package'
task 'deb:install' => 'build:debsrc' do
chdir "pkg/#{pkg.name}-#{pkg.version}" do
sh 'dpkg-buildpackage --diff-ignore=version.rb -us -uc'
sh 'sudo apt-get install -qy --reinstall $(debc --list-debs)'
end
end
desc 'Create source RPM package'
task 'build:rpmsrc' => ['build:tarball', 'pkg/chake.spec'] do
chdir 'pkg' do
sh 'rpmbuild --define "_topdir .rpmbuild" --define "_sourcedir $(pwd)" --define "_srcrpmdir $(pwd)" -bs chake.spec --nodeps'
end
end
file 'pkg/chake.spec' => ['chake.spec.erb', 'lib/chake/version.rb'] do |t|
require 'erb'
pkg = Gem::Specification.load('chake.gemspec')
template = ERB.new(File.read('chake.spec.erb'))
File.open(t.name, 'w') do |f|
f.puts(template.result(binding))
end
puts "Generated #{t.name}"
end
task 'build:all' => ['build:debsrc', 'build:rpmsrc']
desc 'lists changes since last release'
task :changelog do
last_tag = `git tag | sort -V`.split.last
sh 'git', 'shortlog', "#{last_tag}.."
end
task :check_tag do
last_tag = `git tag | sort -V`.split.last
if last_tag == "v#{pkg.version}"
raise "Version #{pkg.version} was already released!"
end
end
desc 'checks if the latest release is properly documented in ChangeLog.md'
task :check_changelog do
sh 'grep', "^#\\s*#{pkg.version}", 'ChangeLog.md'
rescue StandardError
puts "Version #{pkg.version} not documented in ChangeLog.md!"
raise
end
desc 'Updates manifest file'
task :manifest do
manifest = File.read('.manifest')
git = `git ls-files`
if manifest != git
File.write('.manifest', git)
sh 'git commit .manifest -m "Update manifest"'
end
end
desc 'Makes a release'
task release: [:check_tag, :check_changelog, :default, 'bundler:release']
desc 'Check coding style'
task :style do
sh 'rubocop'
end
desc 'Check spelling in the source code'
task :codespell do
sh 'codespell', '--skip=.git', '--skip=coverage', '--skip=*.asc', '--skip=*.swp', '--skip=tags'
end
task default: [:test, :style, :codespell]
task clean: 'bundler:clobber'
load './man/Rakefile'
chake-0.91/lib/ 0000755 0000041 0000041 00000000000 14537556746 013342 5 ustar www-data www-data chake-0.91/lib/chake/ 0000755 0000041 0000041 00000000000 14537556746 014415 5 ustar www-data www-data chake-0.91/lib/chake/wipe.rb 0000644 0000041 0000041 00000000575 14537556746 015715 0 ustar www-data www-data require 'singleton'
module Chake
class Wipe
include Singleton
if system('which', 'wipe', out: '/dev/null', err: :out)
def wipe(file)
system('wipe', '-rfs', file)
end
else
warn 'W: please install the \`wipe\` program for secure deletion, falling back to unlink(2)'
def wipe(file)
File.unlink(file)
end
end
end
end
chake-0.91/lib/chake/version.rb 0000644 0000041 0000041 00000000053 14537556746 016425 0 ustar www-data www-data module Chake
VERSION = '0.91'.freeze
end
chake-0.91/lib/chake/connection.rb 0000644 0000041 0000041 00000003107 14537556746 017102 0 ustar www-data www-data require 'English'
module Chake
Connection = Struct.new(:node)
class Connection
class CommandFailed < RuntimeError
end
def scp
['scp']
end
def scp_dest
''
end
def rsync
['rsync']
end
def rsync_dest
"#{node.path}/"
end
def run(cmd)
node.log('$ %s' % { command: cmd })
io = IO.popen(command_runner + ['/bin/sh'], 'w+', err: %i[child out])
io.write(cmd)
io.close_write
read_output(io)
end
def read_output(io)
io.each_line do |line|
node.log(line.gsub(/\s*$/, ''))
end
io.close
if $CHILD_STATUS
status = $CHILD_STATUS.exitstatus
if status != 0
raise CommandFailed, [node.hostname, 'FAILED with exit status %d' % { status: status }].join(': ')
end
end
end
def run_shell
system(*shell_command)
end
def run_as_root(cmd)
if node.remote_username == 'root'
run(cmd)
else
run("sudo #{cmd}")
end
end
def to_s
self.class.connection_name
end
def skip?
false
end
def self.connection_name
name.split('::').last.downcase
end
def self.inherited(subclass)
super
@connections ||= []
@connections << subclass
end
def self.get(name)
connection = @connections.find { |b| b.connection_name == name }
raise ArgumentError, "Invalid connection name: #{name}" unless connection
connection
end
end
end
require 'chake/connection/ssh'
require 'chake/connection/local'
chake-0.91/lib/chake/config_manager.rb 0000644 0000041 0000041 00000004033 14537556746 017701 0 ustar www-data www-data require 'pathname'
module Chake
class ConfigManager
attr_reader :node
def initialize(node)
@node = node
end
def converge; end
def apply(config); end
def path
"/var/tmp/#{name}.#{node.username}"
end
def name
self.class.short_name
end
def to_s
name
end
def bootstrap_steps
base = File.join(File.absolute_path(File.dirname(__FILE__)), 'bootstrap')
steps = Dir[File.join(base, '*.sh')] + Dir[File.join(base, name, '*.sh')]
steps.sort_by { |f| File.basename(f) }
end
def needs_upload?
true
end
def self.short_name
name.split('::').last.gsub(/([[:lower:]])([[:upper:]])/) do
first = Regexp.last_match(1)
last = Regexp.last_match(2).downcase
"#{first}-#{last}"
end.downcase
end
def self.priority(new_priority = nil)
@priority ||= new_priority || 50
end
def self.inherited(klass)
super
@subclasses ||= []
@subclasses << klass
end
def self.get(node)
available = @subclasses.sort_by(&:priority)
manager = available.find { |c| c.short_name == node.data['config_manager'] }
manager ||= available.find { |c| c.accept?(node) }
raise ArgumentError, "Can't find configuration manager class for node #{node.hostname}. Available: #{available}.join(', ')}" unless manager
manager.new(node)
end
def self.accept?(_node)
false
end
def self.all
@subclasses
end
def self.init
skel = Pathname(__FILE__).parent / 'config_manager' / 'skel' / short_name
skel.glob('**/*').each do |source|
target = source.relative_path_from(skel)
if target.exist?
puts "exists: #{target}"
else
if source.directory?
FileUtils.mkdir_p target
else
FileUtils.cp source, target
end
puts "create: #{target}"
end
end
end
end
end
Dir["#{File.dirname(__FILE__)}/config_manager/*.rb"].sort.each do |f|
require f
end
chake-0.91/lib/chake/config_manager/ 0000755 0000041 0000041 00000000000 14537556746 017354 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/ 0000755 0000041 0000041 00000000000 14537556746 020312 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/chef/ 0000755 0000041 0000041 00000000000 14537556746 021217 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/chef/cookbooks/ 0000755 0000041 0000041 00000000000 14537556746 023210 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/chef/cookbooks/basics/ 0000755 0000041 0000041 00000000000 14537556746 024454 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/ 0000755 0000041 0000041 00000000000 14537556746 026106 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/default.rb 0000644 0000041 0000041 00000000031 14537556746 030051 0 ustar www-data www-data package 'openssh-server'
chake-0.91/lib/chake/config_manager/skel/chef/Rakefile 0000644 0000041 0000041 00000000020 14537556746 022654 0 ustar www-data www-data require 'chake'
chake-0.91/lib/chake/config_manager/skel/chef/nodes.yaml 0000644 0000041 0000041 00000000066 14537556746 023215 0 ustar www-data www-data host1.mycompany.com:
run_list:
- recipe[basics]
chake-0.91/lib/chake/config_manager/skel/chef/config.rb 0000644 0000041 0000041 00000000200 14537556746 023001 0 ustar www-data www-data root = __dir__
file_cache_path "#{root}/cache"
cookbook_path "#{root}/cookbooks"
role_path "#{root}/config/roles"
chake-0.91/lib/chake/config_manager/skel/shell/ 0000755 0000041 0000041 00000000000 14537556746 021421 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/shell/Rakefile 0000644 0000041 0000041 00000000020 14537556746 023056 0 ustar www-data www-data require 'chake'
chake-0.91/lib/chake/config_manager/skel/shell/nodes.yaml 0000644 0000041 0000041 00000000067 14537556746 023420 0 ustar www-data www-data host1.mycompany.com:
shell:
- echo "HELLO WORLD"
chake-0.91/lib/chake/config_manager/skel/itamae/ 0000755 0000041 0000041 00000000000 14537556746 021552 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/itamae/roles/ 0000755 0000041 0000041 00000000000 14537556746 022676 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/itamae/roles/basic.rb 0000644 0000041 0000041 00000000045 14537556746 024303 0 ustar www-data www-data include_recipe '../cookbooks/basics'
chake-0.91/lib/chake/config_manager/skel/itamae/cookbooks/ 0000755 0000041 0000041 00000000000 14537556746 023543 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/itamae/cookbooks/basics/ 0000755 0000041 0000041 00000000000 14537556746 025007 5 ustar www-data www-data chake-0.91/lib/chake/config_manager/skel/itamae/cookbooks/basics/default.rb 0000644 0000041 0000041 00000000031 14537556746 026752 0 ustar www-data www-data package 'openssh-server'
chake-0.91/lib/chake/config_manager/skel/itamae/Rakefile 0000644 0000041 0000041 00000000020 14537556746 023207 0 ustar www-data www-data require 'chake'
chake-0.91/lib/chake/config_manager/skel/itamae/nodes.yaml 0000644 0000041 0000041 00000000064 14537556746 023546 0 ustar www-data www-data host1.mycompany.com:
itamae:
- roles/basic.rb
chake-0.91/lib/chake/config_manager/itamae_remote.rb 0000644 0000041 0000041 00000001566 14537556746 022524 0 ustar www-data www-data require 'shellwords'
require 'chake/config'
require 'chake/tmpdir'
module Chake
class ConfigManager
class ItamaeRemote < ConfigManager
def converge
recipes = node.data['itamae-remote']
return if recipes.empty?
run_itamae(*recipes)
end
def apply(config)
run_itamae(config)
end
def needs_upload?
true
end
def self.accept?(node)
node.data.key?('itamae-remote')
end
private
def run_itamae(*recipes)
cmd = ['itamae', 'local', "--node-json=#{json_config}"]
if node.silent
cmd << '--log-level=warn'
end
cmd += recipes.map { |r| File.join(node.path, r) }
node.run_as_root(Shellwords.join(cmd))
end
def json_config
File.join(node.path, Chake.tmpdir, "#{node.hostname}.json")
end
end
end
end
chake-0.91/lib/chake/config_manager/shell.rb 0000644 0000041 0000041 00000001210 14537556746 021002 0 ustar www-data www-data require 'shellwords'
require 'chake/config'
module Chake
class ConfigManager
class Shell < ConfigManager
def converge
commands = node.data['shell'].join(' && ')
node.run_as_root sh(commands)
end
def apply(config)
node.run_as_root sh(config)
end
def self.accept?(node)
node.data.key?('shell')
end
private
def sh(command)
if node.path
command = "cd #{node.path} && " + command
end
if node.silent
"sh -ec '#{command}' >/dev/null"
else
"sh -xec '#{command}'"
end
end
end
end
end
chake-0.91/lib/chake/config_manager/chef.rb 0000644 0000041 0000041 00000001603 14537556746 020606 0 ustar www-data www-data require 'chake/config'
require 'chake/tmpdir'
module Chake
class ConfigManager
class Chef < ConfigManager
CONFIG = ENV['CHAKE_CHEF_CONFIG'] || 'config.rb'
def converge
node.run_as_root "sh -c 'rm -f #{node.path}/nodes/*.json && chef-solo -c #{node.path}/#{CONFIG} #{logging} -j #{json_config}'"
end
def apply(config)
node.run_as_root "sh -c 'rm -f #{node.path}/nodes/*.json && chef-solo -c #{node.path}/#{CONFIG} #{logging} -j #{json_config} --override-runlist recipe[#{config}]'"
end
priority 99
def self.accept?(_node)
true # this is the default, but after everything else
end
private
def json_config
parts = [node.path, Chake.tmpdir, "#{node.hostname}.json"].compact
File.join(parts)
end
def logging
(node.silent && '-l fatal') || ''
end
end
end
end
chake-0.91/lib/chake/config_manager/itamae.rb 0000644 0000041 0000041 00000002770 14537556746 021147 0 ustar www-data www-data require 'shellwords'
require 'chake/config'
require 'chake/tmpdir'
module Chake
class ConfigManager
class Itamae < ConfigManager
def converge
recipes = node.data['itamae']
return if recipes.empty?
run_itamae(*recipes)
end
def apply(config)
run_itamae(config)
end
def needs_upload?
false
end
def self.accept?(node)
node.data.key?('itamae')
end
private
def run_itamae(*recipes)
cmd = ['itamae']
case node.connection
when Chake::Connection::Ssh
cmd << 'ssh' << "--user=#{node.username}" << "--host=#{node.hostname}"
cmd += ssh_config
when Chake::Connection::Local
if node.username == 'root'
cmd.prepend 'sudo'
end
cmd << 'local'
else
raise NotImplementedError, "Connection type #{node.connection.class} not supported for itamee"
end
cmd << "--node-json=#{json_config}"
if node.silent
cmd << '--log-level=warn'
end
cmd += recipes
node.log("$ #{cmd.join(' ')}")
io = IO.popen(cmd, 'r', err: %i[child out])
node.connection.read_output(io)
end
def json_config
File.join(Chake.tmpdir, "#{node.hostname}.json")
end
def ssh_config
ssh_config = node.connection.send(:ssh_config_file) # FIXME
File.exist?(ssh_config) ? ["--ssh-config=#{ssh_config}"] : []
end
end
end
end
chake-0.91/lib/chake/tmpdir.rb 0000644 0000041 0000041 00000000124 14537556746 016236 0 ustar www-data www-data module Chake
def self.tmpdir
ENV.fetch('CHAKE_TMPDIR', 'tmp/chake')
end
end
chake-0.91/lib/chake/readline.rb 0000644 0000041 0000041 00000002701 14537556746 016525 0 ustar www-data www-data require 'etc'
require 'readline'
require 'chake/tmpdir'
module Chake
class Readline
class << self
def history_file
raise NotImplementedError
end
def history
@history ||= []
end
def prompt
raise NotImplementedError
end
def init
return unless File.exist?(history_file)
@history = File.readlines(history_file).map(&:strip)
end
def finish
return if !File.writable?(File.dirname(history_file)) || history.empty?
File.open(history_file, 'w') do |f|
history.last(500).each do |line|
f.puts(line)
end
end
end
def readline
::Readline::HISTORY.clear
history.each do |cmd|
::Readline::HISTORY.push(cmd)
end
input = ::Readline.readline(prompt)
history.push(input) if input && input.strip != '' && input != @last
input
end
end
class Commands < Readline
def self.history_file
File.join(Chake.tmpdir, '.commands_history')
end
def self.prompt
'$ '
end
end
class Recipes < Readline
def self.history_file
File.join(Chake.tmpdir, '.recipes_history')
end
def self.prompt
'> '
end
end
end
end
Chake::Readline.constants.each do |subclass|
subclass = Chake::Readline.const_get(subclass)
subclass.init
at_exit do
subclass.finish
end
end
chake-0.91/lib/chake/node.rb 0000644 0000041 0000041 00000003430 14537556746 015667 0 ustar www-data www-data require 'uri'
require 'etc'
require 'forwardable'
require 'chake/connection'
require 'chake/config_manager'
module Chake
class Node
extend Forwardable
attr_reader :hostname, :port, :username, :remote_username, :data
attr_accessor :silent
def self.max_node_name_length
@max_node_name_length ||= 0
end
class << self
attr_writer :max_node_name_length
end
def initialize(hostname, data = {})
uri = parse_uri(hostname)
@connection_name = uri.scheme
@hostname = uri.host
@port = uri.port
@username = uri.user || Etc.getpwuid.name
@remote_username = uri.user
@path = uri.path
@data = data
set_max_node_length
end
def connection
@connection ||= Chake::Connection.get(@connection_name).new(self)
end
def_delegators :connection, :run, :run_as_root, :run_shell, :rsync, :rsync_dest, :scp, :scp_dest, :skip?
def config_manager
@config_manager ||= Chake::ConfigManager.get(self)
end
def_delegators :config_manager, :converge, :apply, :path, :bootstrap_steps, :needs_upload?
def path
@path ||= config_manager.path
end
def log(msg)
return if silent
puts("%#{Node.max_node_name_length}s: %s\n" % { host: hostname, msg: msg })
end
private
def parse_uri(hostname)
uri = URI.parse(hostname)
if incomplete_uri(uri)
uri = URI.parse("ssh://#{hostname}")
end
uri.path = nil if uri.path.empty?
uri
end
def incomplete_uri(uri)
!uri.host && ((!uri.scheme && uri.path) || (uri.scheme && uri.opaque))
end
def set_max_node_length
return if @hostname.length <= self.class.max_node_name_length
self.class.max_node_name_length = @hostname.length
end
end
end
chake-0.91/lib/chake/yaml.rb 0000644 0000041 0000041 00000000347 14537556746 015710 0 ustar www-data www-data require 'yaml'
module Chake
module YAML
def self.load_file(filename)
if RUBY_VERSION >= '3.1'
::YAML.load_file(filename, aliases: true)
else
::YAML.load_file(filename)
end
end
end
end
chake-0.91/lib/chake/bootstrap/ 0000755 0000041 0000041 00000000000 14537556746 016432 5 ustar www-data www-data chake-0.91/lib/chake/bootstrap/chef/ 0000755 0000041 0000041 00000000000 14537556746 017337 5 ustar www-data www-data chake-0.91/lib/chake/bootstrap/chef/99_unsupported.sh 0000644 0000041 0000041 00000000724 14537556746 022607 0 ustar www-data www-data echo "---------------------"
echo "Unsupported platform: Installing chef-solo with omnibus package"
echo "---------------------"
echo
for file in /etc/os-release /etc/issue; do
if [ -f $file ]; then
cat $file
break
fi
done
if ! which chef-solo >/dev/null ; then
# Install chef-solo via omnibus package that chef provides
# This script should install chef-solo in any Linux distribution
wget -O- https://opscode.com/chef/install.sh | bash
exit
fi
chake-0.91/lib/chake/bootstrap/chef/02_debian.sh 0000644 0000041 0000041 00000000304 14537556746 021413 0 ustar www-data www-data if [ -x /usr/bin/apt-get ]; then
apt-get update
export DEBIAN_FRONTEND=noninteractive
apt-get -q -y install rsync chef
update-rc.d chef-client disable
service chef-client stop
exit
fi
chake-0.91/lib/chake/bootstrap/chef/01_installed.sh 0000644 0000041 0000041 00000000121 14537556746 022144 0 ustar www-data www-data # chef-solo already installed
if which chef-solo >/dev/null 2>&1; then
exit
fi
chake-0.91/lib/chake/bootstrap/itamae-remote/ 0000755 0000041 0000041 00000000000 14537556746 021163 5 ustar www-data www-data chake-0.91/lib/chake/bootstrap/itamae-remote/99_unsupported.sh 0000644 0000041 0000041 00000000446 14537556746 024434 0 ustar www-data www-data echo "---------------------"
echo "Unsupported platform: Installing itamae with rubygems"
echo "---------------------"
echo
for file in /etc/os-release /etc/issue; do
if [ -f $file ]; then
cat $file
break
fi
done
if ! which itamae >/dev/null ; then
gem install itamae
exit
fi
chake-0.91/lib/chake/bootstrap/itamae-remote/02_debian.sh 0000644 0000041 0000041 00000000211 14537556746 023234 0 ustar www-data www-data if [ -x /usr/bin/apt-get ]; then
apt-get update
export DEBIAN_FRONTEND=noninteractive
apt-get -q -y install rsync itamae
exit
fi
chake-0.91/lib/chake/bootstrap/itamae-remote/01_installed.sh 0000644 0000041 0000041 00000000113 14537556746 023771 0 ustar www-data www-data # itamae already installed
if which itamae >/dev/null 2>&1; then
exit
fi
chake-0.91/lib/chake/bootstrap/00_set_hostname.sh 0000644 0000041 0000041 00000001261 14537556746 021756 0 ustar www-data www-data hostname="$1"
if [ "$(hostname)" != "${hostname}" ]; then
echo "$hostname" > /etc/hostname
hostname --file /etc/hostname
fi
fqdn=$(hostname --fqdn || true)
if [ "$fqdn" != "$hostname" ]; then
# if hostname is bar.example.com, we also want `bar` to be in /etc/hosts
short_hostname=$(echo "$hostname" | cut -d . -f 1)
if [ "$short_hostname" != "$hostname" ] && ! grep -q "\s${short_hostname}" /etc/hosts; then
hostname="$hostname $short_hostname"
fi
printf "127.0.1.1\t%s\n" "$hostname" >> /etc/hosts
fi
# Stop cloud-init from resetting the hostname
if [ -f /etc/cloud/cloud.cfg ]; then
sed -i -e '/^\s*-\s*\(set_hostname\|update_hostname\)/d' /etc/cloud/cloud.cfg
fi
chake-0.91/lib/chake/connection/ 0000755 0000041 0000041 00000000000 14537556746 016554 5 ustar www-data www-data chake-0.91/lib/chake/connection/local.rb 0000644 0000041 0000041 00000000462 14537556746 020175 0 ustar www-data www-data require 'socket'
module Chake
class Connection
class Local < Connection
def command_runner
['sh', '-c']
end
def shell_command
ENV.fetch('SHELL', Etc.getpwuid.shell)
end
def skip?
node.hostname != Socket.gethostname
end
end
end
end
chake-0.91/lib/chake/connection/ssh.rb 0000644 0000041 0000041 00000003021 14537556746 017672 0 ustar www-data www-data module Chake
class Connection
class Ssh < Connection
def scp
['scp', ssh_config, scp_options].flatten.compact
end
def scp_dest
"#{ssh_target}:"
end
def rsync
[ssh_prefix, 'rsync', rsync_ssh].flatten.compact
end
def rsync_dest
[ssh_target, "#{node.path}/"].join(':')
end
def command_runner
[ssh_prefix, 'ssh', ssh_config, ssh_options, ssh_target].flatten.compact
end
def shell_command
command_runner
end
private
def rsync_ssh
@rsync_ssh ||=
begin
ssh_command = 'ssh'
if File.exist?(ssh_config_file)
ssh_command += " -F #{ssh_config_file}"
end
ssh_command += " -p #{node.port}" if node.port
if ssh_command == 'ssh'
[]
else
['-e', ssh_command]
end
end
end
def ssh_config
(File.exist?(ssh_config_file) && ['-F', ssh_config_file]) || []
end
def ssh_config_file
@ssh_config_file ||= ENV.fetch('CHAKE_SSH_CONFIG', '.ssh_config')
end
def ssh_prefix
@ssh_prefix ||= ENV.fetch('CHAKE_SSH_PREFIX', '').split
end
def ssh_target
[node.remote_username, node.hostname].compact.join('@')
end
def ssh_options
(node.port && ['-p', node.port.to_s]) || []
end
def scp_options
(node.port && ['-P', node.port.to_s]) || []
end
end
end
end
chake-0.91/lib/chake/config.rb 0000644 0000041 0000041 00000001313 14537556746 016205 0 ustar www-data www-data require 'chake/node'
require 'chake/yaml'
module Chake
class << self
attr_accessor :nodes
end
end
nodes_file = ENV['CHAKE_NODES'] || 'nodes.yaml'
nodes_directory = ENV['CHAKE_NODES_D'] || 'nodes.d'
nodes = (File.exist?(nodes_file) && Chake::YAML.load_file(nodes_file)) || {}
nodes.values.each do |node|
node['chake_metadata'] = { 'definition_file' => nodes_file }
end
Dir.glob(File.join(nodes_directory, '*.yaml')).sort.each do |f|
file_nodes = Chake::YAML.load_file(f)
file_nodes.values.each do |node|
node['chake_metadata'] = { 'definition_file' => f }
end
nodes.merge!(file_nodes)
end
Chake.nodes = nodes.map { |node, data| Chake::Node.new(node, data) }.reject(&:skip?).uniq(&:hostname)
chake-0.91/lib/chake.rb 0000644 0000041 0000041 00000020506 14537556746 014745 0 ustar www-data www-data require 'yaml'
require 'json'
require 'tmpdir'
require 'chake/config'
require 'chake/version'
require 'chake/readline'
require 'chake/wipe'
desc 'Initializes current directory with sample structure'
task init: 'init:itamae'
Chake::ConfigManager.all.map do |cfgmgr|
desc "Initializes current directory for #{cfgmgr.short_name}"
task "init:#{cfgmgr.short_name}" do
cfgmgr.init
end
end
desc 'list nodes'
task :nodes do
fields = %i[hostname connection config_manager]
lengths = fields.map do |f|
[f.length, Chake.nodes.map { |n| n.send(f).to_s.length }.max].max
end
columns = lengths.map { |l| "%-#{l}s" }.join(' ')
puts(columns % fields)
puts(columns % lengths.map { |l| '-' * l })
Chake.nodes.each do |node|
puts(columns % fields.map { |f| node.send(f) })
end
end
def encrypted_for(node)
encrypted_files = Array(node.data['encrypted'])
if encrypted_files.empty?
encrypted_files = Dir.glob("**/files/{default,host-#{node.hostname}}/*.{asc,gpg}") + Dir.glob('**/files/*.{asc,gpg}')
end
encrypted_files.each_with_object({}) do |key, hash|
hash[key] = key.sub(/\.(asc|gpg)$/, '')
end
end
desc 'list encrypted files per node'
task :encrypted do
Chake.nodes.each do |node|
puts "#{node.hostname}: #{Array(encrypted_for(node).keys).join(', ')}"
end
end
def maybe_decrypt(node)
if node.needs_upload?
return yield
end
files = encrypted_for(node)
files.each do |encrypted, target|
sh "gpg --use-agent --quiet --decrypt --output #{target} #{encrypted}"
end
begin
yield
ensure
files.each do |_, target|
Chake::Wipe.instance.wipe(target)
end
end
end
def if_files_changed(node, group_name, files)
return if files.empty?
hash_io = IO.popen(%w[xargs sha1sum], 'w+')
hash_io.puts(File.join(Chake.tmpdir, "#{node}.bootstrap"))
files.sort.each { |f| hash_io.puts(f) }
hash_io.close_write
current_hash = hash_io.read
hash_file = File.join(Chake.tmpdir, "#{node}.#{group_name}.sha1sum")
hash_on_disk = nil
hash_on_disk = File.read(hash_file) if File.exist?(hash_file)
yield if current_hash != hash_on_disk
FileUtils.mkdir_p(File.dirname(hash_file))
File.write(hash_file, current_hash)
end
def write_json_file(file, data)
File.chmod(0o600, file) if File.exist?(file)
File.open(file, 'w', 0o600) do |f|
f.write(JSON.pretty_generate(data))
f.write("\n")
end
end
desc 'Executed before bootstrapping'
task bootstrap_common: :connect_common
desc 'Executed before uploading'
task upload_common: :connect_common
desc 'Executed before uploading'
task converge_common: :connect_common
desc 'Executed before connecting to any host'
task :connect_common
Chake.nodes.each do |node|
node.silent = Rake.application.options.silent
hostname = node.hostname
bootstrap_script = File.join(Chake.tmpdir, "#{hostname}.bootstrap")
bootstrap_steps = node.bootstrap_steps
bootstrap_code = (["#!/bin/sh\n", "set -eu\n"] + bootstrap_steps.map { |f| File.read(f) }).join
desc "bootstrap #{hostname}"
task "bootstrap:#{hostname}" => :bootstrap_common do
mkdir_p Chake.tmpdir unless File.directory?(Chake.tmpdir)
if !File.exist?(bootstrap_script) || File.read(bootstrap_script) != bootstrap_code
# create bootstrap script
File.write(bootstrap_script, bootstrap_code)
chmod 0o755, bootstrap_script
# copy bootstrap script over
scp = node.scp
target = "/tmp/.chake-bootstrap.#{Etc.getpwuid.name}"
sh *scp, bootstrap_script, node.scp_dest + target
# run bootstrap script
node.run_as_root("#{target} #{hostname}")
end
# overwrite config with current contents
config = File.join(Chake.tmpdir, "#{hostname}.json")
write_json_file(config, node.data)
end
desc "upload data to #{hostname}"
task "upload:#{hostname}" => ["bootstrap:#{hostname}", :upload_common] do
next unless node.needs_upload?
encrypted = encrypted_for(node)
rsync_excludes = (encrypted.values + encrypted.keys).map { |f| ['--exclude', f] }.flatten
rsync_excludes << '--exclude' << '.git/'
rsync_excludes << '--exclude' << 'cache/'
rsync_excludes << '--exclude' << 'nodes/'
rsync_excludes << '--exclude' << 'local-mode-cache/'
rsync = node.rsync + ['-ap'] + ENV.fetch('CHAKE_RSYNC_OPTIONS', '').split
rsync_logging = (Rake.application.options.trace && '--verbose') || '--quiet'
hash_files = Dir.glob(File.join(Chake.tmpdir, '*.sha1sum'))
files = Dir.glob('**/*').reject { |f| File.directory?(f) } - encrypted.keys - encrypted.values - hash_files
if_files_changed(hostname, 'plain', files) do
sh *rsync, '--delete', rsync_logging, *rsync_excludes, './', node.rsync_dest
end
if_files_changed(hostname, 'enc', encrypted.keys) do
Dir.mktmpdir do |tmpdir|
encrypted.each do |encrypted_file, target_file|
target = File.join(tmpdir, target_file)
mkdir_p(File.dirname(target))
rm_f target
File.open(target, 'w', 0o400) do |output|
IO.popen(['gpg', '--quiet', '--batch', '--use-agent', '--decrypt', encrypted_file]) do |data|
output.write(data.read)
end
end
puts "#{target} (decrypted)"
end
sh *rsync, rsync_logging, "#{tmpdir}/", node.rsync_dest
end
end
end
converge_dependencies = [:converge_common, "bootstrap:#{hostname}", "upload:#{hostname}"]
desc "converge #{hostname}"
task "converge:#{hostname}" => converge_dependencies do
maybe_decrypt(node) do
node.converge
end
end
desc 'apply on #{hostname}'
task "apply:#{hostname}", [:recipe] => %i[recipe_input connect_common] do |_task, _args|
maybe_decrypt(node) do
node.apply($recipe_to_apply)
end
end
task "apply:#{hostname}" => converge_dependencies
desc "run a command on #{hostname}"
task "run:#{hostname}", [:command] => %i[run_input connect_common] do
node.run($cmd_to_run)
end
desc "Logs in to a shell on #{hostname}"
task "login:#{hostname}" => :connect_common do
node.run_shell
end
desc 'checks connectivity and setup on all nodes'
task "check:#{hostname}" => :connect_common do
node.run('sudo echo OK')
end
end
task :run_input, :command do |_task, args|
$cmd_to_run = args[:command]
unless $cmd_to_run
puts '# Enter command to run (use arrow keys for history):'
$cmd_to_run = Chake::Readline::Commands.readline
end
if !$cmd_to_run || $cmd_to_run.strip == ''
puts
puts 'I: no command provided, operation aborted.'
exit(1)
end
end
task :recipe_input, :recipe do |_task, args|
$recipe_to_apply = args[:recipe]
unless $recipe_to_apply
recipes = Dir['**/*/recipes/*.rb'].map do |f|
f =~ %r{(.*/)?(.*)/recipes/(.*).rb$}
cookbook = Regexp.last_match(2)
recipe = Regexp.last_match(3)
recipe = nil if recipe == 'default'
[cookbook, recipe].compact.join('::')
end.sort
puts 'Available recipes:'
IO.popen('column', 'w') do |column|
column.puts(recipes)
end
$recipe_to_apply = Chake::Readline::Recipes.readline
if !$recipe_to_apply || $recipe_to_apply.empty?
puts
puts 'I: no recipe provided, operation aborted.'
exit(1)
end
unless recipes.include?($recipe_to_apply)
abort "E: no such recipe: #{$recipe_to_apply}"
end
end
end
desc 'upload to all nodes'
multitask upload: Chake.nodes.map { |node| "upload:#{node.hostname}" }
desc 'bootstrap all nodes'
multitask bootstrap: Chake.nodes.map { |node| "bootstrap:#{node.hostname}" }
desc 'converge all nodes (default)'
multitask 'converge' => Chake.nodes.map { |node| "converge:#{node.hostname}" }
desc 'Apply on all nodes'
multitask 'apply', [:recipe] => Chake.nodes.map { |node| "apply:#{node.hostname}" }
desc 'run on all nodes'
multitask :run, [:command] => Chake.nodes.map { |node| "run:#{node.hostname}" }
task default: :converge
desc 'checks connectivity and setup on all nodes'
multitask check: (Chake.nodes.map { |node| "check:#{node.hostname}" }) do
puts '✓ all hosts OK'
puts ' - ssh connection works'
puts ' - password-less sudo works'
end
desc 'runs a Ruby console in the chake environment'
task :console do
require 'irb'
IRB.setup('__FILE__', argv: [])
workspace = IRB::WorkSpace.new(self)
puts 'chake - interactive console'
puts '---------------------------'
puts 'all node data in available in Chake.nodes'
puts
IRB::Irb.new(workspace).run(IRB.conf)
end
chake-0.91/ChangeLog.md 0000644 0000041 0000041 00000017700 14537556746 014752 0 ustar www-data www-data # 0.91
- itamae: handle empty recipe list
- Add support for configuring encrypted files explicitly
- itamae-remote: handle empty recipe list
- Rakefile: deb:install: install dependencies as well
- activate.sh: add script to use this source dir in a shell
- Make rsync invocations quiet by default
# 0.90.3
- `itamae_spec`: fix rspec warning about syntax for `expect { }.to raise`
- bootstrap: `00_set_hostname.sh`: don't set hostname if not needed
- Chake::Connection: add missing require for `$CHILD_STATUS`
# 0.90.2
- upload: make sure to reupload on config manager changes
- Apply suggestions by rubocop 1.39.0
- Chake::Connection: avoid setting constant inside of block
- rubocop: keep assignment to `test_files` in the gemspec
- gemspec: set `spec.metadata['rubygems_mfa_required']`
# 0.90.1
* Fix loading node data under ruby < 3.1
# 0.90
* itamae: use --sudo when root for local backend
* Chake::ConfigManager: fix typo
* chake/config: allow aliases in YAML data
* codespell: ignore tags file
* ChangeLog.md: fix typo found by codespell
* Implement new configuration manager: itamae-remote
# 0.82
* gemspec: drop bundler version constraint
* Chake::Wipe: improve wording in warning message
* chake/config: store the node definition file in the node data
# 0.81.1
* manpages: make sure all instances of \' are fixed
* rake nodes: format output by ourselves
# 0.81
* bootstrap/chef: exit if chef-solo is available
* Always bootstrap nodes
* Decrypt files in place when upload is not needed
* itamae: handle silent mode
* manpages: drop accute accent erroneously added by ronn
# 0.80
This release adds support for multiple configuration managers. Chef is now only
one of the options. There is also now support for configuration management with
itamae, and lightweight configuration management tool inspired by Chef, and via
shell commands. This should be mostly transparent to current Chef users, but
new repositories initiated by chake will use itamae by default.
Other notable changes:
* rake nodes: list configuration manager and format as table
* Chake::Connection: fix handling of stderr
* Rebootstrap nodes when changing config managers
* bootstrap, upload: skip when config manager does not need them
# 0.21.2
* Chake::Backend#run: don't strip leading whitespace
# 0.21.1
* Fix converge when the connection is not already made as root. This bug was
introduced by the change in the previous release.
# 0.21
* converge, apply: allow removing data from the node JSON attributes
# 0.20
* check: give some feedback by running `sudo echo OK` instead of `sudo true`
* Get rid of global variables
* bin/chake: make rake run one thread for each node
* Chake::Backend: run commands by opening a shell and writing to it
* Document Chake.nodes
# 0.19
* Protect node JSON files from other users
# 0.18
* add console task
* manpage: fix header transformation
* manpage: ignore intermediary .adoc file
# 0.17.1
* manpage: drop ad-hoc handling of `SOURCE_DATE_EPOCH` (let asciidoctor handle
it)
# 0.17
* make rsync exclude extra directories who are created as root by chef-solo at
the server side. This fixes the case where upload phase when the SSH user is
not root.
# 0.16
* make `run` also capture stderr, for now mixed together with stdout. In the
future that may be improved for example to print stderr output in red when
running on a TTY.
# 0.15
* improve text in the parallel execution docs
* add new hook: `connect_common`, which will run before any attempt to connect
to any node.
* make output of `check` target more explicit about what was tested
# 0.14
* Fix typo in README.md
* thanks to Luciano Prestes Cavalcanti
* Turn "all hosts" tasks (converge, upload, bootstrap, run, apply) into
multitasks. This will make them run in parallel.
# 0.13
* transmit decrypted files with mode 0400
* Use the Omnibus packages from Chef upstream on platforms where we don't have
proper Chef packages from the OS official repository.
# 0.12
* Switch manpage build from ronn to asciidoctor
* Add ability to override the Chef configuration file by setting
`$CHAKE_CHEF_CONFIG` (default: `config.rb`)
* bootstrap: ensure short hostname is in /etc/hosts
# 0.11
* bootstrap: make sure FQDN matches hostname
* Add `rake check` task to check SSH connectivity and sudo setup
* Add tasks to apply a single recipe to nodes: `rake apply[recipe]` and `rake
apply:$NODE[recipe]`. If `[recipe]` is not passed in the command line, the
user is prompted for the recipe name.
* run task changed to have the same interface and behavior as the new apply
task: `rake run[command]`, or `rake run:$NODE[command]`. If `[command]` is
not passed in the command line, the user is prompted for the command.
# 0.10.2
* Fix check for modified files at the upload phase. Now chake will properly
avoiding rsync calls when there is no changed files since the latest upload.
* Fix generated RPM spec file. Will now properly build, install, and work under
both CentOS 7 and Fedora 22+.
* Collect test coverage statistics when running tests.
* Added dependency on simplecov
# 0.10.1
* actually implement support for custom ports in Node URL's. Despite being
documented, that didn't actually work until now.
# 0.10
* Add hook functionality. See README/manpage for documentation.
* README.md: a few reviews
# 0.9.1
* fix manpage installation path
# 0.9
* fix build step for obs uploads
* add infrastructure to build and install a manpage
* Add support for a nodes.d/ directory; very useful when dealing with a larger
amount of nodes.
# 0.8
* gemspec: minor improvements in the long description
* LICENSE.txt: fixed license name
* run: print small message before prompting
* Add history support for the `run` tasks
* Abort `run` tasks if no command is provided
# 0.7
* gemspec: improve summary and description
* Also for encrypted files under $cookbook/files/, and not only under
$cookbook/files/\*/.
* Allow overriding tmpdir with `$CHAKE_TMPDIR`
* Stop cloud-init from resetting the hostname
# 0.6
* Support a ssh prefix command by setting `$CHAKE_SSH_PREFIX` in the
environment. For example, `CHAKE_SSH_PREFIX=tsocks` will make all ssh
invocations as `tocks ssh ...` instead of just `ssh ...`.
# 0.5
* Add a task login:$host that you can use to easily log in to any of your
hosts.
# 0.4.3
* When running remote commands as root, run `sudo COMMAND` directly instead of
`sudo sh -c "COMMAND"`. Under over-restrictive sudo setups (i.e. one in which
you cannot run a shell as root), `sudo sh -c "FOO"` will not be allowed.
# 0.4.2
* tmp/chake: create only when actually needed
* Control nodes files with `$CHAKE_NODES`
# 0.4.1
* Don't always assume the local username as the remote username for SSH
connections:
* `user@host`: connect with `user@host`
* `host`: connect with `host` (username will be obtained by SSH itself from
either its configuration files or the current username)
# 0.4.0
* Redesign build of RPM package
* Output of command run on nodes is now aligned
* Change storage of temporary files from .tmp to tmp/chake
* The JSON node attributes files generated in tmp/chake are not readable
* SSH config file can now be controlled with the `$CHAKE_SSH_CONFIG`
environment variable
* Extra options for rsync can now be passed in the `$CHAKE_RSYNC_OPTIONS`
environment variable
* Chake::VERSION is now available in Rakefiles
* update test suite to use new rspec syntax instead the old one which is
obsolete in rspec 3.
* Thanks to Athos Ribeiro.
# 0.3.3
* rsync: exclude cache/ to work with the version of rsync in OSX
# 0.3.2
* Now finally, hopefully, really fix RPM builds
* chake init: rename 'myhost' → 'basics'
* The official home is on gitlab
* Completed basic documentation
# 0.3.1
* Fix setting hostname when bootstrapping
* Rakefile: do not allow releases without a changelog entry
* Now *really* fix RPM builds, hopefully
# 0.3
* Fix RPM build
* bootstrap: set hostname
# 0.2.3
* No functional changes
* Small changes to make chake compatible with Debian 7, and most of the
RPM-based distributions
chake-0.91/Gemfile 0000644 0000041 0000041 00000000132 14537556746 014063 0 ustar www-data www-data source 'https://rubygems.org'
# Specify your gem's dependencies in chake.gemspec
gemspec
chake-0.91/README.shell.md 0000644 0000041 0000041 00000001440 14537556746 015160 0 ustar www-data www-data chake-shell(7) -- configure chake nodes with shell
==================================================
## Description
This configuration manager is a simpler wrapper for running a list of shell
commands on the nodes.
## Configuration
The _shell_ configuration manager requires one key called `shell`, and the
value must be a list of strings representing the list of commands to run on the
node when converging.
```yaml
host1.mycompany.com:
shell:
- echo "HELLO WORLD"
```
## Bootstrapping
Very little bootstrapping is required for this configuration manager, as we
hope every node you could possibly want to manage with it already has a POSIX
shell as `/bin/sh`. During bootstrapping, only the node hostname will be set
according to your chake configuration.
## See also
* **chake(1)**
chake-0.91/chake.gemspec 0000644 0000041 0000041 00000002645 14537556746 015223 0 ustar www-data www-data lib = File.expand_path('lib', __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'chake/version'
Gem::Specification.new do |spec|
spec.name = 'chake'
spec.version = Chake::VERSION
spec.authors = ['Antonio Terceiro']
spec.email = ['terceiro@softwarelivre.org']
spec.summary = 'serverless configuration management tool for chef'
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."
spec.homepage = 'https://gitlab.com/terceiro/chake'
spec.license = 'MIT'
spec.files = File.read('.manifest').split("\n") + ['.manifest']
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']
spec.add_development_dependency 'bundler'
spec.add_development_dependency 'ronn-ng'
spec.add_development_dependency 'rspec'
spec.add_development_dependency 'rubocop'
spec.add_development_dependency 'simplecov'
spec.add_dependency 'rake'
spec.metadata['rubygems_mfa_required'] = 'true'
end
chake-0.91/LICENSE.txt 0000644 0000041 0000041 00000002106 14537556746 014416 0 ustar www-data www-data Copyright (c) 2014 Antonio Terceiro
The Expat License (a.k.a. "MIT")
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
chake-0.91/.gitlab-ci.yml 0000644 0000041 0000041 00000000637 14537556746 015236 0 ustar www-data www-data image: debian:testing
.install: &install
- apt-get update && apt-get install -qy ruby asciidoctor ruby-bundler ruby-rspec rubocop ruby-simplecov codespell ronn
tests:
before_script: *install
script:
- rake test
manpages:
before_script: *install
script:
- rake man
style:
before_script: *install
script:
- rake style
codespell:
before_script: *install
script:
- rake codespell
chake-0.91/.ackrc 0000644 0000041 0000041 00000000047 14537556746 013661 0 ustar www-data www-data --ignore-dir=coverage
--ignore-dir=pkg