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,112 @@
1
+ require 'roadforest/remote-host'
2
+ require 'logical-construct/protocol'
3
+
4
+ module LogicalConstruct
5
+ class NodeClient
6
+ class ManifestBuilder
7
+ include Protocol::PlanValidation
8
+ def initialize(graph_focus)
9
+ @graph_focus = graph_focus
10
+ end
11
+
12
+ def plans_list
13
+ @graph_focus.find_or_add([:lc, "plans"]).as_list
14
+ end
15
+
16
+ def add_plan(plan_archive)
17
+ name = File::basename(plan_archive)
18
+ plans_list.append_node("##{name}") do |node|
19
+ node[[:rdf, "type" ]] = [:lc, "Need"]
20
+ node[[ :lc, "name" ]] = name
21
+ node[[ :lc, "digest"]] = file_checksum(plan_archive)
22
+ end
23
+ end
24
+ end
25
+
26
+ def initialize
27
+ @plan_archives = []
28
+ @silent = false
29
+ end
30
+
31
+ attr_accessor :plan_archives, :node_url, :server, :silent
32
+
33
+ def server
34
+ @server ||= RoadForest::RemoteHost.new(node_url).tap do |server|
35
+ #server.graph_transfer.trace = true
36
+ end
37
+ end
38
+
39
+ def report(item)
40
+ puts item unless silent
41
+ end
42
+
43
+ def state
44
+ state = nil
45
+ server.getting do |root|
46
+ state = page_labeled("Current Status", root)[:lc, "node-state"]
47
+ end
48
+ state
49
+ end
50
+
51
+ def resolved?
52
+ state.downcase == "resolved"
53
+ end
54
+
55
+ def page_labeled(label, focus)
56
+ concept = focus.all(:skos, "hasTopConcept").find do |concept|
57
+ concept.all(:skos, "prefLabel").include?(label) or concept.all(:skos, "altLabel").include?(label)
58
+ end
59
+ concept.first(:foaf, "page")
60
+ end
61
+
62
+ def deliver_manifest
63
+ report "Delivering manifest"
64
+ messages = []
65
+ server.putting do |root|
66
+ messages = []
67
+ needs = page_labeled("Server Manifest", root)
68
+
69
+ builder = ManifestBuilder.new(needs)
70
+
71
+ plan_archives.each do |archive|
72
+ messages << "Adding #{archive}"
73
+ builder.add_plan(archive)
74
+ end
75
+ end
76
+ report messages
77
+ end
78
+
79
+ def deliver_plans
80
+ loop do
81
+ needs = []
82
+ server.getting do |root|
83
+ needs = []
84
+ unresolved = page_labeled("Unresolved Plans", root)
85
+
86
+ unresolved[:lc, "plans"].as_list.each do |need|
87
+ needs << [need[:lc, "name"], need[:lc, "contents"]]
88
+ end
89
+ end
90
+ if needs.empty?
91
+ report "Target needs fulfilled"
92
+ break
93
+ end
94
+
95
+ report "Delivering plan archives"
96
+ needs.each do |need|
97
+ name, path = *need
98
+ plan = plan_archives.find do |plan|
99
+ File.basename(plan) == name
100
+ end
101
+
102
+ next if plan.nil?
103
+
104
+ File::open(plan, "r") do |file|
105
+ report " Delivering #{name}"
106
+ server.put_file(path, "application/x-gtar-compressed", file) #sorta like a ukulele
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,2 @@
1
+ require 'logical-construct/plan/core'
2
+ require 'logical-construct/plan/standalone-bundle'
@@ -0,0 +1,45 @@
1
+ require 'mattock'
2
+ require 'logical-construct/target/implementation'
3
+
4
+ module LogicalConstruct
5
+ module Plan
6
+ class Core < ::Mattock::Tasklib
7
+ path(:plan_rakefile, "plan.rake")
8
+
9
+ def resolve_configuration
10
+ self.absolute_path = plan_rakefile.pathname.dirname
11
+
12
+ resolve_paths
13
+ super
14
+ end
15
+
16
+ def define
17
+ in_namespace do
18
+ task :compile => 'compile:finished'
19
+ namespace :compile do
20
+ task_spine :preflight, :perform, :finished
21
+ end
22
+
23
+ task :install => "install:finished"
24
+ namespace :install do
25
+ task_spine :preflight, :perform, :finished
26
+ end
27
+
28
+ Target::Implementation.task_list.each_cons(2) do |first, second|
29
+ task second => "construct:#{first}"
30
+ end
31
+ end
32
+
33
+ namespace :construct do
34
+ Target::Implementation.task_list.each do |name|
35
+ task name => self[name]
36
+ end
37
+
38
+ [:install, :compile].each do |task|
39
+ task task => self[task]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,80 @@
1
+ require 'mattock'
2
+ require 'mattock/bundle-command-task'
3
+
4
+ module LogicalConstruct
5
+ module Plan
6
+ class StandaloneBundle < Mattock::TaskLib
7
+ include Mattock::CommandLineDSL
8
+ default_namespace :bundler
9
+
10
+ dir(:target_dir,
11
+ path(:gemfile, "Gemfile"),
12
+ dir(:gems, "gems"))
13
+
14
+ def default_configuration(core)
15
+ super
16
+ target_dir.absolute_path = core.absolute_path
17
+ end
18
+
19
+ def resolve_configuration
20
+ resolve_paths
21
+ super
22
+ end
23
+
24
+ def define
25
+ directory gems.absolute_path
26
+
27
+ #TODO: Experiment with
28
+ #
29
+ #bundle install
30
+ #bundle package --all
31
+ #....
32
+ #bundle install --local
33
+ in_namespace do
34
+ #bundler unconditionally re-produces bundle/setup.rb, which means
35
+ #that plans that use this tasklib will always be re-packed, even when
36
+ #the contents haven't changed significantly.
37
+ #
38
+ #It's possible that a "hash difference" file task could be written
39
+ #for setup.rb? Kind of a hack. Or else tell bundler that it's config
40
+ #dir goes in a temp directory, and then we move things, or local
41
+ #rsync --checksum or something.
42
+ Mattock::BundleCommandTask.define_task(:standalone => gems.absolute_path) do |bundle_build|
43
+ bundle_build.command = (
44
+ cmd("cd", target_dir.absolute_path) &
45
+ cmd("bundle", "install"){|bundler|
46
+ bundler.options << "--gemfile=" + gemfile.absolute_path
47
+ bundler.options << "--path=" + gems.absolute_path
48
+ bundler.options << "--standalone"
49
+ bundler.options << "--binstubs=bin"
50
+ })
51
+ end
52
+
53
+ task :pristine => gems.absolute_path do
54
+ gem_scope = [Gem.ruby_engine, Gem::ConfigMap[:ruby_version]].join("/")
55
+
56
+ cmd_env = {
57
+ "RUBYOPT" => "",
58
+ "GEM_HOME" => gems.pathname.join(gem_scope).to_s #XXX update this to use gem_scope from flight-deck
59
+ }
60
+
61
+ cmd("gem pristine") do |gem|
62
+ gem.options << "--all"
63
+ gem.options << "--extensions"
64
+ gem.command_environment.merge! cmd_env
65
+ end.must_succeed!
66
+ end
67
+
68
+ task :load_setup do
69
+ require gems.pathname.join("bundler/setup")
70
+ end
71
+ end
72
+
73
+ task 'install:perform' => self[:pristine]
74
+ task 'compile:perform' => self[:standalone]
75
+ task 'compile:preflight' => gemfile.absolute_path
76
+ task :preflight => self[:load_setup]
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,41 @@
1
+ module LogicalConstruct
2
+ class TCPPortOpenCheck
3
+ def initialize(port)
4
+ @host = "localhost"
5
+ @port = port
6
+ @timeout = 0
7
+ @retry_delay = 0.5
8
+ yield self if block_given?
9
+ end
10
+ attr_accessor :host, :port
11
+ attr_accessor :timeout, :retry_delay
12
+
13
+ def open_socket
14
+ TCPSocket.new @host, @port
15
+ end
16
+
17
+ def open?(timeout = nil)
18
+ timeout ||= @timeout
19
+ start_time = Time.now
20
+ test_conn = open_socket()
21
+ return true
22
+ rescue Errno::ECONNREFUSED
23
+ if Time.now - start_time > timeout
24
+ return false
25
+ else
26
+ sleep @retry_delay
27
+ retry
28
+ end
29
+ ensure
30
+ test_conn.close if test_conn.respond_to? :close
31
+ end
32
+
33
+ def fail_if_open!
34
+ raise "Port is open, should be closed: #{@host}:#{@port}" if open?(0)
35
+ end
36
+
37
+ def fail_if_closed!
38
+ raise "Port is closed, should be open: #{@host}:#{@port}" unless open?
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,2 @@
1
+ require 'logical-construct/protocol/plan-validation'
2
+ require 'logical-construct/protocol/vocabulary'
@@ -0,0 +1,46 @@
1
+ require 'openssl'
2
+ module LogicalConstruct
3
+ module Protocol
4
+ module PlanValidation
5
+ class DigestFailure < ::StandardError; end
6
+
7
+ def digest
8
+ @digest ||= OpenSSL::Digest::SHA256.new
9
+ end
10
+
11
+ def check_digest(checksum, path, target_path=nil)
12
+ if file_checksum(path) != checksum
13
+ raise DigestFailure, "Digest failure for #{target_path || path}"
14
+ end
15
+ end
16
+
17
+ #The biggest digest-block-length-aligned chunk under 4MB
18
+ BIG_CHUNK = 4 * 1024 * 1024
19
+ def chunk_size
20
+ @chunk_size ||= (BIG_CHUNK / digest.block_length).floor * digest.block_length
21
+ end
22
+
23
+ def realpath(path)
24
+ File::readlink(path.to_s)
25
+ rescue Errno::EINVAL, Errno::ENOENT
26
+ path
27
+ end
28
+
29
+ def file_checksum(path)
30
+ digest.reset
31
+ File::open(realpath(path)) do |file|
32
+ while chunk = file.read(chunk_size)
33
+ digest.update(chunk)
34
+ end
35
+ end
36
+ digest.hexdigest
37
+ end
38
+
39
+ def generate_checksum(data)
40
+ digest.reset
41
+ digest << data
42
+ digest.hexdigest
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,127 @@
1
+ require 'socket'
2
+ require 'mattock'
3
+ require 'mattock/command-task'
4
+
5
+ module LogicalConstruct
6
+ class SSHTunnel < Mattock::Tasklib
7
+ class OpenPortTask < Mattock::Rake::Task
8
+ default_taskname :open_port
9
+ setting :base_port
10
+ setting :found_port
11
+
12
+ def resolve_configuration
13
+ self.found_port = base_port
14
+ super
15
+ end
16
+
17
+ def configure_discovery(base_port)
18
+ self.base_port = base_port
19
+ return proxy_value.found_port
20
+ end
21
+
22
+ def action(args)
23
+ test_server = TCPServer.new("0.0.0.0", found_port)
24
+ rescue Errno::EADDRINUSE
25
+ self.found_port += 1
26
+ retry
27
+ ensure
28
+ test_server.close
29
+ end
30
+ end
31
+
32
+ class CreateSSHMaster < Mattock::Rake::CommandTask
33
+ default_taskname :create_ssh_master
34
+
35
+ setting :target_address
36
+
37
+ def verify_command
38
+ cmd("ssh", "-o ControlMaster=auto", "-O check", target_address)
39
+ end
40
+
41
+ def command
42
+ cmd("ssh", "-Nf", "-o ControlMaster=auto", "-o ControlPersist=300", "-o ExitOnForwardFailure=yes", target_address)
43
+ end
44
+
45
+ def action(args)
46
+ super
47
+ rescue StandardError => se
48
+ puts "Attempting to recover from: #{se.message}"
49
+ retry
50
+ end
51
+ end
52
+
53
+ class CreateTunnel < Mattock::Rake::CommandTask
54
+ default_taskname :create_tunnel
55
+
56
+ setting :target_address
57
+ setting :local_target_port
58
+ setting :remote_target_port
59
+
60
+ def command
61
+ cmd("ssh", "-O", "forward", "-L", "localhost:#{local_target_port}:localhost:#{remote_target_port}", target_address)
62
+ end
63
+ end
64
+
65
+ class CancelTask < Mattock::Rake::CommandTask
66
+ default_taskname :cancel_tunnel
67
+
68
+ setting :target_address
69
+
70
+ def verify_command
71
+ cmd("ssh", "-o ControlMaster=auto", "-O check", target_address)
72
+ end
73
+
74
+ def check_verification_command
75
+ !super
76
+ end
77
+
78
+ def command
79
+ cmd("ssh", "-n", "-o ControlMaster=auto", "-O exit", target_address)
80
+ end
81
+ end
82
+
83
+ default_namespace :ssh_tunnel
84
+
85
+ runtime_setting :target_address
86
+ setting :target_port, 10000
87
+ setting :local_target_port
88
+ setting :remote_target_port
89
+
90
+ def resolve_configuration
91
+ self.local_target_port ||= target_port
92
+ self.remote_target_port ||= target_port
93
+ super
94
+ end
95
+
96
+ def wrap(task_name)
97
+ task task_name => self[:create_tunnel]
98
+ task self[:cancel_tunnel] => task_name
99
+ end
100
+
101
+ def define
102
+ in_namespace do
103
+ open_port = OpenPortTask.define_task do |open_port|
104
+ self.local_target_port = open_port.configure_discovery(local_target_port)
105
+ end
106
+
107
+ master = CreateSSHMaster.define_task(self) do |master|
108
+ copy_settings_to(master)
109
+ end
110
+
111
+ create = CreateTunnel.define_task(self) do |create|
112
+ copy_settings_to(create)
113
+ end
114
+
115
+ cancel = CancelTask.define_task(self) do |cancel|
116
+ copy_settings_to(cancel)
117
+ end
118
+
119
+ task_spine create.task_name, :close_tunnel, :run
120
+
121
+ task master.task_name => open_port.task_name
122
+ task create.task_name => master.task_name
123
+ task :close_tunnel => cancel.task_name
124
+ end
125
+ end
126
+ end
127
+ end