loom-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +99 -0
  6. data/Guardfile +54 -0
  7. data/Rakefile +6 -0
  8. data/bin/loom +185 -0
  9. data/lib/env/development.rb +1 -0
  10. data/lib/loom.rb +44 -0
  11. data/lib/loom/all.rb +20 -0
  12. data/lib/loom/config.rb +106 -0
  13. data/lib/loom/core_ext.rb +37 -0
  14. data/lib/loom/dsl.rb +60 -0
  15. data/lib/loom/facts.rb +13 -0
  16. data/lib/loom/facts/all.rb +2 -0
  17. data/lib/loom/facts/fact_file_provider.rb +86 -0
  18. data/lib/loom/facts/fact_set.rb +138 -0
  19. data/lib/loom/host_spec.rb +32 -0
  20. data/lib/loom/inventory.rb +124 -0
  21. data/lib/loom/logger.rb +141 -0
  22. data/lib/loom/method_signature.rb +174 -0
  23. data/lib/loom/mods.rb +4 -0
  24. data/lib/loom/mods/action_proxy.rb +105 -0
  25. data/lib/loom/mods/all.rb +3 -0
  26. data/lib/loom/mods/mod_loader.rb +80 -0
  27. data/lib/loom/mods/module.rb +113 -0
  28. data/lib/loom/pattern.rb +15 -0
  29. data/lib/loom/pattern/all.rb +7 -0
  30. data/lib/loom/pattern/definition_context.rb +74 -0
  31. data/lib/loom/pattern/dsl.rb +176 -0
  32. data/lib/loom/pattern/hook.rb +28 -0
  33. data/lib/loom/pattern/loader.rb +48 -0
  34. data/lib/loom/pattern/reference.rb +71 -0
  35. data/lib/loom/pattern/reference_set.rb +169 -0
  36. data/lib/loom/pattern/result_reporter.rb +77 -0
  37. data/lib/loom/runner.rb +209 -0
  38. data/lib/loom/shell.rb +12 -0
  39. data/lib/loom/shell/all.rb +10 -0
  40. data/lib/loom/shell/api.rb +48 -0
  41. data/lib/loom/shell/cmd_result.rb +33 -0
  42. data/lib/loom/shell/cmd_wrapper.rb +164 -0
  43. data/lib/loom/shell/core.rb +226 -0
  44. data/lib/loom/shell/harness_blob.rb +26 -0
  45. data/lib/loom/shell/harness_command_builder.rb +50 -0
  46. data/lib/loom/shell/session.rb +25 -0
  47. data/lib/loom/trap.rb +44 -0
  48. data/lib/loom/version.rb +3 -0
  49. data/lib/loomext/all.rb +4 -0
  50. data/lib/loomext/corefacts.rb +6 -0
  51. data/lib/loomext/corefacts/all.rb +8 -0
  52. data/lib/loomext/corefacts/facter_provider.rb +24 -0
  53. data/lib/loomext/coremods.rb +5 -0
  54. data/lib/loomext/coremods/all.rb +13 -0
  55. data/lib/loomext/coremods/exec.rb +50 -0
  56. data/lib/loomext/coremods/files.rb +104 -0
  57. data/lib/loomext/coremods/net.rb +33 -0
  58. data/lib/loomext/coremods/package/adapter.rb +100 -0
  59. data/lib/loomext/coremods/package/package.rb +62 -0
  60. data/lib/loomext/coremods/user.rb +82 -0
  61. data/lib/loomext/coremods/vm.rb +0 -0
  62. data/lib/loomext/coremods/vm/all.rb +6 -0
  63. data/lib/loomext/coremods/vm/vbox.rb +84 -0
  64. data/loom.gemspec +39 -0
  65. data/loom/inventory.yml +13 -0
  66. data/scripts/harness.sh +242 -0
  67. data/spec/loom/host_spec_spec.rb +101 -0
  68. data/spec/loom/inventory_spec.rb +154 -0
  69. data/spec/loom/method_signature_spec.rb +275 -0
  70. data/spec/loom/pattern/dsl_spec.rb +207 -0
  71. data/spec/loom/shell/cmd_wrapper_spec.rb +239 -0
  72. data/spec/loom/shell/harness_blob_spec.rb +42 -0
  73. data/spec/loom/shell/harness_command_builder_spec.rb +36 -0
  74. data/spec/runloom.sh +35 -0
  75. data/spec/scripts/harness_spec.rb +385 -0
  76. data/spec/spec_helper.rb +94 -0
  77. data/spec/test.loom +370 -0
  78. data/spec/test_loom_spec.rb +57 -0
  79. metadata +287 -0
@@ -0,0 +1,26 @@
1
+ require 'base64'
2
+ require 'digest/sha1'
3
+
4
+ module Loom::Shell
5
+
6
+ # A blob of commands fit for sending to the harness.
7
+ class HarnessBlob
8
+
9
+ def initialize(cmd_blob)
10
+ @cmd_blob = cmd_blob
11
+ end
12
+
13
+ attr_reader :cmd_blob
14
+
15
+ def encoded_script
16
+ # TODO: Fix this trailing newline hack, it is here to make encoding
17
+ # consistent with the harness.sh script, which is a bit messy with how it
18
+ # treats trailing newlines.
19
+ Base64.encode64(cmd_blob + "\n")
20
+ end
21
+
22
+ def checksum
23
+ Digest::SHA1.hexdigest encoded_script
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ module Loom
2
+ module Shell
3
+
4
+ HarnessMissingStdin = Class.new Loom::LoomError
5
+
6
+ class HarnessCommandBuilder
7
+
8
+ # TODO: Resolve a real script path.
9
+ SCRIPT = "./scripts/harness.sh"
10
+
11
+ DEFAULT_RUN_OPTS = {
12
+ :cmd_shell => "/bin/dash",
13
+ :record_file => "/opt/loom/commands"
14
+ }
15
+
16
+ def initialize(harness_blob)
17
+ @harness_blob = harness_blob
18
+ @run_opts = DEFAULT_RUN_OPTS.dup
19
+ end
20
+
21
+ def run_cmd
22
+ build_cmd :run, @harness_blob.checksum, *hash_to_opts_array(@run_opts),
23
+ {
24
+ :stdin => @harness_blob.encoded_script
25
+ }
26
+ end
27
+
28
+ def check_cmd
29
+ build_cmd :check, @harness_blob.checksum, {
30
+ :stdin => @harness_blob.encoded_script
31
+ }
32
+ end
33
+
34
+ private
35
+ def hash_to_opts_array(hash)
36
+ hash.to_a.map do |tuple|
37
+ "--%s %s" % [tuple.first, tuple.last]
38
+ end
39
+ end
40
+
41
+ def build_cmd(cmd, *args, stdin: nil)
42
+ raise HarnessMissingStdin unless stdin
43
+
44
+ heredoc = "<<'HARNESS_EOS'\n#{stdin}\nHARNESS_EOS"
45
+ cmd = "--" + cmd.to_s
46
+ "%s %s 2>/dev/null - %s %s" % [SCRIPT, cmd, args.join(" "), heredoc]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ module Loom::Shell
2
+ class Session
3
+ def initialize
4
+ @command_results = []
5
+ @success = true
6
+ end
7
+
8
+ attr_reader :command_results
9
+
10
+ def success?
11
+ @success
12
+ end
13
+
14
+ def last
15
+ @command_results.last
16
+ end
17
+
18
+ def <<(command_result)
19
+ @command_results << command_result
20
+ unless command_result.is_test
21
+ @success &&= command_result.success?
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/loom/trap.rb ADDED
@@ -0,0 +1,44 @@
1
+ module Loom
2
+ class Trap
3
+
4
+ class SignalExit < Loom::LoomError
5
+ attr_reader :signal
6
+
7
+ def initialize(signal)
8
+ @signal = signal
9
+ end
10
+ end
11
+
12
+ # See `man 7 signal` or #{Signal.list}
13
+ module Sig
14
+ HUP = "HUP"
15
+ INT = "INT"
16
+ QUIT = "QUIT"
17
+ TERM = "TERM"
18
+ USR1 = "USR1"
19
+ USR2 = "USR2"
20
+ end
21
+
22
+ def self.install(signal, trap_handler)
23
+ raise "unknown signal => #{signal}" unless Signal.list[signal]
24
+ Signal.trap signal do
25
+ trap_handler.handle(signal)
26
+ end
27
+ end
28
+
29
+ class Handler
30
+
31
+ def initialize(&handler)
32
+ @signal_handle_counts = {}
33
+ @handler = handler
34
+ end
35
+
36
+ def handle(signal)
37
+ @signal_handle_counts[signal] ||= 0
38
+ @signal_handle_counts[signal] += 1
39
+
40
+ @handler.call signal, @signal_handle_counts[signal]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Loom
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ LoomExt = Module.new
2
+
3
+ require_relative "corefacts"
4
+ require_relative "coremods"
@@ -0,0 +1,6 @@
1
+ module LoomExt
2
+ module CoreFacts
3
+ end
4
+ end
5
+
6
+ require_relative "corefacts/all"
@@ -0,0 +1,8 @@
1
+ module LoomExt
2
+ module CoreFacts
3
+ end
4
+ end
5
+
6
+ require "loom"
7
+
8
+ require_relative "facter_provider"
@@ -0,0 +1,24 @@
1
+ module LoomExt::CoreFacts
2
+ class FacterProvider < Loom::Facts::Provider
3
+
4
+ Loom::Facts::Provider.register_factory(self) do |host_spec, shell, loom_config|
5
+ FacterProvider.new host_spec, shell
6
+ end
7
+
8
+ def initialize(host_spec, shell)
9
+ @has_facter = shell.test :which, "facter"
10
+ disable(host_spec) unless @has_facter
11
+ @shell = shell
12
+ end
13
+
14
+ def collect_facts
15
+ unless @has_facter
16
+ Loom.log.error "facter not installed"
17
+ return {}
18
+ end
19
+
20
+ yaml_facts = @shell.capture :facter, "--yaml"
21
+ yaml_facts.nil? ? {} : YAML.load(yaml_facts)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ module LoomExt
2
+ module CoreMods
3
+ end
4
+ end
5
+ require_relative "coremods/all"
@@ -0,0 +1,13 @@
1
+ module LoomExt
2
+ module CoreMods
3
+ end
4
+ end
5
+
6
+ require "loom"
7
+
8
+ require_relative "exec"
9
+ require_relative "files"
10
+ require_relative "net"
11
+ require_relative "user"
12
+ require_relative "package/package"
13
+ require_relative "vm/all"
@@ -0,0 +1,50 @@
1
+ module LoomExt::CoreMods
2
+
3
+ FailError = Class.new Loom::ExecutionError
4
+
5
+ ##
6
+ # Executes shell commands from patterns. e.g.
7
+ #
8
+ # loom << :echo, "hello there"
9
+ class Exec < Loom::Mods::Module
10
+ register_mod :exec, :alias => [:x, :<<] do |*cmd, **opts|
11
+ shell.execute *cmd, **opts
12
+ end
13
+ end
14
+
15
+ class ChangeDirectory < Loom::Mods::Module
16
+ register_mod :change_directory, :alias => :cd do |path, &block|
17
+ shell.cd path, &block
18
+ end
19
+ end
20
+
21
+ class Timeout < Loom::Mods::Module
22
+ register_mod :timeout do |timeout: 60, &block|
23
+ shell.wrap(:timeout, timeout, :should_quote => false, &block)
24
+ end
25
+ end
26
+
27
+ class Time < Loom::Mods::Module
28
+ register_mod :time do |&block|
29
+ shell.wrap(:time, :should_quote => false, &block)
30
+ end
31
+ end
32
+
33
+ class Sudo < Loom::Mods::Module
34
+ register_mod :sudo do |user: nil, cmd: nil, &block|
35
+ shell.sudo user, cmd, &block
36
+ end
37
+ end
38
+
39
+ class Test < Loom::Mods::Module
40
+ register_mod :test do |*cmd|
41
+ shell.test *cmd
42
+ end
43
+ end
44
+
45
+ class Fail < Loom::Mods::Module
46
+ register_mod :fail do |message=nil|
47
+ raise FailError, message
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,104 @@
1
+ module LoomExt::CoreMods
2
+ class Files < Loom::Mods::Module
3
+
4
+ register_mod :files
5
+
6
+ def init_action(paths)
7
+ @paths = [paths].flatten.compact
8
+ end
9
+
10
+ private
11
+ ##
12
+ # Executes #{action} for each path in #{paths} or #{@paths}. If
13
+ # action is not given, a block is expected to which each path will
14
+ # be passed.
15
+ def each_path(action: nil, flags: nil, &block)
16
+ raise 'use either action or block in each_path' if action && block_given?
17
+ raise 'use either action or block in each_path' unless action || block_given?
18
+
19
+ @paths.each do |p|
20
+ next unless p
21
+
22
+ raise "prefix relative paths with '.': #{p}" unless p.match /^[.]?\//
23
+ if block
24
+ yield p
25
+ else
26
+ shell.execute action, flags, p
27
+ end
28
+ end
29
+
30
+ # Return self for chaining in pattern files
31
+ self
32
+ end
33
+
34
+ module Actions
35
+
36
+ def cat
37
+ each_path do |p|
38
+ shell.capture :cat, p
39
+ end
40
+ end
41
+
42
+ def rm
43
+ each_path do |p|
44
+ shell.capture :rm, "-f", p
45
+ end
46
+ end
47
+
48
+ def match?(pattern: /./)
49
+ all = true
50
+ each_path do |p|
51
+ file = shell.capture :cat, p
52
+ all &&= file.match(pattern)
53
+ end
54
+ all
55
+ end
56
+
57
+ def gsub(pattern: nil, replace: nil, &block)
58
+ each_path do |p|
59
+ contents = shell.capture :cat, p
60
+ if contents
61
+ contents.gsub!(pattern, replace, &block) unless pattern.nil?
62
+ write contents
63
+ end
64
+ end
65
+ end
66
+
67
+ def chown(user: nil, group: nil)
68
+ group_arg = group && ":" + group.to_s
69
+
70
+ each_path do |p|
71
+ shell.execute :chown, [user, group_arg].compact.map(&:to_s).join, p
72
+ end
73
+ end
74
+
75
+ def touch
76
+ each_path :action => :touch
77
+ end
78
+
79
+ def mkdir(flags: nil, **opts)
80
+ each_path :action => :mkdir, :flags => flags
81
+ end
82
+
83
+ def append(text="")
84
+ each_path do |p|
85
+ loom.test "[ -f #{p} ]"
86
+
87
+ redirect = Loom::Shell::CmdRedirect.append_stdout p
88
+ cmd = Loom::Shell::CmdWrapper.new(
89
+ :"/bin/echo", "-e", text, redirect: redirect)
90
+ shell.execute cmd
91
+ end
92
+ end
93
+
94
+ def write(text="")
95
+ each_path do |p|
96
+ loom.x :"/bin/echo", "-e", text, :piped_cmds => [[:tee, p]]
97
+ end
98
+ end
99
+
100
+ end
101
+ end
102
+
103
+ Files.import_actions Files::Actions
104
+ end
@@ -0,0 +1,33 @@
1
+ module LoomExt::CoreMods
2
+ class Net < Loom::Mods::Module
3
+
4
+ NoNetworkError = Class.new Loom::ExecutionError
5
+
6
+ register_mod :net
7
+
8
+ def init_action(timeout: 10, check_host: "8.8.8.8")
9
+ @net_timeout = timeout
10
+ @check_host = check_host
11
+ end
12
+
13
+ module Actions
14
+
15
+ def has_net?
16
+ loom.timeout :timeout => @net_timeout do
17
+ loom.test :sh, "-c", "while ! ping -c1 #{@check_host}; do true; done"
18
+ end
19
+ end
20
+
21
+ def check_net
22
+ raise NoNetworkError, "no network available" unless has_net?
23
+ end
24
+
25
+ def with_net(&block)
26
+ check_net
27
+ yield if block_given?
28
+ end
29
+ end
30
+
31
+ import_actions Net::Actions
32
+ end
33
+ end
@@ -0,0 +1,100 @@
1
+ module LoomExt::CoreMods
2
+ class Package < Loom::Mods::Module
3
+
4
+ class PkgAdapter
5
+
6
+ attr_reader :loom
7
+
8
+ def initialize(loom)
9
+ @loom = loom
10
+ end
11
+
12
+ def ensure_installed(pkg_name)
13
+ install(pkg_name) unless installed?(pkg_name)
14
+ end
15
+
16
+ def installed?(pkg_name)
17
+ raise 'not implemnted'
18
+ end
19
+
20
+ def install(pkg_name)
21
+ raise 'not implemented'
22
+ end
23
+
24
+ def uninstall(pkg_name)
25
+ raise 'not implemented'
26
+ end
27
+
28
+ def update_cache
29
+ raise 'not implemented'
30
+ end
31
+
32
+ def upgrade(pkg_name)
33
+ raise 'not implemented'
34
+ end
35
+ end
36
+
37
+ class GemAdapter < PkgAdapter
38
+ def installed?(pkg_name)
39
+ loom.test "ruby -r#{pkg_name} -e exit"
40
+ end
41
+
42
+ def install(pkg_name)
43
+ loom << "gem install #{pkg_name}"
44
+ end
45
+ end
46
+
47
+ class DpkgAdapter < PkgAdapter
48
+
49
+ def installed?(pkg_name)
50
+ loom.test "dpkg -s #{pkg_name}"
51
+ end
52
+ end
53
+
54
+ class AptAdapter < DpkgAdapter
55
+
56
+ def install(pkg_name)
57
+ loom.net.with_net { loom << "echo apt-get install #{pkg_name}" }
58
+ end
59
+
60
+ def uninstall(pkg_name)
61
+ loom << "echo apt uninstall"
62
+ end
63
+
64
+ def update_cache
65
+ loom.net.with_net { loom << "apt update" }
66
+ end
67
+
68
+ def upgrade(pkg_name)
69
+ loom.net.with_net { loom << "apt upgrade" }
70
+ end
71
+ end
72
+
73
+ class RpmAdapter < PkgAdapter
74
+
75
+ def installed?(pkg_name)
76
+ loom.test :rpm, "-q #{pkg_name}"
77
+ end
78
+
79
+ end
80
+
81
+ class DnfAdapter < RpmAdapter
82
+
83
+ def install(pkg_name)
84
+ loom.net.with_net { loom << "dnf install #{pkg_name}" }
85
+ end
86
+
87
+ def uninstall(pkg_name)
88
+ loom << "echo dnf uninstall"
89
+ end
90
+
91
+ def update_cache
92
+ loom.net.with_net { loom << "dnf updateinfo" }
93
+ end
94
+
95
+ def upgrade(pkg_name)
96
+ raise 'not implemented'
97
+ end
98
+ end
99
+ end
100
+ end