linecook 1.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|