logical-construct 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/bin/flight-deck +3 -0
  2. data/doc/DESIGN +48 -0
  3. data/doc/EC2-baking-notes +70 -0
  4. data/doc/ExampleStartupRakefile +152 -0
  5. data/doc/ExampleTargetRakefile +4 -0
  6. data/doc/TODO +148 -0
  7. data/doc/Vb-EC2-translation-notes +96 -0
  8. data/doc/hating-chef +32 -0
  9. data/lib/logical-construct/archive-tasks.rb +307 -0
  10. data/lib/logical-construct/ground-control.rb +4 -1
  11. data/lib/logical-construct/ground-control/build-plan.rb +95 -0
  12. data/lib/logical-construct/ground-control/core.rb +1 -1
  13. data/lib/logical-construct/ground-control/generate-manifest.rb +67 -0
  14. data/lib/logical-construct/ground-control/provision.rb +73 -168
  15. data/lib/logical-construct/ground-control/run-on-target.rb +1 -1
  16. data/lib/logical-construct/ground-control/setup.rb +1 -4
  17. data/lib/logical-construct/ground-control/setup/copy-files.rb +2 -2
  18. data/lib/logical-construct/ground-control/tools.rb +66 -0
  19. data/lib/logical-construct/node-client.rb +112 -0
  20. data/lib/logical-construct/plan.rb +2 -0
  21. data/lib/logical-construct/plan/core.rb +45 -0
  22. data/lib/logical-construct/plan/standalone-bundle.rb +80 -0
  23. data/lib/logical-construct/port-open-check.rb +41 -0
  24. data/lib/logical-construct/protocol.rb +2 -0
  25. data/lib/logical-construct/protocol/plan-validation.rb +46 -0
  26. data/lib/logical-construct/protocol/ssh-tunnel.rb +127 -0
  27. data/lib/logical-construct/protocol/vocabulary.rb +8 -0
  28. data/lib/logical-construct/target/Implement.rake +8 -0
  29. data/lib/logical-construct/target/command-line.rb +90 -0
  30. data/lib/logical-construct/target/flight-deck.rb +341 -0
  31. data/lib/logical-construct/target/implementation.rb +33 -0
  32. data/lib/logical-construct/target/plan-records.rb +317 -0
  33. data/lib/logical-construct/target/resolution-server.rb +153 -0
  34. data/lib/logical-construct/target/{unpack-cookbook.rb → unpack-plan.rb} +8 -4
  35. data/lib/logical-construct/template-file.rb +41 -0
  36. data/lib/templates/Rakefile.erb +8 -0
  37. data/spec/ground-control/smoke-test.rb +8 -7
  38. data/spec/node_resolution.rb +62 -0
  39. data/spec/target/plan-records.rb +142 -0
  40. data/spec/target/provisioning.rb +21 -0
  41. data/spec_help/file-sandbox.rb +12 -6
  42. data/spec_help/fixtures/Manifest +1 -0
  43. data/spec_help/fixtures/source/one.tbz +1 -0
  44. data/spec_help/fixtures/source/three.tbz +1 -0
  45. data/spec_help/fixtures/source/two.tbz +1 -0
  46. data/spec_help/spec_helper.rb +5 -7
  47. metadata +165 -72
  48. data/lib/logical-construct/ground-control/setup/build-files.rb +0 -93
  49. data/lib/logical-construct/ground-control/setup/create-construct-directory.rb +0 -22
  50. data/lib/logical-construct/ground-control/setup/install-init.rb +0 -32
  51. data/lib/logical-construct/resolving-task.rb +0 -141
  52. data/lib/logical-construct/satisfiable-task.rb +0 -87
  53. data/lib/logical-construct/target.rb +0 -4
  54. data/lib/logical-construct/target/chef-solo.rb +0 -37
  55. data/lib/logical-construct/target/platforms.rb +0 -51
  56. data/lib/logical-construct/target/platforms/aws.rb +0 -8
  57. data/lib/logical-construct/target/platforms/default/chef-config.rb +0 -134
  58. data/lib/logical-construct/target/platforms/default/resolve-configuration.rb +0 -44
  59. data/lib/logical-construct/target/platforms/default/volume.rb +0 -11
  60. data/lib/logical-construct/target/platforms/virtualbox.rb +0 -8
  61. data/lib/logical-construct/target/platforms/virtualbox/volume.rb +0 -15
  62. data/lib/logical-construct/target/provision.rb +0 -36
  63. data/lib/logical-construct/target/sinatra-resolver.rb +0 -99
  64. data/lib/logical-construct/testing/resolve-configuration.rb +0 -32
  65. data/lib/logical-construct/testing/resolving-task.rb +0 -15
  66. data/lib/templates/chef.rb.erb +0 -9
  67. data/lib/templates/construct.init.d.erb +0 -18
  68. data/lib/templates/resolver/finished.html.erb +0 -1
  69. data/lib/templates/resolver/index.html.erb +0 -17
  70. data/lib/templates/resolver/task-file-form.html.erb +0 -6
  71. data/lib/templates/resolver/task-form.html.erb +0 -6
  72. data/spec/resolution.rb +0 -147
  73. data/spec/target/chef-config.rb +0 -67
  74. data/spec/target/chef-solo.rb +0 -55
  75. data/spec/target/platforms.rb +0 -36
  76. data/spec/target/smoke-test.rb +0 -45
  77. data/spec_help/ungemmer.rb +0 -36
@@ -0,0 +1,8 @@
1
+ require 'rdf'
2
+
3
+ module LogicalConstruct
4
+ module Protocol
5
+ class LC < ::RDF::Vocabulary("http://lrdesign.com/vocabularies/logical-construct#")
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ require 'logical-construct/target/implementation.rb'
2
+
3
+ include LogicalConstruct::Target
4
+
5
+ impl = Implementation.new do
6
+ end
7
+
8
+ task :default => impl[:complete]
@@ -0,0 +1,90 @@
1
+ require 'logical-construct/target/flight-deck'
2
+
3
+ module LogicalConstruct
4
+ module Target
5
+ class CommandLine < ::Rake::Application
6
+ def initialize(argv)
7
+ @argv = argv
8
+ @manifest_path = nil
9
+ @flight_deck_tasks = []
10
+ @plan_options = []
11
+ super()
12
+ end
13
+
14
+ def go
15
+ init(File::basename($0))
16
+ Rake.application = self
17
+ FlightDeck.new do |control|
18
+ control.namespace_name = nil
19
+ control.top_level_tasks = @implement_tasks
20
+ control.manifest_path = @manifest_path
21
+ control.plan_options = @plan_options
22
+ end
23
+ top_level
24
+ end
25
+
26
+ def collect_tasks
27
+ super
28
+ @implement_tasks = @top_level_tasks
29
+ @top_level_tasks = @flight_deck_tasks
30
+ if @top_level_tasks.empty?
31
+ @top_level_tasks = ['implement']
32
+ end
33
+ end
34
+
35
+ def mutate_options(patterns, options, &block)
36
+ patterns.each do |switch|
37
+ option = options.find{|list| /^#{switch}/ =~ list[0]}
38
+ next if option.nil?
39
+ previous_handler = option.pop
40
+ option.push lambda{|value| yield(switch, previous_handler, value)}
41
+ lambda{|value| @plan_options << "#{switch}=#{value}"}
42
+ end
43
+ options
44
+ end
45
+
46
+ def collect_plan_option(name, value)
47
+ if value == true or value.nil? or value.empty?
48
+ @plan_options << name
49
+ else
50
+ @plan_options << "#{name}=#{value}"
51
+ end
52
+ end
53
+
54
+ def forward_to_plans(options)
55
+ patterns = %w{--prereqs}
56
+ new_options = []
57
+ patterns.each do |switch|
58
+ option = options.find{|list| /^#{switch}/ =~ list[0]}
59
+ next if option.nil?
60
+ new_options << [option[0].sub(/^--/, "--flight-deck-"), option[-2], option[-1]]
61
+ end
62
+
63
+ new_options + mutate_options(patterns, options) do |switch, previous_handler, value|
64
+ collect_plan_option(switch, value)
65
+ end
66
+ end
67
+
68
+ def mirror_to_plans(options)
69
+ mutate_options(%w{--trace}, options) do |switch, previous_handler, value|
70
+ previous_handler[value]
71
+ collect_plan_option(switch, value)
72
+ end
73
+ end
74
+
75
+ def add_options(options)
76
+ return options + [
77
+ [ "--control-task", "-C TASK",
78
+ "Alter the tasks run by flight-deck itself (rather than task run by the plans)",
79
+ lambda{|value| @flight_deck_tasks << value} ],
80
+ [ "--manifest-file", "-M MANIFEST",
81
+ "Supply a starting server manifest (gotten from e.g. AWS userdata)",
82
+ lambda{|value| @manifest_path = value } ] ]
83
+ end
84
+
85
+ def standard_rake_options
86
+ sort_options( add_options( forward_to_plans( mirror_to_plans( super))))
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,341 @@
1
+ require 'mattock'
2
+ require 'logical-construct/archive-tasks'
3
+ require 'logical-construct/node-client'
4
+ require 'logical-construct/port-open-check'
5
+
6
+ module LogicalConstruct
7
+ module Target
8
+ class FlightDeck < ::Mattock::Tasklib
9
+ include Mattock::CommandLineDSL
10
+ class ResolutionServerTask < ::Mattock::Rake::Task
11
+ path(:log_file, "resolution-server.log")
12
+ dir(:plans, "plans",
13
+ dir(:delivered, "delivered"),
14
+ dir(:current, "current"),
15
+ dir(:stored, "stored"))
16
+
17
+ setting :port, 30712
18
+
19
+
20
+ def resolve_configuration
21
+ resolve_paths
22
+ super
23
+ end
24
+
25
+ def needed?
26
+ if ::Rake.application.options.trace
27
+ ::Rake.application.trace("Checking to see if a service is running at #{port}")
28
+ if TCPPortOpenCheck.new(port).open?
29
+ ::Rake.application.trace(" ...yes - task not needed")
30
+ return false
31
+ else
32
+ ::Rake.application.trace(" ...no - task needed")
33
+ return true
34
+ end
35
+ else
36
+ !TCPPortOpenCheck.new(port).open?
37
+ end
38
+ end
39
+
40
+ def action(args)
41
+ require 'roadforest/server'
42
+ require 'logical-construct/target/resolution-server'
43
+ require 'webrick/accesslog'
44
+ services = ResolutionServer::ServicesHost.new
45
+
46
+ services.plan_records.directories.delivered = delivered.absolute_path
47
+ services.plan_records.directories.current = current.absolute_path
48
+ services.plan_records.directories.stored = stored.absolute_path
49
+
50
+ logfile = File::open(log_file.absolute_path, "a")
51
+ logfile.sync = true
52
+
53
+ application = ResolutionServer::Application.new("http://localhost:#{port}", services)
54
+ application.configure do |config|
55
+ config.port = port
56
+ config.adapter_options = {
57
+ :Logger => WEBrick::Log.new(logfile),
58
+ :AccessLog => [
59
+ [logfile, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
60
+ [logfile, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
61
+ ]
62
+ }
63
+ end
64
+ application.run
65
+ end
66
+ end
67
+
68
+ class DaemonizedResolutionServerTask < ResolutionServerTask
69
+ def daemonize
70
+ fail "Can't daemonize without a block" unless block_given?
71
+
72
+ pid = fork do
73
+ begin
74
+ Process::setsid
75
+ ObjectSpace::each_object(IO) do |io|
76
+ begin
77
+ if (0..2).cover?(io.fileno)
78
+ begin
79
+ io.reopen("/dev/null")
80
+ rescue IOError => ioe
81
+ raise "#{ioe.inspect} while trying to reopen #{io}"
82
+ end
83
+ else
84
+ io.close
85
+ end
86
+ rescue IOError
87
+ #io errors when closing or fileno aren't a problem
88
+ end
89
+ end
90
+
91
+ yield
92
+ rescue Object => ex
93
+ File::open("daemonize-crash-log", "w") do |log|
94
+ log.write("#{([ex.class.name, ex.message, ex.to_s] + ex.backtrace).join("\n")}")
95
+ end
96
+ end
97
+
98
+ Kernel.exit!
99
+ end
100
+ Process.detach(pid)
101
+ end
102
+
103
+ def action(args)
104
+ daemonize{ super }
105
+ end
106
+ end
107
+
108
+ class UniqueProcessTask < ::Mattock::Rake::Task
109
+ dir(:rundir, "run",
110
+ dir(:lockdir, "lock"),
111
+ path(:lockfile))
112
+
113
+ def resolve_configuration
114
+ lockfile.relative_path ||= [task_name, "lock"].join(".")
115
+ resolve_paths
116
+ super
117
+ end
118
+
119
+ def action(args)
120
+ require 'pathname'
121
+ Pathname.new(lockdir.absolute_path).mkpath
122
+ File::open(lockfile.absolute_path, File::CREAT|File::EXCL|File::WRONLY, 0600) do |file|
123
+ file.write(Process.pid)
124
+ end
125
+ at_exit{ File::unlink(lockfile.absolute_path) }
126
+ rescue Errno::EEXIST
127
+ pid = File::open(lockfile.absolute_path, File::RDONLY) do |file|
128
+ file.read.to_i
129
+ end
130
+ begin
131
+ Process.kill(0, pid)
132
+ puts "Another process (pid: #{pid}) already owns #{lockfile.absolute_path}"
133
+ puts "Exiting"
134
+ exit(1)
135
+ rescue Errno::ESRCH, RangeError
136
+ #process doesn't exist
137
+ File::unlink(lockfile.absolute_path)
138
+ end
139
+ retry #if we get here it's because the previous process is dead and we've cleaned up
140
+ end
141
+ end
142
+
143
+ dir(:plans, "plans",
144
+ dir(:delivered, "delivered"),
145
+ dir(:current, "current"),
146
+ dir(:stored, "stored"),
147
+ dir(:plan_dirs, "installs"),
148
+ path(:plans_live, "live"),
149
+ path(:plans_temp, "temp"),
150
+ )
151
+
152
+ setting :local_server, "http://localhost:30712/"
153
+ setting :top_level_tasks, []
154
+ setting :plan_options, []
155
+ setting :manifest_path, nil
156
+
157
+ default_namespace :flight_deck
158
+
159
+ def resolve_configuration
160
+ self.absolute_path = Pathname.pwd.join($0).dirname.dirname.to_s
161
+ resolve_paths
162
+ super
163
+ end
164
+
165
+ def define
166
+ in_namespace do
167
+ UniqueProcessTask.define_task(:unique_process)
168
+
169
+ namespace :server do
170
+ desc "Run resolution server synchronously"
171
+ ResolutionServerTask.define_task(:run) do |run|
172
+ copy_settings_to(run)
173
+ end
174
+
175
+ DaemonizedResolutionServerTask.define_task(:daemonized) do |daemon|
176
+ copy_settings_to(daemon)
177
+ end
178
+
179
+ task :report_started => :daemonized do
180
+ puts "** Started resolution server"
181
+ end
182
+
183
+ task :deliver_manifest => :report_started do
184
+ unless manifest_path.nil?
185
+ puts "*** the --manifest_path option is a placeholder for the real feature ***"
186
+ end
187
+ end
188
+ end
189
+
190
+ namespace :plans do
191
+ task :wait_for_server => "server:deliver_manifest" do
192
+ client = NodeClient.new
193
+ client.node_url = "http://localhost:30712/"
194
+
195
+ puts "** Waiting for ground control resolution"
196
+ current_state = nil
197
+ until (state = client.state) == "resolved"
198
+ if current_state != state
199
+ puts "** Current state: #{state}"
200
+ end
201
+ current_state = state
202
+ sleep 1
203
+ end
204
+ puts "** Server ready - proceeding..."
205
+ end
206
+
207
+ desc "Check that current plans are received and unpack them for implementation"
208
+ task :unpack => 'unpack:finished'
209
+
210
+ namespace :unpack do
211
+ desc "Remove old versions of the the live plans directory"
212
+ task :clean_up do
213
+ retain = []
214
+ [plans_live.pathname, plans_temp.pathname].each do |path|
215
+ begin
216
+ retain << path.realpath
217
+ rescue Errno::ENOENT
218
+ end
219
+ end
220
+
221
+ if(plan_dirs.pathname.exist?)
222
+ plan_dirs.pathname.children(true).each do |dir|
223
+ next if retain.include?(dir.realpath)
224
+ cmd("rm", "-rf", dir.to_s).run
225
+ end
226
+ end
227
+ end
228
+
229
+ directory plan_dirs.absolute_path
230
+
231
+ file plans_temp.absolute_path => plan_dirs.absolute_path do
232
+ require 'tmpdir'
233
+
234
+ dir = Dir::mktmpdir(nil, plan_dirs.absolute_path)
235
+ cmd("ln") do |link|
236
+ link.options << "-sfnT"
237
+ link.options << dir
238
+ link.options << plans_temp.absolute_path
239
+ end.must_succeed!
240
+ end
241
+
242
+ task :make_temp_live do
243
+ cmd("mv") do |mv_t|
244
+ mv_t.options << "-T"
245
+ mv_t.options << plans_temp.absolute_path
246
+ mv_t.options << plans_live.absolute_path
247
+ end.must_succeed!
248
+ end
249
+
250
+ #This task creates other tasks here and now for unpacking the
251
+ #current plans - we don't know until now which plan archives
252
+ #we're installing
253
+ #We could sort of use a rule, but we don't know anything about
254
+ #the files in the archives.
255
+ task :all => :wait_for_server do
256
+ complete_task = task :complete
257
+
258
+ Pathname.new(current.absolute_path).each_child(true) do |archive_file|
259
+ name = archive_file.basename.sub(/[.].*\Z/,'').to_s
260
+ temp_plan = plans_temp.pathname.join(name)
261
+
262
+ namespace(name) do
263
+ unpack_task = UnpackTarballTask.define_task(:unpack => plans_temp.absolute_path) do |unpack|
264
+ unpack.unpacked_parent.absolute_path = plans_temp.absolute_path
265
+ unpack.archive_parent.absolute_path = current.absolute_path
266
+ unpack.basename = name
267
+ end
268
+ unpack_task.create_target_dependencies
269
+
270
+ plan_file = unpack_task.unpacked_dir.pathname.join("plan.rake")
271
+ file plan_file
272
+
273
+ task :install => unpack_task.target_files do
274
+ source_path = unpack_task.unpacked_dir.pathname
275
+ if (install_rake = source_path.join("plan.rake")).exist?
276
+ ruby_libs = [source_path.join("lib"), ENV["RUBYLIB"]]
277
+ (cmd("cd", source_path) &
278
+ cmd("rake", "--rakefile", install_rake, "#{name}:install").set_env("RUBYLIB", ruby_libs.join(":"))
279
+ ).must_succeed!
280
+ end
281
+ end
282
+ end
283
+
284
+ task :complete => "#{name}:install"
285
+ end
286
+
287
+ complete_task.invoke
288
+ end
289
+
290
+ task :tidy_up => [:make_temp_live, :clean_up]
291
+ task :finished => [:wait_for_server, :tidy_up, :make_temp_live]
292
+ task :make_temp_live => :all
293
+ end
294
+
295
+ task :implement => :unpack do
296
+ libpath = []
297
+ gempath = []
298
+ rake_modules = []
299
+
300
+
301
+ plans_live.pathname.each_child(false) do |plan_path|
302
+ if (libdir = plans_live.pathname + plan_path + "lib").directory?
303
+ libpath << libdir
304
+ end
305
+
306
+ if (plan_module = plans_live.pathname + plan_path + "plan.rake").file?
307
+ rake_modules << plan_path + "plan"
308
+ end
309
+ end
310
+
311
+ rake_command = cmd("rake") do |rake|
312
+ rake.options += ["--libdir", plans_live.pathname]
313
+ rake_modules.each do |mod|
314
+ rake.options += ["--require", mod]
315
+ end
316
+
317
+ rake.options << "--rakefile " + File::expand_path("../Implement.rake", __FILE__)
318
+
319
+ #rake.options << "--trace"
320
+ rake.options += plan_options
321
+
322
+ rake.options += top_level_tasks
323
+ rake.command_environment["RUBYLIB"] = libpath.join(":")
324
+ end
325
+
326
+ puts "*** Beginning Implementation"
327
+ rake_command.replace_us
328
+ end
329
+
330
+ task :wait_for_server => :unique_process
331
+ file plans_temp.absolute_path => :unique_process
332
+ task :implement => :unique_process
333
+ end
334
+
335
+ task :implement => 'plans:implement'
336
+ task :start_server => 'server:daemonized'
337
+ end
338
+ end
339
+ end
340
+ end
341
+ end