-
Notifications
You must be signed in to change notification settings - Fork 1
DO NOT MERGE YET: Helper functions for qe SGW tests #389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
5146d18
CBG-5213 support dropping rosmar buckets
torcolvin da34ba2
Fix use of SyncGatewayUserClient
torcolvin e30562e
add helper functions for SG tests
torcolvin a1c9ed9
fix wait_for_deleted
torcolvin 561f985
Potential fix for pull request finding
torcolvin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,15 @@ | ||
| import asyncio | ||
| import contextlib | ||
| import re | ||
| import ssl | ||
| from abc import ABC, abstractmethod | ||
| from json import dumps, loads | ||
| from pathlib import Path | ||
| from typing import Any, cast | ||
| from typing import Any, AsyncIterator, cast | ||
| from urllib.parse import urljoin | ||
|
|
||
| import requests | ||
| import tenacity | ||
| from aiohttp import BasicAuth, ClientError, ClientSession, ClientTimeout, TCPConnector | ||
| from aiohttp.client_exceptions import ClientConnectorError | ||
| from opentelemetry.trace import get_tracer | ||
|
|
@@ -17,7 +19,7 @@ | |
| from cbltest.assertions import _assert_not_null | ||
| from cbltest.httplog import get_next_writer | ||
| from cbltest.jsonhelper import _get_typed_required | ||
| from cbltest.logging import cbl_error, cbl_info, cbl_warning | ||
| from cbltest.logging import cbl_error, cbl_info, cbl_trace, cbl_warning | ||
| from cbltest.utils import assert_not_null | ||
| from cbltest.version import VERSION | ||
|
|
||
|
|
@@ -529,7 +531,7 @@ def __init__(self, response: dict): | |
| class _SyncGatewayBase: | ||
| """ | ||
| Base class for Sync Gateway clients containing common document and database operations. | ||
| This class should not be instantiated directly - use SyncGateway or SyncGatewayPublic instead. | ||
| This class should not be instantiated directly - use SyncGateway or SyncGatewayUserClient instead. | ||
| """ | ||
|
|
||
| def __init__( | ||
|
|
@@ -556,21 +558,6 @@ def __init__( | |
| port, | ||
| BasicAuth(username, password, "ascii"), | ||
| ) | ||
| r = requests.get( | ||
| f"{scheme}{url}:{port}/_config", | ||
| auth=(username, password), | ||
| # disable hostname verification as we do in _create_session | ||
| verify=False, | ||
| timeout=10, | ||
| ) | ||
| r.raise_for_status() | ||
| try: | ||
| self.using_rosmar = r.json()["bootstrap"]["server"].startswith("rosmar") | ||
| except AttributeError: | ||
| raise CblTestError( | ||
| "Unexpected response {r.json()} from Sync Gateway /_config endpoint, cannot determine if using Rosmar" | ||
| ) from None | ||
| self.using_rosmar = False | ||
|
|
||
| @property | ||
| def hostname(self) -> str: | ||
|
|
@@ -587,6 +574,11 @@ def secure(self) -> bool: | |
| """Gets whether the Sync Gateway instance uses TLS""" | ||
| return self.__secure | ||
|
|
||
| @property | ||
| def scheme(self) -> str: | ||
| """Gets the URL scheme to use when connecting to the Sync Gateway instance (http or https)""" | ||
| return "https://" if self.secure else "http://" | ||
|
|
||
| def _create_session( | ||
| self, secure: bool, scheme: str, url: str, port: int, auth: BasicAuth | None | ||
| ) -> ClientSession: | ||
|
|
@@ -644,11 +636,19 @@ async def _send_request( | |
|
|
||
| return ret_val | ||
|
|
||
| async def supports_version_vectors(self) -> bool: | ||
| """Return true if Sync Gateway supports version vectors.""" | ||
| resp_data = await self._send_request("get", "/_cluster_info") | ||
| for bucket in resp_data: | ||
| return bucket.get("enable_cross_cluster_versioning") | ||
|
Comment on lines
+642
to
+643
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually true, what if the field isn't there, atleast wrapping this in a |
||
| raise CblTestError( | ||
| "Could not determine if Sync Gateway supports version vectors, need at least one database" | ||
| ) | ||
|
|
||
| async def get_version(self) -> CouchbaseVersion: | ||
| # Telemetry not really important for this call | ||
| scheme = "https://" if self.secure else "http://" | ||
| async with self._create_session( | ||
| self.secure, scheme, self.hostname, 4984, None | ||
| self.secure, self.scheme, self.hostname, 4984, None | ||
| ) as s: | ||
| resp = await self._send_request("get", "/", session=s) | ||
| assert isinstance(resp, dict) | ||
|
|
@@ -659,7 +659,7 @@ async def get_version(self) -> CouchbaseVersion: | |
|
|
||
| def tls_cert(self) -> str | None: | ||
| if not self.secure: | ||
| cbl_warning( | ||
| cbl_trace( | ||
| "Sync Gateway instance not using TLS, returning empty tls_cert..." | ||
| ) | ||
| return None | ||
|
|
@@ -1345,9 +1345,8 @@ async def get_document_revision_public( | |
| ) | ||
| params = {"rev": revision} | ||
|
|
||
| scheme = "https://" if self.secure else "http://" | ||
| async with self._create_session( | ||
| self.secure, scheme, self.hostname, 4984, auth | ||
| self.secure, self.scheme, self.hostname, 4984, auth | ||
| ) as session: | ||
| return await self._send_request("GET", path, params=params, session=session) | ||
|
|
||
|
|
@@ -1615,6 +1614,21 @@ def __init__( | |
| """ | ||
| super().__init__(url, username, password, port, secure) | ||
| self.__public_port = public_port | ||
| r = requests.get( | ||
| f"{self.scheme}{url}:{port}/_config", | ||
| auth=(username, password), | ||
| # disable hostname verification as we do in _create_session | ||
| verify=False, | ||
| timeout=10, | ||
| ) | ||
| r.raise_for_status() | ||
| try: | ||
| self.using_rosmar = r.json()["bootstrap"]["server"].startswith("rosmar") | ||
| except AttributeError: | ||
| raise CblTestError( | ||
| "Unexpected response {r.json()} from Sync Gateway /_config endpoint, cannot determine if using Rosmar" | ||
| ) from None | ||
| self.using_rosmar = False | ||
|
Comment on lines
+1626
to
+1631
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make this an formatted string genuinely. |
||
|
|
||
| def create_collection_access_dict(self, input: dict[str, list[str]]) -> dict: | ||
| """ | ||
|
|
@@ -1846,9 +1860,8 @@ async def start(self, config_name: str = "bootstrap") -> None: | |
| # Check if SGW is already running by probing the public endpoint (4984) | ||
| try: | ||
| # Use a short timeout to distinguish "not running" from "slow" | ||
| scheme = "https://" if self.secure else "http://" | ||
| async with self._create_session( | ||
| self.secure, scheme, self.hostname, 4984, None | ||
| self.secure, self.scheme, self.hostname, 4984, None | ||
| ) as session: | ||
| async with session.get("/", timeout=ClientTimeout(total=5)) as resp: | ||
| if resp.status == 200: | ||
|
|
@@ -1874,6 +1887,22 @@ async def start(self, config_name: str = "bootstrap") -> None: | |
| # Wait a bit for SGW to fully initialize | ||
| await asyncio.sleep(5) | ||
|
|
||
| @tenacity.retry( | ||
| wait=tenacity.wait_fixed(0.1), | ||
| # Sync Gateway polling time is 10s, so wait 60s for polling time + any additional work | ||
| stop=tenacity.stop_after_delay(60), | ||
| reraise=True, | ||
| retry=tenacity.retry_if_exception_type(AssertionError), | ||
| ) | ||
| async def wait_for_no_databases(self, bucket_name: str): | ||
| with self._tracer.start_as_current_span("get_all_dbs"): | ||
| resp = await self._send_request("get", "/_all_dbs?verbose=true") | ||
| assert isinstance(resp, list), resp | ||
| for db in resp: | ||
| assert db["bucket"] != bucket_name, ( | ||
| f"Database {db=} is still backed by bucket {bucket_name}" | ||
| ) | ||
|
|
||
| async def wait_for_db_gone_clusterwide( | ||
| self, | ||
| sync_gateways: list["SyncGateway"], | ||
|
|
@@ -1931,23 +1960,28 @@ async def wait_for_db_up( | |
| # Wait for the node to settle down after coming online | ||
| await asyncio.sleep(settle_online) | ||
|
|
||
| @contextlib.asynccontextmanager | ||
| @contextlib.asynccontextmanager | ||
| async def create_user_client( | ||
| self, | ||
| db_name: str, | ||
| username: str, | ||
| password: str, | ||
| channels: list[str], | ||
| ) -> "SyncGatewayUserClient": | ||
| ) -> AsyncIterator["SyncGatewayUserClient"]: | ||
| """ | ||
| Helper method to create a user with channel access and return a user-specific SG client. | ||
| Helper method to create a user with channel access and yield a user-specific SG client. | ||
|
|
||
| This is a convenience method for tests that need to verify user-level access control. | ||
| Use it with ``async with`` only. The yielded client is automatically closed when the | ||
| context exits and must not be used outside that block. | ||
|
|
||
| :param db_name: The database name | ||
| :param username: The username to create | ||
| :param password: The password for the user | ||
| :param channels: List of channels the user should have access to | ||
| :return: A SyncGatewayUserClient instance authenticated as the user (uses public port) | ||
| :return: An async context manager yielding a SyncGatewayUserClient authenticated as | ||
| the user (uses public port) | ||
| """ | ||
| # Clean up user if exists from previous run | ||
| await self.delete_user(db_name, username) | ||
|
|
@@ -1959,13 +1993,17 @@ async def create_user_client( | |
| ) | ||
|
|
||
| # Return user-specific SG client for public API access | ||
| return SyncGatewayUserClient( | ||
| client = SyncGatewayUserClient( | ||
| self.hostname, | ||
| username, | ||
| password, | ||
| port=self.__public_port, | ||
| secure=self.secure, | ||
| ) | ||
| try: | ||
| yield client | ||
| finally: | ||
| await client.close() | ||
|
torcolvin marked this conversation as resolved.
|
||
|
|
||
| async def start_isgr(self, db_name: str, payload: ISGRPayload) -> str: | ||
| """ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,5 +12,8 @@ | |
| "log_level": "debug", | ||
| "log_keys": ["*"] | ||
| } | ||
| }, | ||
| "unsupported": { | ||
| "rosmar_bucket_management": true | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.