logical-construct 0.0.1.localtesting → 0.0.1
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/core.rb +0 -1
- data/lib/logical-construct/ground-control/provision.rb +146 -29
- data/lib/logical-construct/ground-control/run-on-target.rb +4 -4
- data/lib/logical-construct/ground-control/setup.rb +5 -14
- data/lib/logical-construct/ground-control/setup/build-files.rb +31 -10
- data/lib/logical-construct/ground-control/setup/copy-files.rb +31 -19
- data/lib/logical-construct/ground-control/setup/create-construct-directory.rb +1 -0
- data/lib/logical-construct/ground-control/setup/install-init.rb +32 -0
- data/lib/logical-construct/resolving-task.rb +108 -5
- data/lib/logical-construct/satisfiable-task.rb +25 -2
- data/lib/logical-construct/target/chef-solo.rb +12 -15
- data/lib/logical-construct/target/platforms/default/chef-config.rb +57 -12
- data/lib/logical-construct/target/platforms/default/resolve-configuration.rb +22 -3
- data/lib/logical-construct/target/provision.rb +10 -0
- data/lib/logical-construct/target/sinatra-resolver.rb +16 -19
- data/lib/logical-construct/testing/resolve-configuration.rb +8 -0
- data/lib/templates/Gemfile.erb +3 -1
- data/lib/templates/chef.rb.erb +6 -1
- data/lib/templates/construct.init.d.erb +15 -0
- data/lib/templates/resolver/index.html.erb +11 -2
- data/lib/templates/resolver/task-file-form.html.erb +6 -0
- data/spec/ground-control/smoke-test.rb +23 -0
- data/spec/resolution.rb +147 -0
- data/spec/target/chef-config.rb +3 -0
- data/spec/target/chef-solo.rb +8 -0
- data/spec/target/smoke-test.rb +45 -0
- metadata +44 -14
- data/lib/logical-construct/ground-control/setup/bundle-setup.rb +0 -36
- data/lib/logical-construct/ground-control/setup/ensure-env.rb +0 -15
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'mattock'
|
2
|
+
require 'logical-construct/ground-control/run-on-target'
|
3
|
+
|
4
|
+
module LogicalConstruct
|
5
|
+
module GroundControl
|
6
|
+
class InstallInit < RunOnTarget
|
7
|
+
required_fields :source_path, :target_path
|
8
|
+
setting :construct_dir
|
9
|
+
setting :service_name, "logical-construct"
|
10
|
+
setting :initd_path, "/etc/init.d"
|
11
|
+
|
12
|
+
def default_configuration(setup)
|
13
|
+
super
|
14
|
+
setup.copy_settings_to(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def resolve_configuration
|
18
|
+
super
|
19
|
+
self.source_path ||= File::join(construct_dir, "construct.init.d")
|
20
|
+
self.target_path ||= File::join(initd_path, service_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def define
|
24
|
+
remote_task(:install_init) do |task|
|
25
|
+
task.command = cmd("install", "-T", source_path, target_path) &
|
26
|
+
["rc-update", "add", service_name, "default"]
|
27
|
+
end
|
28
|
+
bracket_task(:remote_config, :install_init, :remote_setup)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rake/task'
|
2
2
|
require 'mattock/task'
|
3
|
+
require 'logical-construct/satisfiable-task'
|
3
4
|
|
4
5
|
module LogicalConstruct
|
5
6
|
#Ensures that all it's deps are satisfied before proceeding - the action for
|
@@ -17,22 +18,124 @@ module LogicalConstruct
|
|
17
18
|
#So, of note: you'll only get invoked once, ever
|
18
19
|
#You'll only be executed if #needed?
|
19
20
|
#Deps will get invoked (ish) even if not #needed?
|
20
|
-
|
21
|
+
module SatisfiableManager
|
22
|
+
def default_configuration(*configurables)
|
23
|
+
super
|
24
|
+
self.satisfiables = configurables.find_all do |conf|
|
25
|
+
conf.is_a? SatisfiableTask
|
26
|
+
end.map{|sat| sat.rake_task}
|
27
|
+
end
|
28
|
+
|
29
|
+
def define
|
30
|
+
super
|
31
|
+
satisfiables.each do |sat|
|
32
|
+
sat.enhance([rake_task.name])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_satisfiable(task)
|
37
|
+
task = task.rake_task if task.respond_to? :rake_task
|
38
|
+
satisfiables << task
|
39
|
+
task.enhance([rake_task.name])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'yaml'
|
44
|
+
require 'digest'
|
45
|
+
module ResolutionProtocol
|
46
|
+
def digest
|
47
|
+
@digest ||= Digest::SHA2.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def file_checksum(path)
|
51
|
+
generate_checksum(File::read(path))
|
52
|
+
end
|
53
|
+
|
54
|
+
def generate_checksum(data)
|
55
|
+
digest.reset
|
56
|
+
digest << data
|
57
|
+
digest.hexdigest
|
58
|
+
end
|
59
|
+
|
60
|
+
def web_path(task_name)
|
61
|
+
"/" + task_name.gsub(":", "/")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
21
65
|
class ResolvingTask < Rake::Task
|
22
66
|
include Mattock::TaskMixin
|
67
|
+
include SatisfiableManager
|
68
|
+
setting :satisfiables, []
|
69
|
+
|
23
70
|
def needed?
|
24
|
-
|
71
|
+
!unsatisfied.empty?
|
25
72
|
end
|
26
73
|
|
27
|
-
def
|
28
|
-
|
74
|
+
def unsatisfied
|
75
|
+
satisfiables.find_all{|task| task.needed?}
|
29
76
|
end
|
30
77
|
|
31
78
|
def execute(args=nil)
|
32
79
|
super
|
33
80
|
if needed?
|
34
|
-
raise "Task #{name} failed to satisfy: #{
|
81
|
+
raise "Task #{name} failed to satisfy: #{unsatisfied.inspect}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Manifest < SatisfiableTask
|
87
|
+
include SatisfiableManager
|
88
|
+
include ResolutionProtocol
|
89
|
+
|
90
|
+
setting :satisfiables, []
|
91
|
+
nil_field :manifest
|
92
|
+
|
93
|
+
default_taskname "Manifest"
|
94
|
+
|
95
|
+
def invalid_checksum(checksum, path)
|
96
|
+
return false unless File::exists?(path)
|
97
|
+
return true if checksum.nil? or checksum.empty?
|
98
|
+
return checksum != file_checksum(path)
|
99
|
+
end
|
100
|
+
|
101
|
+
def needed?
|
102
|
+
manifest.nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
def fulfill(data)
|
106
|
+
self.manifest = YAML::load(data)
|
107
|
+
satisfiables.each do |sat|
|
108
|
+
path = sat.target_path
|
109
|
+
checksum = manifest[sat.name]
|
110
|
+
if invalid_checksum(checksum, path)
|
111
|
+
File::rename(path, path + ".invalid")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class GenerateManifest < Mattock::Task
|
118
|
+
include ResolutionProtocol
|
119
|
+
setting :hash, {}
|
120
|
+
setting :resolutions
|
121
|
+
setting :receiving_name
|
122
|
+
|
123
|
+
def default_configuration(resolution_host)
|
124
|
+
super
|
125
|
+
self.resolutions = resolution_host.resolutions
|
126
|
+
end
|
127
|
+
|
128
|
+
def data_checksum(path, data)
|
129
|
+
hash[path] = generate_checksum(data)
|
130
|
+
end
|
131
|
+
|
132
|
+
def action
|
133
|
+
resolutions.each_pair do |destination, data|
|
134
|
+
data = data.call if data.respond_to? :call
|
135
|
+
data = data.read if data.respond_to? :read
|
136
|
+
hash[destination] = generate_checksum(data)
|
35
137
|
end
|
138
|
+
resolutions[receiving_name] = YAML::dump(hash)
|
36
139
|
end
|
37
140
|
end
|
38
141
|
end
|
@@ -20,9 +20,24 @@ module LogicalConstruct
|
|
20
20
|
#Swallowed
|
21
21
|
end
|
22
22
|
|
23
|
+
def prefer_file?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
23
27
|
def receive(data)
|
24
28
|
return unless needed?
|
25
29
|
fulfill(data)
|
30
|
+
if data.respond_to? :path
|
31
|
+
fulfill_file(data)
|
32
|
+
elsif data.respond_to? :read
|
33
|
+
fulfill(data.read)
|
34
|
+
else
|
35
|
+
fulfill(data.to_s)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def receive_file(file)
|
40
|
+
fulfill(file.read)
|
26
41
|
end
|
27
42
|
|
28
43
|
def fulfill(string)
|
@@ -39,13 +54,21 @@ module LogicalConstruct
|
|
39
54
|
class SatisfiableFileTask < SatisfiableTask
|
40
55
|
setting :target_path
|
41
56
|
|
57
|
+
def prefer_file?
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
42
61
|
def criteria(task)
|
43
62
|
File::exists?(target_path)
|
44
63
|
end
|
45
64
|
|
46
|
-
def
|
65
|
+
def fulfill_file(file)
|
66
|
+
FileUtils::move(file.path, target_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
def fulfill(data)
|
47
70
|
File::open(target_path, "w") do |file|
|
48
|
-
file.write(
|
71
|
+
file.write(data)
|
49
72
|
end
|
50
73
|
end
|
51
74
|
end
|
@@ -6,35 +6,32 @@ module LogicalConstruct
|
|
6
6
|
|
7
7
|
settings(
|
8
8
|
:chef_solo_bin => "chef-solo",
|
9
|
-
:config_file => "/etc/chef/solo.rb",
|
10
9
|
:daemonize => nil,
|
11
10
|
:user => nil,
|
12
11
|
:group => nil,
|
13
12
|
:node_name => nil
|
14
13
|
)
|
15
14
|
|
15
|
+
setting :config_file
|
16
|
+
|
16
17
|
def default_configuration(chef_config)
|
18
|
+
super
|
17
19
|
self.config_file = chef_config.solo_rb
|
18
20
|
end
|
19
21
|
|
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
22
|
def define
|
31
23
|
in_namespace do
|
32
|
-
|
33
|
-
|
34
|
-
|
24
|
+
Mattock::CommandTask.new(:run) do |task|
|
25
|
+
task.command = Mattock::CommandLine.new(chef_solo_bin) do |cmd|
|
26
|
+
cmd.options << "--config #{config_file}" unless config_file.nil?
|
27
|
+
cmd.options << "--daemonize" if daemonize
|
28
|
+
cmd.options << "--user #{user}" if user
|
29
|
+
cmd.options << "--group #{group}" if group
|
30
|
+
cmd.options << "--node_name #{node_name}" if node_name
|
31
|
+
end
|
35
32
|
end
|
36
33
|
end
|
37
|
-
|
34
|
+
bracket_task(:build_configs, :run, :provision)
|
38
35
|
end
|
39
36
|
end
|
40
37
|
end
|
@@ -1,22 +1,29 @@
|
|
1
1
|
require 'mattock/tasklib'
|
2
2
|
require 'mattock/template-host'
|
3
|
+
require 'mattock/command-line'
|
3
4
|
require 'logical-construct/satisfiable-task'
|
4
5
|
|
5
6
|
module LogicalConstruct
|
6
7
|
module Default
|
8
|
+
#XXX Should get broken into at least 2 smaller tasklibs
|
7
9
|
class ChefConfig < Mattock::Tasklib
|
8
10
|
include Mattock::TemplateHost
|
9
11
|
default_namespace :chef_config
|
10
12
|
|
11
13
|
required_field :construct_dir
|
12
|
-
required_fields :
|
14
|
+
required_fields :cookbook_path, :data_bags_path, :json_attribs,
|
15
|
+
:cookbook_tarball_path, :file_cache_path, :secret_data_tarball_path, :normal_data_tarball_path
|
13
16
|
required_field :valise
|
14
17
|
settings :solo_rb => "/etc/chef/solo.rb",
|
18
|
+
:etc_chef => "/etc/chef",
|
19
|
+
:data_bags_relpath => "data-bags",
|
15
20
|
:cookbook_relpath => "cookbooks",
|
16
21
|
:cookbook_tarball_relpath => "cookbooks.tgz",
|
22
|
+
:secret_data_tarball_relpath => "secret_data.tgz",
|
23
|
+
:normal_data_tarball_relpath => "normal_data.tgz",
|
17
24
|
:json_attribs_relpath => "node.json"
|
18
25
|
|
19
|
-
setting :
|
26
|
+
setting :resolution
|
20
27
|
|
21
28
|
nil_fields :recipe_url, :role_path, :role_relpath
|
22
29
|
|
@@ -24,23 +31,38 @@ module LogicalConstruct
|
|
24
31
|
super
|
25
32
|
self.construct_dir = provision.construct_dir
|
26
33
|
self.valise = provision.valise
|
27
|
-
self.
|
34
|
+
self.resolution = resolution
|
28
35
|
end
|
29
36
|
|
37
|
+
#XXX Hints about decomposing this Tasklib: there are settings for
|
38
|
+
#chef/solo.rb, which are only incidentally related to the tarball
|
39
|
+
#unpacking tasks - which are themselves closely related
|
30
40
|
def resolve_configuration
|
31
41
|
if unset?(file_cache_path)
|
32
42
|
self.file_cache_path = File::expand_path('chef', construct_dir)
|
33
43
|
end
|
34
|
-
|
35
44
|
self.file_cache_path = File::expand_path(file_cache_path)
|
45
|
+
|
36
46
|
if unset?(cookbook_path) and !cookbook_relpath.nil?
|
37
47
|
self.cookbook_path = File::expand_path(cookbook_relpath, file_cache_path)
|
38
48
|
end
|
39
49
|
|
50
|
+
if unset?(data_bags_path) and !data_bags_relpath.nil?
|
51
|
+
self.data_bags_path = File::expand_path(data_bags_relpath, file_cache_path)
|
52
|
+
end
|
53
|
+
|
40
54
|
if unset?(cookbook_tarball_path) and !cookbook_tarball_relpath.nil?
|
41
55
|
self.cookbook_tarball_path = File::expand_path(cookbook_tarball_relpath, file_cache_path)
|
42
56
|
end
|
43
57
|
|
58
|
+
if unset?(secret_data_tarball_path) and !secret_data_tarball_relpath.nil?
|
59
|
+
self.secret_data_tarball_path = File::expand_path(secret_data_tarball_relpath, file_cache_path)
|
60
|
+
end
|
61
|
+
|
62
|
+
if unset?(normal_data_tarball_path) and !normal_data_tarball_relpath.nil?
|
63
|
+
self.normal_data_tarball_path = File::expand_path(normal_data_tarball_relpath, file_cache_path)
|
64
|
+
end
|
65
|
+
|
44
66
|
self.solo_rb = File::expand_path(solo_rb, file_cache_path)
|
45
67
|
|
46
68
|
if unset?(json_attribs) and !json_attribs_relpath.nil?
|
@@ -53,36 +75,59 @@ module LogicalConstruct
|
|
53
75
|
super
|
54
76
|
end
|
55
77
|
|
78
|
+
include Mattock::CommandLineDSL
|
56
79
|
def define
|
57
80
|
in_namespace do
|
81
|
+
directory etc_chef
|
58
82
|
directory file_cache_path
|
59
83
|
|
60
84
|
Mattock::CommandTask.new(:unpack_cookbooks => :cookbook_tarball) do |task|
|
61
|
-
task.command =
|
85
|
+
task.command = cmd("cd", file_cache_path) & cmd("tar", "-xzf", cookbook_tarball_path)
|
86
|
+
end
|
87
|
+
|
88
|
+
Mattock::CommandTask.new(:unpack_secret_data => :secret_data_tarball) do |task|
|
89
|
+
task.command = cmd("cd", file_cache_path) & cmd("tar", "-xzf", secret_data_tarball_path)
|
90
|
+
end
|
91
|
+
|
92
|
+
Mattock::CommandTask.new(:unpack_normal_data => :normal_data_tarball) do |task|
|
93
|
+
task.command = cmd("cd", file_cache_path) & cmd("tar", "-xzf", normal_data_tarball_path)
|
62
94
|
end
|
63
95
|
|
64
|
-
file solo_rb => [
|
96
|
+
file solo_rb => [etc_chef, :json_attribs, :unpack_cookbooks, :unpack_secret_data, :unpack_normal_data] do
|
65
97
|
File::open(solo_rb, "w") do |file|
|
66
98
|
file.write(render("chef.rb.erb"))
|
67
99
|
end
|
68
100
|
end
|
69
101
|
|
70
|
-
SatisfiableFileTask.new(:json_attribs => file_cache_path) do |task|
|
102
|
+
resolution.add_file(SatisfiableFileTask.new(:json_attribs => file_cache_path) do |task|
|
71
103
|
task.target_path = json_attribs
|
72
|
-
end
|
104
|
+
end)
|
73
105
|
|
74
|
-
SatisfiableFileTask.new(:cookbook_tarball => file_cache_path) do |task|
|
106
|
+
resolution.add_file(SatisfiableFileTask.new(:cookbook_tarball => file_cache_path) do |task|
|
75
107
|
task.target_path = cookbook_tarball_path
|
76
|
-
end
|
108
|
+
end)
|
109
|
+
|
110
|
+
resolution.add_file(SatisfiableFileTask.new(:secret_data_tarball => file_cache_path) do |task|
|
111
|
+
task.target_path = secret_data_tarball_path
|
112
|
+
end)
|
113
|
+
|
114
|
+
resolution.add_file(SatisfiableFileTask.new(:normal_data_tarball => file_cache_path) do |task|
|
115
|
+
task.target_path = normal_data_tarball_path
|
116
|
+
end)
|
77
117
|
|
78
118
|
unless role_path.nil?
|
79
119
|
directory role_path
|
80
120
|
file solo_rb => role_path
|
81
121
|
end
|
122
|
+
|
123
|
+
desc "Delete all the chef config files (to re-provision)"
|
124
|
+
task :clobber do
|
125
|
+
cmd("rm", "-rf", file_cache_path)
|
126
|
+
end
|
82
127
|
end
|
83
128
|
|
84
|
-
|
85
|
-
task
|
129
|
+
file solo_rb => resolution[:Manifest]
|
130
|
+
task :build_configs => solo_rb
|
86
131
|
end
|
87
132
|
end
|
88
133
|
end
|
@@ -8,16 +8,35 @@ module LogicalConstruct
|
|
8
8
|
setting :port, 51076
|
9
9
|
setting :valise
|
10
10
|
|
11
|
+
def initialize(*args, &block)
|
12
|
+
@pending_satisfiables = []
|
13
|
+
@resolver = nil
|
14
|
+
@manifest = nil
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
11
18
|
def default_configuration(provision)
|
19
|
+
super
|
12
20
|
self.valise = provision.valise
|
13
21
|
end
|
14
22
|
|
23
|
+
def add_file(file_satisfiable)
|
24
|
+
if @resolver.nil?
|
25
|
+
@pending_satifiables << file_satisfiable
|
26
|
+
else
|
27
|
+
@resolver.add_satisfiable(file_satisfiable)
|
28
|
+
@manifest.add_satisfiable(file_satisfiable)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
15
32
|
def define
|
16
33
|
in_namespace do
|
17
|
-
|
18
|
-
|
34
|
+
@manifest = LogicalConstruct::Manifest.new(*@pending_satisfiables)
|
35
|
+
|
36
|
+
@resolver = LogicalConstruct::SinatraResolver.new(*([@manifest] + @pending_satisfiables)) do |task|
|
37
|
+
copy_settings_to(task)
|
19
38
|
end
|
20
|
-
|
39
|
+
@pending_satisfiables.clear
|
21
40
|
end
|
22
41
|
end
|
23
42
|
end
|