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,33 @@
1
+ require 'mattock/tasklib'
2
+
3
+ module LogicalConstruct
4
+ module Target
5
+ class Implementation < ::Mattock::Tasklib
6
+ def self.task_list
7
+ [
8
+ :preflight, #Is this node acceptable?
9
+ :settings, #Shared in-memory configuration for the overall setup
10
+ :setup, #write configuration to disk for implementation tools (e.g. Chef, Puppet, apt-get)
11
+ :files, #deliver files from plan to filesystem
12
+ :execute, #run implementation tools
13
+ :configure, #install application configuration files (e.g. /etc/apache/http.conf)
14
+ :complete #All done - depends on everything.
15
+ ]
16
+ end
17
+
18
+ default_namespace :construct
19
+
20
+ def define
21
+ in_namespace do
22
+ task_spine( *self.class.task_list )
23
+
24
+ self.class.task_list.each do |taskname|
25
+ task taskname do
26
+ puts "*** Implementation stage complete: #{taskname}"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,317 @@
1
+ require 'fileutils'
2
+ require 'thread'
3
+ require 'logical-construct/protocol'
4
+
5
+ module LogicalConstruct
6
+ module ResolutionServer
7
+ class PlanRecords
8
+ include Enumerable
9
+
10
+ Record = Struct.new(:name, :filehash)
11
+ Directories = Struct.new(:delivered, :current, :stored)
12
+
13
+ def initialize
14
+ @record_lock = Mutex.new
15
+ @records = []
16
+ @directories = Directories.new(nil, nil, nil)
17
+ end
18
+ attr_reader :directories
19
+
20
+ def reset!
21
+ each do |record|
22
+ record.cancel!
23
+ end
24
+ @records.clear
25
+ clear_files(directories.delivered)
26
+ clear_files(directories.current)
27
+ end
28
+ alias reset reset!
29
+
30
+ def clear_files(directory)
31
+ dirpath = Pathname.new(directory)
32
+ dirpath.mkpath
33
+ dirpath.each_child do |delivered|
34
+ delivered.delete
35
+ end
36
+ end
37
+
38
+ def total_state
39
+ return "no-plans-yet" if @records.empty?
40
+ return "resolved" if @records.all?(&:resolved?)
41
+ return "unresolved"
42
+ end
43
+
44
+ def find(name)
45
+ record = @records.find{|record| record.name == name}
46
+ end
47
+
48
+ def each(&block)
49
+ @records.each(&block)
50
+ end
51
+
52
+ def add(name, hash)
53
+ record = nil
54
+ @record_lock.synchronize do
55
+ if @records.any?{|record| record.name == name}
56
+ raise "Cannot add a second plan requirement for #{name}"
57
+ end
58
+ record = States::Unresolved.new(self, Record.new(name, hash))
59
+ @records << record
60
+ end
61
+ record.resolve
62
+
63
+ return find(name)
64
+ end
65
+
66
+ def change(old_state, new_state_class)
67
+ unless old_state.alive?
68
+ raise "Tried to change from old invalid state: #{old_state}"
69
+ end
70
+ new_state = new_state_class.new(self, old_state.record)
71
+
72
+ @record_lock.synchronize do
73
+ @records.delete(old_state)
74
+ @records << new_state
75
+ old_state.cancel!
76
+ end
77
+ new_state.enter
78
+
79
+ return new_state
80
+ end
81
+ end
82
+
83
+ module States
84
+ class PlanState
85
+ include Protocol::PlanValidation
86
+ include FileTest
87
+
88
+ def initialize(records, record)
89
+ @records, @record = records, record
90
+ end
91
+ attr_reader :record
92
+
93
+ def inspect
94
+ "#<#{self.class.name}:#{"0x%0x"%object_id} #{name||"dead"}:#{filehash}>"
95
+ end
96
+
97
+ def ==(other)
98
+ return true if self.equal?(other)
99
+ return false if !self.alive? or !other.alive?
100
+ return (other.class.equal?(self.class) and
101
+ other.name.equal?(self.name) and
102
+ other.filehash.equal?(self.filehash))
103
+ end
104
+
105
+
106
+ def name
107
+ return nil unless alive?
108
+ @record.name
109
+ end
110
+
111
+ def filehash
112
+ return nil unless alive?
113
+ @record.filehash
114
+ end
115
+
116
+ def enter
117
+ end
118
+
119
+ def cancel!
120
+ @record = nil
121
+ end
122
+
123
+ def alive?
124
+ !@record.nil?
125
+ end
126
+
127
+ def exists?(path)
128
+ super(realpath(path))
129
+ end
130
+
131
+ def delivered_plans_dir
132
+ @records.directories.delivered
133
+ end
134
+
135
+ def current_plans_dir
136
+ @records.directories.current
137
+ end
138
+
139
+ def stored_plans_dir
140
+ @records.directories.stored
141
+ end
142
+
143
+ def resolved?
144
+ false
145
+ end
146
+
147
+ def can_receive?
148
+ false
149
+ end
150
+
151
+ def change(next_state)
152
+ @records.change(self, next_state)
153
+ end
154
+
155
+ def received_path
156
+ @received_path ||= Pathname.new(delivered_plans_dir) + name
157
+ end
158
+
159
+ def storage_path_for(actual_hash)
160
+ @stored_path ||= Pathname.new(stored_plans_dir) + [name, actual_hash].join(".")
161
+ end
162
+
163
+ def current_path
164
+ @current_path ||= Pathname.new(current_plans_dir) + name
165
+ end
166
+
167
+ def resolve
168
+ warn "Cannot resolve plan in current state: #{state}"
169
+ end
170
+
171
+ def receive
172
+ warn "Cannot receive file in current state: #{state}"
173
+ end
174
+
175
+ def state
176
+ self.class.name.sub(/.*::/,'').downcase
177
+ end
178
+
179
+ def join
180
+ return
181
+ end
182
+ end
183
+
184
+ class Unresolved < PlanState
185
+ def can_receive?
186
+ true
187
+ end
188
+
189
+ def store_received_file(actual_hash)
190
+ stored_path = storage_path_for(actual_hash)
191
+ FileUtils.mkdir_p(stored_plans_dir)
192
+ unless exists?(stored_path)
193
+ FileUtils.mv(received_path, stored_path)
194
+ FileUtils.symlink(stored_path, received_path)
195
+ end
196
+ end
197
+
198
+ def enter
199
+ unless alive?
200
+ super
201
+ return false
202
+ end
203
+
204
+ unless exists?(received_path)
205
+ return false
206
+ end
207
+
208
+ actual_hash = file_checksum(received_path)
209
+
210
+ store_received_file(actual_hash)
211
+
212
+ if actual_hash == filehash
213
+ FileUtils.cp(received_path.readlink, current_path.to_s)
214
+ change(Resolved)
215
+ else
216
+ received_path.delete
217
+ return false
218
+ end
219
+ end
220
+ alias receive enter
221
+
222
+ def resolve
223
+ unless alive?
224
+ super
225
+ return false
226
+ end
227
+ change(Resolving)
228
+ end
229
+ end
230
+
231
+ class Resolving < PlanState
232
+ def cancel!
233
+ unless @thread.nil?
234
+ if @working
235
+ @thread.kill
236
+ end
237
+ end
238
+ super
239
+ end
240
+
241
+ def join
242
+ super if @thread.nil?
243
+ @thread.join
244
+ end
245
+
246
+ def enter
247
+ @working = true
248
+ @thread = Thread.new do
249
+ ResolutionMethods.run_all(self)
250
+ @working = false
251
+ change(Unresolved)
252
+ end
253
+ end
254
+ end
255
+
256
+ class Resolved < PlanState
257
+ def resolved?
258
+ true
259
+ end
260
+ end
261
+ end
262
+
263
+ module ResolutionMethods
264
+ class << self
265
+ def resolution_methods
266
+ @methods ||= []
267
+ end
268
+
269
+ def add_method(name, klass)
270
+ resolution_methods << [name, klass]
271
+ end
272
+
273
+ def run_all(state)
274
+ resolution_methods.each do |name, klass|
275
+ klass.new(state).run
276
+ end
277
+ end
278
+ end
279
+
280
+ class ResolutionMethod
281
+ include Protocol::PlanValidation
282
+ include FileTest
283
+
284
+ def self.register(name)
285
+ ResolutionMethods.add_method(name, self)
286
+ end
287
+
288
+ def exists?(path)
289
+ super(realpath(path))
290
+ end
291
+
292
+ def initialize(state)
293
+ @state = state
294
+ end
295
+ attr_reader :state
296
+
297
+ def run
298
+
299
+ end
300
+ end
301
+
302
+ class LocalStorage < ResolutionMethod
303
+ register :local_storage
304
+
305
+ def run
306
+ stored_path = state.storage_path_for(state.record.filehash)
307
+ received_path = state.received_path
308
+
309
+ if exists?(stored_path) and not (exists?(received_path) or symlink?(received_path))
310
+ FileUtils.symlink(stored_path, received_path)
311
+ end
312
+ end
313
+ end
314
+ end
315
+
316
+ end
317
+ end
@@ -0,0 +1,153 @@
1
+ require 'rdf/vocab/skos'
2
+ require 'roadforest/server'
3
+ require 'logical-construct/protocol'
4
+ require 'logical-construct/target/plan-records'
5
+
6
+ module LogicalConstruct
7
+ module ResolutionServer
8
+ class Application < ::RoadForest::Application
9
+ def setup
10
+ router.add :root, [], :read_only, Models::Navigation
11
+ router.add :status, ["status"], :read_only, Models::ServerStatus
12
+ router.add :manifest, ["manifest"], :leaf, Models::ServerManifest
13
+ router.add :unresolved_plans, ["unresolved_plans"], :parent, Models::UnresolvedPlansList
14
+ router.add :full_plans, ["full_plans"], :parent, Models::FullPlansList
15
+ router.add :plan, ["plans",'*'], :leaf, Models::Plan
16
+ router.add :file_content, ["files","*"], :leaf, Models::PlanContent
17
+ end
18
+ end
19
+
20
+ class ServicesHost < ::RoadForest::Application::ServicesHost
21
+ attr_accessor :plan_records
22
+
23
+ def initialize
24
+ @plan_records = PlanRecords.new
25
+ end
26
+
27
+ def destination_dir
28
+ plan_records.directories.delivered
29
+ end
30
+ end
31
+
32
+ module Models
33
+ class Navigation < RoadForest::RDFModel
34
+ def exists?
35
+ true
36
+ end
37
+
38
+ def update(graph)
39
+ return false
40
+ end
41
+
42
+ def nav_entry(graph, name, path)
43
+ graph.add_node([:skos, :hasTopConcept], "#" + name) do |entry|
44
+ entry[:rdf, :type] = [:skos, "Concept"]
45
+ entry[:skos, :prefLabel] = name
46
+ entry[:foaf, "page"] = path
47
+ end
48
+ end
49
+
50
+ def fill_graph(graph)
51
+ graph[:rdf, "type"] = [:skos, "ConceptScheme"]
52
+ nav_entry(graph, "Server Manifest", path_for(:manifest))
53
+ nav_entry(graph, "Unresolved Plans", path_for(:unresolved_plans))
54
+ nav_entry(graph, "All Plans", path_for(:full_plans))
55
+ nav_entry(graph, "Current Status", path_for(:status))
56
+ end
57
+ end
58
+
59
+ class ServerStatus < RoadForest::RDFModel
60
+ def data
61
+ services.plan_records.total_state
62
+ end
63
+
64
+ def fill_graph(graph)
65
+ graph[[:lc, "node-state"]] = data
66
+ end
67
+ end
68
+
69
+ class ServerManifest < RoadForest::RDFModel
70
+ def fill_graph(graph)
71
+ graph.add_list(:lc, "plans") do |list|
72
+ services.plan_records.each do |record|
73
+ list << path_for(:plan, '*' => record.name)
74
+ end
75
+ end
76
+ end
77
+
78
+ def graph_update(graph)
79
+ services.plan_records.reset!
80
+
81
+ graph[:lc, "plans"].as_list.each do |plan|
82
+ services.plan_records.add(plan.first(:lc, "name"), plan.first(:lc, "digest"))
83
+ end
84
+
85
+ new_graph
86
+ end
87
+ end
88
+
89
+ class FullPlansList < RoadForest::RDFModel
90
+ def exists?
91
+ true
92
+ end
93
+
94
+ def update(graph)
95
+ end
96
+
97
+ def add_child(graph)
98
+ services.plan_records.add(graph.first(:lc, "name"), graph.first(:lc, "digest"))
99
+ end
100
+
101
+ def plan_records
102
+ services.plan_records
103
+ end
104
+
105
+ def fill_graph(graph)
106
+ graph.add_list(:lc, "plans") do |list|
107
+ plan_records.each do |record|
108
+ list << path_for(:plan, '*' => record.name)
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ class UnresolvedPlansList < FullPlansList
115
+ def plan_records
116
+ #recheck resolution?
117
+ services.plan_records.find_all{|record| !record.resolved?}
118
+ end
119
+ end
120
+
121
+ class PlanContent < RoadForest::BlobModel
122
+ add_type "text/plain", TypeHandlers::Handler.new
123
+ add_type "application/octet-stream", TypeHandlers::Handler.new
124
+ add_type "application/x-gtar-compressed", TypeHandlers::Handler.new
125
+
126
+ def update(file)
127
+ name = params.remainder
128
+ record = services.plan_records.find(name)
129
+ raise "Unexpected file: #{name}" if record.nil?
130
+ raise "Plan already resolved: #{name}" unless record.can_receive?
131
+
132
+ super(file)
133
+
134
+ record.receive
135
+
136
+ nil
137
+ end
138
+ end
139
+
140
+ class Plan < RoadForest::RDFModel
141
+ def data
142
+ @data = services.plan_records.find(params.remainder)
143
+ end
144
+
145
+ def fill_graph(graph)
146
+ graph[[:lc, "name"]] = data.name
147
+ graph[[:lc, "digest"]] = data.filehash
148
+ graph[[:lc, "contents"]] = path_for(:file_content)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end