Skip to content

Commit a000e1e

Browse files
committed
Add sgr cloud ephemeral-tunnel command
1 parent 0340e53 commit a000e1e

4 files changed

Lines changed: 107 additions & 36 deletions

File tree

splitgraph/cloud/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
INGESTION_JOB_STATUS,
6262
JOB_LOGS,
6363
PROFILE_UPSERT,
64+
PROVISION_EPHEMERAL_TUNNEL,
6465
PROVISION_REPOSITORY_TUNNEL,
6566
REPO_CONDITIONS,
6667
REPO_PARAMS,
@@ -1121,3 +1122,21 @@ def provision_repository_tunnel(self, namespace: str, repository: str) -> Tuple[
11211122
response.json()["data"]["provisionRepositoryTunnel"]["tunnelConnectHost"],
11221123
response.json()["data"]["provisionRepositoryTunnel"]["tunnelConnectPort"],
11231124
)
1125+
1126+
def provision_ephemeral_tunnel(self) -> Tuple[str, str, int, str, int]:
1127+
response = self._gql(
1128+
{
1129+
"query": PROVISION_EPHEMERAL_TUNNEL,
1130+
"operationName": "ProvisionEphemeralTunnel",
1131+
"variables": {},
1132+
},
1133+
handle_errors=True,
1134+
anonymous_ok=False,
1135+
)
1136+
return (
1137+
response.json()["data"]["provisionEphemeralTunnel"]["secretToken"],
1138+
response.json()["data"]["provisionEphemeralTunnel"]["tunnelConnectHost"],
1139+
response.json()["data"]["provisionEphemeralTunnel"]["tunnelConnectPort"],
1140+
response.json()["data"]["provisionEphemeralTunnel"]["privateAddressHost"],
1141+
response.json()["data"]["provisionEphemeralTunnel"]["privateAddressPort"],
1142+
)

splitgraph/cloud/queries.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,17 @@
289289
}
290290
"""
291291

292+
PROVISION_EPHEMERAL_TUNNEL = """mutation ProvisionEphemeralTunnel {
293+
provisionEphemeralTunnel {
294+
secretToken,
295+
tunnelConnectHost,
296+
tunnelConnectPort,
297+
privateAddressHost,
298+
privateAddressPort
299+
}
300+
}
301+
"""
302+
292303
EXPORT_JOB_STATUS = """query ExportJobStatus($taskId: UUID!) {
293304
exportJobStatus(taskId: $taskId) {
294305
taskId

splitgraph/cloud/tunnel_client.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
from os import path
55
from typing import IO, Any, Dict, Optional, cast
66

7+
from splitgraph.config import CONFIG
8+
from splitgraph.config.config import get_singleton
79
from splitgraph.core.repository import Repository
810

911
RATHOLE_CLIENT_CONFIG_FILENAME = "rathole-client.toml"
1012

1113
RATHOLE_CLIENT_CONFIG_TEMPLATE = """
1214
[client]
13-
remote_addr = "{tunnel_server_management_address}"
15+
remote_addr = "{tunnel_connect_address}"
1416
1517
[client.transport]
1618
type = "tls"
@@ -19,55 +21,56 @@
1921
{trusted_root_line}
2022
hostname = "{tls_hostname}"
2123
22-
[client.services."{namespace}/{repository}"]
24+
[client.services."{section_id}"]
2325
local_addr = "{local_address}"
24-
# token is provisioner JWT token
25-
token = "{provisioning_token}"
26+
token = "{secret_token}"
2627
2728
"""
2829

2930

3031
def get_rathole_client_config(
31-
tunnel_server_management_address: str,
32+
tunnel_connect_address: str,
3233
tls_hostname: str,
3334
local_address: str,
34-
provisioning_token: str,
35-
namespace: str,
36-
repository: str,
35+
secret_token: str,
36+
section_id: str,
3737
trusted_root: Optional[str],
3838
) -> str:
3939
trusted_root_line = f'trusted_root = "{trusted_root}"' if trusted_root else ""
4040
return RATHOLE_CLIENT_CONFIG_TEMPLATE.format(
41-
tunnel_server_management_address=tunnel_server_management_address,
41+
tunnel_connect_address=tunnel_connect_address,
4242
tls_hostname=tls_hostname,
4343
local_address=local_address,
44-
provisioning_token=provisioning_token,
45-
namespace=namespace,
46-
repository=repository,
44+
secret_token=secret_token,
45+
section_id=section_id,
4746
trusted_root_line=trusted_root_line,
4847
)
4948

5049

50+
def get_rathole_client_binary_path():
51+
config_dir = os.path.dirname(get_singleton(CONFIG, "SG_CONFIG_FILE"))
52+
return os.path.join(config_dir, "rathole")
53+
54+
5155
def write_rathole_client_config(
52-
provisioning_token: str,
53-
tunnel_server_management_host: str,
54-
tunnel_server_management_port: int,
56+
section_id: str,
57+
secret_token: str,
58+
tunnel_connect_host: str,
59+
tunnel_connect_port: int,
60+
local_address: str,
5561
tls_hostname: Optional[str],
56-
repository: Repository,
57-
params: Dict[str, Any],
58-
config_dir: str,
5962
) -> str:
6063
# in production, this will be None, but for dev instances, we need to
6164
# specify rootCA.pem
65+
config_dir = os.path.dirname(get_singleton(CONFIG, "SG_CONFIG_FILE"))
6266
trusted_root = os.environ.get("REQUESTS_CA_BUNDLE") or os.environ.get("SSL_CERT_FILE")
6367
rathole_client_config = get_rathole_client_config(
6468
# TODO: replace these stub values with response of provisioning call
65-
tunnel_server_management_address=f"{tunnel_server_management_host}:{tunnel_server_management_port}",
66-
tls_hostname=tls_hostname or tunnel_server_management_host,
67-
local_address=f"{params['host']}:{params['port']}",
68-
provisioning_token=provisioning_token,
69-
namespace=repository.namespace,
70-
repository=repository.repository,
69+
tunnel_connect_address=f"{tunnel_connect_host}:{tunnel_connect_port}",
70+
tls_hostname=tls_hostname or tunnel_connect_host,
71+
local_address=local_address,
72+
secret_token=secret_token,
73+
section_id=section_id,
7174
trusted_root=trusted_root,
7275
)
7376
config_filename = path.join(config_dir, RATHOLE_CLIENT_CONFIG_FILENAME)

splitgraph/commandline/cloud.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from copy import copy
1010
from glob import glob
1111
from pathlib import Path
12-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, cast
12+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, OrderedDict, Tuple, cast
1313
from urllib.parse import quote, urlparse
1414

1515
import click
@@ -18,6 +18,7 @@
1818
from splitgraph.cloud.models import AddExternalRepositoryRequest, IntrospectionMode
1919
from splitgraph.cloud.project.models import Metadata, SplitgraphYAML
2020
from splitgraph.cloud.tunnel_client import (
21+
get_rathole_client_binary_path,
2122
launch_rathole_client,
2223
write_rathole_client_config,
2324
)
@@ -1239,29 +1240,65 @@ def tunnel_c(remote: str, repositories_file: List[Path], repository: "CoreReposi
12391240
f"Repository {repository.namespace}/{repository.repository} is not tunneled"
12401241
)
12411242

1242-
config_dir = os.path.dirname(get_singleton(CONFIG, "SG_CONFIG_FILE"))
12431243
# TODO: Get current version of rathole client for architecture.
12441244
# user must manually download rathole for now
1245-
rathole_client_binary_path = os.path.join(config_dir, "rathole")
12461245

12471246
from splitgraph.cloud import GQLAPIClient
12481247

12491248
client = GQLAPIClient(remote)
1250-
(secretToken, tunnelConnectHost, tunnelConnectPort) = client.provision_repository_tunnel(
1249+
(secret_token, tunnel_connect_host, tunnel_connect_port) = client.provision_repository_tunnel(
12511250
repository.namespace, repository.repository
12521251
)
12531252

1253+
section_id = f"{repository.namespace}/{repository.repository}"
1254+
local_address = f"{external.params['host']}:{external.params['port']}"
1255+
1256+
rathole_client_config_path = write_rathole_client_config(
1257+
section_id,
1258+
secret_token,
1259+
tunnel_connect_host,
1260+
tunnel_connect_port,
1261+
local_address,
1262+
tunnel_connect_host,
1263+
)
1264+
1265+
print("launching rathole client")
1266+
launch_rathole_client(get_rathole_client_binary_path(), rathole_client_config_path)
1267+
1268+
1269+
@click.command("ephemeral-tunnel")
1270+
@click.option("--remote", default="data.splitgraph.com", help="Name of the remote registry to use.")
1271+
@click.argument("host", type=str)
1272+
@click.argument("port", type=int)
1273+
def ephemeral_tunnel_c(remote: str, host: str, port: int):
1274+
"""
1275+
Start ephemeral tunnel.
1276+
"""
1277+
1278+
from splitgraph.cloud import GQLAPIClient
1279+
1280+
client = GQLAPIClient(remote)
1281+
(
1282+
secret_token,
1283+
tunnel_connect_host,
1284+
tunnel_connect_port,
1285+
private_address_host,
1286+
private_address_port,
1287+
) = client.provision_ephemeral_tunnel()
1288+
12541289
rathole_client_config_path = write_rathole_client_config(
1255-
secretToken,
1256-
tunnelConnectHost,
1257-
tunnelConnectPort,
1258-
tunnelConnectHost,
1259-
repository,
1260-
external.params,
1261-
config_dir,
1290+
private_address_host,
1291+
secret_token,
1292+
tunnel_connect_host,
1293+
tunnel_connect_port,
1294+
f"{host}:{port}",
1295+
tunnel_connect_host,
1296+
)
1297+
print(
1298+
f"To connect to {host}:{port} from Splitgraph, use the following connection parameters:\nHost: {private_address_host}\nPort: {private_address_port}"
12621299
)
12631300
print("launching rathole client")
1264-
launch_rathole_client(rathole_client_binary_path, rathole_client_config_path)
1301+
launch_rathole_client(get_rathole_client_binary_path(), rathole_client_config_path)
12651302

12661303

12671304
@click.group("cloud")
@@ -1292,3 +1329,4 @@ def cloud_c():
12921329
cloud_c.add_command(validate_c)
12931330
cloud_c.add_command(seed_c)
12941331
cloud_c.add_command(tunnel_c)
1332+
cloud_c.add_command(ephemeral_tunnel_c)

0 commit comments

Comments
 (0)