【问题标题】:Local unit testing Google Cloud Storage signed URL本地单元测试 Google Cloud Storage 签名 URL
【发布时间】:2016-05-16 08:26:34
【问题描述】:

我正在使用 App Engine 编写一个新应用程序,并且由于文档建议不要使用 Blobstore API,我正在使用 Google Cloud Storage 客户端 (GCS)。一切都很好,但我希望能够将“签名的 url”返回给客户端,这样他们就可以在不通过应用程序的情况下获取 GCS 资源。我相信这就是签名网址的用途。

但是如何测试呢?我可以成功测试来自客户端的 GCS 调用,但我不知道如何使用 urlfetch 测试客户端的 HTTP 调用。

以下是说明我的问题的完整测试用例:

import base64
import mimetypes
import urllib
import urllib2
from datetime import datetime, timedelta
import time

from google.appengine.api import app_identity
from google.appengine.datastore import datastore_stub_util
from google.appengine.ext import testbed
from google.appengine.ext import ndb
import unittest

import cloudstorage

# IS THIS RIGHT ?
GCS_API_ACCESS_ENDPOINT = 'http://localhost:8000/_ah/gcs'


def sign_url(bucket_object, expires_after_seconds=60):
    """ cloudstorage signed url to download cloudstorage object without login
        Docs : https://cloud.google.com/storage/docs/access-control?hl=bg#Signed-URLs
        API : https://cloud.google.com/storage/docs/reference-methods?hl=bg#getobject
    """
    # source: https://github.com/voscausa/appengine-gcs-signed-url/blob/05b8a93e2777679d40af62cc5ffce933216e6a85/sign_url.py
    method = 'GET'
    gcs_filename = urllib.quote(bucket_object)
    content_md5, content_type = None, None

    # expiration : number of seconds since epoch
    expiration_dt = datetime.utcnow() + timedelta(seconds=expires_after_seconds)
    expiration = int(time.mktime(expiration_dt.timetuple()))

    # Generate the string to sign.
    signature_string = '\n'.join([
        method,
        content_md5 or '',
        content_type or '',
        str(expiration),
        gcs_filename])

    signature_bytes = app_identity.sign_blob(signature_string)[1]
    google_access_id = app_identity.get_service_account_name()

    # Set the right query parameters. we use a gae service account for the id
    query_params = {'GoogleAccessId': google_access_id,
                    'Expires': str(expiration),
                    'Signature': base64.b64encode(signature_bytes)}

    # Return the built URL.
    result = '{endpoint}{resource}?{querystring}'.format(endpoint=GCS_API_ACCESS_ENDPOINT,
                                                         resource=gcs_filename,
                                                         querystring=urllib.urlencode(query_params))
    return result


FILE_DATA = "This is file contents."
MIME = "text/plain"


class TestGCS(unittest.TestCase):
    def setUp(self):
        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=0)
        self.testbed.init_datastore_v3_stub(consistency_policy=self.policy)
        self.testbed.init_app_identity_stub()
        self.testbed.init_memcache_stub()
        self.testbed.init_urlfetch_stub()
        self.testbed.init_blobstore_stub()
        ndb.get_context().clear_cache()

    def tearDown(self):
        self.testbed.deactivate()

    def test_gcs_works(self):
        with cloudstorage.open('/mybucket/test.txt', 'w', content_type=MIME) as f:
            f.write(FILE_DATA)
        with cloudstorage.open('/mybucket/test.txt', 'r') as f:
            data = f.read()
        print(data)
        self.assertEqual(data, FILE_DATA)

    def test_signurl(self):
        url = sign_url('/mybucket/test.txt')
        # FIXME: Not yet working as we have no idea on how to access local GCS during the test.
        result = urllib2.urlopen(url)
        self.assertEqual(200, result.code)
        self.assertEqual(FILE_DATA, result.read())

【问题讨论】:

  • GCS_API_ACCESS_ENDPOIND 没有本地端点。您只能使用:storage.googleapis.comlocalhost:8000/_ah/gcs 是本地/SDK Google Google Cloud Storage 客户端库的端点,供您的 test_gcs_works() 使用
  • @voscausa 你的意思是我不能从本地开发测试 GCS HTTP 访问? test_gcs_works 仅使用 cloudstorage api,但我需要测试 HTTP 访问。我打算在应用程序中做的是将用户重定向到我创建的签名 URL,这样我就不必直接从应用程序请求周期提供文件。
  • 您可以在SDK中测试GCS和service_accounts,但使用签名url时没有本地appengine GCS服务。
  • 新手元问题:如何将此评论标记为帖子的正确答案? :( 或者您可以发布一个答案,说明 GCS URL 无法测试,只能进行 API 调用?谢谢。

标签: google-app-engine google-cloud-storage google-app-engine-python


【解决方案1】:

您可以在 SDK 中测试 GCS 和 service_accounts,但使用签名 url 时没有本地 appengine GCS 服务。

但您可以使用服务帐户和谷歌云服务测试您的本地应用。

服务帐户可以很容易地授权 appengine 对其他 Google API 和服务的请求。

要在 appengine SDK 中使用服务帐户,您必须在 run the development server 时添加两个未记录的选项:

  1. --appidentity_email_address=<SERVICE_ACCOUNT_EMAIL_ADDRESS>
  2. --appidentity_private_key_path=<PEM_KEY_PATH>

更多信息在this request for documentation issue

您可以在您的 appengine 云项目的developers console permissions section 中创建或查找服务帐号。
您可以为服务帐号创建和下载 p12 密钥。

使用 OpenSSL 将此 p12 密钥转换为 RSA pem 密钥。

我在 Windows 上使用了this OpenSSL installer

要在 Windows 中创建 pem 密钥文件,请使用:

openssl pkcs12 -in <P12_KEY_PATH> -nocerts -nodes -passin pass:notasecret | openssl rsa -out <PEM_KEY_PATH>

现在您可以在开发服务器中使用您的云应用服务帐号,并使用app_identity 对请求进行签名和授权。

【讨论】:

  • 我知道服务帐户,但这仅允许针对 Google 实时服务器进行测试,这有悖于本地测试的目的。根据您之前的评论,目前无法在本地测试 GCS URL,只能调用 API。
  • 如果您发现某个功能没有实现,但您希望它实现,您可以向相关的公共问题跟踪器 (PIT) 发布功能请求。在这种情况下,最好发布到App Engine PIT,因为单元测试存根/url 功能将是 GAE SDK 和现有测试环境/工具的一部分。
  • @voscausa 我经常看到你的帖子(问题和答案)。 GCS,他们正在拯救我的培根。谢谢!
猜你喜欢
  • 2014-06-28
  • 1970-01-01
  • 2015-12-13
  • 1970-01-01
  • 2014-01-12
  • 2017-04-03
  • 2017-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多