Source code for evergreen.config

# -*- encoding: utf-8 -*-
"""Get configuration about connecting to evergreen."""
from __future__ import absolute_import

import os
import subprocess
from collections import namedtuple
from typing import Dict, Optional

import yaml

EvgAuth = namedtuple("EvgAuth", ["username", "api_key", "jwt"])

DEFAULT_NETWORK_TIMEOUT_SEC = 5 * 60
DEFAULT_API_SERVER = "https://evergreen.mongodb.com"  # for use with api key
DEFAULT_CORP_API_SERVER = "https://evergreen.corp.mongodb.com"  # for use with JWT

CONFIG_FILE_LOCATIONS = [
    os.path.expanduser(os.path.join("~", "cli_bin", ".evergreen.yml")),
    os.path.expanduser(os.path.join("~", ".evergreen.yml")),
]


[docs]def read_evergreen_from_file(filename: str) -> Dict: """ Read evergreen config from given filename. :param filename: Filename to read config. :return: Config read from file. """ with open(filename, "r") as fstream: return yaml.safe_load(fstream)
[docs]def read_evergreen_config() -> Optional[Dict]: """ Search known location for the evergreen config file. :return: First found evergreen configuration. """ for filename in [filename for filename in CONFIG_FILE_LOCATIONS if os.path.isfile(filename)]: return read_evergreen_from_file(filename) return None
[docs]def get_auth_from_config(config: Dict) -> EvgAuth: """ Get the evergreen authentication from the specified config dict. :param config: Evergreen configuration. :return: Authentication information for evergreen. """ return EvgAuth(username=config.get("user", ""), api_key=config.get("api_key", ""), jwt="")
[docs]def get_auth() -> Optional[EvgAuth]: """ Get the evergreen authentication object. Falls back to JWT if no api_key is found in config. :return: Authentication information for evergreen. """ conf = read_evergreen_config() if conf: auth = get_auth_from_config(conf) if auth.api_key: # Only return auth if it has an api_key return auth # Try to get JWT if no api_key found if jwt := get_jwt(): return EvgAuth(username="", api_key="", jwt=jwt) return None
[docs]def get_jwt() -> Optional[str]: """ Get a JWT token using the 'kanopy-oidc' command. :return: JWT token if successful, None otherwise. """ try: process = subprocess.Popen( ["kanopy-oidc", "login", "-n", "-f", "device"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) jwt = get_jwt_from_process(process) process.wait() if process.returncode != 0: print( f"Warning: evergreen client jwt command failed with return code {process.returncode}" ) return None elif not jwt: print("Warning: No JWT token was obtained from evergreen client") return None return jwt except FileNotFoundError: print("Warning: evergreen client not found in PATH") except Exception as e: print(f"Warning: Error getting JWT token: {str(e)}") return None
[docs]def get_jwt_from_process(process: subprocess.Popen) -> Optional[str]: """ Extract JWT token from process output while displaying other output to user. :param process: Subprocess with stdout and stderr pipes :return: JWT token if found, None otherwise """ jwt = None if process.stdout: for line in process.stdout: # Check if line looks like a JWT (starts with 'ey' and is long) if len(line) > 30 and line.startswith("ey"): jwt = line.strip() else: print(line.strip()) # Show non-JWT output to user # Show any error output if process.stderr: for line in process.stderr: print(line.strip()) return jwt