Skip to content

Commit 31485f4

Browse files
committed
Updated login functionality
1 parent c3cd83b commit 31485f4

File tree

6 files changed

+66
-186
lines changed

6 files changed

+66
-186
lines changed

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ kubernetes>=25.3.0,<27
55
codeflare-torchx==0.6.0.dev0
66
cryptography==40.0.2
77
executing==1.2.0
8-
jinja2==3.1.2

src/codeflare_sdk/cluster/auth.py

Lines changed: 42 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,12 @@
2020
"""
2121

2222
import abc
23-
import pathlib
24-
from kubernetes import config
25-
from jinja2 import Environment, FileSystemLoader
2623
import os
24+
from kubernetes import client, config
2725

2826
global path_set
2927
path_set = False
3028

31-
"""
32-
auth = KubeConfigFileAuthentication(
33-
kube_config_path="config"
34-
)
35-
auth.load_kube_config()
36-
37-
38-
"""
39-
4029

4130
class Authentication(metaclass=abc.ABCMeta):
4231
"""
@@ -83,7 +72,7 @@ def logout(self):
8372

8473
class TokenAuthentication(Authentication):
8574
"""
86-
`TokenAuthentication` is a subclass of `Authentication`. It can be used to authenticate to an OpenShift
75+
`TokenAuthentication` is a subclass of `Authentication`. It can be used to authenticate to a Kubernetes
8776
cluster when the user has an API token and the API server address.
8877
"""
8978

@@ -93,186 +82,88 @@ def __init__(
9382
server: str = None,
9483
skip_tls: bool = False,
9584
ca_cert_path: str = "/etc/pki/tls/certs/ca-bundle.crt",
96-
username: str = "user",
9785
):
9886
"""
9987
Initialize a TokenAuthentication object that requires a value for `token`, the API Token
100-
and `server`, the API server address for authenticating to an OpenShift cluster.
88+
and `server`, the API server address for authenticating to a Kubernetes cluster.
10189
"""
10290

10391
self.token = token
10492
self.server = server
10593
self.skip_tls = skip_tls
10694
self.ca_cert_path = ca_cert_path
107-
self.username = username
10895

10996
def login(self) -> str:
11097
"""
111-
This function is used to login to a Kubernetes cluster using the user's API token and API server address.
98+
This function is used to log in to a Kubernetes cluster using the user's API token and API server address.
11299
Depending on the cluster, a user can choose to login in with `--insecure-skip-tls-verify` by setting `skip_tls`
113100
to `True` or `--certificate-authority` by setting `skip_tls` to false and providing a path to a ca bundle with `ca_cert_path`.
114-
115-
If a user does not have a Kubernetes config file one is created from a template with the appropriate user functionality
116-
and if they do it is updated with new credentials.
117101
"""
118-
dir = pathlib.Path(__file__).parent.parent.resolve()
119-
home = os.path.expanduser("~")
102+
global path_set
103+
global api_client
120104
try:
121-
security = "insecure-skip-tls-verify: false"
105+
configuration = client.Configuration()
106+
configuration.api_key_prefix["authorization"] = "Bearer"
107+
configuration.host = self.server
108+
configuration.api_key["authorization"] = self.token
122109
if self.skip_tls == False:
123-
security = "certificate-authority: %s" % self.ca_cert_path
110+
configuration.ssl_ca_cert = self.ca_cert_path
124111
else:
125-
security = "insecure-skip-tls-verify: true"
126-
127-
env = Environment(
128-
loader=FileSystemLoader(f"{dir}/templates"),
129-
trim_blocks=True,
130-
lstrip_blocks=True,
131-
)
132-
template = env.get_template("config.yaml")
133-
server = self.server
134-
cluster_name = server[8:].replace(".", "-")
135-
# If there is no .kube folder it is created.
136-
if not os.path.isdir("%s/.kube" % home):
137-
os.mkdir("%s/.kube" % home)
138-
139-
# If a config file exists then it will be updated with new fields and values.
140-
if os.path.isfile("%s/.kube/config" % home):
141-
file = open(r"%s/.kube/config" % home, "r").readlines()
142-
write_file = open(r"%s/.kube/config" % home, "w")
143-
existing = False
144-
# Check for existing config
145-
for line in file:
146-
if self.server in line:
147-
existing = True
148-
149-
if existing == False:
150-
for line in file:
151-
# All of these fields are given new lines underneath with credentials info.
152-
if "clusters:" in line:
153-
write_file.write(line)
154-
write_file.write(
155-
"- cluster:\n %(security)s\n server: %(server)s\n name: %(cluster)s\n"
156-
% {
157-
"security": security,
158-
"server": self.server,
159-
"cluster": cluster_name,
160-
}
161-
)
162-
continue
163-
if "contexts:" in line:
164-
write_file.write(line)
165-
write_file.write(
166-
"- context:\n cluster: %(cluster)s\n namespace: default\n user: %(user)s/%(cluster)s\n name: default/%(cluster)s/%(user)s\n"
167-
% {"cluster": cluster_name, "user": self.username}
168-
)
169-
continue
170-
if "current-context:" in line:
171-
write_file.write(
172-
"current-context: default/{}/{}\n".format(
173-
cluster_name, self.username
174-
)
175-
)
176-
continue
177-
if "users:" in line:
178-
write_file.write(line)
179-
write_file.write(
180-
"- name: {}/{}\n user:\n token: {}\n".format(
181-
self.username, cluster_name, self.token
182-
)
183-
)
184-
continue
185-
186-
write_file.write(line)
187-
else:
188-
# If there is an existing config just update the token and username
189-
for line in file:
190-
if "users:" in line:
191-
write_file.write(line)
192-
write_file.write(
193-
"- name: {}/{}\n user:\n token: {}\n".format(
194-
self.username, cluster_name, self.token
195-
)
196-
)
197-
continue
198-
write_file.write(line)
112+
configuration.verify_ssl = False
113+
api_client = client.ApiClient(configuration)
114+
path_set = False
115+
return "Logged into %s" % self.server
116+
except client.ApiException as exception:
117+
return exception
199118

200-
response = "Updated config file at %s/.kube/config" % home
201-
else:
202-
# Create a new config file from the config template and store it in HOME/.kube
203-
file = open("%s/.kube/config" % home, "w")
204-
file.write(
205-
template.render(
206-
security=security,
207-
server=server,
208-
cluster=cluster_name,
209-
context_name="default/{}/{}".format(
210-
cluster_name, self.username
211-
),
212-
current_context="default/{}/{}".format(
213-
cluster_name, self.username
214-
),
215-
username="{}/{}".format(self.username, cluster_name),
216-
token=self.token,
217-
)
218-
)
219-
response = (
220-
"Logged in and created new config file at %s/.kube/config" % home
221-
)
222-
except:
223-
response = "Error logging in. Have you inputted correct credentials?"
224-
return response
119+
def api_config_handler():
120+
"""
121+
This function is used to load the api client if the user has logged in
122+
"""
123+
if api_client != None and path_set == False:
124+
return api_client
125+
else:
126+
return None
225127

226128
def logout(self) -> str:
227129
"""
228130
This function is used to logout of a Kubernetes cluster.
229131
"""
230-
home = os.path.expanduser("~")
231-
file = open(r"%s/.kube/config" % home, "r")
232-
lines = file.readlines()
233-
line_count = 0
234-
for line in lines:
235-
if (
236-
"- name: {}/{}".format(self.username, self.server[8:].replace(".", "-"))
237-
not in line.strip()
238-
):
239-
line_count = line_count + 1
240-
else:
241-
break
242-
# The name, user and token are removed from the config file
243-
with open(r"%s/.kube/config" % home, "w") as file:
244-
for number, line in enumerate(lines):
245-
if number not in [line_count, line_count + 1, line_count + 2]:
246-
file.write(line)
247-
print("logged out of user %s" % self.username)
132+
global path_set
133+
path_set = False
134+
global api_client
135+
api_client = None
248136

249137

250138
class KubeConfigFileAuthentication(KubeConfiguration):
251139
"""
252140
A class that defines the necessary methods for passing a user's own Kubernetes config file.
253-
Specifically this class defines the `load_kube_config()`, `config_check()` and `remove_config()` functions.
141+
Specifically this class defines the `load_kube_config()` and `config_check()` functions.
254142
"""
255143

256144
def __init__(self, kube_config_path: str = None):
257145
self.kube_config_path = kube_config_path
258146

259147
def load_kube_config(self):
148+
"""
149+
Function for loading a user's own predefined Kubernetes config file.
150+
"""
260151
global path_set
152+
global api_client
261153
try:
262154
path_set = True
263-
print("Loaded user config file at path %s" % self.kube_config_path)
264-
response = config.load_kube_config(self.kube_config_path)
155+
api_client = None
156+
config.load_kube_config(self.kube_config_path)
157+
response = "Loaded user config file at path %s" % self.kube_config_path
265158
except config.ConfigException:
266159
path_set = False
267160
raise Exception("Please specify a config file path")
268161
return response
269162

270163
def config_check():
271-
if path_set == False:
164+
"""
165+
Function for loading the config file at the default config location ~/.kube/config if the user has not
166+
specified their own config file or has logged in with their token and server.
167+
"""
168+
if path_set == False and api_client == None:
272169
config.load_kube_config()
273-
274-
def remove_config(self) -> str:
275-
global path_set
276-
path_set = False
277-
os.remove(self.kube_config_path)
278-
print("Removed config file")

src/codeflare_sdk/cluster/awload.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
from kubernetes import client, config
2727
from .cluster import _kube_api_error_handling
28-
from .auth import KubeConfigFileAuthentication
28+
from .auth import KubeConfigFileAuthentication, TokenAuthentication
2929

3030

3131
class AWManager:
@@ -60,7 +60,9 @@ def submit(self) -> None:
6060
"""
6161
try:
6262
KubeConfigFileAuthentication.config_check()
63-
api_instance = client.CustomObjectsApi()
63+
api_instance = client.CustomObjectsApi(
64+
TokenAuthentication.api_config_handler()
65+
)
6466
api_instance.create_namespaced_custom_object(
6567
group="mcad.ibm.com",
6668
version="v1beta1",
@@ -85,7 +87,9 @@ def remove(self) -> None:
8587

8688
try:
8789
KubeConfigFileAuthentication.config_check()
88-
api_instance = client.CustomObjectsApi()
90+
api_instance = client.CustomObjectsApi(
91+
TokenAuthentication.api_config_handler()
92+
)
8993
api_instance.delete_namespaced_custom_object(
9094
group="mcad.ibm.com",
9195
version="v1beta1",

src/codeflare_sdk/cluster/cluster.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
from ray.job_submission import JobSubmissionClient
2626

27-
from .auth import KubeConfigFileAuthentication
27+
from .auth import KubeConfigFileAuthentication, TokenAuthentication
2828
from ..utils import pretty_print
2929
from ..utils.generate_yaml import generate_appwrapper
3030
from .config import ClusterConfiguration
@@ -37,7 +37,6 @@
3737
)
3838

3939
from kubernetes import client, config
40-
4140
import yaml
4241
import executing
4342

@@ -116,7 +115,9 @@ def up(self):
116115
namespace = self.config.namespace
117116
try:
118117
KubeConfigFileAuthentication.config_check()
119-
api_instance = client.CustomObjectsApi()
118+
api_instance = client.CustomObjectsApi(
119+
TokenAuthentication.api_config_handler()
120+
)
120121
with open(self.app_wrapper_yaml) as f:
121122
aw = yaml.load(f, Loader=yaml.FullLoader)
122123
api_instance.create_namespaced_custom_object(
@@ -137,7 +138,9 @@ def down(self):
137138
namespace = self.config.namespace
138139
try:
139140
KubeConfigFileAuthentication.config_check()
140-
api_instance = client.CustomObjectsApi()
141+
api_instance = client.CustomObjectsApi(
142+
TokenAuthentication.api_config_handler()
143+
)
141144
api_instance.delete_namespaced_custom_object(
142145
group="mcad.ibm.com",
143146
version="v1beta1",
@@ -249,7 +252,9 @@ def cluster_dashboard_uri(self) -> str:
249252
"""
250253
try:
251254
KubeConfigFileAuthentication.config_check()
252-
api_instance = client.CustomObjectsApi()
255+
api_instance = client.CustomObjectsApi(
256+
TokenAuthentication.api_config_handler()
257+
)
253258
routes = api_instance.list_namespaced_custom_object(
254259
group="route.openshift.io",
255260
version="v1",
@@ -366,7 +371,7 @@ def _kube_api_error_handling(e: Exception): # pragma: no cover
366371
def _app_wrapper_status(name, namespace="default") -> Optional[AppWrapper]:
367372
try:
368373
KubeConfigFileAuthentication.config_check()
369-
api_instance = client.CustomObjectsApi()
374+
api_instance = client.CustomObjectsApi(TokenAuthentication.api_config_handler())
370375
aws = api_instance.list_namespaced_custom_object(
371376
group="mcad.ibm.com",
372377
version="v1beta1",
@@ -385,7 +390,7 @@ def _app_wrapper_status(name, namespace="default") -> Optional[AppWrapper]:
385390
def _ray_cluster_status(name, namespace="default") -> Optional[RayCluster]:
386391
try:
387392
KubeConfigFileAuthentication.config_check()
388-
api_instance = client.CustomObjectsApi()
393+
api_instance = client.CustomObjectsApi(TokenAuthentication.api_config_handler())
389394
rcs = api_instance.list_namespaced_custom_object(
390395
group="ray.io",
391396
version="v1alpha1",
@@ -405,7 +410,7 @@ def _get_ray_clusters(namespace="default") -> List[RayCluster]:
405410
list_of_clusters = []
406411
try:
407412
KubeConfigFileAuthentication.config_check()
408-
api_instance = client.CustomObjectsApi()
413+
api_instance = client.CustomObjectsApi(TokenAuthentication.api_config_handler())
409414
rcs = api_instance.list_namespaced_custom_object(
410415
group="ray.io",
411416
version="v1alpha1",
@@ -427,7 +432,7 @@ def _get_app_wrappers(
427432

428433
try:
429434
KubeConfigFileAuthentication.config_check()
430-
api_instance = client.CustomObjectsApi()
435+
api_instance = client.CustomObjectsApi(TokenAuthentication.api_config_handler())
431436
aws = api_instance.list_namespaced_custom_object(
432437
group="mcad.ibm.com",
433438
version="v1beta1",
@@ -454,7 +459,7 @@ def _map_to_ray_cluster(rc) -> Optional[RayCluster]:
454459
status = RayClusterStatus.UNKNOWN
455460

456461
KubeConfigFileAuthentication.config_check()
457-
api_instance = client.CustomObjectsApi()
462+
api_instance = client.CustomObjectsApi(TokenAuthentication.api_config_handler())
458463
routes = api_instance.list_namespaced_custom_object(
459464
group="route.openshift.io",
460465
version="v1",

0 commit comments

Comments
 (0)