-2

How does one set the Project ID for the C++ Google Cloud Storage client?

The example code below throws an exception:

Permanent error SignBlob: Received HTTP status code: 404

If I set the environment variable, the code works.

set GOOGLE_CLOUD_PROJECT=example_project_id

My goal is to not use environment variables. The Project ID is retrieved from a JSON configuration file.

#include "google/cloud/storage/client.h"

int main()
{
    namespace gcs = ::google::cloud::storage;
    using ::google::cloud::StatusOr;

    std::string project_id = "example_project_id";
    std::string bucket_name = "example_bucket_name";
    std::string object_name = "example_object_name";

    auto client = gcs::Client(
        google::cloud::Options{}
            .set<gcs::ProjectIdOption>(project_id)
    );

    StatusOr<std::string> signed_url = client.CreateV4SignedUrl(
        "GET",
        std::move(bucket_name),
        std::move(object_name),
        gcs::SignedUrlDuration(std::chrono::minutes(15)));

    if (!signed_url)
    {
        std::cout << signed_url.status().message() << "\n";
        throw std::move(signed_url).status();
    }

    std::cout
        << "The signed url is: " << *signed_url << "\n\n"
        << "You can use this URL with any user agent, for example:\n"
        << "curl '" << *signed_url << "'\n";
}

Update 2023-05-27 after @coryan's answer.

If I remove setting the client option google::cloud::Options{}.set<gcs::ProjectIdOption>(project_id), clear the environment variable GOOGLE_CLOUD_PROJECT, and set the SigningAccount to a valid service account email address that I have IAM permission to use, the following code works.

However, that does not explain why <gcs::ProjectIdOption> does not work in the above example. While setting the environment variable GOOGLE_CLOUD_PROJECT does enable the above example to work.

#include "google/cloud/storage/client.h"

void test1()
{
    namespace gcs = ::google::cloud::storage;
    using ::google::cloud::StatusOr;

    std::string project_id = "example_project_id";
    std::string bucket_name = "example_bucket_name";
    std::string object_name = "example_object_name";
    std::string sa_email = "example_service_account_email";

    auto client = gcs::Client();

    StatusOr<std::string> signed_url = client.CreateV4SignedUrl(
        "GET",
        std::move(bucket_name),
        std::move(object_name),
        gcs::SignedUrlDuration(std::chrono::minutes(15)),
        gcs::SigningAccount(sa_email)
    );

    if (!signed_url)
    {
        std::cout << signed_url.status().message() << "\n";
        throw std::move(signed_url).status();
    }

    std::cout << "The signed url is: " << *signed_url << "\n\n";
}
John Hanley
  • 74,467
  • 6
  • 95
  • 159
  • Hey John. Do you want to get the project ID from your default (and personal) credential? If so, did you try to add a default quota project? `gcloud auth application-default set-quota-project XXXXXX` – guillaume blaquiere May 27 '23 at 09:07
  • @guillaumeblaquiere - No, I want to set the Project ID in the c++ storage client. Yes, I have quota project correctly set. Currently, I have to set the environment variable `GOOGLE_CLOUD_PROJECT` as the client is ignoring `google::cloud::Options{}.set(project_id)` and I do not know why. – John Hanley May 27 '23 at 09:18
  • I'm very bad at C++. But looking to the doc, you could use common option. Did you try this one? https://googleapis.dev/cpp/google-cloud-common/2.10.1/structgoogle_1_1cloud_1_1UserProjectOption.html – guillaume blaquiere May 27 '23 at 09:30
  • @guillaumeblaquiere - thank you but that is not the correct setting. I am trying to tell the c++ storage client the Project ID, and besides setting the environment variable, I cannot find the technique. There is an options array, but the client is not picking up that option - `gcs::ProjectIdOption`. – John Hanley May 27 '23 at 09:35
  • 1
    I filed https://github.com/googleapis/google-cloud-cpp/issues/11742 to fix the inconsistency around `ProjectIdOption`. – coryan May 30 '23 at 12:43

1 Answers1

1

Why do you think the project id is what is missing here? I suspect this has nothing to do with the ProjectIdOption but with a missing default value for the signing service account email. I have filed #11740 because the error message is not useful at all.

When the credentials are obtained from a service account key file, the client library create the signed URL locally, using the private key from the key file.

When the credentials are not a service account key file (user accounts, the GCE metadata service, whatever), the client library can use IAM signBlob to sign the blob.

In the latter case it needs the service account email of the request to sign. For some credential types there is a default email associated with the credentials. But sometimes there is none, or the value cannot be retrieved, for example, the metadata service may be unresponsive.

Without a default value for the service account email, the client library sends a request to IAM signBlob with an empty email address. This results in a 404 error without additional details.

The application can provide a service account email, as shown in the CreateV4SignedURL() examples:

https://googleapis.dev/cpp/google-cloud-storage/latest/classgoogle_1_1cloud_1_1storage_1_1Client.html#a4600b6b5123e51283168ca2cf53926bf

If that is not the problem, please let us know and we will try to get this fixed.

coryan
  • 723
  • 3
  • 5
  • If I set `GOOGLE_CLOUD_PROJECT`, my example works. The current authentication ID is user credentials. I searched the Google library source code and I cannot find where the code reads back ``, so I think that is an unimplemented feature. I also ran the code in the Visual Studio debugger, and I could not see it processed either. – John Hanley May 27 '23 at 17:05
  • I logged the service account that the SDK is using: `service- – John Hanley May 27 '23 at 17:09