minionizer 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.gitignore +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +37 -15
- data/README.md +4 -0
- data/Rakefile +17 -0
- data/lib/core/file_injection.rb +11 -8
- data/lib/core/folder_creation.rb +12 -0
- data/lib/core/public_ssh_key_injection.rb +40 -0
- data/lib/core/task_template.rb +25 -0
- data/lib/core/user_creation.rb +18 -0
- data/lib/minionizer.rb +3 -0
- data/lib/minionizer/command_execution.rb +60 -0
- data/lib/minionizer/session.rb +29 -14
- data/lib/minionizer/version.rb +1 -1
- data/minionizer.gemspec +5 -4
- data/test/integration/core_library_test.rb +193 -0
- data/test/role_template.rb.erb +16 -0
- data/test/test_helper.rb +125 -32
- data/test/unit/lib/core/file_injection_test.rb +45 -12
- data/test/unit/lib/core/folder_creation_test.rb +58 -0
- data/test/unit/lib/core/public_ssh_key_injection_test.rb +46 -0
- data/test/unit/lib/core/task_template_test.rb +35 -0
- data/test/unit/lib/core/user_creation_test.rb +44 -0
- data/test/unit/lib/minionizer/command_execution_test.rb +85 -0
- data/test/unit/lib/minionizer/configuration_test.rb +4 -2
- data/test/unit/lib/minionizer/minion_test.rb +3 -3
- data/test/unit/lib/minionizer/minionization_test.rb +8 -8
- data/test/unit/lib/minionizer/role_template_test.rb +2 -2
- data/test/unit/lib/minionizer/session_test.rb +65 -20
- metadata +53 -20
- data/test/integration/acceptance_test.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82bef054c00cc362c8c6bde7b0557b1ec7ea8966
|
4
|
+
data.tar.gz: 496df270bcb0c2c4fe65d3bb291ef5d9d9673e69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b437e19e86a6bc97c546ba32193a77812c0710c50b375927ddd0c49b129fab0e96b4b91ab9e6456a409b10061e7ca04398220bb125c4a85a7c56382d06d67621
|
7
|
+
data.tar.gz: ef1cc521975e07f8fbee54cd3e3f5415d0610f2dfb6bc6cb5a0f5b79725d05942f4c9f1baf8e915729ecb6a9d32a9bdf111c936f233f7c76dd9abf1797668588
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: 'travis-ci'
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -2,35 +2,57 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
minionizer (0.0.1)
|
5
|
-
activesupport
|
6
|
-
net-ssh
|
5
|
+
activesupport (~> 4.1)
|
6
|
+
net-ssh (~> 2.9)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: http://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activesupport (4.
|
11
|
+
activesupport (4.1.1)
|
12
12
|
i18n (~> 0.6, >= 0.6.9)
|
13
|
-
|
14
|
-
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
15
|
thread_safe (~> 0.1)
|
16
|
-
tzinfo (~>
|
17
|
-
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
coveralls (0.7.0)
|
18
|
+
multi_json (~> 1.3)
|
19
|
+
rest-client
|
20
|
+
simplecov (>= 0.7)
|
21
|
+
term-ansicolor
|
22
|
+
thor
|
23
|
+
docile (1.1.3)
|
18
24
|
fakefs (0.5.2)
|
19
25
|
i18n (0.6.9)
|
26
|
+
json (1.8.1)
|
20
27
|
metaclass (0.0.4)
|
21
|
-
|
28
|
+
mime-types (2.2)
|
29
|
+
minitest (5.3.3)
|
22
30
|
mocha (1.0.0)
|
23
31
|
metaclass (~> 0.0.1)
|
24
|
-
multi_json (1.
|
25
|
-
net-ssh (2.
|
26
|
-
|
27
|
-
|
28
|
-
|
32
|
+
multi_json (1.10.0)
|
33
|
+
net-ssh (2.9.0)
|
34
|
+
rake (10.3.1)
|
35
|
+
rest-client (1.6.7)
|
36
|
+
mime-types (>= 1.16)
|
37
|
+
simplecov (0.8.2)
|
38
|
+
docile (~> 1.1.0)
|
39
|
+
multi_json
|
40
|
+
simplecov-html (~> 0.8.0)
|
41
|
+
simplecov-html (0.8.0)
|
42
|
+
term-ansicolor (1.3.0)
|
43
|
+
tins (~> 1.0)
|
44
|
+
thor (0.19.1)
|
45
|
+
thread_safe (0.3.3)
|
46
|
+
tins (1.1.0)
|
47
|
+
tzinfo (1.1.0)
|
48
|
+
thread_safe (~> 0.1)
|
29
49
|
|
30
50
|
PLATFORMS
|
31
51
|
ruby
|
32
52
|
|
33
53
|
DEPENDENCIES
|
34
|
-
|
54
|
+
coveralls
|
55
|
+
fakefs (~> 0.5)
|
35
56
|
minionizer!
|
36
|
-
mocha
|
57
|
+
mocha (~> 1.0)
|
58
|
+
rake (~> 10.3)
|
data/README.md
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
[](https://codeclimate.com/github/jsgarvin/minionizer)
|
2
|
+
[](https://travis-ci.org/jsgarvin/minionizer)
|
3
|
+
[](https://coveralls.io/r/jsgarvin/minionizer)
|
4
|
+
[](http://badge.fury.io/rb/minionizer)
|
5
|
+
[](https://gemnasium.com/jsgarvin/minionizer)
|
2
6
|
|
3
7
|
# Minionizer
|
4
8
|
|
data/Rakefile
CHANGED
@@ -18,6 +18,9 @@ namespace :test do
|
|
18
18
|
relay_output(vagrant_command(:up))
|
19
19
|
unless snapshot_plugin_installed?
|
20
20
|
relay_output(vagrant_command('plugin install vagrant-vbox-snapshot'))
|
21
|
+
end
|
22
|
+
unless test_snapshot_exists?
|
23
|
+
sleep 5
|
21
24
|
relay_output(vagrant_command('snapshot take blank-test-slate'))
|
22
25
|
end
|
23
26
|
end
|
@@ -36,6 +39,10 @@ def snapshot_plugin_installed?
|
|
36
39
|
Gem::Version.new(vagrant_plugins['vagrant-vbox-snapshot']) >= Gem::Version.new('0.0.4')
|
37
40
|
end
|
38
41
|
|
42
|
+
def test_snapshot_exists?
|
43
|
+
vagrant_snapshots.include?('blank-test-slate')
|
44
|
+
end
|
45
|
+
|
39
46
|
def vagrant_plugins
|
40
47
|
Hash.new.tap do |hash|
|
41
48
|
`cd #{vagrant_path}; vagrant plugin list`.split("\n").each do |plugin_string|
|
@@ -46,6 +53,16 @@ def vagrant_plugins
|
|
46
53
|
end
|
47
54
|
end
|
48
55
|
|
56
|
+
def vagrant_snapshots
|
57
|
+
Array.new.tap do |snapshots|
|
58
|
+
`cd #{vagrant_path}; vagrant snapshot list`.split("\n").each do |snapshot_string|
|
59
|
+
if snapshot_string.match(/Name\: ([^\(]+)/)
|
60
|
+
snapshots << $1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
49
66
|
def vagrant_path
|
50
67
|
File.expand_path('../test', __FILE__)
|
51
68
|
end
|
data/lib/core/file_injection.rb
CHANGED
@@ -1,19 +1,22 @@
|
|
1
1
|
module Minionizer
|
2
|
-
class FileInjection
|
3
|
-
attr_reader :session
|
2
|
+
class FileInjection < TaskTemplate
|
4
3
|
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
session.exec("
|
4
|
+
def call
|
5
|
+
session.exec("mkdir --parents #{target_directory}")
|
6
|
+
session.exec("echo '#{contents_from(source_path)}' > #{target_path}")
|
7
|
+
session.exec("chmod #{mode} #{target_path}") if respond_to?(:mode)
|
8
|
+
session.exec("chown #{owner} #{target_path}") if respond_to?(:owner)
|
9
|
+
session.exec("chgrp #{group} #{target_path}") if respond_to?(:group)
|
11
10
|
end
|
12
11
|
|
13
12
|
#######
|
14
13
|
private
|
15
14
|
#######
|
16
15
|
|
16
|
+
def target_directory
|
17
|
+
File.dirname(target_path)
|
18
|
+
end
|
19
|
+
|
17
20
|
def contents_from(source)
|
18
21
|
File.open(source).read.strip
|
19
22
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Minionizer
|
2
|
+
class FolderCreation < TaskTemplate
|
3
|
+
|
4
|
+
def call
|
5
|
+
session.exec("mkdir --parents #{path}")
|
6
|
+
session.exec("chmod #{mode} #{path}") if respond_to?(:mode)
|
7
|
+
session.exec("chown #{owner} #{path}") if respond_to?(:owner)
|
8
|
+
session.exec("chgrp #{group} #{path}") if respond_to?(:group)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Minionizer
|
2
|
+
class PublicSshKeyInjection < TaskTemplate
|
3
|
+
|
4
|
+
def call
|
5
|
+
file_injection.call
|
6
|
+
ensure
|
7
|
+
temp_file.unlink
|
8
|
+
end
|
9
|
+
|
10
|
+
#######
|
11
|
+
private
|
12
|
+
#######
|
13
|
+
|
14
|
+
def file_injection
|
15
|
+
@file_injection ||= file_injection_creator.new(session, file_injection_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def file_injection_creator
|
19
|
+
options[:file_injection_creator] ||= FileInjection
|
20
|
+
end
|
21
|
+
|
22
|
+
def file_injection_options
|
23
|
+
{
|
24
|
+
source_path: temp_file.path,
|
25
|
+
target_path: "~#{target_username}/.ssh/authorized_keys",
|
26
|
+
owner: target_username,
|
27
|
+
group: target_username
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def temp_file
|
32
|
+
@temp_file ||= Tempfile.new('MinionizerPublicKeys').tap do |temp_file|
|
33
|
+
Dir.glob("data/public_keys/*.pubkey") do |key_file|
|
34
|
+
temp_file.puts File.open(key_file).read
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Minionizer
|
2
|
+
class TaskTemplate
|
3
|
+
attr_reader :session, :options
|
4
|
+
|
5
|
+
def initialize(session, options = {})
|
6
|
+
@session = session
|
7
|
+
@options = options.with_indifferent_access
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(method_name, *arguments, &block)
|
11
|
+
if options.key?(method_name)
|
12
|
+
options[method_name]
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond_to?(method_name, include_private = false)
|
19
|
+
options.key?(method_name) || super
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Minionizer
|
2
|
+
class UserCreation < TaskTemplate
|
3
|
+
|
4
|
+
def call
|
5
|
+
unless user_exists?
|
6
|
+
session.exec("adduser --disabled-password --gecos '#{name}' #{username}")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def user_exists?
|
13
|
+
session.exec("id #{username}")
|
14
|
+
rescue CommandExecution::CommandError
|
15
|
+
return false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/minionizer.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
1
2
|
require 'active_support/inflector'
|
3
|
+
require 'erb'
|
2
4
|
require 'net/ssh'
|
3
5
|
require 'singleton'
|
4
6
|
require 'yaml'
|
5
7
|
|
8
|
+
require_relative 'core/task_template'
|
6
9
|
Dir[File.dirname(__FILE__) + '/**/*.rb'].each { |file| require file }
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Minionizer
|
2
|
+
|
3
|
+
class CommandExecution
|
4
|
+
class CommandError < StandardError; end
|
5
|
+
class InvocationError < StandardError; end
|
6
|
+
|
7
|
+
attr_reader :connection, :command
|
8
|
+
|
9
|
+
def initialize(connection, command)
|
10
|
+
@connection = connection
|
11
|
+
@command = command
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
execute_command
|
16
|
+
check_exit_code
|
17
|
+
return results
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def execute_command
|
23
|
+
connection.open_channel do |channel|
|
24
|
+
execute_command_inside_channel(channel)
|
25
|
+
end
|
26
|
+
connection.loop
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute_command_inside_channel(channel)
|
30
|
+
channel.exec(command) do |_, success|
|
31
|
+
if success
|
32
|
+
compile_results(channel)
|
33
|
+
else
|
34
|
+
raise InvocationError.new("Failed to invoke command: #{command} ")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_exit_code
|
40
|
+
if exit_failure?
|
41
|
+
raise CommandError.new("\"#{command}\" returned exit code #{results[:exit_code]}/#{results[:exit_signal]}/#{results[:stderr]}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def exit_failure?
|
46
|
+
results[:exit_code].to_i != 0
|
47
|
+
end
|
48
|
+
|
49
|
+
def results
|
50
|
+
@results ||= {stdout: '', stderr: ''}
|
51
|
+
end
|
52
|
+
|
53
|
+
def compile_results(channel)
|
54
|
+
channel.on_data { |_, data| results[:stdout] += data.strip }
|
55
|
+
channel.on_extended_data { |_, data| results[:stderr] += data.to_s }
|
56
|
+
channel.on_request('exit-status') { |_,data| results[:exit_code] = data.read_long }
|
57
|
+
channel.on_request('exit-signal') { |_,data| results[:exit_signal] = data.read_string }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/minionizer/session.rb
CHANGED
@@ -1,39 +1,54 @@
|
|
1
1
|
module Minionizer
|
2
2
|
class Session
|
3
|
-
attr_reader :fqdn, :username, :password, :connector
|
3
|
+
attr_reader :fqdn, :username, :password, :connector, :command_executor
|
4
4
|
|
5
|
-
def initialize(fqdn, credentials, connector = Net::SSH)
|
5
|
+
def initialize(fqdn, credentials, connector = Net::SSH, command_executor = CommandExecution)
|
6
6
|
@fqdn = fqdn
|
7
7
|
@username = credentials['username']
|
8
8
|
@password = credentials['password']
|
9
9
|
@connector = connector
|
10
|
+
@command_executor = command_executor
|
10
11
|
end
|
11
12
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
13
|
+
def sudo(*commands)
|
14
|
+
@with_sudo = true
|
15
|
+
if commands.any?
|
16
|
+
return exec(*commands)
|
15
17
|
else
|
16
|
-
|
18
|
+
yield self
|
17
19
|
end
|
20
|
+
ensure
|
21
|
+
@with_sudo = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def exec(*commands)
|
25
|
+
results = commands.map { |command| execution(command).call }
|
26
|
+
results.length == 1 ? results.first : results
|
18
27
|
end
|
19
28
|
|
20
29
|
#######
|
21
30
|
private
|
22
31
|
#######
|
23
32
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
raise StandardError.new(output)
|
30
|
-
end
|
33
|
+
def execution(command)
|
34
|
+
if with_sudo?
|
35
|
+
command_executor.new(connection, prefix_sudo(command))
|
36
|
+
else
|
37
|
+
command_executor.new(connection, command)
|
31
38
|
end
|
32
|
-
connection.loop
|
33
39
|
end
|
34
40
|
|
35
41
|
def connection
|
36
42
|
@connection ||= connector.start(fqdn, username, password: password)
|
37
43
|
end
|
44
|
+
|
45
|
+
def prefix_sudo(command)
|
46
|
+
%Q{sudo bash -c "#{command}"}
|
47
|
+
end
|
48
|
+
|
49
|
+
def with_sudo?
|
50
|
+
@with_sudo
|
51
|
+
end
|
52
|
+
|
38
53
|
end
|
39
54
|
end
|
data/lib/minionizer/version.rb
CHANGED
data/minionizer.gemspec
CHANGED
@@ -10,11 +10,12 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.summary = %q{Simple server provisioning and management.}
|
11
11
|
s.description = %q{Minionizer aims to be a light weight server provisioning tool without bloat or steep learning curves.}
|
12
12
|
|
13
|
-
s.add_dependency('activesupport')
|
14
|
-
s.add_dependency('net-ssh')
|
13
|
+
s.add_dependency('activesupport', '~> 4.1')
|
14
|
+
s.add_dependency('net-ssh', '~> 2.9')
|
15
15
|
|
16
|
-
s.add_development_dependency('fakefs')
|
17
|
-
s.add_development_dependency('mocha')
|
16
|
+
s.add_development_dependency('fakefs', '~> 0.5')
|
17
|
+
s.add_development_dependency('mocha', '~> 1.0')
|
18
|
+
s.add_development_dependency('rake', '~> 10.3')
|
18
19
|
|
19
20
|
s.files = `git ls-files`.split("\n")
|
20
21
|
s.test_files = `git ls-files -- test/*`.split("\n")
|