Class: Chef::TrainTransport

Inherits:
Object
  • Object
show all
Extended by:
ChefConfig::Mixin::Credentials
Defined in:
lib/chef/train_transport.rb

Class Method Summary collapse

Class Method Details

.build_transport(logger = Chef::Log.with_child(subsystem: "transport")) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/chef/train_transport.rb', line 94

def self.build_transport(logger = Chef::Log.with_child(subsystem: "transport"))
  return nil unless Chef::Config.target_mode?

  # TODO: Consider supporting parsing the protocol from a URI passed to `--target`
  #
  train_config = {}

  # Load the target_mode config context from Chef::Config, and place any valid settings into the train configuration
  tm_config = Chef::Config.target_mode
  protocol = tm_config.protocol
  train_config = tm_config.to_hash.select { |k| Train.options(protocol).key?(k) }
  Chef::Log.trace("Using target mode options from #{Chef::Dist::PRODUCT} config file: #{train_config.keys.join(", ")}") if train_config

  # Load the credentials file, and place any valid settings into the train configuration
  credentials = load_credentials(tm_config.host)
  if credentials
    valid_settings = credentials.select { |k| Train.options(protocol).key?(k) }
    valid_settings[:enable_password] = credentials[:enable_password] if credentials.key?(:enable_password)
    train_config.merge!(valid_settings)
    Chef::Log.trace("Using target mode options from credentials file: #{valid_settings.keys.join(", ")}") if valid_settings
  end

  train_config[:logger] = logger

  # Train handles connection retries for us
  Train.create(protocol, train_config)
rescue SocketError => e # likely a dns failure, not caught by train
  e.message.replace "Error connecting to #{train_config[:target]} - #{e.message}"
  raise e
rescue Train::PluginLoadError
  logger.error("Invalid target mode protocol: #{protocol}")
  exit(false)
end

.contains_split_fqdn?(hash, fqdn) ⇒ Boolean

Toml creates hashes when a key is separated by periods, e.g.

host.example.org

> { host: { example: { org: {} } } }

Returns true if the above example is true

A hostname has to be specified as [‘host.example.org’] This will be a common mistake so we should catch it

Returns:

  • (Boolean)


53
54
55
56
57
58
59
60
61
62
# File 'lib/chef/train_transport.rb', line 53

def self.contains_split_fqdn?(hash, fqdn)
  fqdn.split(".").reduce(hash) do |h, k|
    v = h[k]
    if Hash === v
      v
    else
      break false
    end
  end
end

.credentials_file_pathObject

ChefConfig::Mixin::Credentials.credentials_file_path is designed around knife, overriding it here.

Credentials file preference:

1) target_mode.credentials_file 2) /etc/chef/TARGET_MODE_HOST/credentials 3) #credentials_file_path from parent ($HOME/.chef/credentials)

Raises:

  • (ArgumentError)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/chef/train_transport.rb', line 73

def self.credentials_file_path
  tm_config = Chef::Config.target_mode
  profile = tm_config.host

  credentials_file =
    if tm_config.credentials_file && File.exist?(tm_config.credentials_file)
      tm_config.credentials_file
    elsif File.exist?(Chef::Config.platform_specific_path("#{Chef::Dist::CONF_DIR}/#{profile}/credentials"))
      Chef::Config.platform_specific_path("#{Chef::Dist::CONF_DIR}/#{profile}/credentials")
    else
      super
    end

  raise ArgumentError, "No credentials file found for target '#{profile}'" unless credentials_file
  raise ArgumentError, "Credentials file specified for target mode does not exist: '#{credentials_file}'" unless File.exist?(credentials_file)

  Chef::Log.debug("Loading credentials file '#{credentials_file}' for target '#{profile}'")

  credentials_file
end

.load_credentials(profile) ⇒ Object

Returns a RFC099 credentials profile as a hash



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/chef/train_transport.rb', line 29

def self.load_credentials(profile)
  # Tomlrb.load_file returns a hash with keys as strings
  credentials = parse_credentials_file
  if contains_split_fqdn?(credentials, profile)
    Chef::Log.warn("Credentials file #{credentials_file_path} contains target '#{profile}' as a Hash, expected a string.")
    Chef::Log.warn("Hostnames must be surrounded by single quotes, e.g. ['host.example.org']")
  end

  # host names must be specified in credentials file as ['foo.example.org'] with quotes
  if !credentials.nil? && !credentials[profile].nil?
    credentials[profile].map { |k, v| [k.to_sym, v] }.to_h # return symbolized keys to match Train.options()
  else
    nil
  end
end