logical-construct 0.0.5 → 0.1.0

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.
Files changed (77) hide show
  1. data/bin/flight-deck +3 -0
  2. data/doc/DESIGN +48 -0
  3. data/doc/EC2-baking-notes +70 -0
  4. data/doc/ExampleStartupRakefile +152 -0
  5. data/doc/ExampleTargetRakefile +4 -0
  6. data/doc/TODO +148 -0
  7. data/doc/Vb-EC2-translation-notes +96 -0
  8. data/doc/hating-chef +32 -0
  9. data/lib/logical-construct/archive-tasks.rb +307 -0
  10. data/lib/logical-construct/ground-control.rb +4 -1
  11. data/lib/logical-construct/ground-control/build-plan.rb +95 -0
  12. data/lib/logical-construct/ground-control/core.rb +1 -1
  13. data/lib/logical-construct/ground-control/generate-manifest.rb +67 -0
  14. data/lib/logical-construct/ground-control/provision.rb +73 -168
  15. data/lib/logical-construct/ground-control/run-on-target.rb +1 -1
  16. data/lib/logical-construct/ground-control/setup.rb +1 -4
  17. data/lib/logical-construct/ground-control/setup/copy-files.rb +2 -2
  18. data/lib/logical-construct/ground-control/tools.rb +66 -0
  19. data/lib/logical-construct/node-client.rb +112 -0
  20. data/lib/logical-construct/plan.rb +2 -0
  21. data/lib/logical-construct/plan/core.rb +45 -0
  22. data/lib/logical-construct/plan/standalone-bundle.rb +80 -0
  23. data/lib/logical-construct/port-open-check.rb +41 -0
  24. data/lib/logical-construct/protocol.rb +2 -0
  25. data/lib/logical-construct/protocol/plan-validation.rb +46 -0
  26. data/lib/logical-construct/protocol/ssh-tunnel.rb +127 -0
  27. data/lib/logical-construct/protocol/vocabulary.rb +8 -0
  28. data/lib/logical-construct/target/Implement.rake +8 -0
  29. data/lib/logical-construct/target/command-line.rb +90 -0
  30. data/lib/logical-construct/target/flight-deck.rb +341 -0
  31. data/lib/logical-construct/target/implementation.rb +33 -0
  32. data/lib/logical-construct/target/plan-records.rb +317 -0
  33. data/lib/logical-construct/target/resolution-server.rb +153 -0
  34. data/lib/logical-construct/target/{unpack-cookbook.rb → unpack-plan.rb} +8 -4
  35. data/lib/logical-construct/template-file.rb +41 -0
  36. data/lib/templates/Rakefile.erb +8 -0
  37. data/spec/ground-control/smoke-test.rb +8 -7
  38. data/spec/node_resolution.rb +62 -0
  39. data/spec/target/plan-records.rb +142 -0
  40. data/spec/target/provisioning.rb +21 -0
  41. data/spec_help/file-sandbox.rb +12 -6
  42. data/spec_help/fixtures/Manifest +1 -0
  43. data/spec_help/fixtures/source/one.tbz +1 -0
  44. data/spec_help/fixtures/source/three.tbz +1 -0
  45. data/spec_help/fixtures/source/two.tbz +1 -0
  46. data/spec_help/spec_helper.rb +5 -7
  47. metadata +165 -72
  48. data/lib/logical-construct/ground-control/setup/build-files.rb +0 -93
  49. data/lib/logical-construct/ground-control/setup/create-construct-directory.rb +0 -22
  50. data/lib/logical-construct/ground-control/setup/install-init.rb +0 -32
  51. data/lib/logical-construct/resolving-task.rb +0 -141
  52. data/lib/logical-construct/satisfiable-task.rb +0 -87
  53. data/lib/logical-construct/target.rb +0 -4
  54. data/lib/logical-construct/target/chef-solo.rb +0 -37
  55. data/lib/logical-construct/target/platforms.rb +0 -51
  56. data/lib/logical-construct/target/platforms/aws.rb +0 -8
  57. data/lib/logical-construct/target/platforms/default/chef-config.rb +0 -134
  58. data/lib/logical-construct/target/platforms/default/resolve-configuration.rb +0 -44
  59. data/lib/logical-construct/target/platforms/default/volume.rb +0 -11
  60. data/lib/logical-construct/target/platforms/virtualbox.rb +0 -8
  61. data/lib/logical-construct/target/platforms/virtualbox/volume.rb +0 -15
  62. data/lib/logical-construct/target/provision.rb +0 -36
  63. data/lib/logical-construct/target/sinatra-resolver.rb +0 -99
  64. data/lib/logical-construct/testing/resolve-configuration.rb +0 -32
  65. data/lib/logical-construct/testing/resolving-task.rb +0 -15
  66. data/lib/templates/chef.rb.erb +0 -9
  67. data/lib/templates/construct.init.d.erb +0 -18
  68. data/lib/templates/resolver/finished.html.erb +0 -1
  69. data/lib/templates/resolver/index.html.erb +0 -17
  70. data/lib/templates/resolver/task-file-form.html.erb +0 -6
  71. data/lib/templates/resolver/task-form.html.erb +0 -6
  72. data/spec/resolution.rb +0 -147
  73. data/spec/target/chef-config.rb +0 -67
  74. data/spec/target/chef-solo.rb +0 -55
  75. data/spec/target/platforms.rb +0 -36
  76. data/spec/target/smoke-test.rb +0 -45
  77. data/spec_help/ungemmer.rb +0 -36
@@ -0,0 +1,67 @@
1
+ require 'logical-construct/node-client'
2
+
3
+ module LogicalConstruct
4
+ class GenerateManifest < Mattock::Tasklib
5
+ default_namespace :manifest
6
+
7
+ setting :plan_archives, []
8
+ setting :graph_format, :jsonld
9
+ setting :target_address, 'localhost'
10
+ setting :target_port, 51076
11
+
12
+ def default_configuration(provision)
13
+ super
14
+ self.plan_archives = provision.proxy_value.plan_archives
15
+ self.target_address = provision.proxy_value.target_address
16
+ self.target_port = provision.proxy_value.local_target_port
17
+ end
18
+
19
+ def node_url
20
+ "http://#{target_address}:#{target_port}"
21
+ end
22
+
23
+ def define
24
+ in_namespace do
25
+ desc "Dump manifest (mostly for debugging)"
26
+ task :dump, [:format] do |task, args|
27
+ require 'rdf/turtle'
28
+ format = args[:format] || graph_format
29
+ format = format.to_sym
30
+
31
+ base_url = "urn:manifest"
32
+
33
+ graph = ::RDF::Graph.new
34
+ focus = RoadForest::RDF::GraphFocus.new(base_url, graph)
35
+ builder = NodeClient::ManifestBuilder.new(focus)
36
+
37
+ plan_archives.each do |archive|
38
+ builder.add(archive)
39
+ end
40
+
41
+ puts(RDF::Writer.for(format).buffer(:base_uri => base_url) do |writer|
42
+ focus.relevant_prefixes.each do |prefix, uri|
43
+ writer.prefix(prefix, uri)
44
+ end
45
+ writer.insert(graph)
46
+ end)
47
+ end
48
+
49
+ task :deliver do |task|
50
+ client = NodeClient.new
51
+ client.node_url = node_url
52
+ client.plan_archives = plan_archives
53
+ client.deliver_manifest
54
+ end
55
+
56
+ task :fulfill do |task|
57
+ client = NodeClient.new
58
+ client.node_url = node_url
59
+ client.plan_archives = plan_archives
60
+ client.deliver_plans
61
+ end
62
+ end
63
+ task self[:dump] => root_task
64
+ task self[:deliver] => root_task
65
+ end
66
+ end
67
+ end
@@ -1,204 +1,109 @@
1
1
  require 'mattock'
2
- require 'json'
3
- require 'logical-construct/resolving-task'
2
+
3
+ require 'logical-construct/ground-control/generate-manifest'
4
+ require 'logical-construct/ground-control/build-plan'
5
+ require 'logical-construct/protocol/ssh-tunnel'
4
6
 
5
7
  module LogicalConstruct
6
8
  module GroundControl
7
9
  class Provision < Mattock::Tasklib
8
- class WebConfigure < Mattock::Task
9
- include ResolutionProtocol
10
-
11
- setting :target_protocol, "http"
12
- setting :target_address, nil
13
- setting :target_port, 51076
14
- runtime_setting :target_url
15
- setting :resolutions
16
- runtime_setting :web_resolutions
17
-
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]
24
- end
25
-
26
- def resolve(path)
27
- resolved = web_resolutions.fetch(path)
28
- if resolved.respond_to? :call
29
- resolved = resolved.call
30
- end
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
75
- end
76
- end
77
-
78
10
  default_namespace :provision
79
11
 
80
- setting :valise
81
12
  setting :target_protocol, "http"
82
13
  setting(:target_address, nil).isnt(:copiable)
83
- setting :target_port, 51076
84
- setting :resolutions
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
108
- setting :json_attribs, ""
109
-
110
- def default_configuration(core)
111
- core.copy_settings_to(self)
112
- super
113
- self.cookbooks.path = "cookbooks"
114
- self.secret_data.path = "data-bags/secret"
115
- self.normal_data.path = "data-bags"
116
- self.resolutions = {}
117
- self.roles = {}
118
- self.node_attribs = { "run_list" => [] }
119
- end
120
-
121
- def resolve_configuration
122
- super
123
- self.json_attribs_path ||= File::join(marshalling_path, "node.json")
14
+ setting :local_target_port, 51076
15
+ setting :remote_target_port, 30712
124
16
 
125
- self.cookbooks.file_list ||= Rake::FileList[cookbooks.path + "/**/*"].exclude(%r{/[.]git/}).exclude(%r{[.]sw[.]$})
126
- self.secret_data.file_list ||= Rake::FileList[secret_data.path + "/**/*"].exclude(%r{[.]sw[.]$})
127
- self.normal_data.file_list ||=
128
- Rake::FileList[normal_data.path + "/**/*"].exclude(%r{^#{secret_data.path}}).exclude(%r{[.]sw[.]$})
17
+ setting :plan_archives, []
129
18
 
130
- self.cookbooks.tarball_path ||= File::join(marshalling_path, "cookbooks.tgz")
131
- self.secret_data.tarball_path ||= File::join(marshalling_path, "secret_data_bags.tgz")
132
- self.normal_data.tarball_path ||= File::join(marshalling_path, "normal_data_bags.tgz")
19
+ dir(:marshalling, "marshall")
20
+ dir(:plan_source, "plans")
133
21
 
134
- resolutions["chef_config:cookbook_tarball"] ||= proc do
135
- File::open(cookbooks.tarball_path, "rb")
136
- end
137
-
138
- resolutions["chef_config:secret_data_tarball"] ||= proc do
139
- File::open(secret_data.tarball_path, "rb")
140
- end
141
-
142
- resolutions["chef_config:normal_data_tarball"] ||= proc do
143
- File::open(normal_data.tarball_path, "rb")
144
- end
22
+ def resolve_configuration
23
+ resolve_paths
24
+ super
145
25
  end
146
26
 
147
- include Mattock::CommandLineDSL
148
27
  def define
28
+ manifest = nil
149
29
  in_namespace do
150
- directory marshalling_path
30
+ directory marshalling.absolute_path
151
31
 
152
32
  task :collect, [:ipaddr] do |task, args|
153
33
  self.target_address = args[:ipaddr]
154
34
  end
155
35
 
156
- task :build_json_attribs, [:role] do |task, args|
157
- unless args[:role].nil?
158
- self.node_attribs["run_list"] = roles[args[:role]]
159
- end
160
- self.json_attribs = JSON.pretty_generate(node_attribs)
161
- resolutions["chef_config:json_attribs"] ||= json_attribs
36
+ tunnel = LogicalConstruct::SSHTunnel.new do |tunnel|
37
+ tunnel.target_address = proxy_value.target_address
38
+ copy_settings_to(tunnel)
39
+ self.local_target_port = tunnel.proxy_value.local_target_port
162
40
  end
163
41
 
164
- desc "Print attribs (optionally: for :role)"
165
- task :inspect_attribs, [:role] => :build_json_attribs do
166
- puts json_attribs
42
+ manifest = LogicalConstruct::GenerateManifest.new(self)
43
+
44
+ start_flight = Mattock::Rake::RemoteCommandTask.define_task(:start_flight => :collect) do |start_flight|
45
+ start_flight.remote_server.address = proxy_value.target_address
46
+ start_flight.command =
47
+ Mattock::CommandLine.new("nohup",
48
+ "/opt/logical-construct/bin/flight-deck",
49
+ "-C start_server")
50
+ start_flight.command.redirect_stdin("/dev/null")
51
+ start_flight.command.redirect_stdout("/opt/logical-construct/flight_deck_server.log")
52
+ start_flight.command.copy_stream_to(2, 1)
53
+ start_flight.command.redirections << "&"
167
54
  end
168
55
 
169
- file secret_data.tarball_path => [marshalling_path] + secret_data.file_list do
170
- cmd("tar", "--exclude **/*.sw?", "-czf", secret_data.tarball_path, secret_data.path).must_succeed!
56
+ start_resolution = Mattock::Rake::RemoteCommandTask.define_task(:start_resolution => :deliver_manifest) do |start_flight|
57
+ start_flight.remote_server.address = proxy_value.target_address
58
+ start_flight.verbose = 3
59
+ start_flight.command =
60
+ Mattock::CommandLine.new("nohup",
61
+ "/opt/logical-construct/bin/flight-deck")
62
+ #TODO: Mattock CommandLine needs a "background"
63
+ #TODO: RemoteCommandTask should wrap the whole
64
+ #nohup-redirect-background thing
65
+ start_flight.command.redirect_stdin("/dev/null")
66
+ start_flight.command.redirect_stdout("/opt/logical-construct/flight_deck.log")
67
+ start_flight.command.copy_stream_to(2, 1)
68
+ start_flight.command.redirections << "&"
171
69
  end
172
70
 
173
- file normal_data.tarball_path => [marshalling_path] + normal_data.file_list do
174
- cmd("tar",
175
- "--exclude **/*.sw?",
176
- "--exclude #{secret_data.path}",
177
- "-czf", normal_data.tarball_path, normal_data.path).must_succeed!
178
- end
71
+ task manifest.root_task => :collect
179
72
 
180
- file cookbooks.tarball_path => [marshalling_path] + cookbooks.file_list do
181
- cmd("tar", "--exclude .git", "--exclude **/*.sw?", "-czf", cookbooks.tarball_path, cookbooks.path).must_succeed!
182
- end
73
+ task_spine(:start_flight, :deliver_manifest, :fulfill_manifest, :start_resolution, :complete_provision)
74
+ task :deliver_manifest => manifest[:deliver]
75
+ task :fulfill_manifest => manifest[:fulfill]
183
76
 
184
- manifest = LogicalConstruct::GenerateManifest.new(self, :manifest =>
185
- [
186
- cookbooks.tarball_path,
187
- secret_data.tarball_path,
188
- normal_data.tarball_path,
189
- :collect
190
- ]) do |manifest|
191
- manifest.receiving_name = "configuration:Manifest"
192
- end
77
+ tunnel.wrap(manifest[:deliver])
78
+ tunnel.wrap(manifest[:fulfill])
79
+ task :complete_provision => tunnel[:close_tunnel]
80
+ end
81
+
82
+ desc "Provision :ipaddr with specified configs"
83
+ task root_task, [:ipaddr] => self[:complete_provision]
84
+ end
85
+
86
+ def plan_task(name, &block)
87
+ plan = BuildPlan.new(self) do |build|
88
+ build.basename = name
89
+ yield build if block_given?
90
+ end
91
+ task self[:manifest] => plan.archive_path
193
92
 
194
- WebConfigure.new(:web_configure => [:collect, :build_json_attribs, :manifest, cookbooks.tarball_path]) do |task|
195
- self.proxy_settings_to(task)
196
- task.target_address = proxy_value.target_address
93
+ in_namespace do
94
+ namespace :package do
95
+ desc "Compile and archive plan #{name.inspect}"
96
+ task name => plan.archive_path
197
97
  end
198
98
  end
199
99
 
200
- desc "Provision :ipaddr with specified configs (optionally: for :role)"
201
- task root_task, [:ipaddr, :role] => self[:web_configure]
100
+ plan_archives << plan.archive_path
101
+ end
102
+
103
+ def plans(*names)
104
+ names.each do |name|
105
+ plan_task(name)
106
+ end
202
107
  end
203
108
  end
204
109
  end
@@ -18,7 +18,7 @@ module LogicalConstruct
18
18
  def remote_task(name, comment = nil)
19
19
  in_namespace do
20
20
  desc comment unless comment.nil?
21
- Mattock::RemoteCommandTask.new(name) do |task|
21
+ Mattock::Rake::RemoteCommandTask.define_task(name) do |task|
22
22
  task.ssh_options += SSH_OPTIONS
23
23
 
24
24
  task.runtime_definition do |task|
@@ -6,7 +6,7 @@ module LogicalConstruct
6
6
 
7
7
  settings(
8
8
  :remote_server => nested( :address => nil, :user => "root"),
9
- :construct_dir => "/var/logical-construct"
9
+ :construct_dir => "/opt/logical-construct"
10
10
  )
11
11
  nil_fields :valise, :platform
12
12
 
@@ -47,7 +47,4 @@ module LogicalConstruct
47
47
  end
48
48
  end
49
49
 
50
- require 'logical-construct/ground-control/setup/build-files'
51
- require 'logical-construct/ground-control/setup/create-construct-directory'
52
50
  require 'logical-construct/ground-control/setup/copy-files'
53
- require 'logical-construct/ground-control/setup/install-init'
@@ -1,7 +1,7 @@
1
1
  require 'mattock/command-task'
2
2
 
3
3
  module LogicalConstruct
4
- class RemoteCopyFile < Mattock::CommandTask
4
+ class RemoteCopyFile < Mattock::Rake::CommandTask
5
5
  nil_fields :destination_address
6
6
  nil_fields :source_dir, :destination_dir, :basename
7
7
  required_fields :source_path, :destination_path
@@ -58,7 +58,7 @@ module LogicalConstruct
58
58
 
59
59
  def define
60
60
  in_namespace do
61
- RemoteCopyFile.new(self, :construct_dir) do |task|
61
+ RemoteCopyFile.define_task(self, :construct_dir) do |task|
62
62
  task.runtime_definition do
63
63
  task.remote_server = remote_server
64
64
  end
@@ -0,0 +1,66 @@
1
+ require 'mattock'
2
+
3
+ module LogicalConstruct
4
+ module GroundControl
5
+ class Tools < ::Mattock::Tasklib
6
+ include Mattock::CommandLineDSL
7
+
8
+ default_namespace :tools
9
+
10
+ dir(:plans, "plans")
11
+
12
+ def resolve_configuration
13
+ resolve_paths
14
+ super
15
+ end
16
+
17
+ def plans_dir
18
+ plans.absolute_path.sub(%r{/$},'')
19
+ end
20
+
21
+ def define
22
+ in_namespace do
23
+ namespace :create_plan do
24
+ rule %r{\A#{plans_dir}/[^/]*\Z} do |task, args|
25
+ cmd("mkdir", "-p", task.name).must_succeed! #ok
26
+ end
27
+
28
+ rule(%r{\A#{plans_dir}/[^/]*/plan\.rake\Z}, [:name] => ['%d']) do |task, args|
29
+ require 'logical-construct/target/implementation'
30
+
31
+ File::open(task.name, "w") do |file|
32
+ indent = 16
33
+ file.write(<<-EOR.gsub(/^#{" "*indent}/,''))
34
+ require 'logical-construct/plan'
35
+ include LogicalConstruct::Plan
36
+
37
+ core = Core.new do |core|
38
+ core.namespace_name = :#{args[:name]}
39
+ core.plan_rakefile.absolute_path = __FILE__
40
+ end
41
+
42
+ core.in_namespace do
43
+ #Plan tasks go here
44
+ #
45
+ #Important tasks to make dependencies to:
46
+
47
+ EOR
48
+
49
+ Target::Implementation.task_list.each do |task_name|
50
+ file.puts(" #task :#{task_name}")
51
+ end
52
+
53
+ file.puts("end")
54
+ end
55
+ end
56
+ end
57
+
58
+ desc "Create a new plan to be part of a provisioning"
59
+ task :create_plan, [:name] do |task, args|
60
+ Rake::Task[File::join(plans_dir, args[:name], "plan.rake")].invoke(args[:name])
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end