Skip to content

Commit fdc2eb6

Browse files
committed
Merge commit '2060741013d876b62c730183eb8892c1e56d3488' from main
2 parents ec78e84 + 2060741 commit fdc2eb6

38 files changed

+19899
-356
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: "[Py3.8][Py3.10] Operators Tests"
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
paths:
7+
- "ads/opctl/operator/**"
8+
- "**requirements.txt"
9+
- ".github/workflows/run-operators*.yml"
10+
- "test-requirements-operators.txt"
11+
12+
# Cancel in progress workflows on pull_requests.
13+
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
16+
cancel-in-progress: true
17+
18+
permissions:
19+
contents: read
20+
21+
# hack for https://github.com/actions/cache/issues/810#issuecomment-1222550359
22+
env:
23+
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5
24+
25+
jobs:
26+
test:
27+
name: python ${{ matrix.python-version }}
28+
runs-on: ubuntu-latest
29+
timeout-minutes: 60
30+
31+
strategy:
32+
fail-fast: false
33+
matrix:
34+
python-version: ["3.8", "3.10.8"]
35+
36+
steps:
37+
- uses: actions/checkout@v3
38+
39+
- uses: actions/setup-python@v4
40+
with:
41+
python-version: ${{ matrix.python-version }}
42+
cache: "pip"
43+
cache-dependency-path: |
44+
pyproject.toml
45+
"**requirements.txt"
46+
"test-requirements-operators.txt"
47+
48+
- uses: ./.github/workflows/set-dummy-conf
49+
name: "Test config setup"
50+
51+
- name: "Run Operators Tests"
52+
timeout-minutes: 60
53+
shell: bash
54+
run: |
55+
set -x # print commands that are executed
56+
$CONDA/bin/conda init
57+
source /home/runner/.bashrc
58+
pip install -r test-requirements-operators.txt
59+
python -m pytest -v -p no:warnings --durations=5 tests/operators

.github/workflows/run-unittests-py38-cov-report.yml

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,18 @@ jobs:
3232
strategy:
3333
fail-fast: false
3434
matrix:
35-
test-path: ["tests/unitary/with_extras tests/unitary/default_setup", "tests/unitary/with_extras/model"]
35+
name: ["unitary", "slow_tests"]
3636
include:
37-
- test-path: "tests/unitary/with_extras tests/unitary/default_setup"
38-
ignore-path: "--ignore tests/unitary/with_extras/model --ignore tests/unitary/with_extras/feature_store"
39-
name: "unitary"
40-
- test-path: "tests/unitary/with_extras/model"
41-
name: "model"
37+
- name: "unitary"
38+
test-path: "tests/unitary"
39+
# `model` tests running in "slow_tests",
40+
# `feature_store` tests has its own test suite
41+
ignore-path: |
42+
--ignore tests/unitary/with_extras/model \
43+
--ignore tests/unitary/with_extras/feature_store \
44+
--ignore tests/unitary/with_extras/hpo
45+
- name: "slow_tests"
46+
test-path: "tests/unitary/with_extras/model"
4247

4348
steps:
4449
- uses: actions/checkout@v3
@@ -68,7 +73,7 @@ jobs:
6873
pip install -e ".[forecast]"
6974
7075
- name: "Run unitary tests folder with maximum ADS dependencies"
71-
timeout-minutes: 60
76+
timeout-minutes: 40
7277
shell: bash
7378
env:
7479
CONDA_PREFIX: /usr/share/miniconda
@@ -82,9 +87,17 @@ jobs:
8287
ln -s ../ads ads
8388
ln -s ../.coveragerc .coveragerc
8489
90+
# Run hpo tests, which hangs if run together with all unitary tests
91+
if [[ ${{ matrix.name }} == "unitary" ]]; then
92+
python -m pytest -v -p no:warnings -n auto --dist loadfile \
93+
--cov-append --cov=ads --cov-report=xml --cov-report=html \
94+
tests/unitary/with_extras/hpo
95+
fi
96+
8597
# Run tests
8698
python -m pytest -v -p no:warnings --durations=5 \
87-
-n auto --dist loadfile --cov=ads --cov-report=xml --cov-report=html \
99+
-n auto --dist loadfile \
100+
--cov-append --cov=ads --cov-report=xml --cov-report=html \
88101
${{ matrix.test-path }} ${{ matrix.ignore-path }}
89102
90103
- name: "Save coverage files"
@@ -123,11 +136,11 @@ jobs:
123136
# Prepare file paths to .coverage files
124137
# Filenames taken from job.test last step with name - "Save coverage files"
125138
FILE_UNITARY="cov-reports-unitary/.coverage"; [[ ! -f $FILE_UNITARY ]] && FILE_UNITARY=""
126-
FILE_MODEL="cov-reports-model/.coverage"; [[ ! -f $FILE_MODEL ]] && FILE_MODEL=""
139+
FILE_SLOW="cov-reports-slow_tests/.coverage"; [[ ! -f $FILE_SLOW ]] && FILE_SLOW=""
127140
128141
# Combine coverage files
129142
pip install coverage
130-
coverage combine $FILE_UNITARY $FILE_MODEL
143+
coverage combine $FILE_UNITARY $FILE_SLOW
131144
132145
# Make html report
133146
coverage html
@@ -151,7 +164,7 @@ jobs:
151164
# Prepare file paths to coverage xml files
152165
# Filenames taken from job.test last step with name - "Save coverage files"
153166
FILE1="cov-reports-unitary/coverage.xml"; [[ ! -f $FILE1 ]] && FILE1=""
154-
FILE2="cov-reports-model/coverage.xml"; [[ ! -f $FILE2 ]] && FILE2=""
167+
FILE2="cov-reports-slow_tests/coverage.xml"; [[ ! -f $FILE2 ]] && FILE2=""
155168
echo "FILE1=$FILE1" >> $GITHUB_ENV
156169
echo "FILE2=$FILE2" >> $GITHUB_ENV
157170

.github/workflows/run-unittests-py39-py310.yml

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,21 @@ jobs:
3333
fail-fast: false
3434
matrix:
3535
python-version: ["3.9", "3.10"]
36-
test-path: ["tests/unitary/with_extras tests/unitary/default_setup", "tests/unitary/with_extras/model"]
36+
name: ["unitary", "slow_tests"]
3737
include:
38-
- test-path: "tests/unitary/with_extras tests/unitary/default_setup"
39-
ignore-path: "--ignore tests/unitary/with_extras/model --ignore tests/unitary/with_extras/feature_store --ignore tests/unitary/with_extras/operator/forecast"
40-
name: "unitary"
41-
- test-path: "tests/unitary/with_extras/model"
42-
name: "model"
38+
- name: "unitary"
39+
test-path: "tests/unitary"
40+
# `model` tests running in "slow_tests",
41+
# `feature_store` tests has its own test suite
42+
# `forecast` tests not supported in python 3.9,3.10 (automlx dependency). Tests are running in python3.8 test env, see run-unittests-py38-cov-report.yml
43+
# 'hpo' tests hangs if run together with all unitary tests. Tests running in separate command before running all unitary
44+
ignore-path: |
45+
--ignore tests/unitary/with_extras/model \
46+
--ignore tests/unitary/with_extras/feature_store \
47+
--ignore tests/unitary/with_extras/operator/forecast \
48+
--ignore tests/unitary/with_extras/hpo
49+
- name: "slow_tests"
50+
test-path: "tests/unitary/with_extras/model"
4351

4452
steps:
4553
- uses: actions/checkout@v3
@@ -62,8 +70,19 @@ jobs:
6270
name: "Test env setup"
6371
timeout-minutes: 20
6472

73+
- name: "Run hpo tests"
74+
timeout-minutes: 10
75+
shell: bash
76+
if: ${{ matrix.name }} == "unitary"
77+
run: |
78+
set -x # print commands that are executed
79+
80+
# Run hpo tests, which hangs if run together with all unitary tests
81+
python -m pytest -v -p no:warnings -n auto --dist loadfile \
82+
tests/unitary/with_extras/hpo
83+
6584
- name: "Run unitary tests folder with maximum ADS dependencies"
66-
timeout-minutes: 60
85+
timeout-minutes: 30
6786
shell: bash
6887
env:
6988
CONDA_PREFIX: /usr/share/miniconda

ads/common/auth.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -158,29 +158,29 @@ def set_auth(
158158
... )
159159
>>> ads.set_auth("security_token", config=config) # Set security token authentication from provided config
160160
161-
>>> singer = oci.signer.Signer(
161+
>>> signer = oci.signer.Signer(
162162
... user=ocid1.user.oc1..<unique_ID>,
163163
... fingerprint=<fingerprint>,
164164
... tenancy=ocid1.tenancy.oc1..<unique_ID>,
165165
... region=us-ashburn-1,
166166
... private_key_content=<private key content>,
167167
... )
168-
>>> ads.set_auth(singer=singer) # Set api keys authentication with private key content based on provided signer
168+
>>> ads.set_auth(signer=signer) # Set api keys authentication with private key content based on provided signer
169169
170-
>>> singer = oci.signer.Signer(
170+
>>> signer = oci.signer.Signer(
171171
... user=ocid1.user.oc1..<unique_ID>,
172172
... fingerprint=<fingerprint>,
173173
... tenancy=ocid1.tenancy.oc1..<unique_ID>,
174174
... region=us-ashburn-1,
175175
... private_key_file_location=<private key content>,
176176
... )
177-
>>> ads.set_auth(singer=singer) # Set api keys authentication with private key file location based on provided signer
177+
>>> ads.set_auth(signer=signer) # Set api keys authentication with private key file location based on provided signer
178178
179-
>>> singer = oci.auth.signers.get_resource_principals_signer()
180-
>>> ads.auth.create_signer(config={}, singer=signer) # resource principals authentication dictionary created
179+
>>> signer = oci.auth.signers.get_resource_principals_signer()
180+
>>> ads.auth.create_signer(config={}, signer=signer) # resource principals authentication dictionary created
181181
182182
>>> signer_callable = oci.auth.signers.ResourcePrincipalsFederationSigner
183-
>>> ads.set_auth(signer_callable=signer_callable) # Set resource principal federation singer callable
183+
>>> ads.set_auth(signer_callable=signer_callable) # Set resource principal federation signer callable
184184
185185
>>> signer_callable = oci.auth.signers.InstancePrincipalsSecurityTokenSigner
186186
>>> signer_kwargs = dict(log_requests=True) # will log the request url and response data when retrieving
@@ -400,7 +400,7 @@ def create_signer(
400400
... }
401401
>>> auth = ads.auth.create_signer(config=config) # api_key type of authentication dictionary with private key file location created based on provided config
402402
403-
>>> singer = oci.auth.signers.get_resource_principals_signer()
403+
>>> signer = oci.auth.signers.get_resource_principals_signer()
404404
>>> auth = ads.auth.create_signer(config={}, signer=signer) # resource principals authentication dictionary created
405405
406406
>>> auth = ads.auth.create_signer(auth_type='instance_principal') # instance principals authentication dictionary created
@@ -517,7 +517,7 @@ def get_signer(
517517
) -> Dict:
518518
"""
519519
Provides config and signer based given parameters. If oci_config (api key config file location) and
520-
oci_profile specified new signer will ge generated. Else singer of a type specified in OCI_CLI_AUTH
520+
oci_profile specified new signer will ge generated. Else signer of a type specified in OCI_CLI_AUTH
521521
environment variable will be used to generate signer and return. If OCI_CLI_AUTH not set,
522522
resource principal signer will be provided. Accepted values for OCI_CLI_AUTH: 'api_key',
523523
'instance_principal', 'resource_principal'.
@@ -929,9 +929,9 @@ def register(cls, signer_type: str, signer: Any) -> None:
929929
Parameters
930930
----------
931931
signer_type: str
932-
Singer type to be registers
932+
signer type to be registers
933933
signer: RecordParser
934-
A new Singer class to be registered.
934+
A new signer class to be registered.
935935
936936
Returns
937937
-------

ads/common/oci_client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from oci.resource_search import ResourceSearchClient
1818
from oci.secrets import SecretsClient
1919
from oci.vault import VaultsClient
20+
2021
logger = logging.getLogger(__name__)
2122

2223

@@ -64,13 +65,14 @@ def _client_impl(self, client):
6465
"data_labeling_dp": DataLabelingClient,
6566
"data_labeling_cp": DataLabelingManagementClient,
6667
"resource_search": ResourceSearchClient,
67-
"data_catalog": DataCatalogClient
68+
"data_catalog": DataCatalogClient,
6869
}
6970
try:
7071
from oci.feature_store import FeatureStoreClient
72+
7173
client_map["feature_store"] = FeatureStoreClient
7274
except ImportError:
73-
logger.warning("OCI SDK with feature store support is not installed")
75+
logger.debug("OCI SDK with feature store support is not installed")
7476
pass
7577

7678
assert (

ads/opctl/config/merger.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ def _fill_config_with_defaults(self, ads_config_path: str) -> None:
124124
exec_config.get("auth") or AuthType.API_KEY
125125
)
126126
# determine profile
127-
if self.config["execution"]["auth"] != AuthType.API_KEY:
127+
if self.config["execution"]["auth"] in (
128+
AuthType.RESOURCE_PRINCIPAL,
129+
AuthType.INSTANCE_PRINCIPAL,
130+
):
128131
profile = self.config["execution"]["auth"].upper()
129132
exec_config.pop("oci_profile", None)
130133
self.config["execution"]["oci_profile"] = None

ads/opctl/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
# OPERATOR
3333
OPERATOR_MODULE_PATH = "ads.opctl.operator.lowcode"
3434
OPERATOR_IMAGE_WORK_DIR = "/etc/operator"
35+
OVERRIDE_KWARGS = "override_kwargs"
3536

3637

3738
class RUNTIME_TYPE(ExtendedEnum):

ads/opctl/decorator/common.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
from functools import wraps
88
from typing import Callable, Dict, List
99

10+
import click
11+
1012
from ads.common.auth import AuthContext
1113
from ads.opctl import logger
1214
from ads.opctl.config.base import ConfigProcessor
1315
from ads.opctl.config.merger import ConfigMerger
16+
from ads.opctl.constants import OVERRIDE_KWARGS
1417

1518
RUN_ID_FIELD = "run_id"
1619

@@ -101,3 +104,26 @@ def wrapper(*args, **kwargs) -> Dict:
101104
return func(*args, **kwargs)
102105

103106
return wrapper
107+
108+
109+
def with_click_unknown_args(func: Callable) -> Callable:
110+
"""The decorator to parse the click unknown arguments and put them into kwargs."""
111+
112+
@wraps(func)
113+
def wrapper(*args, **kwargs) -> Dict:
114+
kwargs[OVERRIDE_KWARGS] = {}
115+
try:
116+
click_context = next(
117+
item for item in args if isinstance(item, click.core.Context)
118+
)
119+
kwargs[OVERRIDE_KWARGS] = {
120+
key[2:]: value
121+
for key, value in zip(click_context.args[::2], click_context.args[1::2])
122+
}
123+
except Exception as ex:
124+
logger.debug(ex)
125+
pass
126+
127+
return func(*args, **kwargs)
128+
129+
return wrapper

ads/opctl/distributed/cmds.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@
44
# Copyright (c) 2022, 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-
import os
8-
import fsspec
9-
import subprocess
10-
import yaml
117
import json
8+
import os
129
import re
13-
import docker
10+
import subprocess
1411
from configparser import ConfigParser
15-
16-
from ads.common.auth import create_signer, AuthType, AuthContext
1712
from urllib.parse import urlparse
18-
from ads.jobs import Job, DataScienceJobRun
13+
14+
import fsspec
15+
import yaml
16+
17+
from ads.common.auth import AuthContext, AuthType, create_signer
18+
from ads.common.decorator.runtime_dependency import (
19+
OptionalDependency,
20+
runtime_dependency,
21+
)
22+
from ads.jobs import Job
1923
from ads.jobs.builders.runtimes.artifact import Artifact
2024
from ads.opctl.utils import publish_image as publish_image_cmd
2125
from ads.opctl.utils import run_command
@@ -137,6 +141,7 @@ def show_config_info(job_id, work_dir, cluster_file_name, worker_info, **kwargs)
137141
)
138142

139143

144+
@runtime_dependency(module="docker", install_from=OptionalDependency.OPCTL)
140145
def verify_image(img_name):
141146
"""
142147
Verify if the input image exists in OCI registry
@@ -151,6 +156,8 @@ def verify_image(img_name):
151156
-------
152157
153158
"""
159+
import docker
160+
154161
client = docker.from_env()
155162
try:
156163
client.images.get_registry_data(img_name)

0 commit comments

Comments
 (0)