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.
Files changed (61) hide show
  1. data/{History → History.rdoc} +3 -2
  2. data/README.rdoc +93 -0
  3. data/bin/linecook +32 -56
  4. data/bin/linecook_run +19 -6
  5. data/bin/linecook_scp +12 -4
  6. data/doc/vm_setup.rdoc +75 -0
  7. data/lib/linecook.rb +3 -2
  8. data/lib/linecook/attributes.rb +33 -8
  9. data/lib/linecook/command.rb +61 -0
  10. data/lib/linecook/command_set.rb +85 -0
  11. data/lib/linecook/command_utils.rb +20 -0
  12. data/lib/linecook/commands/build.rb +108 -57
  13. data/lib/linecook/commands/compile.rb +181 -0
  14. data/lib/linecook/commands/{helper.rb → compile_helper.rb} +123 -94
  15. data/lib/linecook/commands/run.rb +43 -39
  16. data/lib/linecook/commands/snapshot.rb +24 -24
  17. data/lib/linecook/commands/ssh.rb +7 -7
  18. data/lib/linecook/commands/start.rb +10 -10
  19. data/lib/linecook/commands/state.rb +7 -7
  20. data/lib/linecook/commands/stop.rb +3 -3
  21. data/lib/linecook/commands/{vbox_command.rb → virtual_box_command.rb} +31 -29
  22. data/lib/linecook/cookbook.rb +149 -131
  23. data/lib/linecook/executable.rb +28 -0
  24. data/lib/linecook/package.rb +177 -361
  25. data/lib/linecook/proxy.rb +4 -10
  26. data/lib/linecook/recipe.rb +289 -369
  27. data/lib/linecook/test.rb +114 -98
  28. data/lib/linecook/utils.rb +31 -41
  29. data/lib/linecook/version.rb +2 -6
  30. metadata +120 -68
  31. data/HowTo/Control Virtual Machines +0 -106
  32. data/HowTo/Generate Scripts +0 -268
  33. data/HowTo/Run Scripts +0 -87
  34. data/HowTo/Setup Virtual Machines +0 -76
  35. data/README +0 -117
  36. data/lib/linecook/commands.rb +0 -11
  37. data/lib/linecook/commands/command.rb +0 -58
  38. data/lib/linecook/commands/command_error.rb +0 -12
  39. data/lib/linecook/commands/env.rb +0 -89
  40. data/lib/linecook/commands/init.rb +0 -86
  41. data/lib/linecook/commands/package.rb +0 -57
  42. data/lib/linecook/template.rb +0 -17
  43. data/lib/linecook/test/command_parser.rb +0 -75
  44. data/lib/linecook/test/file_test.rb +0 -197
  45. data/lib/linecook/test/regexp_escape.rb +0 -86
  46. data/lib/linecook/test/shell_test.rb +0 -177
  47. data/lib/linecook/test/shim.rb +0 -71
  48. data/templates/Gemfile +0 -3
  49. data/templates/Rakefile +0 -146
  50. data/templates/_gitignore +0 -4
  51. data/templates/attributes/project_name.rb +0 -3
  52. data/templates/config/ssh +0 -14
  53. data/templates/cookbook +0 -10
  54. data/templates/files/example.txt +0 -1
  55. data/templates/helpers/project_name/echo.erb +0 -4
  56. data/templates/packages/abox.yml +0 -2
  57. data/templates/project_name.gemspec +0 -30
  58. data/templates/recipes/abox.rb +0 -16
  59. data/templates/templates/example.erb +0 -1
  60. data/templates/test/project_name_test.rb +0 -24
  61. data/templates/test/test_helper.rb +0 -14
@@ -1,6 +1,7 @@
1
- == 1.2.1 2011/05/26
1
+ == 2.0.0 2012/01/05
2
2
 
3
- * made cookbook pick up alternate attributes files
3
+ Big rewrite. Expect little backwards compatibility although the basic idea
4
+ remains the same.
4
5
 
5
6
  == 1.2.0 2011/05/23
6
7
 
@@ -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]
@@ -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/commands'
11
- registry = Linecook::Commands::Command.registry
4
+ require 'linecook/executable'
12
5
 
13
- cmd = ARGV.shift
14
- cmd_class = registry[cmd]
6
+ if ARGV.empty?
7
+ ARGV.unshift('--help')
8
+ end
15
9
 
16
- if cmd_class.nil?
17
- case cmd
18
- when nil, '-h', '--help'
19
- puts "usage: linecook [options] COMMAND"
20
- puts
21
- puts "commands: "
22
- registry.keys.sort.each do |key|
23
- cmd_class = registry[key]
24
- puts " %-16s %s" % [key, cmd_class.desc]
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
- exit
32
- when '-v', '--version'
33
- puts "linecook version #{Linecook::VERSION} -- #{Linecook::WEBSITE}"
34
- exit
35
- else
36
- puts "unknown command: #{cmd.inspect}"
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 "options:"
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 $!.exitstatus
65
- end
40
+ exit 1
41
+ end
@@ -1,13 +1,14 @@
1
1
  #! /bin/sh
2
2
  ############################################################################
3
3
 
4
- ssh_config_file=${SSH_CONFIG_FILE:-config/ssh}
5
- remote_dir=${REMOTE_DIR:-$(pwd)/linecook}
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
- ssh -q -t -t -F "$ssh_config_file" "$host" -- "$remote_dir/$remote_script" 2>/dev/null </dev/null
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) ###################################
@@ -1,12 +1,13 @@
1
1
  #! /bin/sh
2
2
  ############################################################################
3
3
 
4
- ssh_config_file=${SSH_CONFIG_FILE:-config/ssh}
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
-
@@ -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.
@@ -1,4 +1,5 @@
1
- require 'linecook/package'
1
+ require "linecook/version"
2
2
 
3
3
  module Linecook
4
- end
4
+ # Your code goes here...
5
+ end
@@ -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)