Module: ChefConfig::Mixin::TrainTransport

Includes:
Credentials
Defined in:
lib/chef-config/mixin/train_transport.rb

Constant Summary

Constants included from Credentials

Credentials::GLOBAL_CONFIG_HASHES, Credentials::SUPPORTED_SECRETS_PROVIDERS

Instance Attribute Summary collapse

Attributes included from Credentials

#credentials_config

Instance Method Summary collapse

Methods included from Credentials

#credentials_profile, #default_secrets_provider, #global_options, #parse_credentials_file, #resolve_secret, #resolve_secret_hashicorp, #resolve_secrets, #valid_secrets_provider?

Instance Attribute Details

#loggerObject

Returns the value of attribute logger.



28
29
30
# File 'lib/chef-config/mixin/train_transport.rb', line 28

def logger
  @logger
end

Instance Method Details

#build_transportObject



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/chef-config/mixin/train_transport.rb', line 110

def build_transport
  return nil unless config.target_mode?

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

  # Load the target_mode config context from config, and place any valid settings into the train configuration
  tm_config = config.target_mode

  # Load the credentials file, and place any valid settings into the train configuration
  credentials = load_credentials(tm_config.host)

  protocol = credentials&.dig(:transport_protocol) || tm_config.protocol
  train_config = tm_config.to_hash.select { |k| Train.options(protocol).key?(k) }
  logger.trace("Using target mode options from #{ChefUtils::Dist::Infra::PRODUCT} config file: #{train_config.keys.join(", ")}") if train_config

  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)
    logger.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]} via #{protocol} - #{e.message}"
  raise e
rescue Train::PluginLoadError
  logger.error("Invalid target mode protocol: #{protocol}")
  exit(1)
end

#configObject

Raises:

  • (NotImplementedError)


146
147
148
# File 'lib/chef-config/mixin/train_transport.rb', line 146

def config
  raise NotImplementedError
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)


63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/chef-config/mixin/train_transport.rb', line 63

def contains_split_fqdn?(hash, fqdn)
  return unless fqdn.include?(".")

  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) environment variable CHEF_CREDENTIALS_FILE 2) target_mode.credentials_file 3) /etc/chef/TARGET_MODE_HOST/credentials 4) user configuration ($HOME/.chef/target_credentials)

Raises:

  • (ArgumentError)


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/chef-config/mixin/train_transport.rb', line 86

def credentials_file_path
  tm_config = config.target_mode
  profile = tm_config.host

  env_file = ENV["CHEF_CREDENTIALS_FILE"]
  credentials_file =
    if env_file && File.exist?(env_file)
      env_file
    elsif tm_config.credentials_file && File.exist?(tm_config.credentials_file)
      tm_config.credentials_file
    elsif File.exist?(config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/#{profile}/credentials"))
      config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/#{profile}/credentials")
    else
      PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR, "target_credentials").freeze
    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)

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

  credentials_file
end

#initialize(logger) ⇒ Object



30
31
32
# File 'lib/chef-config/mixin/train_transport.rb', line 30

def initialize(logger)
  @logger = logger
end

#load_credentials(profile) ⇒ Object

Returns a RFC099 credentials profile as a hash



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/chef-config/mixin/train_transport.rb', line 37

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

  resolve_secrets(profile)

  # host names must be specified in credentials file as ['foo.example.org'] with quotes
  if !credentials_config.nil? && !credentials_config[profile].nil?
    credentials_config[profile].transform_keys(&:to_sym) # return symbolized keys to match Train.options()
  else
    nil
  end
end