tinychef 0.0.2 → 0.0.3
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.
- data/bin/tinychef +16 -25
- data/lib/tinychef.rb +11 -2
- data/lib/tinychef/boot_script.rb +58 -0
- data/lib/tinychef/commands.rb +61 -0
- data/lib/tinychef/data_bag.rb +80 -0
- data/lib/tinychef/key.rb +39 -0
- data/lib/tinychef/new_directory.rb +24 -1
- data/lib/tinychef/node_run.rb +5 -10
- data/lib/tinychef/version.rb +1 -1
- metadata +6 -2
data/bin/tinychef
CHANGED
|
@@ -4,6 +4,7 @@ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
|
|
4
4
|
|
|
5
5
|
require 'optparse'
|
|
6
6
|
require 'tinychef'
|
|
7
|
+
require 'tinychef/commands'
|
|
7
8
|
|
|
8
9
|
parser = OptionParser.new do |opts|
|
|
9
10
|
opts.banner = "Usage: tinychef COMMAND [ARGS]"
|
|
@@ -23,6 +24,7 @@ parser = OptionParser.new do |opts|
|
|
|
23
24
|
opts.separator " bag:create NAME Creates an empty databag"
|
|
24
25
|
opts.separator " bag:encrypt NAME Encrypts a databag"
|
|
25
26
|
opts.separator " bag:decrypt NAME Decrypts a databag"
|
|
27
|
+
opts.separator " key:generate Generate a new random key"
|
|
26
28
|
opts.separator " key:lock Lock the key with a password"
|
|
27
29
|
opts.separator " key:unlock Unlock the key for a work session"
|
|
28
30
|
opts.separator " secure Remove all unsecure files from this folder"
|
|
@@ -34,35 +36,24 @@ end
|
|
|
34
36
|
|
|
35
37
|
parser.parse!
|
|
36
38
|
|
|
39
|
+
include Tinychef::Commands
|
|
40
|
+
|
|
37
41
|
begin
|
|
38
42
|
case ARGV[0]
|
|
39
|
-
when 'new'
|
|
40
|
-
|
|
41
|
-
when '
|
|
42
|
-
when 'bag:
|
|
43
|
-
when 'bag:
|
|
44
|
-
when '
|
|
45
|
-
when 'key:lock'
|
|
46
|
-
when 'key:unlock'
|
|
47
|
-
when 'secure'
|
|
48
|
-
when 'unsecure'
|
|
49
|
-
when 'run'
|
|
50
|
-
options = {}
|
|
51
|
-
|
|
52
|
-
options[:node] = ARGV[1]
|
|
53
|
-
|
|
54
|
-
if ARGV.size == 3 # assume the destination is not given
|
|
55
|
-
options[:dest] = nil ; options[:run_list] = ARGV[2]
|
|
56
|
-
else
|
|
57
|
-
options[:dest] = ARGV[2] ; options[:run_list] = ARGV[3]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
Tinychef::NodeRun.new(options).start
|
|
43
|
+
when 'new' then new_command
|
|
44
|
+
when 'boot' then boot_command
|
|
45
|
+
when 'bag:create' then bag_create_command
|
|
46
|
+
when 'bag:encrypt' then bag_encrypt_command
|
|
47
|
+
when 'bag:decrypt' then bag_decrypt_command
|
|
48
|
+
when 'key:generate' then key_generate_command
|
|
49
|
+
when 'key:lock' then key_lock_command
|
|
50
|
+
when 'key:unlock' then key_unlock_command
|
|
51
|
+
when 'secure' then secure_command
|
|
52
|
+
when 'unsecure' then unsecure_command
|
|
53
|
+
when 'run' then run_command
|
|
61
54
|
else
|
|
62
55
|
puts parser.help
|
|
63
56
|
end
|
|
64
|
-
rescue Tinychef::
|
|
57
|
+
rescue Tinychef::OptionsError, RuntimeError => e
|
|
65
58
|
abort e.message
|
|
66
|
-
|
|
67
|
-
|
|
68
59
|
end
|
data/lib/tinychef.rb
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'chef/encrypted_data_bag_item'
|
|
3
|
+
require 'pp'
|
|
4
|
+
|
|
1
5
|
require "tinychef/version"
|
|
6
|
+
require 'tinychef/key'
|
|
7
|
+
require 'tinychef/data_bag'
|
|
8
|
+
require 'tinychef/boot_script'
|
|
9
|
+
require 'tinychef/node_run'
|
|
2
10
|
|
|
3
|
-
module Tinychef
|
|
11
|
+
module Tinychef
|
|
12
|
+
class OptionsError < Exception; end
|
|
13
|
+
end
|
|
4
14
|
|
|
5
|
-
require 'tinychef/node_run'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'tinychef/node'
|
|
3
|
+
require 'tinychef/destination'
|
|
4
|
+
|
|
5
|
+
module Tinychef
|
|
6
|
+
class BootScript
|
|
7
|
+
|
|
8
|
+
OPTIONS_ERROR = <<-EOH
|
|
9
|
+
Required arguments:
|
|
10
|
+
|
|
11
|
+
DEST: ssh username@host string to use to connect to host.
|
|
12
|
+
SCRIPT: optional path to the boot script (boot.sh by default).
|
|
13
|
+
You must craft your Chef Solo boot script by yourself,
|
|
14
|
+
something like this: https://gist.github.com/3153784
|
|
15
|
+
|
|
16
|
+
EOH
|
|
17
|
+
|
|
18
|
+
attr_reader :dest, :script
|
|
19
|
+
|
|
20
|
+
def initialize(dest, script)
|
|
21
|
+
script ||= 'boot.sh'
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
@dest = Tinychef::Destination.new(dest)
|
|
25
|
+
@script = Pathname.new(script)
|
|
26
|
+
rescue => e
|
|
27
|
+
raise OptionsError.new BootScript::OPTIONS_ERROR
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
file_check
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def run
|
|
34
|
+
push_to_remote
|
|
35
|
+
execute_remote
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def file_check
|
|
41
|
+
unless script.exist?
|
|
42
|
+
raise RuntimeError.new "File #{script} not found. Craft your boot script, something like this: https://gist.github.com/3153784"
|
|
43
|
+
end
|
|
44
|
+
unless script.extname == '.sh'
|
|
45
|
+
raise RuntimeError.new "File must be a shell script (.sh extension please)"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def push_to_remote
|
|
50
|
+
system %Q{ scp #{script} #{dest}: }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def execute_remote
|
|
54
|
+
system %Q{ ssh -t #{dest} "sh #{script.basename}" }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Tinychef
|
|
2
|
+
module Commands
|
|
3
|
+
|
|
4
|
+
def new_command
|
|
5
|
+
dir_name = ARGV[1]
|
|
6
|
+
Tinychef::NewDirectory.new(dir_name).create
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def boot_command
|
|
10
|
+
dest = ARGV[1] ; script = ARGV[2]
|
|
11
|
+
Tinychef::BootScript.new(dest, script).run
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def bag_create_command
|
|
15
|
+
recipe = ARGV[1] ; name = ARGV[2]
|
|
16
|
+
Tinychef::DataBag.new(recipe, name).create
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def bag_encrypt_command
|
|
20
|
+
recipe = ARGV[1] ; name = ARGV[2]
|
|
21
|
+
Tinychef::DataBag.new(recipe, name).encrypt
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def bag_decrypt_command
|
|
25
|
+
recipe = ARGV[1] ; name = ARGV[2]
|
|
26
|
+
Tinychef::DataBag.new(recipe, name).decrypt
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def key_lock_command
|
|
30
|
+
Tinychef::Key.new(ARGV[1]).password_protect
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def key_unlock_command
|
|
34
|
+
Tinychef::Key.new(ARGV[1]).unlock_and_restore
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def key_generate_command
|
|
38
|
+
Tinychef::Key.new(ARGV[1]).generate
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def secure_command
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def unsecure_command
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def run_command
|
|
48
|
+
options = {}
|
|
49
|
+
|
|
50
|
+
options[:node] = ARGV[1]
|
|
51
|
+
|
|
52
|
+
if ARGV.size == 3 # assume the destination is not given
|
|
53
|
+
options[:dest] = nil ; options[:run_list] = ARGV[2]
|
|
54
|
+
else
|
|
55
|
+
options[:dest] = ARGV[2] ; options[:run_list] = ARGV[3]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
Tinychef::NodeRun.new(options).start
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Tinychef
|
|
2
|
+
class DataBag
|
|
3
|
+
|
|
4
|
+
OPTIONS_ERROR = <<-EOH
|
|
5
|
+
Required arguments:
|
|
6
|
+
|
|
7
|
+
RECIPE: Databags must be archived under the name of a recipe.
|
|
8
|
+
NAME: Name of this databag.
|
|
9
|
+
|
|
10
|
+
EOH
|
|
11
|
+
|
|
12
|
+
attr_reader :recipe, :name, :ruby_name, :ruby_path, :json_name, :json_path
|
|
13
|
+
|
|
14
|
+
def initialize(recipe, name)
|
|
15
|
+
|
|
16
|
+
if recipe.nil? || name.nil?
|
|
17
|
+
raise OptionsError.new OPTIONS_ERROR
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@name = name
|
|
21
|
+
@ruby_name = "#{name}.rb"
|
|
22
|
+
@json_name = "#{name}.json"
|
|
23
|
+
@recipe = recipe
|
|
24
|
+
|
|
25
|
+
@dir = Pathname.new(File.join('data_bags', @recipe))
|
|
26
|
+
|
|
27
|
+
@ruby_path = @dir.join @ruby_name
|
|
28
|
+
@json_path = @dir.join @json_name
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create
|
|
32
|
+
prepare_dir
|
|
33
|
+
create_empty_file
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def decrypt
|
|
37
|
+
data = JSON.parse File.read(json_path)
|
|
38
|
+
secret = Chef::EncryptedDataBagItem.load_secret( key.path )
|
|
39
|
+
encrypted_data = Chef::EncryptedDataBagItem.new(data, secret)
|
|
40
|
+
new_hash = {}
|
|
41
|
+
data.keys.each do |key|
|
|
42
|
+
new_hash[key] = encrypted_data[key]
|
|
43
|
+
end
|
|
44
|
+
file = File.open ruby_path, 'w'
|
|
45
|
+
file.write new_hash.inspect
|
|
46
|
+
file.close
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def encrypt
|
|
50
|
+
data = eval File.read(ruby_path)
|
|
51
|
+
secret = Chef::EncryptedDataBagItem.load_secret( key.path )
|
|
52
|
+
encrypted_data = Chef::EncryptedDataBagItem.encrypt_data_bag_item(data, secret)
|
|
53
|
+
file = File.open json_path, 'w'
|
|
54
|
+
file.write encrypted_data.to_json
|
|
55
|
+
file.close
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def key
|
|
61
|
+
Tinychef::Key.new
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def prepare_dir
|
|
65
|
+
dir = Pathname.new(File.join 'data_bags', recipe)
|
|
66
|
+
dir.mkpath unless dir.exist?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def create_empty_file
|
|
70
|
+
if ruby_path.exist?
|
|
71
|
+
RuntimeError.new 'File #{ruby_path.to_s} already exists.'
|
|
72
|
+
else
|
|
73
|
+
file = File.new ruby_path, 'a'
|
|
74
|
+
file.write %Q^{ "id" => "#{name}" }^
|
|
75
|
+
file.close
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/tinychef/key.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Tinychef
|
|
2
|
+
class Key
|
|
3
|
+
|
|
4
|
+
OPTIONS_ERROR = <<-EOH
|
|
5
|
+
Required arguments:
|
|
6
|
+
|
|
7
|
+
NAME: the name of the secret file.
|
|
8
|
+
|
|
9
|
+
EOH
|
|
10
|
+
|
|
11
|
+
attr_reader :name, :path
|
|
12
|
+
|
|
13
|
+
def initialize(name='secret')
|
|
14
|
+
raise if name.nil?
|
|
15
|
+
|
|
16
|
+
@name = name
|
|
17
|
+
@path = Pathname.new "#{name}.key"
|
|
18
|
+
rescue
|
|
19
|
+
raise OptionsError.new OPTIONS_ERROR
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def password_protect
|
|
23
|
+
system "openssl aes-256-cbc -salt -in #{path} -out #{path}.aes"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def unlock_and_restore
|
|
27
|
+
system "openssl aes-256-cbc -d -salt -in #{path}.aes -out #{path}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate
|
|
31
|
+
if path.exist?
|
|
32
|
+
raise RuntimeError.new "File #{path} exists."
|
|
33
|
+
else
|
|
34
|
+
system "openssl rand -base64 512 | tr -d '\r\n' > #{path}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -1,6 +1,29 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
1
3
|
module Tinychef
|
|
2
4
|
class NewDirectory
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
DIR_MAP = {
|
|
7
|
+
'cookbooks' => nil,
|
|
8
|
+
'data_bags' => nil,
|
|
9
|
+
'imported_cookbooks' => nil,
|
|
10
|
+
'nodes' => nil,
|
|
11
|
+
'roles' => nil,
|
|
12
|
+
'vendor' => nil,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
attr_reader :path
|
|
16
|
+
|
|
17
|
+
def initialize(name)
|
|
18
|
+
@path = Pathname.new(name)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create
|
|
22
|
+
path.mkpath unless path.exist?
|
|
23
|
+
|
|
24
|
+
DIR_MAP.each do |key, value|
|
|
25
|
+
path.join(key).mkpath
|
|
26
|
+
end
|
|
4
27
|
end
|
|
5
28
|
end
|
|
6
29
|
end
|
data/lib/tinychef/node_run.rb
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
require 'tinychef/node'
|
|
2
2
|
require 'tinychef/destination'
|
|
3
|
+
require 'tinychef/new_directory'
|
|
3
4
|
|
|
4
5
|
module Tinychef
|
|
5
6
|
class NodeRun
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
def initialize
|
|
9
|
-
super <<-EOH
|
|
10
|
-
|
|
8
|
+
OPTIONS_ERROR = <<-EOH
|
|
11
9
|
Required arguments:
|
|
12
10
|
|
|
13
11
|
NODE: node file to execute
|
|
@@ -15,11 +13,9 @@ Required arguments:
|
|
|
15
13
|
If missing, this is guessed by the node file name.
|
|
16
14
|
RUN_LIST: an optional RUN_LIST to be passed to chef-solo executable.
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
end
|
|
20
|
-
end
|
|
16
|
+
EOH
|
|
21
17
|
|
|
22
|
-
RECIPES_DIR="recipes"
|
|
18
|
+
RECIPES_DIR = "recipes"
|
|
23
19
|
|
|
24
20
|
attr_reader :dest, :node, :run_list, :run_list_command
|
|
25
21
|
|
|
@@ -36,8 +32,7 @@ Required arguments:
|
|
|
36
32
|
prepare_run_list_command
|
|
37
33
|
|
|
38
34
|
rescue => e
|
|
39
|
-
|
|
40
|
-
raise OptionsError
|
|
35
|
+
raise OptionsError.new NodeRun::OPTIONS_ERROR
|
|
41
36
|
end
|
|
42
37
|
|
|
43
38
|
def start
|
data/lib/tinychef/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tinychef
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-12-
|
|
12
|
+
date: 2012-12-30 00:00:00.000000000 Z
|
|
13
13
|
dependencies: []
|
|
14
14
|
description: Simple command line tool for Chef Solo recipes
|
|
15
15
|
email:
|
|
@@ -26,7 +26,11 @@ files:
|
|
|
26
26
|
- Rakefile
|
|
27
27
|
- bin/tinychef
|
|
28
28
|
- lib/tinychef.rb
|
|
29
|
+
- lib/tinychef/boot_script.rb
|
|
30
|
+
- lib/tinychef/commands.rb
|
|
31
|
+
- lib/tinychef/data_bag.rb
|
|
29
32
|
- lib/tinychef/destination.rb
|
|
33
|
+
- lib/tinychef/key.rb
|
|
30
34
|
- lib/tinychef/new_directory.rb
|
|
31
35
|
- lib/tinychef/node.rb
|
|
32
36
|
- lib/tinychef/node_run.rb
|