logical-construct 0.0.5 → 0.1.0

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