logical-construct 0.0.5 → 0.1.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 (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