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,106 +0,0 @@
1
- = Control Virtual Machines
2
-
3
- Linecook provides these commands to control VirtualBox VMs:
4
-
5
- # ex: 'linecook start'
6
- start start a vm
7
- stop stop a vm
8
- state print the vm state
9
- save take a vm snapshop
10
- ssh ssh to a vm
11
-
12
- Most of these commands wrap the VirtualBox command line tool VBoxManage in
13
- very simple ways. With these commands it's straightforward to make an
14
- iterative script development workflow to build a server component, save the
15
- server state, build another component, and then at any time reset and rebuild
16
- the whole thing from scratch.
17
-
18
- Linecook uses a standard ssh config file to run scripts; the same config file
19
- is also used to configure the VM control commands. More details are presented
20
- in the help to {run scripts}[link:files/HowTo/Run%20Scripts.html], but suffice
21
- these configs will access the 'abox' VM built by the {VM Setup
22
- procedure}[link:files/HowTo/Setup%20Virtual%20Machines.html]:
23
-
24
- [config/ssh]
25
- Host abox
26
- HostName localhost
27
- User linecook
28
- Port 2220
29
-
30
- Now you can control the VM like this:
31
-
32
- # start the VM, resetting to a snapshot
33
- linecook start --snapshot base
34
-
35
- # ssh to the VM
36
- linecook ssh
37
-
38
- # take a snapshot
39
- linecook snapshot modified
40
-
41
- # reset a snapshot, removing all children
42
- linecook snapshot --reset base
43
-
44
- # stop the VM and go home
45
- linecook stop
46
-
47
- == Multiple VMs
48
-
49
- To control multiple VMs, build and configure ssh access to each. For example
50
- make a 'bbox' VM as before but configure ssh access on port 2221:
51
-
52
- - name: bbox
53
- - hostname: bbox-ubuntu
54
- VBoxManage modifyvm bbox --natpf1 'bbox-ssh,tcp,,2221,,22'
55
-
56
- Now add an entry for bbox in the ssh config file (note the * Host can be used
57
- to declare default settings):
58
-
59
- [config/ssh]
60
- Host abox
61
- Port 2220
62
-
63
- Host bbox
64
- Port 2221
65
-
66
- Host *
67
- HostName localhost
68
- User linecook
69
-
70
- Now same linecook commands will control both VMs:
71
-
72
- # start all (or a subset) the VMs to a known snapshot
73
- linecook start --snapshot base
74
- linecook start --snapshot base abox
75
-
76
- # ssh to the VMs one at a time
77
- linecook ssh abox
78
- linecook ssh bbox
79
-
80
- # move the current snapshot forward for all (or a subset) the VMs
81
- linecook snapshot modified
82
- linecook snapshot modified abox
83
-
84
- # reset a snapshot, removing all children for all (or a subset) the VMs
85
- linecook snapshot --reset base
86
- linecook snapshot --reset base abox
87
-
88
- # stop all (or a subset) the VMs
89
- linecook stop
90
- linecook stop abox
91
-
92
- == Host and VM Names
93
-
94
- The Host config corresponds to the package name that will be run on the VM; ie
95
- the 'abox.yml' package goes to the 'abox' Host. Implicitly the VM will also be
96
- named 'abox' in VirtualBox (such that if you opened the VirtualBox application
97
- you'd see 'abox'). If you name it something else, then declare the actual VM
98
- name next to the Host config using a linecook-specific comment like this:
99
-
100
- [config/ssh]
101
- ...
102
- Host abox # [the_vm_name]
103
- ...
104
-
105
- The Host config, 'abox' in this case, is the input for all linecook commands;
106
- linecook resolves the VM name internally when interacting with VirtualBox.
@@ -1,268 +0,0 @@
1
- = Generate Scripts
2
-
3
- Linecook generates scripts and puts them into a directory. Once you have the
4
- scripts you can compress them, share them, run them, or do whatever you please
5
- - at that point they're ordinary files sitting in a directory. Linecook refers
6
- to the directories containing generated scripts as packages.
7
-
8
- The best way to understand how Linecook generates scripts is to start by
9
- making a package with a known script, and then rewrite the script using
10
- Linecook. At each step of the rewrite you can rebuild the package and verify
11
- the script is reproduced.
12
-
13
- The command to rebuild a package is:
14
-
15
- linecook build -Ilib
16
-
17
- Or if you have a proper Gemfile in your project dir:
18
-
19
- linecook build
20
-
21
- This tutorial is designed such that if you make the files as specified, you
22
- can use it to rebuild the same package (functionally or literally) at the end
23
- of each section.
24
-
25
- == Packages
26
-
27
- Packages are located in the packages directory by default. The simplest
28
- package is a directory containing a single executable script. Assuming a bare
29
- Ubuntu VM and a bash shell, start with this script to setup a minimal
30
- development environment:
31
-
32
- [packages/demo/run]
33
- sudo apt-get -y install git
34
- git config --global user.name "John Doe"
35
- git config --global user.email "john.doe@example.com"
36
- sudo apt-get -y install ruby
37
-
38
- This package represents the end result of 'linecook build'. To actually build
39
- it, a package file need to be made. The package file describes what goes into
40
- the package and is written in YAML; by default the package file is named like
41
- the final package, but with a .yml extension.
42
-
43
- As an example, this package file generates the 'run' script from the 'demo' recipe and puts it into the package during build.
44
-
45
- [packages/demo.yml]
46
- linecook:
47
- package:
48
- recipes:
49
- run: demo # package/path: recipe_name
50
-
51
- However this package file is overly verbose; the recipe with the same name as
52
- the package is used to generate the run script by default, so this equivalent:
53
-
54
- [packages/demo.yml]
55
- {}
56
-
57
- At this point a build (ie 'linecook build') raises an error; To see the
58
- package file in action we need the demo recipe.
59
-
60
- == Recipes
61
-
62
- Recipes are ruby code that generates text during a build. Typically the text
63
- is shell script, and therefore the result of a recipe is a script file, but
64
- the text could be a config file, SQL, or whatever.
65
-
66
- Specifically recipes are code that gets executed in the context of a
67
- Linecook::Recipe instance. These instances have a 'target' IO object (usually
68
- a Tempfile) to which the generated text is written. The most basic recipe
69
- simply writes the script content to the target.
70
-
71
- [recipes/demo.rb]
72
- target.puts <<-SCRIPT
73
- sudo apt-get -y install ruby1.8
74
- sudo apt-get -y install git
75
- git config --global user.name "John Doe"
76
- git config --global user.email "john.doe@example.com"
77
- SCRIPT
78
-
79
- Now a build will write the script to the target, then move the corresponding
80
- Tempfile to become 'packages/demo/run'. As code, recipes work like this:
81
-
82
- class Recipe
83
- attr_accessor :target
84
- def initialize
85
- @target = ""
86
- end
87
- end
88
-
89
- recipe = Recipe.new
90
- recipe.instance_eval File.read('recipes/demo.rb')
91
- recipe.target[0, 31] # => "sudo apt-get -y install ruby1.8"
92
-
93
- This exercise illustrates the great and powerful truth of recipes - they are
94
- simply a context to generate text and write it to a file. A direct consequence
95
- of this design is that commands (for example debugging command) can always be
96
- inserted into a script in a trivial manner.
97
-
98
- Obviously recipes can do more.
99
-
100
- == Attributes
101
-
102
- Attributes allow variables to be separated from the recipe code, such that
103
- they may be overridden on a per-package basis, in the package file. Recipes
104
- have a nested 'attrs' hash (which is literally a Hash) that merges together
105
- the defaults and overrides to provide attributes access.
106
-
107
- Using attributes:
108
-
109
- [attributes/git.rb]
110
- attrs['git']['package'] = 'git'
111
- attrs['git']['config']['user.name'] = 'John Doe'
112
- attrs['git']['config']['user.email'] = 'john.doe@example.com'
113
-
114
- [attributes/ruby.rb]
115
- attrs['ruby']['package'] = 'ruby1.9.1'
116
-
117
- [packages/demo.yml]
118
- ruby:
119
- package: ruby1.8
120
-
121
- [recipes/demo.rb]
122
- attributes 'ruby'
123
- attributes 'git'
124
- #########################################################################
125
- target.puts <<-SCRIPT
126
- sudo apt-get -y install #{attrs['ruby']['package']}
127
- sudo apt-get -y install #{attrs['git']['package']}
128
- git config --global user.name "#{attrs['git']['config']['user.name']}"
129
- git config --global user.email "#{attrs['git']['config']['user.email']}"
130
- SCRIPT
131
-
132
- Attribute files must be included using the Recipe#attributes method; no
133
- attributes are included by default. This ensures that attributes will be
134
- deterministic in cases where two attribute files (unwisely) use the same
135
- namespace.
136
-
137
- The overrides specified in the package file are always available, however.
138
- They function as a kind of environment for recipes; since they are overrides,
139
- they take precedence over any values set by attribute files.
140
-
141
- Take note of two details. First, there is no special code needed to set nested
142
- attributes in an attributes file. Attributes files are executed in the context
143
- of a Linecook::Attributes instance which provides this auto-nesting behavior.
144
- Second, as a convention, attrs are accessed using string keys because it's
145
- cleaner to use string keys in the package file.
146
-
147
- == Helpers
148
-
149
- Helpers allow you to define methods that generate text. Helper methods are
150
- defined as ERB files under the 'helpers' directory and compile into an
151
- ordinary module under the 'lib' directory. For example (assuming the
152
- attribute and package files from above):
153
-
154
- [helpers/demo/install.erb]
155
- Installs a package using apt-get.
156
- (package)
157
- --
158
- sudo apt-get -y install <%= package %>
159
-
160
-
161
- [helpers/demo/set_git_config.erb]
162
- Sets a global git config.
163
- (key, value)
164
- --
165
- git config --global <%= key %> "<%= value %>"
166
-
167
-
168
- [recipes/demo.rb]
169
- attributes 'ruby'
170
- attributes 'git'
171
- helpers 'demo'
172
- #########################################################################
173
- install attrs['ruby']['package']
174
- install attrs['git']['package']
175
- ['user.name', 'user.email'].each do |key|
176
- set_git_config key, attrs['git']['config'][key]
177
- end
178
-
179
- When you define a helper, you're literally defining a method in a module that
180
- you can use in your recipe to generate text. This example generates the 'Demo'
181
- module which adds 'install' and 'set_git_config' into the recipe context. In
182
- fact the 'helpers' method is equivalent to:
183
-
184
- require 'demo'
185
- extend Demo
186
-
187
- The exact details are elegant but unimportant to the workflow, which can be
188
- summed up like this: put an ERB template into a file, include the directory
189
- using Recipe#helpers, and now the template is available as a method with
190
- inputs. Whenever you call the method, you make text that gets written to
191
- target.
192
-
193
- To capture the output of a template without writing it to target, prefix the
194
- method with an underscore. The output can then be used as an input to another
195
- helper... see the {README}[link:files/README.html] for an example.
196
-
197
- == Files
198
-
199
- Files are files of any sort that you might want to include in a package. They
200
- could be an archive of some sort, a binary, or a stock script you don't need a
201
- recipe to reproduce.
202
-
203
- Files are typically included via a recipe like this:
204
-
205
- [files/gitconfig]
206
- [user]
207
- name = John Doe
208
- email = john.doe@example.com
209
-
210
- [recipes/demo.rb]
211
- target.puts <<-SCRIPT
212
- sudo apt-get -y install ruby1.8
213
- sudo apt-get -y install git
214
- cp "#{ file_path "gitconfig" }" ~/.gitconfig
215
- SCRIPT
216
-
217
- The only 'trick' here is that the return value of file_path is a path that
218
- will be correct at runtime, when the script is on the remote server
219
- (specifically it will be like "${0%/run}/gitconfig", which works because $0
220
- will be the full path to the recipe).
221
-
222
- == Templates
223
-
224
- Templates work the same as files except, as you may imagine, they are ERB
225
- templates that evaluate with whatever locals you provide them. The attrs hash
226
- is local by default.
227
-
228
- [attributes/git.rb]
229
- attrs['git']['package'] = 'git'
230
- attrs['git']['config']['user.name'] = 'John Doe'
231
- attrs['git']['config']['user.email'] = 'john.doe@example.com'
232
-
233
- [templates/gitconfig.erb]
234
- [user]
235
- name = <%= attrs['git']['config']['user.name'] %>
236
- email = <%= attrs['git']['config']['user.email'] %>
237
-
238
- [recipes/demo.rb]
239
- attributes 'git'
240
- #########################################################################
241
- target.puts <<-SCRIPT
242
- sudo apt-get -y install ruby1.8
243
- sudo apt-get -y install git
244
- cp "#{ template_path "gitconfig.erb" }" ~/.gitconfig
245
- SCRIPT
246
-
247
- == {Linebook}[http://rubygems.org/gems/linebook/]
248
-
249
- The techniques presented here are sufficient to work with many scripts in many
250
- situations but they are quite bare. Eventually, or perhaps immediately, you
251
- will want a suite of standard helpers. The canonical helper library is
252
- {Linebook}[http://rubygems.org/gems/linebook/].
253
-
254
- See the {Linebook
255
- documentation}[http://rubydoc.info/gems/linebook/file/README] to learn helpers
256
- for flow control, file system tests, commands, chaining, redirection,
257
- heredocs, and other convenience methods.
258
-
259
- [recipes/demo.rb]
260
- helpers 'linebook/shell'
261
-
262
- unless_ _file?('/tmp/message') do
263
- cat.to('/tmp/message').heredoc do
264
- writeln 'hello world!'
265
- end
266
- end
267
-
268
- Enjoy!
@@ -1,87 +0,0 @@
1
- = Run Scripts
2
-
3
- Linecook runs scripts using the 'run' command. Internally run copies scripts
4
- to a server using scp and then executes the script using ssh. More
5
- specifically run copies a *package* to the server and executes a script within
6
- the package using ssh. A package is simply a directory containing scripts and
7
- any files the scripts use.
8
-
9
- To manually recreate what run does, make a directory with an script, copy it,
10
- and execute as below (assuming you set up the 'abox' server {as described
11
- earlier}[link:files/Tutorial/1%20-%20VM%20Setup.html]). Note that the script
12
- is made executable locally such that scp will make the script executable on
13
- the server.
14
-
15
- mkdir demo
16
- cat > demo/script.sh <<"DOC"
17
- echo "# $(whoami)@$(hostname): hello world!"
18
- DOC
19
- chmod +x demo/script.sh
20
-
21
- scp -r -P 2220 demo linecook@localhost:/tmp/demo
22
- ssh -p 2220 linecook@localhost -- "/tmp/demo/script.sh"
23
- # linecook@abox-ubuntu: hello world!
24
-
25
- To simplify this a little, move the redundant information used by scp and ssh
26
- can into a standard ssh config file. Linecook looks for an ssh config file in
27
- 'config/ssh' by default:
28
-
29
- mkdir config
30
- cat > config/ssh <<DOC
31
- Host abox
32
- User linecook
33
- HostName localhost
34
- Port 2220
35
- DOC
36
-
37
- Now run will copy the package under the 'packages' directory with the same
38
- name as the Host config, and execute the specified script:
39
-
40
- mkdir packages
41
- mv demo packages/abox
42
- linecook run --script script.sh abox
43
- # linecook@abox-ubuntu: hello world!
44
-
45
- To run multiple packages at once, simply add more hosts to the ssh config
46
- file. Run copies each package to the appropriate host and then runs the same
47
- script on each (in alphabetic order by host). So say you also set up a 'bbox'
48
- same as 'abox':
49
-
50
- cat >> config/ssh <<DOC
51
- Host abox
52
- Port 2220
53
-
54
- Host bbox
55
- Port 2221
56
-
57
- Host *
58
- User linecook
59
- HostName localhost
60
- DOC
61
- cp packages/abox packages/bbox
62
-
63
- Then:
64
-
65
- linecook run --script script.sh abox bbox
66
- # linecook@abox-ubuntu: hello world!
67
- # linecook@bbox-ubuntu: hello world!
68
-
69
- Note that by default run will execute the 'run' script on all hosts configured
70
- in 'config/ssh'. Leveraging these defaults simplifies the last command:
71
-
72
- mv packages/abox/script.sh packages/abox/run
73
- mv packages/bbox/script.sh packages/bbox/run
74
- linecook run
75
- # linecook@abox-ubuntu: hello world!
76
- # linecook@bbox-ubuntu: hello world!
77
-
78
- If you've never used scp/ssh with a config file then this may seem unfamiliar
79
- but hopefully quite graceful. For scp/ssh masters, these are equivalent for
80
- each host:
81
-
82
- linecook run --remote-dir=/tmp/linecook SCRIPT ARGS...
83
-
84
- scp -q -r -p -F config/ssh packages/host "host:/tmp/linecook"
85
- ssh -q -t -t -F config/ssh host -- "/tmp/linecook/SCRIPT ARGS..."
86
-
87
- Now that you can run scripts, onward to generating scripts!