logical-construct 0.0.1.localtesting
Sign up to get free protection for your applications and to get access to all the features.
- 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
|