stackit-core 0.3.1__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stackit_core-0.3.1 → stackit_core-0.4.0}/PKG-INFO +1 -1
- {stackit_core-0.3.1 → stackit_core-0.4.0}/pyproject.toml +1 -1
- {stackit_core-0.3.1 → stackit_core-0.4.0}/src/stackit/core/auth_methods/key_auth.py +28 -2
- {stackit_core-0.3.1 → stackit_core-0.4.0}/.gitignore +0 -0
- {stackit_core-0.3.1 → stackit_core-0.4.0}/LICENSE.md +0 -0
- {stackit_core-0.3.1 → stackit_core-0.4.0}/NOTICE.txt +0 -0
- {stackit_core-0.3.1 → stackit_core-0.4.0}/README.md +0 -0
- {stackit_core-0.3.1 → stackit_core-0.4.0}/src/stackit/core/__init__.py +0 -0
- {stackit_core-0.3.1 → stackit_core-0.4.0}/src/stackit/core/auth_methods/token_auth.py +0 -0
- {stackit_core-0.3.1 → stackit_core-0.4.0}/src/stackit/core/authorization.py +0 -0
- {stackit_core-0.3.1 → stackit_core-0.4.0}/src/stackit/core/configuration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stackit-core
|
|
3
|
-
Version: v0.
|
|
3
|
+
Version: v0.4.0
|
|
4
4
|
Summary: Core functionality for the STACKIT SDK for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/stackitcloud/stackit-sdk-python
|
|
6
6
|
Project-URL: Issues, https://github.com/stackitcloud/stackit-sdk-python/issues
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "stackit-core"
|
|
3
|
-
version = "v0.
|
|
3
|
+
version = "v0.4.0"
|
|
4
4
|
description = "Core functionality for the STACKIT SDK for Python"
|
|
5
5
|
authors = [{ name = "STACKIT Developer Tools", email = "developer-tools@stackit.cloud" }]
|
|
6
6
|
requires-python = ">=3.10,<4.0"
|
|
@@ -54,6 +54,8 @@ class KeyAuth(AuthBase):
|
|
|
54
54
|
executor: ThreadPoolExecutor
|
|
55
55
|
refresh_future: Optional[futures.Future]
|
|
56
56
|
service_account_key: ServiceAccountKey
|
|
57
|
+
_stop: threading.Event
|
|
58
|
+
_refresh_thread: Optional[threading.Thread]
|
|
57
59
|
|
|
58
60
|
def __init__(
|
|
59
61
|
self,
|
|
@@ -66,6 +68,8 @@ class KeyAuth(AuthBase):
|
|
|
66
68
|
self.executor = ThreadPoolExecutor(max_workers=1)
|
|
67
69
|
self.access_token = None
|
|
68
70
|
self.refresh_future = None
|
|
71
|
+
self._stop = threading.Event()
|
|
72
|
+
self._refresh_thread = None
|
|
69
73
|
self.__create_initial_token()
|
|
70
74
|
self.__fetch_token_from_endpoint()
|
|
71
75
|
self.__start_token_refresh_task()
|
|
@@ -92,6 +96,24 @@ class KeyAuth(AuthBase):
|
|
|
92
96
|
r.headers["Authorization"] = f"Bearer {self.access_token}"
|
|
93
97
|
return r
|
|
94
98
|
|
|
99
|
+
def close(self) -> None:
|
|
100
|
+
"""Stop the background token refresh and release its worker.
|
|
101
|
+
|
|
102
|
+
Call this when the instance is no longer needed, for example after
|
|
103
|
+
replacing it during key rotation in a long-running process. Without it
|
|
104
|
+
the refresh thread keeps running and keeps requesting tokens. When it
|
|
105
|
+
returns, no refresh is running and none will start. Safe to call more
|
|
106
|
+
than once.
|
|
107
|
+
"""
|
|
108
|
+
# Set under the lock so the refresh loop cannot submit after shutdown.
|
|
109
|
+
with self.lock:
|
|
110
|
+
self._stop.set()
|
|
111
|
+
atexit.unregister(self.__shutdown)
|
|
112
|
+
thread = self._refresh_thread
|
|
113
|
+
if thread is not None and thread is not threading.current_thread():
|
|
114
|
+
thread.join()
|
|
115
|
+
self.executor.shutdown(wait=True, cancel_futures=True)
|
|
116
|
+
|
|
95
117
|
def __create_initial_token(self) -> None:
|
|
96
118
|
payload = {
|
|
97
119
|
"iss": self.service_account_key.credentials.issuer,
|
|
@@ -126,9 +148,10 @@ class KeyAuth(AuthBase):
|
|
|
126
148
|
|
|
127
149
|
def __start_token_refresh_task(self):
|
|
128
150
|
def token_refresh_task():
|
|
129
|
-
while
|
|
130
|
-
time.sleep(self.TOKEN_EXPIRY_CHECK_INTERVAL.total_seconds())
|
|
151
|
+
while not self._stop.wait(self.TOKEN_EXPIRY_CHECK_INTERVAL.total_seconds()):
|
|
131
152
|
with self.lock:
|
|
153
|
+
if self._stop.is_set():
|
|
154
|
+
return
|
|
132
155
|
if self.refresh_future is not None and self.refresh_future.done():
|
|
133
156
|
self.refresh_future.result()
|
|
134
157
|
if self.__is_token_expired(self.access_token) and (
|
|
@@ -139,6 +162,7 @@ class KeyAuth(AuthBase):
|
|
|
139
162
|
thread = threading.Thread(target=token_refresh_task)
|
|
140
163
|
thread.daemon = True
|
|
141
164
|
thread.start()
|
|
165
|
+
self._refresh_thread = thread
|
|
142
166
|
|
|
143
167
|
def __refresh_token(self):
|
|
144
168
|
with self.lock:
|
|
@@ -156,6 +180,8 @@ class KeyAuth(AuthBase):
|
|
|
156
180
|
|
|
157
181
|
last_exception = None
|
|
158
182
|
for _ in range(self.MAX_REFRESH_RETRIES):
|
|
183
|
+
if self._stop.is_set():
|
|
184
|
+
return
|
|
159
185
|
try:
|
|
160
186
|
response = requests.post(self.token_endpoint, data=body, timeout=self.timeout)
|
|
161
187
|
response.raise_for_status()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|