Skip to content

Commit

Permalink
feat: add option to authenticate with api token instead of password (#…
Browse files Browse the repository at this point in the history
…460)

Co-authored-by: Bruno MATEU <pro+github@brunomat.eu>
Co-authored-by: Bruno MATEU <mateubruno@gmail.com>
  • Loading branch information
3 people authored Sep 19, 2023
1 parent a6d128d commit 35bfa8b
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 6 deletions.
14 changes: 13 additions & 1 deletion docs/content/configuration/defaults.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
2 changes: 2 additions & 0 deletions docs/content/configuration/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
12 changes: 10 additions & 2 deletions prometheuspvesd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,20 @@ def _get_config(self):
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
Expand Down
12 changes: 12 additions & 0 deletions prometheuspvesd/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ def _auth(self):
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"],
Expand Down
12 changes: 12 additions & 0 deletions prometheuspvesd/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 4 additions & 2 deletions prometheuspvesd/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ def __init__(self, vmid, hostname, ipv4_address, ipv6_address, pve_type):
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(" ", "_")
Expand Down
2 changes: 2 additions & 0 deletions prometheuspvesd/test/data/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 14 additions & 0 deletions prometheuspvesd/test/fixtures/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -154,6 +166,8 @@ def defaults():
"password": "",
"server": "",
"user": "",
"token_name": "",
"token_value": "",
"verify_ssl": True
},
"service": True,
Expand Down
59 changes: 59 additions & 0 deletions prometheuspvesd/test/unit/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__",
Expand Down
2 changes: 2 additions & 0 deletions prometheuspvesd/test/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 35bfa8b

Please sign in to comment.