Skip to content

Commit 25b60f4

Browse files
authored
MRG: Merge pull request #548 from octue/feature/support-poetry-based-apps-on-cloud-run
Add periodic monitor messages and support poetry-based apps on Cloud Run
2 parents d5d1bb6 + f2641e9 commit 25b60f4

File tree

19 files changed

+678
-460
lines changed

19 files changed

+678
-460
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ repos:
2323
- id: black
2424
args: ["--line-length", "120"]
2525

26-
- repo: https://gitlab.com/pycqa/flake8
27-
rev: 3.9.2
26+
- repo: https://github.com/pycqa/flake8
27+
rev: 6.0.0
2828
hooks:
2929
- id: flake8
3030
language_version: python3

docs/source/inter_service_compatibility.rst

Lines changed: 123 additions & 121 deletions
Large diffs are not rendered by default.

octue/cli.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,17 @@ def deploy():
363363
)
364364
@click.option("--no-cache", is_flag=True, help="If provided, don't use the Docker cache.")
365365
@click.option("--update", is_flag=True, help="If provided, allow updates to an existing service.")
366-
def cloud_run(service_config, update, no_cache):
366+
@click.option(
367+
"--revision-tag",
368+
type=str,
369+
default=None,
370+
help="A tag to use for this revision of the service (e.g. 1.3.7). This overrides the `OCTUE_SERVICE_REVISION_TAG` "
371+
"environment variable if it's present. If this option isn't given and the environment variable isn't present, a "
372+
"random 'cool name' tag is generated e.g 'curious-capybara'.",
373+
)
374+
def cloud_run(service_config, update, no_cache, revision_tag):
367375
"""Deploy a python app to Google Cloud Run as an Octue service or digital twin."""
368-
CloudRunDeployer(service_config).deploy(update=update, no_cache=no_cache)
376+
CloudRunDeployer(service_config, revision_tag=revision_tag).deploy(update=update, no_cache=no_cache)
369377

370378

371379
@deploy.command()
@@ -385,7 +393,15 @@ def cloud_run(service_config, update, no_cache):
385393
help="If provided, skip creating and running the build trigger and just deploy a pre-built image to Dataflow",
386394
)
387395
@click.option("--image-uri", type=str, default=None, help="The actual image URI to use when creating the Dataflow job.")
388-
def dataflow(service_config, no_cache, update, dataflow_job_only, image_uri):
396+
@click.option(
397+
"--revision-tag",
398+
type=str,
399+
default=None,
400+
help="A tag to use for this revision of the service (e.g. 1.3.7). This overrides the `OCTUE_SERVICE_REVISION_TAG` "
401+
"environment variable if it's present. If this option isn't given and the environment variable isn't present, a "
402+
"random 'cool name' tag is generated e.g 'curious-capybara'.",
403+
)
404+
def dataflow(service_config, no_cache, update, dataflow_job_only, image_uri, revision_tag):
389405
"""Deploy a python app to Google Dataflow as an Octue service or digital twin."""
390406
if bool(importlib.util.find_spec("apache_beam")):
391407
# Import the Dataflow deployer only if the `apache-beam` package is available (due to installing `octue` with
@@ -397,7 +413,7 @@ def dataflow(service_config, no_cache, update, dataflow_job_only, image_uri):
397413
"`pip install octue[dataflow]`"
398414
)
399415

400-
deployer = DataflowDeployer(service_config)
416+
deployer = DataflowDeployer(service_config, revision_tag=revision_tag)
401417

402418
if dataflow_job_only:
403419
deployer.create_streaming_dataflow_job(image_uri=image_uri, update=update)

octue/cloud/deployment/google/base_deployer.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import time
55
from abc import abstractmethod
66

7+
import coolname
78
import yaml
89

910
from octue.cloud.service_id import convert_service_id_to_pub_sub_form, create_service_sruid, get_service_sruid_parts
@@ -35,13 +36,20 @@ class BaseDeployer:
3536
pubsub-emulator 0.6.0
3637
```
3738
39+
The service revision tag resolution order is:
40+
1. The `revision_tag` variable if present
41+
2. The `OCTUE_SERVICE_REVISION_TAG` environment variable if present (returned by `get_service_sruid_parts`)
42+
3. A generated 'cool name'
43+
3844
:param str octue_configuration_path: the path to the `octue.yaml` file if it's not in the current working directory
45+
:param str|None image_uri_template: the image URI template to use to name and store images with Cloud Build
46+
:param str|None revision_tag: a tag to use for this revision of the service (e.g. 1.3.7). This overrides the `OCTUE_SERVICE_REVISION_TAG` environment variable if it's present. If this option isn't given and the environment variable isn't present, a random 'cool name' tag is generated e.g 'curious-capybara'.
3947
:return None:
4048
"""
4149

4250
TOTAL_NUMBER_OF_STAGES = None
4351

44-
def __init__(self, octue_configuration_path, image_uri_template=None):
52+
def __init__(self, octue_configuration_path, image_uri_template=None, revision_tag=None):
4553
self.service_configuration = ServiceConfiguration.from_file(octue_configuration_path)
4654

4755
# Generated attributes.
@@ -52,6 +60,12 @@ def __init__(self, octue_configuration_path, image_uri_template=None):
5260
self.service_configuration
5361
)
5462

63+
if revision_tag:
64+
self.service_revision_tag = revision_tag
65+
66+
if not self.service_revision_tag:
67+
self.service_revision_tag = coolname.generate_slug(2)
68+
5569
self.service_sruid = create_service_sruid(
5670
namespace=self.service_namespace,
5771
name=self.service_name,
@@ -76,6 +90,8 @@ def __init__(self, octue_configuration_path, image_uri_template=None):
7690
f"[SUCCESS] Service deployed - it can be questioned via Pub/Sub at {self.service_sruid!r}."
7791
)
7892

93+
print(f"Ready to deploy {self.service_sruid!r}.")
94+
7995
@abstractmethod
8096
def deploy(self, no_cache=False, update=False):
8197
"""Deploy the octue app.

octue/cloud/deployment/google/cloud_run/Dockerfile

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@ ENV PYTHONUNBUFFERED True
66
ENV PROJECT_ROOT=/app
77
WORKDIR $PROJECT_ROOT
88

9-
RUN apt-get update -y && apt-get install -y --fix-missing build-essential && rm -rf /var/lib/apt/lists/*
9+
RUN apt-get update -y && apt-get install -y --fix-missing build-essential curl && rm -rf /var/lib/apt/lists/*
1010

1111
COPY . .
1212

13-
# Install requirements (supports requirements.txt and setup.py; both will be run if both are present.)
14-
RUN if [ ! -f "requirements.txt" ] && [ ! -f "setup.py" ]; then exit 1; fi
13+
# Install poetry if necessary.
14+
ENV POETRY_HOME=/etc/poetry
15+
ENV PATH "$POETRY_HOME/bin:$PATH"
16+
RUN if [ -f "pyproject.toml" ]; then curl -sSL https://install.python-poetry.org | python3 - && poetry config virtualenvs.create false; fi
17+
18+
# Install dependencies - supports `setup.py`, `requirements.txt`, and `pyproject.toml` via poetry. All dependency files
19+
# are installed from if present.
20+
RUN if [ ! -f "requirements.txt" ] && [ ! -f "setup.py" ] && [ ! -f "pyproject.toml" ]; then exit 1; fi
1521
RUN if [ -f "requirements.txt" ]; then pip install --upgrade pip && pip install -r requirements.txt; fi
1622
RUN if [ -f "setup.py" ]; then pip install --upgrade pip && pip install -e .; fi
23+
RUN if [ -f "pyproject.toml" ]; then poetry install --only main; fi
1724

1825
EXPOSE $PORT
1926

octue/cloud/deployment/google/cloud_run/deployer.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ class CloudRunDeployer(BaseDeployer):
2626
```
2727
2828
:param str octue_configuration_path: the path to the `octue.yaml` file if it's not in the current working directory
29+
:param str|None image_uri_template: the image URI template to use to name and store images with Cloud Build
30+
:param str revision_tag: a tag to use for this revision of the service (e.g. 1.3.7). This overrides the `OCTUE_SERVICE_REVISION_TAG` environment variable if it's present. If this option isn't given and the environment variable isn't present, a random 'cool name' tag is generated e.g 'curious-capybara'.
2931
:return None:
3032
"""
3133

3234
TOTAL_NUMBER_OF_STAGES = 5
3335

34-
def __init__(self, octue_configuration_path, image_uri_template=None):
35-
super().__init__(octue_configuration_path, image_uri_template)
36+
def __init__(self, octue_configuration_path, image_uri_template=None, revision_tag=None):
37+
super().__init__(octue_configuration_path, image_uri_template, revision_tag=revision_tag)
3638
self.build_trigger_description = f"Build the {self.service_sruid!r} service and deploy it to Cloud Run."
3739

3840
def deploy(self, no_cache=False, update=False):
@@ -137,6 +139,7 @@ def _generate_cloud_build_configuration(self, no_cache=False):
137139
f"--region={self.service_configuration.region}",
138140
f"--memory={self.service_configuration.memory}",
139141
f"--cpu={self.service_configuration.cpus}",
142+
f"--execution-environment={self.service_configuration.execution_environment}",
140143
f"--set-env-vars={environment_variables}",
141144
"--timeout=3600",
142145
f"--concurrency={self.service_configuration.concurrency}",

octue/cloud/deployment/google/dataflow/deployer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ class DataflowDeployer(BaseDeployer):
3232
```
3333
3434
:param str octue_configuration_path: the path to the `octue.yaml` file if it's not in the current working directory
35+
:param str|None image_uri_template: the image URI template to use to name and store images with Cloud Build
36+
:param str revision_tag: a tag to use for this revision of the service (e.g. 1.3.7). This overrides the `OCTUE_SERVICE_REVISION_TAG` environment variable if it's present. If this option isn't given and the environment variable isn't present, a random 'cool name' tag is generated e.g 'curious-capybara'.
3537
:return None:
3638
"""
3739

3840
TOTAL_NUMBER_OF_STAGES = 3
3941

40-
def __init__(self, octue_configuration_path, image_uri_template=None):
41-
super().__init__(octue_configuration_path, image_uri_template)
42+
def __init__(self, octue_configuration_path, image_uri_template=None, revision_tag=None):
43+
super().__init__(octue_configuration_path, image_uri_template, revision_tag=revision_tag)
4244
self.build_trigger_description = f"Build the {self.service_sruid!r} service and deploy it to Dataflow."
4345

4446
if not self.service_configuration.temporary_files_location:

octue/cloud/emulators/child.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ class ChildEmulator:
1919
2020
:param str|None id: the ID of the child; a UUID is generated if none is provided
2121
:param dict|None backend: a dictionary including the key "name" with a value of the name of the type of backend (e.g. "GCPPubSubBackend") and key-value pairs for any other parameters the chosen backend expects; a mock backend is used if none is provided
22-
:param str|None internal_service_name: the name to give to the internal service used to ask questions to the child; defaults to "<id>-parent"
22+
:param str internal_service_name: the name to give to the internal service used to ask questions to the child
2323
:param list(dict)|None messages: the list of messages to send to the parent
2424
:return None:
2525
"""
2626

27-
def __init__(self, id=None, backend=None, internal_service_name=None, messages=None):
27+
def __init__(self, id=None, backend=None, internal_service_name="local/local:local", messages=None):
2828
self.messages = messages or []
2929

3030
backend = copy.deepcopy(backend or {"name": "GCPPubSubBackend", "project_name": "emulated-project"})
@@ -36,7 +36,7 @@ def __init__(self, id=None, backend=None, internal_service_name=None, messages=N
3636

3737
self._parent = MockService(
3838
backend=backend,
39-
service_id=internal_service_name or f"{self.id}-parent",
39+
service_id=internal_service_name,
4040
children={self._child.id: self._child},
4141
)
4242

octue/configuration.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def __init__(
4242
memory="128Mi",
4343
cpus=1,
4444
minimum_instances=0,
45+
execution_environment="gen2",
4546
temporary_files_location=None,
4647
setup_file_path=None,
4748
service_account_email=None,
@@ -72,6 +73,7 @@ def __init__(
7273
self.memory = memory
7374
self.cpus = cpus
7475
self.minimum_instances = minimum_instances
76+
self.execution_environment = execution_environment
7577

7678
# Dataflow services only.
7779
self.temporary_files_location = temporary_files_location

octue/metadata/recorded_questions.jsonl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@
5757
{"parent_sdk_version": "0.40.2", "question": {"data": "{\"input_values\": {\"height\": 4, \"width\": 72}, \"input_manifest\": \"{\\n \\\"datasets\\\": {\\n \\\"my_dataset\\\": \\\"/var/folders/sk/hf5fbp616c77tsys9lz55qn40000gp/T/tmprj5sl5al\\\"\\n },\\n \\\"id\\\": \\\"6ce744fb-fce6-4346-949a-109675ca7a5a\\\",\\n \\\"name\\\": null\\n}\", \"children\": null}", "attributes": {"question_uuid": "42d9a880-8672-4be6-a3ae-b8518a9e58db", "forward_logs": "1", "allow_save_diagnostics_data_on_crash": "1", "octue_sdk_version": "0.40.2"}}}
5858
{"parent_sdk_version": "0.41.0", "question": {"data": "{\"input_values\": {\"height\": 4, \"width\": 72}, \"input_manifest\": \"{\\n \\\"datasets\\\": {\\n \\\"my_dataset\\\": \\\"/var/folders/sk/hf5fbp616c77tsys9lz55qn40000gp/T/tmpnc9s8hwm\\\"\\n },\\n \\\"id\\\": \\\"b21ca765-9dac-4291-a301-daa4d282f1fd\\\",\\n \\\"name\\\": null\\n}\", \"children\": null}", "attributes": {"question_uuid": "76ae0850-50b7-4c7d-a22b-c8bcd0ab2bb3", "forward_logs": "1", "allow_save_diagnostics_data_on_crash": "1", "octue_sdk_version": "0.41.0"}}}
5959
{"parent_sdk_version": "0.41.1", "question": {"data": "{\"input_values\": {\"height\": 4, \"width\": 72}, \"input_manifest\": {\"id\": \"6e57125c-3e32-407c-b0f8-6403d2db6d8a\", \"name\": null, \"datasets\": {\"my_dataset\": \"/var/folders/sk/hf5fbp616c77tsys9lz55qn40000gp/T/tmpsri5p05d\"}}, \"children\": null}", "attributes": {"question_uuid": "07bfa681-a93c-44e4-9ee7-a33e9518a77c", "forward_logs": "1", "allow_save_diagnostics_data_on_crash": "1", "octue_sdk_version": "0.41.1"}}}
60+
{"parent_sdk_version": "0.42.0", "question": {"data": "{\"input_values\": {\"height\": 4, \"width\": 72}, \"input_manifest\": {\"id\": \"0a0f80fc-0e81-4a3e-b345-84e7b3e049c6\", \"name\": null, \"datasets\": {\"my_dataset\": \"/var/folders/sk/hf5fbp616c77tsys9lz55qn40000gp/T/tmplu287_ud\"}}, \"children\": null}", "attributes": {"question_uuid": "fcd103db-6b9d-497b-a4d6-1c8f8c4403b6", "forward_logs": "1", "allow_save_diagnostics_data_on_crash": "1", "octue_sdk_version": "0.42.0"}}}

0 commit comments

Comments
 (0)