linecook 1.2.1 → 2.0.0

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