Search code examples
pythondockergoogle-cloud-platformgoogle-vision

StatusCode.UNAUTHENTICATED when running Vision API demo in Python/Flask running in Docker


I've followed the instructions the Google Client Libraries page for the Vision API to get started with the Vision API in Python (I'm running 2.7). Since my code is running in Docker (a Flask app), I've followed the instructions in the following way:

  1. Added google-cloud-vision and google-cloud libraries to my requirements.txt file.
  2. Created a json account credentials file and set the location of this file as an environment variable named GOOGLE_APPLICATION_CREDENTIALS
  3. ran gcloud init successfully while "ssh'd" into my web app's docker container
  4. Copied the client library example in the link above exactly into a test view to run the code

The steps above result in the error below:

> RetryError: GaxError(Exception occurred in retry method that was not
> classified as transient, caused by <_Rendezvous of RPC that terminated
> with (StatusCode.UNAUTHENTICATED, Traceback (most recent call last):  
> File "src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi",
> line 154, in grpc._cython.cygrpc.plugin_get_metadata
> (src/python/grpcio/grpc/_cython/cygrpc.c:7054)   File
> "/usr/local/lib/python2.7/site-packages/grpc/_plugin_wrapping.py",
> line 106, in __call__
>     AuthMetadataPluginCallback(wrapped_cygrpc_callback))   File "/usr/local/lib/python2.7/site-packages/google/auth/transport/grpc.py",
> line 73, in __call__
>     callback(self._get_authorization_headers(context), None)   File "/usr/local/lib/python2.7/site-packages/google/auth/transport/grpc.py",
> line 61, in _get_authorization_headers
>     headers)   File "/usr/local/lib/python2.7/site-packages/google/auth/credentials.py",
> line 121, in before_request
>     self.refresh(request)   File "/usr/local/lib/python2.7/site-packages/google/oauth2/service_account.py",
> line 310, in refresh
>     request, self._token_uri, assertion)   File "/usr/local/lib/python2.7/site-packages/google/oauth2/_client.py",
> line 143, in jwt_grant
>     response_data = _token_endpoint_request(request, token_uri, body)   File
> "/usr/local/lib/python2.7/site-packages/google/oauth2/_client.py",
> line 98, in _token_endpoint_request
>     body = urllib.parse.urlencode(body) AttributeError: 'Module_six_moves_urllib_parse' object has no attribute 'urlencode'
> )>)

I'm thinking the problem has to do with my crendtials since it says StatusCode.UNAUTHENTICATED, but I haven't been able to get this fixed. Could anyone help? Thanks!


Solution

  • TL;DR - You're having issues with the Credentials as you've already observed. Using Application Default Credentials will be much simpler and will make your application portable across multiple environments. More specifically, if you already have a working set of credentials in gcloud, you can do a one-time step to activate Application Default Credentials in gcloud using gcloud auth application-default login.

    Longer Version

    Greg, I tried the example on my machine, following the steps mentioned on the Cloud Vision API Client libraries page and it works for me.

    In case, if the steps were not clear, I'll walk you over:

    1. Install the google-cloud-vision python package. (From your post you seem to have done this already).

      pip install --upgrade google-cloud-vision

    2. Set up Application Default Credentials on your machine / docker container. You seemed to mention that you ran gcloud init successfully. This is not sufficient to set up Application Default Credentials. Instead, you will have to run the following command mentioned in the steps which will ask you to open a link in the browser and you will have to paste back the confirmation from the browser back into your command line.

      gcloud auth application-default login

    3. Download the test image wakeupcat.jpg or use an image of your own. This part unfortunately is not clearly mentioned on the page.

    4. Create a file named cloud-vision-example.py or any other name you prefer with the following content (NOTE: I've updated the image file path here):

    cloud-vision-example.py

    import io
    import os
    
    # Imports the Google Cloud client library
    from google.cloud import vision
    
    # Instantiates a client
    vision_client = vision.Client()
    
    # The name of the image file to annotate
    file_name = os.path.join('.', 'wakeupcat.jpg')
    
    # Loads the image into memory
    with io.open(file_name, 'rb') as image_file:
        content = image_file.read()
        image = vision_client.image(
            content=content)
    
    # Performs label detection on the image file
    labels = image.detect_labels()
    
    print('Labels:')
    for label in labels:
        print(label.description)
    
    1. Run the python script. python cloud-vision-example.py.

    Output:

    $ python cloud-vision-example.py
    Labels:
    cat
    photo caption
    small to medium sized cats
    cat like mammal
    whiskers
    snout
    kitten
    asian
    

    How Application Default Credentials work

    • If you would like to use the credentials from a service account, you can create a new service account, download the private key json file and point the environment variable GOOGLE_APPLICATION_CREDENTIALS at the json file (it looks like you did, but you might want to double check if you downloaded the right credentials and the path is valid).

    • If GOOGLE_APPLICATION_CREDENTIALS is not set, the client library will try to use the gcloud application default credentials (which should have been setup prior to this).

    • If your app is running on App Engine or Compute Engine, the associated built-in service account will be used as the last resort.