-1

I have developed an application using GCP- using python, and have tested it on local environtment and it was running successfully.

My apps access google drive API using local OAuth credentials.json when tested. But after I try to deploy in on cloud run using ADC, the problem occurs.

This is my Code

import logging
import google.auth
from flask import Flask
# Impor Request untuk refresh
from google.auth.transport.requests import Request
import google.auth.exceptions # Untuk menangkap error ADC
import os # Untuk os.environ.get

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
app = Flask(__name__)

# Scope yang diperlukan untuk Google Drive API
SCOPES = ['https://www.googleapis.com/auth/drive']

# Route untuk Health Check (Sangat Cepat)
@app.route('/')
def health_check():
    """Route sederhana untuk health check Cloud Run."""
    logging.info("Health check endpoint '/' accessed.")
    return "OK", 200 # Harus kembali dengan cepat

# Route Baru untuk Melakukan Tes ADC
@app.route('/check-adc')
def perform_adc_check():
    """Melakukan tes ADC yang sebenarnya, termasuk workaround quota project."""
    credentials = None
    project_id = None
    try:
        logging.info("ADC Check: Attempting google.auth.default()...")
        # Coba dapatkan kredensial
        credentials, project_id = google.auth.default(scopes=SCOPES)
        logging.info(f"ADC Check: Found credentials. Project ID: {project_id}, Type: {type(credentials)}")

        # Coba log email SA jika ada
        if hasattr(credentials, 'service_account_email'):
             logging.info(f"ADC Check: Service Account Email: {credentials.service_account_email}")
        else:
             logging.info("ADC Check: Service Account Email attribute not found.")

        # <<< PERCOBAAN: Set Quota Project ID jika kosong >>>
        quota_project_set_attempted = False
        if project_id and hasattr(credentials, '_quota_project_id') and not credentials._quota_project_id:
            logging.warning(f"ADC Check: Quota project ID was missing on ADC credentials. Attempting to set it explicitly to: {project_id}")
            quota_project_set_attempted = True
            try:
                # Mengatur atribut internal (gunakan dengan hati-hati)
                setattr(credentials, '_quota_project_id', project_id)
                logging.info("ADC Check: Successfully set _quota_project_id attribute.")
            except Exception as set_attr_err:
                logging.error(f"ADC Check: Failed to set _quota_project_id attribute: {set_attr_err}")
        # <<< AKHIR PERCOBAAN >>>

        # Lakukan validasi eksplisit dan log hasilnya (setelah potensi modifikasi)
        is_valid = credentials and credentials.valid
        valid_check_message = f"ADC Check: Credentials valid check result (after potential quota project set: {quota_project_set_attempted}): {is_valid}"
        logging.info(valid_check_message)


        if is_valid:
            # Coba dapatkan token (ini akan memicu refresh jika perlu)
            try:
                # Gunakan Request yang sudah diimpor
                logging.info("ADC Check: Attempting credentials.refresh()...")
                credentials.refresh(Request())
                logging.info("ADC Check: Token refresh/acquisition successful.")
                return f"ADC Check OK. Valid: {is_valid}. Project: {project_id}", 200
            except Exception as refresh_err:
                logging.error(f"ADC Check: Error during token refresh/acquisition: {refresh_err}", exc_info=True)
                return f"ADC Check Found, Valid Check: {is_valid}, BUT Refresh/Acquire Failed: {refresh_err}", 500
        else:
            logging.error("ADC Check: Credentials found but determined invalid.")
            # Coba berikan detail lebih jika ada error internal
            error_detail = ""
            if hasattr(credentials, '_quota_project_id') and not credentials._quota_project_id:
                 error_detail += " Quota project ID might be missing (even after set attempt)."
            # Tambahkan pemeriksaan lain jika perlu
            return f"ADC Check Found, BUT Invalid. Valid Check: {is_valid}. Project: {project_id}.{error_detail}", 500

    except google.auth.exceptions.DefaultCredentialsError as adc_err:
        logging.error(f"ADC Check: ADC not configured or default failed: {adc_err}", exc_info=True)
        return f"ADC Check FAILED to find default credentials: {adc_err}", 500
    except Exception as e:
        logging.error(f"ADC Check: Unexpected error: {e}", exc_info=True)
        return f"ADC Check FAILED with unexpected error: {e}", 500

if __name__ == '__main__':
    # Ambil port dari environment variable PORT, default ke 8080 jika tidak ada
    port = int(os.environ.get('PORT', 8080))
    # Jalankan tanpa debug mode untuk lingkungan mirip produksi
    # Gunakan host '0.0.0.0' agar bisa diakses dari luar container
    app.run(host='0.0.0.0', port=port, debug=False)

The cloud run is running, but when I try to access and try the apps it shows : ADC Check Found, BUT Invalid. Valid Check: False. Project: xxx

This is the log 2025-05-04 21:30:15.692 ICT GET500210 B7 msChrome 135 https://"my link apps"/check-adc 2025-05-04 21:30:15.696 ICT 2025-05-04 14:30:15,696 - INFO - ADC Check: Attempting google.auth.default()... 2025-05-04 21:30:15.700 ICT 2025-05-04 14:30:15,700 - INFO - ADC Check: Found credentials. Project ID: "my project", Type: <class 'google.auth.compute_engine.credentials.Credentials'> 2025-05-04 21:30:15.700 ICT 2025-05-04 14:30:15,700 - INFO - ADC Check: Service Account Email: default 2025-05-04 21:30:15.700 ICT 2025-05-04 14:30:15,700 - WARNING - ADC Check: Quota project ID was missing on ADC credentials. Attempting to set it explicitly to: "my project" 2025-05-04 21:30:15.700 ICT 2025-05-04 14:30:15,700 - INFO - ADC Check: Successfully set _quota_project_id attribute. 2025-05-04 21:30:15.700 ICT 2025-05-04 14:30:15,700 - INFO - ADC Check: Credentials valid check result (after potential quota project set: True): False 2025-05-04 21:30:15.700 ICT 2025-05-04 14:30:15,700 - ERROR - ADC Check: Credentials found but determined invalid.

Could you give me solution for this problem? I have grant Editor permission for the service account, share the gdrive folder to the service account emails, and try to read the documentation, but there is no answer.

This is the screenshoot for the permission of the service account: enter image description here

1 Answer 1

0

After trying to deploy locallly first and via cloud run, I have fixed the issue. It is just need to refresh the ADC credentials, due to the validity status false (expired).
Here is the code :

def _get_credentials():
    """
    Mendapatkan kredensial menggunakan Application Default Credentials (ADC).
    Di lingkungan Cloud Run, ini akan menggunakan service account yang terkait.
    """
    try:
        logging.info("Attempting to get credentials using google.auth.default()...")
        credentials, project_id = google.auth.default(scopes=SCOPES)
        logging.info(f"Successfully obtained ADC. Project ID: {project_id}, Type: {type(credentials)}")

        if hasattr(credentials, 'service_account_email'):
             logging.info(f"ADC: Service Account Email (from ADC): {credentials.service_account_email}")
        else:
             logging.info("ADC: Service Account Email attribute not found (expected for user creds from gcloud auth application-default login, not typical for SA on Cloud Run).")

        # Periksa validitas awal ADC
        adc_initially_valid = credentials and credentials.valid
        logging.info(f"ADC: Credentials initial valid check: {adc_initially_valid}")

        # Jika kredensial ADC awalnya tidak valid, coba lakukan refresh.
        # Ini akan memastikan upaya refresh selalu dilakukan jika ADC tidak langsung valid.
        if not adc_initially_valid and credentials:
            logging.warning("ADC: Credentials initially invalid. Attempting refresh...")
            try:
                credentials.refresh(Request())
                logging.info("ADC: Credentials refresh attempt completed.") # Log setelah refresh dicoba
            except Exception as adc_refresh_err:
                logging.error(f"ADC: Failed during credentials refresh attempt: {adc_refresh_err}", exc_info=True)
                # Biarkan credentials.valid akan diperiksa di bawah
        
        if not credentials.valid:
            # Ini seharusnya tidak terjadi di Cloud Run dengan SA yang dikonfigurasi dengan benar.
            logging.warning("Credentials obtained but are not marked as valid after potential refresh. This might indicate an issue with the ADC setup or permissions.")
            # Anda bisa memutuskan untuk mengembalikan None di sini jika validitas sangat krusial
            # return None

        return credentials
    except google.auth.exceptions.DefaultCredentialsError as e:
        logging.error(f"Failed to get Application Default Credentials: {e}", exc_info=True)
        return None
    except Exception as e:
        logging.error(f"An unexpected error occurred while getting credentials: {e}", exc_info=True)
        return None

@app.route('/check-adc')
def perform_adc_check():
    """Melakukan tes untuk mendapatkan ADC."""
    logging.info("Performing ADC check...")
    credentials = _get_credentials()
    if credentials and credentials.valid:
        logging.info("ADC check successful: Credentials obtained and are valid.")
        project_id_msg = f"Project ID from ADC: {credentials.project_id}" if hasattr(credentials, 'project_id') else "Project ID not directly available on credentials object."
        return f"ADC Check OK: Credentials obtained and are valid. {project_id_msg}", 200
    elif credentials and not credentials.valid:
        logging.error("ADC check found credentials, but they are NOT valid.")
        return "ADC Check FAILED: Credentials obtained but are not valid.", 500
    else:
        logging.error("ADC check FAILED: Could not obtain credentials.")
        return "ADC Check FAILED: Could not obtain credentials.", 500

Thanks

Sign up to request clarification or add additional context in comments.

2 Comments

The line of code credentials, project_id = google.auth.default(scopes=SCOPES) is a red flag in my opinion. I think you're doing more work than you need to. Google's SDKs are meant to work without manually setting up auth as long as you have set up the service account on Cloud Run correctly. You should be able to just make a service account, assign it the needed role (through either the GCP IAM & Admin screen or the Google Drive app via "share" like you would share to a user), and then select that service account to be the Cloud Run service's identity as you deploy it.
Also worth noting that even if you're running your code locally instead of Cloud Run, you still don't need your code to manually auth like that. As long as you create a key for your service account, download it to your computer, and ensure the environment variable GOOGLE_APPLICATION_CREDENTIALS is set to the path to that file as you start your program locally, it will auth correctly. ADC is designed so that you have zero changes to make to your code depending on which environment it's running in for it to be authed with Google. It will be automatic or that key file path env var.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.