Class: CemAcpt::NodeInventory

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/cem_acpt/shared_objects.rb

Overview

Provides a thread-safe inventory of test nodes.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

current_log_config, #current_log_config, current_log_format, current_log_level, #current_log_level, included, #logger, new_log_config, #new_log_config, new_log_formatter, new_log_level, #new_log_level

Constructor Details

#initializeNodeInventory

Returns a new instance of NodeInventory.



167
168
169
170
171
172
173
# File 'lib/cem_acpt/shared_objects.rb', line 167

def initialize
  @inventory = Concurrent::Map.new
  @lock = Concurrent::ReadWriteLock.new
  @claimed = Concurrent::Set.new
  @save_on_claim = false
  @save_file_path = 'spec/fixtures/node_inventory.yaml'
end

Instance Attribute Details

#save_file_pathObject

Returns the value of attribute save_file_path.



165
166
167
# File 'lib/cem_acpt/shared_objects.rb', line 165

def save_file_path
  @save_file_path
end

Instance Method Details

#add(node_name, node_data) ⇒ Object

Adds a new node to the inventory.

Parameters:

  • node_name (String)

    The name of the node to add.

  • node_data (Hash)

    The node data to add.



188
189
190
# File 'lib/cem_acpt/shared_objects.rb', line 188

def add(node_name, node_data)
  @inventory.put_if_absent(node_name, node_data)
end

#claim(node_name) ⇒ String

Claims a node, which returns the node data and adds that node to the claimed set. Raises an error if the node is already claimed or does not exist. This is used during acceptance testing to ensure that nodes are not reused.

Parameters:

  • node_name (String)

    The name of the node to claim.

Returns:

  • (String)

    The name of the node that was claimed.

Raises:



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/cem_acpt/shared_objects.rb', line 232

def claim(node_name)
  @lock.with_write_lock do
    unless @inventory.keys.include?(node_name)
      raise NodeDoesNotExistError, "Node #{node_name} does not exist in inventory"
    end

    if @claimed.include?(node_name)
      raise NodeClaimedError, "Node #{node_name} is already tainted and cannot be claimed"
    end

    @claimed.add(node_name)
    save_no_lock!(@save_file_path) if @save_on_claim
    node_name
  end
end

#claim_by_property(property_path, value) ⇒ String

Claims a node based on a property of the node data and a value that property should equal. If a node is found but is already claimed, the method will continue to search for an unclaimed node. Raises an error if no valid node is found. This is used during acceptance testing to ensure that nodes are not reused.

Parameters:

  • property_path (String)

    The dot-separated property path to check.

  • value (Object)

    The value to check for.

Returns:

  • (String)

    The name of the node that was claimed.

Raises:



256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/cem_acpt/shared_objects.rb', line 256

def claim_by_property(property_path, value)
  claimed_set = @claimed.dup
  claim_name = nil
  @inventory.each_pair do |node_name, node_data|
    next if claimed_set.include?(node_name)

    claim_name = node_name if node_data.dot_dig(property_path) == value
  end
  return claim(claim_name) unless claim_name.nil?

  raise NodeDoesNotExistError, "No node found with property #{property_path} == #{value}"
end

#claimed?(node_name) ⇒ Boolean

Checks if a node is claimed.

Parameters:

  • node_name (String)

    The name of the node to check.

Returns:

  • (Boolean)

    True if the node is claimed, false otherwise.

Raises:



218
219
220
221
222
# File 'lib/cem_acpt/shared_objects.rb', line 218

def claimed?(node_name)
  raise NodeDoesNotExistError, "Node #{node_name} does not exist" unless @inventory.keys.include?(node_name)

  @claimed.include?(node_name)
end

#clear!Object

Clears the inventory and removes claimed nodes. Thread-safe.



288
289
290
291
292
# File 'lib/cem_acpt/shared_objects.rb', line 288

def clear!
  @lock.with_write_lock do
    clear_no_lock!
  end
end

#clear_no_lock!Object

Clears the inventory and removes claimed nodes. DOES NOT USE LOCK. If this is called outside of a lock, it will not be thread-safe.



282
283
284
285
# File 'lib/cem_acpt/shared_objects.rb', line 282

def clear_no_lock!
  nodes.each { |node| delete(node) }
  @claimed.clear
end

#delete(node_name) ⇒ Object

Deletes a node from the inventory.

Parameters:

  • node_name (String)

    The name of the node to delete.



271
272
273
# File 'lib/cem_acpt/shared_objects.rb', line 271

def delete(node_name)
  @inventory.delete(node_name)
end

#get(node_name) ⇒ Object

Returns the node data for a given node.

Parameters:

  • node_name (String)

    The name of the node to get data for.



194
195
196
# File 'lib/cem_acpt/shared_objects.rb', line 194

def get(node_name)
  @inventory[node_name]
end

#load(file_path = @save_file_path) ⇒ Object

Loads a node inventory from a yaml file. Thread-safe.

Parameters:

  • file_path (String) (defaults to: @save_file_path)

    The path to the file to load from.



353
354
355
356
357
# File 'lib/cem_acpt/shared_objects.rb', line 353

def load(file_path = @save_file_path)
  @lock.with_write_lock do
    load_no_lock!(file_path)
  end
end

#load_no_lock!(file_path = @save_file_path) ⇒ Object

Loads a node inventory from a file. DOES NOT USE LOCK. If this is called outside of a lock, it will not be thread-safe.



339
340
341
342
343
344
345
346
347
348
349
# File 'lib/cem_acpt/shared_objects.rb', line 339

def load_no_lock!(file_path = @save_file_path)
  require 'net/ssh/proxy/command' # If ProxyCommand is used in ssh options, this is required.
  clear_no_lock!
  YAML.load_file(file_path).each_pair do |node_name, node_data|
    if node_name == :claimed
      @claimed = Set.new(node_data)
    else
      add(node_name, node_data)
    end
  end
end

#merge!(node_name, new_node_data) ⇒ Object

Merges new node data in with the existing node data for the given node.

Parameters:

  • node_name (String)

    The name of the node to merge data for.

  • new_node_data (Hash)

    The new node data to merge.



209
210
211
212
213
# File 'lib/cem_acpt/shared_objects.rb', line 209

def merge!(node_name, new_node_data)
  @inventory.merge_pair(node_name, new_node_data) do |old_data|
    old_data.deep_merge(new_node_data)
  end
end

#nodesObject

Returns all node names in the current inventory.



276
277
278
# File 'lib/cem_acpt/shared_objects.rb', line 276

def nodes
  @inventory.keys
end

#property?(node_name, property_path) ⇒ Boolean

Checks if the inventory contains a node with the given property.

Parameters:

  • node_name (String)

    The name of the node to check.

  • property_path (String)

    The dot-separated property path to check.

Returns:

  • (Boolean)


297
298
299
# File 'lib/cem_acpt/shared_objects.rb', line 297

def property?(node_name, property_path)
  !!@inventory[node_name].dot_dig(property_path)
end

#save(file_path = @save_file_path) ⇒ Object

Saves the current node inventory to a yaml file. Thread-safe.

Parameters:

  • file_path (String) (defaults to: @save_file_path)

    The path to the file to save to.



331
332
333
334
335
# File 'lib/cem_acpt/shared_objects.rb', line 331

def save(file_path = @save_file_path)
  @lock.with_write_lock do
    save_no_lock!(file_path)
  end
end

#save_no_lock!(file_path = @save_file_path) ⇒ Object

Saves the current node inventory to a file. DOES NOT USE LOCK. If this is called outside of a lock, it will not be thread-safe.



323
324
325
326
327
# File 'lib/cem_acpt/shared_objects.rb', line 323

def save_no_lock!(file_path = @save_file_path)
  File.open(file_path, 'w') do |file|
    file.write(to_yaml)
  end
end

#save_on_claimObject

When called, enables saving the inventory to a file on claim.



176
177
178
# File 'lib/cem_acpt/shared_objects.rb', line 176

def save_on_claim
  @save_on_claim = true
end

#save_on_claim?Boolean

Checks if save_on_claim is enabled.

Returns:

  • (Boolean)


181
182
183
# File 'lib/cem_acpt/shared_objects.rb', line 181

def save_on_claim?
  @save_on_claim
end

#set(node_name, property_path, value) ⇒ Object

Sets a specific property on a node in the inventory.

Parameters:

  • node_name (String)

    The name of the node to set the property on.

  • property_path (String)

    The dot-separated property path to set.

  • value (Object)

    The value to set.



202
203
204
# File 'lib/cem_acpt/shared_objects.rb', line 202

def set(node_name, property_path, value)
  @inventory[node_name].dot_store(property_path, value)
end

#to_hObject

Returns a hash of the inventory.



302
303
304
305
306
307
308
309
# File 'lib/cem_acpt/shared_objects.rb', line 302

def to_h
  h = {}
  @inventory.each_pair do |node_name, node_data|
    h[node_name] = node_data.to_h
  end
  h[:claimed] = @claimed.to_a
  h
end

#to_json(*args) ⇒ Object

Returns a JSON string of the inventory.



317
318
319
# File 'lib/cem_acpt/shared_objects.rb', line 317

def to_json(*args)
  to_h.to_json(*args)
end

#to_yamlObject

Returns a YAML string of the inventory.



312
313
314
# File 'lib/cem_acpt/shared_objects.rb', line 312

def to_yaml
  to_h.to_yaml
end