pipekit-sdk 2.0.1__tar.gz → 2.1.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.
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/PKG-INFO +3 -1
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/pyproject.toml +2 -1
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/service.py +109 -1
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/LICENSE +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/README.md +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/__init__.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/_helpers.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/__init__.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/_config.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/big.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/inf.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/intstr.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/model.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/resource.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/v1.py +0 -0
- {pipekit_sdk-2.0.1 → pipekit_sdk-2.1.0}/src/pipekit_sdk/models/v1alpha1.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pipekit-sdk
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Pipekit Python SDK
|
|
5
5
|
Home-page: https://pipekit.io
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -16,8 +16,10 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
20
|
Requires-Dist: hera (>=5.5.2)
|
|
20
21
|
Requires-Dist: pydantic (<3)
|
|
22
|
+
Requires-Dist: pyjwt (>=2.9.0,<3.0.0)
|
|
21
23
|
Requires-Dist: responses (>=0.25.3,<0.26.0)
|
|
22
24
|
Project-URL: Documentation, https://docs.pipekit.io/
|
|
23
25
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pipekit-sdk"
|
|
3
|
-
version = "2.0
|
|
3
|
+
version = "2.1.0"
|
|
4
4
|
description = "Pipekit Python SDK"
|
|
5
5
|
authors = ["Pipekit <ci@pipekit.io>"]
|
|
6
6
|
license = "BSD-3-Clause"
|
|
@@ -27,6 +27,7 @@ python = ">=3.8,<4"
|
|
|
27
27
|
hera = ">=5.5.2"
|
|
28
28
|
pydantic = "<3"
|
|
29
29
|
responses = "^0.25.3"
|
|
30
|
+
pyjwt = "^2.9.0"
|
|
30
31
|
|
|
31
32
|
[tool.poetry.group.dev.dependencies]
|
|
32
33
|
ruff = "*"
|
|
@@ -2,19 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
-
from
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Generator, List, Optional, cast
|
|
6
9
|
|
|
10
|
+
import jwt
|
|
7
11
|
import requests
|
|
8
12
|
from hera.workflows import CronWorkflow, Workflow
|
|
9
13
|
from hera.workflows.models import WorkflowCreateRequest
|
|
14
|
+
from pydantic import BaseModel, ValidationError
|
|
10
15
|
|
|
11
16
|
from pipekit_sdk._helpers import is_valid_uuid
|
|
12
17
|
from pipekit_sdk.models.model import Cluster, Logs, Pipe, PipeRun
|
|
13
18
|
|
|
14
19
|
|
|
20
|
+
class _LoginResponse(BaseModel):
|
|
21
|
+
"""LoginResponse is a model for Login responses."""
|
|
22
|
+
|
|
23
|
+
token_type: str
|
|
24
|
+
access_token: str
|
|
25
|
+
expires_in: int
|
|
26
|
+
refresh_token: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _AccessToken(BaseModel):
|
|
30
|
+
"""AccessToken is a model for decoded access tokens."""
|
|
31
|
+
|
|
32
|
+
identityUUID: str
|
|
33
|
+
ClusterUUID: str
|
|
34
|
+
OrgUUID: str
|
|
35
|
+
isActive: bool
|
|
36
|
+
products: List[str]
|
|
37
|
+
iss: str
|
|
38
|
+
sub: str
|
|
39
|
+
exp: int
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _ValidationResult(Enum):
|
|
43
|
+
OK = 0
|
|
44
|
+
INVALID_TOKEN = 1
|
|
45
|
+
EXPIRED_TOKEN = 2
|
|
46
|
+
|
|
47
|
+
|
|
15
48
|
class PipekitService:
|
|
16
49
|
"""PipekitService is a wrapper around the Pipekit API. It provides a simple interface to interact with Pipekit."""
|
|
17
50
|
|
|
51
|
+
@staticmethod
|
|
52
|
+
def __from_home_dir() -> Optional[_LoginResponse]:
|
|
53
|
+
home = Path.home()
|
|
54
|
+
pipekit_token_file_path = os.path.join(home, ".pipekit", "token")
|
|
55
|
+
try:
|
|
56
|
+
with open(pipekit_token_file_path, "r") as f:
|
|
57
|
+
raw_data = f.read()
|
|
58
|
+
return _LoginResponse.model_validate_json(raw_data)
|
|
59
|
+
except ValidationError:
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def __write_home_dir(login_resp: _LoginResponse):
|
|
64
|
+
home = Path.home()
|
|
65
|
+
pipekit_token_file_path = os.path.join(home, ".pipekit", "token")
|
|
66
|
+
with open(pipekit_token_file_path, "w") as f:
|
|
67
|
+
payload = login_resp.model_dump_json(indent=2)
|
|
68
|
+
f.write(payload)
|
|
69
|
+
|
|
18
70
|
def __init__(
|
|
19
71
|
self,
|
|
20
72
|
username: str = "",
|
|
@@ -38,8 +90,60 @@ class PipekitService:
|
|
|
38
90
|
self.access_token = token
|
|
39
91
|
return
|
|
40
92
|
|
|
93
|
+
maybe_resp = self.__from_home_dir()
|
|
94
|
+
self.token_data = maybe_resp
|
|
95
|
+
|
|
96
|
+
if self.token_data is not None:
|
|
97
|
+
self.__token_login_flow()
|
|
98
|
+
return
|
|
99
|
+
|
|
41
100
|
self.__login()
|
|
42
101
|
|
|
102
|
+
def __update_token(self) -> None:
|
|
103
|
+
login_url = f"{self.id_url}/api/id/v1/oauth/token"
|
|
104
|
+
assert self.token_data is not None, "Token data was None, this implies a bug"
|
|
105
|
+
login_request = {"grant_type": "refresh_token", "refresh_token": self.token_data.refresh_token}
|
|
106
|
+
login_response = requests.post(
|
|
107
|
+
login_url,
|
|
108
|
+
data=json.dumps(login_request),
|
|
109
|
+
headers={"Content-Type": "application/json"},
|
|
110
|
+
timeout=10,
|
|
111
|
+
)
|
|
112
|
+
if login_response.status_code // 100 != 2:
|
|
113
|
+
raise Exception(
|
|
114
|
+
"Couldn't refresh access token, obtained non 2xx status code, try logging in again via the cli"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
login_response_model = _LoginResponse.model_validate(login_response.json())
|
|
118
|
+
self.__write_home_dir(login_response_model)
|
|
119
|
+
self.access_token = login_response_model.access_token
|
|
120
|
+
|
|
121
|
+
def __validate_token(self) -> _ValidationResult:
|
|
122
|
+
assert self.token_data is not None
|
|
123
|
+
try:
|
|
124
|
+
decoded_payload = jwt.decode(self.token_data.access_token, options={"verify_signature": False})
|
|
125
|
+
acc_tk = _AccessToken.model_validate(decoded_payload)
|
|
126
|
+
exp_time = datetime.fromtimestamp(acc_tk.exp)
|
|
127
|
+
now_time = datetime.now()
|
|
128
|
+
|
|
129
|
+
if exp_time <= now_time:
|
|
130
|
+
return _ValidationResult.EXPIRED_TOKEN
|
|
131
|
+
except jwt.PyJWTError:
|
|
132
|
+
return _ValidationResult.INVALID_TOKEN
|
|
133
|
+
return _ValidationResult.OK
|
|
134
|
+
|
|
135
|
+
def __token_login_flow(self) -> None:
|
|
136
|
+
assert self.token_data is not None, "token was None when it was expected to be valid"
|
|
137
|
+
res = self.__validate_token()
|
|
138
|
+
if res == _ValidationResult.EXPIRED_TOKEN:
|
|
139
|
+
self.__update_token()
|
|
140
|
+
elif res == _ValidationResult.INVALID_TOKEN:
|
|
141
|
+
raise RuntimeError(
|
|
142
|
+
"Token was invalid, if the `.pipekit/token` file is corrupted, please delete the file and login again"
|
|
143
|
+
)
|
|
144
|
+
else:
|
|
145
|
+
self.access_token = self.token_data.access_token
|
|
146
|
+
|
|
43
147
|
def __login(self) -> None:
|
|
44
148
|
login_request = {
|
|
45
149
|
"username": self.username,
|
|
@@ -486,3 +590,7 @@ class PipekitService:
|
|
|
486
590
|
|
|
487
591
|
for log in self.get_logs(run_uuid, pod_name=pod_name, container_name=container_name):
|
|
488
592
|
self.__print_log_message(log)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
if __name__ == "__main__":
|
|
596
|
+
p = PipekitService()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|