arkitekt-next 0.7.37__py3-none-any.whl → 0.7.39__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.

@@ -5,6 +5,7 @@ from cryptography.hazmat.primitives import serialization as crypto_serialization
5
5
  from cryptography.hazmat.primitives.asymmetric import rsa
6
6
  from cryptography.hazmat.backends import default_backend as crypto_default_backend
7
7
  from typing import Dict
8
+ from arkitekt_next.bloks.secret import SecretBlok
8
9
  from arkitekt_next.bloks.services.admin import AdminService
9
10
  from arkitekt_next.bloks.services.db import DBService
10
11
  from arkitekt_next.bloks.services.gateway import GatewayService
@@ -16,11 +17,11 @@ from dataclasses import asdict
16
17
 
17
18
  from arkitekt_next.bloks.services.redis import RedisService
18
19
  from blok import blok, InitContext, ExecutionContext, Option
20
+ from blok.bloks.services.dns import DnsService
19
21
  from blok.tree import YamlFile, Repo
20
22
  from blok import blok, InitContext
21
23
 
22
24
 
23
-
24
25
  DEFAULT_ARKITEKT_URL = "http://localhost:8000"
25
26
 
26
27
 
@@ -131,45 +132,45 @@ class LokBlok:
131
132
  self.tokens = []
132
133
  self.groups = []
133
134
  self.secret_key = secrets.token_hex(16)
134
- self.scopes = {"hallo": "welt"}
135
+ self.scopes = {"openid": "The open id connect scope"}
135
136
  self.key = None
136
137
  self.deployment_name = "default"
137
- self.base_routes = [
138
- "ht",
139
- "o",
140
- "graphql",
141
- ".well-known",
142
- "f",
143
- "admin",
144
- "static",
145
- "accounts",
146
- ]
147
138
  self.token_expiry_seconds = 700000
148
-
149
- def get_dependencies(self):
150
- return [
151
- "io.livekit.livekit",
152
- "live.arkitekt.postgres",
153
- "live.arkitekt.redis",
154
- "live.arkitekt.admin",
155
- "live.arkitekt.gateway",
156
- ]
139
+ self.preformed_redeem_tokens = [secrets.token_hex(16) for i in range(80)]
140
+ self.registered_tokens = {}
157
141
 
158
142
  def retrieve_credentials(self) -> LokCredentials:
159
143
  return LokCredentials(
160
144
  public_key=self.public_key, key_type="RS256", issuer="lok"
161
145
  )
162
146
 
163
- def retrieve_labels(self, service_name: str) -> list[str]:
147
+ def retrieve_labels(self, service_name: str, builder_name: str) -> list[str]:
164
148
  return [
165
149
  f"fakts.service={service_name}",
166
- f"fakts.builder=arkitekt.{service_name}",
150
+ f"fakts.builder={builder_name}",
167
151
  ]
168
152
 
153
+ def retrieve_token(self, user: str = "admin") -> str:
154
+ new_token = self.secret_blok.retrieve_secret()
155
+ self.registered_tokens[user] = new_token
156
+
157
+ return new_token
158
+
169
159
  def register_scopes(self, scopes_dict: Dict[str, str]) -> LokCredentials:
170
160
  self.scopes = self.scopes | scopes_dict
171
161
 
172
- def preflight(self, init: InitContext, gateway: GatewayService, db: DBService, redis: RedisService, admin: AdminService, livekit: LivekitService, scopes: list[Dict[str, str]]):
162
+ def preflight(
163
+ self,
164
+ init: InitContext,
165
+ gateway: GatewayService,
166
+ db: DBService,
167
+ redis: RedisService,
168
+ admin: AdminService,
169
+ secrets: SecretBlok,
170
+ livekit: LivekitService,
171
+ dns: DnsService,
172
+ scopes: list[Dict[str, str]],
173
+ ):
173
174
  for key, value in init.kwargs.items():
174
175
  setattr(self, key, value)
175
176
 
@@ -178,15 +179,15 @@ class LokBlok:
178
179
 
179
180
  self.scopes = {scope["scope"]: scope["description"] for scope in scopes}
180
181
 
181
- for i in self.base_routes:
182
- gateway.expose(
183
- i, 80, self.host, strip_prefix=False
184
- )
182
+ gateway.expose("lok", 80, self.host, strip_prefix=False)
183
+ gateway.expose_mapped(".well-known", 80, self.host, "lok")
185
184
 
185
+ self.secret_blok = secrets
186
186
  self.postgress_access = db.register_db(self.host)
187
187
  self.redis_access = redis.register()
188
188
  self.admin_access = admin.retrieve()
189
189
  self.local_access = livekit.retrieve_access()
190
+ self.dns_result = dns.get_dns_result()
190
191
  self.initialized = True
191
192
 
192
193
  def build(self, context: ExecutionContext):
@@ -220,6 +221,12 @@ class LokBlok:
220
221
 
221
222
  db_service["command"] = self.command
222
223
 
224
+ trusted_origins = []
225
+
226
+ for i in self.dns_result.hostnames:
227
+ trusted_origins.append(f"http://{i}")
228
+ trusted_origins.append(f"https://{i}")
229
+
223
230
  configuration = YamlFile(
224
231
  **{
225
232
  "db": asdict(self.postgress_access),
@@ -230,17 +237,22 @@ class LokBlok:
230
237
  "hosts": ["*"],
231
238
  "secret_key": self.secret_key,
232
239
  },
233
- "redis": asdict(self.redis_access),
240
+ "redis": asdict(self.redis_access),
234
241
  "lok": asdict(self.retrieve_credentials()),
235
242
  "private_key": self.private_key,
236
243
  "public_key": self.public_key,
237
244
  "scopes": self.scopes,
238
- "redeem_tokens": [token for token in self.tokens],
245
+ "redeem_tokens": [
246
+ {"user": name, "token": token}
247
+ for name, token in self.registered_tokens.items()
248
+ ],
239
249
  "groups": [group for group in self.groups],
240
250
  "deployment": {"name": self.deployment_name},
241
251
  "livekit": asdict(self.local_access),
242
252
  "token_expire_seconds": self.token_expiry_seconds,
243
253
  "apps": [],
254
+ "force_script_name": "lok",
255
+ "csrf_trusted_origins": trusted_origins,
244
256
  }
245
257
  )
246
258
 
@@ -270,82 +282,82 @@ class LokBlok:
270
282
 
271
283
  with_fakts_url = Option(
272
284
  subcommand="db_name",
273
- help="The fakts url for connection",
285
+ help="The name of the database",
274
286
  default="db_name",
287
+ show_default=True,
275
288
  )
276
289
  with_users = Option(
277
290
  subcommand="users",
278
- help="The fakts url for connection",
291
+ help="Users that should be greated by default. Format is name:password",
279
292
  default=["admin:admin"],
280
293
  multiple=True,
281
294
  type=USER,
295
+ show_default=True,
282
296
  )
283
297
  with_groups = Option(
284
298
  subcommand="groups",
285
- help="The fakts url for connection",
299
+ help="Groups that should be greated by default. Format is name:description",
286
300
  default=["admin:admin_group"],
287
301
  multiple=True,
288
302
  type=GROUP,
289
- )
290
- with_redeem_token = Option(
291
- subcommand="tokens",
292
- help="The fakts url for connection",
293
- default=[],
294
- multiple=True,
295
- type=TOKEN,
303
+ show_default=True,
296
304
  )
297
305
  with_scopes = Option(
298
306
  subcommand="scopes",
299
- help="The scopes",
307
+ help="Additional scopes that should be created (normally handled by the bloks)",
300
308
  default=[f"{key}:{value}" for key, value in self.scopes.items()],
301
309
  multiple=True,
302
310
  type=SCOPE,
303
311
  )
304
312
  with_repo = Option(
305
313
  subcommand="with_repo",
306
- help="The fakts url for connection",
314
+ help="Which repo should we use when building the service? Only active if build_repo or mount_repo is active",
307
315
  default=self.repo,
316
+ show_default=True,
308
317
  )
309
318
  with_repo = Option(
310
319
  subcommand="command",
311
- help="The fakts url for connection",
320
+ help="Which command should be run when starting the service?",
312
321
  default=self.command,
322
+ show_default=True,
313
323
  )
314
324
  mount_repo = Option(
315
325
  subcommand="mount_repo",
316
326
  help="The fakts url for connection",
317
- is_flag=True,
327
+ type=bool,
318
328
  default=False,
319
329
  )
320
330
  build_repo = Option(
321
331
  subcommand="build_repo",
322
- help="The fakts url for connection",
323
- is_flag=True,
332
+ help="Should we build the container from the repo?",
333
+ type=bool,
324
334
  default=False,
335
+ show_default=True,
325
336
  )
326
337
  with_host = Option(
327
338
  subcommand="host",
328
- help="The fakts url for connection",
339
+ help="Which internal hostname should be used",
329
340
  default=self.host,
341
+ show_default=True,
330
342
  )
331
343
  #
332
344
  with_public_key = Option(
333
345
  subcommand="public_key",
334
- help="The fakts url for connection",
346
+ help="The public key for the JWT creation",
335
347
  default=public_key,
336
348
  required=True,
337
349
  callback=validate_public_key,
338
350
  )
339
351
  with_private_key = Option(
340
352
  subcommand="private_key",
341
- help="The fakts url for connection",
353
+ help="The corresponding private key for the JWT creation",
342
354
  default=private_key,
343
355
  callback=validate_private_key,
344
356
  required=True,
345
357
  )
346
358
  with_secret_key = Option(
347
359
  subcommand="secret_key",
348
- help="The fakts url for connection",
360
+ help="The secret key to use for the django service",
349
361
  default=self.secret_key,
350
362
  )
351
363
 
@@ -357,7 +369,6 @@ class LokBlok:
357
369
  with_groups,
358
370
  build_repo,
359
371
  with_host,
360
- with_redeem_token,
361
372
  with_private_key,
362
373
  with_public_key,
363
374
  with_scopes,
@@ -7,7 +7,11 @@ from blok import blok, InitContext
7
7
 
8
8
  from blok import blok, InitContext, ExecutionContext, Option
9
9
  from blok.tree import YamlFile, Repo
10
- from arkitekt_next.bloks.funcs import create_default_service_yaml
10
+ from arkitekt_next.bloks.funcs import (
11
+ create_default_service_dependencies,
12
+ create_default_service_yaml,
13
+ DefaultService,
14
+ )
11
15
 
12
16
 
13
17
  class AccessCredentials(BaseModel):
@@ -23,8 +27,11 @@ class MikroBlok:
23
27
  def __init__(self) -> None:
24
28
  self.host = "mikro"
25
29
  self.command = "bash run-debug.sh"
26
- self.repo = "https://github.com/jhnnsrs/mikro-server-next"
27
- self.scopes = {"read_image": "Read image from the database"}
30
+ self.repo = "https://github.com/arkitektio/mikro-server-next"
31
+ self.scopes = {
32
+ "mikro_read": "Read image from the database",
33
+ "mikro_write": "Write image to the database",
34
+ }
28
35
  self.image = "jhnnsrs/mikro:next"
29
36
  self.mount_repo = False
30
37
  self.build_repo = False
@@ -32,16 +39,10 @@ class MikroBlok:
32
39
  self.secret_key = secrets.token_hex(16)
33
40
 
34
41
  def get_dependencies(self):
35
- return [
36
- "live.arkitekt.mount",
37
- "live.arkitekt.config",
38
- "live.arkitekt.gateway",
39
- "live.arkitekt.postgres",
40
- "live.arkitekt.lok",
41
- "live.arkitekt.admin",
42
- "live.arkitekt.redis",
43
- "live.arkitekt.s3",
44
- ]
42
+ return create_default_service_dependencies()
43
+
44
+ def get_builder(self):
45
+ return "arkitekt.generic"
45
46
 
46
47
  def preflight(self, init: InitContext):
47
48
  for key, value in init.kwargs.items():
@@ -68,13 +69,13 @@ class MikroBlok:
68
69
  mount_repo = Option(
69
70
  subcommand="mount_repo",
70
71
  help="The fakts url for connection",
71
- is_flag=True,
72
+ type=bool,
72
73
  default=self.mount_repo,
73
74
  )
74
75
  build_repo = Option(
75
76
  subcommand="build_repo",
76
77
  help="The fakts url for connection",
77
- is_flag=True,
78
+ type=bool,
78
79
  default=self.build_repo,
79
80
  )
80
81
  with_host = Option(
@@ -1,5 +1,9 @@
1
1
  import click
2
2
 
3
+ from arkitekt_next.bloks.services.config import ConfigService
4
+ from arkitekt_next.bloks.services.gateway import GatewayService
5
+ from arkitekt_next.bloks.services.name import NameService
6
+ from arkitekt_next.bloks.services.secret import SecretService
3
7
  from blok import blok, InitContext, ExecutionContext, Option
4
8
  from blok.tree import YamlFile, Repo
5
9
  from pydantic import BaseModel
@@ -50,24 +54,21 @@ class MinioBlok:
50
54
  self.preformed_access_keys = [secrets.token_hex(16) for i in range(100)]
51
55
  self.preformed_secret_keys = [secrets.token_hex(16) for i in range(100)]
52
56
 
53
- def get_identifier(self):
54
- return "live.arkitekt.s3"
55
-
56
- def get_dependencies(self):
57
- return ["live.arkitekt.config", "live.arkitekt.gateway"]
58
-
59
57
  def create_buckets(self, buckets: list[str]) -> S3Credentials:
60
- new_access_key = self.preformed_access_keys.pop()
61
- new_secret_key = self.preformed_secret_keys.pop()
58
+ new_access_key = self.secret_service.retrieve_secret()
59
+ new_secret_key = self.secret_service.retrieve_secret()
62
60
 
63
61
  bucket_map = {}
64
62
 
65
63
  for bucket in buckets:
66
- bucket_map[bucket] = self.preformed_bucket_names.pop()
64
+ bucket_name = self.name_service.retrieve_name()
65
+ bucket_map[bucket] = bucket_name
66
+ self.gateway_service.expose(bucket_name, 9000, self.host)
67
67
 
68
68
  self.buckets.extend(bucket_map.values())
69
69
 
70
70
  creds = S3Credentials(
71
+ name=self.name_service.retrieve_name(),
71
72
  access_key=new_access_key,
72
73
  buckets=bucket_map,
73
74
  host=self.host,
@@ -81,21 +82,22 @@ class MinioBlok:
81
82
 
82
83
  return creds
83
84
 
84
- def preflight(self, init: InitContext):
85
+ def preflight(
86
+ self,
87
+ init: InitContext,
88
+ secret: SecretService,
89
+ name: NameService,
90
+ gateway: GatewayService,
91
+ config: ConfigService,
92
+ ):
85
93
  for key, value in init.kwargs.items():
86
94
  setattr(self, key, value)
87
95
 
88
- self.preformed_bucket_names = list(
89
- init.kwargs.get("preformed_bucket_names", [])
90
- )
91
- self.preformed_access_keys = list(init.kwargs.get("preformed_access_keys", []))
92
- self.preformed_secret_keys = list(init.kwargs.get("preformed_secret_keys", []))
93
-
94
- init.dependencies["live.arkitekt.gateway"].expose_default(9000, self.host)
96
+ self.secret_service = secret
97
+ self.name_service = name
98
+ self.gateway_service = gateway
95
99
 
96
- self.config_path = init.dependencies["live.arkitekt.config"].get_path(
97
- self.host + ".yaml"
98
- )
100
+ self.config_path = config.get_path(self.host + ".yaml")
99
101
 
100
102
  def build(self, context: ExecutionContext):
101
103
  minio_service_init = {
@@ -107,7 +109,7 @@ class MinioBlok:
107
109
  "environment": {
108
110
  "MINIO_ROOT_PASSWORD": self.password,
109
111
  "MINIO_ROOT_USER": self.username,
110
- "MINIO_HOST": f"{self.host}:9000",
112
+ "MINIO_HOST": f"http://{self.host}:9000",
111
113
  },
112
114
  "image": self.init_image,
113
115
  "volumes": [f"{self.config_path}:/workspace/config.yaml"],
@@ -132,7 +134,19 @@ class MinioBlok:
132
134
  )
133
135
 
134
136
  context.file_tree.set_nested(
135
- *self.config_path.split("/"), YamlFile(**{"buckets": list(self.buckets)})
137
+ *self.config_path.split("/"),
138
+ YamlFile(
139
+ buckets=[{"name": i} for i in self.buckets],
140
+ users=[
141
+ {
142
+ "access_key": i.access_key,
143
+ "secret_key": i.secret_key,
144
+ "name": i.name,
145
+ "policies": ["readwrite"],
146
+ }
147
+ for i in self.registered_clients
148
+ ],
149
+ ),
136
150
  )
137
151
 
138
152
  def get_options(self):
@@ -151,30 +165,9 @@ class MinioBlok:
151
165
  help="The fakts url for connection",
152
166
  default=self.password,
153
167
  )
154
- with_preformed_bucket_names = Option(
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 = Option(
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 = Option(
167
- subcommand="preformed_secret_keys",
168
- help="The fakts url for connection",
169
- multiple=True,
170
- default=self.preformed_secret_keys,
171
- )
172
168
 
173
169
  return [
174
170
  with_host,
175
171
  with_password,
176
172
  with_username,
177
- with_preformed_bucket_names,
178
- with_preformed_acces_key,
179
- with_preformed_secret_keys,
180
173
  ]
@@ -0,0 +1,34 @@
1
+ from pydantic import BaseModel
2
+ from typing import Dict, Any
3
+ from arkitekt_next.bloks.services.name import NameService
4
+ from blok import blok, InitContext, Option, ExecutionContext
5
+ from blok.tree import YamlFile
6
+ from arkitekt_next.bloks.services.secret import SecretService
7
+ import secrets
8
+ import namegenerator
9
+
10
+ @blok(NameService)
11
+ class PreformedNamesBlok:
12
+ def __init__(self) -> None:
13
+ self.preformed_names = [namegenerator.gen() for _ in range(100)]
14
+ self.used_names = []
15
+
16
+ def preflight(self, init: InitContext, preformed_names: list[str]):
17
+ self.preformed_names = list(preformed_names)
18
+ self.used_names = []
19
+
20
+ def retrieve_name(self) -> str:
21
+ name = self.preformed_names.pop()
22
+ self.used_names.append(name)
23
+ return name
24
+
25
+
26
+ def get_options(self):
27
+ config_path = Option(
28
+ subcommand="preformed_names",
29
+ help="A list of preformed names",
30
+ default=self.preformed_names,
31
+ multiple=True,
32
+ )
33
+
34
+ return [config_path]
@@ -65,7 +65,7 @@ class RedisBlok:
65
65
  with_skip = Option(
66
66
  subcommand="skip",
67
67
  help="Skip docker creation (if using external redis?)",
68
- is_flag=True,
68
+ type=bool,
69
69
  default=self.skip,
70
70
  )
71
71
  with_image = Option(
@@ -1,7 +1,10 @@
1
1
  from typing import Dict, Any
2
2
  import secrets
3
3
 
4
- from arkitekt_next.bloks.funcs import create_default_service_yaml
4
+ from arkitekt_next.bloks.funcs import (
5
+ create_default_service_dependencies,
6
+ create_default_service_yaml,
7
+ )
5
8
  from blok import blok, InitContext, ExecutionContext, Option
6
9
  from blok.tree import Repo, YamlFile
7
10
 
@@ -12,7 +15,10 @@ class RekuestBlok:
12
15
  self.host = "rekuest"
13
16
  self.command = "bash run-debug.sh"
14
17
  self.repo = "https://github.com/jhnnsrs/rekuest-server_next"
15
- self.scopes = {"read_image": "Read image from the database"}
18
+ self.scopes = {
19
+ "rekuest_agent": "Act as an agent",
20
+ "rekuest_call": "Call other apps with rekuest",
21
+ }
16
22
  self.mount_repo = False
17
23
  self.build_repo = False
18
24
  self.buckets = ["media"]
@@ -21,16 +27,10 @@ class RekuestBlok:
21
27
  self.image = "jhnnsrs/rekuest:next"
22
28
 
23
29
  def get_dependencies(self):
24
- return [
25
- "live.arkitekt.mount",
26
- "live.arkitekt.config",
27
- "live.arkitekt.gateway",
28
- "live.arkitekt.postgres",
29
- "live.arkitekt.lok",
30
- "live.arkitekt.admin",
31
- "live.arkitekt.redis",
32
- "live.arkitekt.s3",
33
- ]
30
+ return create_default_service_dependencies()
31
+
32
+ def get_builder(self):
33
+ return "arkitekt.rekuest"
34
34
 
35
35
  def preflight(self, init: InitContext):
36
36
  for key, value in init.kwargs.items():
@@ -47,23 +47,25 @@ class RekuestBlok:
47
47
  with_repo = Option(
48
48
  subcommand="with_repo",
49
49
  help="Which repo should we use when building the service? Only active if build_repo or mount_repo is active",
50
+ type=str,
50
51
  default=self.repo,
51
52
  )
52
53
  with_command = Option(
53
54
  subcommand="command",
54
55
  help="Which command should be run when starting the service",
56
+ type=str,
55
57
  default=self.command,
56
58
  )
57
59
  mount_repo = Option(
58
60
  subcommand="mount_repo",
59
61
  help="Should we mount the repo into the container?",
60
- is_flag=True,
62
+ type=bool,
61
63
  default=self.mount_repo,
62
64
  )
63
65
  build_repo = Option(
64
66
  subcommand="build_repo",
65
67
  help="Should we build the container from the repo?",
66
- is_flag=True,
68
+ type=bool,
67
69
  default=self.build_repo,
68
70
  )
69
71
  with_host = Option(
@@ -0,0 +1,33 @@
1
+ from pydantic import BaseModel
2
+ from typing import Dict, Any
3
+ from blok import blok, InitContext, Option, ExecutionContext
4
+ from blok.tree import YamlFile
5
+ from arkitekt_next.bloks.services.secret import SecretService
6
+ import secrets
7
+
8
+ @blok(SecretService)
9
+ class SecretBlok:
10
+ def __init__(self) -> None:
11
+ self.preformed_secrets = [secrets.token_urlsafe(32) for _ in range(100)]
12
+ self.registered_secrets = []
13
+
14
+ def preflight(self, init: InitContext, preformed_secrets: list[str]):
15
+ self.preformed_secrets = list(preformed_secrets)
16
+
17
+ def retrieve_secret(self) -> str:
18
+ new_secret = self.preformed_secrets.pop()
19
+ self.registered_secrets.append(new_secret)
20
+ return new_secret
21
+
22
+
23
+
24
+ def get_options(self):
25
+ config_path = Option(
26
+ subcommand="preformed_secrets",
27
+ help="Which path to use for configs",
28
+ default=self.preformed_secrets,
29
+ multiple=True,
30
+ show_default=False,
31
+ )
32
+
33
+ return [config_path]
@@ -0,0 +1,27 @@
1
+ from .admin import AdminService
2
+ from .lok import LokService
3
+ from .db import DBService
4
+ from .redis import RedisService
5
+ from .s3 import S3Service
6
+ from .config import ConfigService
7
+ from .mount import MountService
8
+ from .secret import SecretService
9
+ from .gateway import GatewayService
10
+ from .livekit import LivekitService
11
+
12
+
13
+ __all__ = [
14
+ "AdminService",
15
+ "LokService",
16
+ "DBService",
17
+ "RedisService",
18
+ "S3Service",
19
+ "ConfigService",
20
+ "MountService",
21
+ "SecretService",
22
+ "GatewayService",
23
+ "LivekitService",
24
+ "MountService",
25
+ "ConfigService",
26
+ "SecretService",
27
+ ]
@@ -8,13 +8,20 @@ from blok import blok, InitContext, service
8
8
 
9
9
  @blok("live.arkitekt.gateway")
10
10
  class GatewayService(Protocol):
11
+ def expose(
12
+ self, path_name: str, port: int, host: str, strip_prefix: bool = True
13
+ ): ...
11
14
 
12
- def expose(self, path_name: str, port: int, host: str, strip_prefix: bool = True):
13
- ...
15
+ def expose_mapped(self, path_name: str, port: int, host: str, to_name: str): ...
14
16
 
15
- def expose_default(self, port: int, host: str):
16
- ...
17
+ def expose_default(self, port: int, host: str): ...
17
18
 
18
- def expose_port(self, port: int, host: str, tls: bool = False):
19
- ...
20
-
19
+ def expose_port(self, port: int, host: str, tls: bool = False): ...
20
+
21
+ def expose_port_to(self, port: int, host: str, to_port: str, tls: bool = False): ...
22
+
23
+ def get_internal_host(self): ...
24
+
25
+ def get_https_port(self): ...
26
+
27
+ def get_http_port(self): ...