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
@@ -1,69 +1,186 @@
|
|
1
1
|
require 'mattock'
|
2
|
-
require '
|
2
|
+
require 'json'
|
3
|
+
require 'logical-construct/resolving-task'
|
3
4
|
|
4
5
|
module LogicalConstruct
|
5
6
|
module GroundControl
|
6
7
|
class Provision < Mattock::Tasklib
|
7
8
|
class WebConfigure < Mattock::Task
|
8
|
-
|
9
|
-
|
9
|
+
include ResolutionProtocol
|
10
|
+
|
11
|
+
setting :target_protocol, "http"
|
12
|
+
setting :target_address, nil
|
13
|
+
setting :target_port, 51076
|
14
|
+
runtime_setting :target_url
|
10
15
|
setting :resolutions, {}
|
16
|
+
runtime_setting :web_resolutions
|
11
17
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
href = link['href']
|
19
|
-
connection.post(href, {"data" => resolve(href)})
|
20
|
-
end
|
18
|
+
def resolve_runtime_configuration
|
19
|
+
super
|
20
|
+
self.target_url ||= "#{target_protocol}://#{target_address}:#{target_port}/"
|
21
|
+
self.web_resolutions = Hash[resolutions.map do |name, value|
|
22
|
+
[web_path(name), value]
|
23
|
+
end]
|
21
24
|
end
|
22
25
|
|
23
26
|
def resolve(path)
|
24
|
-
resolved =
|
27
|
+
resolved = web_resolutions.fetch(path)
|
25
28
|
if resolved.respond_to? :call
|
26
29
|
resolved = resolved.call
|
27
30
|
end
|
28
31
|
return resolved
|
32
|
+
rescue KeyError
|
33
|
+
puts "Can't find a resolution for #{path} in #{web_resolutions.keys.inspect} (ex #{resolutions.keys})"
|
34
|
+
raise
|
35
|
+
end
|
36
|
+
|
37
|
+
def uri(options)
|
38
|
+
uri_class = URI.scheme_list[target_protocol.upcase]
|
39
|
+
uri_hash = {:host => target_address, :port => target_port}
|
40
|
+
return uri_class.build(uri_hash.merge(options)).to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def resolution_needed
|
44
|
+
index = RestClient.get(uri(:path => '/'))
|
45
|
+
body = Nokogiri::HTML(index.body)
|
46
|
+
return body.xpath("//a[@rel='requirement']")
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
#XXX I would like this to become an actual RESTful client at some
|
51
|
+
#point, but seems it would mean building it from scratch
|
52
|
+
def action
|
53
|
+
require 'uri'
|
54
|
+
require 'rest-client'
|
55
|
+
require 'nokogiri'
|
56
|
+
|
57
|
+
until (link = resolution_needed.first).nil?
|
58
|
+
href = link['href']
|
59
|
+
begin
|
60
|
+
response = RestClient.post(uri(:path => href), :data => resolve(href))
|
61
|
+
rescue RestClient::InternalServerError => ex
|
62
|
+
require 'tempfile'
|
63
|
+
file = Tempfile.open('provision-error.html')
|
64
|
+
path = file.path
|
65
|
+
file.close!
|
66
|
+
|
67
|
+
File::open(path, "w") do |file|
|
68
|
+
file.write ex.http_body
|
69
|
+
end
|
70
|
+
puts "Written error response to #{path}"
|
71
|
+
puts "Try: chromium #{path}"
|
72
|
+
fail "Unsuccessful response from server!"
|
73
|
+
end
|
74
|
+
end
|
29
75
|
end
|
30
76
|
end
|
31
77
|
|
32
78
|
default_namespace :provision
|
33
79
|
|
34
80
|
setting :valise
|
35
|
-
setting :
|
36
|
-
setting
|
81
|
+
setting :target_protocol, "http"
|
82
|
+
setting(:target_address, nil).isnt(:copiable)
|
83
|
+
setting :target_port, 51076
|
37
84
|
setting :resolutions, {}
|
38
|
-
setting :marshalling_path
|
39
|
-
|
85
|
+
setting :marshalling_path, "marshall"
|
86
|
+
|
87
|
+
setting(:secret_data, nested {
|
88
|
+
setting :path
|
89
|
+
setting :tarball_path
|
90
|
+
setting :file_list
|
91
|
+
})
|
92
|
+
|
93
|
+
setting(:normal_data, nested {
|
94
|
+
setting :path
|
95
|
+
setting :tarball_path
|
96
|
+
setting :file_list
|
97
|
+
})
|
98
|
+
|
99
|
+
setting(:cookbooks, nested {
|
100
|
+
setting :path
|
101
|
+
setting :tarball_path
|
102
|
+
setting :file_list
|
103
|
+
})
|
104
|
+
|
105
|
+
setting :json_attribs_path
|
106
|
+
setting :roles, {}
|
107
|
+
setting :node_attribs, { "run_list" => [] }
|
108
|
+
setting :json_attribs, ""
|
40
109
|
|
41
110
|
def default_configuration(core)
|
42
111
|
core.copy_settings_to(self)
|
43
112
|
super
|
113
|
+
self.cookbooks.path = "cookbooks"
|
114
|
+
self.secret_data.path = "data-bags/secret"
|
115
|
+
self.normal_data.path = "data-bags"
|
116
|
+
end
|
117
|
+
|
118
|
+
def resolve_configuration
|
119
|
+
super
|
120
|
+
self.json_attribs_path ||= File::join(marshalling_path, "node.json")
|
121
|
+
|
122
|
+
self.cookbooks.file_list ||= Rake::FileList[cookbooks.path + "/**/*"].exclude(%r{/[.]git/}).exclude(%r{[.]sw[.]$})
|
123
|
+
self.secret_data.file_list ||= Rake::FileList[secret_data.path + "/**/*"].exclude(%r{[.]sw[.]$})
|
124
|
+
self.normal_data.file_list ||=
|
125
|
+
Rake::FileList[normal_data.path + "/**/*"].exclude(%r{^#{secret_data.path}}).exclude(%r{[.]sw[.]$})
|
126
|
+
|
127
|
+
self.cookbooks.tarball_path ||= File::join(marshalling_path, "cookbooks.tgz")
|
128
|
+
self.secret_data.tarball_path ||= File::join(marshalling_path, "secret_data_bags.tgz")
|
129
|
+
self.normal_data.tarball_path ||= File::join(marshalling_path, "normal_data_bags.tgz")
|
130
|
+
|
131
|
+
resolutions["chef_config:cookbook_tarball"] ||= proc do
|
132
|
+
File::open(cookbooks.tarball_path, "rb")
|
133
|
+
end
|
134
|
+
|
135
|
+
resolutions["chef_config:secret_data_tarball"] ||= proc do
|
136
|
+
File::open(secret_data.tarball_path, "rb")
|
137
|
+
end
|
138
|
+
|
139
|
+
resolutions["chef_config:normal_data_tarball"] ||= proc do
|
140
|
+
File::open(normal_data.tarball_path, "rb")
|
141
|
+
end
|
44
142
|
end
|
45
143
|
|
144
|
+
include Mattock::CommandLineDSL
|
46
145
|
def define
|
47
146
|
in_namespace do
|
48
|
-
|
49
|
-
|
147
|
+
directory marshalling_path
|
148
|
+
|
149
|
+
task :collect, [:ipaddr, :role] do |task, args|
|
150
|
+
self.target_address = args[:ipaddr]
|
151
|
+
unless args[:role].nil?
|
152
|
+
self.node_attribs["run_list"] = roles[args[:role]]
|
153
|
+
end
|
154
|
+
self.json_attribs = JSON.pretty_generate(node_attribs)
|
155
|
+
resolutions["chef_config:json_attribs"] ||= json_attribs
|
50
156
|
end
|
51
157
|
|
52
|
-
|
53
|
-
|
158
|
+
file secret_data.tarball_path => [marshalling_path] + secret_data.file_list do
|
159
|
+
cmd("tar", "--exclude **/*.sw?", "-czf", secret_data.tarball_path, secret_data.path).must_succeed!
|
54
160
|
end
|
55
161
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
162
|
+
file normal_data.tarball_path => [marshalling_path] + normal_data.file_list do
|
163
|
+
cmd("tar",
|
164
|
+
"--exclude **/*.sw?",
|
165
|
+
"--exclude #{secret_data.path}",
|
166
|
+
"-czf", normal_data.tarball_path, normal_data.path).must_succeed!
|
62
167
|
end
|
63
|
-
end
|
64
168
|
|
65
|
-
|
169
|
+
file cookbooks.tarball_path => [marshalling_path] + cookbooks.file_list do
|
170
|
+
cmd("tar", "--exclude .git", "--exclude **/*.sw?", "-czf", cookbooks.tarball_path, cookbooks.path).must_succeed!
|
171
|
+
end
|
172
|
+
|
173
|
+
manifest = LogicalConstruct::GenerateManifest.new(self, :manifest => [cookbooks.tarball_path, :collect]) do |manifest|
|
174
|
+
manifest.receiving_name = "configuration:Manifest"
|
175
|
+
end
|
176
|
+
|
177
|
+
WebConfigure.new(:web_configure => [:collect, :manifest, cookbooks.tarball_path]) do |task|
|
178
|
+
self.proxy_settings_to(task)
|
179
|
+
task.target_address = proxy_value.target_address
|
180
|
+
end
|
181
|
+
end
|
66
182
|
|
183
|
+
task root_task, [:ipaddr] => self[:web_configure]
|
67
184
|
end
|
68
185
|
end
|
69
186
|
end
|
@@ -5,6 +5,9 @@ module LogicalConstruct
|
|
5
5
|
class RunOnTarget < Mattock::TaskLib
|
6
6
|
include Mattock::CommandLineDSL
|
7
7
|
|
8
|
+
SSH_OPTIONS = [ "ControlMaster=auto", "ControlPersist=3600",
|
9
|
+
"StrictHostKeyChecking=no", "UserKnownHostsFile=/dev/null" ]
|
10
|
+
|
8
11
|
runtime_setting(:remote_server)
|
9
12
|
|
10
13
|
def default_configuration(setup)
|
@@ -16,10 +19,7 @@ module LogicalConstruct
|
|
16
19
|
in_namespace do
|
17
20
|
desc comment unless comment.nil?
|
18
21
|
Mattock::RemoteCommandTask.new(name) do |task|
|
19
|
-
task.ssh_options
|
20
|
-
task.ssh_options << "ControlPersist=3600"
|
21
|
-
task.ssh_options << "StrictHostKeyChecking=no"
|
22
|
-
task.ssh_options << "UserKnownHostsFile=/dev/null"
|
22
|
+
task.ssh_options += SSH_OPTIONS
|
23
23
|
|
24
24
|
task.runtime_definition do |task|
|
25
25
|
copy_settings_to(task)
|
@@ -12,6 +12,7 @@ module LogicalConstruct
|
|
12
12
|
nil_fields :valise
|
13
13
|
|
14
14
|
def default_configuration(core)
|
15
|
+
super
|
15
16
|
core.copy_settings_to(self)
|
16
17
|
end
|
17
18
|
|
@@ -19,17 +20,9 @@ module LogicalConstruct
|
|
19
20
|
in_namespace do
|
20
21
|
task :collect, [:address] do |t, args|
|
21
22
|
remote_server.address = args[:address]
|
22
|
-
p :collect => remote_server
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
task :remote_groundwork => [:local_setup]
|
28
|
-
|
29
|
-
task :remote_config => [:remote_groundwork]
|
30
|
-
|
31
|
-
task :remote_setup => [:remote_config]
|
32
|
-
|
25
|
+
task_spine(:collect, :local_setup, :remote_groundwork, :remote_config, :remote_setup)
|
33
26
|
task :complete => [:local_setup, :remote_setup]
|
34
27
|
end
|
35
28
|
|
@@ -40,18 +33,16 @@ module LogicalConstruct
|
|
40
33
|
def default_subtasks
|
41
34
|
in_namespace do
|
42
35
|
CreateConstructDirectory.new(self)
|
43
|
-
EnsureEnv.new(self)
|
44
|
-
BundleSetup.new(self)
|
45
36
|
build_files = BuildFiles.new(self)
|
46
37
|
CopyFiles.new(self, build_files)
|
38
|
+
InstallInit.new(self)
|
47
39
|
end
|
48
40
|
end
|
49
41
|
end
|
50
42
|
end
|
51
43
|
end
|
52
44
|
|
53
|
-
require 'logical-construct/ground-control/setup/
|
45
|
+
require 'logical-construct/ground-control/setup/build-files'
|
54
46
|
require 'logical-construct/ground-control/setup/create-construct-directory'
|
55
|
-
require 'logical-construct/ground-control/setup/ensure-env'
|
56
47
|
require 'logical-construct/ground-control/setup/copy-files'
|
57
|
-
require 'logical-construct/ground-control/setup/
|
48
|
+
require 'logical-construct/ground-control/setup/install-init'
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'mattock'
|
2
|
+
require 'mattock/bundle-command-task'
|
3
|
+
|
1
4
|
module LogicalConstruct
|
2
5
|
class ConfigBuilder < Mattock::TaskLib
|
3
6
|
include Mattock::TemplateHost
|
@@ -28,36 +31,54 @@ module LogicalConstruct
|
|
28
31
|
file.write render(source_path)
|
29
32
|
end
|
30
33
|
end
|
34
|
+
file target_path => target_dir
|
31
35
|
task :local_setup => target_path
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
35
39
|
class BuildFiles < Mattock::TaskLib
|
40
|
+
include Mattock::CommandLineDSL
|
41
|
+
|
36
42
|
default_namespace :build_files
|
37
43
|
|
38
44
|
setting(:target_dir, "target_configs")
|
39
|
-
|
45
|
+
required_fields :valise, :construct_dir
|
40
46
|
|
41
47
|
def default_configuration(parent)
|
42
48
|
super
|
43
|
-
|
49
|
+
parent.copy_settings_to(self)
|
44
50
|
end
|
45
51
|
|
46
|
-
attr_reader :built_files
|
47
|
-
|
48
52
|
def define
|
49
|
-
|
53
|
+
rakefile = nil
|
50
54
|
in_namespace do
|
51
55
|
directory target_dir
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
gemfile = ConfigBuilder.new(self) do |task|
|
58
|
+
task.base_name = "Gemfile"
|
59
|
+
end
|
60
|
+
|
61
|
+
Mattock::BundleCommandTask.new(:standalone => gemfile.target_path) do |bundle_build|
|
62
|
+
bundle_build.command = (
|
63
|
+
cmd("cd", target_dir) &
|
64
|
+
cmd("pwd") &
|
65
|
+
cmd("bundle", "install"){|bundler|
|
66
|
+
bundler.options << "--standalone"
|
67
|
+
bundler.options << "--binstubs=bin"
|
68
|
+
})
|
69
|
+
end
|
70
|
+
|
71
|
+
rakefile = ConfigBuilder.new(self) do |task|
|
72
|
+
task.base_name = "Rakefile"
|
73
|
+
end
|
74
|
+
|
75
|
+
initd = ConfigBuilder.new(self) do |task|
|
76
|
+
task.base_name = "construct.init.d"
|
77
|
+
task.extra[:construct_dir] = construct_dir
|
57
78
|
end
|
58
79
|
end
|
59
80
|
desc "Template files to be created on the remote server"
|
60
|
-
task root_task =>
|
81
|
+
task root_task => [rakefile.target_path] + in_namespace(:standalone)
|
61
82
|
task :local_setup => root_task
|
62
83
|
end
|
63
84
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
require 'mattock/command-task'
|
2
2
|
|
3
3
|
module LogicalConstruct
|
4
|
-
class
|
4
|
+
class RemoteCopyFile < Mattock::CommandTask
|
5
5
|
nil_fields :destination_address
|
6
6
|
nil_fields :source_dir, :destination_dir, :basename
|
7
7
|
required_fields :source_path, :destination_path
|
8
8
|
runtime_required_field :remote_server
|
9
9
|
runtime_required_field :command
|
10
|
+
setting :exclude, []
|
10
11
|
|
11
12
|
def default_configuration(copy)
|
13
|
+
super
|
12
14
|
self.remote_server = copy.proxy_value.remote_server
|
13
15
|
end
|
14
16
|
|
@@ -18,14 +20,27 @@ module LogicalConstruct
|
|
18
20
|
self.destination_path ||= File::join(destination_dir, basename)
|
19
21
|
end
|
20
22
|
|
23
|
+
def secure_shell_command
|
24
|
+
escaped_command("ssh") do |ssh|
|
25
|
+
ssh.options += RunOnTarget::SSH_OPTIONS.map{|opt| "-o #{opt}"}
|
26
|
+
ssh.options << "-l #{remote_server.user}" unless remote_server.user.nil?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
21
30
|
def resolve_runtime_configuration
|
22
31
|
self.destination_address ||= [remote_server.address, destination_path].join(":")
|
23
|
-
|
24
|
-
|
32
|
+
|
33
|
+
self.command = cmd("rsync") do |rsync|
|
34
|
+
rsync.options << "-a"
|
35
|
+
rsync.options << "--copy-unsafe-links"
|
36
|
+
rsync.options << "-e #{secure_shell_command}"
|
37
|
+
exclude.each do |pattern|
|
38
|
+
rsync.options << "--exclude #{pattern}"
|
39
|
+
end
|
40
|
+
rsync.options << source_path
|
41
|
+
rsync.options << destination_address
|
25
42
|
end
|
26
|
-
|
27
|
-
"-o ControlMaster=auto",
|
28
|
-
source_path, destination_address)
|
43
|
+
super
|
29
44
|
end
|
30
45
|
end
|
31
46
|
|
@@ -34,8 +49,6 @@ module LogicalConstruct
|
|
34
49
|
|
35
50
|
required_fields :files_dir, :remote_server, :construct_dir
|
36
51
|
|
37
|
-
setting :files, ["Rakefile", "Gemfile"]
|
38
|
-
|
39
52
|
def default_configuration(setup, build_files)
|
40
53
|
super()
|
41
54
|
self.files_dir = build_files.target_dir
|
@@ -44,22 +57,21 @@ module LogicalConstruct
|
|
44
57
|
end
|
45
58
|
|
46
59
|
def define
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
task.
|
51
|
-
task.remote_server = remote_server
|
52
|
-
end
|
53
|
-
task.source_dir = files_dir
|
54
|
-
task.destination_dir = construct_dir
|
55
|
-
task.basename = file
|
60
|
+
in_namespace do
|
61
|
+
RemoteCopyFile.new(self, :construct_dir) do |task|
|
62
|
+
task.runtime_definition do
|
63
|
+
task.remote_server = remote_server
|
56
64
|
end
|
65
|
+
task.exclude << "*.so"
|
66
|
+
task.exclude << "*.dynlib"
|
67
|
+
task.source_path = File::join(files_dir, "*")
|
68
|
+
task.destination_path = construct_dir
|
57
69
|
end
|
58
|
-
bracket_task(:local_setup, file, :remote_config)
|
59
70
|
end
|
71
|
+
bracket_task(:remote_groundwork, :construct_dir, :remote_config)
|
60
72
|
|
61
73
|
desc "Copy locally generated files to the remote server"
|
62
|
-
task root_task => in_namespace(
|
74
|
+
task root_task => in_namespace(:construct_dir)
|
63
75
|
task :remote_config => root_task
|
64
76
|
end
|
65
77
|
end
|