feat: add option to authenticate with api token instead of password (#460)

Co-authored-by: Bruno MATEU <pro+github@brunomat.eu>
Co-authored-by: Bruno MATEU <mateubruno@gmail.com>
This commit is contained in:
Robert Kaussow 2023-09-19 09:00:30 +02:00 committed by GitHub
parent a6d128d605
commit 35bfa8bc6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 131 additions and 6 deletions

View File

@ -35,18 +35,30 @@ include_vmid: []
exclude_tags: []
include_tags: []
# Set either password or token_name and token_value
pve:
server:
user:
password:
token_name:
token_value:
auth_timeout: 5
verify_ssl: true
# Example
# Example with password
# pve:
# server: proxmox.example.com
# user: root
# password: secure
# auth_timeout: 5
# verify_ssl: true
# Example with API token
# pve:
# server: proxmox.example.com
# user: root
# token_name: pve_sd
# token_value: 01234567-89ab-cdef-0123-456789abcdef
# auth_timeout: 5
# verify_ssl: true
```

View File

@ -36,6 +36,8 @@ PROMETHEUS_PVE_SD_INCLUDE_TAGS=
PROMETHEUS_PVE_SD_PVE_SERVER=
PROMETHEUS_PVE_SD_PVE_USER=
PROMETHEUS_PVE_SD_PVE_PASSWORD=
PROMETHEUS_PVE_SD_PVE_TOKEN_NAME=
PROMETHEUS_PVE_SD_PVE_TOKEN_VALUE=
PROMETHEUS_PVE_SD_PVE_AUTH_TIMEOUT=5
PROMETHEUS_PVE_SD_PVE_VERIFY_SSL=true
```

View File

@ -114,12 +114,20 @@ class PrometheusSD:
self.log.sysexit_with_message(f"Can not set log level.\n{e!s}")
required = [("pve.server", config.config["pve"]["server"]),
("pve.user", config.config["pve"]["user"]),
("pve.password", config.config["pve"]["password"])]
("pve.user", config.config["pve"]["user"])]
for name, value in required:
if not value:
self.log.sysexit_with_message(f"Option '{name}' is required but not set")
if (
not config.config["pve"]["password"]
and not (config.config["pve"]["token_name"] and config.config["pve"]["token_value"])
):
self.log.sysexit_with_message(
"Either 'pve.password' or 'pve.token_name' and 'pve.token_value' "
"are required but not set"
)
self.logger.info(f"Using config file {config.config_file}")
return config

View File

@ -44,6 +44,18 @@ class ProxmoxClient:
self.config.config["pve"]["server"], self.config.config["pve"]["user"]
)
)
if self.config.config["pve"]["token_name"]:
self.logger.debug("Using token login")
return ProxmoxAPI(
self.config.config["pve"]["server"],
user=self.config.config["pve"]["user"],
token_name=self.config.config["pve"]["token_name"],
token_value=self.config.config["pve"]["token_value"],
verify_ssl=to_bool(self.config.config["pve"]["verify_ssl"]),
timeout=self.config.config["pve"]["auth_timeout"]
)
return ProxmoxAPI(
self.config.config["pve"]["server"],
user=self.config.config["pve"]["user"],

View File

@ -135,6 +135,18 @@ class Config:
"file": True,
"type": environs.Env().str
},
"pve.token_name": {
"default": "",
"env": "PVE_TOKEN_NAME",
"file": True,
"type": environs.Env().str
},
"pve.token_value": {
"default": "",
"env": "PVE_TOKEN_VALUE",
"file": True,
"type": environs.Env().str
},
"pve.auth_timeout": {
"default": 5,
"env": "PVE_AUTH_TIMEOUT",

View File

@ -19,8 +19,10 @@ class Host:
self.add_label("vmid", vmid)
def __str__(self):
return f"{self.hostname}({self.vmid}): {self.pve_type} \
{self.ipv4_address} {self.ipv6_address}"
return (
f"{self.hostname}({self.vmid}): "
f"{self.pve_type} {self.ipv4_address} {self.ipv6_address}"
)
def add_label(self, key, value):
key = key.replace("-", "_").replace(" ", "_")

View File

@ -24,5 +24,7 @@ pve:
server: proxmox.example.com
user: root
password: secure
token_name: pve_sd
token_value: 01234567-89ab-cdef-0123-456789abcdef
auth_timeout: 5
verify_ssl: true

View File

@ -114,6 +114,18 @@ def builtins():
"file": True,
"type": environs.Env().str
},
"pve.token_name": {
"default": "dummyname",
"env": "PVE_TOKEN_NAME",
"file": True,
"type": environs.Env().str
},
"pve.token_value": {
"default": "dummyvalue",
"env": "PVE_TOKEN_VALUE",
"file": True,
"type": environs.Env().str
},
"pve.auth_timeout": {
"default": 5,
"env": "PVE_AUTH_TIMEOUT",
@ -154,6 +166,8 @@ def defaults():
"password": "",
"server": "",
"user": "",
"token_name": "",
"token_value": "",
"verify_ssl": True
},
"service": True,

View File

@ -29,6 +29,65 @@ def test_cli_required_error(mocker, capsys):
assert e.value.code == 1
@pytest.mark.parametrize(
"testinput", [{
"pve.user": "dummy",
"pve.password": "",
"pve.token_name": "",
"pve.token_value": ""
}, {
"pve.user": "dummy",
"pve.password": "",
"pve.token_name": "dummy",
"pve.token_value": ""
}, {
"pve.user": "dummy",
"pve.password": "",
"pve.token_name": "",
"pve.token_value": "dummy"
}]
)
def test_cli_auth_required_error(mocker, capsys, builtins, testinput):
for key, value in testinput.items():
builtins[key]["default"] = value
mocker.patch.dict(Config.SETTINGS, builtins)
mocker.patch.object(ProxmoxClient, "_auth", return_value=mocker.create_autospec(ProxmoxAPI))
mocker.patch.object(PrometheusSD, "_fetch", return_value=True)
with pytest.raises(SystemExit) as e:
PrometheusSD()
stdout, stderr = capsys.readouterr()
assert "Either 'pve.password' or 'pve.token_name' and 'pve.token_value' are required but not set" in stderr
assert e.value.code == 1
@pytest.mark.parametrize(
"testinput", [{
"pve.password": "dummy",
"pve.token_name": "",
"pve.token_value": ""
}, {
"pve.password": "",
"pve.token_name": "dummy",
"pve.token_value": "dummy"
}]
)
def test_cli_auth_no_error(mocker, capsys, builtins, testinput):
for key, value in testinput.items():
builtins[key]["default"] = value
mocker.patch.dict(Config.SETTINGS, builtins)
mocker.patch.object(ProxmoxClient, "_auth", return_value=mocker.create_autospec(ProxmoxAPI))
mocker.patch.object(PrometheusSD, "_fetch", return_value=True)
psd = PrometheusSD()
for key, value in testinput.items():
assert psd.config.config["pve"][key.split(".")[1]] == value
def test_cli_config_error(mocker, capsys):
mocker.patch(
"prometheuspvesd.config.SingleConfig.__init__",

View File

@ -19,6 +19,8 @@ def test_yaml_config(mocker, defaults):
defaults["pve"]["user"] = "root"
defaults["pve"]["password"] = "secure"
defaults["pve"]["server"] = "proxmox.example.com"
defaults["pve"]["token_name"] = "pve_sd"
defaults["pve"]["token_value"] = "01234567-89ab-cdef-0123-456789abcdef"
assert config.config == defaults

View File

@ -66,7 +66,7 @@ sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*"]
[tool.pytest.ini_options]
addopts = "prometheuspvesd --cov=prometheuspvesd --cov-report=xml:coverage.xml --cov-report=term --no-cov-on-fail"
addopts = "prometheuspvesd --cov=prometheuspvesd --cov-report=xml:coverage.xml --cov-report=term-missing --no-cov-on-fail --cov-fail-under=80"
filterwarnings = [
"ignore::FutureWarning",
"ignore::DeprecationWarning",