@@ -114,6 +114,15 @@ def _client_verify_tls(self):
114114
115115 @property
116116 def job_client (self ):
117+ """
118+ Get the Ray Job Submission Client for this cluster.
119+
120+ Note: If connecting to a cluster with mTLS enabled, ensure you have called
121+ cluster.wait_ready() first to automatically generate TLS certificates.
122+ """
123+ # Check for certificates before creating client
124+ self ._check_tls_certs_exist ()
125+
117126 k8client = get_api_client ()
118127 if self ._job_submission_client :
119128 return self ._job_submission_client
@@ -538,10 +547,46 @@ def details(self, print_to_console: bool = True) -> RayCluster:
538547 pretty_print .print_clusters ([cluster ])
539548 return cluster
540549
550+ def _check_tls_certs_exist (self ):
551+ """
552+ Check if TLS certificates exist and print helpful warning if not.
553+
554+ This is called by connection methods (cluster_uri, local_client_url, job_client)
555+ to help users debug mTLS connection issues.
556+ """
557+ from codeflare_sdk .common .utils .generate_cert import _get_tls_base_dir
558+ from pathlib import Path
559+
560+ cert_dir = _get_tls_base_dir () / f"{ self .config .name } -{ self .config .namespace } "
561+
562+ if not cert_dir .exists () or not (cert_dir / "tls.crt" ).exists ():
563+ print ("\n " + "=" * 70 )
564+ print ("⚠️ WARNING: TLS Certificates Not Found!" )
565+ print ("=" * 70 )
566+ print (f"Expected location: { cert_dir } " )
567+ print ()
568+ print ("TLS certificates are required for mTLS connections to Ray clusters." )
569+ print ("Without certificates, your connection will likely fail with a timeout" )
570+ print ("or TLS handshake error." )
571+ print ()
572+ print ("To fix this issue:" )
573+ print (" 1. Call cluster.wait_ready() after cluster.apply()" )
574+ print (" → This automatically generates certificates when cluster is ready" )
575+ print ()
576+ print (" 2. Or manually generate certificates:" )
577+ print (" from codeflare_sdk.common.utils import generate_cert" )
578+ print (f" generate_cert.generate_tls_cert('{ self .config .name } ', '{ self .config .namespace } ')" )
579+ print (f" generate_cert.export_env('{ self .config .name } ', '{ self .config .namespace } ')" )
580+ print ("=" * 70 + "\n " )
581+
541582 def cluster_uri (self ) -> str :
542583 """
543584 Returns a string containing the cluster's URI.
585+
586+ Note: If connecting to a cluster with mTLS enabled, ensure you have called
587+ cluster.wait_ready() first to automatically generate TLS certificates.
544588 """
589+ self ._check_tls_certs_exist ()
545590 return f"ray://{ self .config .name } -head-svc.{ self .config .namespace } .svc:10001"
546591
547592 def refresh_certificates (self ):
@@ -685,7 +730,13 @@ def local_client_url(self):
685730 Returns:
686731 str:
687732 The Ray client URL based on the ingress domain.
733+
734+ Note: If connecting to a cluster with mTLS enabled, ensure you have called
735+ cluster.wait_ready() first to automatically generate TLS certificates.
688736 """
737+ # Check if TLS certificates exist and provide helpful warning if not
738+ self ._check_tls_certs_exist ()
739+
689740 ingress_domain = _get_ingress_domain (self )
690741 return f"ray://{ ingress_domain } "
691742
0 commit comments