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