From 63896b13fc87e27a6110dee1596652506fdd9353 Mon Sep 17 00:00:00 2001 From: Tim Swast Date: Thu, 9 Jun 2016 14:37:49 -0700 Subject: [PATCH] Add Bigtable hello world sample. (#371) * Add Bigtable hello world sample. * Update secrets for Bigtable tests. --- .coveragerc | 1 + .travis.yml | 3 +- bigtable/hello/README.md | 67 ++++++++++++++++++++ bigtable/hello/main.py | 95 +++++++++++++++++++++++++++++ bigtable/hello/main_test.py | 47 ++++++++++++++ bigtable/hello/requirements.txt | 1 + conftest.py | 4 +- nox.py | 9 ++- scripts/prepare-testing-project.sh | 8 ++- secrets.tar.enc | Bin 14368 -> 10272 bytes testing/resources/test-env.tmpl.sh | 2 + 11 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 bigtable/hello/README.md create mode 100644 bigtable/hello/main.py create mode 100644 bigtable/hello/main_test.py create mode 100644 bigtable/hello/requirements.txt diff --git a/.coveragerc b/.coveragerc index 5053cd951f23..3e7b8619c0b5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,6 +1,7 @@ [run] include = appengine/* + bigtable/* bigquery/* blog/* cloud_logging/* diff --git a/.travis.yml b/.travis.yml index 0566d6b8ab6c..b7172e7b046d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,14 +15,13 @@ env: - GOOGLE_APPLICATION_CREDENTIALS=${TRAVIS_BUILD_DIR}/testing/resources/service-account.json - GOOGLE_CLIENT_SECRETS=${TRAVIS_BUILD_DIR}/testing/resources/client-secrets.json - GAE_ROOT=${HOME}/.cache/ - - secure: Orp9Et2TIwCG/Hf59aa0NUDF1pNcwcS4TFulXX175918cFREOzf/cNZNg+Ui585ZRFjbifZdc858tVuCVd8XlxQPXQgp7bwB7nXs3lby3LYg4+HD83Gaz7KOWxRLWVor6IVn8OxeCzwl6fJkdmffsTTO9csC4yZ7izHr+u7hiO4= + - secure: TQ0e6XKeFwVkoqgOJH9f/afyRouUSC6s7LC32C4JS+O2X4sXyXTPXACmzu5wCW0BXPc6HvITMLvkf7g6XXyGlCPkjM8Uw5Vg5F9+cwN1HMlI+gK6bMGTUfrwN5ruFT+KmEnD4F93NY3xkDbZd0fw23d/mVloTc6V0gUsxEUkuhM= addons: apt: packages: - portaudio19-dev before_install: - pip install --upgrade pip wheel virtualenv - # for speech api sample - openssl aes-256-cbc -k "$secrets_password" -in secrets.tar.enc -out secrets.tar -d - tar xvf secrets.tar install: diff --git a/bigtable/hello/README.md b/bigtable/hello/README.md new file mode 100644 index 000000000000..6fc0473a33de --- /dev/null +++ b/bigtable/hello/README.md @@ -0,0 +1,67 @@ +# Cloud Bigtable Hello World + +This is a simple application that demonstrates using the [Google Cloud Client +Library][gcloud-python] to connect to and interact with Cloud Bigtable. + +[gcloud-python]: https://github.com/GoogleCloudPlatform/gcloud-python + + +## Provision a cluster + +Follow the instructions in the [user documentation](https://cloud.google.com/bigtable/docs/creating-cluster) +to create a Google Cloud Platform project and Cloud Bigtable cluster if necessary. +You'll need to reference your project ID, zone and cluster ID to run the application. + + +## Run the application + +First, set your [Google Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) + +Install the dependencies with pip. + +``` +$ pip install -r requirements.txt +``` + +Run the application. Replace the command-line parameters with values for your cluster. + +``` +$ python main.py my-project my-cluster us-central1-c +``` + +You will see output resembling the following: + +``` +Create table Hello-Bigtable-1234 +Write some greetings to the table +Scan for all greetings: + greeting0: Hello World! + greeting1: Hello Cloud Bigtable! + greeting2: Hello HappyBase! +Delete table Hello-Bigtable-1234 +``` + +## Understanding the code + +The [application](main.py) uses the [Google Cloud Bigtable HappyBase +package][Bigtable HappyBase], an implementation of the [HappyBase][HappyBase] +library, to make calls to Cloud Bigtable. It demonstrates several basic +concepts of working with Cloud Bigtable via this API: + +[Bigtable HappyBase]: https://googlecloudplatform.github.io/gcloud-python/stable/happybase-package.html +[HappyBase]: http://happybase.readthedocs.io/en/latest/index.html + +- Creating a [Connection][HappyBase Connection] to a Cloud Bigtable + [Cluster][Cluster API]. +- Using the [Connection][HappyBase Connection] interface to create, disable and + delete a [Table][HappyBase Table]. +- Using the Connection to get a Table. +- Using the Table to write rows via a [put][HappyBase Table Put] and scan + across multiple rows using [scan][HappyBase Table Scan]. + +[Cluster API]: https://googlecloudplatform.github.io/gcloud-python/stable/bigtable-cluster.html +[HappyBase Connection]: https://googlecloudplatform.github.io/gcloud-python/stable/happybase-connection.html +[HappyBase Table]: https://googlecloudplatform.github.io/gcloud-python/stable/happybase-table.html +[HappyBase Table Put]: https://googlecloudplatform.github.io/gcloud-python/stable/happybase-table.html#gcloud.bigtable.happybase.table.Table.put +[HappyBase Table Scan]: https://googlecloudplatform.github.io/gcloud-python/stable/happybase-table.html#gcloud.bigtable.happybase.table.Table.scan + diff --git a/bigtable/hello/main.py b/bigtable/hello/main.py new file mode 100644 index 000000000000..eab295161ff1 --- /dev/null +++ b/bigtable/hello/main.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +# Copyright 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Demonstrates how to connect to Cloud Bigtable and run some basic operations. + +Prerequisites: + +- Create a Cloud Bigtable cluster. + https://cloud.google.com/bigtable/docs/creating-cluster +- Set your Google Application Default Credentials. + https://developers.google.com/identity/protocols/application-default-credentials +- Set the GCLOUD_PROJECT environment variable to your project ID. + https://support.google.com/cloud/answer/6158840 +""" + +import argparse +import uuid + +from gcloud import bigtable +from gcloud.bigtable import happybase + + +def main(project, cluster_id, zone, table_name): + # The client must be created with admin=True because it will create a + # table. + client = bigtable.Client(project=project, admin=True) + + with client: + cluster = client.cluster(zone, cluster_id) + cluster.reload() + connection = happybase.Connection(cluster=cluster) + + print('Creating the {} table.'.format(table_name)) + column_family_name = 'cf1' + connection.create_table( + table_name, + { + column_family_name: dict() # Use default options. + }) + table = connection.table(table_name) + + print('Writing some greetings to the table.') + column_name = '{fam}:greeting'.format(fam=column_family_name) + greetings = [ + 'Hello World!', + 'Hello Cloud Bigtable!', + 'Hello HappyBase!', + ] + for value in greetings: + # Use a random key to distribute writes more evenly across shards. + # See: https://cloud.google.com/bigtable/docs/schema-design + row_key = str(uuid.uuid4()) + table.put(row_key, {column_name: value}) + + print('Scanning for all greetings:') + for key, row in table.scan(): + print('\t{}: {}'.format(key, row[column_name])) + + print('Deleting the {} table.'.format(table_name)) + connection.delete_table(table_name) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='A sample application that connects to Cloud' + + ' Bigtable.', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument( + 'project', + help='Google Cloud Platform project ID that contains the Cloud' + + ' Bigtable cluster.') + parser.add_argument( + 'cluster', help='ID of the Cloud Bigtable cluster to connect to.') + parser.add_argument( + 'zone', help='Zone that contains the Cloud Bigtable cluster.') + parser.add_argument( + '--table', + help='Table to create and destroy.', + default='Hello-Bigtable') + + args = parser.parse_args() + main(args.project, args.cluster, args.zone, args.table) diff --git a/bigtable/hello/main_test.py b/bigtable/hello/main_test.py new file mode 100644 index 000000000000..7826a5dbeab3 --- /dev/null +++ b/bigtable/hello/main_test.py @@ -0,0 +1,47 @@ +# Copyright 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random +import re +import sys + +from hello import main + +import pytest + +TABLE_NAME_FORMAT = 'Hello-Bigtable-{}' +TABLE_NAME_RANGE = 10000 + + +@pytest.mark.skipif( + sys.version_info >= (3, 0), + reason=("grpc doesn't yet support python3 " + 'https://github.com/grpc/grpc/issues/282')) +def test_main(cloud_config, capsys): + table_name = TABLE_NAME_FORMAT.format( + random.randrange(TABLE_NAME_RANGE)) + main( + cloud_config.project, + cloud_config.bigtable_cluster, + cloud_config.bigtable_zone, + table_name) + + out, _ = capsys.readouterr() + assert re.search( + re.compile(r'Creating the Hello-Bigtable-[0-9]+ table\.'), out) + assert re.search(re.compile(r'Writing some greetings to the table\.'), out) + assert re.search(re.compile(r'Scanning for all greetings'), out) + assert re.search(re.compile(r'greeting0: Hello World!'), out) + assert re.search( + re.compile(r'Deleting the Hello-Bigtable-[0-9]+ table\.'), out) diff --git a/bigtable/hello/requirements.txt b/bigtable/hello/requirements.txt new file mode 100644 index 000000000000..93130c943785 --- /dev/null +++ b/bigtable/hello/requirements.txt @@ -0,0 +1 @@ +gcloud[grpc]==0.14.0 diff --git a/conftest.py b/conftest.py index 8bf0e8d2dcf4..1a3348aedbef 100644 --- a/conftest.py +++ b/conftest.py @@ -28,7 +28,9 @@ def cloud_config(): return Namespace( project=os.environ.get('GCLOUD_PROJECT'), storage_bucket=os.environ.get('CLOUD_STORAGE_BUCKET'), - client_secrets=os.environ.get('GOOGLE_CLIENT_SECRETS')) + client_secrets=os.environ.get('GOOGLE_CLIENT_SECRETS'), + bigtable_cluster=os.environ.get('BIGTABLE_CLUSTER'), + bigtable_zone=os.environ.get('BIGTABLE_ZONE')) def get_resource_path(resource, local_path): diff --git a/nox.py b/nox.py index 46f2abd333d0..af042e1ea852 100644 --- a/nox.py +++ b/nox.py @@ -26,8 +26,13 @@ '-x', '--no-success-flaky-report', '--cov', '--cov-config', '.coveragerc', '--cov-append', '--cov-report='] -# Speech is temporarily disabled. -TESTS_BLACKLIST = set(('./appengine/standard', './testing', './speech')) +# Bigtable and Speech are disabled because they use gRPC, which does not yet +# support Python 3. See: https://github.com/grpc/grpc/issues/282 +TESTS_BLACKLIST = set(( + './appengine/standard', + './bigtable', + './speech', + './testing')) APPENGINE_BLACKLIST = set() diff --git a/scripts/prepare-testing-project.sh b/scripts/prepare-testing-project.sh index fafcf89eae1f..c66ddf3e3c67 100755 --- a/scripts/prepare-testing-project.sh +++ b/scripts/prepare-testing-project.sh @@ -22,6 +22,12 @@ echo "Creating cloud storage bucket." gsutil mb gs://$GCLOUD_PROJECT gsutil defacl set public-read gs://$GCLOUD_PROJECT +echo "Creating bigtable resources." +gcloud alpha bigtable clusters create bigtable-test \ + --description="Test cluster" \ + --nodes=3 \ + --zone=us-central1-c + echo "Creating bigquery resources." gcloud alpha bigquery datasets create test_dataset gcloud alpha bigquery datasets create ephemeral_test_dataset @@ -38,4 +44,4 @@ echo "Creating pubsub resources." gcloud alpha pubsub topics create gae-mvm-pubsub-topic echo "To finish setup, follow this link to enable APIs." -echo "https://console.cloud.google.com/flows/enableapi?project=${GCLOUD_PROJECT}&apiid=bigquery,cloudmonitoring,compute_component,datastore,datastore.googleapis.com,dataproc,dns,plus,pubsub,logging,storage_api,vision.googleapis.com" +echo "https://console.cloud.google.com/flows/enableapi?project=${GCLOUD_PROJECT}&apiid=bigtable,bigtableclusteradmin,bigtabletableadmin,bigquery,cloudmonitoring,compute_component,datastore,datastore.googleapis.com,dataproc,dns,plus,pubsub,logging,storage_api,vision.googleapis.com" diff --git a/secrets.tar.enc b/secrets.tar.enc index 3dc345619bd616c4f343a6169f80fe48524acdc0..dbf6a4502425fc6c8b7a4a15836138100ac75237 100644 GIT binary patch literal 10272 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 literal 14368 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�H6wOq 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 a$FOK;G^$@eVKUo)Xm3P+YQPbgk!1*uqWXLQ diff --git a/testing/resources/test-env.tmpl.sh b/testing/resources/test-env.tmpl.sh index e1a03d65cc83..65a53826f8f1 100644 --- a/testing/resources/test-env.tmpl.sh +++ b/testing/resources/test-env.tmpl.sh @@ -1,6 +1,8 @@ # Environment variables for system tests. export GCLOUD_PROJECT=your-project-id export CLOUD_STORAGE_BUCKET=$GCLOUD_PROJECT +export BIGTABLE_CLUSTER=bigtable-test +export BIGTABLE_ZONE=us-central1-c # Environment variables for App Engine Flexible system tests. export GA_TRACKING_ID=