Skip to content

Commit a64b9cb

Browse files
committed
add config and setup for spanner-pg-connector
TAG=agy CONV=2dff9ccf-debd-4647-8a3e-3814e4c6013a
1 parent ccb1b6a commit a64b9cb

11 files changed

Lines changed: 509 additions & 2 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,9 @@ samples/**/composer.lock
4141
samples/**/.phpunit.result.cache
4242
src/test/**/vendor
4343
src/test/**/composer.lock
44+
45+
# PG Starter local build outputs and runtime caches
46+
/custom-jre/
47+
/install_path.txt
48+
/pgadapter.jar
49+
/pgadapter.jsa

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@
398398
</plugins>
399399
</build>
400400
</profile>
401+
401402
<profile>
402403
<id>format</id>
403404
<activation>

src/main/java/com/google/cloud/spanner/pgadapter/Server.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public static void main(String[] args) {
109109
}
110110

111111
@VisibleForTesting
112-
public static void runCommand(
112+
public static int runCommand(
113113
ProxyServer proxyServer, @Nullable String database, String... command)
114114
throws IOException, InterruptedException {
115115
ProcessBuilder builder = new ProcessBuilder();
@@ -121,7 +121,7 @@ public static void runCommand(
121121
}
122122
builder.inheritIO();
123123
Process process = builder.start();
124-
process.waitFor();
124+
return process.waitFor();
125125
}
126126

127127
/**
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.cloud.spanner.pgadapter;
16+
17+
import com.google.cloud.spanner.pgadapter.logging.DefaultLogConfiguration;
18+
import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata;
19+
import com.google.cloud.spanner.pgadapter.metadata.OptionsMetadata.SslMode;
20+
import io.opentelemetry.api.OpenTelemetry;
21+
22+
/**
23+
* A simplified connector executable that sets up PGAdapter and runs a command (like psql) against
24+
* it. * Usage: spgc <command> [args...] Example: spgc psql -d
25+
* "projects/my-project/instances/my-inst/databases/my-db"
26+
*/
27+
public class SpannerPGConnector {
28+
29+
public static void main(String[] args) {
30+
// Scan for unsupported connection overrides
31+
for (String arg : args) {
32+
if (arg.equals("-h")
33+
|| arg.startsWith("--host")
34+
|| arg.equals("-p")
35+
|| arg.startsWith("--port")) {
36+
System.err.println("Error: Explicit host/port flags (-h/-p) are not supported.");
37+
System.err.println("spanner-pg-connector manages the connection routing dynamically.");
38+
System.exit(1);
39+
}
40+
}
41+
42+
if (args.length == 0) {
43+
System.out.println("Usage: spgc <command> [args...]");
44+
System.out.println("Example: spgc psql -d \"projects/p/instances/i/databases/d\"");
45+
System.exit(1);
46+
}
47+
48+
try {
49+
DefaultLogConfiguration.disableLogging();
50+
OptionsMetadata.Builder builder =
51+
OptionsMetadata.newBuilder()
52+
.setPort(0)
53+
.setSslMode(SslMode.Disable)
54+
.disableUnixDomainSockets();
55+
56+
String projectId = System.getenv("GOOGLE_CLOUD_PROJECT");
57+
if (projectId != null) {
58+
builder.setProject(projectId);
59+
}
60+
String instanceId = System.getenv("SPANNER_INSTANCE");
61+
if (instanceId != null) {
62+
builder.setInstance(instanceId);
63+
}
64+
65+
if (System.getenv("SPANNER_EMULATOR_HOST") != null) {
66+
builder.autoConfigureEmulator();
67+
}
68+
69+
OptionsMetadata options = builder.build();
70+
71+
OpenTelemetry openTelemetry = Server.setupOpenTelemetry(options);
72+
ProxyServer proxyServer = new ProxyServer(options, openTelemetry);
73+
proxyServer.startServer();
74+
75+
int exitCode = 0;
76+
try {
77+
exitCode = Server.runCommand(proxyServer, null, args);
78+
} finally {
79+
proxyServer.stopServer();
80+
}
81+
System.exit(exitCode);
82+
83+
} catch (Exception e) {
84+
e.printStackTrace();
85+
System.exit(1);
86+
}
87+
}
88+
}

starter/cloudbuild.yaml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Google Cloud Build - Spanner PG Starter Release Pipeline
2+
# Automates compiling the starter code, JRE stripping, packaging, and deploying to GCS.
3+
4+
substitutions:
5+
_VERSION: '0.0.0'
6+
_AR_LOCATION: 'us-central1'
7+
_AR_REPOSITORY: 'spanner-pg-connector'
8+
9+
steps:
10+
# Step 1: Compile and package using assembly profile (JDK 25)
11+
- name: 'mirror.gcr.io/library/maven:3.9-eclipse-temurin-25'
12+
id: 'build-assembly'
13+
entrypoint: 'mvn'
14+
args: ['-Passembly', '-DskipTests', '-Dfmt.skip', 'clean', 'package']
15+
16+
# Step 2: Assemble the minimal platform-specific JREs via jlink cross-linking (JDK 25)
17+
- name: 'mirror.gcr.io/library/eclipse-temurin:25'
18+
id: 'link-jre'
19+
entrypoint: 'sh'
20+
args:
21+
- '-c'
22+
- |
23+
set -e
24+
apt-get update && apt-get install -y unzip curl
25+
26+
JDK_VER="jdk-25.0.3+9"
27+
28+
mkdir -p /tmp/downloads /tmp/jmods
29+
30+
# Download target JMOD packages directly from Adoptium APIs
31+
curl -fsSL -o /tmp/downloads/linux-x64.tar.gz "https://api.adoptium.net/v3/binary/latest/25/ga/linux/x64/jmods/hotspot/normal/eclipse"
32+
curl -fsSL -o /tmp/downloads/mac-aarch64.tar.gz "https://api.adoptium.net/v3/binary/latest/25/ga/mac/aarch64/jmods/hotspot/normal/eclipse"
33+
curl -fsSL -o /tmp/downloads/mac-x64.tar.gz "https://api.adoptium.net/v3/binary/latest/25/ga/mac/x64/jmods/hotspot/normal/eclipse"
34+
curl -fsSL -o /tmp/downloads/windows-x64.zip "https://api.adoptium.net/v3/binary/latest/25/ga/windows/x64/jmods/hotspot/normal/eclipse"
35+
36+
# Extract target JMOD directories
37+
tar -xzf /tmp/downloads/linux-x64.tar.gz -C /tmp/
38+
mv "/tmp/$${JDK_VER}-jmods" /tmp/jmods/linux-x64
39+
40+
tar -xzf /tmp/downloads/mac-aarch64.tar.gz -C /tmp/
41+
mv "/tmp/$${JDK_VER}-jmods" /tmp/jmods/mac-aarch64
42+
43+
tar -xzf /tmp/downloads/mac-x64.tar.gz -C /tmp/
44+
mv "/tmp/$${JDK_VER}-jmods" /tmp/jmods/mac-x64
45+
46+
unzip -q /tmp/downloads/windows-x64.zip -d /tmp/
47+
mv "/tmp/$${JDK_VER}-jmods" /tmp/jmods/windows-x64
48+
49+
# Strip JRE to the bare minimum required for PGAdapter and Spanner Clients
50+
# Excludes unused desktop/GUI, RMI, scripting, and compiler modules
51+
MODULES="java.base,java.management,java.naming,java.security.jgss,java.sql,jdk.unsupported,jdk.crypto.cryptoki,jdk.crypto.ec"
52+
53+
# Execute cross-linking using the host JVM's native jlink command
54+
jlink --module-path /tmp/jmods/linux-x64 --add-modules $${MODULES} --strip-debug --no-man-pages --no-header-files --compress=zip-9 --output target/custom-jres/linux-x64
55+
jlink --module-path /tmp/jmods/mac-aarch64 --add-modules $${MODULES} --strip-debug --no-man-pages --no-header-files --compress=zip-9 --output target/custom-jres/mac-aarch64
56+
jlink --module-path /tmp/jmods/mac-x64 --add-modules $${MODULES} --strip-debug --no-man-pages --no-header-files --compress=zip-9 --output target/custom-jres/mac-x64
57+
jlink --module-path /tmp/jmods/windows-x64 --add-modules $${MODULES} --strip-debug --no-man-pages --no-header-files --compress=zip-9 --output target/custom-jres/windows-x64
58+
59+
# Step 3: Bundle assets into target platform packages
60+
- name: 'mirror.gcr.io/library/alpine:3.20'
61+
id: 'package-bundle'
62+
entrypoint: 'sh'
63+
args:
64+
- '-c'
65+
- |
66+
apk add --no-cache zip
67+
68+
mkdir -p target/release/versioned-package target/release/latest-assets
69+
70+
# Linux x64
71+
mkdir -p target/dist/linux-x64
72+
cp target/pgadapter/pgadapter.jar target/dist/linux-x64/
73+
cp -r target/pgadapter/lib target/dist/linux-x64/
74+
cp -r target/custom-jres/linux-x64 target/dist/linux-x64/custom-jre
75+
cp starter/spanner-pg-connector target/dist/linux-x64/
76+
cp starter/spgc target/dist/linux-x64/
77+
tar -czf target/release/versioned-package/spanner-pg-connector-linux-x64.tar.gz -C target/dist/linux-x64 spanner-pg-connector spgc pgadapter.jar lib/ custom-jre/
78+
79+
# macOS aarch64
80+
mkdir -p target/dist/mac-aarch64
81+
cp target/pgadapter/pgadapter.jar target/dist/mac-aarch64/
82+
cp -r target/pgadapter/lib target/dist/mac-aarch64/
83+
cp -r target/custom-jres/mac-aarch64 target/dist/mac-aarch64/custom-jre
84+
cp starter/spanner-pg-connector target/dist/mac-aarch64/
85+
cp starter/spgc target/dist/mac-aarch64/
86+
tar -czf target/release/versioned-package/spanner-pg-connector-mac-aarch64.tar.gz -C target/dist/mac-aarch64 spanner-pg-connector spgc pgadapter.jar lib/ custom-jre/
87+
88+
# macOS x64
89+
mkdir -p target/dist/mac-x64
90+
cp target/pgadapter/pgadapter.jar target/dist/mac-x64/
91+
cp -r target/pgadapter/lib target/dist/mac-x64/
92+
cp -r target/custom-jres/mac-x64 target/dist/mac-x64/custom-jre
93+
cp starter/spanner-pg-connector target/dist/mac-x64/
94+
cp starter/spgc target/dist/mac-x64/
95+
tar -czf target/release/versioned-package/spanner-pg-connector-mac-x64.tar.gz -C target/dist/mac-x64 spanner-pg-connector spgc pgadapter.jar lib/ custom-jre/
96+
97+
# Windows x64
98+
mkdir -p target/dist/windows-x64
99+
cp target/pgadapter/pgadapter.jar target/dist/windows-x64/
100+
cp -r target/pgadapter/lib target/dist/windows-x64/
101+
cp -r target/custom-jres/windows-x64 target/dist/windows-x64/custom-jre
102+
cp starter/spanner-pg-connector.cmd target/dist/windows-x64/
103+
cp starter/spgc.cmd target/dist/windows-x64/
104+
cd target/dist/windows-x64
105+
zip -q -r ../../release/versioned-package/spanner-pg-connector-windows-x64.zip spanner-pg-connector.cmd spgc.cmd pgadapter.jar lib/ custom-jre/
106+
cd ../../..
107+
108+
109+
options:
110+
requestedVerifyOption: VERIFIED
111+
logging: CLOUD_LOGGING_ONLY
112+
113+
artifacts:
114+
generic_artifacts:
115+
- registry_path: 'projects/${PROJECT_ID}/locations/${_AR_LOCATION}/repositories/${_AR_REPOSITORY}/packages/spanner-pg-connector/versions/${_VERSION}'
116+
folder: 'target/release/versioned-package'
117+

starter/install.ps1

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# install.ps1
2+
# PowerShell script to install Spanner PG Connector on Windows
3+
4+
$Project = "span-cloud-testing"
5+
$Location = "us-central1"
6+
$Repository = "spanner-pg-connector"
7+
$Package = "spanner-pg-connector"
8+
$Version = $env:VERSION
9+
$InstallDir = Join-Path $HOME ".spanner-pg-connector"
10+
11+
# Resolve the latest version if not explicitly specified
12+
if ([string]::IsNullOrEmpty($Version)) {
13+
Write-Host "Querying Artifact Registry for the latest release version..."
14+
if ($Token) {
15+
$Uri = "https://artifactregistry.googleapis.com/v1/projects/$Project/locations/$Location/repositories/$Repository/packages/spanner-pg-connector/versions"
16+
$Result = Invoke-RestMethod -Uri $Uri -Headers @{"Authorization" = "Bearer $Token"}
17+
$Version = $Result.versions.name | ForEach-Object { Split-Path $_ -Leaf } | Where-Object { $_ -match '^\d+\.\d+\.\d+$' } | Sort-Object {[version]$_} | Select-Object -Last 1
18+
} else {
19+
$VersionsList = gcloud artifacts versions list --package=spanner-pg-connector --project=$Project --location=$Location --repository=$Repository --format="value(name)"
20+
$Version = $VersionsList | Where-Object { $_ -match '^\d+\.\d+\.\d+$' } | Sort-Object {[version]$_} | Select-Object -Last 1
21+
}
22+
23+
if ([string]::IsNullOrEmpty($Version)) {
24+
Write-Error "Error: Could not resolve the latest version from Artifact Registry."
25+
Exit 1
26+
}
27+
Write-Host "Resolved latest version: $Version" -ForegroundColor Cyan
28+
}
29+
30+
Write-Host "Installing Spanner PG Connector..." -ForegroundColor Green
31+
32+
# 1. Clean previous installation
33+
if (Test-Path $InstallDir) {
34+
Remove-Item -Recurse -Force $InstallDir
35+
}
36+
New-Item -ItemType Directory -Path $InstallDir | Out-Null
37+
38+
# 2. Authenticate and download package
39+
Write-Host "Downloading package from Artifact Registry..."
40+
$Token = gcloud auth application-default print-access-token
41+
$Headers = @{
42+
"Authorization" = "Bearer $Token"
43+
}
44+
$DownloadUrl = "https://artifactregistry.googleapis.com/v1/projects/$Project/locations/$Location/repositories/$Repository/files/spanner-pg-connector:$Version:spanner-pg-connector-windows-x64.zip:download?alt=media"
45+
46+
$ZipPath = Join-Path $env:TEMP "spanner-pg-connector-windows-x64.zip"
47+
Invoke-RestMethod -Uri $DownloadUrl -Headers $Headers -OutFile $ZipPath
48+
49+
# 3. Extract zip
50+
Write-Host "Extracting files to $InstallDir..."
51+
Expand-Archive -Path $ZipPath -DestinationPath $InstallDir -Force
52+
Remove-Item $ZipPath
53+
54+
# 4. Add to user PATH environment variable if not already present
55+
Write-Host "Configuring environment PATH..."
56+
$UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
57+
if ($UserPath -split ";" -notcontains $InstallDir) {
58+
[Environment]::SetEnvironmentVariable("Path", "$UserPath;$InstallDir", "User")
59+
$env:PATH = "$env:PATH;$InstallDir"
60+
Write-Host "Added $InstallDir to user PATH." -ForegroundColor Cyan
61+
}
62+
Write-Host "-----------------------------------------------------" -ForegroundColor Green
63+
Write-Host "Installation complete!" -ForegroundColor Green
64+
Write-Host "Please restart your PowerShell session or environment to begin using: spgc psql"
65+
Write-Host "-----------------------------------------------------"

0 commit comments

Comments
 (0)