Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elastic\Transport\NodePool\Node::__construct(): Argument #1 ($host) must be of type string, array given #1214

Open
xiaomlove opened this issue Apr 3, 2022 · 11 comments

Comments

@xiaomlove
Copy link

Code snippet

$hosts=  [
    [
        'host' => '',
        'port' => '',
        'scheme' => '',
        'user' => '',
        'pass' => '',
    ]
],
$builder = ClientBuilder::create()->setHosts($hosts);
$builder->build();

Error

 TypeError 

  Elastic\Transport\NodePool\Node::__construct(): Argument #1 ($host) must be of type string, array given, called in /www/wwwroot/nexusphp/vendor/elastic/transport/src/NodePool/SimpleNodePool.php on line 52

  at vendor/elastic/transport/src/NodePool/Node.php:28
     24▕ {
     25▕     protected UriInterface $uri;
     26▕     protected bool $alive = true;
     27▕ 
  ➜  28▕     public function __construct(string $host)
     29▕     {
     30▕         if (substr($host, 0, 5) !== 'http:' && substr($host, 0, 6) !== 'https:') {
     31▕             $host = sprintf("http://%s", $host);
     32▕         }

  1   vendor/elastic/transport/src/NodePool/SimpleNodePool.php:52
      Elastic\Transport\NodePool\Node::__construct()

  2   vendor/elastic/transport/src/TransportBuilder.php:110
      Elastic\Transport\NodePool\SimpleNodePool::setHosts()

  3   vendor/elasticsearch/elasticsearch/src/ClientBuilder.php:366
      Elastic\Transport\TransportBuilder::build()

Env

PHP: 8.0.16
elasticsearch/elasticsearch v8.0.1
elastic/transport v8.0.1

@yao3060
Copy link

yao3060 commented Apr 4, 2022

I have the similar issue: "No alive nodes."

I can retrieve the nomal response from GuzzleHttp\Client but the Elastic\Elasticsearch\ClientBuilder

Here is my PHP Code

<?php

error_reporting(E_ALL);
ini_set("display_errors", 1);

use Elastic\Elasticsearch\ClientBuilder;
use Symfony\Component\HttpClient\Psr18Client;

require __DIR__  . '/vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://es01:9200', [
    'auth' => ['elastic', getenv('ELASTIC_PASSWORD')],
    "verify" => __DIR__ . "/es01.crt"
]);

echo  '<pre>';
print_r(json_decode($res->getBody()->getContents()));
echo  '</pre>';
echo '<hr />';

echo  '<pre>';
$client = ClientBuilder::create()
    ->setHosts(['https://es01:9200'])
    ->setBasicAuthentication('elastic', getenv('ELASTIC_PASSWORD'))
    ->setCABundle(__DIR__ . '/es01.crt')
    ->setHttpClient(new Psr18Client)
    ->build();

$client->info();

here is the output:

stdClass Object
(
    [name] => es01
    [cluster_name] => docker-cluster
    [cluster_uuid] => qW-gk-YaSTavPGaUTWO0cQ
    [version] => stdClass Object
        (
            [number] => 8.1.2
            [build_flavor] => default
            [build_type] => docker
            [build_hash] => 31df9689e80bad366ac20176aa7f2371ea5eb4c1
            [build_date] => 2022-03-29T21:18:59.991429448Z
            [build_snapshot] => 
            [lucene_version] => 9.0.0
            [minimum_wire_compatibility_version] => 7.17.0
            [minimum_index_compatibility_version] => 7.0.0
        )

    [tagline] => You Know, for Search
)
Fatal error: Uncaught Elastic\Transport\Exception\NoNodeAvailableException: No alive nodes. All the 1 nodes seem to be down. in /var/www/html/vendor/elastic/transport/src/NodePool/SimpleNodePool.php:77
Stack trace:
#0 /var/www/html/vendor/elastic/transport/src/Transport.php(292): Elastic\Transport\NodePool\SimpleNodePool->nextNode()
#1 /var/www/html/vendor/elasticsearch/elasticsearch/src/Client.php(164): Elastic\Transport\Transport->sendRequest(Object(GuzzleHttp\Psr7\Request))
#2 /var/www/html/vendor/elasticsearch/elasticsearch/src/Traits/ClientEndpointsTrait.php(858): Elastic\Elasticsearch\Client->sendRequest(Object(GuzzleHttp\Psr7\Request))
#3 /var/www/html/search.php(32): Elastic\Elasticsearch\Client->info()
#4 {main}
  thrown in /var/www/html/vendor/elastic/transport/src/NodePool/SimpleNodePool.php on line 77

My Env is

  • php8.8
  • "elasticsearch/elasticsearch": "^8.0"
  • docker

my docker-compose.yaml

version: "3.1"

services:
  php:
    image: php:8.0-apache
    container_name: ${COMPOSE_PROJECT_NAME}_php
    env_file: .env
    restart: always
    ports:
      - "${WORDPRESS_HTTP_PORT}:80"
    depends_on:
      - cache
    volumes:
      - ./src:/var/www/html
    networks:
      - default
      - base_share_network

  cache:
    image: redis:alpine
    container_name: ${COMPOSE_PROJECT_NAME}_cache
    restart: always

  es_setup:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - ./tmp/certs:/usr/share/elasticsearch/config/certs
    user: "0"
    command: >
      bash -c '
        if [ x${ELASTIC_PASSWORD} == x ]; then
          echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
          exit 1;
        elif [ x${KIBANA_PASSWORD} == x ]; then
          echo "Set the KIBANA_PASSWORD environment variable in the .env file";
          exit 1;
        fi;
        if [ ! -f certs/ca.zip ]; then
          echo "Creating CA";
          bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
          unzip config/certs/ca.zip -d config/certs;
        fi;
        if [ ! -f certs/certs.zip ]; then
          echo "Creating certs";
          echo -ne \
          "instances:\n"\
          "  - name: es01\n"\
          "    dns:\n"\
          "      - es01\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          > config/certs/instances.yml;
          bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
          unzip config/certs/certs.zip -d config/certs;
        fi;
        echo "Setting file permissions"
        chown -R root:root config/certs;
        find . -type d -exec chmod 750 \{\} \;;
        find . -type f -exec chmod 640 \{\} \;;
        echo "Waiting for Elasticsearch availability";
        until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
        echo "Setting kibana_system password";
        until curl -s -X POST --cacert config/certs/ca/ca.crt -u elastic:${ELASTIC_PASSWORD} -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
        echo "All done!";
      '
    healthcheck:
      test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"]
      interval: 1s
      timeout: 5s
      retries: 120

  es01:
    depends_on:
      es_setup:
        condition: service_healthy
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - ./tmp/certs:/usr/share/elasticsearch/config/certs
      - ./tmp/esdata01:/usr/share/elasticsearch/data
    ports:
      - ${ES_PORT}:9200
    environment:
      - node.name=es01
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es01/es01.key
      - xpack.security.http.ssl.certificate=certs/es01/es01.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.http.ssl.verification_mode=certificate
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es01/es01.key
      - xpack.security.transport.ssl.certificate=certs/es01/es01.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - xpack.license.self_generated.type=${LICENSE}
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

  kibana:
    depends_on:
      es01:
        condition: service_healthy
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    volumes:
      - ./tmp/certs:/usr/share/kibana/config/certs
      - ./tmp/kibanadata:/usr/share/kibana/data
    ports:
      - ${KIBANA_PORT}:5601
    environment:
      - SERVERNAME=kibana
      - ELASTICSEARCH_HOSTS=https://es01:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
    mem_limit: ${MEM_LIMIT}
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

networks:
  base_share_network:
    external: true

@sebastiaanluca
Copy link

sebastiaanluca commented Apr 5, 2022

Same error here, same structure.

            'hosts' => [
                [
                    'scheme' => env('ELASTICSEARCH_SCHEME'),
                    'host' => env('ELASTICSEARCH_HOST', 'localhost'),
                    'port' => env('ELASTICSEARCH_PORT', 9200),
                    'path' => env('ELASTICSEARCH_PATH', null),
                    'user' => env('ELASTICSEARCH_USER'),
                    'pass' => env('ELASTICSEARCH_PASS'),
                ],
            ],

PHP 8.1.4
elastic/transport v8.0.1
elasticsearch/elasticsearch v8.1.2

In the mean time, it does accept simple strings. Here's one built from the above config file, passing in the first item of hosts to $host (I just needed the one). sprintf or simple string concatenation could've worked too, but strtr allows me to type out the format for better readability.

strtr(
    'scheme://user:pass@host:port/path',
    $host,
);

@ezimuel
Copy link
Contributor

ezimuel commented Apr 11, 2022

@xiaomlove , @yao3060 , @sebastiaanluca the new major release of elasticsearch-php v8 does not support anymore the hosts settings using the following associative array syntax:

'hosts' => [
                  [
                      'scheme' => 'https',
                      'host' => 'localhost',
                      'port' => 9200,
                      'user' => 'my-user',
                      'pass' => 'my-pass'
                  ],
              ],

In order to set this configuration you can use an array of strings, as follows:

'hosts' => [ 'https://localhost:9200'],
'basicAuthentication' => ['my-user', 'my-pass']

or specify everything on the URL, as follows:

'hosts' => [ 'https://my-user:my-pass@localhost:9200'],

We should add this BC breaks in the list reported in CHANGELOG.md.

@sebastiaanluca
Copy link

The "everything as one URL" doesn't seem to work here (using the ClientBuilder). It ignores the user and password, can only set it using setBasicAuthentication(). So configuring different nodes with different credentials is not possible.

@ezimuel
Copy link
Contributor

ezimuel commented Apr 12, 2022

@sebastiaanluca what is the URL are you using? Can you var_dump the $response object?

So configuring different nodes with different credentials is not possible.

This is an interesting feedback, I didn't think about it. I'll try to find a solution for this use case, thanks!

@chongshengdz
Copy link

has the problem resolved yet?

@mgsmus
Copy link

mgsmus commented May 29, 2022

@ezimuel @xiaomlove @yao3060 @sebastiaanluca @chongshengdz

After some of effort, I successfully connected as follows (elasticsearch/elasticsearch v8.2.2):

use GuzzleHttp\Client;

$client = ClientBuilder::create()
    ->setHttpClient(new Client(['verify' => false]))
    ->setHosts(['https://localhost:9200'])
    ->setBasicAuthentication('elastic', 'my-password')
    ->build();

return $client->info()->asArray();
{
  "name": "f9aa1d7717c3",
  "cluster_name": "docker-cluster",
  "cluster_uuid": "k-D273EUQySfh_srkCY-wg",
  "version": {
    "number": "8.2.2",
    "build_flavor": "default",
    "build_type": "docker",
    "build_hash": "9876968ef3c745186b94fdabd4483e01499224ef",
    "build_date": "2022-05-25T15:47:06.259735307Z",
    "build_snapshot": false,
    "lucene_version": "9.1.0",
    "minimum_wire_compatibility_version": "7.17.0",
    "minimum_index_compatibility_version": "7.0.0"
  },
  "tagline": "You Know, for Search"
}

I followed these steps for installation in Self-managed tab:
https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html#run-elasticsearch

@ezimuel
Copy link
Contributor

ezimuel commented Jun 29, 2022

@mgsmus the solution that you reported is not really secure since you are switching off the TLS check about the certificate. Anyway, this can be a workaround only if your PHP runs on the same localhost. I do not recommend to switch off the TLS certificate validation outside localhost.

@ezimuel
Copy link
Contributor

ezimuel commented Jun 29, 2022

So configuring different nodes with different credentials is not possible.

@chongshengdz no, we don't have a solution at the moment.

@michaelarnauts
Copy link

The "everything as one URL" doesn't seem to work here (using the ClientBuilder). It ignores the user and password, can only set it using setBasicAuthentication(). So configuring different nodes with different credentials is not possible.

It took me a while to figure this out... I think this used to work in earlier versions.

I solved this by creating the client like this:

        // Extract host and port from $this->host
        $host = parse_url($this->host);

        // Build the client
        $this->client = ClientBuilder::create()
            ->setHosts([$host['host'] . ':' . $host['port']])
            ->setBasicAuthentication($host['user'], $host['pass'])
            ->build();

@sh3bang
Copy link

sh3bang commented Oct 19, 2023

Important note: you cant set path anymore, i.e. no elastic hosts like http://foo.com/search

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants