0

I followed the quickstart guide for Firestore in Python but I couldn't get it to run as I got this error message:

grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:
    status = StatusCode.UNAVAILABLE
    details = "Name resolution failure"
    debug_error_string = "{"created":"@1554833859.769886000","description":"Failed to create subchannel","file":"src/core/ext/filters/client_channel/client_channel.cc","file_line":2267,"referenced_errors":[{"created":"@1554833859.769576000","description":"Name resolution failure","file":"src/core/ext/filters/client_channel/request_routing.cc","file_line":165,"grpc_status":14}]}"
...
google.api_core.exceptions.ServiceUnavailable: 503 Name resolution failure

This is my code:

db = firestore.Client()
doc_ref = db.collection(u'users').document(u'alovelace')
doc_ref.set({
    u'first': u'Ada',
    u'last': u'Lovelace',
    u'born': 1815
})

# Then query for documents
users_ref = db.collection(u'users')
docs = users_ref.get()

for doc in docs:
    print(u'{} => {}'.format(doc.id, doc.to_dict()))
  1. The data is there: the viewer
  2. The environment on Mac is set like this: export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json for authentication
  3. google-cloud-firestore is installed in a new virtualenv as described in the quickstart guide.
  4. I made sure that I'm using the right gcloud project with: gcloud config set project example4

There must me something simple missing. Any hints are appreciated!

Sandro
  • 1,757
  • 1
  • 19
  • 29
  • I've opened a support ticket for that as well. See https://github.com/googleapis/google-cloud-python/issues/7679 – Sandro Apr 10 '19 at 20:44

2 Answers2

0

As I haven't got an answer yet I had to use the Firestore REST API as an alternative and pushed it to my GitHub Repo.

The downside is, that it needs custom JWT token generation. I worked for me like this:

import json
import time
import jwt
from jwt.contrib.algorithms.pycrypto import RSAAlgorithm

from definitions import AUTH_FILE_PATH


class JWT(object):

    def get_token(self):
        """
        Returns the jwt token using the configured `/auth.json` file and a default expiration of an hour.
        """
        # The implementation has been guided by this page:
        # https://developers.google.com/identity/protocols/OAuth2ServiceAccount

        try:
            jwt.register_algorithm('RS256', RSAAlgorithm(RSAAlgorithm.SHA256))
        except ValueError:
            # Algorithm already has a handler
            pass

        with open(AUTH_FILE_PATH, 'r') as f:
            auth = json.load(f)
        iat = time.time()
        # exp is set to expire the token maximum of an hour later
        # see https://developers.google.com/identity/protocols/OAuth2ServiceAccount#formingclaimset
        exp = iat + 3600
        payload = {'iss': auth['client_email'],
                   'sub': auth['client_email'],
                   # see https://github.com/googleapis/googleapis/blob/master/google/firestore/firestore_v1.yaml
                   # name: firestore.googleapis.com # Service name
                   # - name: google.firestore.v1.Firestore # API name
                   # 'aud': 'https://SERVICE_NAME/API_NAME'
                   'aud': 'https://firestore.googleapis.com/google.firestore.v1.Firestore',
                   'iat': iat,
                   'exp': exp
                   }
        additional_headers = {'kid': auth['private_key_id']}

        # For jwt docs see: https://pyjwt.readthedocs.io/en/latest/
        return jwt.encode(payload, auth['private_key'], headers=additional_headers, algorithm='RS256')

Thats the source file. It can then be used like this:

jwt = JWT()
        token = 'Bearer ' + jwt.get_token().decode("utf-8")
        base_api_url = 'https://content-firestore.googleapis.com/v1'
        project = 'example5-237118'
        database = '/databases/(default)/'
        endpoint_prefix = base_api_url + '/projects/' + project + database

        # list collections
        endpoint = 'documents:listCollectionIds'
        print(requests.post(endpoint_prefix + endpoint, headers={'Authorization': token}).text)

See source file.

Sandro
  • 1,757
  • 1
  • 19
  • 29
0

I could solve the problem by setting os.environ['GRPC_DNS_RESOLVER'] = 'native'

Sandro
  • 1,757
  • 1
  • 19
  • 29