arkitekt-next 0.7.31__py3-none-any.whl → 0.7.33__py3-none-any.whl
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.
Potentially problematic release.
This version of arkitekt-next might be problematic. Click here for more details.
- arkitekt_next/__blok__.py +25 -0
- arkitekt_next/bloks/__init__.py +1 -0
- arkitekt_next/bloks/admin.py +53 -0
- arkitekt_next/bloks/arkitekt.py +51 -0
- arkitekt_next/bloks/fluss.py +138 -0
- arkitekt_next/bloks/gateway.py +153 -0
- arkitekt_next/bloks/kabinet.py +143 -0
- arkitekt_next/bloks/livekit.py +71 -0
- arkitekt_next/bloks/lok.py +345 -0
- arkitekt_next/bloks/mikro.py +152 -0
- arkitekt_next/bloks/minio.py +180 -0
- arkitekt_next/bloks/postgres.py +92 -0
- arkitekt_next/bloks/redis.py +86 -0
- arkitekt_next/bloks/services.py +2 -0
- arkitekt_next/cli/commands/run/dev.py +2 -0
- arkitekt_next/cli/commands/run/prod.py +1 -0
- arkitekt_next/cli/options.py +7 -0
- {arkitekt_next-0.7.31.dist-info → arkitekt_next-0.7.33.dist-info}/METADATA +1 -1
- {arkitekt_next-0.7.31.dist-info → arkitekt_next-0.7.33.dist-info}/RECORD +22 -8
- {arkitekt_next-0.7.31.dist-info → arkitekt_next-0.7.33.dist-info}/LICENSE +0 -0
- {arkitekt_next-0.7.31.dist-info → arkitekt_next-0.7.33.dist-info}/WHEEL +0 -0
- {arkitekt_next-0.7.31.dist-info → arkitekt_next-0.7.33.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from cryptography.hazmat.primitives import serialization as crypto_serialization
|
|
5
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
6
|
+
from cryptography.hazmat.backends import default_backend as crypto_default_backend
|
|
7
|
+
from typing import Dict
|
|
8
|
+
import yaml
|
|
9
|
+
import secrets
|
|
10
|
+
|
|
11
|
+
from blok import blok, InitContext, ExecutionContext, CLIOption
|
|
12
|
+
from blok.tree import YamlFile, Repo
|
|
13
|
+
from blok import blok, InitContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
DEFAULT_ARKITEKT_URL = "http://localhost:8000"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LokCredentials(BaseModel):
|
|
20
|
+
issuer: str
|
|
21
|
+
key_type: str
|
|
22
|
+
public_key: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Define a custom user type that will parse and validate the user input
|
|
26
|
+
class UserParamType(click.ParamType):
|
|
27
|
+
name = "user"
|
|
28
|
+
|
|
29
|
+
def convert(self, value, param, ctx):
|
|
30
|
+
if isinstance(value, dict):
|
|
31
|
+
return value
|
|
32
|
+
try:
|
|
33
|
+
name, password = value.split(":")
|
|
34
|
+
return {"name": name, "password": password}
|
|
35
|
+
except ValueError:
|
|
36
|
+
self.fail(
|
|
37
|
+
f"User '{value}' is not in the correct format. It should be 'name:password'.",
|
|
38
|
+
param,
|
|
39
|
+
ctx,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
USER = UserParamType()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Define a custom user type that will parse and validate the user input
|
|
47
|
+
class GroupParamType(click.ParamType):
|
|
48
|
+
name = "group"
|
|
49
|
+
|
|
50
|
+
def convert(self, value, param, ctx):
|
|
51
|
+
if isinstance(value, dict):
|
|
52
|
+
return value
|
|
53
|
+
try:
|
|
54
|
+
name, description = value.split(":")
|
|
55
|
+
return {"name": name, "description": description}
|
|
56
|
+
except ValueError:
|
|
57
|
+
self.fail(
|
|
58
|
+
f"User '{value}' is not in the correct format. It should be 'name:password'.",
|
|
59
|
+
param,
|
|
60
|
+
ctx,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
GROUP = GroupParamType()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RedeemTokenParamType(click.ParamType):
|
|
68
|
+
name = "redeem_token"
|
|
69
|
+
|
|
70
|
+
def convert(self, value, param, ctx):
|
|
71
|
+
if isinstance(value, dict):
|
|
72
|
+
assert "user" in value, f"scope is required {value}"
|
|
73
|
+
assert "token" in value, f"description is required {value}"
|
|
74
|
+
return value
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
user, token = value.split(":")
|
|
78
|
+
return {"user": user, "token": token}
|
|
79
|
+
except ValueError:
|
|
80
|
+
self.fail(
|
|
81
|
+
f"RedeemToken '{value}' is not in the correct format. It should be 'username:token'.",
|
|
82
|
+
param,
|
|
83
|
+
ctx,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
TOKEN = RedeemTokenParamType()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ScopeParamType(click.ParamType):
|
|
91
|
+
name = "scope"
|
|
92
|
+
|
|
93
|
+
def convert(self, value, param, ctx):
|
|
94
|
+
if isinstance(value, dict):
|
|
95
|
+
assert "scope" in value, f"scope is required {value}"
|
|
96
|
+
assert "description" in value, f"description is required {value}"
|
|
97
|
+
return value
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
name, description = value.split(":")
|
|
101
|
+
return {"scope": name, "description": description}
|
|
102
|
+
except ValueError:
|
|
103
|
+
self.fail(
|
|
104
|
+
f"Scopes '{value}' is not in the correct format. It should be 'scope:description'.",
|
|
105
|
+
param,
|
|
106
|
+
ctx,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
SCOPE = ScopeParamType()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@blok("live.arkitekt.lok")
|
|
114
|
+
class LokBlok:
|
|
115
|
+
db_name: str
|
|
116
|
+
|
|
117
|
+
def __init__(self) -> None:
|
|
118
|
+
self.db_name = "lok_db"
|
|
119
|
+
self.mount_repo = False
|
|
120
|
+
self.build_repo = False
|
|
121
|
+
self.private_key = None
|
|
122
|
+
self.public_key = None
|
|
123
|
+
self.host = "lok"
|
|
124
|
+
self.with_repo = False
|
|
125
|
+
self.command = "bash run-debug.sh"
|
|
126
|
+
self.repo = "https://github.com/jhnnsrs/lok-server-next"
|
|
127
|
+
self.users = []
|
|
128
|
+
self.tokens = []
|
|
129
|
+
self.groups = []
|
|
130
|
+
self.secret_key = secrets.token_hex(16)
|
|
131
|
+
self.scopes = {"hallo": "welt"}
|
|
132
|
+
self.key = None
|
|
133
|
+
|
|
134
|
+
def get_dependencies(self):
|
|
135
|
+
return [
|
|
136
|
+
"live.arkitekt.postgres",
|
|
137
|
+
"live.arkitekt.redis",
|
|
138
|
+
"live.arkitekt.admin",
|
|
139
|
+
"live.arkitekt.gateway",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
def retrieve_credentials(self) -> LokCredentials:
|
|
143
|
+
return LokCredentials(
|
|
144
|
+
public_key=self.public_key, key_type="RS256", issuer="lok"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def register_scopes(self, scopes_dict: Dict[str, str]) -> LokCredentials:
|
|
148
|
+
self.scopes = self.scopes | scopes_dict
|
|
149
|
+
|
|
150
|
+
def init(self, init: InitContext):
|
|
151
|
+
for key, value in init.kwargs.items():
|
|
152
|
+
setattr(self, key, value)
|
|
153
|
+
|
|
154
|
+
assert self.public_key, "Public key is required"
|
|
155
|
+
assert self.private_key, "Private key is required"
|
|
156
|
+
|
|
157
|
+
kwargs = init.kwargs
|
|
158
|
+
deps = init.dependencies
|
|
159
|
+
scopes = kwargs.get("scopes", [])
|
|
160
|
+
self.scopes = {scope["scope"]: scope["description"] for scope in scopes}
|
|
161
|
+
|
|
162
|
+
self.postgress_access = deps["live.arkitekt.postgres"].register_db(self.host)
|
|
163
|
+
self.redis_access = deps["live.arkitekt.redis"].register()
|
|
164
|
+
self.admin_access = deps["live.arkitekt.admin"].retrieve()
|
|
165
|
+
self.initialized = True
|
|
166
|
+
|
|
167
|
+
def build(self, context: ExecutionContext):
|
|
168
|
+
depends_on = []
|
|
169
|
+
|
|
170
|
+
if self.redis_access.dependency:
|
|
171
|
+
depends_on.append(self.redis_access.dependency)
|
|
172
|
+
|
|
173
|
+
if self.postgress_access.dependency:
|
|
174
|
+
depends_on.append(self.postgress_access.dependency)
|
|
175
|
+
|
|
176
|
+
db_service = {
|
|
177
|
+
"labels": ["fakts.service=live.arkitekt.lok", "fakts.builder=arkitekt.lok"],
|
|
178
|
+
"depends_on": depends_on,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if self.mount_repo:
|
|
182
|
+
context.file_tree.set_nested("mounts", "lok", Repo(self.repo))
|
|
183
|
+
db_service["volumes"] = ["./mounts/lok:/lok"]
|
|
184
|
+
|
|
185
|
+
if self.build_repo:
|
|
186
|
+
context.file_tree.set_nested("mounts", "lok", Repo(self.repo))
|
|
187
|
+
db_service["build"] = "./mounts/lok"
|
|
188
|
+
|
|
189
|
+
db_service["command"] = self.command
|
|
190
|
+
|
|
191
|
+
configuration = YamlFile(
|
|
192
|
+
**{
|
|
193
|
+
"db": self.postgress_access.dict(),
|
|
194
|
+
"users": [user for user in self.users],
|
|
195
|
+
"django": {
|
|
196
|
+
"admin": self.admin_access.dict(),
|
|
197
|
+
"debug": True,
|
|
198
|
+
"hosts": ["*"],
|
|
199
|
+
"secret_key": self.secret_key,
|
|
200
|
+
},
|
|
201
|
+
"redis": self.redis_access.dict(),
|
|
202
|
+
"lok": self.retrieve_credentials().dict(),
|
|
203
|
+
"private_key": self.private_key,
|
|
204
|
+
"public_key": self.public_key,
|
|
205
|
+
"scopes": self.scopes,
|
|
206
|
+
"redeem_tokens": [token for token in self.tokens],
|
|
207
|
+
"groups": [group for group in self.groups],
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
context.file_tree.set_nested("configs", "lok.yaml", configuration)
|
|
212
|
+
|
|
213
|
+
context.docker_compose.set_nested("services", self.host, db_service)
|
|
214
|
+
|
|
215
|
+
def get_options(self):
|
|
216
|
+
key = rsa.generate_private_key(
|
|
217
|
+
public_exponent=65537, key_size=2048, backend=crypto_default_backend()
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
private_key = key.private_bytes(
|
|
221
|
+
crypto_serialization.Encoding.PEM,
|
|
222
|
+
crypto_serialization.PrivateFormat.PKCS8,
|
|
223
|
+
crypto_serialization.NoEncryption(),
|
|
224
|
+
).decode()
|
|
225
|
+
|
|
226
|
+
public_key = (
|
|
227
|
+
key.public_key()
|
|
228
|
+
.public_bytes(
|
|
229
|
+
crypto_serialization.Encoding.OpenSSH,
|
|
230
|
+
crypto_serialization.PublicFormat.OpenSSH,
|
|
231
|
+
)
|
|
232
|
+
.decode()
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
with_fakts_url = CLIOption(
|
|
236
|
+
subcommand="db_name",
|
|
237
|
+
help="The fakts url for connection",
|
|
238
|
+
default="db_name",
|
|
239
|
+
)
|
|
240
|
+
with_users = CLIOption(
|
|
241
|
+
subcommand="users",
|
|
242
|
+
help="The fakts url for connection",
|
|
243
|
+
default=["admin:admin"],
|
|
244
|
+
multiple=True,
|
|
245
|
+
type=USER,
|
|
246
|
+
)
|
|
247
|
+
with_groups = CLIOption(
|
|
248
|
+
subcommand="groups",
|
|
249
|
+
help="The fakts url for connection",
|
|
250
|
+
default=["admin:admin_group"],
|
|
251
|
+
multiple=True,
|
|
252
|
+
type=GROUP,
|
|
253
|
+
)
|
|
254
|
+
with_redeem_token = CLIOption(
|
|
255
|
+
subcommand="tokens",
|
|
256
|
+
help="The fakts url for connection",
|
|
257
|
+
default=[],
|
|
258
|
+
multiple=True,
|
|
259
|
+
type=TOKEN,
|
|
260
|
+
)
|
|
261
|
+
with_scopes = CLIOption(
|
|
262
|
+
subcommand="scopes",
|
|
263
|
+
help="The scopes",
|
|
264
|
+
default=[f"{key}:{value}" for key, value in self.scopes.items()],
|
|
265
|
+
multiple=True,
|
|
266
|
+
type=SCOPE,
|
|
267
|
+
)
|
|
268
|
+
with_repo = CLIOption(
|
|
269
|
+
subcommand="with_repo",
|
|
270
|
+
help="The fakts url for connection",
|
|
271
|
+
default=self.repo,
|
|
272
|
+
)
|
|
273
|
+
with_repo = CLIOption(
|
|
274
|
+
subcommand="command",
|
|
275
|
+
help="The fakts url for connection",
|
|
276
|
+
default=self.command,
|
|
277
|
+
)
|
|
278
|
+
mount_repo = CLIOption(
|
|
279
|
+
subcommand="mount_repo",
|
|
280
|
+
help="The fakts url for connection",
|
|
281
|
+
is_flag=True,
|
|
282
|
+
default=False,
|
|
283
|
+
)
|
|
284
|
+
build_repo = CLIOption(
|
|
285
|
+
subcommand="build_repo",
|
|
286
|
+
help="The fakts url for connection",
|
|
287
|
+
is_flag=True,
|
|
288
|
+
default=False,
|
|
289
|
+
)
|
|
290
|
+
with_host = CLIOption(
|
|
291
|
+
subcommand="host",
|
|
292
|
+
help="The fakts url for connection",
|
|
293
|
+
default=self.host,
|
|
294
|
+
)
|
|
295
|
+
#
|
|
296
|
+
with_public_key = CLIOption(
|
|
297
|
+
subcommand="public_key",
|
|
298
|
+
help="The fakts url for connection",
|
|
299
|
+
default=public_key,
|
|
300
|
+
required=True,
|
|
301
|
+
callback=validate_public_key,
|
|
302
|
+
)
|
|
303
|
+
with_private_key = CLIOption(
|
|
304
|
+
subcommand="private_key",
|
|
305
|
+
help="The fakts url for connection",
|
|
306
|
+
default=private_key,
|
|
307
|
+
callback=validate_private_key,
|
|
308
|
+
required=True,
|
|
309
|
+
)
|
|
310
|
+
with_secret_key = CLIOption(
|
|
311
|
+
subcommand="secret_key",
|
|
312
|
+
help="The fakts url for connection",
|
|
313
|
+
default=self.secret_key,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
return [
|
|
317
|
+
with_fakts_url,
|
|
318
|
+
with_users,
|
|
319
|
+
with_repo,
|
|
320
|
+
mount_repo,
|
|
321
|
+
with_groups,
|
|
322
|
+
build_repo,
|
|
323
|
+
with_host,
|
|
324
|
+
with_redeem_token,
|
|
325
|
+
with_private_key,
|
|
326
|
+
with_public_key,
|
|
327
|
+
with_scopes,
|
|
328
|
+
with_secret_key,
|
|
329
|
+
]
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def validate_public_key(ctx, param, value):
|
|
333
|
+
if not value.startswith("ssh-rsa"):
|
|
334
|
+
raise click.BadParameter(
|
|
335
|
+
f"Public key must be in ssh-rsa format. Started with {value}"
|
|
336
|
+
)
|
|
337
|
+
return value
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def validate_private_key(ctx, param, value):
|
|
341
|
+
if not value.startswith("-----BEGIN PRIVATE KEY-----"):
|
|
342
|
+
raise click.BadParameter(
|
|
343
|
+
f"Private key must be in PEM format. Started with {value}"
|
|
344
|
+
)
|
|
345
|
+
return value
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
import yaml
|
|
5
|
+
import secrets
|
|
6
|
+
from blok import blok, InitContext
|
|
7
|
+
|
|
8
|
+
from blok import blok, InitContext, ExecutionContext, CLIOption
|
|
9
|
+
from blok.tree import YamlFile, Repo
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AccessCredentials(BaseModel):
|
|
13
|
+
password: str
|
|
14
|
+
username: str
|
|
15
|
+
host: str
|
|
16
|
+
port: str
|
|
17
|
+
db_name: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@blok("live.arkitekt.mikro")
|
|
21
|
+
class MikroBlok:
|
|
22
|
+
def __init__(self) -> None:
|
|
23
|
+
self.host = "mikro"
|
|
24
|
+
self.command = "bash run-debug.sh"
|
|
25
|
+
self.repo = "https://github.com/jhnnsrs/mikro-server-next"
|
|
26
|
+
self.scopes = {"read_image": "Read image from the database"}
|
|
27
|
+
self.mount_repo = True
|
|
28
|
+
self.build_repo = True
|
|
29
|
+
self.secret_key = secrets.token_hex(16)
|
|
30
|
+
|
|
31
|
+
def get_identifier(self):
|
|
32
|
+
return "live.arkitekt.mikro"
|
|
33
|
+
|
|
34
|
+
def get_dependencies(self):
|
|
35
|
+
return [
|
|
36
|
+
"live.arkitekt.gateway",
|
|
37
|
+
"live.arkitekt.postgres",
|
|
38
|
+
"live.arkitekt.lok",
|
|
39
|
+
"live.arkitekt.admin",
|
|
40
|
+
"live.arkitekt.redis",
|
|
41
|
+
"live.arkitekt.s3",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
def init(self, init: InitContext):
|
|
45
|
+
for key, value in init.kwargs.items():
|
|
46
|
+
setattr(self, key, value)
|
|
47
|
+
|
|
48
|
+
deps = init.dependencies
|
|
49
|
+
deps["live.arkitekt.lok"].register_scopes(self.scopes)
|
|
50
|
+
|
|
51
|
+
self.gateway_access = deps["live.arkitekt.gateway"].expose(
|
|
52
|
+
self.host, 80, self.host
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self.postgress_access = deps["live.arkitekt.postgres"].register_db(self.host)
|
|
56
|
+
self.redis_access = deps["live.arkitekt.redis"].register()
|
|
57
|
+
self.lok_access = deps["live.arkitekt.lok"].retrieve_credentials()
|
|
58
|
+
self.admin_access = deps["live.arkitekt.admin"].retrieve()
|
|
59
|
+
self.minio_access = deps["live.arkitekt.s3"].retrieve_credentials(
|
|
60
|
+
["zarr", "media"]
|
|
61
|
+
)
|
|
62
|
+
self.initialized = True
|
|
63
|
+
|
|
64
|
+
def build(self, context: ExecutionContext):
|
|
65
|
+
depends_on = []
|
|
66
|
+
|
|
67
|
+
if self.redis_access.dependency:
|
|
68
|
+
depends_on.append(self.redis_access.dependency)
|
|
69
|
+
|
|
70
|
+
if self.postgress_access.dependency:
|
|
71
|
+
depends_on.append(self.postgress_access.dependency)
|
|
72
|
+
|
|
73
|
+
db_service = {
|
|
74
|
+
"labels": [
|
|
75
|
+
"fakts.service=live.arkitekt.mikro",
|
|
76
|
+
"fakts.builder=arkitekt.mikro",
|
|
77
|
+
],
|
|
78
|
+
"depends_on": depends_on,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if self.mount_repo:
|
|
82
|
+
context.file_tree.set_nested("mounts", self.host, Repo(self.repo))
|
|
83
|
+
db_service["volumes"] = [f"./mounts/{self.host}:/workspace"]
|
|
84
|
+
|
|
85
|
+
if self.build_repo:
|
|
86
|
+
context.file_tree.set_nested("mounts", self.host, Repo(self.repo))
|
|
87
|
+
db_service["build"] = f"./mounts/{self.host}"
|
|
88
|
+
|
|
89
|
+
db_service["command"] = self.command
|
|
90
|
+
|
|
91
|
+
configuration = YamlFile(
|
|
92
|
+
**{
|
|
93
|
+
"db": self.postgress_access.dict(),
|
|
94
|
+
"django": {
|
|
95
|
+
"admin": self.admin_access.dict(),
|
|
96
|
+
"debug": True,
|
|
97
|
+
"hosts": ["*"],
|
|
98
|
+
"secret_key": self.secret_key,
|
|
99
|
+
},
|
|
100
|
+
"redis": self.redis_access.dict(),
|
|
101
|
+
"lok": self.lok_access.dict(),
|
|
102
|
+
"s3": self.minio_access.dict(),
|
|
103
|
+
"scopes": self.scopes,
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
context.file_tree.set_nested("configs", "mikro.yaml", configuration)
|
|
108
|
+
|
|
109
|
+
context.docker_compose.set_nested("services", self.host, db_service)
|
|
110
|
+
|
|
111
|
+
def get_options(self):
|
|
112
|
+
with_repo = CLIOption(
|
|
113
|
+
subcommand="with_repo",
|
|
114
|
+
help="The fakts url for connection",
|
|
115
|
+
default=self.repo,
|
|
116
|
+
)
|
|
117
|
+
with_command = CLIOption(
|
|
118
|
+
subcommand="command",
|
|
119
|
+
help="The fakts url for connection",
|
|
120
|
+
default=self.command,
|
|
121
|
+
)
|
|
122
|
+
mount_repo = CLIOption(
|
|
123
|
+
subcommand="mount_repo",
|
|
124
|
+
help="The fakts url for connection",
|
|
125
|
+
is_flag=True,
|
|
126
|
+
default=True,
|
|
127
|
+
)
|
|
128
|
+
build_repo = CLIOption(
|
|
129
|
+
subcommand="build_repo",
|
|
130
|
+
help="The fakts url for connection",
|
|
131
|
+
is_flag=True,
|
|
132
|
+
default=True,
|
|
133
|
+
)
|
|
134
|
+
with_host = CLIOption(
|
|
135
|
+
subcommand="host",
|
|
136
|
+
help="The fakts url for connection",
|
|
137
|
+
default=self.host,
|
|
138
|
+
)
|
|
139
|
+
with_secret_key = CLIOption(
|
|
140
|
+
subcommand="secret_key",
|
|
141
|
+
help="The fakts url for connection",
|
|
142
|
+
default=self.secret_key,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return [
|
|
146
|
+
with_repo,
|
|
147
|
+
mount_repo,
|
|
148
|
+
build_repo,
|
|
149
|
+
with_host,
|
|
150
|
+
with_command,
|
|
151
|
+
with_secret_key,
|
|
152
|
+
]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from blok import blok, InitContext, ExecutionContext, CLIOption
|
|
4
|
+
from blok.tree import YamlFile, Repo
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from typing import Dict, Optional
|
|
7
|
+
import secrets
|
|
8
|
+
from blok import blok, InitContext
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class S3Credentials(BaseModel):
|
|
12
|
+
access_key: str
|
|
13
|
+
buckets: Dict[str, str]
|
|
14
|
+
host: str
|
|
15
|
+
port: int
|
|
16
|
+
secret_key: str
|
|
17
|
+
protocol: str
|
|
18
|
+
dependency: Optional[str] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BucketMapParamType(click.ParamType):
|
|
22
|
+
name = "redeem_token"
|
|
23
|
+
|
|
24
|
+
def convert(self, value, param, ctx):
|
|
25
|
+
if isinstance(value, dict):
|
|
26
|
+
return value
|
|
27
|
+
try:
|
|
28
|
+
user, token = value.split(":")
|
|
29
|
+
return {"user": user, "token": token}
|
|
30
|
+
except ValueError:
|
|
31
|
+
self.fail(
|
|
32
|
+
f"RedeemToken '{value}' is not in the correct format. It should be 'username:token'.",
|
|
33
|
+
param,
|
|
34
|
+
ctx,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
TOKEN = BucketMapParamType()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@blok("live.arkitekt.s3")
|
|
42
|
+
class MinioBlok:
|
|
43
|
+
db_name: str
|
|
44
|
+
|
|
45
|
+
def __init__(self) -> None:
|
|
46
|
+
self.users = []
|
|
47
|
+
self.username = secrets.token_hex(16)
|
|
48
|
+
self.password = secrets.token_hex(16)
|
|
49
|
+
self.protocol = "http"
|
|
50
|
+
self.host = "minio"
|
|
51
|
+
self.port = 9000
|
|
52
|
+
self.skip = False
|
|
53
|
+
self.scopes = {}
|
|
54
|
+
self.buckets = []
|
|
55
|
+
self.registered_clients = []
|
|
56
|
+
self.preformed_bucket_names = [secrets.token_hex(16) for i in range(100)]
|
|
57
|
+
self.preformed_access_keys = [secrets.token_hex(16) for i in range(100)]
|
|
58
|
+
self.preformed_secret_keys = [secrets.token_hex(16) for i in range(100)]
|
|
59
|
+
|
|
60
|
+
def get_identifier(self):
|
|
61
|
+
return "live.arkitekt.s3"
|
|
62
|
+
|
|
63
|
+
def get_dependencies(self):
|
|
64
|
+
return []
|
|
65
|
+
|
|
66
|
+
def retrieve_credentials(self, buckets: list[str]) -> S3Credentials:
|
|
67
|
+
new_access_key = self.preformed_access_keys.pop()
|
|
68
|
+
new_secret_key = self.preformed_secret_keys.pop()
|
|
69
|
+
|
|
70
|
+
bucket_map = {}
|
|
71
|
+
|
|
72
|
+
for bucket in buckets:
|
|
73
|
+
bucket_map[bucket] = self.preformed_bucket_names.pop()
|
|
74
|
+
|
|
75
|
+
self.buckets.extend(bucket_map.values())
|
|
76
|
+
|
|
77
|
+
creds = S3Credentials(
|
|
78
|
+
access_key=new_access_key,
|
|
79
|
+
buckets=bucket_map,
|
|
80
|
+
host=self.host,
|
|
81
|
+
port=self.port,
|
|
82
|
+
secret_key=new_secret_key,
|
|
83
|
+
protocol=self.protocol,
|
|
84
|
+
dependency=self.host if not self.skip else None,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
self.registered_clients.append(creds)
|
|
88
|
+
|
|
89
|
+
return creds
|
|
90
|
+
|
|
91
|
+
def init(self, init: InitContext):
|
|
92
|
+
for key, value in init.kwargs.items():
|
|
93
|
+
setattr(self, key, value)
|
|
94
|
+
|
|
95
|
+
self.preformed_bucket_names = list(
|
|
96
|
+
init.kwargs.get("preformed_bucket_names", [])
|
|
97
|
+
)
|
|
98
|
+
self.preformed_access_keys = list(init.kwargs.get("preformed_access_keys", []))
|
|
99
|
+
self.preformed_secret_keys = list(init.kwargs.get("preformed_secret_keys", []))
|
|
100
|
+
|
|
101
|
+
def build(self, context: ExecutionContext):
|
|
102
|
+
minio_service_init = {
|
|
103
|
+
"depends_on": {
|
|
104
|
+
"minio": {
|
|
105
|
+
"condition": "service_started",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
"environment": {
|
|
109
|
+
"MINIO_ROOT_PASSWORD": self.password,
|
|
110
|
+
"MINIO_ROOT_USER": self.username,
|
|
111
|
+
"MINIO_HOST": f"{self.host}:9000",
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
minio_service = {
|
|
116
|
+
"environment": {
|
|
117
|
+
"MINIO_ROOT_PASSWORD": self.password,
|
|
118
|
+
"MINIO_ROOT_USER": self.username,
|
|
119
|
+
},
|
|
120
|
+
"image": "minio/minio:RELEASE.2023-02-10T18-48-39Z",
|
|
121
|
+
"volumes": {
|
|
122
|
+
"./data": "/data",
|
|
123
|
+
},
|
|
124
|
+
"labels": ["fakts.service=live.arkitekt.s3", "fakts.builder=arkitekt.s3"],
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
context.file_tree.set_nested("data", {})
|
|
128
|
+
|
|
129
|
+
context.docker_compose.set_nested("services", self.host, minio_service)
|
|
130
|
+
context.docker_compose.set_nested(
|
|
131
|
+
"services", f"{self.host}_init", minio_service_init
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
configuration = YamlFile(**{"buckets": list(self.buckets)})
|
|
135
|
+
|
|
136
|
+
context.file_tree.set_nested("configs", "minio.yaml", configuration)
|
|
137
|
+
|
|
138
|
+
def get_options(self):
|
|
139
|
+
with_host = CLIOption(
|
|
140
|
+
subcommand="host",
|
|
141
|
+
help="The fakts url for connection",
|
|
142
|
+
default=self.host,
|
|
143
|
+
)
|
|
144
|
+
with_username = CLIOption(
|
|
145
|
+
subcommand="username",
|
|
146
|
+
help="The fakts url for connection",
|
|
147
|
+
default=self.username,
|
|
148
|
+
)
|
|
149
|
+
with_password = CLIOption(
|
|
150
|
+
subcommand="password",
|
|
151
|
+
help="The fakts url for connection",
|
|
152
|
+
default=self.password,
|
|
153
|
+
)
|
|
154
|
+
with_preformed_bucket_names = CLIOption(
|
|
155
|
+
subcommand="preformed_bucket_names",
|
|
156
|
+
help="The fakts url for connection",
|
|
157
|
+
multiple=True,
|
|
158
|
+
default=self.preformed_bucket_names,
|
|
159
|
+
)
|
|
160
|
+
with_preformed_acces_key = CLIOption(
|
|
161
|
+
subcommand="preformed_access_keys",
|
|
162
|
+
help="The fakts url for connection",
|
|
163
|
+
multiple=True,
|
|
164
|
+
default=self.preformed_access_keys,
|
|
165
|
+
)
|
|
166
|
+
with_preformed_secret_keys = CLIOption(
|
|
167
|
+
subcommand="preformed_secret_keys",
|
|
168
|
+
help="The fakts url for connection",
|
|
169
|
+
multiple=True,
|
|
170
|
+
default=self.preformed_secret_keys,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return [
|
|
174
|
+
with_host,
|
|
175
|
+
with_password,
|
|
176
|
+
with_username,
|
|
177
|
+
with_preformed_bucket_names,
|
|
178
|
+
with_preformed_acces_key,
|
|
179
|
+
with_preformed_secret_keys,
|
|
180
|
+
]
|