├── .github ├── FUNDING.yml └── workflows │ └── unit-tests.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src ├── GcpMetadata.php └── GcpMetadata └── Error.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jeromegamez 2 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | unit-tests: 8 | name: PHP ${{matrix.php}}, ${{matrix.stability}} deps 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | php: [7.4, 8.0, 8.1] 14 | stability: [lowest, stable] 15 | 16 | env: 17 | extensions: dom, mbstring, xml 18 | key: cache-v1 19 | 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v3 23 | 24 | - name: Setup cache environment 25 | id: cache-env 26 | uses: shivammathur/cache-extensions@v1 27 | with: 28 | php-version: ${{ matrix.php }} 29 | extensions: ${{ env.extensions }} 30 | key: ${{ env.key }} 31 | 32 | - name: Cache extensions 33 | uses: actions/cache@v3 34 | with: 35 | path: ${{ steps.cache-env.outputs.dir }} 36 | key: ${{ steps.cache-env.outputs.key }} 37 | restore-keys: ${{ steps.cache-env.outputs.key }} 38 | 39 | - name: Setup PHP 40 | uses: shivammathur/setup-php@v2 41 | with: 42 | php-version: ${{ matrix.php }} 43 | extensions: ${{ env.extensions }} 44 | tools: composer 45 | coverage: none 46 | 47 | - name: Setup problem matchers for PHP 48 | run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" 49 | 50 | - name: Get Composer Cache Directory 51 | id: composer-cache 52 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 53 | 54 | - name: Cache dependencies 55 | uses: actions/cache@v3 56 | with: 57 | path: ${{steps.composer-cache.outputs.dir}} 58 | key: ${{matrix.php}}-composer-${{matrix.stability}}-${{hashFiles('**/composer.json')}} 59 | restore-keys: ${{matrix.php}}-${{matrix.stability}}-composer- 60 | 61 | - name: Install dependencies 62 | run: composer update --prefer-${{matrix.stability}} --prefer-dist --no-interaction --no-suggest --no-progress 63 | 64 | - name: Setup Problem Matchers for PHPUnit 65 | run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 66 | 67 | - name: Execute tests 68 | run: vendor/bin/phpunit 69 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 1.3.2 - 2022-06-22 4 | 5 | * Raised minimum version of Guzzle to address [CVE-2022-31090](https://github.com/advisories/GHSA-25mq-v84q-4j7r) 6 | and [CVE-2022-31091](https://github.com/advisories/GHSA-q559-8m2m-g699) 7 | 8 | ## 1.3.1 - 2022-06-13 9 | 10 | * Raised minimum version of Guzzle to address [CVE-2022-31042](https://github.com/advisories/GHSA-f2wf-25xc-69c9) 11 | 12 | ## 1.3.0 - 2022-05-26 13 | 14 | * Dropped support for PHP <7.4 15 | * Raised minimum version of Guzzle to address [CVE-2022-29248](https://github.com/advisories/GHSA-cwmx-hcrq-mhc3) 16 | 17 | ## 1.2.0 - 2021-05-26 18 | 19 | * Added support for Guzzle 7 20 | * Raised minimum required Guzzle 6 version from 6.0 to 6.3.3 21 | * Added support for PHP 8 22 | * Dropped support for PHP <7.2 23 | 24 | ## 1.1.0 - 2020-03-19 25 | 26 | * Use IP address instead of hostname for the Google Metadata service to avoid slow requests when not on Compute Engine 27 | * The availability status of the Metadata Service is now cached to save requests 28 | * Configured the underlying HTTP client to be less resilient, but faster 29 | 30 | ## 1.0.1 - 2018-07-28 31 | 32 | ### Bugfixes 33 | 34 | * Changed `Kreait\Error` to `Kreait\GcpMetadata\Error` (noone uses this library yet, so I'll pretend it's a non-breaking change :) 35 | 36 | ## 1.0 - 2018-07-27 37 | 38 | * Initial release 39 | 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Jérôme Gamez, https://github.com/jeromegamez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GCP Metadata 2 | 3 | > Get the metadata from a Google Cloud Platform environment. 4 | 5 | [![Current version](https://img.shields.io/packagist/v/kreait/gcp-metadata.svg)](https://packagist.org/packages/kreait/gcp-metadata) 6 | [![Supported PHP version](https://img.shields.io/packagist/php-v/kreait/gcp-metadata.svg)]() 7 | [![GitHub license](https://img.shields.io/github/license/kreait/gcp-metadata-php.svg)](https://github.com/kreait/gcp-metadata-php/blob/main/LICENSE) 8 | [![Unit Tests](https://github.com/kreait/gcp-metadata-php/workflows/Unit%20Tests/badge.svg)](https://github.com/kreait/gcp-metadata-php/actions) 9 | [![Sponsor](https://img.shields.io/static/v1?logo=GitHub&label=Sponsor&message=%E2%9D%A4&color=ff69b4)](https://github.com/sponsors/jeromegamez) 10 | 11 | ```bash 12 | $ composer install kreait/gcp-metadata 13 | ``` 14 | 15 | ```php 16 | use Kreait\GcpMetadata; 17 | 18 | $metadata = new GcpMetadata(); 19 | ``` 20 | 21 | #### Check if the metadata server is available 22 | 23 | ```php 24 | $isAvailable = $metadata->isAvailable(); 25 | ``` 26 | 27 | #### Get all available instance properties 28 | 29 | ```php 30 | $data = $metadata->instance(); 31 | ``` 32 | 33 | #### Get all available project properties 34 | 35 | ```php 36 | $data = $metadata->project(); 37 | ``` 38 | 39 | #### Access a specific property 40 | 41 | ```php 42 | $data = $metadata->instance('hostname'); 43 | ``` 44 | 45 | #### Wrap queries in a try/catch block if you don't check for availability 46 | 47 | ```php 48 | use Kreait\GcpMetadata; 49 | 50 | $metadata = new GcpMetadata(); 51 | 52 | if ($metadata->isAvailable()) { 53 | echo $metadata->instance('hostname'); 54 | } 55 | 56 | try { 57 | echo $metadata->instance('hostname'); 58 | } catch (GcpMetadata\Error $e) { 59 | echo $e->getMessage(); 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kreait/gcp-metadata", 3 | "description": "Get the metadata from a Google Cloud Platform environment.", 4 | "keywords": ["google", "google-cloud", "google-cloud-engine", "gce", "google-cloud-platform", "gcp"], 5 | "type": "library", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Jérôme Gamez", 10 | "email": "jerome@gamez.name" 11 | } 12 | ], 13 | "require": { 14 | "php": "^7.4|^8.0", 15 | "guzzlehttp/guzzle": "^6.5.8|^7.4.5", 16 | "psr/http-message": "^1.0" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "^9.5.20" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Kreait\\": "src" 24 | } 25 | }, 26 | "autoload-dev": { 27 | "psr-4": { 28 | "Kreait\\": "tests" 29 | } 30 | }, 31 | "scripts": { 32 | "tests": "vendor/bin/phpunit" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/GcpMetadata.php: -------------------------------------------------------------------------------- 1 | client = $client ?? $this->createClient(); 33 | } 34 | 35 | private function createClient(): Client 36 | { 37 | return new Client([ 38 | 'connect_timeout' => 1.0, // Default is 0 = indefinitely 39 | 'timeout' => 1.0 // Default is 0 = indefinitely 40 | ]); 41 | } 42 | 43 | public function isAvailable(): bool 44 | { 45 | if ($this->isAvailable !== null) { 46 | return $this->isAvailable; 47 | } 48 | 49 | try { 50 | $this->instance(); 51 | return $this->isAvailable = true; 52 | } catch (\Throwable $e) { 53 | return $this->isAvailable = false; 54 | } 55 | } 56 | 57 | public function instance(string $property = '', array $params = []) 58 | { 59 | return $this->request('instance', $property, $params); 60 | } 61 | 62 | public function project(string $property = '', array $params = []) 63 | { 64 | return $this->request('project', $property, $params); 65 | } 66 | 67 | private function request(string $type, string $property = '', array $params = []) 68 | { 69 | $property = ltrim($property, '/'); 70 | 71 | $url = self::baseUrl.$type.'/'.$property; 72 | 73 | $options = [ 74 | 'headers' => [self::flavorHeaderName => self::flavorHeaderValue], 75 | 'query' => $params, 76 | 'http_errors' => false, 77 | ]; 78 | 79 | try { 80 | $response = $this->client->request('GET', $url, $options); 81 | } catch (ConnectException $e) { 82 | throw new Error('Unable to connect: '.$e->getMessage()); 83 | } 84 | 85 | $this->verifyHttpStatus($response); 86 | $this->verifyHeaders($response); 87 | 88 | return $this->parseResponse($response); 89 | } 90 | 91 | private function verifyHttpStatus(ResponseInterface $response): void 92 | { 93 | if (($statusCode = $response->getStatusCode()) !== 200) { 94 | throw new Error('Unsuccessful response status code: '.$statusCode); 95 | } 96 | } 97 | 98 | private function verifyHeaders(ResponseInterface $response): void 99 | { 100 | if ($response->getHeaderLine(self::flavorHeaderName) !== self::flavorHeaderValue) { 101 | throw new Error('"'.self::flavorHeaderName.'" header is missing or incorrect.'); 102 | } 103 | } 104 | 105 | /** 106 | * @param ResponseInterface $response 107 | * @return string|string[] 108 | */ 109 | private function parseResponse(ResponseInterface $response) 110 | { 111 | $body = trim((string) $response->getBody()); 112 | $lines = explode("\n", $body); 113 | 114 | if (\count($lines) === 1) { 115 | return $body; 116 | } 117 | 118 | return $lines; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/GcpMetadata/Error.php: -------------------------------------------------------------------------------- 1 |