Skip to content

Commit 97dfea7

Browse files
shubha-rajanfhinkelkurtisvg
authored
fix(cloud-sql): update samples to show TCP and Unix Socket connection methods (#1836)
* added separate TCP/Unix Socket methods in mysql sample * run proxy with TCP in build script * replace stubbed mysql test with e2e test * added TCP and unix socket instructions to README * added methods for TCP/unix socket connection in Postgres and updated tests * rename CONNECTION_NAME to INSTANCE_CONNECTION_NAME * update postgres README with TCP/Unix socket connection instructions * update createTable.js to work with TCP connections Co-authored-by: Kurtis Van Gent <[email protected]> Co-authored-by: F. Hinkelmann <[email protected]> Co-authored-by: Kurtis Van Gent <[email protected]>
1 parent a2b2745 commit 97dfea7

File tree

12 files changed

+295
-86
lines changed

12 files changed

+295
-86
lines changed

.kokoro/build.sh

+9-6
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ export DB_NAME="kokoro_ci"
3232
export DB_USER="kokoro_ci"
3333
export DB_PASS=$(cat $KOKORO_GFILE_DIR/secrets-sql-password.txt)
3434
if [[ $SQL_CLIENT == 'pg' ]]; then
35-
export CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-pg-connection-name.txt)
35+
export INSTANCE_CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-pg-connection-name.txt)
3636
elif [[ $SQL_CLIENT == 'sqlserver' ]]; then
37-
export CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-sqlserver-connection-name.txt)
37+
export INSTANCE_CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-sqlserver-connection-name.txt)
3838
elif [[ $SQL_CLIENT == 'mysql' ]]; then
39-
export CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-mysql-connection-name.txt)
39+
export INSTANCE_CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-mysql-connection-name.txt)
4040
fi
4141

4242

@@ -99,10 +99,13 @@ if [[ $SQL_CLIENT ]]; then
9999
wget --quiet https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy
100100
chmod +x cloud_sql_proxy
101101
if [[ $SQL_CLIENT == 'sqlserver' ]]; then
102-
./cloud_sql_proxy -instances="${CONNECTION_NAME}"=tcp:1433 &>> cloud_sql_proxy.log &
102+
./cloud_sql_proxy -instances="${INSTANCE_CONNECTION_NAME}"=tcp:1433 &>> cloud_sql_proxy.log &
103+
elif [[ $SQL_CLIENT == 'mysql' ]]; then
104+
export DB_HOST=127.0.0.1:3306
105+
./cloud_sql_proxy -instances="${INSTANCE_CONNECTION_NAME}"=tcp:3306 &>> cloud_sql_proxy.log &
103106
else
104-
mkdir /cloudsql; chmod 777 /cloudsql
105-
./cloud_sql_proxy -dir=/cloudsql -instances="${CONNECTION_NAME}" &>> cloud_sql_proxy.log &
107+
export DB_HOST=127.0.0.1:5432
108+
./cloud_sql_proxy -instances="${INSTANCE_CONNECTION_NAME}"=tcp:5432 &>> cloud_sql_proxy.log &
106109
fi
107110
fi
108111

cloud-sql/mysql/mysql/README.md

+67-6
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,76 @@ secure solution such as [Cloud KMS](https://cloud.google.com/kms/) to help keep
3434
To run this application locally, download and install the `cloud_sql_proxy` by
3535
[following the instructions](https://cloud.google.com/sql/docs/mysql/sql-proxy#install).
3636

37-
Once the proxy is ready, use the following command to start the proxy in the
38-
background:
37+
Instructions are provided below for using the proxy with a TCP connection or a Unix Domain Socket.
38+
On Linux or Mac OS you can use either option, but on Windows the proxy currently requires a TCP
39+
connection.
40+
41+
### Launch proxy with TCP
42+
43+
To run the sample locally with a TCP connection, set environment variables and launch the proxy as
44+
shown below.
45+
46+
#### Linux / Mac OS
47+
Use these terminal commands to initialize environment variables:
48+
```bash
49+
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
50+
export DB_HOST='127.0.0.1:3306'
51+
export DB_USER='<DB_USER_NAME>'
52+
export DB_PASS='<DB_PASSWORD>'
53+
export DB_NAME='<DB_NAME>'
54+
```
55+
56+
Then use this command to launch the proxy in the background:
57+
```bash
58+
./cloud_sql_proxy -instances=<project-id>:<region>:<instance-name>=tcp:3306 -credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
59+
```
60+
61+
#### Windows/PowerShell
62+
Use these PowerShell commands to initialize environment variables:
63+
```powershell
64+
$env:GOOGLE_APPLICATION_CREDENTIALS="<CREDENTIALS_JSON_FILE>"
65+
$env:DB_HOST="127.0.0.1:3306"
66+
$env:DB_USER="<DB_USER_NAME>"
67+
$env:DB_PASS="<DB_PASSWORD>"
68+
$env:DB_NAME="<DB_NAME>"
69+
```
70+
71+
Then use this command to launch the proxy in a separate PowerShell session:
72+
```powershell
73+
Start-Process -filepath "C:\<path to proxy exe>" -ArgumentList "-instances=<project-id>:<region>:<instance-name>=tcp:3306 -credential_file=<CREDENTIALS_JSON_FILE>"
74+
```
75+
76+
### Launch proxy with Unix Domain Socket
77+
NOTE: this option is currently only supported on Linux and Mac OS. Windows users should use the
78+
[Launch proxy with TCP](#launch-proxy-with-tcp) option.
79+
80+
To use a Unix socket, you'll need to create a directory and give write access to the user running
81+
the proxy. For example:
82+
```bash
83+
sudo mkdir /path/to/the/new/directory
84+
sudo chown -R $USER /path/to/the/new/directory
85+
```
86+
You'll also need to initialize an environment variable containing the directory you just created:
87+
```bash
88+
export DB_SOCKET_PATH=/path/to/the/new/directory
89+
```
90+
91+
Use these terminal commands to initialize other environment variables as well:
92+
```bash
93+
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
94+
export INSTANCE_CONNECTION_NAME='<MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>'
95+
export DB_USER='<DB_USER_NAME>'
96+
export DB_PASS='<DB_PASSWORD>'
97+
export DB_NAME='<DB_NAME>'
98+
```
99+
100+
Then use this command to launch the proxy in the background:
101+
39102
```bash
40-
./cloud_sql_proxy -dir=/cloudsql --instances=$CLOUD_SQL_CONNECTION_NAME --credential_file=$GOOGLE_APPLICATION_CREDENTIALS
103+
./cloud_sql_proxy -dir=$DB_SOCKET_PATH --instances=$INSTANCE_CONNECTION_NAME --credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
41104
```
42-
Note: Make sure to run the command under a user with write access in the
43-
`/cloudsql` directory. This proxy will use this folder to create a unix socket
44-
the application will use to connect to Cloud SQL.
45105

106+
### Testing the application
46107
Next, setup install the requirements with `npm`:
47108
```bash
48109
npm install

cloud-sql/mysql/mysql/app.flexible.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ env_variables:
2121
DB_PASS: MY_DB_PASSWORD
2222
DB_NAME: MY_DATABASE
2323
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
24-
CLOUD_SQL_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
24+
INSTANCE_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
2525

2626
beta_settings:
2727
# The connection name of your instance, available by using

cloud-sql/mysql/mysql/app.standard.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ env_variables:
2020
DB_PASS: MY_DB_PASSWORD
2121
DB_NAME: MY_DATABASE
2222
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
23-
CLOUD_SQL_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
23+
INSTANCE_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>

cloud-sql/mysql/mysql/server.js

+38-11
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,43 @@ const logger = winston.createLogger({
4141
transports: [new winston.transports.Console(), loggingWinston],
4242
});
4343

44-
// [START cloud_sql_mysql_mysql_create]
45-
const createPool = async () => {
44+
// [START cloud_sql_mysql_mysql_create_tcp]
45+
const createTcpPool = async (config) => {
46+
// Extract host and port from socket address
47+
const dbSocketAddr = process.env.DB_HOST.split(":")
48+
49+
// Establish a connection to the database
4650
return await mysql.createPool({
4751
user: process.env.DB_USER, // e.g. 'my-db-user'
4852
password: process.env.DB_PASS, // e.g. 'my-db-password'
4953
database: process.env.DB_NAME, // e.g. 'my-database'
50-
// If connecting via unix domain socket, specify the path
51-
socketPath: `/cloudsql/${process.env.CLOUD_SQL_CONNECTION_NAME}`,
52-
// If connecting via TCP, enter the IP and port instead
53-
// host: 'localhost',
54-
// port: 3306,
54+
host: dbSocketAddr[0], // e.g. '127.0.0.1'
55+
port: dbSocketAddr[1], // e.g. '3306'
56+
// ... Specify additional properties here.
57+
...config
58+
});
59+
}
60+
// [END cloud_sql_mysql_mysql_create_tcp]
5561

56-
//[START_EXCLUDE]
62+
// [START cloud_sql_mysql_mysql_create_socket]
63+
const createUnixSocketPool = async (config) => {
64+
const dbSocketPath = process.env.DB_SOCKET_PATH || "/cloudsql"
65+
66+
// Establish a connection to the database
67+
return await mysql.createPool({
68+
user: process.env.DB_USER, // e.g. 'my-db-user'
69+
password: process.env.DB_PASS, // e.g. 'my-db-password'
70+
database: process.env.DB_NAME, // e.g. 'my-database'
71+
// If connecting via unix domain socket, specify the path
72+
socketPath: `${dbSocketPath}/${process.env.INSTANCE_CONNECTION_NAME}`,
73+
// Specify additional properties here.
74+
...config
75+
});
76+
}
77+
// [END cloud_sql_mysql_mysql_create_socket]
5778

79+
const createPool = async () => {
80+
const config = {
5881
// [START cloud_sql_mysql_mysql_limit]
5982
// 'connectionLimit' is the maximum number of connections the pool is allowed
6083
// to keep at once.
@@ -81,9 +104,13 @@ const createPool = async () => {
81104
// The mysql module automatically uses exponential delays between failed
82105
// connection attempts.
83106
// [END cloud_sql_mysql_mysql_backoff]
84-
85-
//[END_EXCLUDE]
86-
});
107+
}
108+
if (process.env.DB_HOST) {
109+
return await createTcpPool(config);
110+
} else {
111+
return await createUnixSocketPool(config);
112+
}
113+
87114
};
88115
// [END cloud_sql_mysql_mysql_create]
89116

cloud-sql/mysql/mysql/test/server.test.js

+24-14
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,35 @@
1414

1515
'use strict';
1616

17+
const path = require('path');
1718
const request = require('supertest');
18-
const sinon = require('sinon');
1919
const assert = require('assert');
2020

21-
// Stub out MySQL calls
22-
const stubMysql = sinon.stub(require('promise-mysql'));
23-
const poolStub = sinon.stub();
24-
const queryStub = sinon.stub();
25-
queryStub.withArgs(sinon.match('SELECT COUNT(vote_id)')).resolves([{count: 1}]);
26-
queryStub.withArgs(sinon.match('SELECT candidate, time_cast')).resolves([]);
27-
poolStub['query'] = queryStub;
28-
stubMysql.createPool.returns(poolStub);
21+
const SAMPLE_PATH = path.join(__dirname, '../server.js');
2922

30-
const server = require('../server.js');
23+
const server = require(SAMPLE_PATH);
3124

32-
it('check index page', async () => {
33-
const response = await request(server).get('/').timeout(3000);
25+
after(() => {
26+
server.close();
27+
});
28+
29+
it('should display the default page', async () => {
30+
await request(server)
31+
.get('/')
32+
.expect(200)
33+
.expect((response) => {
34+
assert.ok(response.text.includes('Tabs VS Spaces'));
35+
});
36+
});
37+
38+
it('should handle insert error', async () => {
39+
const expectedResult = 'Invalid team specified';
3440

35-
assert.strictEqual(response.status, 200);
41+
await request(server)
42+
.post('/')
43+
.expect(400)
44+
.expect((response) => {
45+
assert.ok(response.text.includes(expectedResult));
46+
});
3647
});
3748

38-
server.close();

cloud-sql/postgres/knex/README.md

+71-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ and password that you specify for the default 'postgres' user.
1414
3. Create a database for your application by following these [instructions](https://cloud.google.com/sql/docs/postgres/create-manage-databases). Note the database name.
1515

1616
4. Create a service account with the 'Cloud SQL Client' permissions by following these
17-
[instructions](https://cloud.google.com/sql/docs/mysql/connect-external-app#4_if_required_by_your_authentication_method_create_a_service_account).
17+
[instructions](https://cloud.google.com/sql/docs/postgres/connect-external-app#4_if_required_by_your_authentication_method_create_a_service_account).
1818
Download a JSON key to use to authenticate your connection.
1919

2020
5. Use the information noted in the previous steps:
@@ -35,14 +35,76 @@ Setting up the Cloud SQL database for the app requires setting up the app for lo
3535
1. To run this application locally, download and install the `cloud_sql_proxy` by
3636
[following the instructions](https://cloud.google.com/sql/docs/postgres/sql-proxy#install).
3737

38-
Once the proxy is ready, use the following command to start the proxy in the
39-
background:
40-
```bash
41-
./cloud_sql_proxy -dir=/cloudsql -instances=$CLOUD_SQL_CONNECTION_NAME -credential_file=$GOOGLE_APPLICATION_CREDENTIALS
42-
```
43-
Note: Make sure to run the command under a user with write access in the
44-
`/cloudsql` directory. This proxy will use this folder to create a unix socket
45-
the application will use to connect to Cloud SQL.
38+
Instructions are provided below for using the proxy with a TCP connection or a Unix Domain Socket.
39+
On Linux or Mac OS you can use either option, but on Windows the proxy currently requires a TCP
40+
connection.
41+
42+
### Launch proxy with TCP
43+
44+
To run the sample locally with a TCP connection, set environment variables and launch the proxy as
45+
shown below.
46+
47+
#### Linux / Mac OS
48+
Use these terminal commands to initialize environment variables:
49+
```bash
50+
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
51+
export DB_HOST='127.0.0.1:5432'
52+
export DB_USER='<DB_USER_NAME>'
53+
export DB_PASS='<DB_PASSWORD>'
54+
export DB_NAME='<DB_NAME>'
55+
```
56+
57+
Then use this command to launch the proxy in the background:
58+
```bash
59+
./cloud_sql_proxy -instances=<project-id>:<region>:<instance-name>=tcp:5432 -credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
60+
```
61+
62+
#### Windows/PowerShell
63+
Use these PowerShell commands to initialize environment variables:
64+
```powershell
65+
$env:GOOGLE_APPLICATION_CREDENTIALS="<CREDENTIALS_JSON_FILE>"
66+
$env:DB_HOST="127.0.0.1:5432"
67+
$env:DB_USER="<DB_USER_NAME>"
68+
$env:DB_PASS="<DB_PASSWORD>"
69+
$env:DB_NAME="<DB_NAME>"
70+
```
71+
72+
Then use this command to launch the proxy in a separate PowerShell session:
73+
```powershell
74+
Start-Process -filepath "C:\<path to proxy exe>" -ArgumentList "-instances=<project-id>:<region>:<instance-name>=tcp:5432 -credential_file=<CREDENTIALS_JSON_FILE>"
75+
```
76+
77+
### Launch proxy with Unix Domain Socket
78+
NOTE: this option is currently only supported on Linux and Mac OS. Windows users should use the
79+
[Launch proxy with TCP](#launch-proxy-with-tcp) option.
80+
81+
To use a Unix socket, you'll need to create a directory and give write access to the user running
82+
the proxy. For example:
83+
```bash
84+
sudo mkdir /path/to/the/new/directory
85+
sudo chown -R $USER /path/to/the/new/directory
86+
```
87+
You'll also need to initialize an environment variable containing the directory you just created:
88+
```bash
89+
export DB_SOCKET_PATH=/path/to/the/new/directory
90+
```
91+
92+
Use these terminal commands to initialize other environment variables as well:
93+
```bash
94+
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
95+
export INSTANCE_CONNECTION_NAME='<MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>'
96+
export DB_USER='<DB_USER_NAME>'
97+
export DB_PASS='<DB_PASSWORD>'
98+
export DB_NAME='<DB_NAME>'
99+
```
100+
101+
Then use this command to launch the proxy in the background:
102+
103+
```bash
104+
./cloud_sql_proxy -dir=$DB_SOCKET_PATH --instances=$INSTANCE_CONNECTION_NAME --credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
105+
```
106+
107+
### Testing the application
46108

47109
2. Next, install the Node.js packages necessary to run the app locally by running the following command:
48110

cloud-sql/postgres/knex/app.flexible.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ env_variables:
2121
DB_PASS: MY_DB_PASSWORD
2222
DB_NAME: MY_DATABASE
2323
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
24-
CLOUD_SQL_INSTANCE_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
24+
INSTANCE_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
2525

2626
beta_settings:
2727
# The connection name of your instance, available by using

cloud-sql/postgres/knex/app.standard.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ env_variables:
2020
DB_PASS: MY_DB_PASSWORD
2121
DB_NAME: MY_DATABASE
2222
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
23-
CLOUD_SQL_INSTANCE_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
23+
INSTANCE_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
2424

2525
beta_settings:
2626
# The connection name of your instance, available by using

cloud-sql/postgres/knex/createTable.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,17 @@
1717
const Knex = require('knex');
1818

1919
const createTable = async (config) => {
20+
const socketPath = process.env.DB_SOCKET_PATH || "/cloudsql";
21+
2022
// Connect to the database
21-
config.host = `/cloudsql/${config.connectionName}`;
23+
if (config.dbHost){
24+
const dbSocketAddr = config.dbHost.split(":");
25+
config.host = dbSocketAddr[0];
26+
config.port = dbSocketAddr[1];
27+
} else {
28+
config.host = `${socketPath}/${config.connectionName}`;
29+
}
30+
2231
const knex = Knex({client: 'pg', connection: config});
2332

2433
// Create the "votes" table
@@ -41,7 +50,7 @@ const createTable = async (config) => {
4150

4251
require('yargs')
4352
.command(
44-
'* <user> <password> <database> <connectionName>',
53+
'* <user> <password> <database> <connectionName> [dbHost]',
4554
'Create a "votes" table',
4655
{},
4756
createTable

0 commit comments

Comments
 (0)