From 97127b5369dadf1170db21087a270646cbfbf39c Mon Sep 17 00:00:00 2001 From: Alberto Donato Date: Tue, 20 Jan 2026 16:44:43 +0100 Subject: [PATCH] feat: add support for using API keys in addition to tokens [ENG-6324] [ENG-6324](https://stacklet.atlassian.net/browse/ENG-6324) Acked-by: Alberto Donato ### what support using API keys in place of tokens (via `STACKLET_API_KEY` env var) ### why provide support for API keys ### testing tested locally, added tests ### docs updated readme --- README.md | 13 +++++++++++++ stacklet/client/platform/utils.py | 8 +++++--- tests/test_utils.py | 12 ++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2dc76e3..d4aaf87 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,19 @@ This will prompt for a password. It is also possible to use the `--password` arg to avoid the prompt, though that is a less secure option as it can expose the password through command history and process listings. + +### With API keys + +It's possible to use the CLI with API keys. In this case the `login` step is +not run, but the API key is set in the environment via `STACKLET_API_KEY`. + +This can be done once via `export STACKLET_API_KEY=` or by passing it before every command, like: + +``` +STACKLET_API_KEY= stacklet-admin +``` + + ## Running Commands Commands are grouped into command groups, for example, all the commands relating to accounts can be diff --git a/stacklet/client/platform/utils.py b/stacklet/client/platform/utils.py index f73be4b..04ed4b0 100644 --- a/stacklet/client/platform/utils.py +++ b/stacklet/client/platform/utils.py @@ -3,6 +3,7 @@ import logging import os +from pathlib import Path import click @@ -91,9 +92,10 @@ def wrap_command(func, options, required=False, prompt=False): def get_token(): - with open(os.path.expanduser(StackletContext.DEFAULT_CREDENTIALS), "r") as f: - token = f.read() - return token + if token := os.getenv("STACKLET_API_KEY"): + return token + + return Path(StackletContext.DEFAULT_CREDENTIALS).expanduser().read_text() def default_options(*args, **kwargs): diff --git a/tests/test_utils.py b/tests/test_utils.py index ee977e6..1fb2eb1 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -29,6 +29,18 @@ def test_get_token(self, patched_context): token = get_token() self.assertEqual(token, "foo") + def test_get_token_env(self): + token = "foo" + with patch.dict(os.environ, {"STACKLET_API_KEY": token}): + self.assertEqual(get_token(), "foo") + + @patch("stacklet.client.platform.utils.StackletContext") + def test_get_token_env_precedence(self, patched_context): + patched_context.DEFAULT_CREDENTIALS = self.credential_file.name + token = "another" + with patch.dict(os.environ, {"STACKLET_API_KEY": token}): + self.assertEqual(get_token(), "another") + def test_get_log_level(self): self.assertEqual(get_log_level(-9999), 50) self.assertEqual(get_log_level(0), 40)