loom-core 0.0.1

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 (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