Skip to content

Commit

Permalink
Adding GCEMetadataStream.
Browse files Browse the repository at this point in the history
  • Loading branch information
Takashi Matsuo committed Nov 10, 2015
1 parent a0d4e91 commit b90c04d
Show file tree
Hide file tree
Showing 3 changed files with 297 additions and 0 deletions.
173 changes: 173 additions & 0 deletions src/Compute/Metadata/GCEMetadataStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?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;

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

/**
* A stream wrapper for GCE metadata.
*
* You can get the GCE metadata values very easily like:
*
* use Google\Cloud\Compute\Metadata\GCEMetadataStream;
*
* GCEMetadataStream::register();
* $val = file_get_contents('gce-metadata://project/mykey')
* or
* $val = file_get_contents('gce-metadata://instance/mykey')
*/
class GCEMetadataStream
{
/**
* The base URL for the metadata.
*/
const METADATA_BASE_URL = 'http://metadata.google.internal/computeMetadata/v1/';

/**
* The current seek position.
*/
private $position;

/**
* The metadata value.
*/
private $val;

/**
* The metadata reader.
*/
private $reader;

/**
* A static method to register this stream wrapper.
*/
public static function register()
{
stream_wrapper_register(
'gce-metadata',
'\Google\Cloud\Compute\Metadata\GCEMetadataStream'
)
or die('Failed to register GCEMetadataStream');
}

public function __construct()
{
$this->reader = new StreamReader();
}

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

// @codingStandardsIgnoreStart

// These methods are mandatory for implementing stream wrappers,
// so marking them with ignore tag.
//
// TODO: Find a way to just suppress certain types of errors with
// phpcs.
public function stream_open($path, $mode, $options, &$opened_path)
{
$url = parse_url($path);
if ($url['host'] != 'project' and $url['host'] != 'instance') {
return false;
}
$metadata_url = self::METADATA_BASE_URL.$url['host'].'/attributes'
.$url['path'];
$this->val = $this->reader->read($metadata_url);
$this->position = 0;

return true;
}

public function stream_stat()
{
return array();
}

public function stream_read($count)
{
$ret = substr($this->val, $this->position, $count);
$this->position += strlen($ret);

return $ret;
}

public function stream_write($data)
{
trigger_error('stream_write is not implemented.', E_WARNING);
}

public function stream_tell()
{
return $this->position;
}

public function stream_eof()
{
return $this->position >= strlen($this->val);
}

public function stream_seek($offset, $whence)
{
switch ($whence) {
case SEEK_SET:
if ($offset < strlen($this->val) && $offset >= 0) {
$this->position = $offset;

return true;
} else {
return false;
}
break;

case SEEK_CUR:
if ($offset >= 0) {
$this->position += $offset;

return true;
} else {
return false;
}
break;

case SEEK_END:
if (strlen($this->val) + $offset >= 0) {
$this->position = strlen($this->val) + $offset;

return true;
} else {
return false;
}
break;

default:
return false;
}
}

public function stream_metadata($path, $option, $var)
{
return false;
}
// @codingStandardsIgnoreEnd
}
44 changes: 44 additions & 0 deletions src/Compute/Metadata/Readers/StreamReader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?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 header whose presence indicates GCE presence.
*/
const FLAVOR_HEADER = 'Metadata-Flavor';

public function read($url)
{
$options = array(
'http' => array(
'method' => 'GET',
'header' => self::FLAVOR_HEADER.": Google\r\n",
),
);
$context = stream_context_create($options);

return file_get_contents($url, false, $context);
}
}
80 changes: 80 additions & 0 deletions tests/ComputeMetadataTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?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\GCEMetadataStream;

class ComputeMetadataTest extends \PHPUnit_Framework_TestCase
{
protected $mock;

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

public function testProjectMetadata()
{
$expected_url = 'http://metadata.google.internal/computeMetadata/v1/'
.'project/attributes/mykey';
$expected_val = 'myval';
$this->mock->expects($this->once())
->method('read')
->with($this->equalTo($expected_url))
->willReturn($expected_val);
$dummy = '';
$this->stream->stream_open('gce-metadata://project/mykey', null, null,
$dummy);
$val = $this->stream->stream_read(10);
$this->assertEquals($expected_val, $val);
}

public function testInstanceMetadata()
{
$expected_url = 'http://metadata.google.internal/computeMetadata/v1/'
.'instance/attributes/mykey';
$expected_val = 'myval';
$this->mock->expects($this->once())
->method('read')
->with($this->equalTo($expected_url))
->willReturn($expected_val);
$dummy = '';
$this->stream->stream_open('gce-metadata://instance/mykey', null, null,
$dummy);
$val = $this->stream->stream_read(10);
$this->assertEquals($expected_val, $val);
}

public function testBogusMetadata()
{
$expected_val = false;
$this->mock->expects($this->never())
->method('read')
->with($this->anything());
$dummy = '';
$this->stream->stream_open('gce-metadata://bogus/mykey', null, null,
$dummy);
$val = $this->stream->stream_read(10);
$this->assertEquals($expected_val, $val);
}
}

0 comments on commit b90c04d

Please sign in to comment.