From 743c0555d709712520dbbe7ff9913fa386a66333 Mon Sep 17 00:00:00 2001 From: JosepSampe Date: Sat, 30 May 2026 00:20:16 +0200 Subject: [PATCH 1/5] Update oracle backend --- config/config_template.yaml | 3 +++ .../source/compute_config/oracle_functions.md | 19 +++++++++++-------- .../serverless/backends/oracle_f/config.py | 9 +++++++++ .../serverless/backends/oracle_f/oracle_f.py | 1 + lithops/storage/backends/oracle_oss/config.py | 5 +++++ 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/config/config_template.yaml b/config/config_template.yaml index c960891af..b19ddb5e6 100644 --- a/config/config_template.yaml +++ b/config/config_template.yaml @@ -390,6 +390,9 @@ #worker_processes: 1 #invoke_pool_threads: 64 #runtime_include_function: False + #docker_server: .ocir.io # Auto-set to {region}.ocir.io if not provided + #docker_user: / + #docker_password: # From Oracle Cloud auth tokens page # Oracle Object Storage – object storage (beta) #oracle_oss: diff --git a/docs/source/compute_config/oracle_functions.md b/docs/source/compute_config/oracle_functions.md index acdfa79b6..7d7f6422a 100644 --- a/docs/source/compute_config/oracle_functions.md +++ b/docs/source/compute_config/oracle_functions.md @@ -12,7 +12,7 @@ Lithops with *Oracle Functions* as serverless compute backend. python3 -m pip install lithops[oracle] ``` -2. Access to your [Oracle Cloud Console](https://cloud.oracle.com/) and activate your Functions service instance. +2. Access your [Oracle Cloud Console](https://cloud.oracle.com/) and activate your Functions service instance. ## Configuration @@ -46,7 +46,7 @@ Now that the dynamic group is set up, you'll need to create a policy that allows 1. **Open the navigation menu again.** Under Governance and Administration, go to Identity, and then click [**Policies**](https://cloud.oracle.com/identity/domains/policies). -2. Choose your **compartment** and Click on the **Create Policy** button. +2. Choose your **compartment** and click on the **Create Policy** button. 3. **In the Create Policy dialog box:** @@ -61,7 +61,7 @@ Now that the dynamic group is set up, you'll need to create a policy that allows 4. **Click Create** to create the policy. -### Configure lithops +### Configure Lithops Now, your Oracle Functions have the necessary permissions to manage resources in your Oracle Cloud Infrastructure tenancy. 1. Navigate to the [VCNs page](https://cloud.oracle.com/networking/vcns) and create a new VCN using the **VCN Wizard**. Then choose *create VCN with Internet Connectivity*. In the next page, you can uncheck `Use DNS hostnames in this VCN` and leave the rest of the parameters as provided by default. @@ -85,12 +85,12 @@ oracle: oracle_f: subnet_id: + docker_user: / + docker_password: ``` -Also, remember to log in to your Oracle container registry before you build your runtime, since runtimes are uploaded to the Oracle container registry. `` is most likely your email address. You can create a new auth token [here](https://cloud.oracle.com/identity/domains/my-profile/auth-tokens). - -``` +```bash docker login .ocir.io -u / -p ``` @@ -113,9 +113,12 @@ docker login .ocir.io -u / -p .ocir.io` |no | Oracle Container Registry URL. Auto-set to `{region}.ocir.io` from `oracle.region` if not provided | +|oracle_f | docker_user | |no | OCIR username in the form `/`. Required when building or pushing runtime images. `` is most likely your email address | +|oracle_f | docker_password | |no | OCIR auth token. Required when building or pushing runtime images. Create one [here](https://cloud.oracle.com/identity/domains/my-profile/auth-tokens). Lithops logs in automatically before pushing runtime images | |oracle_f | max_workers | 300 | no | Max number of workers. Oracle limits the total to 60 GB RAM across any number of workers | |oracle_f | worker_processes | 1 | no | Number of Lithops processes within a given worker. This can be used to parallelize function activations within a worker | -|oracle_f | runtime | |no | Runtime name you built and deployed using the lithops client| +|oracle_f | runtime | |no | Runtime name you built and deployed using the Lithops client| |oracle_f | runtime_memory | 256 |no | Memory limit in MB. Default 256MB | |oracle_f | runtime_timeout | 300 |no | Runtime timeout in seconds. Default 5 minutes | |oracle_f | runtime_include_function | False | no | If set to true, Lithops will automatically build a new runtime, including the function's code, instead of transferring it through the storage backend at invocation time. This is useful when the function's code size is large (in the order of 10s of MB) and the code does not change frequently | @@ -130,7 +133,7 @@ lithops hello -b oracle_f -s oracle_oss ## Viewing the execution logs -You can view the function executions logs in your local machine using the *lithops client*: +You can view the function execution logs on your local machine using the *Lithops client*: ```bash lithops logs poll diff --git a/lithops/serverless/backends/oracle_f/config.py b/lithops/serverless/backends/oracle_f/config.py index d160a60a5..0bc53be91 100644 --- a/lithops/serverless/backends/oracle_f/config.py +++ b/lithops/serverless/backends/oracle_f/config.py @@ -94,6 +94,10 @@ def load_config(config_data=None): msg = f'"{param}" is mandatory in the "oracle" section of the configuration' raise Exception(msg) + config_data['oracle']['key_file'] = os.path.abspath( + os.path.expanduser(config_data['oracle']['key_file']) + ) + for param in REQ_PARAMS_2: if param not in config_data['oracle_f']: msg = f'"{param}" is mandatory in the "oracle_f" section of the configuration' @@ -106,3 +110,8 @@ def load_config(config_data=None): temp = copy.deepcopy(config_data['oracle_f']) config_data['oracle_f'].update(config_data['oracle']) config_data['oracle_f'].update(temp) + + if 'docker_server' not in config_data['oracle_f']: + config_data['oracle_f']['docker_server'] = ( + f"{config_data['oracle_f']['region']}.ocir.io" + ) diff --git a/lithops/serverless/backends/oracle_f/oracle_f.py b/lithops/serverless/backends/oracle_f/oracle_f.py index 0c52a4af6..7b43d3ebf 100644 --- a/lithops/serverless/backends/oracle_f/oracle_f.py +++ b/lithops/serverless/backends/oracle_f/oracle_f.py @@ -213,6 +213,7 @@ def build_runtime(self, runtime_name, dockerfile, extra_args=[]): os.remove(config.FH_ZIP_LOCATION) logger.debug(f'Pushing runtime {image_name} to Oracle Cloud Container Registry') + utils.login_docker(self.config, docker_path) if utils.is_podman(docker_path): cmd = f'{docker_path} push {image_name} --format docker --remove-signatures' else: diff --git a/lithops/storage/backends/oracle_oss/config.py b/lithops/storage/backends/oracle_oss/config.py index f6319e84e..905f3c256 100644 --- a/lithops/storage/backends/oracle_oss/config.py +++ b/lithops/storage/backends/oracle_oss/config.py @@ -14,6 +14,7 @@ # import copy +import os CONNECTION_POOL_SIZE = 300 @@ -34,6 +35,10 @@ def load_config(config_data=None): msg = f'"{param}" is mandatory under "oracle" section of the configuration' raise Exception(msg) + config_data['oracle']['key_file'] = os.path.abspath( + os.path.expanduser(config_data['oracle']['key_file']) + ) + temp = copy.deepcopy(config_data['oracle_oss']) config_data['oracle_oss'].update(config_data['oracle']) config_data['oracle_oss'].update(temp) From d262e11405417907bf68addc596dafb072cc8ce0 Mon Sep 17 00:00:00 2001 From: JosepSampe Date: Sat, 30 May 2026 00:42:47 +0200 Subject: [PATCH 2/5] Update --- CHANGELOG.md | 13 ++++++++----- lithops/serverless/backends/oracle_f/oracle_f.py | 7 ++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f3eb913..7f229c824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Added - [GCP Compute Engine] Added new GCP Compute Engine standalone backend - [Core] Added support for variable-length parameters in functions passed to the executor. +- [Standalone] Added rollback logic to remove partially created resources when initialization fails +- [Python] Added support for Python 3.14 ### Changed - [K8s] Auto-detect cluster architecture when building runtimes. @@ -12,7 +14,7 @@ - [K8s] Added configuration for pod and container `securityContext`. - [Docs] Corrected MinIO/Ceph config template keys and removed obsolete Kubernetes image references. - [GCP Functions] Updated `gcp_functions` backend to Google Cloud Run functions (Cloud Functions v2 API). -- [Python] Updated all backend python versions (deprecated Python 3.9, added Python 3.14) +- [Python] Updated all backends python versions (deprecated Python 3.9) - [Azure Funcions] Updated default funcions plan to Flex Consumption - [AWS EC2] Updated default Ubuntu Image to Ubuntu 24 - [Azure VMS] Updated default Ubuntu Image to Ubuntu 24 @@ -20,10 +22,11 @@ ### Fixed - [K8s] Fixed default runtime builds impacted by Debian Buster end-of-life. - [GCP Cloud Run] Added Artifact Registry (`pkg.dev`) runtime deployment support -- [K8s] Run default runtime image as non-root user (uid 1000) (#1469) -- [AWS] Fixed AWS backends execution -- [GCP] Fixed GCP backends execution -- [Azure] Fixed Azure backends execution +- [K8s] Run default runtime image as non-root user (uid 1000) +- [AWS] Fixed EC2 standalone initialization in consume mode and ECR authentication when building runtimes +- [GCP] Fixed Cloud Functions runtime deletion and gRPC fork warnings during multiprocess execution on macOS +- [Azure] Fixed Azure Functions deployment on Flex Consumption and consolidated container registry login across Azure backends +- [Oracle] Fixed API key path resolution with `~` expansion and added configurable OCIR registry login ## [v3.6.4] diff --git a/lithops/serverless/backends/oracle_f/oracle_f.py b/lithops/serverless/backends/oracle_f/oracle_f.py index 7b43d3ebf..a80cac8f0 100644 --- a/lithops/serverless/backends/oracle_f/oracle_f.py +++ b/lithops/serverless/backends/oracle_f/oracle_f.py @@ -213,7 +213,12 @@ def build_runtime(self, runtime_name, dockerfile, extra_args=[]): os.remove(config.FH_ZIP_LOCATION) logger.debug(f'Pushing runtime {image_name} to Oracle Cloud Container Registry') - utils.login_docker(self.config, docker_path) + docker_user = self.config.get('docker_user') + docker_password = self.config.get('docker_password') + docker_server = self.config.get('docker_server') + if docker_user and docker_password: + logger.debug('Container registry credentials found in config. Logging in into the registry') + utils.docker_login(docker_user, docker_password, docker_server) if utils.is_podman(docker_path): cmd = f'{docker_path} push {image_name} --format docker --remove-signatures' else: From 938565dbd4d7e67e54b1936ab07e1622963a5b2f Mon Sep 17 00:00:00 2001 From: JosepSampe Date: Mon, 1 Jun 2026 17:22:59 +0200 Subject: [PATCH 3/5] Update OCI --- config/config_template.yaml | 4 +- .../source/compute_config/oracle_functions.md | 10 +--- .../serverless/backends/oracle_f/config.py | 3 +- .../serverless/backends/oracle_f/oracle_f.py | 46 +++++++++++++++++-- .../storage/backends/oracle_oss/oracle_oss.py | 11 ++++- runtime/oracle_f/Dockerfile | 3 +- 6 files changed, 57 insertions(+), 20 deletions(-) diff --git a/config/config_template.yaml b/config/config_template.yaml index c58464bcd..4565cc3de 100644 --- a/config/config_template.yaml +++ b/config/config_template.yaml @@ -392,8 +392,8 @@ #invoke_pool_threads: 64 #runtime_include_function: False #docker_server: .ocir.io # Auto-set to {region}.ocir.io if not provided - #docker_user: / - #docker_password: # From Oracle Cloud auth tokens page + #docker_user: / # Optional, auto-set from OCI user email + #docker_password: # Mandatory. From Oracle Cloud auth tokens page # Oracle Object Storage – object storage (beta) #oracle_oss: diff --git a/docs/source/compute_config/oracle_functions.md b/docs/source/compute_config/oracle_functions.md index 7d7f6422a..8243dd3e6 100644 --- a/docs/source/compute_config/oracle_functions.md +++ b/docs/source/compute_config/oracle_functions.md @@ -85,15 +85,9 @@ oracle: oracle_f: subnet_id: - docker_user: / docker_password: ``` - -```bash -docker login .ocir.io -u / -p -``` - ## Summary of configuration keys for Oracle: |Group|Key|Default|Mandatory|Additional info| @@ -114,8 +108,8 @@ docker login .ocir.io -u / -p .ocir.io` |no | Oracle Container Registry URL. Auto-set to `{region}.ocir.io` from `oracle.region` if not provided | -|oracle_f | docker_user | |no | OCIR username in the form `/`. Required when building or pushing runtime images. `` is most likely your email address | -|oracle_f | docker_password | |no | OCIR auth token. Required when building or pushing runtime images. Create one [here](https://cloud.oracle.com/identity/domains/my-profile/auth-tokens). Lithops logs in automatically before pushing runtime images | +|oracle_f | docker_user | |no | OCIR login username. Auto-set to `{tenancy_namespace}/{email}` from your OCI user profile when omitted. Format: `/`. `` is most likely your email address. Set manually for federated accounts | +|oracle_f | docker_password | |yes | OCIR auth token. Required to build and push runtime images. Create one [here](https://cloud.oracle.com/identity/domains/my-profile/auth-tokens). Lithops logs in automatically before pushing runtime images | |oracle_f | max_workers | 300 | no | Max number of workers. Oracle limits the total to 60 GB RAM across any number of workers | |oracle_f | worker_processes | 1 | no | Number of Lithops processes within a given worker. This can be used to parallelize function activations within a worker | |oracle_f | runtime | |no | Runtime name you built and deployed using the Lithops client| diff --git a/lithops/serverless/backends/oracle_f/config.py b/lithops/serverless/backends/oracle_f/config.py index 0bc53be91..a23c35baf 100644 --- a/lithops/serverless/backends/oracle_f/config.py +++ b/lithops/serverless/backends/oracle_f/config.py @@ -39,7 +39,6 @@ # Update pip RUN pip install --upgrade --ignore-installed setuptools six pip \ && pip install --upgrade --no-cache-dir --ignore-installed \ - fn \ fdk \ redis \ httplib2 \ @@ -72,7 +71,7 @@ && mv entry_point.py handler/ -ENV PYTHONPATH "${PYTHONPATH}:${FUNCTION_DIR}" +ENV PYTHONPATH="${FUNCTION_DIR}" ENTRYPOINT ["/usr/local/bin/fdk", "handler/entry_point.py", "handler"] """ diff --git a/lithops/serverless/backends/oracle_f/oracle_f.py b/lithops/serverless/backends/oracle_f/oracle_f.py index a80cac8f0..770ec49e1 100644 --- a/lithops/serverless/backends/oracle_f/oracle_f.py +++ b/lithops/serverless/backends/oracle_f/oracle_f.py @@ -23,6 +23,7 @@ import oci from oci.functions import FunctionsManagementClient from oci.functions import FunctionsInvokeClient +from oci.identity import IdentityClient from oci.object_storage import ObjectStorageClient from lithops import utils @@ -50,6 +51,7 @@ def __init__(self, oci_config, internal_storage): self.app_name = oci_config.get( 'application_name', f'{config.APP_NAME}_{self.user[-8:-1].lower()}') + self.signer = None self.cf_client = self._init_functions_mgmt_client() self.app_id = self._get_application_id(self.app_name) @@ -58,18 +60,54 @@ def __init__(self, oci_config, internal_storage): msg = COMPUTE_CLI_MSG.format('Oracle Functions') logger.info(f"{msg} - Region: {self.region}") + def _get_docker_user(self): + """ + Return the OCIR docker login username. + + If docker_user is not in config, derive it as + `/` using the OCI user profile. + docker_password must still be provided manually (Oracle auth token). + """ + if os.path.isfile(self.key_file): + identity_client = IdentityClient(self.config) + else: + identity_client = IdentityClient(config={}, signer=self.signer) + + user = identity_client.get_user(self.user).data + username = user.email + if not username: + raise Exception( + 'Could not determine OCIR docker_user automatically. ' + 'Set docker_user under oracle_f in the configuration.' + ) + + docker_user = f'{self.namespace}/{username}' + logger.debug(f'Using OCIR docker login user {docker_user}') + self.config['docker_user'] = docker_user + return docker_user + def _init_functions_mgmt_client(self): if os.path.isfile(self.key_file): return FunctionsManagementClient(config=self.config) - else: + if os.environ.get('OCI_RESOURCE_PRINCIPAL_VERSION'): self.signer = oci.auth.signers.get_resource_principals_signer() return FunctionsManagementClient(config={}, signer=self.signer) + raise Exception( + f"Oracle API key file '{self.key_file}' does not exist. " + "Configure a valid 'key_file' under the 'oracle' section, " + "or run inside OCI with resource principals enabled." + ) def _init_functions_invk_client(self, endpoint): if os.path.isfile(self.key_file): return FunctionsInvokeClient(config=self.config, service_endpoint=endpoint) - else: + if self.signer: return FunctionsInvokeClient(config={}, service_endpoint=endpoint, signer=self.signer) + raise Exception( + f"Oracle API key file '{self.key_file}' does not exist. " + "Configure a valid 'key_file' under the 'oracle' section, " + "or run inside OCI with resource principals enabled." + ) def _get_namespace(self): """ @@ -213,10 +251,10 @@ def build_runtime(self, runtime_name, dockerfile, extra_args=[]): os.remove(config.FH_ZIP_LOCATION) logger.debug(f'Pushing runtime {image_name} to Oracle Cloud Container Registry') - docker_user = self.config.get('docker_user') docker_password = self.config.get('docker_password') docker_server = self.config.get('docker_server') - if docker_user and docker_password: + if docker_password: + docker_user = self.config.get('docker_user') or self._get_docker_user() logger.debug('Container registry credentials found in config. Logging in into the registry') utils.docker_login(docker_user, docker_password, docker_server) if utils.is_podman(docker_path): diff --git a/lithops/storage/backends/oracle_oss/oracle_oss.py b/lithops/storage/backends/oracle_oss/oracle_oss.py index f5ec60b59..7d18ee681 100644 --- a/lithops/storage/backends/oracle_oss/oracle_oss.py +++ b/lithops/storage/backends/oracle_oss/oracle_oss.py @@ -48,10 +48,17 @@ def __init__(self, oci_config): def _init_storage_client(self): if os.path.isfile(self.key_file): + logger.debug(f"Using key file {self.key_file} for authentication") return ObjectStorageClient(self.config) - else: + if os.environ.get('OCI_RESOURCE_PRINCIPAL_VERSION'): + logger.debug(f"Key file {self.key_file} not found. Using resource principals") self.signer = oci.auth.signers.get_resource_principals_signer() return ObjectStorageClient(config={}, signer=self.signer) + raise Exception( + f"Oracle API key file '{self.key_file}' does not exist. " + "Configure a valid 'key_file' under the 'oracle' section, " + "or run inside OCI with resource principals enabled." + ) def get_client(self): return self @@ -61,7 +68,7 @@ def generate_bucket_name(self): Generates a unique bucket name """ user = self.config['user'] - self.config['storage_bucket'] = f'lithops-{self.region}-{user[-8:-1].lower()}' + self.config['storage_bucket'] = f'lithops-{self.region_name}-{user[-8:-1].lower()}' return self.config['storage_bucket'] diff --git a/runtime/oracle_f/Dockerfile b/runtime/oracle_f/Dockerfile index 7ef0489d4..708a5c614 100644 --- a/runtime/oracle_f/Dockerfile +++ b/runtime/oracle_f/Dockerfile @@ -13,7 +13,6 @@ RUN apt-get update \ RUN pip install --upgrade --ignore-installed setuptools six pip \ && pip install --upgrade --no-cache-dir --ignore-installed \ - fn \ fdk \ redis \ httplib2 \ @@ -46,5 +45,5 @@ RUN unzip lithops_oracle.zip \ && mv entry_point.py handler/ -ENV PYTHONPATH "${PYTHONPATH}:${FUNCTION_DIR}" +ENV PYTHONPATH="${FUNCTION_DIR}" ENTRYPOINT ["/usr/local/bin/fdk", "handler/entry_point.py", "handler"] From f5bf6b558ec7bb6cb8ca763a3c412e20be749ca1 Mon Sep 17 00:00:00 2001 From: JosepSampe Date: Mon, 1 Jun 2026 17:25:20 +0200 Subject: [PATCH 4/5] Update --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f229c824..ee0a5442d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,8 @@ - [AWS] Fixed EC2 standalone initialization in consume mode and ECR authentication when building runtimes - [GCP] Fixed Cloud Functions runtime deletion and gRPC fork warnings during multiprocess execution on macOS - [Azure] Fixed Azure Functions deployment on Flex Consumption and consolidated container registry login across Azure backends -- [Oracle] Fixed API key path resolution with `~` expansion and added configurable OCIR registry login +- [Oracle Object Storage] Fixed authentication with `~` in `key_file`, resource principal fallback, and bucket name generation +- [Oracle Functions] Fixed Python 3.12 runtime build, added OCIR registry login with auto-derived `docker_user`, and default `docker_server` from region ## [v3.6.4] From eaa812d80c2e475390710c873d5b4faf089de6a6 Mon Sep 17 00:00:00 2001 From: JosepSampe Date: Mon, 1 Jun 2026 18:06:34 +0200 Subject: [PATCH 5/5] Update --- .../serverless/backends/oracle_f/oracle_f.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lithops/serverless/backends/oracle_f/oracle_f.py b/lithops/serverless/backends/oracle_f/oracle_f.py index 770ec49e1..57921b1ce 100644 --- a/lithops/serverless/backends/oracle_f/oracle_f.py +++ b/lithops/serverless/backends/oracle_f/oracle_f.py @@ -127,17 +127,23 @@ def _format_function_name(self, runtime_name, runtime_memory, version=__version_ def _format_image_name(self, runtime_name): """ - Formats OC image name from runtime name + Formats container image name from runtime name using docker_server. """ - if 'ocir.io' not in runtime_name: - image_name = f'{self.region}.ocir.io/{self.namespace}/{runtime_name}' - else: + if 'ocir.io' in runtime_name or 'docker.io' in runtime_name: image_name = runtime_name - - if ':' not in image_name: - image_name = f'{image_name}:latest' - - return image_name + else: + docker_server = self.config.get('docker_server', f'{self.region}.ocir.io') + if 'docker.io' in docker_server: + prefix = self.config.get('docker_user') + if not prefix: + raise Exception( + 'docker_user is required under oracle_f when docker_server is Docker Hub' + ) + else: + prefix = self.namespace + image_name = f'{docker_server}/{prefix}/{runtime_name}' + + return image_name if ':' in image_name else f'{image_name}:latest' def get_runtime_key(self, runtime_name, runtime_memory, version=__version__): """ @@ -159,7 +165,7 @@ def get_runtime_key(self, runtime_name, runtime_memory, version=__version__): def _get_default_runtime_image_name(self): py_version = utils.CURRENT_PY_VERSION.replace('.', '') - return self._format_image_name(f'lithops-default-runtime-v{py_version}') + return self._format_image_name(f'lithops-oracle-default-runtime-v{py_version}') def clean(self, all=False): """