Source code for igwn_robot_auth.htcondor
# Copyright (c) 2025 Cardiff University
# SPDX-License-Identifier: MIT
"""HTCondor utilities for IGWN Robot Auth."""
from __future__ import annotations
__author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
import logging
import shlex
import shutil
import subprocess
from pathlib import Path
log = logging.getLogger(__name__)
CONDOR_GETTOKEN_OPTS_PARAM = "SEC_CREDENTIAL_GETTOKEN_OPTS"
CONDOR_USER_CONFIG_FILE_PARAM = "USER_CONFIG_FILE"
CONDOR_VAULT_STORER = shutil.which("condor_vault_storer") or "condor_vault_storer"
def _get_config(param: str) -> str:
"""Get a parameter from the HTCondor config."""
try:
import htcondor2 as htcondor
except ImportError: # python < 25
import htcondor
value = htcondor.param[param]
log.debug("htcondor.param: %s = %s", param, value)
return value
[docs]
def condor_config_path() -> Path:
"""Return the path of the HTCondor user config."""
user_config = _get_config(CONDOR_USER_CONFIG_FILE_PARAM)
return Path.home() / ".condor" / user_config
def _match_config(args: list[str]) -> None:
"""Match the current HTCondor configuration against the given ``args``."""
found = _get_config(CONDOR_GETTOKEN_OPTS_PARAM)
foundargs = set(shlex.split(found))
if foundargs.symmetric_difference(args):
argstr = shlex.join(args)
msg = (
f"HTCondor user_config value for {CONDOR_GETTOKEN_OPTS_PARAM} "
f"in {condor_config_path()} doesn't match current options: "
f"{found!r} vs {argstr!r}"
)
raise ValueError(msg)
[docs]
def check_condor_config(
**options,
) -> None:
"""Check that the HTCondor user config matches the specified options."""
args = [f"--{key}={value}" for key, value in options.items()]
try:
_match_config(args)
except ValueError as exc:
config = condor_config_path()
if not config.exists():
msg = f"""No HTCondor user_config file found at {config}.
Please consider creating one with the following contents:
{CONDOR_GETTOKEN_OPTS_PARAM} = {' '.join(args)}
"""
raise ValueError(msg) from exc
raise
[docs]
def condor_vault_storer(
oauth_service: str = "igwn",
*,
verbose: bool = True,
**htgettoken_opts,
) -> None:
"""Call out to ``condor_vault_storer`` with the given options.
This function just runs `subprocess.check_call`.
Parameters
----------
oauth_service : `str`, optional
The ``oauth_service`` argument to use.
This is typically just the name of the token issuer.
Default is ``"igwn"``.
verbose : `bool`, optional
Show verbose output.
htgettoken_opts
Other ``key=value`` arguments are passed verbatim to the
``condor_vault_storer`` call.
"""
check_condor_config(**htgettoken_opts)
log.debug("Condor user_config matches current options")
verbosearg = ("-v",) if verbose else ()
log.debug("Executing condor_vault_storer")
cmd = [
CONDOR_VAULT_STORER,
*verbosearg,
oauth_service,
]
cmdstr = shlex.join(cmd)
log.debug("$ %s", cmdstr)
try:
subprocess.check_call(cmd, shell=False)
except subprocess.CalledProcessError as exc:
exc.cmd = cmdstr
raise