Skip to content

Commit

Permalink
Adding GCE metadata lib.
Browse files Browse the repository at this point in the history
  • Loading branch information
Takashi Matsuo committed Nov 10, 2015
1 parent a0d4e91 commit e9cdb95
Show file tree
Hide file tree
Showing 3 changed files with 285 additions and 0 deletions.
114 changes: 114 additions & 0 deletions src/Compute/Metadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

/**
* Copyright 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Google\Cloud\Compute;

use Google\Cloud\Compute\Metadata\Readers\StreamReader;

/**
* A library for accessing the GCE metadata.
*
* You can get the GCE metadata values very easily like:
*
* use Google\Cloud\Compute\Metadata;
*
* $metadata = new Metadata();
* $project_id = $metadata->getProjectId();
*
* $val = $metadata->getProjectMetadata($key);
*/
class Metadata
{
/**
* The metadata reader.
*/
private $reader;

/**
* The project id.
*/
private $projectId;

/**
* Whether or not running on GCE.
*/
private $onGCE;

/**
* We use StreamReader for the default implementation for fetching the URL.
*/
public function __construct()
{
$this->reader = new StreamReader();
}

/**
* A method to replace the reader implementation.
*/
public function setReader($reader)
{
$this->reader = $reader;
}

/**
* This method retrieves a single metadata value for a given path.
*/
public function get($path)
{
return $this->reader->read($path);
}

/**
* This method detects the project id and returns it.
*/
public function getProjectId()
{
if (! isset($this->projectId)) {
$this->projectId = $this->reader->read('project/project-id');
}
return $this->projectId;
}

/**
* This method detects whether or not running on GCE.
*/
public function isRunningOnGCE()
{
if (! isset($this->onGCE)) {
$this->onGCE = $this->reader->onGCE();
}
return $this->onGCE;
}

/**
* This method fetches the project custom metadta and returns it.
*/
public function getProjectMetadata($key)
{
$path = 'project/attributes/'.$key;
return $this->get($path);
}

/**
* This method fetches the instance custom metadta and returns it.
*/
public function getInstanceMetadata($key)
{
$path = 'instance/attributes/'.$key;
return $this->get($path);
}
}
85 changes: 85 additions & 0 deletions src/Compute/Metadata/Readers/StreamReader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

/**
* Copyright 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Google\Cloud\Compute\Metadata\Readers;

/**
* A class only reading the metadata URL with an appropriate header.
*
* This class makes it easy to test the MetadataStream class.
*/
class StreamReader
{
/**
* The base PATH for the metadata.
*/
const BASE_URL = 'http://169.254.169.254/computeMetadata/v1/';

/**
* The default host name for metadata.
*/
const HOST = 'metadata.google.internal';

/**
* The header whose presence indicates GCE presence.
*/
const FLAVOR_HEADER = 'Metadata-Flavor: Google';

/**
* A common context for this reader.
*/
private $context;

/**
* We create the common context in the constructor.
*/
public function __construct()
{
$options = array(
'http' => array(
'method' => 'GET',
'header' => self::FLAVOR_HEADER,
),
);
$this->context = stream_context_create($options);
}

/**
* A method to read the metadata value for a given path.
*/
public function read($path)
{
$url = self::BASE_URL.$path;
return file_get_contents($url, false, $this->context);
}

/**
* A method to detect whether it's running on Compute Engine or not.
*/
public function onGCE()
{
// We use the hostname for failing faster on non-gce environment.
$url = 'http://'.self::HOST;
$result = @file_get_contents($url, false, $this->context);
if ($result != false
&& array_key_exists('Metadata-Flavor', $http_response_header)
&& $http_response_header['Metadata-Flavor'] == 'Google') {
return true;
}
return false;
}
}
86 changes: 86 additions & 0 deletions tests/ComputeMetadataTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

/**
* Copyright 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Google\Cloud\tests;

use Google\Cloud\Compute\Metadata;

class ComputeMetadataTest extends \PHPUnit_Framework_TestCase
{
protected $mock;
protected $metadata;

public function setUp()
{
$this->metadata = new Metadata();
$this->mock = $this->getMockBuilder(
'\Google\Cloud\Compute\Metadata\Readers\StreamReader')
->setMethods(array('read', 'onGCE'))
->getmock();
$this->metadata->setReader($this->mock);
}

public function testProjectMetadata()
{
$expected_path = 'project/attributes/mykey';
$expected_val = 'myval';
$this->mock->expects($this->once())
->method('read')
->with($this->equalTo($expected_path))
->willReturn($expected_val);
$val = $this->metadata->getProjectMetadata('mykey');
$this->assertEquals($expected_val, $val);
}

public function testInstanceMetadata()
{
$expected_path = 'instance/attributes/mykey';
$expected_val = 'myval';
$this->mock->expects($this->once())
->method('read')
->with($this->equalTo($expected_path))
->willReturn($expected_val);
$val = $this->metadata->getInstanceMetadata('mykey');
$this->assertEquals($expected_val, $val);
}

public function testGetProjectId()
{
$expected_path = 'project/project-id';
$expected_val = 'my-project';
$this->mock->expects($this->once())
->method('read')
->with($this->equalTo($expected_path))
->willReturn($expected_val);
$project_id = $this->metadata->getProjectId();
$this->assertEquals($expected_val, $project_id);
// Ensure this value is cached thus we `read` only once.
$this->metadata->getProjectId();
}

public function testIsRunningOnGCE()
{
$expected_val = true;
$this->mock->expects($this->once())
->method('onGCE')
->willReturn($expected_val);
$onGCE = $this->metadata->isRunningOnGCE();
$this->assertEquals($expected_val, $onGCE);
// Ensure this value is cached thus we call onGCE only once.
$this->metadata->isRunningOnGCE();
}
}

0 comments on commit e9cdb95

Please sign in to comment.