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.
- data/bin/flight-deck +3 -0
- data/doc/DESIGN +48 -0
- data/doc/EC2-baking-notes +70 -0
- data/doc/ExampleStartupRakefile +152 -0
- data/doc/ExampleTargetRakefile +4 -0
- data/doc/TODO +148 -0
- data/doc/Vb-EC2-translation-notes +96 -0
- data/doc/hating-chef +32 -0
- data/lib/logical-construct/archive-tasks.rb +307 -0
- data/lib/logical-construct/ground-control.rb +4 -1
- data/lib/logical-construct/ground-control/build-plan.rb +95 -0
- data/lib/logical-construct/ground-control/core.rb +1 -1
- data/lib/logical-construct/ground-control/generate-manifest.rb +67 -0
- data/lib/logical-construct/ground-control/provision.rb +73 -168
- data/lib/logical-construct/ground-control/run-on-target.rb +1 -1
- data/lib/logical-construct/ground-control/setup.rb +1 -4
- data/lib/logical-construct/ground-control/setup/copy-files.rb +2 -2
- data/lib/logical-construct/ground-control/tools.rb +66 -0
- data/lib/logical-construct/node-client.rb +112 -0
- data/lib/logical-construct/plan.rb +2 -0
- data/lib/logical-construct/plan/core.rb +45 -0
- data/lib/logical-construct/plan/standalone-bundle.rb +80 -0
- data/lib/logical-construct/port-open-check.rb +41 -0
- data/lib/logical-construct/protocol.rb +2 -0
- data/lib/logical-construct/protocol/plan-validation.rb +46 -0
- data/lib/logical-construct/protocol/ssh-tunnel.rb +127 -0
- data/lib/logical-construct/protocol/vocabulary.rb +8 -0
- data/lib/logical-construct/target/Implement.rake +8 -0
- data/lib/logical-construct/target/command-line.rb +90 -0
- data/lib/logical-construct/target/flight-deck.rb +341 -0
- data/lib/logical-construct/target/implementation.rb +33 -0
- data/lib/logical-construct/target/plan-records.rb +317 -0
- data/lib/logical-construct/target/resolution-server.rb +153 -0
- data/lib/logical-construct/target/{unpack-cookbook.rb → unpack-plan.rb} +8 -4
- data/lib/logical-construct/template-file.rb +41 -0
- data/lib/templates/Rakefile.erb +8 -0
- data/spec/ground-control/smoke-test.rb +8 -7
- data/spec/node_resolution.rb +62 -0
- data/spec/target/plan-records.rb +142 -0
- data/spec/target/provisioning.rb +21 -0
- data/spec_help/file-sandbox.rb +12 -6
- data/spec_help/fixtures/Manifest +1 -0
- data/spec_help/fixtures/source/one.tbz +1 -0
- data/spec_help/fixtures/source/three.tbz +1 -0
- data/spec_help/fixtures/source/two.tbz +1 -0
- data/spec_help/spec_helper.rb +5 -7
- metadata +165 -72
- data/lib/logical-construct/ground-control/setup/build-files.rb +0 -93
- data/lib/logical-construct/ground-control/setup/create-construct-directory.rb +0 -22
- data/lib/logical-construct/ground-control/setup/install-init.rb +0 -32
- data/lib/logical-construct/resolving-task.rb +0 -141
- data/lib/logical-construct/satisfiable-task.rb +0 -87
- data/lib/logical-construct/target.rb +0 -4
- data/lib/logical-construct/target/chef-solo.rb +0 -37
- data/lib/logical-construct/target/platforms.rb +0 -51
- data/lib/logical-construct/target/platforms/aws.rb +0 -8
- data/lib/logical-construct/target/platforms/default/chef-config.rb +0 -134
- data/lib/logical-construct/target/platforms/default/resolve-configuration.rb +0 -44
- data/lib/logical-construct/target/platforms/default/volume.rb +0 -11
- data/lib/logical-construct/target/platforms/virtualbox.rb +0 -8
- data/lib/logical-construct/target/platforms/virtualbox/volume.rb +0 -15
- data/lib/logical-construct/target/provision.rb +0 -36
- data/lib/logical-construct/target/sinatra-resolver.rb +0 -99
- data/lib/logical-construct/testing/resolve-configuration.rb +0 -32
- data/lib/logical-construct/testing/resolving-task.rb +0 -15
- data/lib/templates/chef.rb.erb +0 -9
- data/lib/templates/construct.init.d.erb +0 -18
- data/lib/templates/resolver/finished.html.erb +0 -1
- data/lib/templates/resolver/index.html.erb +0 -17
- data/lib/templates/resolver/task-file-form.html.erb +0 -6
- data/lib/templates/resolver/task-form.html.erb +0 -6
- data/spec/resolution.rb +0 -147
- data/spec/target/chef-config.rb +0 -67
- data/spec/target/chef-solo.rb +0 -55
- data/spec/target/platforms.rb +0 -36
- data/spec/target/smoke-test.rb +0 -45
- data/spec_help/ungemmer.rb +0 -36
@@ -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
|