loom-core 0.0.6 → 0.0.9

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +31 -0
  3. data/Gemfile.lock +108 -56
  4. data/Rakefile +2 -0
  5. data/bin/loom +3 -0
  6. data/docs/architecture.jpg +0 -0
  7. data/gentags.sh +2 -0
  8. data/lib/loom/all.rb +1 -1
  9. data/lib/loom/config.rb +2 -2
  10. data/lib/loom/method_signature.rb +1 -1
  11. data/lib/loom/mods/action_proxy.rb +2 -2
  12. data/lib/loom/mods/mod_loader.rb +10 -5
  13. data/lib/loom/mods/module.rb +6 -5
  14. data/lib/loom/pattern/all.rb +1 -0
  15. data/lib/loom/pattern/definition_context.rb +7 -5
  16. data/lib/loom/pattern/dsl.rb +184 -119
  17. data/lib/loom/pattern/expanding_reference.rb +82 -6
  18. data/lib/loom/pattern/loader.rb +2 -18
  19. data/lib/loom/pattern/pattern.rb +52 -0
  20. data/lib/loom/pattern/reference.rb +17 -13
  21. data/lib/loom/pattern/reference_set.rb +71 -50
  22. data/lib/loom/runner/all.rb +2 -0
  23. data/lib/loom/runner/execution_context.rb +9 -0
  24. data/lib/loom/{dsl.rb → runner/sshkit_connector.rb} +5 -7
  25. data/lib/loom/runner.rb +46 -33
  26. data/lib/loom/shell/api.rb +3 -3
  27. data/lib/loom/shell/cmd_wrapper.rb +5 -2
  28. data/lib/loom/shell/core.rb +16 -16
  29. data/lib/loom/shell.rb +4 -2
  30. data/lib/loom/version.rb +1 -1
  31. data/lib/loomext/coremods/exec.rb +1 -0
  32. data/lib/loomext/coremods/systemd/all.rb +1 -0
  33. data/lib/loomext/coremods/systemd/hostname.rb +10 -0
  34. data/loom.TAGS +797 -0
  35. data/loom.gemspec +4 -3
  36. data/spec/.loom/error_handling.loom +1 -0
  37. data/spec/.loom/fail.loom +27 -13
  38. data/spec/.loom/files.loom +1 -0
  39. data/spec/.loom/inventory.yml +3 -0
  40. data/spec/.loom/net.loom +1 -0
  41. data/spec/.loom/pattern_context.loom +1 -0
  42. data/spec/.loom/pkg.loom +1 -0
  43. data/spec/.loom/regression.loom +23 -0
  44. data/spec/.loom/shell.loom +1 -0
  45. data/spec/.loom/test.loom +21 -4
  46. data/spec/.loom/user.loom +1 -0
  47. data/spec/.loom/vms.loom +1 -0
  48. data/spec/loom/host_spec_spec.rb +1 -1
  49. data/spec/loom/pattern/dsl_spec.rb +3 -2
  50. data/spec/shared/loom_internals_helper.rb +1 -1
  51. data/spec/test_loom_spec.rb +102 -42
  52. data/test +15 -0
  53. metadata +40 -17
@@ -1,22 +1,25 @@
1
1
  module Loom::Pattern
2
+ # See Loom::Pattern::Pattern for the difference between refs and patterns.
2
3
  class Reference
3
4
 
4
- attr_reader :slug, :source_file, :desc
5
+ attr_reader :slug, :source_file, :desc, :pattern
5
6
 
6
- def initialize(slug, unbound_method, source_file, definition_ctx, description)
7
+ def initialize(slug, pattern, source_file, definition_ctx)
7
8
  @slug = slug
8
- @unbound_method = unbound_method
9
9
  @source_file = source_file
10
10
  @definition_ctx = definition_ctx
11
- @desc = description
11
+ @desc = pattern.description
12
+ @pattern = pattern
12
13
  end
13
14
 
14
- def is_expanding?
15
- false
15
+ # Used by Loom::Pattern::Loader to expand weaves. A Pattern::Reference it
16
+ # just expands to itself.
17
+ def expand_slugs
18
+ @slug
16
19
  end
17
20
 
18
21
  def call(shell_api, host_fact_set)
19
- run_context = RunContext.new @unbound_method, @definition_ctx
22
+ run_context = RunContext.new @pattern, @definition_ctx
20
23
 
21
24
  fact_set = @definition_ctx.fact_set host_fact_set
22
25
  Loom.log.debug5(self) {
@@ -30,7 +33,7 @@ module Loom::Pattern
30
33
  begin
31
34
  run_context.run shell_api, fact_set
32
35
  rescue => e
33
- error_msg = "error executing '#{slug}' in #{source_file} => #{e} \n%s"
36
+ error_msg = "error executing '#{slug}' in #{source_file} =>\n\t#{e}\n%s"
34
37
  Loom.log.error(error_msg % e.backtrace.join("\n\t"))
35
38
  raise
36
39
  end
@@ -42,8 +45,8 @@ module Loom::Pattern
42
45
  # A small class to bind the unbound_method to and provide context
43
46
  # in the case of errors.
44
47
  class RunContext
45
- def initialize(unbound_method, definition_ctx)
46
- @bound_method = unbound_method.bind self
48
+ def initialize(pattern, definition_ctx)
49
+ @pattern = pattern
47
50
  @definition_ctx = definition_ctx
48
51
  end
49
52
 
@@ -55,7 +58,7 @@ module Loom::Pattern
55
58
  Loom.log.debug1(self) { "before hooks => #{before_hooks}"}
56
59
  before_hooks.each do |hook|
57
60
  Loom.log.debug2(self) { "executing before hook => #{hook}"}
58
- self.instance_exec shell_api, fact_set, &hook.block
61
+ instance_exec shell_api, fact_set, &hook.block
59
62
  end
60
63
 
61
64
  # This is the entry point into calling patterns.
@@ -64,14 +67,15 @@ module Loom::Pattern
64
67
  Loom.log.debug1(self) { "after hooks => #{after_hooks}" }
65
68
  after_hooks.each do |hook|
66
69
  Loom.log.debug2(self) { "executing after hook => #{hook}"}
67
- self.instance_exec shell_api, fact_set, &hook.block
70
+ instance_exec shell_api, fact_set, &hook.block
68
71
  end
69
72
  end
70
73
  end
71
74
 
72
75
  private
76
+
73
77
  def apply_pattern(*args)
74
- @bound_method.call *args
78
+ instance_exec(*args, &@pattern.pattern_block)
75
79
  end
76
80
  end
77
81
 
@@ -3,10 +3,13 @@
3
3
  # into a reference set. There's also a lot of dark magic w/ generating
4
4
  # stacktraces to get the correct .loom file line (but I forget if that's done
5
5
  # here or in pattern/reference.rb maybe?)
6
+
7
+ # NB: The use of the word "mod" or "module" in this file probably means a
8
+ # ::Module, not a Loom::Mods::Module
6
9
  module Loom::Pattern
7
10
 
8
11
  DuplicatePatternRef = Class.new Loom::LoomError
9
- UnknownPatternMethod = Class.new Loom::LoomError
12
+ NoReferenceForSlug = Class.new Loom::LoomError
10
13
  InvalidPatternNamespace = Class.new Loom::LoomError
11
14
 
12
15
  ##
@@ -16,7 +19,11 @@ module Loom::Pattern
16
19
  class << self
17
20
  def load_from_file(path)
18
21
  Loom.log.debug1(self) { "loading patterns from file => #{path}" }
19
- Builder.create File.read(path), path
22
+ builder(File.read(path), path).build
23
+ end
24
+
25
+ def builder(file_src, file_path)
26
+ Builder.create(file_src, file_path)
20
27
  end
21
28
  end
22
29
 
@@ -34,21 +41,20 @@ module Loom::Pattern
34
41
 
35
42
  def get_pattern_ref(slug)
36
43
  ref = @slug_to_ref_map[slug]
37
- raise UnknownPatternMethod, slug unless ref
44
+ raise NoReferenceForSlug, slug unless ref
38
45
  ref
39
46
  end
40
47
  alias_method :[], :get_pattern_ref
41
48
 
42
49
  def merge!(ref_set)
43
- self.add_pattern_refs(ref_set.pattern_refs)
50
+ add_pattern_refs(ref_set.pattern_refs)
44
51
  end
45
52
 
46
53
  def add_pattern_refs(refs)
47
- map = @slug_to_ref_map
48
54
  refs.each do |ref|
49
55
  Loom.log.debug2(self) { "adding ref to set => #{ref.slug}" }
50
- raise DuplicatePatternRef, ref.slug if map[ref.slug]
51
- map[ref.slug] = ref
56
+ raise DuplicatePatternRef, ref.slug if @slug_to_ref_map[ref.slug]
57
+ @slug_to_ref_map[ref.slug] = ref
52
58
  end
53
59
  end
54
60
 
@@ -57,83 +63,95 @@ module Loom::Pattern
57
63
 
58
64
  class << self
59
65
  def create(ruby_code, source_file)
66
+ # Creates an anonymous parent module in which to evaluate the .loom
67
+ # file src. This module acts as a global context for the .loom file.
68
+ # TODO: How should this be hardened?
60
69
  shell_module = Module.new
61
70
  shell_module.include Loom::Pattern
62
71
  # TODO: I think this is my black magic for capturing stacktrace
63
72
  # info... I forget the details. Add documentation.
73
+ # TODO: This is where I would need to hack into to auto-include
74
+ # Loom::Pattern in .loom file modules
64
75
  shell_module.module_eval ruby_code, source_file, 1
65
76
  shell_module.namespace ""
66
77
 
67
- self.new(shell_module, source_file).build
78
+ self.new shell_module, source_file
68
79
  end
69
80
  end
70
81
 
71
82
  def initialize(shell_module, source_file)
72
83
  @shell_module = shell_module
73
- @pattern_mod_specs = pattern_mod_specs
74
84
  @source_file = source_file
75
85
  end
76
86
 
77
87
  def build
78
88
  ref_set = ReferenceSet.new
89
+
90
+ dsl_specs = create_dsl_specs
91
+ pattern_refs = create_pattern_refs dsl_specs, ref_set
92
+
79
93
  ref_set.add_pattern_refs pattern_refs
80
94
  ref_set
81
95
  end
82
96
 
83
97
  private
84
- def pattern_refs
85
- @pattern_mod_specs.map { |mod_spec| refs_for_mod_spec mod_spec }.flatten
98
+ # TODO: I don't like passing in the ref set build target here.... but
99
+ # ExpandingReference needs it... Can I do this w/o an instance variable
100
+ # and w/o plumbing the param through methods?
101
+ def create_pattern_refs(dsl_specs, builder_target_ref_set)
102
+ dsl_specs.flat_map do |dsl_spec|
103
+ create_refs_for_dsl_spec dsl_spec, builder_target_ref_set
104
+ end
86
105
  end
87
106
 
88
- def refs_for_mod_spec(mod_spec)
89
- mod = mod_spec[:module]
90
- context = context_for_mod_spec mod_spec
107
+ def create_refs_for_dsl_spec(dsl_spec, builder_target_ref_set)
108
+ context = create_defn_context_for_dsl_spec dsl_spec
91
109
 
92
- mod_spec[:pattern_methods].map do |m|
93
- slug = compute_slug mod_spec[:namespace_list], m
110
+ dsl_spec[:dsl_builder].patterns.map do |pattern|
111
+ slug = compute_slug dsl_spec[:namespace_list], pattern.name
94
112
 
95
- if mod.is_weave?(m)
113
+ case pattern.kind
114
+ when :weave
96
115
  Loom.log.debug2(self) { "adding ExpandingReference for weave: #{slug}" }
97
- build_expanding_reference(m, slug, mod)
116
+ create_expanding_reference(pattern, slug, builder_target_ref_set)
98
117
  else
99
118
  Loom.log.debug2(self) { "adding Reference for pattern: #{slug}" }
100
- build_pattern_reference(m, slug, context, mod)
119
+ create_pattern_reference(pattern, slug, context)
101
120
  end
102
121
  end
103
122
  end
104
123
 
105
- def build_expanding_reference(weave_name, slug, mod)
106
- desc = mod.pattern_description weave_name
124
+ def create_expanding_reference(pattern, slug, ref_set)
125
+ desc = pattern.description
107
126
  Loom.log.warn "no descripiton for weave => #{slug}" unless desc
108
127
 
109
- ExpandingReference.new slug, mod.weave_slugs[weave_name], @source_file, desc
128
+ ExpandingReference.new slug, pattern, ref_set
110
129
  end
111
130
 
112
- def build_pattern_reference(pattern_name, slug, context, mod)
113
- method = mod.pattern_method pattern_name
114
- desc = mod.pattern_description pattern_name
131
+ def create_pattern_reference(pattern, slug, context)
132
+ desc = pattern.description
115
133
  Loom.log.warn "no descripiton for pattern => #{slug}" unless desc
116
134
 
117
- Reference.new slug, method, @source_file, context, desc
135
+ Reference.new slug, pattern, @source_file, context
118
136
  end
119
137
 
120
- def context_for_mod_spec(mod_spec)
121
- parents = mod_spec[:parent_modules].find_all do |mod|
122
- is_pattern_module mod
138
+ # Creates a DefinitionContext for dsl_module by flattening and mapping
139
+ # parent ::Modules to a contextualized DefinitionContext.
140
+ def create_defn_context_for_dsl_spec(dsl_spec)
141
+ parents = dsl_spec[:parent_modules].find_all do |mod|
142
+ dsl_module? mod
123
143
  end
124
144
  parent_context = parents.reduce(nil) do |parent_ctx, parent_mod|
125
- DefinitionContext.new parent_mod, parent_ctx
145
+ DefinitionContext.new parent_mod.dsl_builder, parent_ctx
126
146
  end
127
-
128
- mod = mod_spec[:module]
129
- DefinitionContext.new mod, parent_context
147
+ DefinitionContext.new dsl_spec[:dsl_builder], parent_context
130
148
  end
131
149
 
132
150
  def compute_slug(namespace_list, pattern_method_name)
133
151
  namespace_list.dup.push(pattern_method_name).join ":"
134
152
  end
135
153
 
136
- def mod_namespace_list(pattern, parent_modules)
154
+ def create_namespace_list(pattern, parent_modules)
137
155
  mods = parent_modules.dup << pattern
138
156
  mods.reduce([]) do |memo, mod|
139
157
  mod_name = if mod.respond_to?(:namespace) && mod.namespace
@@ -149,32 +167,35 @@ module Loom::Pattern
149
167
  end
150
168
  end
151
169
 
152
- def pattern_mod_specs
153
- pattern_mods = []
154
- traverse_pattern_modules @shell_module do |pattern_mod, parent_modules|
155
- Loom.log.debug2(self) { "found pattern module => #{pattern_mod}" }
156
- pattern_methods = pattern_mod.pattern_methods
170
+ def create_dsl_specs
171
+ dsl_mods = []
172
+ traverse_dsl_modules @shell_module do |dsl_mod, parent_modules|
173
+ Loom.log.debug2(self) { "found pattern module => #{dsl_mod}" }
174
+
175
+ dsl_builder = dsl_mod.dsl_builder
176
+ next if dsl_builder.patterns.empty?
157
177
 
158
- next if pattern_methods.empty?
159
- pattern_mods << {
160
- :namespace_list => mod_namespace_list(pattern_mod, parent_modules),
161
- :pattern_methods => pattern_methods,
162
- :module => pattern_mod,
163
- :parent_modules => parent_modules.dup
178
+ dsl_mods << {
179
+ namespace_list: create_namespace_list(dsl_mod, parent_modules),
180
+ dsl_builder: dsl_builder,
181
+ dsl_module: dsl_mod,
182
+ parent_modules: parent_modules
164
183
  }
165
184
  end
166
- pattern_mods
185
+ dsl_mods
167
186
  end
168
187
 
169
- def is_pattern_module(mod)
188
+ def dsl_module?(mod)
170
189
  mod.included_modules.include? Loom::Pattern
171
190
  end
172
191
 
173
- def traverse_pattern_modules(mod, pattern_parents=[], visited={}, &block)
192
+ # Recursive method to walk the tree of dsl_builders representative of the
193
+ # .loom file pattern modules
194
+ def traverse_dsl_modules(mod, pattern_parents=[], visited={}, &block)
174
195
  return if visited[mod.name] # prevent cycles
175
196
  visited[mod.name] = true
176
197
 
177
- yield mod, pattern_parents.dup if is_pattern_module(mod)
198
+ yield mod, pattern_parents.dup if dsl_module?(mod)
178
199
 
179
200
  # Traverse all sub modules, even ones that aren't
180
201
  # Loom::Pattern[s], since they might contain more sub modules
@@ -185,7 +206,7 @@ module Loom::Pattern
185
206
 
186
207
  pattern_parents << mod
187
208
  sub_modules.each do |sub_mod|
188
- traverse_pattern_modules sub_mod, pattern_parents.dup, visited, &block
209
+ traverse_dsl_modules sub_mod, pattern_parents.dup, visited, &block
189
210
  end
190
211
  end
191
212
  end
@@ -0,0 +1,2 @@
1
+ require_relative "execution_context"
2
+ require_relative "sshkit_connector"
@@ -0,0 +1,9 @@
1
+ module Loom::RunnerModule
2
+ class ExecutionContext
3
+ attr_reader :module_accesses, :fact_sets
4
+
5
+ def initialize(shell_api)
6
+ end
7
+ end
8
+ end
9
+
@@ -5,12 +5,8 @@ require 'socket'
5
5
  # https://github.com/capistrano/sshkit/blob/master/lib/sshkit/backends/abstract.rb
6
6
  # https://github.com/capistrano/sshkit/blob/master/lib/sshkit/backends/netssh.rb
7
7
  # https://github.com/capistrano/sshkit/blob/master/lib/sshkit/backends/local.rb
8
- module Loom
9
-
10
- # TODO: Rename this to something like SSHKitWrapper, DSL is a
11
- # terribly misinformative name.
12
- module DSL
13
-
8
+ module Loom::RunnerModule
9
+ module SSHKitConnector
14
10
  UnexpectedHostError = Class.new Loom::LoomError
15
11
  SSHConnectionError = Class.new Loom::LoomError
16
12
 
@@ -22,7 +18,9 @@ module Loom
22
18
  # +&block+ should accept an SSHKit::Backend and SSHKit::Host
23
19
  def on_host(host_specs, &run_block)
24
20
  host_specs.each do |spec|
25
- raise UnexpectedHostError, "not a HostSpec => #{spec}" unless spec.is_a? HostSpec
21
+ unless spec.is_a? Loom::HostSpec
22
+ raise UnexpectedHostError "not a HostSpec => #{spec}"
23
+ end
26
24
  end
27
25
 
28
26
  host_spec_map = host_specs.reduce({}) do |map, spec|
data/lib/loom/runner.rb CHANGED
@@ -1,10 +1,13 @@
1
1
  module Loom
2
+ # TODO: Make Loom::Runner a module and move this to
3
+ # Loom::Runner::Core. Loom::RunnerModule is set up as a temporary namespace
4
+ # until that happens.
2
5
  class Runner
3
6
 
4
7
  PatternExecutionError = Class.new Loom::LoomError
5
8
  FailFastExecutionError = Class.new PatternExecutionError
6
9
 
7
- include Loom::DSL
10
+ include Loom::RunnerModule::SSHKitConnector
8
11
 
9
12
  def initialize(loom_config, pattern_slugs=[], other_facts={})
10
13
  @pattern_slugs = pattern_slugs
@@ -73,11 +76,13 @@ module Loom
73
76
  exit 97
74
77
  rescue Loom::LoomError => e
75
78
  Loom.log.error "Loom::LoomError => #{e.inspect}, run with -d for more"
76
- Loom.log.debug e.cause.backtrace.join "\n\t"
79
+ backtrace = e.cause.nil? ? e.backtrace : e.cause.backtrace
80
+ Loom.log.debug backtrace.join "\n\t"
81
+ Loom.log.debug backtrace.join "\n\t"
77
82
  exit 98
78
83
  rescue => e
79
- Loom.log.fatal "fatal error => #{e.inspect}"
80
- Loom.log.fatal e.backtrace.join "\n\t"
84
+ # TODO: Make error/exception logging look like this everywhere
85
+ Loom.log.fatal "fatal error =>\n#{e.inspect}\n\t#{e.backtrace.join("\n\t\t")}"
81
86
 
82
87
  loom_files = @loom_config.files.loom_files
83
88
  loom_errors = e.backtrace.select { |line| line =~ /(#{loom_files.join("|")})/ }
@@ -119,7 +124,7 @@ module Loom
119
124
  @pattern_refs = pattern_loader.patterns @pattern_slugs
120
125
 
121
126
  @loom_config[:loomfile_autoloads].each do |path|
122
- Loom.log.debug { "autoloading: #{path}" }
127
+ Loom.log.debug { "requiring config[:loomfile_autoloads]: #{path}" }
123
128
  require path
124
129
  end
125
130
  @mod_loader = Loom::Mods::ModLoader.new @loom_config
@@ -135,40 +140,18 @@ module Loom
135
140
 
136
141
  begin
137
142
  @pattern_refs.each do |pattern_ref|
138
- slug = pattern_ref.slug
139
- pattern_description = "[#{hostname} => #{slug}]"
140
-
141
143
  if @caught_sig_int
142
- Loom.log.warn "caught SIGINT, skipping #{pattern_description}"
144
+ Loom.log.warn "caught SIGINT, skipping #{log_token(pattern_ref, hostname)}"
143
145
  next
144
146
  elsif inventory_list.disabled? hostname
145
147
  Loom.log.warn "host disabled due to previous failure, " +
146
- "skipping: #{pattern_description}"
148
+ "skipping: #{log_token(pattern_ref, hostname)}"
147
149
  next
148
150
  end
149
151
 
150
- Loom.log.debug "collecting facts for => #{pattern_description}"
151
- # Collects facts for each pattern run on each host, this way if one
152
- # pattern run updates would be facts, the next pattern will see the
153
- # new fact.
154
- fact_shell = Loom::Shell.create @mod_loader, sshkit_backend, dry_run
155
- fact_set = Loom::Facts.fact_set(host_spec, fact_shell, @loom_config)
156
- .merge @other_facts
157
-
158
- Loom.log.info "running pattern => #{pattern_description}"
159
- # Creates a new shell for executing the pattern, so as not to be
160
- # tainted by the fact finding shell above.
161
- pattern_shell = Loom::Shell.create @mod_loader, sshkit_backend, dry_run
162
-
163
- Loom.log.warn "dry run only => #{pattern_description}" if dry_run
164
- failures = execute_pattern pattern_ref, pattern_shell, fact_set
165
-
166
- if failures.empty?
167
- Loom.log.debug "success on => #{pattern_description}"
168
- else
169
- Loom.log.error "failures on => #{pattern_description}:\n\t%s" % (
170
- failures.join("\n\t"))
171
- end
152
+ # wip-patternbuilder is adding this feature
153
+ # run_analysis_phase(pattern_ref, host_spec, sshkit_backend)
154
+ run_execution_phase(pattern_ref, host_spec, sshkit_backend, dry_run)
172
155
  end
173
156
  rescue IOError => e
174
157
  # TODO: Try to patch SSHKit for a more specific error for unexpected
@@ -184,6 +167,32 @@ module Loom
184
167
  end
185
168
  end
186
169
 
170
+ def run_execution_phase(pattern_ref, host_spec, sshkit_backend, dry_run)
171
+ log_token = log_token(pattern_ref, host_spec.hostname)
172
+ Loom.log.debug "collecting facts for => #{log_token}"
173
+ # Collects facts for each pattern run on each host, this way if one
174
+ # pattern run updates would be facts, the next pattern will see the
175
+ # new fact.
176
+ fact_shell = Loom::Shell.create @mod_loader, sshkit_backend, dry_run
177
+ fact_set = Loom::Facts.fact_set(host_spec, fact_shell, @loom_config)
178
+ .merge @other_facts
179
+
180
+ Loom.log.info "running pattern => #{log_token}"
181
+ # Creates a new shell for executing the pattern, so as not to be
182
+ # tainted by the fact finding shell above.
183
+ pattern_shell = Loom::Shell.create @mod_loader, sshkit_backend, dry_run
184
+
185
+ Loom.log.warn "dry run only => #{log_token}" if dry_run
186
+ failures = execute_pattern pattern_ref, pattern_shell, fact_set
187
+
188
+ if failures.empty?
189
+ Loom.log.debug "success on => #{log_token}"
190
+ else
191
+ Loom.log.error "failures on => #{log_token}:\n\t%s" % (
192
+ failures.join("\n\t"))
193
+ end
194
+ end
195
+
187
196
  def execute_pattern(pattern_ref, shell, fact_set)
188
197
  shell_session = shell.session
189
198
  hostname = fact_set.hostname
@@ -218,7 +227,11 @@ module Loom
218
227
  run_failure
219
228
  end
220
229
 
221
- private
230
+ def log_token(pattern_ref, hostname)
231
+ slug = pattern_ref.slug
232
+ "[#{hostname} => #{slug}]"
233
+ end
234
+
222
235
  def handle_host_failure_strategy(hostname, failure_summary=nil)
223
236
  failure_strategy = @loom_config.run_failure_strategy.to_sym
224
237
 
@@ -20,14 +20,14 @@ module Loom::Shell
20
20
  end
21
21
 
22
22
  # This is the entry point for `loom.foo` calls from .loom files.
23
- def method_missing(name, *args, &block)
24
- Loom.log.debug3(self) { "shell api => #{name} #{args} #{block}" }
23
+ def method_missing(name, *args, **kwargs, &block)
24
+ Loom.log.debug3(self) { "shell api => #{name} #{args} #{kwargs} #{block}" }
25
25
  # TODO: The relationship between shell and mod_loader seems leaky here, a
26
26
  # Shell::Api should have a shell and not care about the mod_loader,
27
27
  # currently it seems to violate Demeter. The shell should dispatch to the
28
28
  # mod_loader only as an implementation detail. Otherwise this is harder to
29
29
  # test.
30
- @mod_loader.send name, @shell, *args, &block
30
+ @mod_loader.send name, @shell, *args, **kwargs, &block
31
31
  end
32
32
  end
33
33
 
@@ -1,4 +1,4 @@
1
- require "shellwords"
1
+ require "shellwords"
2
2
 
3
3
  module Loom::Shell
4
4
 
@@ -27,7 +27,7 @@ module Loom::Shell
27
27
  parts
28
28
  end
29
29
  end
30
- CmdWrapper.new *cmd_parts.flatten, {
30
+ CmdWrapper.new *cmd_parts.flatten, **{
31
31
  :should_quote => should_quote,
32
32
  :is_wrapped => true
33
33
  }
@@ -39,6 +39,9 @@ module Loom::Shell
39
39
  # @param :redirc [Array<CmdRedirect>] STDIO redirection for the command
40
40
  # in quotes.
41
41
  def initialize(*cmd, should_quote: false, is_wrapped: false, redirect: [])
42
+ if cmd.last.is_a?(Hash)
43
+ raise ArgumentError.new "kwargs mixed into cmd"
44
+ end
42
45
  @cmd_parts = cmd.flatten
43
46
  @should_quote = should_quote
44
47
  @is_wrapped = is_wrapped
@@ -168,22 +168,22 @@ module Loom::Shell
168
168
  cmd_parts.compact!
169
169
  raise "empty command passed to execute" if cmd_parts.empty?
170
170
 
171
- result = if @dry_run
172
- wrap :printf, :first => true do
173
- cmd_result = execute_internal *cmd_parts, **cmd_opts
174
- Loom.log.info do
175
- "\t%s" % prompt_fmt(cmd_result.full_stdout.strip)
176
- end
177
- cmd_result
178
- end
179
- else
180
- execute_internal *cmd_parts, **cmd_opts
181
- end
182
- @session << CmdResult.create_from_sshkit_command(result, is_test, self)
183
-
184
- Loom.log.debug @session.last.stdout unless @session.last.stdout.empty?
185
- Loom.log.debug @session.last.stderr unless @session.last.stderr.empty?
186
- @session.last
171
+ sshkit_result = if @dry_run
172
+ wrap(:printf, first: true) do
173
+ r = execute_internal(*cmd_parts, **cmd_opts)
174
+ Loom.log.info { "\t%s" % prompt_fmt(r.full_stdout.strip) }
175
+ r
176
+ end
177
+ else
178
+ execute_internal(*cmd_parts, **cmd_opts)
179
+ end
180
+ result =
181
+ CmdResult.create_from_sshkit_command(sshkit_result, is_test, self)
182
+
183
+ @session << result
184
+ Loom.log.debug result.stdout unless result.stdout.empty?
185
+ Loom.log.debug result.stderr unless result.stderr.empty?
186
+ result
187
187
  end
188
188
  alias_method :exec, :execute
189
189
 
data/lib/loom/shell.rb CHANGED
@@ -3,8 +3,10 @@ module Loom
3
3
 
4
4
  VerifyError = Class.new Loom::LoomError
5
5
 
6
- def self.create(*args)
7
- Loom::Shell::Core.new *args
6
+ class << self
7
+ def create(mod_loader, sshkit_backend, dry_run, no_exec: false)
8
+ Loom::Shell::Core.new mod_loader, sshkit_backend, dry_run
9
+ end
8
10
  end
9
11
  end
10
12
  end
data/lib/loom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Loom
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.9"
3
3
  end
@@ -62,6 +62,7 @@ module LoomExt::CoreMods
62
62
 
63
63
  class Fail < Loom::Mods::Module
64
64
  register_mod :fail do |message=nil|
65
+ # TODO: create a stacktrace that indicates the raise happened from the .loom file.
65
66
  raise FailError, message
66
67
  end
67
68
  end
@@ -1,2 +1,3 @@
1
1
  require_relative "systemd"
2
+ require_relative "hostname"
2
3
  require_relative "systemd_units"
@@ -0,0 +1,10 @@
1
+ module LoomExt::CoreMods
2
+
3
+ class Hostname < Loom::Mods::Module
4
+ include SystemdCommon
5
+
6
+ register_mod :sethostname do |**opts|
7
+ shell.execute :hostnamectl, **opts
8
+ end
9
+ end
10
+ end