1
0
mirror of https://github.com/openSUSE/osc.git synced 2024-11-12 23:56:13 +01:00

Allow undefined fields in Options and HostOptions

Plugins seem to be using oscrc and osc.conf.config to store their config options.
All fields that are not known to osc are now stored in the 'extra_fields' dictionary
and handled in __getitem__() and __setitem__() as they were regular fields.
Such values are not checked for their types and the dictionary simply holds
strings obtained from oscrc or anything the plugins set through the python API.
This commit is contained in:
Daniel Mach 2023-10-19 14:23:04 +02:00
parent ee5a1c476c
commit a2e7383eca
2 changed files with 62 additions and 2 deletions

View File

@ -124,6 +124,10 @@ HttpHeader = NewType("HttpHeader", Tuple[str, str])
class OscOptions(BaseModel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.extra_fields = {}
# compat function with the config dict
def _get_field_name(self, name):
if name in self.__fields__:
@ -139,8 +143,10 @@ class OscOptions(BaseModel):
# compat function with the config dict
def __getitem__(self, name):
field_name = self._get_field_name(name)
if field_name is None:
field_name = name
return self.extra_fields[name]
try:
return getattr(self, field_name)
except AttributeError:
@ -149,8 +155,11 @@ class OscOptions(BaseModel):
# compat function with the config dict
def __setitem__(self, name, value):
field_name = self._get_field_name(name)
if field_name is None:
field_name = name
self.extra_fields[name] = value
return
setattr(self, field_name, value)
# compat function with the config dict
@ -1836,6 +1845,17 @@ def get_config(override_conffile=None,
config = Options()
config.conffile = conffile
# read 'debug' value before it gets properly stored into Options for early debug messages
if override_debug:
debug_str = str(override_debug)
elif "OSC_DEBUG" in os.environ:
debug_str = os.environ["OSC_DEBUG"]
elif "debug" in cp["general"]:
debug_str = cp["general"]["debug"]
else:
debug_str = "0"
debug = True if debug_str.strip().lower() in ("1", "yes", "true", "on") else False
# read host options first in order to populate apiurl aliases
urls = [i for i in cp.sections() if i != "general"]
for url in urls:
@ -1845,8 +1865,10 @@ def get_config(override_conffile=None,
raise oscerr.ConfigMissingCredentialsError(f"No user found in section {url}", conffile, url)
host_options = HostOptions(apiurl=apiurl, username=username, _parent=config)
known_ini_keys = set()
for name, field in host_options.__fields__.items():
ini_key = field.extra.get("ini_key", name)
known_ini_keys.add(ini_key)
if name == "password":
# we need to handle the password first because it may be stored in a keyring instead of a config file
@ -1862,6 +1884,15 @@ def get_config(override_conffile=None,
host_options.set_value_from_string(name, value)
for key, value in cp[url].items():
if key.startswith("_"):
continue
if key in known_ini_keys:
continue
if debug:
print(f"DEBUG: Config option '[{url}]/{key}' doesn't map to any HostOptions field", file=sys.stderr)
host_options[key] = value
scheme = urlsplit(apiurl)[0]
if scheme == "http" and not host_options.allow_http:
msg = "The apiurl '{apiurl}' uses HTTP protocol without any encryption.\n"
@ -1872,8 +1903,10 @@ def get_config(override_conffile=None,
config.api_host_options[apiurl] = host_options
# read the main options
known_ini_keys = set()
for name, field in config.__fields__.items():
ini_key = field.extra.get("ini_key", name)
known_ini_keys.add(ini_key)
env_key = f"OSC_{name.upper()}"
# priority: env, overrides, config
@ -1900,6 +1933,15 @@ def get_config(override_conffile=None,
config.set_value_from_string(name, value)
for key, value in cp["general"].items():
if key.startswith("_"):
continue
if key in known_ini_keys:
continue
if debug:
print(f"DEBUG: Config option '[general]/{key}' doesn't map to any Options field", file=sys.stderr)
config[key] = value
if overrides:
unused_overrides_str = ", ".join((f"'{i}'" for i in overrides))
raise oscerr.ConfigError(f"Unknown config options: {unused_overrides_str}", "<command-line>")

View File

@ -78,6 +78,7 @@ maintained_update_project_attribute = OBS:UpdateProject
show_download_progress = 0
vc-cmd = /usr/lib/build/vc
status_mtime_heuristic = 0
plugin-option = plugin-general-option
[https://api.opensuse.org]
credentials_mgr_class=osc.credentials.PlaintextConfigFileCredentialsManager
@ -97,6 +98,7 @@ trusted_prj = openSUSE:* SUSE:*
downloadurl = http://example.com/
sshkey = ~/.ssh/id_rsa.pub
disable_hdrmd5_check = 0
plugin-option = plugin-host-option
"""
@ -401,6 +403,22 @@ class TestExampleConfig(unittest.TestCase):
host_options = self.config["api_host_options"][self.config["apiurl"]]
self.assertEqual(host_options["disable_hdrmd5_check"], False)
def test_extra_fields(self):
self.assertEqual(self.config["plugin-option"], "plugin-general-option")
self.assertEqual(self.config.extra_fields, {"plugin-option": "plugin-general-option"})
self.config["new-option"] = "value"
self.assertEqual(self.config["new-option"], "value")
self.assertEqual(self.config.extra_fields, {"plugin-option": "plugin-general-option", "new-option": "value"})
host_options = self.config["api_host_options"][self.config["apiurl"]]
self.assertEqual(host_options["plugin-option"], "plugin-host-option")
self.assertEqual(host_options.extra_fields, {"plugin-option": "plugin-host-option"})
host_options["new-option"] = "value"
self.assertEqual(host_options["new-option"], "value")
self.assertEqual(host_options.extra_fields, {"plugin-option": "plugin-host-option", "new-option": "value"})
class TestFromParent(unittest.TestCase):
def setUp(self):