vantage6 5.0.0a22__py3-none-any.whl → 5.0.0a29__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 vantage6 might be problematic. Click here for more details.

Files changed (55) hide show
  1. tests_cli/test_client_script.py +23 -0
  2. tests_cli/test_server_cli.py +7 -6
  3. tests_cli/test_wizard.py +7 -7
  4. vantage6/cli/__build__ +1 -1
  5. vantage6/cli/algorithm/generate_algorithm_json.py +531 -0
  6. vantage6/cli/algostore/list.py +2 -1
  7. vantage6/cli/algostore/start.py +6 -6
  8. vantage6/cli/algostore/stop.py +3 -2
  9. vantage6/cli/cli.py +25 -0
  10. vantage6/cli/common/decorator.py +3 -1
  11. vantage6/cli/common/start.py +221 -12
  12. vantage6/cli/common/stop.py +90 -0
  13. vantage6/cli/common/utils.py +15 -20
  14. vantage6/cli/config.py +260 -0
  15. vantage6/cli/configuration_manager.py +8 -14
  16. vantage6/cli/configuration_wizard.py +66 -111
  17. vantage6/cli/context/__init__.py +2 -1
  18. vantage6/cli/context/algorithm_store.py +10 -7
  19. vantage6/cli/context/node.py +38 -54
  20. vantage6/cli/context/server.py +36 -5
  21. vantage6/cli/dev/create.py +88 -29
  22. vantage6/cli/dev/data/km_dataset.csv +2401 -0
  23. vantage6/cli/dev/remove.py +99 -98
  24. vantage6/cli/globals.py +24 -4
  25. vantage6/cli/node/common/__init__.py +6 -5
  26. vantage6/cli/node/new.py +4 -3
  27. vantage6/cli/node/remove.py +4 -2
  28. vantage6/cli/node/start.py +33 -42
  29. vantage6/cli/prometheus/monitoring_manager.py +146 -0
  30. vantage6/cli/prometheus/prometheus.yml +5 -0
  31. vantage6/cli/server/files.py +4 -2
  32. vantage6/cli/server/import_.py +7 -7
  33. vantage6/cli/server/list.py +2 -1
  34. vantage6/cli/server/new.py +25 -6
  35. vantage6/cli/server/shell.py +5 -4
  36. vantage6/cli/server/start.py +44 -213
  37. vantage6/cli/server/stop.py +36 -105
  38. vantage6/cli/server/version.py +5 -4
  39. vantage6/cli/template/algo_store_config.j2 +0 -1
  40. vantage6/cli/template/node_config.j2 +3 -1
  41. vantage6/cli/template/server_import_config.j2 +0 -2
  42. vantage6/cli/test/algo_test_scripts/algo_test_arguments.py +29 -0
  43. vantage6/cli/test/algo_test_scripts/algo_test_script.py +92 -0
  44. vantage6/cli/test/client_script.py +151 -0
  45. vantage6/cli/test/common/diagnostic_runner.py +2 -2
  46. vantage6/cli/test/feature_tester.py +5 -2
  47. vantage6/cli/use/context.py +46 -0
  48. vantage6/cli/use/namespace.py +55 -0
  49. vantage6/cli/utils.py +70 -4
  50. {vantage6-5.0.0a22.dist-info → vantage6-5.0.0a29.dist-info}/METADATA +15 -11
  51. vantage6-5.0.0a29.dist-info/RECORD +84 -0
  52. {vantage6-5.0.0a22.dist-info → vantage6-5.0.0a29.dist-info}/WHEEL +1 -1
  53. vantage6-5.0.0a22.dist-info/RECORD +0 -72
  54. {vantage6-5.0.0a22.dist-info → vantage6-5.0.0a29.dist-info}/entry_points.txt +0 -0
  55. {vantage6-5.0.0a22.dist-info → vantage6-5.0.0a29.dist-info}/top_level.txt +0 -0
@@ -2,12 +2,13 @@ import click
2
2
  import docker
3
3
  from colorama import Fore, Style
4
4
 
5
- from vantage6.common import info, warning, error
5
+ from vantage6.common import error, info, warning
6
6
  from vantage6.common.docker.addons import (
7
7
  check_docker_running,
8
8
  remove_container_if_exists,
9
9
  )
10
10
  from vantage6.common.globals import APPNAME, InstanceType
11
+
11
12
  from vantage6.cli.common.decorator import click_insert_context
12
13
  from vantage6.cli.context.algorithm_store import AlgorithmStoreContext
13
14
 
@@ -23,7 +24,7 @@ def cli_algo_store_stop(ctx: AlgorithmStoreContext, all_stores: bool):
23
24
  client = docker.from_env()
24
25
 
25
26
  running_stores = client.containers.list(
26
- filters={"label": f"{APPNAME}-type={InstanceType.ALGORITHM_STORE.value}"}
27
+ filters={"label": f"{APPNAME}-type={InstanceType.ALGORITHM_STORE}"}
27
28
  )
28
29
 
29
30
  if not running_stores:
vantage6/cli/cli.py CHANGED
@@ -24,6 +24,11 @@ from vantage6.cli.node.stop import cli_node_stop
24
24
  from vantage6.cli.node.version import cli_node_version
25
25
  from vantage6.cli.algorithm.create import cli_algorithm_create
26
26
  from vantage6.cli.algorithm.update import cli_algorithm_update
27
+ from vantage6.cli.algorithm.generate_algorithm_json import (
28
+ cli_algorithm_generate_algorithm_json,
29
+ )
30
+
31
+ # from vantage6.cli.test.client_script import cli_test_client_script
27
32
  from vantage6.cli.test.feature_tester import cli_test_features
28
33
 
29
34
  # from vantage6.cli.test.integration_test import cli_test_integration
@@ -34,6 +39,8 @@ from vantage6.cli.algostore.stop import cli_algo_store_stop
34
39
  from vantage6.cli.algostore.files import cli_algo_store_files
35
40
  from vantage6.cli.algostore.list import cli_algo_store_configuration_list
36
41
  from vantage6.cli.algostore.remove import cli_algo_store_remove
42
+ from vantage6.cli.use.context import cli_use_context
43
+ from vantage6.cli.use.namespace import cli_use_namespace
37
44
 
38
45
 
39
46
  # Define the server group
@@ -104,6 +111,9 @@ def cli_algorithm() -> None:
104
111
  # Define the commands for the algorithm group
105
112
  cli_algorithm.add_command(cli_algorithm_create, name="create")
106
113
  cli_algorithm.add_command(cli_algorithm_update, name="update")
114
+ cli_algorithm.add_command(
115
+ cli_algorithm_generate_algorithm_json, name="generate-algorithm-json"
116
+ )
107
117
 
108
118
 
109
119
  # Define the test group
@@ -117,6 +127,7 @@ def cli_test() -> None:
117
127
  # Define the commands for the test group
118
128
  cli_test.add_command(cli_test_features, name="feature-test")
119
129
  # cli_test.add_command(cli_test_integration, name="integration-test")
130
+ # cli_test.add_command(cli_test_client_script, name="client-script")
120
131
 
121
132
 
122
133
  # Define the algorithm-store group
@@ -137,6 +148,19 @@ cli_algo_store.add_command(cli_algo_store_configuration_list, name="list")
137
148
  cli_algo_store.add_command(cli_algo_store_remove, name="remove")
138
149
 
139
150
 
151
+ # Add the use group
152
+ @click.group(name="use")
153
+ def cli_use() -> None:
154
+ """
155
+ Manage Kubernetes context and namespace.
156
+ """
157
+
158
+
159
+ # Define the commands for the use group
160
+ cli_use.add_command(cli_use_context, name="context")
161
+ cli_use.add_command(cli_use_namespace, name="namespace")
162
+
163
+
140
164
  # Define the overall group
141
165
  @click.group(name="cli", context_settings={"show_default": True})
142
166
  def cli_complete() -> None:
@@ -155,3 +179,4 @@ cli_complete.add_command(cli_dev)
155
179
  cli_complete.add_command(cli_algorithm)
156
180
  cli_complete.add_command(cli_test)
157
181
  cli_complete.add_command(cli_algo_store)
182
+ cli_complete.add_command(cli_use)
@@ -1,11 +1,13 @@
1
1
  from functools import wraps
2
2
  from pathlib import Path
3
+
3
4
  import click
4
5
 
5
6
  from vantage6.common import error
6
7
  from vantage6.common.globals import InstanceType
8
+
7
9
  from vantage6.cli.configuration_wizard import select_configuration_questionaire
8
- from vantage6.cli.context import select_context_class, get_context
10
+ from vantage6.cli.context import get_context, select_context_class
9
11
 
10
12
 
11
13
  def click_insert_context(
@@ -1,29 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
1
4
  import os
2
- from threading import Thread
5
+ import re
6
+ import subprocess
3
7
  import time
8
+ from os import PathLike
9
+ from pathlib import Path
10
+ from threading import Thread
11
+
4
12
  import docker
5
- import enum
13
+ from colorama import Fore, Style
6
14
  from docker.client import DockerClient
7
15
  from docker.models.containers import Container
8
- from colorama import Fore, Style
9
16
  from sqlalchemy.engine.url import make_url
10
17
 
11
18
  from vantage6.common import error, info, warning
12
19
  from vantage6.common.context import AppContext
20
+ from vantage6.common.docker.addons import check_docker_running, pull_image
13
21
  from vantage6.common.globals import (
22
+ APPNAME,
14
23
  DEFAULT_ALGO_STORE_IMAGE,
24
+ DEFAULT_CHART_REPO,
25
+ DEFAULT_DOCKER_REGISTRY,
15
26
  DEFAULT_NODE_IMAGE,
16
27
  DEFAULT_SERVER_IMAGE,
17
28
  DEFAULT_UI_IMAGE,
18
29
  InstanceType,
19
- APPNAME,
20
- DEFAULT_DOCKER_REGISTRY,
21
30
  )
22
- from vantage6.common.docker.addons import check_docker_running, pull_image
23
- from vantage6.cli.context import AlgorithmStoreContext, ServerContext
31
+
24
32
  from vantage6.cli.common.utils import print_log_worker
25
- from vantage6.cli.utils import check_config_name_allowed
26
- from vantage6.cli.globals import ServerGlobals, AlgoStoreGlobals
33
+ from vantage6.cli.context import AlgorithmStoreContext, ServerContext
34
+ from vantage6.cli.globals import AlgoStoreGlobals, ServerGlobals
35
+ from vantage6.cli.utils import (
36
+ check_config_name_allowed,
37
+ validate_input_cmd_args,
38
+ )
27
39
 
28
40
 
29
41
  def check_for_start(ctx: AppContext, type_: InstanceType) -> DockerClient:
@@ -57,7 +69,7 @@ def check_for_start(ctx: AppContext, type_: InstanceType) -> DockerClient:
57
69
  )
58
70
  for server in running_servers:
59
71
  if server.name == f"{APPNAME}-{ctx.name}-{ctx.scope}-{type_}":
60
- error(f"Server {Fore.RED}{ctx.name}{Style.RESET_ALL} " "is already running")
72
+ error(f"Server {Fore.RED}{ctx.name}{Style.RESET_ALL} is already running")
61
73
  exit(1)
62
74
  return docker_client
63
75
 
@@ -279,6 +291,8 @@ def mount_database(
279
291
  return mount, environment_vars
280
292
 
281
293
 
294
+ # TODO v5+ remove this function, it is replaced by the `attach_logs` function in
295
+ # `vantage6.cli.common.utils`
282
296
  def attach_logs(container: Container, type_: InstanceType) -> None:
283
297
  """
284
298
  Attach container logs to the console if specified.
@@ -290,8 +304,6 @@ def attach_logs(container: Container, type_: InstanceType) -> None:
290
304
  type_ : InstanceType
291
305
  The type of instance to attach the logs for
292
306
  """
293
- if isinstance(type_, enum.Enum):
294
- type_ = type_.value
295
307
  logs = container.attach(stream=True, logs=True, stdout=True)
296
308
  Thread(target=print_log_worker, args=(logs,), daemon=True).start()
297
309
  while True:
@@ -304,3 +316,200 @@ def attach_logs(container: Container, type_: InstanceType) -> None:
304
316
  f"with {Fore.RED}v6 {type_} stop{Style.RESET_ALL}"
305
317
  )
306
318
  exit(0)
319
+
320
+
321
+ def helm_install(
322
+ release_name: str,
323
+ chart_name: str,
324
+ values_file: str | PathLike | None = None,
325
+ context: str | None = None,
326
+ namespace: str | None = None,
327
+ ) -> None:
328
+ """
329
+ Manage the `helm install` command.
330
+
331
+ Parameters
332
+ ----------
333
+ release_name : str
334
+ The name of the Helm release.
335
+ chart_name : str
336
+ The name of the Helm chart.
337
+ values_file : str, optional
338
+ A single values file to use with the `-f` flag.
339
+ context : str, optional
340
+ The Kubernetes context to use.
341
+ namespace : str, optional
342
+ The Kubernetes namespace to use.
343
+ """
344
+ # Input validation
345
+ validate_input_cmd_args(release_name, "release name")
346
+ validate_input_cmd_args(chart_name, "chart name")
347
+
348
+ values_file = Path(values_file) if values_file else None
349
+ if values_file and not values_file.is_file():
350
+ error(f"Helm chart values file does not exist: {values_file}")
351
+ return
352
+
353
+ validate_input_cmd_args(context, "context name", allow_none=True)
354
+ validate_input_cmd_args(namespace, "namespace name", allow_none=True)
355
+
356
+ # Create the command
357
+ command = [
358
+ "helm",
359
+ "install",
360
+ release_name,
361
+ chart_name,
362
+ "--repo",
363
+ DEFAULT_CHART_REPO,
364
+ "--devel", # ensure using latest version including pre-releases
365
+ ]
366
+
367
+ if values_file:
368
+ command.extend(["-f", str(values_file)])
369
+
370
+ if context:
371
+ command.extend(["--kube-context", context])
372
+
373
+ if namespace:
374
+ command.extend(["--namespace", namespace])
375
+
376
+ try:
377
+ subprocess.run(
378
+ command,
379
+ stdout=subprocess.DEVNULL,
380
+ check=True,
381
+ )
382
+ info(
383
+ f"Successfully installed release '{release_name}' using chart '{chart_name}'."
384
+ )
385
+ except subprocess.CalledProcessError as e:
386
+ error(f"Failed to install release '{release_name}': {e.stderr}")
387
+ except FileNotFoundError:
388
+ error(
389
+ "Helm command not found. Please ensure Helm is installed and available in the PATH."
390
+ )
391
+
392
+
393
+ def start_port_forward(
394
+ service_name: str,
395
+ service_port: int,
396
+ port: int,
397
+ ip: str | None,
398
+ context: str | None = None,
399
+ namespace: str | None = None,
400
+ ) -> None:
401
+ """
402
+ Port forward a kubernetes service.
403
+
404
+ Parameters
405
+ ----------
406
+ service_name : str
407
+ The name of the Kubernetes service to port forward.
408
+ service_port : int
409
+ The port on the service to forward.
410
+ port : int
411
+ The port to listen on.
412
+ ip : str | None
413
+ The IP address to listen on. If None, defaults to localhost.
414
+ context : str | None
415
+ The Kubernetes context to use.
416
+ namespace : str | None
417
+ The Kubernetes namespace to use.
418
+ """
419
+ # Input validation
420
+ validate_input_cmd_args(service_name, "service name")
421
+ if not isinstance(service_port, int) or service_port <= 0:
422
+ error(f"Invalid service port: {service_port}. Must be a positive integer.")
423
+ return
424
+
425
+ if not isinstance(port, int) or port <= 0:
426
+ error(f"Invalid local port: {port}. Must be a positive integer.")
427
+ return
428
+
429
+ if ip and not re.match(
430
+ r"^(localhost|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$", ip
431
+ ):
432
+ error(f"Invalid IP address: {ip}. Must be a valid IPv4 address or 'localhost'.")
433
+ return
434
+
435
+ validate_input_cmd_args(context, "context name", allow_none=True)
436
+ validate_input_cmd_args(namespace, "namespace name", allow_none=True)
437
+
438
+ # Check if the service is ready before starting port forwarding
439
+ info(f"Waiting for service '{service_name}' to become ready...")
440
+ start_time = time.time()
441
+ timeout = 300 # seconds
442
+ while time.time() - start_time < timeout:
443
+ try:
444
+ result = (
445
+ subprocess.check_output(
446
+ [
447
+ "kubectl",
448
+ "get",
449
+ "endpoints",
450
+ service_name,
451
+ "-o",
452
+ "jsonpath={.subsets[*].addresses[*].ip}",
453
+ ]
454
+ )
455
+ .decode()
456
+ .strip()
457
+ )
458
+
459
+ if result:
460
+ info(f"Service '{service_name}' is ready.")
461
+ break
462
+ except subprocess.CalledProcessError:
463
+ pass # ignore and retry
464
+
465
+ time.sleep(2)
466
+ else:
467
+ error(
468
+ f"Timeout: Service '{service_name}' has no ready endpoints after {timeout} seconds."
469
+ )
470
+ return
471
+
472
+ # Create the port forwarding command
473
+ if not ip:
474
+ ip = "localhost"
475
+
476
+ command = [
477
+ "kubectl",
478
+ "port-forward",
479
+ "--address",
480
+ ip,
481
+ f"service/{service_name}",
482
+ f"{port}:{service_port}",
483
+ ]
484
+
485
+ if context:
486
+ command.extend(["--context", context])
487
+
488
+ if namespace:
489
+ command.extend(["--namespace", namespace])
490
+
491
+ # Start the port forwarding process
492
+ try:
493
+ process = subprocess.Popen(
494
+ command,
495
+ stdout=subprocess.DEVNULL,
496
+ stderr=subprocess.PIPE,
497
+ start_new_session=True, # Start in new session to detach from parent
498
+ )
499
+
500
+ # Give the process a moment to start and check if it's still running
501
+ time.sleep(1)
502
+ if process.poll() is not None:
503
+ # Process has already terminated
504
+ e = process.stderr.read().decode() if process.stderr else "Unknown error"
505
+ error(f"Failed to start port forwarding: {e}")
506
+ return
507
+
508
+ info(
509
+ f"Port forwarding started: {ip}:{port} -> {service_name}:{service_port} "
510
+ f"(PID: {str(process.pid)})"
511
+ )
512
+ return
513
+ except Exception as e:
514
+ error(f"Failed to start port forwarding: {e}")
515
+ return
@@ -0,0 +1,90 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+
5
+
6
+ from vantage6.common import error, info, warning
7
+ from vantage6.cli.utils import validate_input_cmd_args
8
+
9
+
10
+ def stop_port_forward(service_name: str) -> None:
11
+ """
12
+ Stop the port forwarding process for a given service name.
13
+
14
+ Parameters
15
+ ----------
16
+ service_name : str
17
+ The name of the service whose port forwarding process should be terminated.
18
+ """
19
+ # Input validation
20
+ validate_input_cmd_args(service_name, "service name")
21
+
22
+ try:
23
+ # Find the process ID (PID) of the port forwarding command
24
+ result = subprocess.run(
25
+ ["pgrep", "-f", f"kubectl port-forward.*{service_name}"],
26
+ check=True,
27
+ text=True,
28
+ capture_output=True,
29
+ )
30
+ pids = result.stdout.strip().splitlines()
31
+
32
+ if not pids:
33
+ warning(f"No port forwarding process found for service '{service_name}'.")
34
+ return
35
+
36
+ for pid in pids:
37
+ subprocess.run(["kill", "-9", pid], check=True)
38
+ info(
39
+ f"Terminated port forwarding process for service '{service_name}' "
40
+ f"(PID: {pid})"
41
+ )
42
+ except subprocess.CalledProcessError as e:
43
+ error(f"Failed to terminate port forwarding: {e}")
44
+
45
+
46
+ def helm_uninstall(
47
+ release_name: str,
48
+ context: str | None = None,
49
+ namespace: str | None = None,
50
+ ) -> None:
51
+ """
52
+ Manage the `helm uninstall` command.
53
+
54
+ Parameters
55
+ ----------
56
+ release_name : str
57
+ The name of the Helm release to uninstall.
58
+ context : str, optional
59
+ The Kubernetes context to use.
60
+ namespace : str, optional
61
+ The Kubernetes namespace to use.
62
+ """
63
+ # Input validation
64
+ validate_input_cmd_args(release_name, "release name")
65
+ validate_input_cmd_args(context, "context name", allow_none=True)
66
+ validate_input_cmd_args(namespace, "namespace name", allow_none=True)
67
+
68
+ # Create the command
69
+ command = ["helm", "uninstall", release_name]
70
+
71
+ if context:
72
+ command.extend(["--kube-context", context])
73
+
74
+ if namespace:
75
+ command.extend(["--namespace", namespace])
76
+
77
+ try:
78
+ subprocess.run(
79
+ command,
80
+ stdout=subprocess.DEVNULL,
81
+ check=True,
82
+ )
83
+ info(f"Successfully uninstalled release '{release_name}'.")
84
+ except subprocess.CalledProcessError as e:
85
+ error(f"Failed to uninstall release '{release_name}': {e.stderr}")
86
+ except FileNotFoundError:
87
+ error(
88
+ "Helm command not found. Please ensure Helm is installed and available in "
89
+ "the PATH."
90
+ )
@@ -1,14 +1,15 @@
1
1
  import enum
2
- import questionary as q
3
- from colorama import Fore, Style
4
- import click
2
+ from subprocess import Popen
5
3
  from typing import Iterable
4
+
5
+ import click
6
6
  import docker
7
- from subprocess import Popen
7
+ import questionary as q
8
+ from colorama import Fore, Style
8
9
 
10
+ from vantage6.common import error, warning
11
+ from vantage6.common.globals import APPNAME, STRING_ENCODING, InstanceType
9
12
 
10
- from vantage6.common import warning, error
11
- from vantage6.common.globals import APPNAME, InstanceType, STRING_ENCODING
12
13
  from vantage6.cli.context import select_context_class
13
14
 
14
15
 
@@ -77,7 +78,7 @@ def get_running_servers(
77
78
  return [server.name for server in running_servers]
78
79
 
79
80
 
80
- def get_server_configuration_list(instance_type: InstanceType.SERVER) -> None:
81
+ def get_server_configuration_list(instance_type: InstanceType) -> None:
81
82
  """
82
83
  Print list of available server configurations.
83
84
 
@@ -89,11 +90,7 @@ def get_server_configuration_list(instance_type: InstanceType.SERVER) -> None:
89
90
  client = docker.from_env()
90
91
  ctx_class = select_context_class(instance_type)
91
92
 
92
- instance_type_value = (
93
- instance_type.value if isinstance(instance_type, enum.Enum) else instance_type
94
- )
95
-
96
- running_server_names = get_running_servers(client, instance_type_value)
93
+ running_server_names = get_running_servers(client, instance_type)
97
94
  header = "\nName" + (21 * " ") + "Status" + (10 * " ") + "System/User"
98
95
 
99
96
  click.echo(header)
@@ -107,26 +104,24 @@ def get_server_configuration_list(instance_type: InstanceType.SERVER) -> None:
107
104
  for config in configs:
108
105
  status = (
109
106
  running
110
- if f"{APPNAME}-{config.name}-system-{instance_type_value}"
111
- in running_server_names
107
+ if f"{APPNAME}-{config.name}-system-{instance_type}" in running_server_names
112
108
  else stopped
113
109
  )
114
- click.echo(f"{config.name:25}" f"{status:25} System ")
110
+ click.echo(f"{config.name:25}{status:25} System ")
115
111
 
116
112
  # user folders
117
113
  configs, f2 = ctx_class.available_configurations(system_folders=False)
118
114
  for config in configs:
119
115
  status = (
120
116
  running
121
- if f"{APPNAME}-{config.name}-user-{instance_type_value}"
122
- in running_server_names
117
+ if f"{APPNAME}-{config.name}-user-{instance_type}" in running_server_names
123
118
  else stopped
124
119
  )
125
- click.echo(f"{config.name:25}" f"{status:25} User ")
120
+ click.echo(f"{config.name:25}{status:25} User ")
126
121
 
127
122
  click.echo("-" * 85)
128
123
  if len(f1) + len(f2):
129
- warning(f"{Fore.RED}Failed imports: {len(f1)+len(f2)}{Style.RESET_ALL}")
124
+ warning(f"{Fore.RED}Failed imports: {len(f1) + len(f2)}{Style.RESET_ALL}")
130
125
 
131
126
 
132
127
  def print_log_worker(logs_stream: Iterable[bytes]) -> None:
@@ -177,6 +172,6 @@ def attach_logs(*labels: list[str]) -> None:
177
172
  labels : list[str]
178
173
  The labels to attach to
179
174
  """
180
- command = ["devspace", "logs", "--follow", "--label-selector", ",".join(labels)]
175
+ command = ["kubectl", "logs", "--follow", "--selector", ",".join(labels)]
181
176
  process = Popen(command, stdout=None, stderr=None)
182
177
  process.wait()