googleapis / google-cloud-php

Google Cloud Client Library for PHP
https://cloud.google.com/php/docs/reference
Apache License 2.0
1.1k stars 436 forks source link

Fatal Error in RequestHandler class with emulator without GRPC extension #7187

Closed mrVrAlex closed 3 months ago

mrVrAlex commented 8 months ago

Hi

Any Client API (for example PubSub) can not work with emulator without grpc extension. When we put in clientConfig 'emulatorHost' & 'hasEmulator' keys (PubSub client set it when exist PUBSUB_EMULATOR_HOST env) then it config passing to RequestHandler constructor and using here https://github.com/googleapis/google-cloud-php/blob/85748387bed4dd1dece6dee255f9efe801f2dbd2/Core/src/RequestHandler.php#L71 And there call $this->emulatorGapicConfig function which part of EmulatorTrait https://github.com/googleapis/google-cloud-php/blob/main/Core/src/EmulatorTrait.php#L44

And without any conditions code try build array config with call \Grpc\ChannelCredentials::createInsecure() which part of GRPC extension.

In result:

Fatal error: Uncaught Error: Class "Grpc\ChannelCredentials" not found in /app/vendor/google/cloud-core/src/EmulatorTrait.php:44
Stack trace:
#0 /app/vendor/google/cloud-core/src/RequestHandler.php(72): Google\Cloud\Core\RequestHandler->emulatorGapicConfig('pubsub:8681')
#1 /app/vendor/google/cloud-pubsub/src/PubSubClient.php(215): Google\Cloud\Core\RequestHandler->__construct(Object(Google\ApiCore\Serializer), Array, Array)

Another (maybe related) issue when try avoid Fatal error with stub class & method for ChannelCredentials and put in config ['transport' => 'resr'] then I got different error:

In CurlFactory.php line 210:

  [GuzzleHttp\Exception\ConnectException]                                      
  cURL error 35: OpenSSL/3.1.4: error:0A00010B:SSL routines::wrong version nu  
  mber (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://p  
  ubsub:8681/v1/projects/emulator-project/topics/local.esbNew:publish?%24alt=  
  json%3Benum-encoding%3Dint  

Because client by some reason choose HTTPS schema instead of HTTP and emulator don't work with SSL...

So this is bug?

Environment details

Steps to reproduce

  1. take official docker php image
  2. run emulator
  3. run code in example

Code example

putenv('PUBSUB_EMULATOR_HOST=localhost:8900');
$pubsub = new PubSubClient(['projectId' => 'my-project']);
bshaffer commented 7 months ago

Hello! Thank you for commenting on this. Does it work when you install the grpc extension for PHP? If so, I suggest doing this.

In the meantime, if the emulator requires gRPC, we should wrap the EmulatorTrait to check if grpc is installed, and throw an exception if it isn't.

mrVrAlex commented 7 months ago

Yes with gRPC extension it is worked properly. But if google/cloud-pubsub v2.x and others (which depends on new EmulatorTrait) not works with emulators via http protocol - then it is major breaking changes? Maybe this should reflect in changelog & documentation? (BTW: in our case we can not use grpc extension in PHP because it conflict with swoole / openswoole extension)

bshaffer commented 6 months ago

(BTW: in our case we can not use grpc extension in PHP because it conflict with swoole / openswoole extension)

Can you elaborate more on this? This is news to me, and it's something we should file as a bug in the grpc repo to get fixed.

bshaffer commented 6 months ago

@mrVrAlex

it is major breaking changes?

No, because the emulators have never worked with REST, only with gRPC. To confirm this, I checked out v0.233.0 (which is the same as google/cloud-pubsub:v1.51.1, the last release before 2.0), and used composer install --prefer-lowest, and received the same error you have here. So AFAICT, REST has never worked in the emulator... there does not seem to be any regression here.

We could open this as a feature request against gcloud, but otherwise this is out of our (the client library team's) hands.

pulzarraider commented 5 months ago

@bshaffer afaik the grpc extension was always optional. We were using the pubsub emulator on local machines without grpc and without any credentials file with PubSub v1, but after upgrading to PubSub v2 we can't because we get error:

In CredentialsWrapper.php line 319:

  Could not construct ApplicationDefaultCredentials

In ApplicationDefaultCredentials.php line 201:

  Your default credentials were not found. To set up Application Default Credentials, see https://c
  loud.google.com/docs/authentication/external/set-up-adc
pulzarraider commented 5 months ago

I have tried some half-empty configuration to avoid the first exceptions, but I got the same error, as it is described in this ticket:

[Symfony\Component\ErrorHandler\Error\ClassNotFoundError]
  Attempted to load class "ChannelCredentials" from namespace "Grpc".
  Did you forget a "use" statement for another namespace?

It would be great if we could use version 2 in the same way with pubsub emulator without the mandatory grpc extension.

kynx commented 3 months ago

I ended up creating a stub file and requiring that to get this working - grpc.php.stub:

<?php

/**
 * PubSubClient assumes that if the PUBSUB_EMULATOR_HOST env var is set that the grpc extension is installed :(
 */

declare(strict_types=1);

namespace Grpc;

if (! class_exists('Grpc\ChannelCredentials')) {
    class ChannelCredentials {
        public static function createInsecure(): null
        {
            return null;
        }
    }
}

Then ensure the PUBSUB_EMULATOR_HOST environment variable is set and instantiate PubSubClient with something like:

        require_once 'grpc.php.stub';

        return new PubSubClient([
            'projectId'         => 'emulator-project',
            'credentialsConfig' => [
                'keyFile'     => [
                    "client_id"     => "fake-fake-fake.apps.googleusercontent.com",
                    "client_secret" => "fake-fake-fake",
                    "refresh_token" => "fake-fake-fake",
                    "type"          => "authorized_user"
                ],
            ],
        ]);

(Note that the docblock on the constructor is wrong - the keyFile stuff needs to be under credentialsConfig).

It'd be great to get grpc working without segfaults on swoole / openswoole. No idea where to start on that one though.

ollosh commented 2 months ago

@pulzarraider I'm stuck with the same issue from your comment https://github.com/googleapis/google-cloud-php/issues/7187#issuecomment-2157990748. If I understood the discussion it boils down to the missing grpc extension even if it wasn't necessary in pubsub.

I presume that once/if your PR https://github.com/googleapis/gax-php/pull/582 gets merged, this should work as in the v1 without any credentials?

kynx commented 2 months ago

@ollosh It should be working if you update to the latest version of google/cloud-core now that https://github.com/googleapis/google-cloud-php/pull/7588 is merged and released. See the comment on that PR to work around it using https instead of http URLs to access the emulator.

pulzarraider commented 2 months ago

@ollosh even with my MR https://github.com/googleapis/gax-php/pull/582 you will still need credentials. I managed to get it working using these fake credentials for emulator:

{"project_id":"emulator-project","client_id":"emulator","client_secret":"emulator","refresh_token":"emulator","type":"authorized_user"}
ollosh commented 2 months ago

@pulzarraider thank you! I've managed to get it working with that and with the changes from https://github.com/googleapis/google-cloud-php/pull/7588. If I understand it correctly, those changes from that PR will become obsolete if https://github.com/googleapis/gax-php/pull/582 gets merged?

pulzarraider commented 2 months ago

@ollosh yes. Code from https://github.com/googleapis/gax-php/pull/582 allows Pub/Sub client to work with emulator without requiring significant modifications to the client to get the http urls mentioned in #7588.

ollosh commented 2 months ago

Amazing, thx for all the info! :crossed_fingers: that it gets merged soon!