Skip to content

Commit ee7ae76

Browse files
committed
Merge branch 'develop' of https://github.com/oracle/accelerated-data-science into ODSC-37692/opctl_yaml_generator
2 parents 889be0f + 0b18dfe commit ee7ae76

File tree

30 files changed

+1178
-153
lines changed

30 files changed

+1178
-153
lines changed

.github/workflows/run-unittests-default_setup.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ on:
44
pull_request:
55
branches:
66
- main
7-
- 'release/**'
7+
- "release/**"
88
- develop
99
paths:
10-
- 'ads/**'
10+
- "ads/**"
1111
- setup.py
12-
- '**requirements.txt'
12+
- "**requirements.txt"
13+
- .github/workflows/run-unittests.yml
14+
- .github/workflows/run-unittests-default_setup.yml
1315

1416
# Cancel in progress workflows on pull_requests.
1517
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
@@ -74,7 +76,6 @@ jobs:
7476
timeout-minutes: 15
7577
shell: bash
7678
env:
77-
NB_SESSION_COMPARTMENT_OCID: ocid1.compartment.oc1.<unique_ocid>
7879
NoDependency: True
7980
run: |
8081
set -x # print commands that are executed

.github/workflows/run-unittests.yml

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ on:
44
pull_request:
55
branches:
66
- main
7-
- 'release/**'
7+
- "release/**"
88
- develop
99
paths:
10-
- 'ads/**'
10+
- "ads/**"
1111
- setup.py
12-
- '**requirements.txt'
12+
- "**requirements.txt"
13+
- .github/workflows/run-unittests.yml
14+
- .github/workflows/run-unittests-default_setup.yml
1315

1416
# Cancel in progress workflows on pull_requests.
1517
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
@@ -85,22 +87,21 @@ jobs:
8587
shell: bash
8688
run: |
8789
set -x # print commands that are executed
88-
90+
8991
sudo apt-get install libkrb5-dev graphviz
9092
$CONDA/bin/conda init
9193
source /home/runner/.bashrc
92-
94+
9395
pip install -r dev-requirements.txt
9496
9597
- name: "Run unitary tests folder with maximum ADS dependencies"
9698
timeout-minutes: 60
9799
shell: bash
98100
env:
99-
NB_SESSION_COMPARTMENT_OCID: ocid1.compartment.oc1.<unique_ocid>
100101
CONDA_PREFIX: /usr/share/miniconda
101102
run: |
102103
set -x # print commands that are executed
103-
104+
104105
# Setup project and tests folder for cov reports to not be overwritten by another parallel step
105106
if [[ ! -z "${{ matrix.cov-reports }}" ]]; then
106107
mkdir -p cov-${{ matrix.name }}
@@ -109,7 +110,7 @@ jobs:
109110
ln -s ../ads ads
110111
ln -s ../.coveragerc .coveragerc
111112
fi
112-
113+
113114
# Run tests
114115
python -m pytest -v -p no:warnings --durations=5 \
115116
-n auto --dist loadfile ${{ matrix.cov-reports }} \
@@ -145,18 +146,18 @@ jobs:
145146
- name: "Calculate overall coverage"
146147
run: |
147148
set -x # print commands that are executed
148-
149+
149150
# Prepare default cov body text
150151
COV_BODY_INTRO="📌 Overall coverage:\n\n"
151152
echo COV_BODY="$COV_BODY_INTRO No success to gather report. 😿" >> $GITHUB_ENV
152-
153+
153154
# Combine coverage files
154155
pip install coverage
155156
coverage combine cov-reports-unitary/.coverage cov-reports-model/.coverage
156-
157+
157158
# Make html report
158159
coverage html
159-
160+
160161
# Calculate overall coverage and update body message
161162
COV=$(grep -E 'pc_cov' htmlcov/index.html | cut -d'>' -f 2 | cut -d'%' -f 1)
162163
if [[ ! -z $COV ]]; then
@@ -168,18 +169,18 @@ jobs:
168169
if: always()
169170
run: |
170171
set -x # print commands that are executed
171-
172+
172173
# Prepare default diff body text
173174
DIFF_BODY_INTRO="📌 Cov diff with **${{ env.COMPARE_BRANCH }}**:\n\n"
174175
echo DIFF_BODY="$DIFF_BODY_INTRO No success to gather report. 😿" >> $GITHUB_ENV
175-
176+
176177
# Prepare file paths to coverage xml files
177178
# Filenames taken from job.test last step with name - "Save coverage files"
178179
FILE1="cov-reports-unitary/coverage.xml"; [[ ! -f $FILE1 ]] && FILE1=""
179180
FILE2="cov-reports-model/coverage.xml"; [[ ! -f $FILE2 ]] && FILE2=""
180181
echo "FILE1=$FILE1" >> $GITHUB_ENV
181182
echo "FILE2=$FILE2" >> $GITHUB_ENV
182-
183+
183184
# Calculate coverage diff and update body message
184185
pip install diff_cover
185186
diff-cover $FILE1 $FILE2 --compare-branch=origin/${{ env.COMPARE_BRANCH }}

ads/ads

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export OCI_PYTHON_SDK_NO_SERVICE_IMPORTS=1
2-
python -m ads.cli "$@"
2+
python3 -m ads.cli "$@"

ads/jobs/ads_job.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ class Job(Builder):
7474
.with_python_path("other_packages")
7575
# Copy files in "code_dir/output" to object storage after job finishes.
7676
.with_output("output", "oci://bucket_name@namespace/path/to/dir")
77+
# Tags
78+
.with_freeform_tag(my_tag="my_value")
79+
.with_defined_tag(**{"Operations": {"CostCenter": "42"}})
7780
)
7881
)
7982
# Create and Run the job

ads/jobs/builders/infrastructure/base.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def run(
6161
args: str = None,
6262
env_var: dict = None,
6363
freeform_tags: dict = None,
64+
defined_tags: dict = None,
6465
wait: bool = False,
6566
):
6667
"""Runs a job on the infrastructure.
@@ -72,9 +73,11 @@ def run(
7273
args : str, optional
7374
Command line arguments for the job run, by default None.
7475
env_var : dict, optional
75-
Environment variable for the job run, by default None
76+
Environment variable for the job run, by default None.
7677
freeform_tags : dict, optional
77-
Freeform tags for the job run, by default None
78+
Freeform tags for the job run, by default None.
79+
defined_tags : dict, optional
80+
Defined tags for the job run, by default None.
7881
wait : bool, optional
7982
Indicate if this method should wait for the run to finish before it returns, by default False.
8083
"""

ads/jobs/builders/infrastructure/dsc_job.py

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@
3535
from ads.jobs.builders.runtimes.container_runtime import ContainerRuntime
3636
from ads.jobs.builders.runtimes.python_runtime import GitPythonRuntime
3737

38-
from ads.common.dsc_file_system import (
39-
OCIFileStorage,
40-
DSCFileSystemManager
41-
)
38+
from ads.common.dsc_file_system import OCIFileStorage, DSCFileSystemManager
4239

4340
logger = logging.getLogger(__name__)
4441

@@ -445,6 +442,8 @@ def run(self, **kwargs) -> DataScienceJobRun:
445442
* command_line_arguments: str
446443
* maximum_runtime_in_minutes: int
447444
* display_name: str
445+
* freeform_tags: dict(str, str)
446+
* defined_tags: dict(str, dict(str, object))
448447
449448
If display_name is not specified, it will be generated as "<JOB_NAME>-run-<TIMESTAMP>".
450449
@@ -845,9 +844,12 @@ class DataScienceJob(Infrastructure):
845844
.with_storage_mount(
846845
{
847846
"src" : "<mount_target_ip_address>:<export_path>",
848-
"dest" : "<destination_directory_name>"
847+
"dest" : "<destination_directory_name>"
849848
}
850849
)
850+
# Tags
851+
.with_freeform_tag(my_tag="my_value")
852+
.with_defined_tag(**{"Operations": {"CostCenter": "42"}})
851853
)
852854
853855
"""
@@ -866,6 +868,8 @@ class DataScienceJob(Infrastructure):
866868
CONST_LOG_ID = "logId"
867869
CONST_LOG_GROUP_ID = "logGroupId"
868870
CONST_STORAGE_MOUNT = "storageMount"
871+
CONST_FREEFORM_TAGS = "freeformTags"
872+
CONST_DEFINED_TAGS = "definedTags"
869873

870874
attribute_map = {
871875
CONST_PROJECT_ID: "project_id",
@@ -880,6 +884,8 @@ class DataScienceJob(Infrastructure):
880884
CONST_LOG_ID: "log_id",
881885
CONST_LOG_GROUP_ID: "log_group_id",
882886
CONST_STORAGE_MOUNT: "storage_mount",
887+
CONST_FREEFORM_TAGS: "freeform_tags",
888+
CONST_DEFINED_TAGS: "defined_tags",
883889
}
884890

885891
shape_config_details_attribute_map = {
@@ -1235,9 +1241,7 @@ def log_group_id(self) -> str:
12351241
"""
12361242
return self.get_spec(self.CONST_LOG_GROUP_ID)
12371243

1238-
def with_storage_mount(
1239-
self, *storage_mount: List[dict]
1240-
) -> DataScienceJob:
1244+
def with_storage_mount(self, *storage_mount: List[dict]) -> DataScienceJob:
12411245
"""Sets the file systems to be mounted for the data science job.
12421246
A maximum number of 5 file systems are allowed to be mounted for a single data science job.
12431247
@@ -1275,6 +1279,36 @@ def storage_mount(self) -> List[dict]:
12751279
"""
12761280
return self.get_spec(self.CONST_STORAGE_MOUNT, [])
12771281

1282+
def with_freeform_tag(self, **kwargs) -> DataScienceJob:
1283+
"""Sets freeform tags
1284+
1285+
Returns
1286+
-------
1287+
DataScienceJob
1288+
The DataScienceJob instance (self)
1289+
"""
1290+
return self.set_spec(self.CONST_FREEFORM_TAGS, kwargs)
1291+
1292+
def with_defined_tag(self, **kwargs) -> DataScienceJob:
1293+
"""Sets defined tags
1294+
1295+
Returns
1296+
-------
1297+
DataScienceJob
1298+
The DataScienceJob instance (self)
1299+
"""
1300+
return self.set_spec(self.CONST_DEFINED_TAGS, kwargs)
1301+
1302+
@property
1303+
def freeform_tags(self) -> dict:
1304+
"""Freeform tags"""
1305+
return self.get_spec(self.CONST_FREEFORM_TAGS, {})
1306+
1307+
@property
1308+
def defined_tags(self) -> dict:
1309+
"""Defined tags"""
1310+
return self.get_spec(self.CONST_DEFINED_TAGS, {})
1311+
12781312
def _prepare_log_config(self) -> dict:
12791313
if not self.log_group_id and not self.log_id:
12801314
return None
@@ -1429,7 +1463,8 @@ def _update_job_infra(self, dsc_job: DSCJob) -> DataScienceJob:
14291463
"Storage mount hasn't been supported in the current OCI SDK installed."
14301464
)
14311465
dsc_job.job_storage_mount_configuration_details_list = [
1432-
DSCFileSystemManager.initialize(file_system) for file_system in self.storage_mount
1466+
DSCFileSystemManager.initialize(file_system)
1467+
for file_system in self.storage_mount
14331468
]
14341469
return self
14351470

@@ -1486,6 +1521,10 @@ def create(self, runtime, **kwargs) -> DataScienceJob:
14861521

14871522
payload["display_name"] = display_name
14881523
payload["job_log_configuration_details"] = self._prepare_log_config()
1524+
if not payload.get("freeform_tags"):
1525+
payload["freeform_tags"] = self.freeform_tags
1526+
if not payload.get("defined_tags"):
1527+
payload["defined_tags"] = self.defined_tags
14891528

14901529
self.dsc_job = DSCJob(**payload)
14911530
# Set Job infra to user values after DSCJob initialized the defaults
@@ -1496,7 +1535,13 @@ def create(self, runtime, **kwargs) -> DataScienceJob:
14961535
return self
14971536

14981537
def run(
1499-
self, name=None, args=None, env_var=None, freeform_tags=None, wait=False
1538+
self,
1539+
name=None,
1540+
args=None,
1541+
env_var=None,
1542+
freeform_tags=None,
1543+
defined_tags=None,
1544+
wait=False,
15001545
) -> DataScienceJobRun:
15011546
"""Runs a job on OCI Data Science job
15021547
@@ -1510,6 +1555,8 @@ def run(
15101555
Environment variable for the job run, by default None
15111556
freeform_tags : dict, optional
15121557
Freeform tags for the job run, by default None
1558+
defined_tags : dict, optional
1559+
Defined tags for the job run, by default None
15131560
wait : bool, optional
15141561
Indicate if this method should wait for the run to finish before it returns, by default False.
15151562
@@ -1524,11 +1571,18 @@ def run(
15241571
raise RuntimeError(
15251572
"Job is not created. Call create() to create the job first."
15261573
)
1527-
tags = self.runtime.freeform_tags
1528-
if not tags:
1529-
tags = {}
1530-
if freeform_tags:
1531-
tags.update(freeform_tags)
1574+
1575+
if not freeform_tags:
1576+
freeform_tags = {}
1577+
runtime_freeform_tags = self.runtime.freeform_tags
1578+
if runtime_freeform_tags:
1579+
freeform_tags.update(runtime_freeform_tags)
1580+
1581+
if not defined_tags:
1582+
defined_tags = {}
1583+
runtime_defined_tags = self.runtime.defined_tags
1584+
if runtime_defined_tags:
1585+
defined_tags.update(runtime_defined_tags)
15321586

15331587
if name:
15341588
envs = self.runtime.envs
@@ -1540,7 +1594,8 @@ def run(
15401594
display_name=name,
15411595
command_line_arguments=args,
15421596
environment_variables=env_var,
1543-
freeform_tags=tags,
1597+
freeform_tags=freeform_tags,
1598+
defined_tags=defined_tags,
15441599
wait=wait,
15451600
)
15461601

ads/jobs/builders/infrastructure/dsc_job_runtime.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from ads.jobs.builders.infrastructure.utils import get_value
3939

4040

41-
class IncompatibleRuntime(Exception): # pragma: no cover
41+
class IncompatibleRuntime(Exception): # pragma: no cover
4242
"""Represents an exception when runtime is not compatible with the OCI data science job configuration.
4343
This exception is designed to be raised during the extraction of a runtime from OCI data science job.
4444
The data science job does not explicitly contain information of the type of the ADS runtime.
@@ -104,6 +104,8 @@ def translate(self, runtime: Runtime) -> dict:
104104
payload["job_configuration_details"] = self._translate_config(runtime)
105105
if runtime.freeform_tags:
106106
payload["freeform_tags"] = runtime.freeform_tags
107+
if runtime.defined_tags:
108+
payload["defined_tags"] = runtime.defined_tags
107109
self.data_science_job.runtime = runtime
108110
return payload
109111

@@ -353,10 +355,14 @@ def _extract_tags(self, dsc_job):
353355
dict
354356
A runtime specification dictionary for initializing a runtime.
355357
"""
358+
tags = {}
356359
value = get_value(dsc_job, "freeform_tags")
357360
if value:
358-
return {Runtime.CONST_TAG: value}
359-
return {}
361+
tags[Runtime.CONST_FREEFORM_TAGS] = value
362+
value = get_value(dsc_job, "defined_tags")
363+
if value:
364+
tags[Runtime.CONST_DEFINED_TAGS] = value
365+
return tags
360366

361367
def _extract_artifact(self, dsc_job):
362368
"""Extract the job artifact from data science job.

0 commit comments

Comments
 (0)