Skip to content

Commit 611b32b

Browse files
authored
COH-30144 Implement NSLookup resolution for Python Client (#193)
* nslookup address resolution code integration
1 parent 7994e6d commit 611b32b

File tree

7 files changed

+424
-14
lines changed

7 files changed

+424
-14
lines changed
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Copyright 2022, 2025, Oracle Corporation and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at
3+
# https://oss.oracle.com/licenses/upl.
4+
5+
name: CI nslookup Build
6+
on:
7+
workflow_dispatch:
8+
schedule:
9+
- cron: "0 5 * * *"
10+
push:
11+
branches:
12+
- '*'
13+
pull_request:
14+
branches: [ main ]
15+
jobs:
16+
ci-nslookup:
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
python-version: ["3.9.x"]
21+
poetry-version: ["1.8.4"]
22+
os: [ubuntu-latest]
23+
coherenceVersion:
24+
- 24.09
25+
- 22.06.10
26+
base-image:
27+
- gcr.io/distroless/java17-debian11
28+
runs-on: ${{ matrix.os }}
29+
steps:
30+
- name: Set up JDK
31+
uses: actions/setup-java@v4
32+
with:
33+
java-version: '17'
34+
distribution: 'zulu'
35+
36+
- uses: actions/checkout@v4
37+
- uses: actions/setup-python@v5
38+
with:
39+
python-version: ${{ matrix.python-version }}
40+
41+
- name: Cache Maven packages
42+
uses: actions/cache@v4
43+
with:
44+
path: ~/.m2
45+
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
46+
restore-keys: ${{ runner.os }}-m2
47+
48+
- name: Install Poetry
49+
shell: bash
50+
run: |
51+
pip install poetry==${{ matrix.poetry-version }}
52+
53+
- name: Install Dependencies
54+
run: python -m poetry install
55+
56+
- name: Configure Validation
57+
run: python -m poetry run make validate-setup
58+
59+
- name: Validate Sources
60+
run: python -m poetry run make validate
61+
62+
- name: Run Coherence Server
63+
shell: bash
64+
run: |
65+
export COHERENCE_VERSION=${{ matrix.coherenceVersion }}
66+
curl -sL https://raw.githubusercontent.com/oracle/coherence-cli/main/scripts/install.sh | bash
67+
cohctl version
68+
cohctl create cluster grpc-cluster -v ${{ matrix.coherenceVersion }} -y -a coherence-grpc-proxy
69+
sleep 20
70+
cohctl monitor health -n localhost:7574 -T 40 -w
71+
72+
- name: Run test
73+
shell: bash
74+
run: |
75+
export COHERENCE_SERVER_ADDRESS=coherence:///localhost:7574
76+
python -m poetry run make test-nslookup
77+
78+
- name: Archive server logs
79+
if: failure()
80+
uses: actions/upload-artifact@v4
81+
with:
82+
name: server-logs-${{matrix.python-version}}-${{ matrix.coherenceVersion }}
83+
path: ./tests/utils/*.txt
84+
retention-days: 10

.pre-commit-config.yaml

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,29 @@ exclude: \w*(_pb2)\w*
88

99
repos:
1010
- repo: https://github.com/pre-commit/pre-commit-hooks
11-
rev: cef0300fd0fc4d2a87a85fa2093c6b283ea36f4b # frozen: v5.0.0
11+
rev: v5.0.0
1212
hooks:
1313
- id: trailing-whitespace
1414
- id: end-of-file-fixer
1515
- id: check-yaml
1616
- id: check-added-large-files
1717

1818
- repo: https://github.com/PyCQA/flake8
19-
rev: e43806be3607110919eff72939fda031776e885a # frozen: 7.1.1
19+
rev: 7.1.1
2020
hooks:
2121
- id: flake8
2222

2323
- repo: https://github.com/psf/black
24-
rev: 1b2427a2b785cc4aac97c19bb4b9a0de063f9547 # frozen: 24.10.0
24+
rev: 24.10.0
2525
hooks:
2626
- id: black
2727

2828
- repo: https://github.com/pre-commit/mirrors-mypy
29-
rev: f56614daa94d5cd733d3b7004c5df9caad267b4a # frozen: v1.13.0
29+
rev: v1.13.0
3030
hooks:
3131
- id: mypy
3232

3333
- repo: https://github.com/PyCQA/isort
34-
rev: c235f5e450b4b84e58d114ed4c589cbf454175a3 # frozen: 5.13.2
34+
rev: 5.13.2
3535
hooks:
3636
- id: isort

Makefile

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# ----------------------------------------------------------------------------------------------------------------------
2-
# Copyright (c) 2022, 2023, Oracle and/or its affiliates.
2+
# Copyright (c) 2022, 2025, Oracle and/or its affiliates.
33
# Licensed under the Universal Permissive License v 1.0 as shown at
44
# https://oss.oracle.com/licenses/upl.
55
#
@@ -171,6 +171,17 @@ generate-proto: ## Generate Proto Files
171171
test: ##
172172
pytest -W error --cov src/coherence --cov-report=term --cov-report=html $(UNIT_TESTS) $(E2E_TESTS)
173173

174+
# ----------------------------------------------------------------------------------------------------------------------
175+
# Run nslookup tests with code coverage
176+
# ----------------------------------------------------------------------------------------------------------------------
177+
.PHONY: test-nslookup
178+
test-nslookup: ##
179+
pytest -W error \
180+
--deselect tests/e2e/test_session.py::test_basics \
181+
--deselect tests/e2e/test_session.py::test_session_lifecycle \
182+
--deselect tests/e2e/test_session.py::test_fail_fast \
183+
$(UNIT_TESTS) $(E2E_TESTS)
184+
174185
# ----------------------------------------------------------------------------------------------------------------------
175186
# Run unit tests with code coverage
176187
# ----------------------------------------------------------------------------------------------------------------------

src/coherence/client.py

+20-4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
from .filter import Filter
5757
from .local_cache import CacheStats, LocalCache, NearCacheOptions
5858
from .messages_pb2 import PageRequest
59+
from .nslookup import AsyncNSLookup
5960
from .processor import EntryProcessor
6061
from .proxy_service_messages_v1_pb2 import ProxyRequest, ProxyResponse
6162
from .proxy_service_v1_pb2_grpc import ProxyServiceStub
@@ -1574,7 +1575,9 @@ class Options:
15741575
"""
15751576
Environment variable to specify the Coherence gRPC server address for the client to connect to. The
15761577
environment variable is used if address is not passed as an argument in the constructor. If the environment
1577-
variable is not set and address is not passed as an argument then `DEFAULT_ADDRESS` is used
1578+
variable is not set and address is not passed as an argument then `DEFAULT_ADDRESS` is used. One can also
1579+
use the 'coherence' gRPC resolver address of "coherence:///host:port" to connect to the Coherence Name
1580+
Service, running on the cluster port, and automatically discover the gRPC endpoints.
15781581
"""
15791582
ENV_REQUEST_TIMEOUT = "COHERENCE_CLIENT_REQUEST_TIMEOUT"
15801583
"""
@@ -1627,9 +1630,11 @@ def __init__(
16271630
"""
16281631
Construct a new :func:`coherence.client.Options`
16291632
1630-
:param address: Address of the target Coherence cluster. If not explicitly set, this defaults
1631-
to :func:`coherence.client.Options.DEFAULT_ADDRESS`. See
1632-
also :func:`coherence.client.Options.ENV_SERVER_ADDRESS`
1633+
:param address: Address of the target Coherence cluster gRPC endpoint of the form "host:port" to connect to.
1634+
If not explicitly set, this defaults to :func:`coherence.client.Options.DEFAULT_ADDRESS`. See
1635+
also :func:`coherence.client.Options.ENV_SERVER_ADDRESS`. One can also use the 'coherence' gRPC resolver
1636+
address of "coherence:///host:port" to connect to the Coherence Name Service, running on the
1637+
cluster port, and automatically discover the gRPC endpoints.
16331638
:param scope: scope name used to link this :func:`coherence.client.Options` to the
16341639
corresponding `ConfigurableCacheFactory` on the server.
16351640
:param request_timeout_seconds: Defines the request timeout, in `seconds`, that will be applied to each
@@ -1667,6 +1672,14 @@ def __init__(
16671672
if tls_options is not None:
16681673
self._tls_options = tls_options
16691674

1675+
async def _resolve_ns_address(self) -> None:
1676+
if self.address.startswith("coherence:///"):
1677+
# Remove the prefix and split into host and port
1678+
_, ns_addr = self._address.split("coherence:///", 1)
1679+
1680+
# Resolve to grpc address from nameservice address
1681+
self._address = await AsyncNSLookup._resolve_nslookup_address(ns_addr)
1682+
16701683
@property
16711684
def tls_options(self) -> Optional[TlsOptions]:
16721685
"""
@@ -1908,6 +1921,9 @@ def __init__(self, session_options: Optional[Options] = None):
19081921

19091922
@staticmethod
19101923
async def create(session_options: Optional[Options] = None) -> Session:
1924+
if session_options is None:
1925+
session_options = Options()
1926+
await session_options._resolve_ns_address()
19111927
session: Session = Session(session_options)
19121928
await session._set_ready(False)
19131929
await session._handshake.handshake()

src/coherence/entry.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# Copyright (c) 2022, 2024, Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at
3+
# https://oss.oracle.com/licenses/upl.
4+
15
from __future__ import annotations
26

37
from typing import Generic, TypeVar

0 commit comments

Comments
 (0)