feat: user_data can load secret manager values
This commit is contained in:
parent
fb0f1e5f2c
commit
5759e4f86b
2 changed files with 76 additions and 24 deletions
|
|
@ -159,6 +159,15 @@ class _RougailConfig:
|
||||||
return not ret
|
return not ret
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def __contains__(
|
||||||
|
self, key,
|
||||||
|
) -> None:
|
||||||
|
try:
|
||||||
|
self.__getitem__(key)
|
||||||
|
except AttributeError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def get_leadership(self, option) -> dict:
|
def get_leadership(self, option) -> dict:
|
||||||
leader = None
|
leader = None
|
||||||
followers = []
|
followers = []
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,9 @@ from tiramisu.error import (
|
||||||
LeadershipError,
|
LeadershipError,
|
||||||
ConfigError,
|
ConfigError,
|
||||||
CancelParam,
|
CancelParam,
|
||||||
|
display_list,
|
||||||
)
|
)
|
||||||
from .utils import undefined
|
from .utils import undefined, get_properties_to_string
|
||||||
from .tiramisu import (
|
from .tiramisu import (
|
||||||
normalize_family,
|
normalize_family,
|
||||||
CONVERT_OPTION,
|
CONVERT_OPTION,
|
||||||
|
|
@ -63,6 +64,7 @@ class UserDatas:
|
||||||
self.values = user_datas
|
self.values = user_datas
|
||||||
self._auto_configure_dynamics()
|
self._auto_configure_dynamics()
|
||||||
self._populate_config()
|
self._populate_config()
|
||||||
|
self.properties_to_string = get_properties_to_string()
|
||||||
if return_values_not_error:
|
if return_values_not_error:
|
||||||
return self.values
|
return self.values
|
||||||
else:
|
else:
|
||||||
|
|
@ -133,6 +135,8 @@ class UserDatas:
|
||||||
identifier = get_identifier_from_dynamic_family(
|
identifier = get_identifier_from_dynamic_family(
|
||||||
tconfig.name(), name
|
tconfig.name(), name
|
||||||
)
|
)
|
||||||
|
if identifier == "{{ identifier }}":
|
||||||
|
continue
|
||||||
if identifier != normalize_family(identifier):
|
if identifier != normalize_family(identifier):
|
||||||
msg = _(
|
msg = _(
|
||||||
'cannot load variable path "{0}", the identifier "{1}" is not valid in {2}'
|
'cannot load variable path "{0}", the identifier "{1}" is not valid in {2}'
|
||||||
|
|
@ -200,16 +204,21 @@ class UserDatas:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def _populate_config(self):
|
def _populate_config(self):
|
||||||
|
dynamics_variable = []
|
||||||
while self.values:
|
while self.values:
|
||||||
value_is_set = False
|
value_is_set = False
|
||||||
for option in self._get_variable(self.config):
|
for option in self._get_variable(self.config):
|
||||||
path = option.path()
|
values_path = path = option.path()
|
||||||
if path not in self.values:
|
if path not in self.values:
|
||||||
continue
|
if path in dynamics_variable or not option.isdynamic():
|
||||||
|
continue
|
||||||
|
values_path = option.path(uncalculated=True)
|
||||||
|
if values_path not in self.values:
|
||||||
|
continue
|
||||||
if self.only_default and option.owner.get() != owners.default:
|
if self.only_default and option.owner.get() != owners.default:
|
||||||
self.values.pop(path)
|
self.values.pop(values_path)
|
||||||
continue
|
continue
|
||||||
options = self.values[path].get("options", {})
|
options = self.values[values_path].get("options", {})
|
||||||
if (
|
if (
|
||||||
options.get("allow_secrets_variables", True) is False
|
options.get("allow_secrets_variables", True) is False
|
||||||
and option.type() == "password"
|
and option.type() == "password"
|
||||||
|
|
@ -217,15 +226,19 @@ class UserDatas:
|
||||||
self.errors.append(
|
self.errors.append(
|
||||||
_(
|
_(
|
||||||
'the variable "{0}" contains secrets and should not be defined in {1}'
|
'the variable "{0}" contains secrets and should not be defined in {1}'
|
||||||
).format(path, self.values[path]["source"])
|
).format(path, self.values[values_path]["source"])
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
value = self.convert_value(
|
value = self.convert_value(
|
||||||
path, option, options, self.values[path]["values"]
|
path, option, options, self.values[values_path]["values"]
|
||||||
)
|
)
|
||||||
|
|
||||||
index = option.index()
|
index = option.index()
|
||||||
if index is not None:
|
if index is not None:
|
||||||
|
if isinstance(value, tuple):
|
||||||
|
self.values[values_path]["values"] = []
|
||||||
|
for i in range(len(option.parent().leader().value.get())):
|
||||||
|
self.values[values_path]["values"].append(value)
|
||||||
|
value = self.values[values_path]["values"]
|
||||||
if not isinstance(value, list) or index >= len(value):
|
if not isinstance(value, list) or index >= len(value):
|
||||||
continue
|
continue
|
||||||
value = value[index]
|
value = value[index]
|
||||||
|
|
@ -233,33 +246,40 @@ class UserDatas:
|
||||||
else:
|
else:
|
||||||
option_without_index = option
|
option_without_index = option
|
||||||
if option.isleader():
|
if option.isleader():
|
||||||
|
# set value for a leader, it began to remove all values!
|
||||||
len_leader = len(option.value.get())
|
len_leader = len(option.value.get())
|
||||||
if len_leader:
|
if len_leader:
|
||||||
for idx in range(len_leader - 1, -1, -1):
|
for idx in range(len_leader - 1, -1, -1):
|
||||||
option.value.pop(idx)
|
option.value.pop(idx)
|
||||||
try:
|
try:
|
||||||
option.value.set(value)
|
self.set_value(option, value, options)
|
||||||
|
# option.value.set(value)
|
||||||
value_is_set = True
|
value_is_set = True
|
||||||
except Exception:
|
except Exception as err:
|
||||||
if path != option.path():
|
if path != option.path():
|
||||||
self.values[option.path()] = self.values.pop(path)
|
self.values[option.path()] = self.values.pop(values_path)
|
||||||
else:
|
else:
|
||||||
if "source" in self.values[path]:
|
if "source" in self.values[values_path]:
|
||||||
option_without_index.information.set(
|
option_without_index.information.set(
|
||||||
"loaded_from",
|
"loaded_from",
|
||||||
_("loaded from {0}").format(self.values[path]["source"]),
|
_("loaded from {0}").format(self.values[values_path]["source"]),
|
||||||
)
|
)
|
||||||
# value is correctly set, remove variable to the set
|
# value is correctly set, remove variable to the set
|
||||||
if index is not None:
|
if index is not None:
|
||||||
# if it's a follower waiting for all followers are sets
|
# if it's a follower waiting for all followers are sets
|
||||||
self.values[path]["values"][index] = undefined
|
self.values[values_path]["values"][index] = undefined
|
||||||
for tmp_value in self.values[path]["values"]:
|
for tmp_value in self.values[values_path]["values"]:
|
||||||
if tmp_value != undefined:
|
if tmp_value != undefined:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.values.pop(path)
|
if path in self.values:
|
||||||
else:
|
self.values.pop(path)
|
||||||
|
else:
|
||||||
|
dynamics_variable.append(path)
|
||||||
|
elif path in self.values:
|
||||||
self.values.pop(path)
|
self.values.pop(path)
|
||||||
|
else:
|
||||||
|
dynamics_variable.append(path)
|
||||||
if not value_is_set:
|
if not value_is_set:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -272,7 +292,11 @@ class UserDatas:
|
||||||
# we don't find variable, apply value just to get error or warning messages
|
# we don't find variable, apply value just to get error or warning messages
|
||||||
for path, options in self.values.items():
|
for path, options in self.values.items():
|
||||||
value = options["values"]
|
value = options["values"]
|
||||||
option = self.config.option(path)
|
|
||||||
|
if options.get('secret_manager'):
|
||||||
|
option = self.config.forcepermissive.option(path)
|
||||||
|
else:
|
||||||
|
option = self.config.option(path)
|
||||||
try:
|
try:
|
||||||
if option.isoptiondescription():
|
if option.isoptiondescription():
|
||||||
if value:
|
if value:
|
||||||
|
|
@ -320,12 +344,10 @@ class UserDatas:
|
||||||
continue
|
continue
|
||||||
self.config.option(path, index).value.set(value)
|
self.config.option(path, index).value.set(value)
|
||||||
else:
|
else:
|
||||||
option.value.set(value)
|
self.set_value(option, value, options.get("options", {}))
|
||||||
except PropertiesOptionError as err:
|
except PropertiesOptionError as err:
|
||||||
if err.code == "property-error":
|
if err.code == "property-error":
|
||||||
properties = err.display_properties(
|
properties = display_list([_(prop) for prop in err.proptype], add_quote=False)
|
||||||
force_property=True, add_quote=False
|
|
||||||
)
|
|
||||||
err_path = err._subconfig.path
|
err_path = err._subconfig.path
|
||||||
display_name = option.description(with_quote=True)
|
display_name = option.description(with_quote=True)
|
||||||
if index is not None:
|
if index is not None:
|
||||||
|
|
@ -381,11 +403,13 @@ class UserDatas:
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
err.prefix = ""
|
err.prefix = ""
|
||||||
if index is not None:
|
if index is not None:
|
||||||
|
type_ = option.type(translation=True)
|
||||||
self.warnings.append(
|
self.warnings.append(
|
||||||
_(
|
_(
|
||||||
'the value "{0}" is invalid for {1} at index "{2}", {3}, it will be ignored when loading from {4}'
|
'the value "{0}" is an invalid {1} for {2} at index "{3}", {4}, it will be ignored when loading from {5}'
|
||||||
).format(
|
).format(
|
||||||
self._display_value(option, value),
|
self._display_value(option, value),
|
||||||
|
type_,
|
||||||
option.description(with_quote=True),
|
option.description(with_quote=True),
|
||||||
index,
|
index,
|
||||||
err,
|
err,
|
||||||
|
|
@ -393,16 +417,35 @@ class UserDatas:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
type_ = option.type(translation=True)
|
||||||
self.warnings.append(
|
self.warnings.append(
|
||||||
_(
|
_(
|
||||||
'the value "{0}" is invalid for {1}, {2}, it will be ignored when loading from {3}'
|
'the value "{0}" is an invalid {1} for {2}, {3}, it will be ignored when loading from {4}'
|
||||||
).format(
|
).format(
|
||||||
self._display_value(option, value),
|
self._display_value(option, value),
|
||||||
|
type_,
|
||||||
option.description(with_quote=True),
|
option.description(with_quote=True),
|
||||||
err,
|
err,
|
||||||
options["source"],
|
options["source"],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except AttributeOptionError as err:
|
||||||
|
if err.code == "option-dynamic":
|
||||||
|
continue
|
||||||
|
raise err from err
|
||||||
|
|
||||||
|
def set_value(self, option, value, options):
|
||||||
|
is_secret_manager = options.get("secret_manager", False)
|
||||||
|
if is_secret_manager and isinstance(value, tuple):
|
||||||
|
# it's a function
|
||||||
|
value = value[0](*value[1:], option=option, warnings=self.warnings, errors=self.errors)
|
||||||
|
if is_secret_manager and "hidden" in option.property.get():
|
||||||
|
option.permissive.add("hidden")
|
||||||
|
option.permissive.add("frozen")
|
||||||
|
option.value.set(value)
|
||||||
|
if is_secret_manager and "hidden" in option.property.get():
|
||||||
|
option.permissive.remove("hidden")
|
||||||
|
option.permissive.remove("frozen")
|
||||||
|
|
||||||
|
|
||||||
def convert_value(option, value):
|
def convert_value(option, value):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue