logical-construct 0.0.1.localtesting
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/lib/logical-construct/ground-control.rb +3 -0
- data/lib/logical-construct/ground-control/core.rb +33 -0
- data/lib/logical-construct/ground-control/provision.rb +70 -0
- data/lib/logical-construct/ground-control/run-on-target.rb +32 -0
- data/lib/logical-construct/ground-control/setup.rb +57 -0
- data/lib/logical-construct/ground-control/setup/build-files.rb +64 -0
- data/lib/logical-construct/ground-control/setup/bundle-setup.rb +36 -0
- data/lib/logical-construct/ground-control/setup/copy-files.rb +66 -0
- data/lib/logical-construct/ground-control/setup/create-construct-directory.rb +21 -0
- data/lib/logical-construct/ground-control/setup/ensure-env.rb +15 -0
- data/lib/logical-construct/resolving-task.rb +38 -0
- data/lib/logical-construct/satisfiable-task.rb +64 -0
- data/lib/logical-construct/target.rb +4 -0
- data/lib/logical-construct/target/chef-solo.rb +40 -0
- data/lib/logical-construct/target/platforms.rb +51 -0
- data/lib/logical-construct/target/platforms/aws.rb +8 -0
- data/lib/logical-construct/target/platforms/default/chef-config.rb +89 -0
- data/lib/logical-construct/target/platforms/default/resolve-configuration.rb +25 -0
- data/lib/logical-construct/target/platforms/default/volume.rb +11 -0
- data/lib/logical-construct/target/platforms/virtualbox.rb +8 -0
- data/lib/logical-construct/target/platforms/virtualbox/volume.rb +15 -0
- data/lib/logical-construct/target/provision.rb +26 -0
- data/lib/logical-construct/target/sinatra-resolver.rb +102 -0
- data/lib/logical-construct/target/unpack-cookbook.rb +40 -0
- data/lib/logical-construct/testing/resolve-configuration.rb +24 -0
- data/lib/logical-construct/testing/resolving-task.rb +15 -0
- data/lib/templates/Gemfile.erb +3 -0
- data/lib/templates/Rakefile.erb +14 -0
- data/lib/templates/chef.rb.erb +4 -0
- data/lib/templates/resolver/finished.html.erb +1 -0
- data/lib/templates/resolver/index.html.erb +8 -0
- data/lib/templates/resolver/task-form.html.erb +6 -0
- data/spec/target/chef-config.rb +64 -0
- data/spec/target/chef-solo.rb +47 -0
- data/spec/target/platforms.rb +36 -0
- data/spec_help/file-sandbox.rb +164 -0
- data/spec_help/gem_test_suite.rb +17 -0
- data/spec_help/mock-resolve.rb +21 -0
- data/spec_help/spec_helper.rb +13 -0
- data/spec_help/ungemmer.rb +36 -0
- metadata +131 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'mattock/task'
|
2
|
+
|
3
|
+
module LogicalConstruct
|
4
|
+
#This task won't kill the run if it fails - the assumption is that there's a
|
5
|
+
#ResolvingTask that depends on this task to make it work.
|
6
|
+
#
|
7
|
+
#Based on my reasoning about Rake (c.f. ResolvingTask):
|
8
|
+
# Each ST should have a configured #needed? that checks it's responsibility
|
9
|
+
# Execute needs to catch errors
|
10
|
+
class SatisfiableTask < Rake::Task
|
11
|
+
include Mattock::TaskMixin
|
12
|
+
|
13
|
+
def execute(args=nil)
|
14
|
+
super
|
15
|
+
if application.options.trace and needed?
|
16
|
+
$stderr.puts "** Unsatisfied: #{name}"
|
17
|
+
end
|
18
|
+
rescue Object => ex
|
19
|
+
warn "#{ex.class}: #{ex.message} while performing #{name}"
|
20
|
+
#Swallowed
|
21
|
+
end
|
22
|
+
|
23
|
+
def receive(data)
|
24
|
+
return unless needed?
|
25
|
+
fulfill(data)
|
26
|
+
end
|
27
|
+
|
28
|
+
def fulfill(string)
|
29
|
+
end
|
30
|
+
|
31
|
+
def needed?
|
32
|
+
return !criteria(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def criteria(me)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class SatisfiableFileTask < SatisfiableTask
|
40
|
+
setting :target_path
|
41
|
+
|
42
|
+
def criteria(task)
|
43
|
+
File::exists?(target_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def fulfill(string)
|
47
|
+
File::open(target_path, "w") do |file|
|
48
|
+
file.write(string)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class SatisfiableEnvTask < SatisfiableTask
|
54
|
+
setting :target_name
|
55
|
+
|
56
|
+
def criteria(task)
|
57
|
+
ENV.has_key?(target_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def fulfill(string)
|
61
|
+
ENV[target_name] = string
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'mattock/tasklib'
|
2
|
+
|
3
|
+
module LogicalConstruct
|
4
|
+
class ChefSolo < Mattock::TaskLib
|
5
|
+
default_namespace :chef_solo
|
6
|
+
|
7
|
+
settings(
|
8
|
+
:chef_solo_bin => "chef-solo",
|
9
|
+
:config_file => "/etc/chef/solo.rb",
|
10
|
+
:daemonize => nil,
|
11
|
+
:user => nil,
|
12
|
+
:group => nil,
|
13
|
+
:node_name => nil
|
14
|
+
)
|
15
|
+
|
16
|
+
def default_configuration(chef_config)
|
17
|
+
self.config_file = chef_config.solo_rb
|
18
|
+
end
|
19
|
+
|
20
|
+
def chef_command
|
21
|
+
Mattock::CommandLine.new(chef_solo_bin) do |cmd|
|
22
|
+
cmd.options << "--config #{config_file}" unless config_file.nil?
|
23
|
+
cmd.options << "--daemonize" if daemonize
|
24
|
+
cmd.options << "--user #{user}" if user
|
25
|
+
cmd.options << "--group #{group}" if group
|
26
|
+
cmd.options << "--node_name #{node_name}" if node_name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def define
|
31
|
+
in_namespace do
|
32
|
+
file config_file
|
33
|
+
task :run => [config_file] do
|
34
|
+
chef_command.run
|
35
|
+
end
|
36
|
+
end
|
37
|
+
task :provision => self[:run]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module LogicalConstruct
|
2
|
+
def self.platforms
|
3
|
+
@platforms ||= {}
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.register_platform(mod)
|
7
|
+
name = mod.name.split('::').last
|
8
|
+
platforms[name] = mod
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.require_platform_files(platform, mod = nil)
|
12
|
+
[
|
13
|
+
['volume', :Volume],
|
14
|
+
['chef-config', :ChefConfig],
|
15
|
+
['resolve-configuration', :ResolveConfiguration],
|
16
|
+
].each do |file, classname|
|
17
|
+
begin
|
18
|
+
require File::join('logical-construct', 'target', 'platforms', platform, file)
|
19
|
+
rescue LoadError
|
20
|
+
raise if mod.nil?
|
21
|
+
klass = Class.new(LogicalConstruct::Default.const_get(classname))
|
22
|
+
mod.const_set(classname, klass)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.Platform(explicit = nil)
|
28
|
+
name = explicit || $DEPLOYMENT_PLATFORM || ENV['LOGCON_DEPLOYMENT_PLATFORM']
|
29
|
+
return platforms.fetch(name)
|
30
|
+
rescue KeyError
|
31
|
+
puts "Cannot find platform specified:"
|
32
|
+
puts " explicit argument: #{explicit.inspect}"
|
33
|
+
puts " $DEPLOYMENT_PLATFORM: #{$DEPLOYMENT_PLATFORM.inspect}"
|
34
|
+
puts " ENV['LOGCON_DEPLOYMENT_PLATFORM']: #{ENV['LOGCON_DEPLOYMENT_PLATFORM'].inspect}"
|
35
|
+
puts " available: #{platforms.keys.inspect}"
|
36
|
+
puts
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
|
40
|
+
module PlatformSpecific
|
41
|
+
def register_platform(name)
|
42
|
+
LogicalConstruct.register_platform(self)
|
43
|
+
LogicalConstruct.require_platform_files(name, self)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
require_platform_files('default')
|
48
|
+
end
|
49
|
+
|
50
|
+
require 'logical-construct/target/platforms/virtualbox'
|
51
|
+
require 'logical-construct/target/platforms/aws'
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'mattock/tasklib'
|
2
|
+
require 'mattock/template-host'
|
3
|
+
require 'logical-construct/satisfiable-task'
|
4
|
+
|
5
|
+
module LogicalConstruct
|
6
|
+
module Default
|
7
|
+
class ChefConfig < Mattock::Tasklib
|
8
|
+
include Mattock::TemplateHost
|
9
|
+
default_namespace :chef_config
|
10
|
+
|
11
|
+
required_field :construct_dir
|
12
|
+
required_fields :file_cache_path, :cookbook_path, :cookbook_tarball_path, :json_attribs
|
13
|
+
required_field :valise
|
14
|
+
settings :solo_rb => "/etc/chef/solo.rb",
|
15
|
+
:cookbook_relpath => "cookbooks",
|
16
|
+
:cookbook_tarball_relpath => "cookbooks.tgz",
|
17
|
+
:json_attribs_relpath => "node.json"
|
18
|
+
|
19
|
+
setting :resolution_task
|
20
|
+
|
21
|
+
nil_fields :recipe_url, :role_path, :role_relpath
|
22
|
+
|
23
|
+
def default_configuration(provision, resolution)
|
24
|
+
super
|
25
|
+
self.construct_dir = provision.construct_dir
|
26
|
+
self.valise = provision.valise
|
27
|
+
self.resolution_task = resolution[:resolve]
|
28
|
+
end
|
29
|
+
|
30
|
+
def resolve_configuration
|
31
|
+
if unset?(file_cache_path)
|
32
|
+
self.file_cache_path = File::expand_path('chef', construct_dir)
|
33
|
+
end
|
34
|
+
|
35
|
+
self.file_cache_path = File::expand_path(file_cache_path)
|
36
|
+
if unset?(cookbook_path) and !cookbook_relpath.nil?
|
37
|
+
self.cookbook_path = File::expand_path(cookbook_relpath, file_cache_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
if unset?(cookbook_tarball_path) and !cookbook_tarball_relpath.nil?
|
41
|
+
self.cookbook_tarball_path = File::expand_path(cookbook_tarball_relpath, file_cache_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
self.solo_rb = File::expand_path(solo_rb, file_cache_path)
|
45
|
+
|
46
|
+
if unset?(json_attribs) and !json_attribs_relpath.nil?
|
47
|
+
self.json_attribs = File::expand_path(json_attribs_relpath, file_cache_path)
|
48
|
+
end
|
49
|
+
|
50
|
+
if role_path.nil? and !role_relpath.nil?
|
51
|
+
self.role_path = File::expand_path(role_relpath, file_cache_path)
|
52
|
+
end
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def define
|
57
|
+
in_namespace do
|
58
|
+
directory file_cache_path
|
59
|
+
|
60
|
+
Mattock::CommandTask.new(:unpack_cookbooks => :cookbook_tarball) do |task|
|
61
|
+
task.command = Mattock::CommandLine.new("tar", "-xzf", cookbook_tarball_path)
|
62
|
+
end
|
63
|
+
|
64
|
+
file solo_rb => [:unpack_cookbooks, :json_attribs, file_cache_path, resolution_task] do
|
65
|
+
File::open(solo_rb, "w") do |file|
|
66
|
+
file.write(render("chef.rb.erb"))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
SatisfiableFileTask.new(:json_attribs => file_cache_path) do |task|
|
71
|
+
task.target_path = json_attribs
|
72
|
+
end
|
73
|
+
|
74
|
+
SatisfiableFileTask.new(:cookbook_tarball => file_cache_path) do |task|
|
75
|
+
task.target_path = cookbook_tarball_path
|
76
|
+
end
|
77
|
+
|
78
|
+
unless role_path.nil?
|
79
|
+
directory role_path
|
80
|
+
file solo_rb => role_path
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
task resolution_task => self[:cookbook_tarball]
|
85
|
+
task resolution_task => self[:json_attribs]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'logical-construct/target/sinatra-resolver'
|
2
|
+
module LogicalConstruct
|
3
|
+
module Default
|
4
|
+
class ResolveConfiguration < Mattock::Tasklib
|
5
|
+
default_namespace 'configuration'
|
6
|
+
|
7
|
+
setting :bind, '0.0.0.0'
|
8
|
+
setting :port, 51076
|
9
|
+
setting :valise
|
10
|
+
|
11
|
+
def default_configuration(provision)
|
12
|
+
self.valise = provision.valise
|
13
|
+
end
|
14
|
+
|
15
|
+
def define
|
16
|
+
in_namespace do
|
17
|
+
resolver = LogicalConstruct::SinatraResolver.new do |task|
|
18
|
+
task.task_name = "resolve"
|
19
|
+
end
|
20
|
+
copy_settings_to(resolver)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'logical-construct/target/platforms/virtualbox'
|
2
|
+
|
3
|
+
module LogicalConstruct
|
4
|
+
module VirtualBox
|
5
|
+
class Volume < Default::Volume
|
6
|
+
def define
|
7
|
+
Mattock::CommandTask.new(self) do |task|
|
8
|
+
task.command = Mattock::CommandLine.new("mount") do |mount|
|
9
|
+
mount.options = [device, mountpoint]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mattock'
|
2
|
+
require 'mattock/template-host'
|
3
|
+
|
4
|
+
module LogicalConstruct
|
5
|
+
class Provision < Mattock::Tasklib
|
6
|
+
include Mattock::ValiseManager
|
7
|
+
extend Mattock::ValiseManager
|
8
|
+
|
9
|
+
settings(
|
10
|
+
:construct_dir => "/var/logical-construct",
|
11
|
+
:attr_source => nil,
|
12
|
+
:config_path => nil
|
13
|
+
)
|
14
|
+
setting :valise
|
15
|
+
setting :search_paths, [rel_dir(__FILE__)]
|
16
|
+
|
17
|
+
def resolve_configuration
|
18
|
+
self.valise = default_valise(search_paths)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def define
|
23
|
+
task_spine(:preflight, :approve_host, :build_configs, :provision)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'logical-construct/resolving-task'
|
2
|
+
require 'mattock/task'
|
3
|
+
require 'mattock/template-host'
|
4
|
+
require 'sinatra'
|
5
|
+
|
6
|
+
module LogicalConstruct
|
7
|
+
class SinatraResolver < ResolvingTask
|
8
|
+
include Mattock::TemplateHost
|
9
|
+
|
10
|
+
def web_path(task)
|
11
|
+
"/" + task.name.gsub(":", "/")
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_collector(resolver, prereqs)
|
15
|
+
klass = Class.new(Sinatra::Application) do
|
16
|
+
class << self
|
17
|
+
attr_accessor :running_server
|
18
|
+
end
|
19
|
+
|
20
|
+
set :show_exceptions => true
|
21
|
+
|
22
|
+
get '/' do
|
23
|
+
resolver.render('resolver/index.html.erb')
|
24
|
+
end
|
25
|
+
|
26
|
+
prereqs.each do |task|
|
27
|
+
path = resolver.web_path(task)
|
28
|
+
|
29
|
+
get path do
|
30
|
+
resolver.render('resolver/task-form.html.erb') do |locals|
|
31
|
+
locals[:task_path] = path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
post path do
|
36
|
+
task.fulfill(request.params["data"])
|
37
|
+
if resolver.needed?
|
38
|
+
redirect to("/")
|
39
|
+
else
|
40
|
+
resolver.render('resolver/finished.html.erb')
|
41
|
+
quit!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
put path do
|
46
|
+
request.body.rewind
|
47
|
+
task.fulfill(request.body.read)
|
48
|
+
if resolver.needed?
|
49
|
+
redirect to("/")
|
50
|
+
else
|
51
|
+
resolver.render('resolver/finished.html.erb')
|
52
|
+
quit!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.rack_handler
|
58
|
+
handler = detect_rack_handler
|
59
|
+
end
|
60
|
+
|
61
|
+
def quit!
|
62
|
+
self.class.quit!(self.class.running_server, "A server")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
return klass
|
66
|
+
end
|
67
|
+
|
68
|
+
setting :bind, "0.0.0.0"
|
69
|
+
setting :port, 51076 #JDL's birthday
|
70
|
+
setting :valise, Mattock::ValiseManager.default_valise("lib")
|
71
|
+
|
72
|
+
def action
|
73
|
+
puts
|
74
|
+
puts "STARTING WEB LISTENER TO RESOLVE PREREQUISITES"
|
75
|
+
puts
|
76
|
+
|
77
|
+
collector = build_collector(self, prerequisite_tasks)
|
78
|
+
handler = collector.rack_handler
|
79
|
+
handler_name = handler.name
|
80
|
+
|
81
|
+
old_handlers = nil
|
82
|
+
handler.run collector, :Host => bind, :Port => port do |server|
|
83
|
+
old_handlers = Hash[
|
84
|
+
[:INT, :TERM].map { |sig| [sig, trap(sig) { collector.quit!(server, handler_name) }] }
|
85
|
+
]
|
86
|
+
collector.running_server = server
|
87
|
+
if server.respond_to? :threaded=
|
88
|
+
server.threaded = settings.threaded
|
89
|
+
end
|
90
|
+
puts "Listening on #{port} with #{handler.name}"
|
91
|
+
end
|
92
|
+
|
93
|
+
old_handlers.each_pair do |signal, handler|
|
94
|
+
trap(signal, handler)
|
95
|
+
end
|
96
|
+
|
97
|
+
rescue Errno::EADDRINUSE => e
|
98
|
+
puts "Port #{port} in use!"
|
99
|
+
exit 1
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|