Skip to content

Commit dd143ea

Browse files
amitkrprajapatiharsh97
authored andcommitted
Implement ads init and ads run for marketplace operator
1 parent 51969cf commit dd143ea

File tree

4 files changed

+146
-26
lines changed

4 files changed

+146
-26
lines changed

ads/opctl/backend/marketplace/local_marketplace.py

Lines changed: 94 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
import json
2-
from typing import Optional, Dict, Union
2+
import os
3+
import subprocess
4+
import tempfile
5+
import time
6+
from subprocess import CompletedProcess
7+
from typing import Optional, Dict, Union, Any
8+
9+
import fsspec
10+
import yaml
11+
from kubernetes import config, client
12+
from kubernetes.watch import watch
313

414
from ads.opctl.backend.marketplace.marketplace_type import (
515
HelmMarketplaceListingDetails,
@@ -40,7 +50,7 @@ class LocalMarketplaceOperatorBackend(Backend):
4050
"""
4151

4252
def __init__(
43-
self, config: Optional[Dict], operator_info: OperatorInfo = None
53+
self, config: Optional[Dict], operator_info: OperatorInfo = None
4454
) -> None:
4555
"""
4656
Instantiates the operator backend.
@@ -70,8 +80,62 @@ def __init__(
7080

7181
self.operator_info = operator_info
7282

83+
def _set_kubernete_env(self) -> None:
84+
os.environ["OCI_CLI_AUTH"] = 'security_token'
85+
os.environ["OCI_CLI_PROFILE"] = 'SESSION_PROFILE'
86+
87+
def _run_helm_install(self, name, chart, **kwargs) -> CompletedProcess:
88+
self._set_kubernete_env()
89+
flags = []
90+
for key, value in kwargs.items():
91+
flags.extend([f"--{key}", f"{value}"])
92+
93+
helm_cmd = ["helm", "install", name, chart, *flags]
94+
print("Running Command:", " ".join(helm_cmd))
95+
return subprocess.run(helm_cmd)
96+
97+
def _save_helm_value_to_yaml(self, helm_values: Dict[str, Any]) -> str:
98+
override_value_path = os.path.join(tempfile.TemporaryDirectory().name, f"values.yaml")
99+
with fsspec.open(override_value_path, mode="w") as f:
100+
f.write(yaml.dump(helm_values))
101+
return override_value_path
102+
103+
def _delete_temp_file(self, temp_file) -> bool:
104+
if os.path.exists(temp_file):
105+
os.remove(temp_file)
106+
return True
107+
108+
return False
109+
110+
def _wait_for_pod_ready(self, namespace, pod_name):
111+
# Configs can be set in Configuration class directly or using helper utility
112+
self._set_kubernete_env()
113+
config.load_kube_config()
114+
v1 = client.CoreV1Api()
115+
116+
def is_pod_ready(pod):
117+
for condition in pod.status.conditions:
118+
if condition.type == "Ready":
119+
return condition.status == 'True'
120+
return False
121+
122+
start_time = time.time()
123+
timeout_seconds = 10 * 60
124+
sleep_time = 20
125+
while True:
126+
pod = v1.list_namespaced_pod(namespace=namespace, label_selector=f"app.kubernetes.io/instance={pod_name}").items[0]
127+
if is_pod_ready(pod):
128+
return 0
129+
if time.time() - start_time >= timeout_seconds:
130+
print("Timed out waiting for pad to get ready.")
131+
break
132+
print(f"Waiting for pod {pod_name} to be ready...")
133+
time.sleep(sleep_time)
134+
return -1
135+
73136
def _run_with_python(self, **kwargs: Dict) -> int:
74-
"""Runs the operator within a local python environment.
137+
"""
138+
Runs the operator within a local python environment.
75139
76140
Returns
77141
-------
@@ -92,10 +156,28 @@ def _run_with_python(self, **kwargs: Dict) -> int:
92156
listing_details: MarketplaceListingDetails = operator.get_listing_details(
93157
operator_spec
94158
)
95-
159+
# operator_spec = self.operator_config['spec']
160+
# helm_values = operator_spec['helmValues']
96161
##Perform backend logic##
162+
# name = 'fs-dp-api-test'
163+
# chart = 'oci://iad.ocir.io/idogsu2ylimg/feature-store-dataplane-api/helm-chart/feature-store-dp-api'
164+
if isinstance(listing_details, HelmMarketplaceListingDetails):
165+
override_value_path = self._save_helm_value_to_yaml(listing_details.helm_values)
166+
helm_install_status = self._run_helm_install(
167+
name=listing_details.name,
168+
chart=listing_details.chart,
169+
**{
170+
"version": listing_details.version,
171+
"namespace": listing_details.namespace,
172+
"values": override_value_path
173+
}
174+
)
97175

98-
return 0
176+
self._delete_temp_file(override_value_path)
177+
if helm_install_status.returncode == 0:
178+
return self._wait_for_pod_ready(listing_details.namespace, listing_details.name)
179+
else:
180+
return -1
99181

100182
def run(self, **kwargs: Dict) -> None:
101183
"""Runs the operator."""
@@ -130,11 +212,11 @@ def run(self, **kwargs: Dict) -> None:
130212
)
131213

132214
def init(
133-
self,
134-
uri: Union[str, None] = None,
135-
overwrite: bool = False,
136-
runtime_type: Union[str, None] = None,
137-
**kwargs: Dict,
215+
self,
216+
uri: Union[str, None] = None,
217+
overwrite: bool = False,
218+
runtime_type: Union[str, None] = None,
219+
**kwargs: Dict,
138220
) -> Union[str, None]:
139221
"""Generates a starter YAML specification for the operator local runtime.
140222
@@ -177,8 +259,8 @@ def init(
177259

178260
return (
179261
operator_runtime_const.MARKETPLACE_RUNTIME_MAP[runtime_type]
180-
.init(**RUNTIME_KWARGS_MAP[runtime_type])
181-
.to_yaml(
262+
.init(**RUNTIME_KWARGS_MAP[runtime_type])
263+
.to_yaml(
182264
uri=uri,
183265
overwrite=overwrite,
184266
note=note,

ads/opctl/backend/marketplace/marketplace_type.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,21 @@ def __init__(self, listing_id: str):
1111

1212
class HelmMarketplaceListingDetails(MarketplaceListingDetails):
1313
def __init__(
14-
self,
15-
listing_id: str,
16-
cluster_id: str,
17-
namespace: str,
18-
ocir_repo: str,
19-
helm_values,
14+
self,
15+
listing_id: str,
16+
name: str,
17+
chart: str,
18+
version: str,
19+
cluster_id: str,
20+
namespace: str,
21+
ocir_repo: str,
22+
helm_values,
2023
):
2124
super().__init__(listing_id)
2225
self.cluster_id = cluster_id
26+
self.name = name
27+
self.chart = chart
28+
self.version = version
2329
self.namespace = namespace
2430
self.ocir_repo = ocir_repo
2531
self.helm_values = helm_values

ads/opctl/operator/lowcode/feature_store_marketplace/__main__.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import sys
23

34
from ads.opctl.backend.marketplace.marketplace_operator_runner import (
@@ -12,12 +13,18 @@
1213

1314
class FeatureStoreOperatorRunner(MarketplaceOperatorRunner):
1415
def get_listing_details(self, operator_config: str) -> MarketplaceListingDetails:
16+
operator_config_json = json.loads(operator_config)
17+
operator_config_spec = operator_config_json['spec']
1518
return HelmMarketplaceListingDetails(
16-
listing_id="sada",
17-
helm_values="{}",
18-
cluster_id="sds",
19-
namespace="asdas",
20-
ocir_repo="sad",
19+
listing_id="<listing_id>",
20+
name='fs-dp-api-test',
21+
chart='oci://iad.ocir.io/idogsu2ylimg/feature-store-dataplane-api/helm-chart/feature-store-dp-api',
22+
version="1.0",
23+
helm_values=operator_config_spec['helmValues'],
24+
cluster_id="<cluster id>",
25+
namespace=operator_config_spec['clusterDetails']['namespace'],
26+
ocir_repo="<ocir_repo>",
27+
2128
)
2229

2330

ads/opctl/operator/lowcode/feature_store_marketplace/cmd.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Copyright (c) 2023 Oracle and/or its affiliates.
55
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
66

7-
from typing import Dict
7+
from typing import Dict, Any
88

99
import click
1010

@@ -14,6 +14,31 @@
1414

1515
from .const import SupportedDatabases
1616

17+
VAULT_OCID_PLACEHOLDER = '<VAULT_OCID>'
18+
SECRET_NAME_PLACEHOLDER = '<SECRET_NAME>'
19+
JDBC_CONNECTION_URL_PLACEHOLDER = '<JDBC_CONNECTION_URL>'
20+
DB_URL_PLACEHOLDER = '<DB_URL>'
21+
22+
23+
def __get_db_config(db_type: str) -> Dict[str, Any]:
24+
if db_type == SupportedDatabases.MySQL:
25+
return {
26+
"mysqlDBConfig": {
27+
"vaultOCID": VAULT_OCID_PLACEHOLDER,
28+
"secretName": SECRET_NAME_PLACEHOLDER,
29+
"jdbcConnectionUrl": JDBC_CONNECTION_URL_PLACEHOLDER,
30+
}
31+
}
32+
elif db_type == SupportedDatabases.ATP:
33+
return {
34+
"atpDBConfig": {
35+
"vaultOCID": JDBC_CONNECTION_URL_PLACEHOLDER,
36+
"dbURL": DB_URL_PLACEHOLDER
37+
}
38+
}
39+
else:
40+
return {}
41+
1742

1843
def init(**kwargs: Dict) -> dict:
1944
"""
@@ -39,7 +64,7 @@ def init(**kwargs: Dict) -> dict:
3964
type=click.Choice(SupportedDatabases.values()),
4065
default=SupportedDatabases.MySQL,
4166
)
42-
67+
selected_db_config = __get_db_config(db_type)
4368
return YamlGenerator(
4469
schema=_load_yaml_from_uri(__file__.replace("cmd.py", "schema.yaml"))
45-
).generate_example_dict(values={"configuredDB": db_type})
70+
).generate_example_dict(values={"configuredDB": db_type, **selected_db_config})

0 commit comments

Comments
 (0)