Skip to content

Commit f1dd625

Browse files
authored
Memorystore sample for App Engine PHP 7.2 (GoogleCloudPlatform#908)
1 parent 506f9b2 commit f1dd625

File tree

7 files changed

+285
-0
lines changed

7 files changed

+285
-0
lines changed

appengine/php72/memorystore/README.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Connect to Cloud Memorystore from Google App Engine
2+
3+
This sample application demonstrates how to use
4+
[Cloud Memorystore for Redis](https://cloud.google.com/memorystore/docs/)
5+
with Google App Engine for PHP 7.2.
6+
7+
**Prerequisites**
8+
9+
- Install the [Google Cloud SDK](https://developers.google.com/cloud/sdk/).
10+
11+
## Setup
12+
13+
Before you can run or deploy the sample, you will need to do the following:
14+
15+
1. Create a [Memorystore instance][memorystore_create]. You can do this from the
16+
[Cloud Console](https://console.developers.google.com) or via the
17+
[Cloud SDK](https://cloud.google.com/sdk). To create it via the SDK use the
18+
following command:
19+
20+
$ gcloud beta redis instances create YOUR_INSTANCE_NAME --region=REGION_ID
21+
22+
1. Update the environment variables `REDIS_HOST` and `REDIS_PORT` in `app.yaml`
23+
with your configuration values. These values are used when the application is
24+
deployed. Run the following command to get the values for your isntance:
25+
26+
$ gcloud beta redis instances describe YOUR_INSTANCE_NAME --region=REGION_ID
27+
28+
[memorystore_create]: https://cloud.google.com/memorystore/docs/redis/creating-managing-instances
29+
30+
## Run locally
31+
32+
You can connect to a local database instance by setting the `REDIS_` environment
33+
variables to your local instance. Alternatively, you can set them to your Cloud
34+
Memorystore instance, but you will need to create a firewall rule for this,
35+
which may be a safety concern.
36+
37+
```sh
38+
cd php-docs-samples/appengine/php72/memorystore
39+
40+
# set local connection parameters
41+
export REDIS_HOST=127.0.0.1
42+
export REDIS_PORT=6379
43+
44+
php -S localhost:8080
45+
```
46+
47+
> be sure the `REDIS_` environment variables are appropriate for your Redis
48+
instance.
49+
50+
Now you can view the app running at [http://localhost:8080](http://localhost:8080)
51+
in your browser.
52+
53+
## Set up Serverless VPC Access
54+
55+
**IMPORTANT** App Engine requires a [Serverless VPC Access][vpc-access]
56+
connector to connect to Memorystore.
57+
58+
In order for App Engine to connect to Memorystore, you must first
59+
[configure a Serverless VPC Access connector][configure-vpc]. For example:
60+
61+
```
62+
# Example command to create a VPC connector. See the docs for more details.
63+
$ gcloud beta compute networks vpc-access connectors create CONNECTOR_NAME \
64+
--region=REGION_ID \
65+
--range="10.8.0.0/28"
66+
--project=PROJECT_ID
67+
```
68+
69+
Next, you neded to [configure App Engine to connect to your VPC network][connecting-appengine].
70+
This is done by modifying [`app.yaml`](app.yaml) and setting the full name of
71+
the connector you just created under `vpc_access_connector`.
72+
73+
```
74+
vpc_access_connector:
75+
name: "projects/PROJECT_ID/locations/REGION_ID/connectors/CONNECTOR_NAME"
76+
```
77+
78+
**Note**: Serverless VPC Access incurs additional billing. See
79+
[pricing][vpc-pricing] for details.
80+
81+
[vpc-access]: https://cloud.google.com/vpc
82+
[configure-vpc]: https://cloud.google.com/vpc/docs/configure-serverless-vpc-access
83+
[connecting-appengine]: https://cloud.google.com/appengine/docs/standard/python/connecting-vpc#configuring
84+
[vpc-pricing]: https://cloud.google.com/compute/pricing#network
85+
86+
## Deploy to App Engine
87+
88+
**IMPORTANT** Because Serverless VPC Connector is in *beta*, you must deploy to App Engine
89+
using the `gcloud beta app deploy` command. Without this, the connection to
90+
Memorystore *will not work*.
91+
92+
```
93+
$ gcloud beta app deploy --project PROJECT_ID
94+
```
95+
96+
Now open `https://{YOUR_PROJECT_ID}.appspot.com/` in your browser to see the running
97+
app.
98+
99+
**Note**: This example requires the `redis.so` extension to be enabled on your App Engine
100+
instance. This is done by including [`php.ini`](php.ini) in your project root.
101+
102+
## Troubleshooting
103+
104+
### Connection timed out
105+
106+
If you receive the error "Error: Connection timed out", it's possible your VPC Connector
107+
is not fully set up. Run the following and check the property `state` is set to READY:
108+
109+
```
110+
$ gcloud beta compute networks vpc-access connectors describe CONNECTOR_NAME --region=REGION_ID
111+
```
112+
113+
If you continue to see the timeout error, try creating a new VPC connector with a different
114+
CIDR `range`.
115+
116+
### Name or service not known
117+
118+
If you receive the following error, make sure you set the `REDIS_HOST` environment variable in `app.yaml` to be the
119+
host of your Memorystore for Redis instance.
120+
121+
```
122+
PHP message: PHP Warning: Redis::connect(): php_network_getaddresses: getaddrinfo failed: Name or service not known in /srv/index.php
123+
```
124+
125+
### Request contains an invalid argument
126+
127+
If you receive this error, it is because either the `gcloud` command to create the VPC
128+
Access connector was missing the `--range` option, or the value supplied to the
129+
`--range` option did not use the `/28` CIDR mask as required. Be sure to supply a valid
130+
CIDR range ending in `/28`.

appengine/php72/memorystore/app.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# This app.yaml is for deploying to instances of Cloud SQL running MySQL.
2+
# See app-postgres.yaml for running Cloud SQL with PostgreSQL.
3+
4+
runtime: php72
5+
6+
# [START gae_memorystore_app_yaml]
7+
# update with Redis instance host IP, port
8+
env_variables:
9+
REDIS_HOST: YOUR_REDIS_HOST
10+
REDIS_PORT: 6379
11+
12+
# Configure your VPC Access connector here
13+
vpc_access_connector:
14+
name: "projects/YOUR_PROJECT_ID/locations/YOUR_REGION/connectors/YOUR_CONNECTOR_NAME"
15+
# [END gae_memorystore_app_yaml]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"require-dev": {
3+
"phpunit/phpunit": "~5",
4+
"google/cloud-tools": "dev-master",
5+
"paragonie/random_compat": "^2.0"
6+
}
7+
}

appengine/php72/memorystore/index.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
/**
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
// Only serve traffic from "/"
19+
switch (@parse_url($_SERVER['REQUEST_URI'])['path']) {
20+
case '/':
21+
break;
22+
default:
23+
http_response_code(404);
24+
exit('Not Found');
25+
}
26+
27+
// Connect to Memorystore from App Engine.
28+
if (!$host = getenv('REDIS_HOST')) {
29+
throw new Exception('The REDIS_HOST environment variable is required');
30+
}
31+
32+
# Memorystore Redis port defaults to 6379
33+
$port = getenv('REDIS_PORT') ?: '6379';
34+
35+
try {
36+
$redis = new Redis();
37+
$redis->connect($host, $port);
38+
} catch (Exception $e) {
39+
return print('Error: ' . $e->getMessage());
40+
}
41+
42+
$value = $redis->incr('counter');
43+
44+
printf('Visitor number: %s', $value);

appengine/php72/memorystore/php.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
; Enable the Redis extension on App Engine
2+
extension=redis.so
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
xml version="1.0" encoding="UTF-8"?>
2+
17+
<phpunit>
18+
<testsuites>
19+
<testsuite name="AppEngine for PHP 7.2 Memorystore test">
20+
<directory>testdirectory>
21+
testsuite>
22+
testsuites>
23+
<logging>
24+
<log type="coverage-clover" target="build/logs/clover.xml"/>
25+
logging>
26+
phpunit>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
2+
/**
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
namespace Google\Cloud\Test\Memorystore;
18+
19+
use Google\Cloud\TestUtils\AppEngineDeploymentTrait;
20+
use Google\Cloud\TestUtils\TestTrait;
21+
use Google\Cloud\TestUtils\FileUtil;
22+
use Symfony\Component\Yaml\Yaml;
23+
24+
class DeployTest extends \PHPUnit_Framework_TestCase
25+
{
26+
use TestTrait;
27+
use AppEngineDeploymentTrait;
28+
29+
public function testIndex()
30+
{
31+
$resp = $this->client->request('GET', '/');
32+
33+
$this->assertEquals('200', $resp->getStatusCode());
34+
$this->assertRegExp('/Visitor number: \d+/', (string) $resp->getBody());
35+
}
36+
37+
public static function beforeDeploy()
38+
{
39+
$host = self::requireEnv('REDIS_HOST');
40+
$connectorName = self::requireEnv('GOOGLE_VPC_ACCESS_CONNECTOR_NAME');
41+
42+
$tmpDir = FileUtil::cloneDirectoryIntoTmp(__DIR__ . '/..');
43+
self::$gcloudWrapper->setDir($tmpDir);
44+
chdir($tmpDir);
45+
46+
$appYaml = Yaml::parse(file_get_contents('app.yaml'));
47+
$appYaml['env_variables']['REDIS_HOST'] = $host;
48+
if ($port = getenv('REDIS_PORT')) {
49+
$appYaml['env_variables']['REDIS_PORT'] = $port;
50+
}
51+
$appYaml['vpc_access_connector']['name'] = $connectorName;
52+
53+
file_put_contents('app.yaml', Yaml::dump($appYaml));
54+
}
55+
56+
private static function doDeploy()
57+
{
58+
// Ensure we use gcloud "beta" deploy
59+
return self::$gcloudWrapper->deploy(['release_version' => 'beta']);
60+
}
61+
}

0 commit comments

Comments
 (0)