Skip to content

Commit

Permalink
config: change show config to communicate env var overwrites (#891)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsrodman authored and tamarrow committed Feb 23, 2017
1 parent 43059b9 commit c8df216
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 13 deletions.
98 changes: 90 additions & 8 deletions cli/dcoscli/config/main.py
Expand Up @@ -110,6 +110,72 @@ def _unset(name):
return 0


def _format_config(file_value, effective_value, name=None, envvar_name=None):
"""
Construct a string to show on a terminal, indicating the value and
possibly other useful things such as the setting name and whether
it is being controlled by an environment variable.
>>> _format_config('x', 'x')
'x'
>>> _format_config('x', 'x', 'setting.name') ->
'setting.name x'
>>> _format_config('x', 'y', envvar_name='ENVVAR')
'y # overwritten by ENVVAR; config file value: x'
>>> _format_config('x', 'y', 'setting.name', envvar_name='ENVVAR')
'setting.name y # overwritten by ENVVAR; config file value: x'
:param file_value: config value present in the toml file
:type file_value: str
:param effective_value: config value either from file or as overwritten
from the environment
:type effective_value: str
:param name: config key (not used for single value show)
:type name: str|None
:param envvar_name: name of environment variable that overwote the value
:type envvar_name: str|None
:returns: formatted string for terminal
:rtype: str
"""

# when declaring that vars are overwritten by the environment,
# line up those messages to this column (unless the var name is long)
overwite_msg_display_column = 35

# When showing all values, don't print the token value;
if name == "core.dcos_acs_token":
print_value = "*"*8
else:
print_value = effective_value

if file_value == effective_value:
if name:
return '%s %s' % (name, print_value)
else:
return effective_value
else:
if not envvar_name:
envvar_name = "N/A" # this should never happen
if name:
s = '%s %s' % (name, print_value)
else:
s = effective_value

left_pad_fmt = '%-{}s'.format(overwite_msg_display_column) # '%-35s'

msg_start = left_pad_fmt + ' # overwritten by environment var %s; '

if print_value != effective_value:
# We're obscuring the effective security token
# so don't report the file value either
msg = msg_start + "config file value differs"
return msg % (s, envvar_name)

msg = msg_start + 'config file value: %s'
return msg % (s, envvar_name, file_value)


def _show(name):
"""
:returns: process status
Expand All @@ -119,19 +185,35 @@ def _show(name):
toml_config = config.get_config(True)

if name is not None:
value = toml_config.get(name)
if value is None:
file_value = toml_config.get(name)
try:
# If the user presented a partial key name, eg 'core' when
# we have 'core.xyz'; we will get an exception here
effective_value, envvar_name = config.get_config_val_envvar(name)
except DCOSException as e:
# The expected case of a partial key name has special
# handling via this mechanism.
if isinstance(file_value, collections.Mapping):
exc_msg = config.generate_choice_msg(name, file_value)
raise DCOSException(exc_msg)
raise # Unexpected errors, pass right along

if effective_value is None:
raise DCOSException("Property {!r} doesn't exist".format(name))
elif isinstance(value, collections.Mapping):
raise DCOSException(config.generate_choice_msg(name, value))
else:
emitter.publish(value)
msg = _format_config(file_value, effective_value,
envvar_name=envvar_name)
emitter.publish(msg)

else:
# Let's list all of the values
for key, value in sorted(toml_config.property_items()):
if key == "core.dcos_acs_token":
value = "*"*8
emitter.publish('{} {}'.format(key, value))
file_value = toml_config.get(key)
effective_value, envvar_name = config.get_config_val_envvar(key)

msg = _format_config(file_value, effective_value, key,
envvar_name=envvar_name)
emitter.publish(msg)

return 0

Expand Down
33 changes: 28 additions & 5 deletions dcos/config.py
Expand Up @@ -55,9 +55,13 @@ def get_config(mutable=False):
return load_from_path(path, mutable)


def get_config_val(name, config=None):
"""Returns the config value for the specified key. Looks for corresponding
environment variable first, and if it doesn't exist, uses the config value.
def get_config_val_envvar(name, config=None):
"""Returns a tuple of the config value for the specified key and
the name of any environment variable which overwrote the value
from the configuration. If no environment variable applies, None
is returned for the environment variable name.
Looks for corresponding environment variable first, and if it
doesn't exist, uses the config value.
- "core" properties get resolved to env variable DCOS_SUBKEY. With the
exception of subkeys that already start with DCOS, in which case we look
for SUBKEY first, and "DCOS_SUBKEY" second, and finally the config value.
Expand All @@ -68,7 +72,7 @@ def get_config_val(name, config=None):
:param config: config
:type config: Toml
:returns: value of 'name' parameter
:rtype: str | None
:rtype: (str | None, str | None)
"""

if config is None:
Expand All @@ -85,7 +89,26 @@ def get_config_val(name, config=None):
else:
env_var = "DCOS_{}_{}".format(section, subkey)

return os.environ.get(env_var) or config.get(name)
return os.environ.get(env_var) or config.get(name), env_var or None


def get_config_val(name, config=None):
"""Returns the config value for the specified key. Looks for corresponding
environment variable first, and if it doesn't exist, uses the config value.
- "core" properties get resolved to env variable DCOS_SUBKEY. With the
exception of subkeys that already start with DCOS, in which case we look
for SUBKEY first, and "DCOS_SUBKEY" second, and finally the config value.
- everything else gets resolved to DCOS_SECTION_SUBKEY
:param name: name of paramater
:type name: str
:param config: config
:type config: Toml
:returns: value of 'name' parameter
:rtype: str | None
"""
val, _ = get_config_val_envvar(name, config=None)
return val


def missing_config_exception(keys):
Expand Down

0 comments on commit c8df216

Please sign in to comment.