Skip to content

Commit aa256bc

Browse files
authored
MRG: Merge pull request #587 from octue/feature/add-get-latest-service-function
Add ability to get latest revision of a service
2 parents f802b9a + b5adc92 commit aa256bc

34 files changed

+603
-238
lines changed

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ repos:
7777
- "^dependencies/([a-z][a-z0-9]*)(-[a-z0-9]+)*$"
7878

7979
- repo: https://github.com/octue/conventional-commits
80-
rev: 0.8.1
80+
rev: 0.9.0
8181
hooks:
8282
- id: check-commit-message-is-conventional
8383
stages: [commit-msg]

docs/source/asking_questions.rst

+55-35
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,11 @@
44
Asking services questions
55
=========================
66

7-
Octue services
8-
==============
9-
10-
There's a growing range of live :ref:`services <service_definition>` in the Octue ecosystem that you can ask questions
11-
to and get answers from. Currently, all of them are related to wind energy. Here's a quick glossary of terms before we
12-
tell you more:
13-
14-
.. admonition:: Definitions
15-
16-
Child
17-
An Octue service that can be asked a question. This name reflects the tree structure of services (specifically,
18-
`a DAG <https://en.wikipedia.org/wiki/Directed_acyclic_graph>`_) formed by the service asking the question (the
19-
parent), the child it asks the question to, any children that the child asks questions to as part of forming
20-
its answer, and so on.
21-
22-
Parent
23-
An Octue service that asks a question to another Octue service (a child).
24-
25-
Asking a question
26-
Sending data (input values and/or an input manifest) to a child for processing/analysis.
27-
28-
Receiving an answer
29-
Receiving data (output values and/or an output manifest) from a child you asked a question to.
30-
31-
Octue ecosystem
32-
The set of services running the ``octue`` SDK as their backend. These services guarantee:
33-
34-
- Defined JSON schemas and validation for input and output data
35-
- An easy interface for asking them questions and receiving their answers
36-
- Logs and exceptions (and potentially monitor messages) forwarded to you
37-
- High availability if deployed in the cloud
38-
39-
407
How to ask a question
418
=====================
42-
You can ask any service a question if you have its service ID, project name, and permissions. The question is formed of
43-
input values and/or an input manifest.
9+
Questions are always asked to a *revision* of a service. You can ask a service a question if you have its
10+
:ref:`SRUID <sruid_definition>`, project name, and the necessary permissions. The question is formed of input values
11+
and/or an input manifest.
4412

4513
.. code-block:: python
4614
@@ -63,6 +31,16 @@ input values and/or an input manifest.
6331
>>> <FilterSet({<Datafile('my_file.csv')>, <Datafile('another_file.csv')>})>
6432
6533
34+
.. _using_latest_revision_tag:
35+
36+
.. note::
37+
38+
Using the ``latest`` service revision tag, or not including one at all, will cause your question to be sent to the
39+
latest deployed revision of the service. This is determined by making a request to a `service registry
40+
<https://django-twined.readthedocs.io/en/latest/>`_ if one or more :ref:`registries are defined
41+
<using_service_registries>`. If none of the service registries contain an entry for this service, a specific service
42+
revision tag must be used.
43+
6644
You can also set the following options when you call :mod:`Child.ask <octue.resources.child.Child.ask>`:
6745

6846
- ``children`` - If the child has children of its own (i.e. grandchildren of the parent), this optional argument can be used to override the child's "default" children. This allows you to specify particular versions of grandchildren to use (see :ref:`this subsection below <overriding_children>`).
@@ -230,3 +208,45 @@ Overriding beyond the first generation
230208
It's an intentional choice to only go one generation deep with overriding children. If you need to be able to specify a
231209
whole tree of children, grandchildren, and so on, please `upvote this issue.
232210
<https://github.com/octue/octue-sdk-python/issues/528>`_
211+
212+
213+
.. _using_service_registries:
214+
215+
Using a service registry
216+
========================
217+
When asking a question, you can optionally specify one or more `service registries
218+
<https://django-twined.readthedocs.io/en/latest/>`_ to resolve SRUIDs against. This is analogous to specifying a
219+
different ``pip`` index for resolving package names when using ``pip install``. If you don't specify any registries, the
220+
default Octue service registry is used.
221+
222+
Specifying service registries can be useful if:
223+
224+
- You have your own private services that aren't on the default Octue service registry
225+
- You want services from one service registry with the same name as in another service registry to be prioritised
226+
227+
Specifying service registries
228+
-----------------------------
229+
You can specify service registries in two ways:
230+
231+
1. Globally for all questions asked inside a service. In the service configuration (``octue.yaml`` file):
232+
233+
.. code-block:: yaml
234+
235+
services:
236+
- namespace: my-organisation
237+
name: my-app
238+
service_registries:
239+
- name: my-registry
240+
endpoint: blah.com/services
241+
242+
2. For questions to a specific child, inside or outside a service:
243+
244+
.. code-block:: python
245+
246+
child = Child(
247+
id="my-organisation/my-service:latest",
248+
backend={"name": "GCPPubSubBackend", "project_name": "my-project"},
249+
service_registries=[
250+
{"name": "my-registry", "endpoint": "blah.com/services"},
251+
]
252+
)

docs/source/creating_services.rst

+1-40
Original file line numberDiff line numberDiff line change
@@ -113,49 +113,10 @@ Dockerfile (optional)
113113

114114
As always, if you need help with this, feel free to drop us a message or raise an issue!
115115

116-
Naming services
117-
===============
118-
119-
.. admonition:: Definitions
120-
121-
Service revision
122-
A specific instance of an Octue service that can be individually addressed. The revision could correspond to a
123-
version of the service, a dynamic development branch for it, or a deliberate duplication or variation of it.
124-
125-
Service revision unique identifier (SRUID)
126-
The combination of a service revisions's namespace, name, and revision tag that uniquely identifies it. For
127-
example, ``octue/my-service:1.3.0`` where the namespace is ``octue``, the name is ``my-service``, and the
128-
revision tag is ``1.3.0``.
129-
130-
Service namespace
131-
The group to which the service belongs e.g. your name or your organisation's name. If in doubt, use the GitHub
132-
handle of the user or organisation publishing the services.
133-
134-
Namespaces must be lower kebab case (i.e. they may contain the letters [a-z], numbers [0-9], and hyphens [-]).
135-
They may not begin or end with hyphens.
136-
137-
Service name
138-
A name to uniquely identify the service within its namespace. This usually corresponds to the name of the GitHub
139-
repository for the service. Names must be lower kebab case (i.e. they may contain the letters [a-z],
140-
numbers [0-9] and hyphens [-]). They may not begin or end with hyphens.
141-
142-
Service revision tag
143-
A tag that uniquely identifies a particular revision of a service. The revision tag could correspond to a commit
144-
hash like ``a3eb45``, a release number like ``0.12.4``, a branch name (e.g. ``development``), a particular
145-
environment the service is deployed in (e.g. ``production``), or a combination like ``0.12.4-production``. Tags
146-
may contain lowercase and uppercase letters, numbers, underscores, periods, and hyphens, but can't start with a
147-
period or a dash. They can contain a maximum of 128 characters. These requirements are the same as the `Docker
148-
tag format <https://docs.docker.com/engine/reference/commandline/tag/>`_.
149-
150-
Service ID
151-
The SRUID is a special case of the service ID. A service ID can be an SRUID or just the service namespace and
152-
name. It can be used to ask a question to a service without specifying a specific revision of it. This enables
153-
asking questions to, for example, the service ``octue/my-service`` and automatically having them routed to its
154-
latest revision. Note that this will be a future feature; currently, you will still be required to provide a
155-
revision tag (i.e. a full SRUID).
156116

157117
Where to specify the namespace, name, and revision tag
158118
------------------------------------------------------
119+
See :ref:`here <service_naming>` for service naming requirements.
159120

160121
**Namespace**
161122

docs/source/index.rst

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ groundwork so you have more time for the science!
1010
.. admonition:: Definition
1111

1212
Octue service
13-
An Octue data service, digital twin, or application that can be asked questions, process them, and send answers.
14-
Octue services can communicate with each other with no extra setup.
13+
An Octue data service, digital twin, or application that can be asked questions, process them, and return
14+
answers. Octue services can communicate with each other with minimal extra setup.
1515

1616

1717
Key features
@@ -82,6 +82,7 @@ We use `GitHub Issues <https://github.com/octue/octue-sdk-python/issues>`_ [#]_
8282
datafile
8383
dataset
8484
manifest
85+
services
8586
asking_questions
8687
creating_services
8788
running_services_locally

docs/source/services.rst

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
.. _services:
2+
3+
==============
4+
Octue services
5+
==============
6+
7+
There's a growing range of live :ref:`services <service_definition>` in the Octue ecosystem that you can ask questions
8+
to and get answers from. Currently, all of them are related to wind energy. Here's a quick glossary of terms before we
9+
tell you more:
10+
11+
.. admonition:: Definitions
12+
13+
Octue service
14+
See :ref:`here <service_definition>`.
15+
16+
Child
17+
An Octue service that can be asked a question. This name reflects the tree structure of services (specifically,
18+
`a DAG <https://en.wikipedia.org/wiki/Directed_acyclic_graph>`_) formed by the service asking the question (the
19+
parent), the child it asks the question to, any children that the child asks questions to as part of forming
20+
its answer, and so on.
21+
22+
Parent
23+
An Octue service that asks a question to another Octue service (a child).
24+
25+
Asking a question
26+
Sending data (input values and/or an input manifest) to a child for processing/analysis.
27+
28+
Receiving an answer
29+
Receiving data (output values and/or an output manifest) from a child you asked a question to.
30+
31+
Octue ecosystem
32+
The set of services running the Octue SDK as their backend. These services guarantee:
33+
34+
- Defined input/output JSON schemas and validation
35+
- An easy and consistent interface for asking them questions and receiving their answers
36+
- Logs, exceptions, and monitor messages forwarded to you
37+
- High availability (if deployed in the cloud)
38+
39+
40+
.. _service_naming:
41+
42+
Service names
43+
=============
44+
45+
Questions are always asked to a *revision* of a service. Services revisions are named in a similar way to docker images.
46+
They look like ``namespace/name:tag`` where the tag is often a semantic version (but doesn't have to be).
47+
48+
.. admonition:: Definitions
49+
50+
Service revision
51+
A specific instance of an Octue service that can be individually addressed. The revision could correspond to a
52+
version of the service, a dynamic development branch for it, or a deliberate duplication or variation of it.
53+
54+
.. _sruid_definition:
55+
56+
Service revision unique identifier (SRUID)
57+
The combination of a service revision's namespace, name, and revision tag that uniquely identifies it. For
58+
example, ``octue/my-service:1.3.0`` where the namespace is ``octue``, the name is ``my-service``, and the
59+
revision tag is ``1.3.0``.
60+
61+
Service namespace
62+
The group to which the service belongs e.g. your name or your organisation's name. If in doubt, use the GitHub
63+
handle of the user or organisation publishing the services.
64+
65+
Namespaces must be lower kebab case (i.e. they may contain the letters [a-z], numbers [0-9], and hyphens [-]).
66+
They may not begin or end with hyphens.
67+
68+
Service name
69+
A name to uniquely identify the service within its namespace. This usually corresponds to the name of the GitHub
70+
repository for the service. Names must be lower kebab case (i.e. they may contain the letters [a-z], numbers
71+
[0-9] and hyphens [-]). They may not begin or end with hyphens.
72+
73+
Service revision tag
74+
A tag that uniquely identifies a particular revision of a service. The revision tag could be a:
75+
76+
- Commit hash (e.g. ``a3eb45``)
77+
- Semantic version (e.g. ``0.12.4``)
78+
- Branch name (e.g. ``development``)
79+
- Particular environment the service is deployed in (e.g. ``production``)
80+
- Combination of these (e.g. ``0.12.4-production``)
81+
82+
Tags may contain lowercase and uppercase letters, numbers, underscores, periods, and hyphens, but can't start
83+
with a period or a dash. They can contain a maximum of 128 characters. These requirements are the same as the
84+
`Docker tag format <https://docs.docker.com/engine/reference/commandline/tag/>`_.
85+
86+
Service ID
87+
The SRUID is a special case of a service ID. A service ID can be an SRUID or just the service namespace and
88+
name. It can be used to ask a question to a service without specifying a specific revision of it. This enables
89+
asking questions to, for example, the service ``octue/my-service`` and automatically having them routed to its
90+
latest revision. :ref:`See here for more info<using_latest_revision_tag>`.

octue/cli.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import copy
22
import functools
3+
import importlib.metadata
34
import importlib.util
45
import json
56
import logging
67
import os
78
import sys
89

910
import click
10-
import pkg_resources
1111
from google import auth
1212

1313
from octue.cloud import storage
1414
from octue.cloud.pub_sub import Subscription, Topic
1515
from octue.cloud.pub_sub.service import Service
16-
from octue.cloud.service_id import convert_service_id_to_pub_sub_form, create_service_sruid, get_service_sruid_parts
16+
from octue.cloud.service_id import convert_service_id_to_pub_sub_form, create_sruid, get_sruid_parts
1717
from octue.cloud.storage import GoogleCloudStorageClient
1818
from octue.configuration import load_service_and_app_configuration
1919
from octue.definitions import MANIFEST_FILENAME, VALUES_FILENAME
@@ -53,7 +53,7 @@
5353
show_default=True,
5454
help="Forces a reset of analysis cache and outputs [For future use, currently not implemented]",
5555
)
56-
@click.version_option(version=pkg_resources.get_distribution("octue").version)
56+
@click.version_option(version=importlib.metadata.version("octue"))
5757
def octue_cli(id, logger_uri, log_level, force_reset):
5858
"""The CLI for the Octue SDK. Use it to start an Octue data service or digital twin locally or run an analysis on
5959
one locally.
@@ -137,6 +137,7 @@ def run(service_config, input_dir, output_file, output_manifest_file, monitor_me
137137
children=app_configuration.children,
138138
output_location=app_configuration.output_location,
139139
crash_diagnostics_cloud_path=service_configuration.crash_diagnostics_cloud_path,
140+
service_registries=service_configuration.service_registries,
140141
)
141142

142143
if monitor_messages_file:
@@ -212,7 +213,7 @@ def start(service_config, revision_tag, timeout, no_rm):
212213
"""
213214
service_revision_tag_override = revision_tag
214215
service_configuration, app_configuration = load_service_and_app_configuration(service_config)
215-
service_namespace, service_name, service_revision_tag = get_service_sruid_parts(service_configuration)
216+
service_namespace, service_name, service_revision_tag = get_sruid_parts(service_configuration)
216217

217218
if service_revision_tag_override and service_revision_tag:
218219
logger.warning(
@@ -222,7 +223,7 @@ def start(service_config, revision_tag, timeout, no_rm):
222223
service_revision_tag_override,
223224
)
224225

225-
service_sruid = create_service_sruid(
226+
service_sruid = create_sruid(
226227
namespace=service_namespace,
227228
name=service_name,
228229
revision_tag=service_revision_tag_override or service_revision_tag,
@@ -262,7 +263,7 @@ def start(service_config, revision_tag, timeout, no_rm):
262263

263264
except ServiceAlreadyExists:
264265
# Generate and use a new revision tag if the service already exists.
265-
service_sruid = create_service_sruid(namespace=service_namespace, name=service_name)
266+
service_sruid = create_sruid(namespace=service_namespace, name=service_name)
266267

267268
while True:
268269
user_confirmation = input(
@@ -438,7 +439,7 @@ def create_push_subscription(
438439
PUSH_ENDPOINT is the HTTP/HTTPS endpoint of the service to push to. It should be fully formed and include the
439440
'https://' prefix
440441
"""
441-
service_sruid = create_service_sruid(namespace=service_namespace, name=service_name, revision_tag=revision_tag)
442+
service_sruid = create_sruid(namespace=service_namespace, name=service_name, revision_tag=revision_tag)
442443
pub_sub_sruid = convert_service_id_to_pub_sub_form(service_sruid)
443444

444445
topic = Topic(name=pub_sub_sruid, project_name=project_name)

octue/cloud/deployment/google/answer_pub_sub_question.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22

33
from octue.cloud.pub_sub.service import Service
4-
from octue.cloud.service_id import create_service_sruid, get_service_sruid_parts
4+
from octue.cloud.service_id import create_sruid, get_sruid_parts
55
from octue.configuration import load_service_and_app_configuration
66
from octue.resources.service_backends import GCPPubSubBackend
77
from octue.runner import Runner
@@ -22,9 +22,9 @@ def answer_question(question, project_name):
2222
:return None:
2323
"""
2424
service_configuration, app_configuration = load_service_and_app_configuration(DEFAULT_SERVICE_CONFIGURATION_PATH)
25-
service_namespace, service_name, service_revision_tag = get_service_sruid_parts(service_configuration)
25+
service_namespace, service_name, service_revision_tag = get_sruid_parts(service_configuration)
2626

27-
service_sruid = create_service_sruid(
27+
service_sruid = create_sruid(
2828
namespace=service_namespace,
2929
name=service_name,
3030
revision_tag=service_revision_tag,
@@ -46,6 +46,7 @@ def answer_question(question, project_name):
4646
crash_diagnostics_cloud_path=service_configuration.crash_diagnostics_cloud_path,
4747
project_name=project_name,
4848
service_id=service_sruid,
49+
service_registries=service_configuration.service_registries,
4950
)
5051

5152
service.run_function = runner.run

0 commit comments

Comments
 (0)