linecook 1.2.1 → 2.0.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.
- data/{History → History.rdoc} +3 -2
- data/README.rdoc +93 -0
- data/bin/linecook +32 -56
- data/bin/linecook_run +19 -6
- data/bin/linecook_scp +12 -4
- data/doc/vm_setup.rdoc +75 -0
- data/lib/linecook.rb +3 -2
- data/lib/linecook/attributes.rb +33 -8
- data/lib/linecook/command.rb +61 -0
- data/lib/linecook/command_set.rb +85 -0
- data/lib/linecook/command_utils.rb +20 -0
- data/lib/linecook/commands/build.rb +108 -57
- data/lib/linecook/commands/compile.rb +181 -0
- data/lib/linecook/commands/{helper.rb → compile_helper.rb} +123 -94
- data/lib/linecook/commands/run.rb +43 -39
- data/lib/linecook/commands/snapshot.rb +24 -24
- data/lib/linecook/commands/ssh.rb +7 -7
- data/lib/linecook/commands/start.rb +10 -10
- data/lib/linecook/commands/state.rb +7 -7
- data/lib/linecook/commands/stop.rb +3 -3
- data/lib/linecook/commands/{vbox_command.rb → virtual_box_command.rb} +31 -29
- data/lib/linecook/cookbook.rb +149 -131
- data/lib/linecook/executable.rb +28 -0
- data/lib/linecook/package.rb +177 -361
- data/lib/linecook/proxy.rb +4 -10
- data/lib/linecook/recipe.rb +289 -369
- data/lib/linecook/test.rb +114 -98
- data/lib/linecook/utils.rb +31 -41
- data/lib/linecook/version.rb +2 -6
- metadata +120 -68
- data/HowTo/Control Virtual Machines +0 -106
- data/HowTo/Generate Scripts +0 -268
- data/HowTo/Run Scripts +0 -87
- data/HowTo/Setup Virtual Machines +0 -76
- data/README +0 -117
- data/lib/linecook/commands.rb +0 -11
- data/lib/linecook/commands/command.rb +0 -58
- data/lib/linecook/commands/command_error.rb +0 -12
- data/lib/linecook/commands/env.rb +0 -89
- data/lib/linecook/commands/init.rb +0 -86
- data/lib/linecook/commands/package.rb +0 -57
- data/lib/linecook/template.rb +0 -17
- data/lib/linecook/test/command_parser.rb +0 -75
- data/lib/linecook/test/file_test.rb +0 -197
- data/lib/linecook/test/regexp_escape.rb +0 -86
- data/lib/linecook/test/shell_test.rb +0 -177
- data/lib/linecook/test/shim.rb +0 -71
- data/templates/Gemfile +0 -3
- data/templates/Rakefile +0 -146
- data/templates/_gitignore +0 -4
- data/templates/attributes/project_name.rb +0 -3
- data/templates/config/ssh +0 -14
- data/templates/cookbook +0 -10
- data/templates/files/example.txt +0 -1
- data/templates/helpers/project_name/echo.erb +0 -4
- data/templates/packages/abox.yml +0 -2
- data/templates/project_name.gemspec +0 -30
- data/templates/recipes/abox.rb +0 -16
- data/templates/templates/example.erb +0 -1
- data/templates/test/project_name_test.rb +0 -24
- data/templates/test/test_helper.rb +0 -14
data/{History → History.rdoc}
RENAMED
data/README.rdoc
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
= Linecook
|
2
|
+
|
3
|
+
A shell script generator.
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
Linecook helps to make scripts more manageable by allowing you to compose them
|
8
|
+
from ERB helpers and attributes files. Scripts can be written at a higher
|
9
|
+
level, and easily reconstructed when something changes. Scripts can be
|
10
|
+
compiled in place, or built into packages that can be used, for example, to
|
11
|
+
provision servers.
|
12
|
+
|
13
|
+
== Usage
|
14
|
+
|
15
|
+
Layout a project.
|
16
|
+
|
17
|
+
mkdir -p attributes helpers packages recipes
|
18
|
+
|
19
|
+
Define attributes.
|
20
|
+
|
21
|
+
cat > attributes/chalkboard.yml <<DOC
|
22
|
+
n: 3
|
23
|
+
color: blue
|
24
|
+
message: I will not manually configure my server
|
25
|
+
DOC
|
26
|
+
|
27
|
+
Define a helper.
|
28
|
+
|
29
|
+
mkdir -p helpers/chalkboard
|
30
|
+
cat > helpers/chalkboard/echo_in_color.erb <<DOC
|
31
|
+
Echo a string in color.
|
32
|
+
(color, str)
|
33
|
+
color_codes = Hash[*%W{
|
34
|
+
black 0;30 red 0;31
|
35
|
+
white 1;37 green 0;32
|
36
|
+
light_gray 0;37 blue 0;34
|
37
|
+
}]
|
38
|
+
--
|
39
|
+
echo -e '\033[<%= color_codes[color.to_s] %>m<%= str %>\033[0m'
|
40
|
+
DOC
|
41
|
+
|
42
|
+
Use both in a recipe.
|
43
|
+
|
44
|
+
cat > recipes/chalkboard.rb <<DOC
|
45
|
+
attributes "chalkboard"
|
46
|
+
helpers "chalkboard"
|
47
|
+
|
48
|
+
attrs['n'].times do
|
49
|
+
echo_in_color attrs['color'], attrs['message']
|
50
|
+
end
|
51
|
+
DOC
|
52
|
+
|
53
|
+
Build the recipe.
|
54
|
+
|
55
|
+
linecook build -c recipes/chalkboard.rb
|
56
|
+
|
57
|
+
Check the packages directory to see the resulting script.
|
58
|
+
|
59
|
+
cat packages/chalkboard/run
|
60
|
+
echo -e '\033[0;34mI will not manually configure my server\033[0m'
|
61
|
+
echo -e '\033[0;34mI will not manually configure my server\033[0m'
|
62
|
+
echo -e '\033[0;34mI will not manually configure my server\033[0m'
|
63
|
+
|
64
|
+
== Installation
|
65
|
+
|
66
|
+
Linecook is available as a {gem}[http://rubygems.org/gems/linecook].
|
67
|
+
|
68
|
+
gem install linecook
|
69
|
+
|
70
|
+
== Development
|
71
|
+
|
72
|
+
Install dependencies using Bundler:
|
73
|
+
|
74
|
+
gem install bundler
|
75
|
+
bundle install
|
76
|
+
|
77
|
+
Build a test VM using the {VM Setup}[rdoc-ref:doc/vm_setup.rdoc] doc, and make
|
78
|
+
a config/ssh file that can can connect to it using the default Host (ie Host
|
79
|
+
*). If you didn't customize anything, then you can use 'config/ssh.example'
|
80
|
+
directly. When setup correctly this prints 'success' with any HOST:
|
81
|
+
|
82
|
+
ssh -F config/ssh [HOST] -- 'echo success'
|
83
|
+
|
84
|
+
Now run the tests:
|
85
|
+
|
86
|
+
rake test
|
87
|
+
|
88
|
+
Report issues and submit pull requests on {GitHub}[https://github.pinnacol.com/pinnacol/linecook].
|
89
|
+
|
90
|
+
== Info
|
91
|
+
|
92
|
+
Developer:: {Simon Chiang}[http://github.com/thinkerbot]
|
93
|
+
License:: {MIT-Style}[link:files/License_txt.html]
|
data/bin/linecook
CHANGED
@@ -1,65 +1,41 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
if File.exists?('Gemfile') && ENV['LINECOOK_USE_BUNDLER'] != 'false'
|
4
|
-
require 'rubygems'
|
5
|
-
require 'bundler'
|
6
|
-
Bundler.setup
|
7
|
-
end
|
8
|
-
|
9
3
|
require 'linecook/version'
|
10
|
-
require 'linecook/
|
11
|
-
registry = Linecook::Commands::Command.registry
|
4
|
+
require 'linecook/executable'
|
12
5
|
|
13
|
-
|
14
|
-
|
6
|
+
if ARGV.empty?
|
7
|
+
ARGV.unshift('--help')
|
8
|
+
end
|
15
9
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
10
|
+
begin
|
11
|
+
Linecook::Executable.run do |callpath, command_class, options|
|
12
|
+
options.on '-h', '--help', 'print this help' do
|
13
|
+
puts "usage: #{callpath.unshift('linecook').join(' ')} #{command_class.signature}"
|
14
|
+
if command_class.respond_to?(:command_list)
|
15
|
+
puts
|
16
|
+
puts "commands:"
|
17
|
+
command_class.command_list.each do |name, command|
|
18
|
+
puts " %-30s %s" % [name, command.desc]
|
19
|
+
end
|
20
|
+
puts
|
21
|
+
else
|
22
|
+
puts command_class.help
|
23
|
+
end
|
24
|
+
puts "options:"
|
25
|
+
puts options.to_s
|
26
|
+
|
27
|
+
exit
|
25
28
|
end
|
26
|
-
puts
|
27
|
-
puts "options:"
|
28
|
-
puts " -%s, --%-10s %s" % ['v', 'version', 'print version']
|
29
|
-
puts " -%s, --%-10s %s" % ['h', 'help', 'print this help']
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
exit 1
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
parser = ConfigParser.new
|
42
|
-
parser.add cmd_class.configurations
|
43
|
-
parser.on '-h', '--help', 'print this help' do
|
44
|
-
puts "usage: linecook [options] #{cmd} #{cmd_class.args}"
|
45
|
-
|
46
|
-
desc = cmd_class.desc.wrap(76, 2, "\n ")
|
47
|
-
unless desc.empty?
|
48
|
-
puts
|
49
|
-
puts " #{desc}"
|
50
|
-
puts
|
30
|
+
if command_class == Linecook::Executable
|
31
|
+
options.on('-v', '--version', 'print version') do |v|
|
32
|
+
puts "linecook version #{Linecook::VERSION} -- #{Linecook::WEBSITE}"
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
end
|
51
36
|
end
|
52
|
-
|
53
|
-
puts
|
54
|
-
puts parser.to_s
|
55
|
-
exit
|
56
|
-
end
|
57
|
-
parser.parse! ARGV
|
58
|
-
|
59
|
-
begin
|
60
|
-
cmd_class.new(parser.config).call ARGV
|
61
|
-
rescue Linecook::Commands::CommandError
|
62
|
-
puts $!.message unless $!.message.strip.empty?
|
37
|
+
rescue Linecook::CommandError
|
38
|
+
puts $!.message
|
63
39
|
puts $!.backtrace if $DEBUG
|
64
|
-
exit
|
65
|
-
end
|
40
|
+
exit 1
|
41
|
+
end
|
data/bin/linecook_run
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
#! /bin/sh
|
2
2
|
############################################################################
|
3
3
|
|
4
|
-
ssh_config_file=${SSH_CONFIG_FILE
|
5
|
-
remote_dir=${REMOTE_DIR
|
4
|
+
ssh_config_file=${SSH_CONFIG_FILE:-~/.ssh/config}
|
5
|
+
remote_dir=${REMOTE_DIR:-\$(pwd)/linecook}
|
6
6
|
remote_script=${REMOTE_SCRIPT:-run}
|
7
|
+
xtrace="false"
|
7
8
|
|
8
|
-
usage="usage: %s [-F SSH_CONFIG_FILE] [-D REMOTE_DIR] [-S REMOTE_SCRIPT] [-h] PACKAGE_DIRS...\n"
|
9
|
+
usage="usage: %s [-F SSH_CONFIG_FILE] [-D REMOTE_DIR] [-S REMOTE_SCRIPT] [-h] [-x] PACKAGE_DIRS...\n"
|
9
10
|
option=" %s %s\n"
|
10
|
-
while getopts "F:D:S:h" opt
|
11
|
+
while getopts "F:D:S:h:x" opt
|
11
12
|
do
|
12
13
|
case $opt in
|
13
14
|
F ) ssh_config_file=$OPTARG ;;
|
@@ -18,20 +19,33 @@ do
|
|
18
19
|
printf "$option" "-D" "the remote package dir"
|
19
20
|
printf "$option" "-S" "the remote script"
|
20
21
|
printf "$option" "-h" "prints this help"
|
22
|
+
printf "$option" "-x" "xtrace this script"
|
21
23
|
exit 0 ;;
|
24
|
+
x ) xtrace="true" ;;
|
22
25
|
\? ) printf "$usage" $0
|
23
26
|
exit 2 ;;
|
24
27
|
esac
|
25
28
|
done
|
26
29
|
shift $(($OPTIND - 1))
|
27
30
|
|
31
|
+
if [ "$xtrace" = "true" ]
|
32
|
+
then set -x
|
33
|
+
fi
|
34
|
+
|
28
35
|
################################### run ####################################
|
29
36
|
|
30
37
|
for package_dir in "$@"
|
31
38
|
do
|
32
39
|
host=$(basename -- "$package_dir")
|
33
40
|
|
34
|
-
|
41
|
+
# allocate pseudo-tty if possible so the script behaves as if the user
|
42
|
+
# manually ran it on the VM, but don't force allocation as it can cause
|
43
|
+
# hanging if no user is available for interaction (ex with su).
|
44
|
+
if tty > /dev/null
|
45
|
+
then ttyopt='-t'
|
46
|
+
fi
|
47
|
+
|
48
|
+
ssh -q $ttyopt -F "$ssh_config_file" "$host" -- "$remote_dir/$remote_script"
|
35
49
|
|
36
50
|
status=$?
|
37
51
|
if [ $status -ne 0 ]
|
@@ -39,7 +53,6 @@ then
|
|
39
53
|
echo "[$status] $remote_dir/$remote_script" >&2
|
40
54
|
exit 1
|
41
55
|
fi
|
42
|
-
|
43
56
|
done
|
44
57
|
|
45
58
|
################################## (run) ###################################
|
data/bin/linecook_scp
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
#! /bin/sh
|
2
2
|
############################################################################
|
3
3
|
|
4
|
-
ssh_config_file=${SSH_CONFIG_FILE
|
4
|
+
ssh_config_file=${SSH_CONFIG_FILE:-~/.ssh/config}
|
5
5
|
remote_dir=${REMOTE_DIR:-\$(pwd)/linecook}
|
6
|
+
xtrace="false"
|
6
7
|
|
7
|
-
usage="usage: %s [-D REMOTE_DIR] [-F SSH_CONFIG_FILE] [-h] PACKAGE_DIRS...\n"
|
8
|
+
usage="usage: %s [-D REMOTE_DIR] [-F SSH_CONFIG_FILE] [-h] [-x] PACKAGE_DIRS...\n"
|
8
9
|
option=" %s %s\n"
|
9
|
-
while getopts "F:D:h" opt
|
10
|
+
while getopts "F:D:h:x" opt
|
10
11
|
do
|
11
12
|
case $opt in
|
12
13
|
F ) ssh_config_file=$OPTARG ;;
|
@@ -15,19 +16,27 @@ do
|
|
15
16
|
printf "$option" "-F" "the ssh config file"
|
16
17
|
printf "$option" "-D" "the remote package dir"
|
17
18
|
printf "$option" "-h" "prints this help"
|
19
|
+
printf "$option" "-x" "xtrace this script"
|
18
20
|
exit 0 ;;
|
21
|
+
x ) xtrace="true" ;;
|
19
22
|
\? ) printf "$usage" $0
|
20
23
|
exit 2 ;;
|
21
24
|
esac
|
22
25
|
done
|
23
26
|
shift $(($OPTIND - 1))
|
24
27
|
|
28
|
+
if [ "$xtrace" = "true" ]
|
29
|
+
then set -x
|
30
|
+
fi
|
31
|
+
|
25
32
|
################################### scp ####################################
|
26
33
|
|
27
34
|
for package_dir in "$@"
|
28
35
|
do
|
29
36
|
host=$(basename -- "$package_dir")
|
30
37
|
|
38
|
+
# Note the subshell substitutions need to be escaped so that they will be
|
39
|
+
# evaluated on the host -- ex: -R '$TMPDIR' (to get TMPDIR on the host)
|
31
40
|
ssh -q -T -F "$ssh_config_file" "$host" -- <<SCRIPT
|
32
41
|
rm -rf "$remote_dir"
|
33
42
|
if [ "\$(dirname "$remote_dir")" != "" ]
|
@@ -47,4 +56,3 @@ fi
|
|
47
56
|
done
|
48
57
|
|
49
58
|
################################## (scp) ###################################
|
50
|
-
|
data/doc/vm_setup.rdoc
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
= Setting up a VM
|
2
|
+
|
3
|
+
The only real requirement to allow testing of Linecook on a VM is that the VM
|
4
|
+
be reachable by ssh. If you choose to run your VMs on VirtualBox and take care
|
5
|
+
with a few snapshots then you can use some extra rake commands that expedite
|
6
|
+
testing. Specifically you can reset+start and stop the VMs using:
|
7
|
+
|
8
|
+
rake start
|
9
|
+
rake stop
|
10
|
+
|
11
|
+
As an example, build a {Ubuntu}[http://www.ubuntu.com/] VM on
|
12
|
+
{VirtualBox}[http://www.virtualbox.org/] using the following:
|
13
|
+
|
14
|
+
- name: ubuntu
|
15
|
+
- Linux/Ubuntu
|
16
|
+
- 512 MB memory
|
17
|
+
- 8 GB dynamically resizing drive
|
18
|
+
|
19
|
+
Add the iso to the cd/dvd device under Settings > Storage. Now start the
|
20
|
+
server and install ubuntu (use default settings unless specified):
|
21
|
+
|
22
|
+
- user/password: linecook
|
23
|
+
- select 'OpenSSH server' in packages to install
|
24
|
+
|
25
|
+
When the server has rebooted and is ready at the login screen, remove the
|
26
|
+
install iso, take a snapshot and setup port forwarding. The snapshot is for
|
27
|
+
convenience. Port forwarding allows you to ssh to yourself on one port (2220)
|
28
|
+
and have that be received by the VM on another port (22).
|
29
|
+
|
30
|
+
(Devices > CD/DVD Devices > Remove disk from virtual drive)
|
31
|
+
VBoxManage snapshot ubuntu take RAW --pause
|
32
|
+
VBoxManage controlvm ubuntu poweroff
|
33
|
+
# wait to fully power off
|
34
|
+
VBoxManage modifyvm ubuntu --natpf1 'ubuntu-ssh,tcp,,2220,,22'
|
35
|
+
VBoxManage -q snapshot ubuntu restore RAW
|
36
|
+
VBoxManage startvm ubuntu
|
37
|
+
|
38
|
+
Transfer your ssh key to the vm. Help to generate ssh keys can be found on
|
39
|
+
{GitHub}[http://help.github.com/key-setup-redirect]:
|
40
|
+
|
41
|
+
scp -P 2220 -o UserKnownHostsFile=/dev/null ~/.ssh/id_rsa.pub linecook@localhost:id_rsa.pub
|
42
|
+
|
43
|
+
Setup SSH for your user:
|
44
|
+
|
45
|
+
vm: mkdir .ssh
|
46
|
+
vm: mv id_rsa.pub .ssh/authorized_keys
|
47
|
+
vm: chmod 0700 .ssh
|
48
|
+
vm: chmod 0600 .ssh/authorized_keys
|
49
|
+
|
50
|
+
Remove the login banner (cleans up test output) and exit.
|
51
|
+
|
52
|
+
vm: sudo rm /etc/motd
|
53
|
+
vm: exit
|
54
|
+
|
55
|
+
Now take some standard snapshots. By taking them at the login screen you can
|
56
|
+
reset to a running VM, which avoids startup overhead.
|
57
|
+
|
58
|
+
VBoxManage snapshot ubuntu take BASE --pause
|
59
|
+
VBoxManage snapshot ubuntu take CURRENT --pause
|
60
|
+
VBoxManage controlvm ubuntu poweroff
|
61
|
+
|
62
|
+
To cleanup the port forwarding (run later, if ever):
|
63
|
+
|
64
|
+
VBoxManage modifyvm ubuntu --natpf1 delete 'ubuntu-ssh'
|
65
|
+
|
66
|
+
Now add configs for this VM to config/ssh:
|
67
|
+
|
68
|
+
Host ubuntu
|
69
|
+
HostName localhost
|
70
|
+
Port 2220
|
71
|
+
User linecook
|
72
|
+
|
73
|
+
Note that the Host, Port, and User should match up to the values you used
|
74
|
+
during the VM setup. To add additional VMs, for instance to test on different
|
75
|
+
operating systems, choose another name/port and repeat.
|
data/lib/linecook.rb
CHANGED
data/lib/linecook/attributes.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Linecook
|
2
|
-
|
2
|
+
|
3
3
|
# Attributes provides a context for specifying default attributes. For
|
4
4
|
# example:
|
5
5
|
#
|
@@ -21,36 +21,61 @@ module Linecook
|
|
21
21
|
NEST_HASH_PROC = Proc.new do |hash, key|
|
22
22
|
hash[key] = Hash.new(&NEST_HASH_PROC)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
class << self
|
26
26
|
# Returns an auto-filling nested hash.
|
27
27
|
def nest_hash
|
28
28
|
Hash.new(&NEST_HASH_PROC)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# Recursively disables automatic nesting of nest_hash hashes.
|
32
32
|
def disable_nest_hash(hash)
|
33
33
|
if hash.default_proc == NEST_HASH_PROC
|
34
34
|
hash.default = nil
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
hash.each_pair do |key, value|
|
38
38
|
if value.kind_of?(Hash)
|
39
39
|
disable_nest_hash(value)
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
hash
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
# An auto-filling nested hash
|
48
48
|
attr_reader :attrs
|
49
|
-
|
49
|
+
|
50
50
|
def initialize
|
51
51
|
@attrs = Attributes.nest_hash
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
|
+
# A list of file extnames that may be loaded by load_attrs
|
55
|
+
def load_attrs_extnames
|
56
|
+
%w{.rb .yml .yaml .json}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Loads the attributes file into attrs. The loading mechanism depends on
|
60
|
+
# the file extname:
|
61
|
+
#
|
62
|
+
# .rb: evaluate in the context of attributes
|
63
|
+
# .yml,.yaml,.json: load as YAML and merge into attrs
|
64
|
+
#
|
65
|
+
# All other file types raise an error.
|
66
|
+
def load_attrs(path)
|
67
|
+
case File.extname(path)
|
68
|
+
when '.rb'
|
69
|
+
instance_eval(File.read(path), path)
|
70
|
+
when '.yml', '.yaml', '.json'
|
71
|
+
attrs.merge!(YAML.load_file(path))
|
72
|
+
else
|
73
|
+
raise "unsupported attributes format: #{path.inspect}"
|
74
|
+
end
|
75
|
+
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
54
79
|
# Disables automatic nesting and returns attrs.
|
55
80
|
def to_hash
|
56
81
|
Attributes.disable_nest_hash(attrs)
|