@ -13,9 +13,9 @@ import stat
import tempfile
import traceback
from collections . abc import Mapping , Sequence
from collections import namedtuple
from collections . abc import Mapping , Sequence
from jinja2 . nativetypes import NativeEnvironment
from ansible . config . data import ConfigData
from ansible . errors import AnsibleOptionsError , AnsibleError
@ -302,7 +302,8 @@ class ConfigManager(object):
self . _parse_config_file ( )
# update constants
self . update_config_data ( )
self . update_config_data ( self . _base_defs , self . _config_file )
self . _base_defs [ ' CONFIG_FILE ' ] = { ' default ' : None , ' type ' : ' path ' }
def _read_config_yaml_file ( self , yml_file ) :
# TODO: handle relative paths as relative to the directory containing the current playbook instead of CWD
@ -447,6 +448,9 @@ class ConfigManager(object):
# use default config
cfile = self . _config_file
if config == ' CONFIG_FILE ' :
return cfile , ' '
# Note: sources that are lists listed in low to high precedence (last one wins)
value = None
origin = None
@ -457,19 +461,18 @@ class ConfigManager(object):
aliases = defs [ config ] . get ( ' aliases ' , [ ] )
# direct setting via plugin arguments, can set to None so we bypass rest of processing/defaults
direct_aliases = [ ]
if direct :
direct_aliases = [ direct [ alias ] for alias in aliases if alias in direct ]
if direct and config in direct :
if config in direct :
value = direct [ config ]
origin = ' Direct '
elif direct and direct_aliases :
else :
direct_aliases = [ direct [ alias ] for alias in aliases if alias in direct ]
if direct_aliases :
value = direct_aliases [ 0 ]
origin = ' Direct '
else:
if valu e is None and variab le s and d efs[ config ] . get ( ' vars ' ) :
# Use 'variable overrides' if present, highest precedence, but only present when querying running play
if variables and defs [ config ] . get ( ' vars ' ) :
value , origin = self . _loop_entries ( variables , defs [ config ] [ ' vars ' ] )
origin = ' var: %s ' % origin
@ -536,11 +539,16 @@ class ConfigManager(object):
raise AnsibleError ( " No setting was provided for required configuration %s " %
to_native ( _get_entry ( plugin_type , plugin_name , config ) ) )
else :
value = defs [ config ] . get ( ' default ' )
origin = ' default '
# skip typing as this is a templated default that will be resolved later in constants, which has needed vars
if plugin_type is None and isinstance ( value , string_types ) and ( value . startswith ( ' {{ ' ) and value . endswith ( ' }} ' ) ) :
return value , origin
value = defs [ config ] . get ( ' default ' )
if isinstance ( value , string_types ) and ( value . startswith ( ' {{ ' ) and value . endswith ( ' }} ' ) ) and variables is not None :
# template default values if possible
# NOTE: cannot use is_template due to circular dep
try :
t = NativeEnvironment ( ) . from_string ( value )
value = t . render ( variables )
except Exception :
pass # not templatable
# ensure correct type, can raise exceptions on mismatched types
try :
@ -598,19 +606,16 @@ class ConfigManager(object):
if defs is None :
defs = self . _base_defs
if configfile is None :
configfile = self . _config_file
if not isinstance ( defs , dict ) :
raise AnsibleOptionsError ( " Invalid configuration definition type: %s for %s " % ( type ( defs ) , defs ) )
# update the constant for config file
self . data . update_setting ( Setting ( ' CONFIG_FILE ' , configfile , ' ' , ' string ' ) )
origin = None
# env and config defs can have several entries, ordered in list from lowest to highest precedence
for config in defs :
if not isinstance ( defs [ config ] , dict ) :
raise AnsibleOptionsError ( " Invalid configuration definition ' %s ' : type is %s " % ( to_native ( config ) , type ( defs [ config ] ) ) )
@ -632,3 +637,6 @@ class ConfigManager(object):
# set the constant
self . data . update_setting ( Setting ( config , value , origin , defs [ config ] . get ( ' type ' , ' string ' ) ) )
# update the constant for config file
self . data . update_setting ( Setting ( ' CONFIG_FILE ' , configfile , ' ' , ' string ' ) )