Skip to content

Commit

Permalink
fix(cloud-sql): update samples to show TCP and Unix Socket connection…
Browse files Browse the repository at this point in the history
… 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 <31518063+kurtisvg@users.noreply.github.com>

Co-authored-by: F. Hinkelmann <franziska.hinkelmann@gmail.com>
Co-authored-by: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 2, 2020
1 parent a2b2745 commit 97dfea7
Show file tree
Hide file tree
Showing 12 changed files with 295 additions and 86 deletions.
15 changes: 9 additions & 6 deletions .kokoro/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ export DB_NAME="kokoro_ci"
export DB_USER="kokoro_ci"
export DB_PASS=$(cat $KOKORO_GFILE_DIR/secrets-sql-password.txt)
if [[ $SQL_CLIENT == 'pg' ]]; then
export CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-pg-connection-name.txt)
export INSTANCE_CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-pg-connection-name.txt)
elif [[ $SQL_CLIENT == 'sqlserver' ]]; then
export CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-sqlserver-connection-name.txt)
export INSTANCE_CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-sqlserver-connection-name.txt)
elif [[ $SQL_CLIENT == 'mysql' ]]; then
export CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-mysql-connection-name.txt)
export INSTANCE_CONNECTION_NAME=$(cat $KOKORO_GFILE_DIR/secrets-mysql-connection-name.txt)
fi


Expand Down Expand Up @@ -99,10 +99,13 @@ if [[ $SQL_CLIENT ]]; then
wget --quiet https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy
chmod +x cloud_sql_proxy
if [[ $SQL_CLIENT == 'sqlserver' ]]; then
./cloud_sql_proxy -instances="${CONNECTION_NAME}"=tcp:1433 &>> cloud_sql_proxy.log &
./cloud_sql_proxy -instances="${INSTANCE_CONNECTION_NAME}"=tcp:1433 &>> cloud_sql_proxy.log &
elif [[ $SQL_CLIENT == 'mysql' ]]; then
export DB_HOST=127.0.0.1:3306
./cloud_sql_proxy -instances="${INSTANCE_CONNECTION_NAME}"=tcp:3306 &>> cloud_sql_proxy.log &
else
mkdir /cloudsql; chmod 777 /cloudsql
./cloud_sql_proxy -dir=/cloudsql -instances="${CONNECTION_NAME}" &>> cloud_sql_proxy.log &
export DB_HOST=127.0.0.1:5432
./cloud_sql_proxy -instances="${INSTANCE_CONNECTION_NAME}"=tcp:5432 &>> cloud_sql_proxy.log &
fi
fi

Expand Down
73 changes: 67 additions & 6 deletions cloud-sql/mysql/mysql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,76 @@ secure solution such as [Cloud KMS](https://cloud.google.com/kms/) to help keep
To run this application locally, download and install the `cloud_sql_proxy` by
[following the instructions](https://cloud.google.com/sql/docs/mysql/sql-proxy#install).

Once the proxy is ready, use the following command to start the proxy in the
background:
Instructions are provided below for using the proxy with a TCP connection or a Unix Domain Socket.
On Linux or Mac OS you can use either option, but on Windows the proxy currently requires a TCP
connection.

### Launch proxy with TCP

To run the sample locally with a TCP connection, set environment variables and launch the proxy as
shown below.

#### Linux / Mac OS
Use these terminal commands to initialize environment variables:
```bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
export DB_HOST='127.0.0.1:3306'
export DB_USER='<DB_USER_NAME>'
export DB_PASS='<DB_PASSWORD>'
export DB_NAME='<DB_NAME>'
```

Then use this command to launch the proxy in the background:
```bash
./cloud_sql_proxy -instances=<project-id>:<region>:<instance-name>=tcp:3306 -credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
```

#### Windows/PowerShell
Use these PowerShell commands to initialize environment variables:
```powershell
$env:GOOGLE_APPLICATION_CREDENTIALS="<CREDENTIALS_JSON_FILE>"
$env:DB_HOST="127.0.0.1:3306"
$env:DB_USER="<DB_USER_NAME>"
$env:DB_PASS="<DB_PASSWORD>"
$env:DB_NAME="<DB_NAME>"
```

Then use this command to launch the proxy in a separate PowerShell session:
```powershell
Start-Process -filepath "C:\<path to proxy exe>" -ArgumentList "-instances=<project-id>:<region>:<instance-name>=tcp:3306 -credential_file=<CREDENTIALS_JSON_FILE>"
```

### Launch proxy with Unix Domain Socket
NOTE: this option is currently only supported on Linux and Mac OS. Windows users should use the
[Launch proxy with TCP](#launch-proxy-with-tcp) option.

To use a Unix socket, you'll need to create a directory and give write access to the user running
the proxy. For example:
```bash
sudo mkdir /path/to/the/new/directory
sudo chown -R $USER /path/to/the/new/directory
```
You'll also need to initialize an environment variable containing the directory you just created:
```bash
export DB_SOCKET_PATH=/path/to/the/new/directory
```

Use these terminal commands to initialize other environment variables as well:
```bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
export INSTANCE_CONNECTION_NAME='<MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>'
export DB_USER='<DB_USER_NAME>'
export DB_PASS='<DB_PASSWORD>'
export DB_NAME='<DB_NAME>'
```

Then use this command to launch the proxy in the background:

```bash
./cloud_sql_proxy -dir=/cloudsql --instances=$CLOUD_SQL_CONNECTION_NAME --credential_file=$GOOGLE_APPLICATION_CREDENTIALS
./cloud_sql_proxy -dir=$DB_SOCKET_PATH --instances=$INSTANCE_CONNECTION_NAME --credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
```
Note: Make sure to run the command under a user with write access in the
`/cloudsql` directory. This proxy will use this folder to create a unix socket
the application will use to connect to Cloud SQL.

### Testing the application
Next, setup install the requirements with `npm`:
```bash
npm install
Expand Down
2 changes: 1 addition & 1 deletion cloud-sql/mysql/mysql/app.flexible.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ env_variables:
DB_PASS: MY_DB_PASSWORD
DB_NAME: MY_DATABASE
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
CLOUD_SQL_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
INSTANCE_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>

beta_settings:
# The connection name of your instance, available by using
Expand Down
2 changes: 1 addition & 1 deletion cloud-sql/mysql/mysql/app.standard.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ env_variables:
DB_PASS: MY_DB_PASSWORD
DB_NAME: MY_DATABASE
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
CLOUD_SQL_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
INSTANCE_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
49 changes: 38 additions & 11 deletions cloud-sql/mysql/mysql/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,43 @@ const logger = winston.createLogger({
transports: [new winston.transports.Console(), loggingWinston],
});

// [START cloud_sql_mysql_mysql_create]
const createPool = async () => {
// [START cloud_sql_mysql_mysql_create_tcp]
const createTcpPool = async (config) => {
// Extract host and port from socket address
const dbSocketAddr = process.env.DB_HOST.split(":")

// Establish a connection to the database
return await mysql.createPool({
user: process.env.DB_USER, // e.g. 'my-db-user'
password: process.env.DB_PASS, // e.g. 'my-db-password'
database: process.env.DB_NAME, // e.g. 'my-database'
// If connecting via unix domain socket, specify the path
socketPath: `/cloudsql/${process.env.CLOUD_SQL_CONNECTION_NAME}`,
// If connecting via TCP, enter the IP and port instead
// host: 'localhost',
// port: 3306,
host: dbSocketAddr[0], // e.g. '127.0.0.1'
port: dbSocketAddr[1], // e.g. '3306'
// ... Specify additional properties here.
...config
});
}
// [END cloud_sql_mysql_mysql_create_tcp]

//[START_EXCLUDE]
// [START cloud_sql_mysql_mysql_create_socket]
const createUnixSocketPool = async (config) => {
const dbSocketPath = process.env.DB_SOCKET_PATH || "/cloudsql"

// Establish a connection to the database
return await mysql.createPool({
user: process.env.DB_USER, // e.g. 'my-db-user'
password: process.env.DB_PASS, // e.g. 'my-db-password'
database: process.env.DB_NAME, // e.g. 'my-database'
// If connecting via unix domain socket, specify the path
socketPath: `${dbSocketPath}/${process.env.INSTANCE_CONNECTION_NAME}`,
// Specify additional properties here.
...config
});
}
// [END cloud_sql_mysql_mysql_create_socket]

const createPool = async () => {
const config = {
// [START cloud_sql_mysql_mysql_limit]
// 'connectionLimit' is the maximum number of connections the pool is allowed
// to keep at once.
Expand All @@ -81,9 +104,13 @@ const createPool = async () => {
// The mysql module automatically uses exponential delays between failed
// connection attempts.
// [END cloud_sql_mysql_mysql_backoff]

//[END_EXCLUDE]
});
}
if (process.env.DB_HOST) {
return await createTcpPool(config);
} else {
return await createUnixSocketPool(config);
}

};
// [END cloud_sql_mysql_mysql_create]

Expand Down
38 changes: 24 additions & 14 deletions cloud-sql/mysql/mysql/test/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,35 @@

'use strict';

const path = require('path');
const request = require('supertest');
const sinon = require('sinon');
const assert = require('assert');

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

const server = require('../server.js');
const server = require(SAMPLE_PATH);

it('check index page', async () => {
const response = await request(server).get('/').timeout(3000);
after(() => {
server.close();
});

it('should display the default page', async () => {
await request(server)
.get('/')
.expect(200)
.expect((response) => {
assert.ok(response.text.includes('Tabs VS Spaces'));
});
});

it('should handle insert error', async () => {
const expectedResult = 'Invalid team specified';

assert.strictEqual(response.status, 200);
await request(server)
.post('/')
.expect(400)
.expect((response) => {
assert.ok(response.text.includes(expectedResult));
});
});

server.close();
80 changes: 71 additions & 9 deletions cloud-sql/postgres/knex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ and password that you specify for the default 'postgres' user.
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.

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

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

Once the proxy is ready, use the following command to start the proxy in the
background:
```bash
./cloud_sql_proxy -dir=/cloudsql -instances=$CLOUD_SQL_CONNECTION_NAME -credential_file=$GOOGLE_APPLICATION_CREDENTIALS
```
Note: Make sure to run the command under a user with write access in the
`/cloudsql` directory. This proxy will use this folder to create a unix socket
the application will use to connect to Cloud SQL.
Instructions are provided below for using the proxy with a TCP connection or a Unix Domain Socket.
On Linux or Mac OS you can use either option, but on Windows the proxy currently requires a TCP
connection.

### Launch proxy with TCP

To run the sample locally with a TCP connection, set environment variables and launch the proxy as
shown below.

#### Linux / Mac OS
Use these terminal commands to initialize environment variables:
```bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
export DB_HOST='127.0.0.1:5432'
export DB_USER='<DB_USER_NAME>'
export DB_PASS='<DB_PASSWORD>'
export DB_NAME='<DB_NAME>'
```

Then use this command to launch the proxy in the background:
```bash
./cloud_sql_proxy -instances=<project-id>:<region>:<instance-name>=tcp:5432 -credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
```

#### Windows/PowerShell
Use these PowerShell commands to initialize environment variables:
```powershell
$env:GOOGLE_APPLICATION_CREDENTIALS="<CREDENTIALS_JSON_FILE>"
$env:DB_HOST="127.0.0.1:5432"
$env:DB_USER="<DB_USER_NAME>"
$env:DB_PASS="<DB_PASSWORD>"
$env:DB_NAME="<DB_NAME>"
```

Then use this command to launch the proxy in a separate PowerShell session:
```powershell
Start-Process -filepath "C:\<path to proxy exe>" -ArgumentList "-instances=<project-id>:<region>:<instance-name>=tcp:5432 -credential_file=<CREDENTIALS_JSON_FILE>"
```

### Launch proxy with Unix Domain Socket
NOTE: this option is currently only supported on Linux and Mac OS. Windows users should use the
[Launch proxy with TCP](#launch-proxy-with-tcp) option.

To use a Unix socket, you'll need to create a directory and give write access to the user running
the proxy. For example:
```bash
sudo mkdir /path/to/the/new/directory
sudo chown -R $USER /path/to/the/new/directory
```
You'll also need to initialize an environment variable containing the directory you just created:
```bash
export DB_SOCKET_PATH=/path/to/the/new/directory
```

Use these terminal commands to initialize other environment variables as well:
```bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service/account/key.json
export INSTANCE_CONNECTION_NAME='<MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>'
export DB_USER='<DB_USER_NAME>'
export DB_PASS='<DB_PASSWORD>'
export DB_NAME='<DB_NAME>'
```

Then use this command to launch the proxy in the background:

```bash
./cloud_sql_proxy -dir=$DB_SOCKET_PATH --instances=$INSTANCE_CONNECTION_NAME --credential_file=$GOOGLE_APPLICATION_CREDENTIALS &
```

### Testing the application

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

Expand Down
2 changes: 1 addition & 1 deletion cloud-sql/postgres/knex/app.flexible.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ env_variables:
DB_PASS: MY_DB_PASSWORD
DB_NAME: MY_DATABASE
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
CLOUD_SQL_INSTANCE_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
INSTANCE_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>

beta_settings:
# The connection name of your instance, available by using
Expand Down
2 changes: 1 addition & 1 deletion cloud-sql/postgres/knex/app.standard.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ env_variables:
DB_PASS: MY_DB_PASSWORD
DB_NAME: MY_DATABASE
# e.g. my-awesome-project:us-central1:my-cloud-sql-instance
CLOUD_SQL_INSTANCE_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>
INSTANCE_CONNECTION_NAME: <MY-PROJECT>:<INSTANCE-REGION>:<MY-DATABASE>

beta_settings:
# The connection name of your instance, available by using
Expand Down
13 changes: 11 additions & 2 deletions cloud-sql/postgres/knex/createTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@
const Knex = require('knex');

const createTable = async (config) => {
const socketPath = process.env.DB_SOCKET_PATH || "/cloudsql";

// Connect to the database
config.host = `/cloudsql/${config.connectionName}`;
if (config.dbHost){
const dbSocketAddr = config.dbHost.split(":");
config.host = dbSocketAddr[0];
config.port = dbSocketAddr[1];
} else {
config.host = `${socketPath}/${config.connectionName}`;
}

const knex = Knex({client: 'pg', connection: config});

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

require('yargs')
.command(
'* <user> <password> <database> <connectionName>',
'* <user> <password> <database> <connectionName> [dbHost]',
'Create a "votes" table',
{},
createTable
Expand Down
Loading

0 comments on commit 97dfea7

Please sign in to comment.