1
votes

I am trying to upload content to a Google bucket using Python SDK in a forked process on a Linux host (Ubuntu 18.04) The operation fails with status code 403:

google.api_core.exceptions.Forbidden: 403 POST https://www.googleapis.com/upload/storage/v1/b/temp-compare/o?uploadType=multipart: ('Request failed with status code', 403, 'Expected one of', )

Example code:

import random
import string
import json
import os
import requests
from datetime import datetime, timedelta
from google.cloud import storage

def upload_to_bucket(plainText):
    gcp_client = storage.Client.from_service_account_json("/path/to/google-bucket-credentials.json")
    bucket = gcp_client.get_bucket('bucket_name')
    bucket_file_name = random_file_name('json', 10)
    blob = bucket.blob(bucket_file_name)
    blob.upload_from_string(plainText)
    url = blob.generate_signed_url(expiration=datetime.utcnow() + timedelta(hours=48), method="GET")
    return url

def random_file_name(ext, length):
    return "%s.%s" % (''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length)), ext)


if __name__ == "__main__":
    os.fork()
    url = upload_to_bucket("just for test")
    res = requests.get(url)
    print(res.content.decode('utf8'))

I have tried using the solutions offered in google-api-python-client broken because of OAuth2? with no lock.

How can this be solved?

2

2 Answers

7
votes

Not surprisingly it turns out i'm the source of the problem. After forking the process the random filename created by random_file_name is the same (probably has something to do with the way a seed is generated) Since the account does not have permission to override the file it gets a 403 response.

If I change random_file_name to be:

def random_file_name(ext, length):
    import uuid
    return  str(uuid.uuid4())[0:length]+"."+ext

All is working as expected.

I would probably be still looking for a solution if it not for @TasosZG advise to use the cloud shell.

3
votes

The issue doesn't seem to be with fork() but with the authentication with the service account. Does it work for you without fork()?

I ran your code and managed to upload and read the content of the file, both with and without fork().

For the authentication with a service account json key I did the following:

  1. Go to Console -> IAM & admin -> Service accounts -> Create service account
  2. Grant it the Storage -> Storage Admin role
  3. Create key (json) and save it in a file key.json in the same folder with my code
  4. Configure the code to authenticate by reading the key of that file client = storage.Client.from_service_account_json("key.json")

And it worked. When I changed the role of the service account to have fewer permissions, it failed giving me a similar error to yours, only it also told me that the service account I used doesn't have storage.buckets.get access on that bucket.