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,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!