Class: QB::Ansible::Module

Inherits:
Object
  • Object
show all
Includes:
NRSER::Log::Mixin, NRSER::Props::Immutable::InstanceVariables
Defined in:
lib/qb/ansible/module.rb

Direct Known Subclasses

QB::Ansible::Modules::Docker::Image

Defined Under Namespace

Modules: Formatters Classes: Response

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(values = {}) ⇒ Module

Construction



380
381
382
383
# File 'lib/qb/ansible/module.rb', line 380

def initialize values = {}
  initialize_props values
  @response = QB::Ansible::Module::Response.new
end

Instance Attribute Details

#argsHash<String, VALUE>

TODO:

May want to get rid of this once using props is totally flushed out.

It should at least be deal with in the constructor somehow so this can be changed to an attr_reader.

The raw parsed arguments. Used for backwards-compatibility with how QB::Ansible::Module used to work before NRSER::Props and #arg.

Returns:

  • (Hash<String, VALUE>)


366
367
368
# File 'lib/qb/ansible/module.rb', line 366

def args
  @args
end

#args_sourcenil | Hash<Symbol, Object>

Optional information on the source of the arguments.

Returns:

  • (nil | Hash<Symbol, Object>)


352
353
354
# File 'lib/qb/ansible/module.rb', line 352

def args_source
  @args_source
end

#responseQB::Ansible::Module::Response (readonly)

The response that will be returned to Ansible (JSON-encoded and written to STDOUT).



374
375
376
# File 'lib/qb/ansible/module.rb', line 374

def response
  @response
end

Class Method Details

.arg(*args, **opts) ⇒ return_type

TODO:

Document arg method.

Returns @todo Document return value.

Parameters:

  • arg_name (type)

    @todo Add name param description.

Returns:

  • (return_type)

    @todo Document return value.



333
334
335
336
337
338
339
340
341
342
# File 'lib/qb/ansible/module.rb', line 333

def self.arg *args, **opts
  name, opts = t.match args.length,
    # Normal {.prop} form
    1, ->( _ ){ [ args[0], opts ] },
    
    # Backwards-compatible form
    2, ->( _ ){ [ args[0], opts.merge( type: args[1] ) ]  }
  
  prop name, **opts
end

.load_argsObject

Load the raw arguments.



245
246
247
248
249
250
251
# File 'lib/qb/ansible/module.rb', line 245

def self.load_args
  if WANT_JSON_mode?
    load_args_from_JSON_file ARGV[0]
  else
    load_args_from_CLI_options
  end
end

.load_args_from_JSON_file(file_path) ⇒ Array<(Hash, Hash?)>

Load args from a file in JSON format.

Parameters:

  • file_path (String | Pathname)

    File path to load from.

Returns:

  • (Array<(Hash, Hash?)>)

    Tuple of:

    1. args:
      • Hash<String, *>
    2. args_source:
      • nil | Hash{ type: :file, path: String, contents: String }


220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/qb/ansible/module.rb', line 220

def self.load_args_from_JSON_file file_path
  file_contents = File.read file_path

  args = JSON.load( file_contents ).with_indifferent_access

  t.hash_( keys: t.str ).check( args ) do |type:, value:|
    binding.erb <<~END
      JSON file contents must load into a `Hash<String, *>`
      
      Loaded value (of class <%= value.class %>):
      
          <%= value.pretty_inspect %>
      
    END
  end
  
  [ args, { type: :file,
            path: file_path.to_s,
            contents: file_contents,
          } ]
end

.run!Object

Run the module!



258
259
260
261
262
263
264
265
# File 'lib/qb/ansible/module.rb', line 258

def self.run!
  handle_run_error do
    setup_io!
    
    args, args_source = load_args
    run_from_args! args, args_source: args_source
  end
end

.run_from_args!(args, args_source: nil) ⇒ Object

Run from a hash-like of argument names mapped to values, with optional info about the source of the arguments.

Parameters:

  • args (#each_pair)

    Argument names (String or Symbol) mapped to their value data.



313
314
315
316
317
318
319
320
321
322
# File 'lib/qb/ansible/module.rb', line 313

def self.run_from_args! args, args_source: nil
  logger.trace "Running from args",
    args: args,
    args_source: args_source
  
  instance = self.from_data args
  instance.args_source = args_source
  instance.args = args
  instance.run!
end

.run_from_JSON_args_file!(file_path) ⇒ Object

Create and run an instance and populate it's args by loading JSON from a file path.

Used to run via Ansible's "WANT_JSON" mode.

Parameters:

  • file_path (String | Pathname)

    Path to the JSON file containing the args.

See Also:



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/qb/ansible/module.rb', line 280

def self.run_from_JSON_args_file! file_path
  file_contents = File.read file_path
  
  args = JSON.load file_contents
  
  t.hash_( keys: t.str ).check( args ) do |type:, value:|
    binding.erb <<~END
      JSON file contents must load into a `Hash<String, *>`
      
      Loaded value (of class <%= value.class %>):
      
          <%= value.pretty_inspect %>
      
    END
  end
  
  run_from_args!  args,
                  args_source: {
                    type: :file,
                    path: file_path,
                    contents: file_contents,
                  }
end

.setup_io!Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/qb/ansible/module.rb', line 118

def self.setup_io!
  # Initialize
  $qb_stdio_client ||= QB::IPC::STDIO::Client.new.connect!
  
  if $qb_stdio_client.log.connected? && NRSER::Log.appender.nil?
    # SemanticLogger::Processor.logger = \
    
    SemanticLogger::Processor.instance.appender.logger = \
      SemanticLogger::Appender::File.new(
        io: $stderr,
        level: :warn,
        formatter: Formatters::Processor.new,
      )
    
    NRSER::Log.setup! \
      application: 'qb',
      sync: true,
      dest: {
        io: $qb_stdio_client.log.socket,
        formatter: Formatters::JSON.new,
      }
  end
  
end

.WANT_JSON_mode?(argv = ARGV) ⇒ Boolean

Is the module being run from Ansible via it's "WANT_JSON" mode?

Tests if argv is a single string argument that is a file path.

Parameters:

  • argv (Array<String>) (defaults to: ARGV)

    The CLI argument strings.

Returns:

  • (Boolean)

    true if argv looks like it came from Ansible's "WANT_JSON" mode.

See Also:



202
203
204
# File 'lib/qb/ansible/module.rb', line 202

def self.WANT_JSON_mode? argv = ARGV
  ARGV.length == 1 && File.file?( ARGV[0] )
end

Instance Method Details

#changed!(facts = {}) ⇒ Object



470
471
472
473
474
475
476
477
478
# File 'lib/qb/ansible/module.rb', line 470

def changed! facts = {}
  response.changed = true
  
  unless facts.empty?
    response.facts.merge! facts
  end
  
  done
end

#debug(*args) ⇒ Object

Forward args to QB.debug if we are connected to a QB STDERR stream (write to STDERR).

Parameters:

  • args

    see QB.debug



413
414
415
# File 'lib/qb/ansible/module.rb', line 413

def debug *args
  logger.debug payload: args
end

#doneObject



481
482
483
# File 'lib/qb/ansible/module.rb', line 481

def done
  exit_json response.to_data( add_class: false ).compact
end

#exit_json(hash) ⇒ Object



486
487
488
489
490
491
492
# File 'lib/qb/ansible/module.rb', line 486

def exit_json hash
  # print JSON response to process' actual STDOUT (instead of $stdout,
  # which may be pointing to the qb parent process)
  STDOUT.print JSON.pretty_generate( hash.stringify_keys )
  
  exit true
end

#fail(msg, **values) ⇒ Object



495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/qb/ansible/module.rb', line 495

def fail msg, **values
  fail_response = QB::Ansible::Module::Response.new \
    failed: true,
    msg: msg.to_s,
    warnings: response.warnings,
    depreciations: response.depreciations
  
  STDOUT.print \
    JSON.pretty_generate( fail_response.to_data( add_class: false ).compact )
  
  exit false
end

#info(msg) ⇒ Object

Deprecated.

Old logging function - use #logger.info instead.



422
423
424
# File 'lib/qb/ansible/module.rb', line 422

def info msg
  logger.info msg
end

#run!Object



454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/qb/ansible/module.rb', line 454

def run!
  result = main
  
  case result
  when nil
    # pass
  when Hash
    response.facts.merge! result
  else
    raise "result of #main should be nil or Hash, found #{ result.inspect }"
  end
  
  done
end

#warn(msg) ⇒ nil

TODO:

Should be incorporated into #logger? Seems like it would need one of:

  1. on_... hooks, like Logger#on_warn, etc.

    This might be nice but I'd rather hold off on throwing more shit into NRSER::Log::Logger for the time being if possible.

  2. Adding a custom appender when we run a module that has a ref to the module instance and so it's Response.

Append a warning message to the #response's QB::Ansible::Module::Response#warnings array and log it.

Parameters:

  • msg (String)

    Non-empty string.

Returns:

  • (nil)


447
448
449
450
451
# File 'lib/qb/ansible/module.rb', line 447

def warn msg
  logger.warn msg
  response.warnings << msg
  nil
end