truefoundry 0.7.0rc4__py3-none-any.whl → 0.7.0rc5__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 truefoundry might be problematic. Click here for more details.

@@ -1,6 +1,7 @@
1
1
  import rich_click as click
2
2
 
3
3
  from truefoundry.cli.const import GROUP_CLS
4
+ from truefoundry.common.utils import is_internal_env_set
4
5
  from truefoundry.deploy.cli.commands.k8s_exec_credential_command import (
5
6
  k8s_exec_credential_command,
6
7
  )
@@ -17,13 +18,13 @@ def get_command():
17
18
 
18
19
  \b
19
20
  Supported resources:
20
- - Kubernetes Exec Credential
21
21
  - Kubeconfig
22
22
  """
23
23
  pass
24
24
 
25
25
 
26
26
  def get_get_command():
27
- get_command.add_command(k8s_exec_credential_command)
28
27
  get_command.add_command(kubeconfig_command)
28
+ if is_internal_env_set():
29
+ get_command.add_command(k8s_exec_credential_command)
29
30
  return get_command
@@ -41,7 +41,7 @@ def k8s_exec_credential_command(cluster: str) -> None:
41
41
  server_url: Optional[str] = get_cluster_server_url(kubeconfig, cluster)
42
42
  if not server_url:
43
43
  raise click.ClickException(
44
- f"Context `{CONTEXT_NAME_FORMAT.format(cluster=cluster)}` for cluster `{cluster}` not found in kubeconfig. \n\nPlease run `tfy get kubeconfig --cluster {cluster}` first."
44
+ f"\nContext {CONTEXT_NAME_FORMAT.format(cluster=cluster)!r} for cluster {cluster!r} not found in kubeconfig. \n\nPlease run 'tfy get kubeconfig --cluster {cluster}' first."
45
45
  )
46
46
  host: str = f"{urlparse(server_url).scheme}://{urlparse(server_url).netloc}"
47
47
  login(host=host, output_hook=NoOutputCallBack())
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Dict, Optional
2
2
  from urllib.parse import urljoin
3
3
 
4
+ import questionary
4
5
  import rich_click as click
5
6
  from rich.console import Console
6
7
 
@@ -19,55 +20,94 @@ from truefoundry.deploy.cli.commands.utils import (
19
20
  console = Console()
20
21
 
21
22
 
22
- def construct_k8s_proxy_server(host: str, cluster: str) -> str:
23
- """Construct the Kubernetes proxy server URL."""
23
+ def _select_cluster(cluster: Optional[str] = None) -> str:
24
+ """
25
+ Retrieve available clusters and either return the specified one after validation
26
+ or allow the user to interactively select from the list.
27
+ """
28
+ from truefoundry.deploy.lib.clients.servicefoundry_client import (
29
+ ServiceFoundryServiceClient,
30
+ )
31
+
32
+ clusters = ServiceFoundryServiceClient().list_clusters()
33
+
34
+ if not clusters:
35
+ raise click.ClickException("No clusters found in your account.")
36
+
37
+ if cluster:
38
+ if not any(c.id == cluster for c in clusters):
39
+ raise click.ClickException(
40
+ f"Cluster {cluster} not found. Either it does not exist or you might not be autthorized to access it"
41
+ )
42
+ return cluster
43
+
44
+ choices = {cluster.id: cluster for cluster in clusters}
45
+ cluster = questionary.select(
46
+ "Available Clusters:", choices=list(choices.keys())
47
+ ).ask()
48
+ if not cluster:
49
+ raise click.ClickException("No cluster selected.")
50
+ return cluster
51
+
52
+
53
+ def _construct_k8s_proxy_server(host: str, cluster: str) -> str:
54
+ """
55
+ Construct the Kubernetes proxy server URL.
56
+ """
24
57
  return urljoin(host, f"api/svc/v1/k8s/proxy/{cluster}")
25
58
 
26
59
 
27
- @click.command(
28
- name="kubeconfig",
29
- cls=COMMAND_CLS,
30
- help="Update kubeconfig with TrueFoundry cluster context",
31
- )
60
+ def _should_update_existing_context(cluster: str, kubeconfig: Dict[str, Any]) -> bool:
61
+ """
62
+ Prompt the user whether to overwrite an existing kubeconfig context.
63
+ """
64
+ server_url = get_cluster_server_url(kubeconfig, cluster)
65
+ if server_url is not None:
66
+ console.print(
67
+ f"\nContext {CONTEXT_NAME_FORMAT.format(cluster=cluster)!r} for cluster {cluster!r} already exists in kubeconfig.\n"
68
+ )
69
+ return click.confirm(
70
+ text="Do you want to update the context?", default=False, err=True
71
+ )
72
+ return True
73
+
74
+
75
+ @click.command(name="kubeconfig", cls=COMMAND_CLS)
32
76
  @click.option(
33
77
  "-c",
34
78
  "--cluster",
35
79
  type=str,
36
- required=True,
37
- help="The cluster id from TrueFoundry",
80
+ required=False,
81
+ help="The cluster id from TrueFoundry. If not provided, an interactive prompt will list available clusters",
38
82
  )
39
83
  @click.option(
40
84
  "--overwrite",
41
85
  is_flag=True,
42
86
  default=False,
43
87
  show_default=True,
44
- help="Overwrite if cluster entry already exists",
88
+ help="Overwrites existing cluster entry without prompting",
45
89
  )
46
90
  @handle_exception_wrapper
47
- def kubeconfig_command(cluster: str, overwrite: bool = False) -> None:
91
+ def kubeconfig_command(cluster: Optional[str] = None, overwrite: bool = False) -> None:
48
92
  """
49
- Execute kubectl commands with authentication through the TrueFoundry CLI.
93
+ Update kubeconfig file to access cluster attached to TrueFoundry Control Plane.
94
+
95
+ By default, credentials are written to ~/.kube/config. You can provide an alternate path by setting the KUBECONFIG environment variable. If KUBECONFIG contains multiple paths, the first one is used.
50
96
  """
51
97
  session = Session.new()
98
+ cluster = _select_cluster(cluster)
52
99
 
53
100
  path = get_kubeconfig_path()
54
- kubeconfig: Dict[str, Any] = get_kubeconfig_content(path=path)
55
- server_url: Optional[str] = get_cluster_server_url(kubeconfig, cluster)
56
- if server_url is not None and not overwrite:
57
- should_update = click.confirm(
58
- text=f"Context `{CONTEXT_NAME_FORMAT.format(cluster=cluster)}` for cluster `{cluster}` already exists in kubeconfig.\nDo you want to update the context?",
59
- default=False,
60
- err=True,
61
- )
62
- if not should_update:
63
- console.print(
64
- "Existing context found. Use --overwrite to force update the context."
65
- )
66
- return
101
+ kubeconfig = get_kubeconfig_content(path=path)
67
102
 
68
- k8s_proxy_server: str = construct_k8s_proxy_server(session.tfy_host, cluster)
103
+ if not overwrite and not _should_update_existing_context(cluster, kubeconfig):
104
+ console.print(
105
+ "Existing context found. Use '--overwrite' to force update the context."
106
+ )
107
+ return
69
108
 
70
- context_name: str = add_update_cluster_context(
109
+ k8s_proxy_server = _construct_k8s_proxy_server(session.tfy_host, cluster)
110
+ context_name = add_update_cluster_context(
71
111
  kubeconfig,
72
112
  cluster,
73
113
  k8s_proxy_server,
@@ -79,10 +119,11 @@ def kubeconfig_command(cluster: str, overwrite: bool = False) -> None:
79
119
  "--cluster",
80
120
  cluster,
81
121
  ],
122
+ envs={"TFY_INTERNAL": "1"},
82
123
  )
83
124
 
84
125
  save_kubeconfig(kubeconfig, path=path)
85
126
  console.print(
86
- f"Updated kubeconfig at {str(path)!r} with context {context_name!r} for cluster {cluster!r}\n"
87
- f"Run `kubectl config use-context {context_name}` to use this context\n"
127
+ f"\nUpdated kubeconfig at {str(path)!r} with context {context_name!r} for cluster {cluster!r}\n\n"
128
+ f"Run 'kubectl config use-context {context_name}' to use this context.\n"
88
129
  )
@@ -32,6 +32,7 @@ from truefoundry.deploy._autogen import models as autogen_models
32
32
  from truefoundry.deploy.io.output_callback import OutputCallBack
33
33
  from truefoundry.deploy.lib.model.entity import (
34
34
  Application,
35
+ Cluster,
35
36
  CreateDockerRepositoryResponse,
36
37
  Deployment,
37
38
  DockerRegistryCredentials,
@@ -110,6 +111,13 @@ class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
110
111
  )
111
112
  return request_handling(response)
112
113
 
114
+ @check_min_cli_version
115
+ def list_clusters(self) -> List[Cluster]:
116
+ url = f"{self._api_server_url}/{VERSION_PREFIX}/clusters"
117
+ response = session_with_retries().get(url, headers=self._get_headers())
118
+ response = request_handling(response)
119
+ return parse_obj_as(List[Cluster], response["data"])
120
+
113
121
  @check_min_cli_version
114
122
  def list_workspaces(
115
123
  self,
@@ -34,6 +34,11 @@ class Entity(Base):
34
34
  updatedAt: datetime.datetime = Field(repr=False)
35
35
 
36
36
 
37
+ class Cluster(Entity):
38
+ id: str
39
+ cloudProvider: str
40
+
41
+
37
42
  class Workspace(Entity):
38
43
  id: str = Field(repr=False)
39
44
  name: str
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: truefoundry
3
- Version: 0.7.0rc4
3
+ Version: 0.7.0rc5
4
4
  Summary: TrueFoundry CLI
5
5
  Author-email: TrueFoundry Team <abhishek@truefoundry.com>
6
6
  Requires-Python: <3.14,>=3.8.1
@@ -69,9 +69,9 @@ truefoundry/deploy/cli/commands/create_command.py,sha256=rCajvQvAfZU10nDZOYpRACb
69
69
  truefoundry/deploy/cli/commands/delete_command.py,sha256=8SriRwg5mEHL4zP0mdDjJ0cKWJTU_1Pjq3450zMr9tk,3889
70
70
  truefoundry/deploy/cli/commands/deploy_command.py,sha256=8aTBvzPaT9xg6KPmpcpqJlmdj4yXzWUfAy6slcoPN74,4123
71
71
  truefoundry/deploy/cli/commands/deploy_init_command.py,sha256=g-jBfrEmhZ0TDWsyqPDn4K6q33EqJSGmBTt1eMYig-w,600
72
- truefoundry/deploy/cli/commands/get_command.py,sha256=V_q-7Zb-4g1J7G8qAT5CRLW3ee2_gj_RiNZUMaNlXs0,779
73
- truefoundry/deploy/cli/commands/k8s_exec_credential_command.py,sha256=oXBRra3jZ_NcseNGdMbQJVPqapz8AelRoiiRnDcym_8,2158
74
- truefoundry/deploy/cli/commands/kubeconfig_command.py,sha256=jsIPltQ0aKrdsaOoSDaw-i3yJPJ6tySFDfjOugoBbWE,2616
72
+ truefoundry/deploy/cli/commands/get_command.py,sha256=bR8tAjQQhimzaTQ57L6BPJwcxQ_SGWCF5CqHDpxgG90,837
73
+ truefoundry/deploy/cli/commands/k8s_exec_credential_command.py,sha256=EknpdufMAEnjSGMG7a-Jj7tkoiS5zmbJRREafb14Alw,2160
74
+ truefoundry/deploy/cli/commands/kubeconfig_command.py,sha256=3u3A_scsdd1UnqxKQgGg8ivZArZKQYVV0Z-ATotbPPc,4176
75
75
  truefoundry/deploy/cli/commands/list_command.py,sha256=zKu_JWY35eMIzgSNFYtmwi2uezZ4k-8yk3C1Vqsvshc,4470
76
76
  truefoundry/deploy/cli/commands/login_command.py,sha256=EHZH3cDQdbVWMkuFxd7JfB0B0Dr89sUmt214ICBmspY,1024
77
77
  truefoundry/deploy/cli/commands/logout_command.py,sha256=u3kfrEp0ETbrz40KjD4GCC3XEZ5YRAlrca_Df4U_mk0,536
@@ -97,7 +97,7 @@ truefoundry/deploy/lib/session.py,sha256=fLdgR6ZDp8-hFl5NTON4ngnWLsMzGxvKtfpDOOw
97
97
  truefoundry/deploy/lib/util.py,sha256=J7r8San2wKo48A7-BlH2-OKTlBO67zlPjLEhMsL8os0,1059
98
98
  truefoundry/deploy/lib/win32.py,sha256=1RcvPTdlOAJ48rt8rCbE2Ufha2ztRqBAE9dueNXArrY,5009
99
99
  truefoundry/deploy/lib/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
- truefoundry/deploy/lib/clients/servicefoundry_client.py,sha256=NjNGmBsmrIfDZFfqJz12B4TVDHMZeAZDf-5eKSnS9zs,26697
100
+ truefoundry/deploy/lib/clients/servicefoundry_client.py,sha256=fmRlPYCimk1ZLbMgdzfJVCbcKRCVnFYL5T3j2uJA0Tc,27037
101
101
  truefoundry/deploy/lib/dao/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
102
  truefoundry/deploy/lib/dao/application.py,sha256=oMszpueXPUfTUuN_XdKwoRjQyqAgWHhZ-10cbprCVdM,9226
103
103
  truefoundry/deploy/lib/dao/apply.py,sha256=5IFERe5sLmZGlavaKTIxL4xPHAme4ZS2Ww0a2rKTyT0,3029
@@ -105,7 +105,7 @@ truefoundry/deploy/lib/dao/delete.py,sha256=uPL2psqWNw2O0oDikXJOlVxmG8n5d3Z0Ia9q
105
105
  truefoundry/deploy/lib/dao/version.py,sha256=AtdW_4O1DPUKdfv2qy6iUJsZ_95vM6z0AqeEy3WDKs8,1130
106
106
  truefoundry/deploy/lib/dao/workspace.py,sha256=6YvfCgWDzAULI3Q6JswyZmP1CwJ5rM-ANsIFkbQia0Q,2349
107
107
  truefoundry/deploy/lib/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
- truefoundry/deploy/lib/model/entity.py,sha256=Up-DDOezkwM2tdqibfLdZO6jmT2pVq6SShB5sobBIGI,8531
108
+ truefoundry/deploy/lib/model/entity.py,sha256=Bp9sLB-M5INCpw5lPmFdygHWS1zvnLicnSiSCi2iqhQ,8591
109
109
  truefoundry/deploy/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
110
  truefoundry/deploy/v2/lib/__init__.py,sha256=WEiVMZXOVljzEE3tpGJil14liIn_PCDoACJ6b3tZ6sI,188
111
111
  truefoundry/deploy/v2/lib/deploy.py,sha256=Og7P6S3Gc8pXRudf2luk2IOb_5oMGoz-h0rQcLWaw7M,11708
@@ -376,7 +376,7 @@ truefoundry/workflow/remote_filesystem/__init__.py,sha256=LQ95ViEjJ7Ts4JcCGOxMPs
376
376
  truefoundry/workflow/remote_filesystem/logger.py,sha256=em2l7D6sw7xTLDP0kQSLpgfRRCLpN14Qw85TN7ujQcE,1022
377
377
  truefoundry/workflow/remote_filesystem/tfy_signed_url_client.py,sha256=xcT0wQmQlgzcj0nP3tJopyFSVWT1uv3nhiTIuwfXYeg,12342
378
378
  truefoundry/workflow/remote_filesystem/tfy_signed_url_fs.py,sha256=nSGPZu0Gyd_jz0KsEE-7w_BmnTD8CVF1S8cUJoxaCbc,13305
379
- truefoundry-0.7.0rc4.dist-info/METADATA,sha256=XhZrgwwmvk_TGO77NQa7Hzh69wSvwqDWQggZhQizke4,2410
380
- truefoundry-0.7.0rc4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
381
- truefoundry-0.7.0rc4.dist-info/entry_points.txt,sha256=xVjn7RMN-MW2-9f7YU-bBdlZSvvrwzhpX1zmmRmsNPU,98
382
- truefoundry-0.7.0rc4.dist-info/RECORD,,
379
+ truefoundry-0.7.0rc5.dist-info/METADATA,sha256=qY09MnUMrEQSVOCK9FYPjgcRNiK8UzEr4U1wp0xE35c,2410
380
+ truefoundry-0.7.0rc5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
381
+ truefoundry-0.7.0rc5.dist-info/entry_points.txt,sha256=xVjn7RMN-MW2-9f7YU-bBdlZSvvrwzhpX1zmmRmsNPU,98
382
+ truefoundry-0.7.0rc5.dist-info/RECORD,,