Retire stackforge/openstack-sdk-php
This commit is contained in:
parent
4536bbc166
commit
9fb2743fc4
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,9 +0,0 @@
|
||||
.idea/
|
||||
build/
|
||||
vendor/
|
||||
|
||||
.DS_Store
|
||||
composer.lock
|
||||
composer.phar
|
||||
phpunit.xml
|
||||
tests/settings.ini*
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=stackforge/openstack-sdk-php.git
|
@ -1,4 +0,0 @@
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
This changelog contains the relevant feature additions and bug fixes.
|
176
LICENSE.txt
176
LICENSE.txt
@ -1,176 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
106
README.rst
106
README.rst
@ -1,103 +1,7 @@
|
||||
OpenStack PHP-Client
|
||||
====================
|
||||
This project is no longer maintained.
|
||||
|
||||
This package provides PHP OpenStack bindings.
|
||||
The contents of this repository are still available in the Git source code
|
||||
management system. To see the contents of this repository before it reached
|
||||
its end of life, please check out the previous commit with
|
||||
"git checkout HEAD^1".
|
||||
|
||||
You can use this library to:
|
||||
|
||||
- Authenticate your application to OpenStack.
|
||||
- Interact with Object Storage (aka Swift).
|
||||
|
||||
Coming soon:
|
||||
|
||||
- Intect with the Compute (Nova) manager.
|
||||
- Interact with other OpenStack services
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- PHP 5.3
|
||||
- An active OpenStack account with the desired services.
|
||||
|
||||
Suggestions
|
||||
~~~~~~~~~~~
|
||||
|
||||
- Enable the cURL extension for full protocol support.
|
||||
|
||||
We also have support for using PHP's native HTTP stream wrapper, but it
|
||||
is not as reliable. We recommend cURL.
|
||||
|
||||
Versioning
|
||||
----------
|
||||
|
||||
We have a goal to be as consistent as possible with `Semantic
|
||||
Versioning <http://semver.org/>`__. For released HP Cloud services this
|
||||
is what you can expect.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
There are currently two methods of installation. We've been considering
|
||||
PEAR and Phar releases, but have currently limited to only Composer and
|
||||
builds because these cover our needs.
|
||||
|
||||
Method #1:
|
||||
~~~~~~~~~~
|
||||
|
||||
Use `Composer <http://getcomposer.org>`__ to download and install the
|
||||
latest version of OpenStack.
|
||||
|
||||
Method #2:
|
||||
~~~~~~~~~~
|
||||
|
||||
Download a tagged release and include it in your project.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Identity Services
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Authenticate, authorize service usage, and retrieve account information.
|
||||
|
||||
Object Storage
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Store files or other data objects in containers on your OpenStack object
|
||||
storage instance. Create, modify and delete containers. Manage ACLs.
|
||||
Read, write, and delete objects. Expose objects in your object storage
|
||||
to other services.
|
||||
|
||||
With full stream wrapper support, you can use built-in PHP functions
|
||||
like ``file_get_contents()``, ``fopen()``, and ``stat()`` for reading
|
||||
and writing files into object storage.
|
||||
|
||||
Autoloading
|
||||
^^^^^^^^^^^
|
||||
|
||||
OpenStack SDK for PHP is `PSR-4
|
||||
compliant <https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4.md>`__,
|
||||
which means that it should work with any PSR-4 autoloader. However, it
|
||||
also comes with its own autoloader for apps that don't yet make use of a
|
||||
standard autoloader.
|
||||
|
||||
Composer Support
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
OpenStack PHP-Client is available as part of the Packagist archive,
|
||||
which means you can use Composer to automatically download, install, and
|
||||
manage revisions to OpenStack from within your project.
|
||||
|
||||
We're big fans of `Composer <http://getcomposer.org>`__.
|
||||
|
||||
More information
|
||||
----------------
|
||||
|
||||
`OpenStack <http://OpenStack.org>`__ is a cloud computing platform that
|
||||
provides many services, inlcuding compute installs, object and block
|
||||
storage, and a host of hosted services.
|
||||
|
||||
This library provides access to those services.
|
||||
|
||||
The best source of documentation is the official API documentation,
|
||||
which is available at http://FIXME
|
||||
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "openstack/openstack-sdk-php",
|
||||
"description": "Access OpenStack services in PHP.",
|
||||
"type": "library",
|
||||
"keywords": ["openstack","keystone","cloud","swift","nova"],
|
||||
"license": "Apache-2.0",
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"guzzlehttp/guzzle": "~4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.1"
|
||||
},
|
||||
"support": {
|
||||
"irc": "irc://irc.freenode.org/openstack-sdks",
|
||||
"issues": "https://bugs.launchpad.net/openstack-sdk-php",
|
||||
"forum": "https://ask.openstack.org/",
|
||||
"wiki": "https://wiki.openstack.org/wiki/OpenStack-SDK-PHP",
|
||||
"source": "http://git.openstack.org/cgit/stackforge/openstack-sdk-php"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OpenStack\\": "src/OpenStack",
|
||||
"OpenStack\\Tests\\": "tests/Tests"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
<?php
|
||||
/** About the OpenStack PHP-Client
|
||||
*
|
||||
* This is the documentation for the OpenStack PHP-Client library.
|
||||
*
|
||||
* Overview
|
||||
*
|
||||
* @see http://www.openstack.org is open source software for
|
||||
* building public and private clouds.
|
||||
*
|
||||
* The PHP-Client library provides PHP developers with a fully tested,
|
||||
* robust, and feature-rich library for working with the OpenStack services.
|
||||
*
|
||||
* Making use of this library will require that you have several pieces of
|
||||
* account information for your OpenStack account:
|
||||
* - account ID and secret key: For cases where you want account-wide
|
||||
* authentication/authorization.
|
||||
* - username/password: Typically, this is the same username/password you use
|
||||
* to access the console.
|
||||
* - tenant ID: This associates an account or user with a bundle of services.
|
||||
* You can find this information in your console.
|
||||
* - endpoint: You will need the URL to the OpenStack endpoint responsible for
|
||||
* authenticating users. This can be found in your console.
|
||||
*
|
||||
* Where To Start
|
||||
*
|
||||
* Cruising a list of methods and classes is not exactly the best way to get
|
||||
* started with a library. It's better to know where to start. Here's
|
||||
* what we suggest:
|
||||
*
|
||||
*- There are a few tutorials inside this documentation that will help you
|
||||
* get started.
|
||||
* @see streams-tutorial Information about stream wrappers.
|
||||
* @see oo-tutorial Getting started with the library itself
|
||||
*- Connecting and logging in is almost inevitably going to be your first
|
||||
* task. For that, you will want to look at IdentityServices.
|
||||
*- ObjectStorage (a.k.a. swift) is the cloud storage system. There are
|
||||
* two ways to use it:
|
||||
* - You can explore the object oriented API, starting with ObjectStorage.
|
||||
* - You can use the PHP stream wrappers to access your object storage. This
|
||||
* is explained in StreamWrapper.
|
||||
*
|
||||
* Learn More
|
||||
*
|
||||
* This documentation is intended to provide a detailed reference to the
|
||||
* PHP-Client library. To learn more about the APIs and OpenStack visit
|
||||
* @see http://api.openstack.org/
|
||||
* @see http://docs.openstack.org/
|
||||
*
|
||||
* Basic Example: Stream Wrappers
|
||||
*
|
||||
* The super-simple stream API:
|
||||
*
|
||||
* <?php
|
||||
* require 'vendor/autoload.php';
|
||||
*
|
||||
* // Turn on stream wrappers.
|
||||
* \OpenStack\Bootstrap::useStreamWrappers();
|
||||
*
|
||||
* // Create a stream context. You can get this
|
||||
* // information (including tenant ID) from your
|
||||
* // OpenStack console.
|
||||
* $cxt = stream_context_create(array(
|
||||
* 'username' => 'foo@example.com',
|
||||
* 'password' => 'secret',
|
||||
* 'tenantid' => '123456',
|
||||
* 'endpoint' => 'http://url.from.hpcloud.com/',
|
||||
* ));
|
||||
*
|
||||
*
|
||||
* // Get an object from the remote object storage and read it as a string
|
||||
* // right into $myObject.
|
||||
* $myObject = file_get_contents('swift://mycontainer/foo.txt', FALSE, $cxt);
|
||||
*
|
||||
* ?>
|
||||
*
|
||||
* With stream wrapper support, you can transparently read and write files to the
|
||||
* ObjectStorage service without using any fancy API at all. Use the
|
||||
* normal file methods like this:
|
||||
*
|
||||
*- fopen()/fclose()
|
||||
*- fread()/fwrite()
|
||||
*- file_get_contents(), stream_get_contents()
|
||||
*- stat()/fstat()
|
||||
*- is_readable()/is_writable()
|
||||
*- And so on
|
||||
* @see http://us3.php.net/manual/en/ref.filesystem.php
|
||||
*
|
||||
* Learn more about this at \OpenStack\ObjectStore\v1\Resource\StreamWrapper.
|
||||
*
|
||||
* Basic Example: Identity Service
|
||||
*
|
||||
* Stream wrappers are nice and all, but
|
||||
* some of us love fancy APIs. So here's an example using the full API
|
||||
* to log in and then dump a list of services that are available to you:
|
||||
*
|
||||
* <?php
|
||||
* require 'vendor/autoload.php';
|
||||
*
|
||||
* use \OpenStack\Identity\v1\IdentityService;
|
||||
*
|
||||
* // Create a new identity service object, and tell it where to
|
||||
* // go to authenticate. This URL can be found in your console.
|
||||
* $identity = new IdentityService('http://get.url.from.hpcloud.com');
|
||||
*
|
||||
* // You can authenticate with a username/password (IdentityService::authenticateAsUser()).
|
||||
* // In either case you can get the info you need from the console.
|
||||
* $username = 'foobar';
|
||||
* $password = 'dgasgasd';
|
||||
* $tenantId = '56545654';
|
||||
*
|
||||
* // $token will be your authorization key when you connect to other
|
||||
* // services. You can also get it from $identity->token().
|
||||
* $token = $identity->authenticateAsUser($username, $password, $tenantId);
|
||||
*
|
||||
* // Get a listing of all of the services you currently have configured in
|
||||
* // OpenStack.
|
||||
* $catalog = $identity->serviceCatalog();
|
||||
*
|
||||
* var_dump($catalog);
|
||||
*
|
||||
* ?>
|
||||
*
|
||||
*- Our classes use PHP namespaces to organize components. If you've never used
|
||||
* them before, don't worry. They're easy to get the hang of.
|
||||
*- The Bootstrap class handles setting up OpenStack services. Read about it at \OpenStack\Bootstrap.
|
||||
*- The IdentityServices class handles authenticating to OpenStack, discovering services, and providing
|
||||
* access to your account. \OpenStack\Identity\v1\IdentityService explains the details, but here are
|
||||
* a few functions you'll want to know:
|
||||
* - \OpenStack\Identity\v1\IdentityService::__construct() tells the object where to connect.
|
||||
* - \OpenStack\Identity\v1\IdentityService::authenticateAsUser() lets you log
|
||||
* in with username and password.
|
||||
* - \OpenStack\Identity\v1\IdentityService::serviceCatalog() tells you about
|
||||
* the services you have activated on this account.
|
||||
*
|
||||
* Basic Example: Object Storage
|
||||
*
|
||||
* Assuming you have an object storage instance available in your service
|
||||
* catalog, we could continue on with something like this:
|
||||
*
|
||||
* <?php
|
||||
* // The explicit way:
|
||||
* // Find out where our ObjectStorage instance lives:
|
||||
* // $storageList = $identity->serviceCatalog('object-storage');
|
||||
* // $objectStorageUrl = storageList[0]['endpoints'][0]['publicURL'];
|
||||
*
|
||||
* // Create a new ObjectStorage instance:
|
||||
* // $objectStore = new \OpenStack\ObjectStore\v1\ObjectStorage($token, $objectStorageUrl);
|
||||
*
|
||||
* // Or let ObjectStorage figure out which instance to use:
|
||||
* $objectStore = \OpenStack\ObjectStore\v1\ObjectStorage::newFromIdentity($identity);
|
||||
*
|
||||
* // List containers:
|
||||
* print_r($objectStore->containers());
|
||||
*
|
||||
* // Get a container named 'stuff':
|
||||
* $container = $objectStore->container('stuff');
|
||||
*
|
||||
* // List all of the objects in that container:
|
||||
* print_r($container->objects());
|
||||
*
|
||||
* // Get an object named 'example.txt'
|
||||
* $obj = $container->object('example.txt');
|
||||
*
|
||||
* // Print that object's contents:
|
||||
* print $obj->content();
|
||||
*
|
||||
* // Actually, since it implements __tostring, we could do this:
|
||||
* print $obj;
|
||||
* ?>
|
||||
*
|
||||
* This shows you a few methods for accessing objects and containers on your
|
||||
* \OpenStack\ObjectStore\v1\ObjectStorage account. There are many functions for
|
||||
* creating and modifying containers and objects, too.
|
||||
*
|
||||
*- \OpenStack\ObjectStore\v1\ObjectStorage is where you will start.
|
||||
*- Container services are in \OpenStack\ObjectStore\v1\ObjectStorage\Container
|
||||
*- There are two classes for objects:
|
||||
* - \OpenStack\ObjectStore\v1\ObjectStorage\Object is for creating new objects.
|
||||
* - \OpenStack\ObjectStore\v1\ObjectStorage\RemoteObject provides better network
|
||||
* performance when reading objects.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @package OpenStack
|
||||
* The OpenStack PHP-Client library.
|
||||
*/
|
||||
/**
|
||||
* @namespace OpenStack.Services
|
||||
* OpenStack classes providing access to various services.
|
||||
*
|
||||
* OpenStack offers a number of services, including Compute (Nova),
|
||||
* and IdentityService.
|
||||
*
|
||||
* This package is reserved for classes that provide access to
|
||||
* services.
|
||||
*/
|
||||
/**
|
||||
* @package OpenStack.Storage
|
||||
* OpenStack classes for remote storage.
|
||||
*
|
||||
* Services for now and the future:
|
||||
*
|
||||
*- ObjectStorage
|
||||
*- Others coming.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @package OpenStack.Storage.ObjectStorage
|
||||
* Classes specific to ObjectStorage.
|
||||
*
|
||||
* The main class is \OpenStack\ObjectStore\v1\ObjectStorage.
|
||||
*/
|
||||
/**
|
||||
* @package OpenStack.Transport
|
||||
* HTTP/REST/JSON classes.
|
||||
*
|
||||
* HTTP/HTTPS is the transport protocol for OpenStack's RESTful services.
|
||||
*
|
||||
* This library provides both CURL and PHP Streams-based HTTP support,
|
||||
* and this package provides a simple REST client architecture, along
|
||||
* with the minimal JSON processing necessary.
|
||||
*
|
||||
*
|
||||
*/
|
||||
?>
|
@ -1,74 +0,0 @@
|
||||
# Using the OpenStack PHP-CLient API
|
||||
|
||||
This tutorial explains how you can use the PHP API to connect to your OpenStack
|
||||
services and interact programmatically.
|
||||
|
||||
## Object Storage (Swift)
|
||||
|
||||
One of the services that OpenStack offers is called "Object Storage".
|
||||
This service provides a useful means of storing objects (usually files)
|
||||
on a service that you control, but that is available to other services
|
||||
in your cloud (and optionally is availably publically).
|
||||
|
||||
This section of the tutorial describes how you can write PHP code to
|
||||
interact with the Object Storage service.
|
||||
|
||||
## Authenticating to Object Storage
|
||||
|
||||
There are two ways to authenticate to Object Storage:
|
||||
|
||||
- Legacy Swift authentication
|
||||
- Control Services authentication
|
||||
|
||||
For legacy swift authentication, you will need to use your Tenant ID, your username,
|
||||
and your password, along with the URL to the Object Storage endpoint.
|
||||
|
||||
### Using Stream Wrappers
|
||||
|
||||
There are two main methods for accessing OpenStack through this library.
|
||||
The first is through PHP *stream wrappers*. In PHP, stream wrappers
|
||||
provide a facility with which you can access various data streams (like
|
||||
a webpage, the data in a ZIP file, or an OpenStack object store) as if
|
||||
they were local files on your file system.
|
||||
|
||||
Stream wrappers have a huge advantage for you: You can use the normal
|
||||
file system functions (`fread()`, `mkdir()`, `file_get_contents()`, etc)
|
||||
to access things not necessarily on your local filesystem. The PHP-Client
|
||||
library integrates with this facility of PHP.
|
||||
|
||||
|
||||
### Using the PHP-Client Classes
|
||||
|
||||
While the stream wrappers are a fantastic way to accomplish many common
|
||||
tasks, sometimes you need a finer level of control, or you wish to use
|
||||
an Object-Oriented API. We provide you with the classes you need to work
|
||||
this way.
|
||||
|
||||
(Deep dark secret: Actually, these are the classes that underly the
|
||||
stream wrappers.)
|
||||
|
||||
In this section of the tutorial, we focus on using this API as a
|
||||
data-access layer.
|
||||
|
||||
#### Main Classes
|
||||
|
||||
- \OpenStack\Bootstrap: Provides services for bootstrapping the library.
|
||||
It's not necessary, but it can be helpful.
|
||||
- \OpenStack\ObjectStorage: The main interface to the OpenStack object
|
||||
storage.
|
||||
|
||||
## Slightly Irreverant Glossary
|
||||
|
||||
*Tenant ID:* You service provider will provide you with
|
||||
an account ID and a secret key, along with a URL, that can be used to
|
||||
access the cloud APIs.
|
||||
|
||||
*Container:* A namespace extension useful for differentiating object
|
||||
space with pseudo-containment logical units. Or, a directory. (see
|
||||
_Object Storage_)
|
||||
|
||||
*Object Storage:* A service provided by OpenStack that allows you to store
|
||||
entire files on the cloud. Files can be organized into _containers_, which are
|
||||
rough analogs to file system directories (or folders).
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use \OpenStack\Identity\v2\IdentityService;
|
||||
use \OpenStack\ObjectStore\v1\ObjectStorage;
|
||||
use \OpenStack\ObjectStore\v1\ObjectStorage\Object;
|
||||
|
||||
// Load these from an ini file.
|
||||
$ini = parse_ini_file(getenv('HOME') . '/.OpenStack.ini');
|
||||
$username = $ini['username'];
|
||||
$password = $ini['password'];
|
||||
$tenantId = $ini['tenantId'];
|
||||
$endpoint = $ini['url'];
|
||||
|
||||
$idService = new IdentityService($endpoint);
|
||||
$token = $idService->authenticateAsUser($username, $password, $tenantId);
|
||||
|
||||
$catalog = $idService->serviceCatalog();
|
||||
|
||||
$store = ObjectStorage::newFromServiceCatalog($catalog, $token);
|
||||
|
||||
$store->createContainer('Example');
|
||||
$container = $store->container('Example');
|
||||
|
||||
$name = 'hello.txt';
|
||||
$content = 'Hello World';
|
||||
$mime = 'text/plain';
|
||||
|
||||
$localObject = new Object($name, $content, $mime);
|
||||
$container->save($localObject);
|
||||
|
||||
$object = $container->object('hello.txt');
|
||||
printf("Name: %s \n", $object->name());
|
||||
printf("Size: %d \n", $object->contentLength());
|
||||
printf("Type: %s \n", $object->contentType());
|
||||
print $object->content() . PHP_EOL;
|
@ -1,480 +0,0 @@
|
||||
Tutorial: Using OpenStack PHP-Client
|
||||
=================
|
||||
|
||||
PHP-Client provides PHP language bindings for the OpenStack APIs.
|
||||
|
||||
In this tutorial, we will walk through the process of creating a simple
|
||||
tool that interacts with OpenStack's Object Storage. The emphasis in this
|
||||
article is on getting started and learning the concepts, not building a
|
||||
polished product.
|
||||
|
||||
**This tutorial focuses on the object-oriented API.** The other way to
|
||||
work with this library is through the stream wrapper. That topic is
|
||||
covered in [another tutorial](@ref streams-tutorial).
|
||||
|
||||
## Pre-flight Check
|
||||
|
||||
PHP-Client has been developed to require PHP 5.3 or later. You are
|
||||
strongly encouraged to also install the CURL PHP extension. Many
|
||||
distributions of PHP come with this enabled. Sometimes, though, you may
|
||||
need to do something like `apt-get php5-curl` or similar. (Don't take
|
||||
our word for it -- check your system's documentation.)
|
||||
|
||||
You can check for both of these conditions by checking the output of
|
||||
`php --info` (on the commandline) or `<?php phpinfo(); ?>`.
|
||||
|
||||
### Check the pilot, too!
|
||||
|
||||
In our pre-flight check, we would be remiss if we didn't point out that
|
||||
there are some requirements for the pilot (that's you), too.
|
||||
|
||||
The PHP-Client library is composed of two parts:
|
||||
|
||||
1. The Object-Oriented part, which is the subject of this tutorial.
|
||||
2. The Stream Wrapper, which is the subject of another tutorial.
|
||||
|
||||
The object-oriented library makes ample use of PHP namespaces. If you've
|
||||
never seen these before, they look like this:
|
||||
|
||||
<?php
|
||||
\OpenStack\ObjectStore\v1\Resource\RemoteObject
|
||||
?>
|
||||
|
||||
The namespace above is read like this: "The RemoteObject class is part
|
||||
of the ObjectStorage package in the Storage package in the base OpenStack
|
||||
package." Those familiar with Java, Python, and other languages will
|
||||
recognize this way of talking (though the backslash is an unfortunate
|
||||
symbol choice).
|
||||
|
||||
For our library, we followed the recommendation of SPR-0, which means
|
||||
that the class above can be found in the file at:
|
||||
|
||||
src/OpenStack/ObjectStore/v1/Resource/RemoteObject.php
|
||||
|
||||
The pattern of matching namespace to file name should (we hope) make it
|
||||
easier for you to navigate our code.
|
||||
|
||||
If this namespace stuff continues to confuse you, you may want to take a
|
||||
look at [the PHP documentation](http://us3.php.net/manual/en/language.namespaces.php),
|
||||
or you may just prefer to keep on reading and learn by example. We don't
|
||||
do anything really fancy with namespaces.
|
||||
|
||||
**In this document, we sometimes replace the backslash (\\) with double
|
||||
colons (`::`) so that links are automatically generated.** So
|
||||
`\OpenStack\Bootstrap` may appear as OpenStack::Bootstrap. The reason for
|
||||
this is [explained elsewhere](@ref styleguide).
|
||||
|
||||
## Step 1: Getting the Library
|
||||
|
||||
You can get the OpenStack PHP-CLient library at the [OpenStack PHP-Client
|
||||
Repository](https://FIXME). The latest code is always
|
||||
available there.
|
||||
|
||||
The project also uses [Composer](http://packagist.org/), and this is the
|
||||
best method for adding PHP-Client to your PHP project.
|
||||
|
||||
For our example, we will assume that the library is accessible in the
|
||||
default include path, so the following line will include the
|
||||
`Bootstrap.php` file:
|
||||
|
||||
include 'OpenStack/Bootstrap.php';
|
||||
|
||||
## Step 2: Bootstrap the Library
|
||||
|
||||
The first thing to do in your application is make sure the OpenStack
|
||||
library is bootstrapped. When we say "bootstrap", what we really mean is
|
||||
letting the library initialize itself.
|
||||
|
||||
The only thing you will need to do is require Composer's PSR-compliant
|
||||
autoloader, like so:
|
||||
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use \OpenStack\Bootstrap;
|
||||
use \OpenStack\Identity\v2\IdentityService;
|
||||
use \OpenStack\ObjectStore\v1\ObjectStorage;
|
||||
use \OpenStack\ObjectStore\v1\Resource\Object;
|
||||
|
||||
The first line should be self-explanatory: we require the main autoloader file
|
||||
which is generated by Composer.
|
||||
|
||||
After that, we declare a list of namespaces that we will use. This way we can
|
||||
refer to classes by their short name, rather than by their fully qualified name.
|
||||
|
||||
There are some other fancy things that OpenStack::Bootstrap can do for
|
||||
you. Most notably, you can pass configuration parameters into it. But
|
||||
for the time being, we are good to go.
|
||||
|
||||
Our library is boostrapped. Next up: Let's connect to our account.
|
||||
|
||||
## Step 3: Connecting
|
||||
|
||||
Our programming goal, in this tutorial, is to interact with the Object
|
||||
Storage service on OpenStack. (Object Storage is, for all intents and
|
||||
purposes, basically a service for storing files in the cloud.)
|
||||
|
||||
But before we can interact directly with Object Storage, we need to
|
||||
authenticate to the system. And to do this, we need the following four
|
||||
pieces of information:
|
||||
|
||||
- Username: The username for an account.
|
||||
- Password: The password associated with the username.
|
||||
- Tenant ID: An identifier that maps an account to a set of services.
|
||||
(In theory at least, one account can have multiple tenant IDs, and one
|
||||
tenant ID can be linked to multiple accounts.)
|
||||
- Endpoint URL: The URL to the Identity Services endpoint for OpenStack.
|
||||
|
||||
Before you issue a forlorn sigh, envisioning some laborious task, let us
|
||||
point out that all of this information is available in one place, Log
|
||||
into the console and go to the `API Keys`
|
||||
page. It's all there.
|
||||
|
||||
### Identity Services
|
||||
|
||||
OpenStack is composed of numerous services. There's the Compute
|
||||
service, the Object Storage service... and so on.
|
||||
|
||||
Authenticating separately to each of these would be a collosal waste of
|
||||
network resources. And behind the scenes, account management would be
|
||||
difficult on the server side.
|
||||
|
||||
That's where Identity Services comes in. It is a central service that
|
||||
handles all things authorization and authentication related. Roughly,
|
||||
it works as follows:
|
||||
|
||||
- The client sends an authentication request
|
||||
- If it fails, the service returns an error
|
||||
- If authentication succeeds, the service returns a time-sensitive token
|
||||
(basically a shared secret) and a "service catalog"
|
||||
|
||||
The *token* is valid for some fixed period of time (say, 30 minutes),
|
||||
during which time it can be used for every other service. Each request
|
||||
to an OpenStack service should send (along with other info) the token. The
|
||||
remote service then validates the token with identity services, saving
|
||||
our app the trouble of making another round trip.
|
||||
|
||||
The *service catalog* lists all of the OpenStack services that the present
|
||||
account can access.
|
||||
|
||||
### Authenticating
|
||||
|
||||
With that little bit of theory behind us, we can now go about
|
||||
authenticating.
|
||||
|
||||
<?php
|
||||
$username = 'ADD USERNAME HERE';
|
||||
$password = 'ADD PASSWORD HERE';
|
||||
$tenantId = 'ADD TENANT ID HERE';
|
||||
$endpoint = 'ADD ENDPOINT URL HERE';
|
||||
|
||||
$idService = new \OpenStack\Identity\v2\IdentityService($endpoint);
|
||||
$token = $idService->authenticateAsUser($username, $password, $tenantId);
|
||||
?>
|
||||
|
||||
Assuming the variables above have been set to include valid data, this
|
||||
script can connect to OpenStack and authenticate.
|
||||
|
||||
When we construct a new OpenStack::Services::IdentityService object, we must pass it the
|
||||
endpoint URL for OpenStack Identity Service. Typically, that URL will
|
||||
look something like this:
|
||||
|
||||
~~~
|
||||
https://region-a.geo-1.identity.hpcloudsvc.com:35357
|
||||
~~~
|
||||
|
||||
The `authenticateAsUser()` method will authenticate to the
|
||||
Identity Services endpoint. For convenience, it returns the
|
||||
authorization token (`$token`), though we can also get the token from
|
||||
`$idService->token()`.
|
||||
|
||||
Note that the `IdentityService` object may throw various exceptions
|
||||
(all subclasses of OpenStack\Common\Exception) during authentication. Failed
|
||||
authentication results in an \OpenStack\Common\Transport\AuthorizationException, while
|
||||
a network failure may result in an \OpenStack\Common\Transport\ServerException.
|
||||
|
||||
Earlier, we talked about the service catalog. Once we've authenticated,
|
||||
we can get the service catalog from `$idService->serviceCatalog()`. It
|
||||
is an associative array, and you can get an idea of what it contains by
|
||||
dumping it with `var_dump()`, should you so desire.
|
||||
|
||||
At this point, we have what we need from Identity Service. It's time to
|
||||
look at Object Storage.
|
||||
|
||||
### IdentityService in a Nutshell
|
||||
|
||||
Instances of `OpenStack\Identity\v2\IdentityService` are responsible for:
|
||||
|
||||
- Authentication
|
||||
- Accessing the service catalog
|
||||
- Accessing account info
|
||||
- Associating tenant IDs with accounts (advanced)
|
||||
|
||||
## Step 4: Connecting to Object Storage
|
||||
|
||||
The Object Storage system is concerned with two classes of things:
|
||||
|
||||
- An Object: A self-contained bundle of data (back in my day, we called
|
||||
them "files").
|
||||
- A Container: A storage container (bucket) for objects.
|
||||
|
||||
Your object storage can have any number of containers, and each
|
||||
container can have any number of objects.
|
||||
|
||||
In the object model for the OpenStack PHP-Client library, a top-level object
|
||||
called OpenStack::Storage::ObjectStorage provides access to the Object
|
||||
Storage service. In this step, we will be working with that object.
|
||||
|
||||
### Getting an ObjectStorage Instance
|
||||
|
||||
Earlier, we created an `IdentityService` instance called `$idService`.
|
||||
We will use that here to get the service catalog. Once we have the
|
||||
catalog, we can have a new `ObjectStorage` instance created for us,
|
||||
configured to talk to our account's Object Storage instance in
|
||||
OpenStack. Along with the service catalog, we also need our token that
|
||||
shows the Object Storage endpoint that we have already authenticated to
|
||||
Identity Services. Earlier, we captured that value in the `$token`
|
||||
variable.
|
||||
|
||||
Now we can get a new `\OpenStack\ObjectStore\v1\ObjectStorage` instance:
|
||||
|
||||
<?php
|
||||
$catalog = $idService->serviceCatalog();
|
||||
|
||||
$store = ObjectStorage::newFromServiceCatalog($catalog, $token);
|
||||
|
||||
// UPDATE: As of Beta 6, you can use newFromIdentity():
|
||||
// $store = ObjectStorage::newFromIdentity($idService);
|
||||
?>
|
||||
|
||||
First we get the service catalog (`$catalog`), and then we use the
|
||||
`ObjectStorage::newFromServiceCatalog()` static method to create the new
|
||||
Object Storage instance.
|
||||
|
||||
The pattern of using a constructor-like static function is used
|
||||
throughout the OpenStack PHP-Client library. Inspired by Objective-C constructors
|
||||
and the Factory design pattern, it makes it possible for a single class
|
||||
to have multiple constructors.
|
||||
|
||||
In particular, many top-level classes provide a
|
||||
`newFromServiceCatalog()` constructor function, since these classes know
|
||||
how to construct instances from a service catalog, thus freeing the
|
||||
developer up from knowing the details of a service catalog entry.
|
||||
|
||||
Now we have an `ObjectStorage` instance that is already configured to
|
||||
talk to our OpenStack object storage service. Next, we can create a
|
||||
container.
|
||||
|
||||
### ObjectStorage in a Nutshell
|
||||
|
||||
Instances of OpenStack::Storage::ObjectStorage are responsbile for:
|
||||
|
||||
- Providing high-level information about the Object Storage service
|
||||
- Creating, deleting, loading, and listing Containers
|
||||
- Modifying Container ACLs
|
||||
|
||||
## Step 5: Adding a Container
|
||||
|
||||
Before we can start putting objects (files) into our Object Storage
|
||||
service, we need a place to put them. An Object Storage service can hold
|
||||
numerous containers (and each container can have different access
|
||||
controls -- a topic we won't get into here).
|
||||
|
||||
Containers are represented in the library by the
|
||||
OpenStack::Storage::ObjectStorage::Container class. And creating a
|
||||
container is done by a method on the `ObjectStorage` object that we
|
||||
created above:
|
||||
|
||||
<?php
|
||||
$store->createContainer('Example');
|
||||
$container = $store->container('Example');
|
||||
?>
|
||||
|
||||
Recall that `$store` is the name of our `ObjectStorage` instance. In the
|
||||
first of the two lines above, we create a new container named `Example`.
|
||||
Then in the second line, we get that container.
|
||||
|
||||
Why is this two steps? The answer is that the OpenStack PHP-Client library mimics
|
||||
the architecture of the underlying API. This is two operations (which
|
||||
means it requires two network requests to the remote host), and so we
|
||||
must perform two operations.
|
||||
|
||||
The `createContainer()` call actually creates the new container on the
|
||||
cloud's Object Storage. The second call connects to the remote object
|
||||
storage, and gets the new container. The container that is returned will
|
||||
have some additional information, such as the amount of space it takes
|
||||
up on the remote storage, and the access control rules for that
|
||||
container. All of this information will be available on the `$container`
|
||||
instance.
|
||||
|
||||
Our `$container` instance is an instance of
|
||||
OpenStack::Storage::ObjectStorage::Container. This object can be used not
|
||||
only to find out about a container, but also to get information about
|
||||
the objects in that container.
|
||||
|
||||
Now that we have a `Container`, we can add an object.
|
||||
|
||||
### The Container in a Nutshell
|
||||
|
||||
(Yes, we realize the irony of that title.)
|
||||
|
||||
A `\OpenStack\ObjectStore\v1\Resource\Container` instance is responsible for the following:
|
||||
|
||||
- Accessing information about the container
|
||||
- Creating, saving, deleting, and listing objects in the container
|
||||
- Providing path-like traversal of objects
|
||||
- Copying objects across containers
|
||||
- Loading a lightweight representation of an object without fetching the
|
||||
entire object (more on this later).
|
||||
|
||||
Among the features of a Container, it can act like an `Iterator` and is
|
||||
`Countable`. That means you can loop through a Container in a `foreach`
|
||||
loop and also use `count($container)` to find out the number of objects
|
||||
in a Container.
|
||||
|
||||
## Step 6: Storing an Object
|
||||
|
||||
Now we are ready to create an object, and then store it in our
|
||||
container.
|
||||
|
||||
Before diving too deeply, it is important to point out a detail: When
|
||||
working with a remote data storage service, we are typically working
|
||||
with a local copy and a remote copy. If our code isn't constructed
|
||||
correctly, it is possible for these two to get out of sync.
|
||||
|
||||
Earlier, we created a container directly on the remote side, and then
|
||||
fetched the container. As we create an object, we are going to do the
|
||||
opposite: We will create a local object, and then save it to the remote
|
||||
storage. Later, we will fetch the remote object.
|
||||
|
||||
<?php
|
||||
$name = 'hello.txt';
|
||||
$content = 'Hello World';
|
||||
$mime = 'text/plain';
|
||||
|
||||
$localObject = new Object($name, $content, $mime);
|
||||
$container->save($localObject);
|
||||
?>
|
||||
|
||||
In the code above, we create `$localObject` with a `$name`, some
|
||||
`$content`, and a `$mime` type. Strictly speaking, only `$name` is
|
||||
required.
|
||||
|
||||
The OpenStack::Storage::ObjectStorage::Object class is used primarily to
|
||||
describe a locally created object. Once we have our new `Object`, we can
|
||||
save it remotely using the `save()` method on our `$container` object.
|
||||
This will push the object to the remote object storage service.
|
||||
|
||||
While we can continue manipulating `$localObject`, we are working with
|
||||
the local version, not the latest version of what's on the server. This
|
||||
is fine if what we are doing is writing more data. However, when
|
||||
examining the content of the object, remember that we are working with
|
||||
the local copy, and its properties may differ from the remote copy's.
|
||||
|
||||
### What if I call save() twice with the same Object?
|
||||
|
||||
Objects, like files on a file system, are referenced by name. Any time
|
||||
you `save()` an object, it will be pushed to the remote object storage
|
||||
server, which will happily replace the old content with your newly
|
||||
submitted content.
|
||||
|
||||
Next let's turn to loading objects from the remote object storage.
|
||||
|
||||
### The Object in a Nutshell
|
||||
|
||||
The `\OpenStack\ObjectStore\v1\Resource\Object` instances are used for:
|
||||
|
||||
- Creating a local object to be stored remotely
|
||||
|
||||
This class is also the base class for the `RemoteObject` class that we
|
||||
will look at later.
|
||||
|
||||
The API is generally constructed so that a developer needn't worry about
|
||||
the differences between an `Object` and a `RemoteObject`. But in all but
|
||||
the edgiest of edge cases, you would only create an instance of
|
||||
`Object`, never of `RemoteObject`.
|
||||
|
||||
## Step 7: Loading an Object
|
||||
|
||||
Containers not only provide the methods for saving objects, but also for
|
||||
loading objects. Thus, we can fetch the object that we just created:
|
||||
|
||||
<?php
|
||||
$object = $container->object('hello.txt');
|
||||
|
||||
printf("Name: %s \n", $object->name());
|
||||
printf("Size: %d \n", $object->contentLength());
|
||||
printf("Type: %s \n", $object->contentType());
|
||||
print $object->content() . PHP_EOL;
|
||||
?>
|
||||
|
||||
The `$object` variable now references an instance of a
|
||||
`\OpenStack\ObjectStore\v1\Resource\RemoteObject` that contains the entire
|
||||
object. `RemoteObject` represents an object that was loaded from the
|
||||
remote server. Along with providing the features of the `Object` class
|
||||
we saw earlier, it also provides numerous optimizations for working over
|
||||
the network.
|
||||
|
||||
Now that we have the object, we print out several pieces of information
|
||||
-- `name()`, `size()`, amd `type()`. Then, using `content()`, we fetch
|
||||
the content of the object.
|
||||
|
||||
### Lazily Loading an Object
|
||||
|
||||
The method we used above to fetch the object is perfect for our needs.
|
||||
It pulls the entire object down in a single request. But imagine this
|
||||
scenario: Our object storage has large media files, and we don't know
|
||||
at loading time whether or not we need to access the body content, or
|
||||
just the other data about the object.
|
||||
|
||||
It would be a time-consuming task to download the entire body of a large
|
||||
media file if we don't actually use the body. On the other hand, from an
|
||||
API standpoint it is great to be able to pass around a single object,
|
||||
and not require the application to know whether or not the body has been
|
||||
retrieved.
|
||||
|
||||
The `RemoteObject` solves this problem using a technique known as "lazy
|
||||
loading". That is, it can pull some of the data right away, but defer
|
||||
fetching the rest of the data until that data is actually needed.
|
||||
|
||||
To fetch an object this way, we can just swap out one line in the
|
||||
example above:
|
||||
|
||||
<?php
|
||||
$object = $container->proxyObject('hello.txt');
|
||||
|
||||
printf("Name: %s \n", $object->name());
|
||||
printf("Size: %d \n", $object->contentLength());
|
||||
printf("Type: %s \n", $object->contentType());
|
||||
print $object->content() . PHP_EOL;
|
||||
?>
|
||||
|
||||
Instead of using `object()`, we now use `proxyObject()`. This method
|
||||
immediately loads the core data about the remote object, but defers
|
||||
fetching the content until the content is requested.
|
||||
|
||||
In the example above, then, one network request is issued by
|
||||
`proxyObject()`, but another is initiated when `$object->content()` is
|
||||
called.
|
||||
|
||||
### The RemoteObject in a Nutshell
|
||||
|
||||
Instances of a `\OpenStack\ObjectStore\v1\Resource\RemoteObject` offer the following features:
|
||||
|
||||
- Access to an object stored on the remote object storage
|
||||
- A proxying mechanism for lazily loading objects
|
||||
- A stream-based API for using stream and file-based PHP functions
|
||||
- Automatic tempfile-based caching for large objects (using
|
||||
`php://temp`).
|
||||
|
||||
`RemoteObject` instances can be updated and then passed to
|
||||
`Container::save()` to update the copy on the server, too.
|
||||
|
||||
## Summary
|
||||
|
||||
At this point we have created a very basic script that connects to
|
||||
OpenStack and works with object storage. Clearly, this only scratches the
|
||||
surface of what the OpenStack PHP-Client library does. But hopefully this is
|
||||
enough to get you started with the library.
|
||||
|
||||
\see oo-tutorial-code.php
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use \OpenStack\Bootstrap;
|
||||
|
||||
Bootstrap::useStreamWrappers();
|
||||
|
||||
$ini = parse_ini_file(getenv('HOME') . '/.OpenStack.ini');
|
||||
$settings = [
|
||||
'account' => $ini['account'],
|
||||
'key' => $ini['secret'],
|
||||
'tenantid' => $ini['tenantId'],
|
||||
'endpoint' => $ini['url'],
|
||||
];
|
||||
Bootstrap::setConfiguration($settings);
|
||||
|
||||
// Create a new file and write it to the object store.
|
||||
$newfile = fopen('swift://Example/my_file.txt', 'w');
|
||||
fwrite($newfile, "Good Morning!");
|
||||
fclose($newfile);
|
||||
|
||||
// Check for an object:
|
||||
if (file_exists('swift://Example/my_file.txt')) {
|
||||
print "Found my_file.txt." . PHP_EOL;
|
||||
}
|
||||
|
||||
// Get an entire object at once:
|
||||
$file = file_get_contents('swift://Example/my_file.txt');
|
||||
print 'File: ' . $file . PHP_EOL;
|
||||
|
||||
$cxt = stream_context_create([
|
||||
'swift' => [
|
||||
'account' => $ini['account'],
|
||||
'key' => $ini['secret'],
|
||||
'tenantid' => $ini['tenantId'],
|
||||
'endpoint' => $ini['url'],
|
||||
],
|
||||
]);
|
||||
|
||||
print file_get_contents('swift://Example/my_file.txt', FALSE, $cxt);
|
@ -1,236 +0,0 @@
|
||||
Tutorial: Using Stream Wrappers {#streams-tutorial}
|
||||
===============================
|
||||
|
||||
This is an introduction to the OpenStack PHP-Client library. While the library is
|
||||
large and feature-rich, this tutorial focuses on the Stream Wrapper
|
||||
feature. (There is also a [tutorial about the object-oriented
|
||||
library](@ref 00-tutorial).)
|
||||
|
||||
## TL;DR
|
||||
|
||||
With a few lines of setup code, you can fetch objects from OpenStack's
|
||||
object storage using built-in PHP functions like this:
|
||||
|
||||
<?php
|
||||
// Create a new file and write it to the object store.
|
||||
$newfile = fopen('swift://Example/my_file.txt', 'w');
|
||||
fwrite($newfile, "Good Morning!");
|
||||
fclose($newfile);
|
||||
|
||||
// Check for an object:
|
||||
if (file_exists('swift://Example/my_file.txt')) {
|
||||
print "Found my_file.txt." . PHP_EOL;
|
||||
}
|
||||
|
||||
// Get an entire object at once:
|
||||
$file = file_get_contents('swift://Example/my_file.txt');
|
||||
print 'File: ' . $file . PHP_EOL;
|
||||
?>
|
||||
|
||||
In fact, the vast majority of file and stream functions work with
|
||||
OpenStack's `swift://` URLs.
|
||||
|
||||
The rest of this tutorial explains how they work.
|
||||
|
||||
## The Setup
|
||||
|
||||
The example above does not show the code necessary for initializing the
|
||||
OpenStack PHP-Client stream wrapper. In this section, we will look at the necessary
|
||||
setup code.
|
||||
|
||||
### Loading Classes
|
||||
|
||||
The OpenStack PHP-Client library is structured following PSR-4 recommendations.
|
||||
Practically speaking, what this means is that applications that use an
|
||||
PSR-4 autoloader may be able to automatically load the OpenStack PHP-Client.
|
||||
|
||||
However, we'll assume that that is not the case. We'll assume that the
|
||||
library needs to be initialized manually.
|
||||
|
||||
What we will do is first load the PHP-Client Bootstrap.php file, and then
|
||||
use the autoloader in that file to load the rest of the library:
|
||||
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use \OpenStack\Bootstrap;
|
||||
|
||||
Bootstrap::useStreamWrappers();
|
||||
|
||||
The first thing the example above does is require Composer's autoloader
|
||||
file, which contains code necessary to autoload anything else we will need.
|
||||
|
||||
Next, we call Bootstrap::useStreamWrappers(), which tells OpenStack to register
|
||||
its stream wrapper classes.
|
||||
|
||||
In a nutshell, PHP allows libraries to map a particular URL pattern to a
|
||||
stream wrapper. PHP-Client registers the `swift://` URL prefix. So any
|
||||
request to a URL beginning with `swift://` will be proxied through the
|
||||
OpenStack PHP-Client library.
|
||||
|
||||
## Setting Up Authentication
|
||||
|
||||
When working with remote OpenStack Object Storage, you must authenticate
|
||||
to the remote system. Authentication requires the following four pieces
|
||||
of information:
|
||||
|
||||
- account: Your account ID
|
||||
- key: Your account's secret key
|
||||
- tenantid: The tenant ID for the services you wish to use
|
||||
- endpoint: The endpoint URL for OpenStack's Identity Service. It usually
|
||||
looks something like this: `https://region-a.geo-1.identity.hpcloudsvc.com:35357`
|
||||
|
||||
All four of these pieces of information can be found in the **API Keys**
|
||||
section of your console account.
|
||||
|
||||
(Note: You can use your username and password instead of account and
|
||||
key, but you still must supply the tenant ID. Instead of supplying
|
||||
`account` and `key`, use `username` and `password`.)
|
||||
|
||||
We are going to look at two ways to set authentication information. The
|
||||
first is global. That means we supply it once, and all stream and file
|
||||
functions automatically use that information. The second is to pass
|
||||
authentication information into the stream context.
|
||||
|
||||
### Global Configuration
|
||||
|
||||
Supplying global account information has two distinct advantages:
|
||||
|
||||
-# It reduces the complexity of your code
|
||||
-# It allows context-less functions like `file_exists` and `stat` to
|
||||
work.
|
||||
|
||||
But it has a disadvantage: *Only one account can be used at a time.* Since
|
||||
that account's information is shared across all stream wrappers, they
|
||||
all share the same account, tenant Id, and service catalog.
|
||||
|
||||
If you are working on an application that needs to connect to more than
|
||||
one account in the same request, you may find this setup imperfect for
|
||||
your needs.
|
||||
|
||||
That said, here's how we set up a global configuration:
|
||||
|
||||
$settings = array(
|
||||
'username' => YOUR_USERNAME,
|
||||
'password' => YOUR_PASSWORD,
|
||||
'tenantid' => YOUR_TENANT_ID,
|
||||
'endpoint' => IDENTITY_SERVICES_URL,
|
||||
);
|
||||
Bootstrap::setConfiguration($settings);
|
||||
|
||||
Basically, what we do above is declare an associative array of
|
||||
configuration parameters and then tell OpenStack::Bootstrap to set these
|
||||
as the default configuration.
|
||||
|
||||
Once the above is done, all of those PHP stream and file functions will
|
||||
just work. All you need to do is pass them `swift://` URLs, and they
|
||||
will do the rest.
|
||||
|
||||
## The Format of Swift URLs
|
||||
|
||||
Early in the tutorial we saw some swift URLs like this:
|
||||
`swift://Example/my_file.txt` . What is this URL referencing?
|
||||
|
||||
The URL above has three important parts, in the form
|
||||
`swift://CONTAINER/OBJECT_NAME`.
|
||||
|
||||
- *swift://*: This is the schema. This part of the URL tells PHP to pass
|
||||
the request to the OpenStack stream wrapper. (Swift, by the way, is the
|
||||
[OpenStack name for object storage](http://openstack.org/projects/storage/).
|
||||
- *Example*: This is the *container name*. In Object Storage parlance, a
|
||||
container is a place to store documents. One account can have lots of
|
||||
containers, and each container can have lots of objects.
|
||||
- *my_file.txt*: This is the object name. An object is basically the
|
||||
same as a file.
|
||||
|
||||
Swift does not support directories, but it does allow slashes in object
|
||||
names. So `swift://Example/this/is/my/file.png' checks the container
|
||||
*Example* for the object named `this/is/my/file.png`.
|
||||
|
||||
(For power users, there are some fancy operations you can do to treat
|
||||
Swift filename parts as if they were directories. Check out
|
||||
`\OpenStack\ObjectStore\v1\Resource\Container`.)
|
||||
|
||||
## Using Stream Contexts for Authentication
|
||||
|
||||
Sometimes it is better to pass authentication information directly to
|
||||
the stream or file function, instead of relying upon a global
|
||||
configuration. PHP provides for this with **stream contexts**.
|
||||
|
||||
Stream contexts have one major downside: Not all PHP functions accept
|
||||
stream contexts. Here are some notable examples:
|
||||
|
||||
- file_exists()
|
||||
- is_readable()
|
||||
- stat()
|
||||
|
||||
(Basically, anything that calls the underlying `stat(3)`.)
|
||||
|
||||
The advantage, though, is that each call can have its own authentication
|
||||
data. This is good for supporting multiple accounts, and can also be
|
||||
used to optimize long-term performance (e.g. by saving authentication
|
||||
tokens in a database and re-using them).
|
||||
|
||||
Here's how a stream context is used:
|
||||
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use \OpenStack\Bootstrap;
|
||||
|
||||
Bootstrap::useStreamWrappers();
|
||||
|
||||
$cxt = stream_context_create(array(
|
||||
'swift' => array(
|
||||
'username' => YOUR_USERNAME,
|
||||
'password' => YOUR_PASSWORD,
|
||||
'tenantid' => YOUR_TENANT_ID,
|
||||
'endpoint' => IDENTITY_SERVICES_URL,
|
||||
),
|
||||
));
|
||||
|
||||
print file_get_contents('swift://Example/my_file.txt', FALSE, $cxt);
|
||||
?>
|
||||
|
||||
The main difference is the creation of `$cxt` using PHP's
|
||||
`stream_context_create()`. To fully understand this, you may want to
|
||||
take a look at the [PHP documentation](http://us3.php.net/manual/en/book.stream.php)
|
||||
for streams.
|
||||
|
||||
## Stream Wrapper As A File System
|
||||
As it was noted earlier in this tutorial, swift does not support directories.
|
||||
Instead the names of a file can be path like with a separator. For example,
|
||||
`swiftfs://Example/path/to/my_file.txt` has a name of `path/to/my_file.txt`.
|
||||
|
||||
To enable applications to use swift in a more directory like manner there is a
|
||||
second stream wrapper with a prefix `swiftfs://`. swiftfs stands for swift file
|
||||
system. It works in a similar manner to to the standard stream wrappers with a
|
||||
few key differences:
|
||||
|
||||
- mkdir will return TRUE is no objects start with the directory you are trying
|
||||
to crate. Otherwise it will return FALSE.
|
||||
- rmdir will return FALSE if any objects start with the directory prefix you are
|
||||
trying to remove. rmdir does not allow you to remove directories with files
|
||||
in them.
|
||||
- Running stat on a directory that is a prefix for some objects (e.g.,
|
||||
`swiftfs://Example/path/to/`) will see this is a prefix for a file and treat
|
||||
it as if it were a directory.
|
||||
|
||||
To use this stream wrapper instead of the standard swift one simple replace the
|
||||
usage of `swift://` with `swiftfs://`.
|
||||
|
||||
## Summary
|
||||
|
||||
This tutorial is focused on using stream wrappers to interact with your
|
||||
OpenStack Object Storage service. We focused on configuring the
|
||||
environment for transparently using PHP functions like `fopen()` and
|
||||
`file_get_contents()` to work with objects in OpenStack's object storage.
|
||||
|
||||
This is just one way of interoperating with the OpenStack PHP-Client library. For
|
||||
more detail-oriented work, you may find the Object Oriented facilities
|
||||
better suited. You can read [the OO tutorial](@ref oo-tutorial) to learn
|
||||
more about that.
|
||||
|
||||
Addidtionally, you may wish to learn more about the internals of the
|
||||
stream wrapper, the main class,
|
||||
`\OpenStack\ObjectStore\v1\Resource\StreamWrapper`, is well-documented.
|
120
doc/style.md
120
doc/style.md
@ -1,120 +0,0 @@
|
||||
Coding and Documentation Style Guide {#styleguide}
|
||||
====================================
|
||||
|
||||
This guide explain coding style, coding structure, and documentation
|
||||
style.
|
||||
|
||||
## TL;DR
|
||||
|
||||
- Read the [coding standards](https://github.com/mattfarina/Coding-Standards)
|
||||
to learn why we code the way we do.
|
||||
- Read about [PHPDoc](http://www.phpdoc.org/)
|
||||
if you're curious about our source code documentation.
|
||||
- Two spaces, no tabs.
|
||||
- WE LOVE GITHUB ISSUES AND PULL REQUESTS
|
||||
|
||||
## Code Style
|
||||
|
||||
The code in this package rigidly conforms to a given coding standard.
|
||||
The standard we use is published <a href="https://github.com/mattfarina/Coding-Standards">here</a> and is based
|
||||
on the Drupal coding standards, the PEAR coding standards, and several
|
||||
other popular standards.
|
||||
|
||||
Important highlights:
|
||||
|
||||
- Indentation uses *two space characters* and no tabs.
|
||||
- Variables and class names use CamelCasing (details above).
|
||||
|
||||
Please do your best to follow coding standards when submitting patches.
|
||||
|
||||
### Object Orientation
|
||||
|
||||
We have chosen to give the library a strongly object-oriented flavor.
|
||||
However, the stream wrapper integration provides a procudural interface
|
||||
to some of the services.
|
||||
|
||||
### Design Patterns and Coding Practices
|
||||
|
||||
Where applicable, we use established coding patterns and practices. Some
|
||||
are PHP specific (like stream wrappers), while most enjoy broader
|
||||
industry support.
|
||||
|
||||
There are a few things a developer should be aware of:
|
||||
|
||||
**Accessors and Mutators**: The naming convention for methods on an
|
||||
object are as follows:
|
||||
|
||||
- A function that accesses an object's data is a *noun*. Thus, the color
|
||||
of a fictional `Pillow` object may be accessed using
|
||||
`Pillow::color()`.
|
||||
- A function that performs an action is verbal, and this includes
|
||||
mutator functions (so-called "setters"). Thus,
|
||||
`Pillow::changeColor()`, `Pillow::setColor()`, and `Pillow::fight()` may
|
||||
all be appropriate mutator names.
|
||||
- Unless a contract (interface or superclass) requires it, we do not ever
|
||||
define "getters".
|
||||
|
||||
**Constructor Functions**: PHP does not support method overloading
|
||||
(that is, declaring multiple methods with the same name, but different
|
||||
signatures). While some languages (viz. Java, C++, C#) allow more than
|
||||
one constructor, PHP limits you to just one constructor.
|
||||
|
||||
One strategy for working around this is to create constructors that take
|
||||
vague or generic parameters, and then perform various inspection tasks
|
||||
to figure out what the parameters are:
|
||||
|
||||
~~~{.php}
|
||||
<?php
|
||||
class Pillow {
|
||||
|
||||
function __construct($name, $a2 = NULL, $a3 = NULL) {
|
||||
|
||||
// ....
|
||||
if (is_string($a2)) {
|
||||
// Do one thing...
|
||||
}
|
||||
elseif (is_object($a2)) {
|
||||
// Do another thing.
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
~~~
|
||||
|
||||
The above quickly degenerates into code that is both slow
|
||||
(because of the inspection tasks) and difficult to read and use.
|
||||
|
||||
Another option, following Objective-C and Vala, is to create constructor
|
||||
functions. These are static functions (in PHP, at least) that can build
|
||||
instances. Constructor functions have signatures like
|
||||
`Pillow::newFromJSON()` and `Pillow::newFromXML()`.
|
||||
|
||||
*This library uses constructor functions.* Generally, a very basic
|
||||
constructor is provided for cases where it is needed, but more complex
|
||||
cases are handled with specialized constructor functions.
|
||||
|
||||
**Namespaces**: The library has been divided up into namespaces
|
||||
according to the following principles:
|
||||
|
||||
- Each broad service category should have a namespace. Currently, the
|
||||
service categories are *Services* and *Storage*.
|
||||
* Services handle computing tasks on behalf of the client.
|
||||
* Storage handles data storage and retrieval
|
||||
- Individual services and storage services may have their own namespace
|
||||
if the number of supporting classes requires this.
|
||||
- The *Transport* namespace deals with lower-level details that are
|
||||
shared across all services.
|
||||
|
||||
Otherwise, we have attempted to keep the namespace relatively shallow.
|
||||
|
||||
### Balancing Performance and Elegance
|
||||
|
||||
Any network-based library must struggle with the inefficiencies of
|
||||
working over a network. This library is no exception. We've done our
|
||||
best to straddle the line between keeping load times down and making the
|
||||
code simple and elegant. Doubtless we have sometimes failed. Please feel
|
||||
free to suggest improvements on either side of the scales.
|
||||
|
||||
## Documentation Style
|
||||
|
||||
This project is documented with PHPDoc.
|
@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit colors="true" bootstrap="./tests/bootstrap.php">
|
||||
<testsuites>
|
||||
<testsuite name="PHPUnit">
|
||||
<directory>tests/Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<log
|
||||
type="coverage-html"
|
||||
target="build/coverage"
|
||||
charset="UTF-8"
|
||||
yui="true"
|
||||
highlight="true"
|
||||
lowUpperBound="35"
|
||||
highLowerBound="70"
|
||||
showUncoveredFiles="true"
|
||||
/>
|
||||
<log type="coverage-clover" target="build/logs/clover.xml"/>
|
||||
</logging>
|
||||
</phpunit>
|
@ -1,298 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack;
|
||||
|
||||
use OpenStack\Identity\v2\IdentityService;
|
||||
use OpenStack\ObjectStore\v1\Resource\StreamWrapper;
|
||||
use OpenStack\ObjectStore\v1\Resource\StreamWrapperFS;
|
||||
|
||||
/**
|
||||
* Bootstrapping services.
|
||||
*
|
||||
* There is no requirement that this class be used. OpenStack is
|
||||
* built to be flexible, and any individual component can be
|
||||
* used directly, with one caveat: No explicit `require` or
|
||||
* `include` calls are made.
|
||||
*
|
||||
* This class provides the following services:
|
||||
*
|
||||
* - Configuration: "global" settings are set here.
|
||||
* See the setConfiguration() method to see how they
|
||||
* can be set, and the config() and hasConfig() methods to see
|
||||
* how configuration might be checked.
|
||||
* - Stream Wrappers: This class can initialize a set of stream
|
||||
* wrappers which will make certain OpenStack services available
|
||||
* through the core PHP stream support.
|
||||
*
|
||||
* Configuration
|
||||
*
|
||||
* Configuration directives can be merged into the existing confiuration
|
||||
* using the setConfiguration method.
|
||||
*
|
||||
* <?php
|
||||
* $config = array(
|
||||
* // We use Guzzle, which defaults to CURL, for a transport layer.
|
||||
* 'transport' => 'OpenStack\Common\Transport\Guzzle\GuzzleAdapter',
|
||||
* // Set the HTTP max wait time to 500 seconds.
|
||||
* 'transport.timeout' => 500,
|
||||
* );
|
||||
* Bootstrap::setConfiguration($config);
|
||||
*
|
||||
* // Check and get params.
|
||||
* if (Bootstrap::hasConf('transport.timeout') {
|
||||
* $to = Bootstrap::conf('transport.timeout');
|
||||
* }
|
||||
*
|
||||
* // Or get a param with a default value:
|
||||
* $val = Bootstrap::conf('someval', 'default value');
|
||||
*
|
||||
* // $val will be set to 'default value' because there
|
||||
* // is no 'someval' configuration param.
|
||||
*
|
||||
* ?>
|
||||
*
|
||||
* STREAM WRAPPERS
|
||||
*
|
||||
* Stream wrappers allow you to use the built-in file manipulation
|
||||
* functions in PHP to interact with other services. Specifically,
|
||||
* the OpenStack stream wrappers allow you to use built-in file commands
|
||||
* to access Object Storage (Swift) and other OpenStack services using
|
||||
* commands like file_get_contents() and fopen().
|
||||
*
|
||||
* It's awesome. Trust me.
|
||||
*/
|
||||
class Bootstrap
|
||||
{
|
||||
const VERSION = '0.0.1';
|
||||
|
||||
public static $config = [
|
||||
// The transport implementation. By default, we use the Guzzle Client
|
||||
'transport' => 'OpenStack\Common\Transport\Guzzle\GuzzleAdapter',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \OpenStack\Identity\v2\IdentityService An identity services object
|
||||
* created from the global settings.
|
||||
*/
|
||||
public static $identity = null;
|
||||
|
||||
/**
|
||||
* @var \OpenStack\Common\Transport\ClientInterface A transport client for requests.
|
||||
*/
|
||||
public static $transport = null;
|
||||
|
||||
/**
|
||||
* Register stream wrappers for OpenStack.
|
||||
*
|
||||
* This registers the ObjectStorage stream wrappers, which allow you to access
|
||||
* ObjectStorage through standard file access mechanisms.
|
||||
*
|
||||
* // Enable stream wrapper.
|
||||
* Bootstrap::useStreamWrappers();
|
||||
*
|
||||
* // Create a context resource.
|
||||
* $cxt = stream_context_create(array(
|
||||
* 'tenantid' => '12de21',
|
||||
* 'username' => 'foobar',
|
||||
* 'password' => 'f78saf7hhlll',
|
||||
* 'endpoint' => 'https://identity.hpcloud.com' // <-- not real URL!
|
||||
* ));
|
||||
*
|
||||
* // Get the contents of a Swift object.
|
||||
* $content = file_get_contents('swift://public/notes.txt', 'r', false, $cxt);
|
||||
*/
|
||||
public static function useStreamWrappers()
|
||||
{
|
||||
self::enableStreamWrapper(
|
||||
StreamWrapper::DEFAULT_SCHEME,
|
||||
'OpenStack\ObjectStore\v1\Resource\StreamWrapper'
|
||||
);
|
||||
self::enableStreamWrapper(
|
||||
StreamWrapperFS::DEFAULT_SCHEME,
|
||||
'OpenStack\ObjectStore\v1\Resource\StreamWrapperFS'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a stream wrapper according to its scheme and class
|
||||
*
|
||||
* @param $scheme Stream wrapper's scheme
|
||||
* @param $class The class that contains stream wrapper functionality
|
||||
*/
|
||||
private static function enableStreamWrapper($scheme, $class)
|
||||
{
|
||||
if (in_array($scheme, stream_get_wrappers())) {
|
||||
stream_wrapper_unregister($scheme);
|
||||
}
|
||||
|
||||
stream_wrapper_register($scheme, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set configuration directives for OpenStack.
|
||||
*
|
||||
* This merges the provided associative array into the existing
|
||||
* configuration parameters (Bootstrap::$config).
|
||||
*
|
||||
* All of the OpenStack classes share the same configuration. This
|
||||
* ensures that a stable runtime environment is maintained.
|
||||
*
|
||||
* Common configuration directives:
|
||||
*
|
||||
* - 'transport': The namespaced classname for the transport that
|
||||
* should be used. Example: \OpenStack\Common\Transport\Guzzle\GuzzleAdapter
|
||||
* - 'transport.debug': The integer 1 for enabling debug, 0 for
|
||||
* disabling. Enabling will turn on verbose debugging output
|
||||
* for any transport that supports it.
|
||||
* - 'transport.timeout': An integer value indicating how long
|
||||
* the transport layer should wait for an HTTP request. A
|
||||
* transport MAY ignore this parameter, but the ones included
|
||||
* with the library honor it.
|
||||
* - 'transport.ssl_verify': Set this to false to turn off SSL certificate
|
||||
* verification. This is NOT recommended, but is sometimes necessary for
|
||||
* certain proxy configurations.
|
||||
* - 'transport.proxy': Set the proxy as a string.
|
||||
* - 'username' and 'password'
|
||||
* - 'tenantid'
|
||||
* - 'endpoint': The full URL to identity services. This is used by stream
|
||||
* wrappers.
|
||||
*
|
||||
* @param array $array An associative array of configuration directives.
|
||||
*/
|
||||
public static function setConfiguration($array)
|
||||
{
|
||||
self::$config = $array + self::$config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration option.
|
||||
*
|
||||
* Get a configuration option by name, with an optional default.
|
||||
*
|
||||
* @param string $name The name of the configuration option to get.
|
||||
* @param mixed $default The default value to return if the name is not found.
|
||||
*
|
||||
* @return mixed The value, if found; or the default, if set; or null.
|
||||
*/
|
||||
public static function config($name = null, $default = null)
|
||||
{
|
||||
// If no name is specified, return the entire config array.
|
||||
if (empty($name)) {
|
||||
return self::$config;
|
||||
}
|
||||
|
||||
// If the config value exists, return that.
|
||||
if (isset(self::$config[$name])) {
|
||||
return self::$config[$name];
|
||||
}
|
||||
|
||||
// Otherwise, just return the default value.
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given configuration option is set.
|
||||
*
|
||||
* if (Bootstrap::hasConfig('transport')) {
|
||||
* syslog(LOG_INFO, 'An alternate transport is supplied.');
|
||||
* }
|
||||
*
|
||||
* @param string $name The name of the item to check for.
|
||||
*
|
||||
* @return boolean true if the named option is set, false otherwise. Note that
|
||||
* the value may be falsey (false, 0, etc.), but if the value is null, this
|
||||
* will return false.
|
||||
*/
|
||||
public static function hasConfig($name)
|
||||
{
|
||||
return isset(self::$config[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a \OpenStack\Identity\v2\IdentityService object from the bootstrap config.
|
||||
*
|
||||
* A factory helper function that uses the bootstrap configuration to create
|
||||
* a ready to use \OpenStack\Identity\v2\IdentityService object.
|
||||
*
|
||||
* @param bool $force Whether to force the generation of a new object even if
|
||||
* one is already cached.
|
||||
*
|
||||
* @return \OpenStack\Identity\v2\IdentityService An authenticated ready to use
|
||||
* \OpenStack\Identity\v2\IdentityService object.
|
||||
* @throws \OpenStack\Common\Exception When the needed configuration to authenticate
|
||||
* is not available.
|
||||
*/
|
||||
public static function identity($force = false)
|
||||
{
|
||||
$transport = self::transport();
|
||||
|
||||
// If we already have an identity make sure the token is not expired.
|
||||
if ($force || is_null(self::$identity) || self::$identity->isExpired()) {
|
||||
|
||||
// Make sure we have an endpoint to use
|
||||
if (!self::hasConfig('endpoint')) {
|
||||
throw new Exception('Unable to authenticate. No endpoint supplied.');
|
||||
}
|
||||
|
||||
// User cannot be an empty string, so we need
|
||||
// to do more checking than self::hasConfig(), which returns true
|
||||
// if an item exists and is an empty string.
|
||||
$user = self::config('username', null);
|
||||
|
||||
// Check if we have a username/password
|
||||
if (!empty($user) && self::hasConfig('password')) {
|
||||
$is = new IdentityService(self::config('endpoint'), $transport);
|
||||
$is->authenticateAsUser($user, self::config('password'), self::config('tenantid', null), self::config('tenantname', null));
|
||||
self::$identity = $is;
|
||||
} else {
|
||||
throw new Exception('Unable to authenticate. No user credentials supplied.');
|
||||
}
|
||||
}
|
||||
|
||||
return self::$identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a transport client.
|
||||
*
|
||||
* @param boolean $reset Whether to recreate the transport client if one already exists.
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ClientInterface A transport client.
|
||||
*/
|
||||
public static function transport($reset = false)
|
||||
{
|
||||
if (is_null(self::$transport) || $reset == true) {
|
||||
$options = [
|
||||
'ssl_verify' => self::config('ssl_verify', true),
|
||||
'timeout' => self::config('timeout', 0), // 0 is no timeout.
|
||||
'debug' => self::config('debug', 0),
|
||||
];
|
||||
$proxy = self::config('proxy', false);
|
||||
if ($proxy) {
|
||||
$options['proxy'] = $proxy;
|
||||
}
|
||||
|
||||
$class = self::config('transport');
|
||||
self::$transport = $class::create($options);
|
||||
}
|
||||
|
||||
return self::$transport;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 OpenStack\Common;
|
||||
/**
|
||||
* The top-level OpenStack exception.
|
||||
*
|
||||
* In most cases, the library will throw a more finely
|
||||
* grained exception, but all exceptions thrown directly
|
||||
* by OpenStack will be an instance of this exception.
|
||||
*/
|
||||
class Exception extends \Exception {}
|
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport;
|
||||
|
||||
/**
|
||||
* Class that implements {@see ClientInterface} and contains common
|
||||
* functionality for clients or client adapters. Most of the methods defined
|
||||
* are purely for convenience, and don't necessarily need a client to implement
|
||||
* them with their own custom logic.
|
||||
*/
|
||||
abstract class AbstractClient implements ClientInterface
|
||||
{
|
||||
public function get($uri, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('GET', $uri, null, $options));
|
||||
}
|
||||
|
||||
public function head($uri, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('HEAD', $uri, null, $options));
|
||||
}
|
||||
|
||||
public function post($uri, $body = null, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('POST', $uri, $body, $options));
|
||||
}
|
||||
|
||||
public function put($uri, $body = null, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('PUT', $uri, $body, $options));
|
||||
}
|
||||
|
||||
public function delete($uri, array $options = [])
|
||||
{
|
||||
return $this->send($this->createRequest('DELETE', $uri, null, $options));
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport;
|
||||
|
||||
/**
|
||||
* Describes a transport client.
|
||||
*
|
||||
* Transport clients are responsible for moving data from the remote cloud to
|
||||
* the local host. Transport clients are responsible only for the transport
|
||||
* protocol, not for the payloads.
|
||||
*
|
||||
* The current OpenStack services implementation is oriented toward
|
||||
* REST-based services, and consequently the transport layers are
|
||||
* HTTP/HTTPS, and perhaps SPDY some day. The interface reflects this.
|
||||
* it is not designed as a protocol-neutral transport layer
|
||||
*/
|
||||
interface ClientInterface
|
||||
{
|
||||
/**
|
||||
* Create a new Request object. To send, use the {see send()} method.
|
||||
*
|
||||
* @param string $method HTTP method
|
||||
* @param string|array|\OpenStack\Common\Transport\Url $uri URL the request will send to
|
||||
* @param string|resource $body Entity body being sent
|
||||
* @param array $options Configuration options, such as headers
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\RequestInterface
|
||||
*/
|
||||
public function createRequest($method,
|
||||
$uri = null,
|
||||
$body = null,
|
||||
array $options = []);
|
||||
|
||||
/**
|
||||
* Sends a request.
|
||||
*
|
||||
* @param \OpenStack\Common\Transport\RequestInterface $request Request to execute
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ResponseInterface
|
||||
*/
|
||||
public function send(RequestInterface $request);
|
||||
|
||||
/**
|
||||
* Execute a GET request.
|
||||
*
|
||||
* @param string|array|\OpenStack\Common\Transport\Url $uri URL the request will send to
|
||||
* @param array $options Configuration options, such as headers
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ResponseInterface
|
||||
*/
|
||||
public function get($uri, array $options = []);
|
||||
|
||||
/**
|
||||
* Execute a HEAD request.
|
||||
*
|
||||
* @param string|array|\OpenStack\Common\Transport\Url $uri URL the request will send to
|
||||
* @param array $options Configuration options, such as headers
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ResponseInterface
|
||||
*/
|
||||
public function head($uri, array $options = []);
|
||||
|
||||
/**
|
||||
* Execute a POST request.
|
||||
*
|
||||
* @param string|array|\OpenStack\Common\Transport\Url $uri URL the request will send to
|
||||
* @param mixed $body Entity body being sent
|
||||
* @param array $options Configuration options, such as headers
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ResponseInterface
|
||||
*/
|
||||
public function post($uri, $body, array $options = []);
|
||||
|
||||
/**
|
||||
* Execute a PUT request.
|
||||
*
|
||||
* @param string|array|\OpenStack\Common\Transport\Url $uri URL the request will send to
|
||||
* @param mixed $body Entity body being sent
|
||||
* @param array $options Configuration options, such as headers
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ResponseInterface
|
||||
*/
|
||||
public function put($uri, $body, array $options = []);
|
||||
|
||||
/**
|
||||
* Execute a DELETE request.
|
||||
*
|
||||
* @param string|array|\OpenStack\Common\Transport\Url $uri URL the request will send to
|
||||
* @param array $options Configuration options, such as headers
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ResponseInterface
|
||||
*/
|
||||
public function delete($uri, array $options = []);
|
||||
|
||||
/**
|
||||
* Sets a particular configuration option, depending on how the client
|
||||
* implements it. It could, for example, alter cURL configuration or a
|
||||
* default header.
|
||||
*
|
||||
* @param string $key The key being updated
|
||||
* @param mixed $value The value being set
|
||||
*/
|
||||
public function setOption($key, $value);
|
||||
|
||||
/**
|
||||
* Returns the value of a particular configuration option. If the options
|
||||
* is not set, NULL is returned.
|
||||
*
|
||||
* @param string $key The option name
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getOption($key);
|
||||
|
||||
/**
|
||||
* Returns the base URL that the client points towards.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseUrl();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
/**
|
||||
* Exception that represents a 409 Conflict HTTP error.
|
||||
*
|
||||
* This class is thrown when a request could not be completed due to a conflict
|
||||
* with the current state of the API resource. For example, when a remote
|
||||
* container cannot be deleted because it is not empty.
|
||||
*/
|
||||
class ConflictException extends RequestException
|
||||
{
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
/**
|
||||
* Exception that represents a 403 Forbidden HTTP error.
|
||||
*
|
||||
* This class is thrown when a server has understood the request, but is
|
||||
* refusing to fulfill it. For example, a user has successfully authenticated
|
||||
* but is not authorized to perform a particular action - perhaps due to ACL
|
||||
* criteria.
|
||||
*/
|
||||
class ForbiddenException extends RequestException
|
||||
{
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
/**
|
||||
* Exception that represents a 411 Length Required HTTP error.
|
||||
*
|
||||
* This class is thrown when a server refused to accept the request without a
|
||||
* defined Content-Length or Content-Type header. For example, this might occur
|
||||
* when uploading an object without the necessary Content- headers.
|
||||
*/
|
||||
class LengthRequiredException extends RequestException
|
||||
{
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
/**
|
||||
* Exception that represents a 405 Method Not Allowed HTTP error.
|
||||
*
|
||||
* This class is thrown when a request's specified method is not allowed by the
|
||||
* server.
|
||||
*/
|
||||
class MethodNotAllowedException extends RequestException
|
||||
{
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
use OpenStack\Common\Exception;
|
||||
use OpenStack\Common\Transport\RequestInterface;
|
||||
use OpenStack\Common\Transport\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Base exception that is thrown for requests that result in a HTTP error.
|
||||
*/
|
||||
class RequestException extends Exception
|
||||
{
|
||||
/** @var \OpenStack\Common\Transport\RequestInterface */
|
||||
protected $request;
|
||||
|
||||
/** @var \OpenStack\Common\Transport\ResponseInterface */
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Construct this exception like any other, but also inject Request and
|
||||
* Response objects in case the user needs them for debugging.
|
||||
*
|
||||
* @param string $errorMessage Human-readable explanation of error
|
||||
* @param \OpenStack\Common\Transport\RequestInterface $request The failed request
|
||||
* @param \OpenStack\Common\Transport\ResponseInterface $response The server's response
|
||||
*/
|
||||
public function __construct($errorMessage, RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
parent::__construct($errorMessage, $response->getStatusCode());
|
||||
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method that creates an appropriate Exception object based on the
|
||||
* Response's status code. The message is constructed here also.
|
||||
*
|
||||
* @param \OpenStack\Common\Transport\RequestInterface $request The failed request
|
||||
* @param \OpenStack\Common\Transport\ResponseInterface $response The API's response
|
||||
* @return self
|
||||
*/
|
||||
public static function create(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$label = 'A HTTP error occurred';
|
||||
|
||||
$status = $response->getStatusCode();
|
||||
|
||||
$exceptions = [
|
||||
401 => 'UnauthorizedException',
|
||||
403 => 'ForbiddenException',
|
||||
404 => 'ResourceNotFoundException',
|
||||
405 => 'MethodNotAllowedException',
|
||||
409 => 'ConflictException',
|
||||
411 => 'LengthRequiredException',
|
||||
422 => 'UnprocessableEntityException',
|
||||
500 => 'ServerException'
|
||||
];
|
||||
|
||||
$message = sprintf(
|
||||
"%s\n[Status] %s (%s)\n[URL] %s\n[Message] %s\n", $label,
|
||||
(string) $request->getUrl(),
|
||||
$status, $response->getReasonPhrase(),
|
||||
(string) $response->getBody()
|
||||
);
|
||||
|
||||
// Find custom exception class or use default
|
||||
$exceptionClass = isset($exceptions[$status])
|
||||
? sprintf("%s\\%s", __NAMESPACE__, $exceptions[$status])
|
||||
: __CLASS__;
|
||||
|
||||
return new $exceptionClass($message, $request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server response.
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ResponseInterface
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request that caused error.
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\RequestInterface
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
/**
|
||||
* Exception that represents a 404 Not Found HTTP error.
|
||||
*
|
||||
* This class is thrown when a server has not found any resource matching the
|
||||
* Request's URI.
|
||||
*/
|
||||
class ResourceNotFoundException extends RequestException
|
||||
{
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
/**
|
||||
* Exception that represents a 500 Internal Server Error.
|
||||
*
|
||||
* This class is thrown when a server encounters an unexpected condition which
|
||||
* prevents it from fulfilling the request. Sometimes this error is used as a
|
||||
* generic catch-all by an OpenStack API.
|
||||
*/
|
||||
class ServerException extends RequestException
|
||||
{
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
/**
|
||||
* Exception that represents a 401 Unauthorized HTTP error.
|
||||
*
|
||||
* This class is thrown when a server indicates that authorization has been
|
||||
* refused for a set of credentials.
|
||||
*/
|
||||
class UnauthorizedException extends RequestException
|
||||
{
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Exception;
|
||||
|
||||
/**
|
||||
* Exception that represents a 422 Unprocessable Entity HTTP error.
|
||||
*
|
||||
* This class is thrown when a request was well-formed but was unable to be
|
||||
* processed due to semantic errors.
|
||||
*/
|
||||
class UnprocessableEntityException extends RequestException
|
||||
{
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Guzzle;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\ClientInterface as GuzzleClientInterface;
|
||||
use GuzzleHttp\Exception\RequestException as GuzzleRequestException;
|
||||
use OpenStack\Bootstrap;
|
||||
use OpenStack\Common\Transport\AbstractClient;
|
||||
use OpenStack\Common\Transport\Exception;
|
||||
use OpenStack\Common\Transport\RequestInterface;
|
||||
|
||||
/**
|
||||
* An adapter class which wraps the Guzzle HTTP client. This adapter satisfies
|
||||
* {@see OpenStack\Common\Transport\ClientInterface}, acting as an intermediary
|
||||
* between Guzzle and our interface, reconciling their differences.
|
||||
*/
|
||||
class GuzzleAdapter extends AbstractClient
|
||||
{
|
||||
/**
|
||||
* @var \GuzzleHttp\Client The client being wrapped.
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* A factory method that allows for the easy creation of this adapter. It
|
||||
* accepts an array of options which will be fed into the Guzzle client.
|
||||
* This method also handles the configuration of the client being wrapped,
|
||||
* such as overriding error handling and the default User-Agent header.
|
||||
*
|
||||
* @param array $options The options passed in to the Guzzle client. For a
|
||||
* full run-through of available configuration values,
|
||||
* view the {@link http://docs.guzzlephp.org/en/latest/clients.html#creating-a-client official docs}.
|
||||
* @return self
|
||||
*/
|
||||
public static function create(array $options = [])
|
||||
{
|
||||
if (empty($options['defaults'])) {
|
||||
$options['defaults'] = [];
|
||||
}
|
||||
|
||||
// Disable Guzzle error handling and define our own error subscriber.
|
||||
// Also override default User-Agent header with our own version.
|
||||
$options['defaults'] += ['exceptions' => false,
|
||||
'subscribers' => [new HttpError()],
|
||||
'headers' => ['User-Agent' => self::getDefaultUserAgent()]
|
||||
];
|
||||
|
||||
// Inject client and pass in options for adapter
|
||||
return new self(new Client($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new Adapter which wraps a Guzzle client.
|
||||
*
|
||||
* @param \GuzzleHttp\ClientInterface $guzzle The Client being wrapped
|
||||
*/
|
||||
public function __construct(GuzzleClientInterface $guzzle)
|
||||
{
|
||||
$this->client = $guzzle;
|
||||
}
|
||||
|
||||
public function createRequest($method, $uri = null, $body = null, array $options = [])
|
||||
{
|
||||
$headers = isset($options['headers']) ? $options['headers'] : [];
|
||||
|
||||
$request = $this->client->createRequest($method, $uri, [
|
||||
'headers' => $headers,
|
||||
'body' => $body,
|
||||
]);
|
||||
|
||||
return new RequestAdapter($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param \OpenStack\Common\Transport\RequestInterface $adapter
|
||||
* @return \OpenStack\Common\Transport\ResponseInterface
|
||||
* @throws \OpenStack\Common\Transport\Exception\RequestException
|
||||
* @throws \GuzzleHttp\Exception\RequestException
|
||||
*/
|
||||
public function send(RequestInterface $adapter)
|
||||
{
|
||||
try {
|
||||
$guzzleResponse = $this->client->send($adapter->getMessage());
|
||||
return new ResponseAdapter($guzzleResponse);
|
||||
} catch (GuzzleRequestException $e) {
|
||||
// In order to satisfy {@see GuzzleHttp\ClientInterface}, Guzzle
|
||||
// wraps all exceptions in its own RequestException class. This is
|
||||
// not useful for our end-users, so we need to make sure our own
|
||||
// versions are returned (Guzzle buffers them).
|
||||
$previous = $e->getPrevious();
|
||||
if ($previous instanceof Exception\RequestException) {
|
||||
throw $previous;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guzzle handles options using the defaults/ prefix. So if a key is passed
|
||||
* in to be set, or got, that contains this prefix - assume that its a
|
||||
* Guzzle option, not an adapter one.
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setOption($key, $value)
|
||||
{
|
||||
$this->client->setDefaultOption($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guzzle handles options using the defaults/ prefix. So if a key is passed
|
||||
* in to be set, or got, that contains this prefix - assume that its a
|
||||
* Guzzle option, not an adapter one.
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getOption($key)
|
||||
{
|
||||
if ($key == 'base_url') {
|
||||
return $this->getBaseUrl();
|
||||
} else {
|
||||
return $this->client->getDefaultOption($key);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->client->getBaseUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends the SDK's version number to the standard Guzzle string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDefaultUserAgent()
|
||||
{
|
||||
return sprintf("OpenStack/%f %s", Bootstrap::VERSION, Client::getDefaultUserAgent());
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Guzzle;
|
||||
|
||||
use GuzzleHttp\Event\CompleteEvent;
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Event\SubscriberInterface;
|
||||
use GuzzleHttp\Subscriber\HttpError as GuzzleHttpError;
|
||||
use OpenStack\Common\Transport\Exception\RequestException;
|
||||
|
||||
/**
|
||||
* A subscriber for capturing Guzzle's HTTP error events and processing them in
|
||||
* a standardised manner.
|
||||
*/
|
||||
class HttpError implements SubscriberInterface
|
||||
{
|
||||
public function getEvents()
|
||||
{
|
||||
return ['complete' => ['onComplete', RequestEvents::VERIFY_RESPONSE]];
|
||||
}
|
||||
|
||||
/**
|
||||
* When a request completes, this method is executed. Because this class
|
||||
* checks for HTTP errors and handles them, this method checks the HTTP
|
||||
* status code and invokes {@see RequestException} if necessary.
|
||||
*
|
||||
* @param CompleteEvent $event
|
||||
* @throws \OpenStack\Common\Transport\Exception\RequestException
|
||||
*/
|
||||
public function onComplete(CompleteEvent $event)
|
||||
{
|
||||
$status = (int) $event->getResponse()->getStatusCode();
|
||||
|
||||
// Has an error occurred (4xx or 5xx status)?
|
||||
if ($status >= 400 && $status <= 505) {
|
||||
$request = new RequestAdapter($event->getRequest());
|
||||
$response = new ResponseAdapter($event->getResponse());
|
||||
throw RequestException::create($request, $response);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Guzzle;
|
||||
|
||||
use OpenStack\Common\Transport\MessageInterface;
|
||||
use GuzzleHttp\Message\MessageInterface as GuzzleMessageInterface;
|
||||
|
||||
/**
|
||||
* An adapter class which wraps {@see GuzzleHttp\Message\MessageInterface}
|
||||
* objects. Until PSR releases a standardised interface that all projects can
|
||||
* share, we need to adapt the different interfaces.
|
||||
*
|
||||
* As you will notice, most of this adapter is a like-for-like method
|
||||
* translation. Although it seems verbose, it is actually a lot more explicit,
|
||||
* clearer and easier to debug than using magic methods.
|
||||
*/
|
||||
class MessageAdapter implements MessageInterface
|
||||
{
|
||||
/** @var \GuzzleHttp\Message\MessageInterface The Guzzle message being wrapped */
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* @param \GuzzleHttp\Message\MessageInterface $guzzleMessage
|
||||
*/
|
||||
public function __construct(GuzzleMessageInterface $guzzleMessage)
|
||||
{
|
||||
$this->setMessage($guzzleMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* This sets the Guzzle object being wrapped.
|
||||
*
|
||||
* @param \GuzzleHttp\Message\MessageInterface $guzzleMessage The object being wrapped.
|
||||
*/
|
||||
public function setMessage(GuzzleMessageInterface $guzzleMessage)
|
||||
{
|
||||
$this->message = $guzzleMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \GuzzleHttp\Message\MessageInterface
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function getProtocolVersion()
|
||||
{
|
||||
return $this->message->getProtocolVersion();
|
||||
}
|
||||
|
||||
public function getBody()
|
||||
{
|
||||
return $this->message->getBody();
|
||||
}
|
||||
|
||||
public function setBody(/* StreamInterface */ $body = null)
|
||||
{
|
||||
$this->message->setBody($body);
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->message->getHeaders();
|
||||
}
|
||||
|
||||
public function hasHeader($header)
|
||||
{
|
||||
return $this->message->hasHeader($header);
|
||||
}
|
||||
|
||||
public function getHeader($header, $asArray = false)
|
||||
{
|
||||
return $this->message->getHeader($header, $asArray);
|
||||
}
|
||||
|
||||
public function setHeader($header, $value)
|
||||
{
|
||||
$this->message->setHeader($header, $value);
|
||||
}
|
||||
|
||||
public function setHeaders(array $headers)
|
||||
{
|
||||
$this->message->setHeaders($headers);
|
||||
}
|
||||
|
||||
public function addHeader($header, $value)
|
||||
{
|
||||
$this->message->addHeader($header, $value);
|
||||
}
|
||||
|
||||
public function addHeaders(array $headers)
|
||||
{
|
||||
$this->message->addHeaders($headers);
|
||||
}
|
||||
|
||||
public function removeHeader($header)
|
||||
{
|
||||
$this->message->removeHeader($header);
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Guzzle;
|
||||
|
||||
use GuzzleHttp\Message\RequestInterface as GuzzleRequestInterface;
|
||||
use OpenStack\Common\Transport\RequestInterface;
|
||||
|
||||
/**
|
||||
* This class wraps {@see \GuzzleHttp\Message\RequestInterface}.
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
class RequestAdapter extends MessageAdapter implements RequestInterface
|
||||
{
|
||||
public function __construct(GuzzleRequestInterface $guzzleRequest)
|
||||
{
|
||||
$this->setMessage($guzzleRequest);
|
||||
}
|
||||
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->message->getMethod();
|
||||
}
|
||||
|
||||
public function setMethod($method)
|
||||
{
|
||||
$this->message->setMethod($method);
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->message->getUrl();
|
||||
}
|
||||
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->message->setUrl($url);
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport\Guzzle;
|
||||
|
||||
use GuzzleHttp\Message\ResponseInterface as GuzzleResponseInterface;
|
||||
use OpenStack\Common\Transport\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This class wraps {@see \GuzzleHttp\Message\ResponseInterface}.
|
||||
*
|
||||
* @inheritDoc
|
||||
*/
|
||||
class ResponseAdapter extends MessageAdapter implements ResponseInterface
|
||||
{
|
||||
public function __construct(GuzzleResponseInterface $guzzleResponse)
|
||||
{
|
||||
$this->setMessage($guzzleResponse);
|
||||
}
|
||||
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->message->getStatusCode();
|
||||
}
|
||||
|
||||
public function getReasonPhrase()
|
||||
{
|
||||
return $this->message->getReasonPhrase();
|
||||
}
|
||||
|
||||
public function json()
|
||||
{
|
||||
return $this->message->json();
|
||||
}
|
||||
}
|
@ -1,160 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport;
|
||||
|
||||
/**
|
||||
* HTTP messages consist of requests from a client to a server and responses
|
||||
* from a server to a client.
|
||||
*
|
||||
* @link https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md#31-psrhttpmessageinterface
|
||||
*/
|
||||
interface MessageInterface
|
||||
{
|
||||
/**
|
||||
* Gets the HTTP protocol version.
|
||||
*
|
||||
* @return string HTTP protocol version.
|
||||
*/
|
||||
public function getProtocolVersion();
|
||||
|
||||
/**
|
||||
* Gets the body of the message.
|
||||
*
|
||||
* @return StreamInterface|null Returns the body, or null if not set.
|
||||
*/
|
||||
public function getBody();
|
||||
|
||||
/**
|
||||
* Sets the body of the message.
|
||||
*
|
||||
* The body MUST be a StreamInterface object. Setting the body to null MUST
|
||||
* remove the existing body.
|
||||
*
|
||||
* @param StreamInterface|null $body Body.
|
||||
*
|
||||
* @return self Returns the message.
|
||||
*
|
||||
* @throws \InvalidArgumentException When the body is not valid.
|
||||
*/
|
||||
public function setBody(/* StreamInterface */ $body = null);
|
||||
|
||||
/**
|
||||
* Gets all message headers.
|
||||
*
|
||||
* The keys represent the header name as it will be sent over the wire, and
|
||||
* each value is an array of strings associated with the header.
|
||||
*
|
||||
* // Represent the headers as a string
|
||||
* foreach ($message->getHeaders() as $name => $values) {
|
||||
* echo $name . ": " . implode(", ", $values);
|
||||
* }
|
||||
*
|
||||
* @return array Returns an associative array of the message's headers.
|
||||
*/
|
||||
public function getHeaders();
|
||||
|
||||
/**
|
||||
* Checks if a header exists by the given case-insensitive name.
|
||||
*
|
||||
* @param string $header Case-insensitive header name.
|
||||
*
|
||||
* @return bool Returns true if any header names match the given header
|
||||
* name using a case-insensitive string comparison. Returns false if
|
||||
* no matching header name is found in the message.
|
||||
*/
|
||||
public function hasHeader($header);
|
||||
|
||||
/**
|
||||
* Retrieve a header by the given case-insensitive name.
|
||||
*
|
||||
* By default, this method returns all of the header values of the given
|
||||
* case-insensitive header name as a string concatenated together using
|
||||
* a comma. Because some header should not be concatenated together using a
|
||||
* comma, this method provides a Boolean argument that can be used to
|
||||
* retrieve the associated header values as an array of strings.
|
||||
*
|
||||
* @param string $header Case-insensitive header name.
|
||||
* @param bool $asArray Set to true to retrieve the header value as an
|
||||
* array of strings.
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function getHeader($header, $asArray = false);
|
||||
|
||||
/**
|
||||
* Sets a header, replacing any existing values of any headers with the
|
||||
* same case-insensitive name.
|
||||
*
|
||||
* The header values MUST be a string or an array of strings.
|
||||
*
|
||||
* @param string $header Header name
|
||||
* @param string|array $value Header value(s)
|
||||
*
|
||||
* @return self Returns the message.
|
||||
*/
|
||||
public function setHeader($header, $value);
|
||||
|
||||
/**
|
||||
* Sets headers, replacing any headers that have already been set on the
|
||||
* message.
|
||||
*
|
||||
* The array keys MUST be a string. The array values must be either a
|
||||
* string or an array of strings.
|
||||
*
|
||||
* @param array $headers Headers to set.
|
||||
*
|
||||
* @return self Returns the message.
|
||||
*/
|
||||
public function setHeaders(array $headers);
|
||||
|
||||
/**
|
||||
* Appends a header value to any existing values associated with the
|
||||
* given header name.
|
||||
*
|
||||
* @param string $header Header name to add
|
||||
* @param string $value Value of the header
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addHeader($header, $value);
|
||||
|
||||
/**
|
||||
* Merges in an associative array of headers.
|
||||
*
|
||||
* Each array key MUST be a string representing the case-insensitive name
|
||||
* of a header. Each value MUST be either a string or an array of strings.
|
||||
* For each value, the value is appended to any existing header of the same
|
||||
* name, or, if a header does not already exist by the given name, then the
|
||||
* header is added.
|
||||
*
|
||||
* @param array $headers Associative array of headers to add to the message
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addHeaders(array $headers);
|
||||
|
||||
/**
|
||||
* Remove a specific header by case-insensitive name.
|
||||
*
|
||||
* @param string $header HTTP header to remove
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function removeHeader($header);
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport;
|
||||
|
||||
/**
|
||||
* A HTTP request message.
|
||||
*
|
||||
* @link https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md#32-psrhttprequestinterface
|
||||
* @link http://tools.ietf.org/html/rfc2616#section-5
|
||||
*/
|
||||
interface RequestInterface extends MessageInterface
|
||||
{
|
||||
/**
|
||||
* Gets the HTTP method of the request.
|
||||
*
|
||||
* @return string Returns the request method.
|
||||
*/
|
||||
public function getMethod();
|
||||
|
||||
/**
|
||||
* Sets the method to be performed on the resource identified by the
|
||||
* Request-URI. While method names are case case-sensitive, implementations
|
||||
* SHOULD convert the method to all uppercase characters.
|
||||
*
|
||||
* @param string $method Case-insensitive method.
|
||||
*
|
||||
* @return self Returns the request.
|
||||
*/
|
||||
public function setMethod($method);
|
||||
|
||||
/**
|
||||
* Gets the request URL.
|
||||
*
|
||||
* @return string Returns the URL as a string.
|
||||
*/
|
||||
public function getUrl();
|
||||
|
||||
/**
|
||||
* Sets the request URL.
|
||||
*
|
||||
* The URL MUST be a string, or an object that implements the
|
||||
* `__toString()` method.
|
||||
*
|
||||
* @param string $url Request URL.
|
||||
*
|
||||
* @return self Reference to the request.
|
||||
* @throws \InvalidArgumentException If the URL is invalid.
|
||||
*/
|
||||
public function setUrl($url);
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport;
|
||||
|
||||
/**
|
||||
* A HTTP response message.
|
||||
*
|
||||
* @link https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md#33-psrhttpresponseinterface
|
||||
* @link http://tools.ietf.org/html/rfc2616#section-6
|
||||
*/
|
||||
interface ResponseInterface extends MessageInterface
|
||||
{
|
||||
/**
|
||||
* Gets the response Status-Code, a 3-digit integer result code of the
|
||||
* server's attempt to understand and satisfy the request.
|
||||
*
|
||||
* @return integer Status code.
|
||||
*/
|
||||
public function getStatusCode();
|
||||
|
||||
/**
|
||||
* Gets the response Reason-Phrase, a short textual description of the
|
||||
* Status-Code.
|
||||
*
|
||||
* Because a Reason-Phrase is not a required element in response
|
||||
* Status-Line, the Reason-Phrase value MAY be null. Implementations MAY
|
||||
* choose to return the default RFC 2616 recommended reason phrase for the
|
||||
* response's Status-Code.
|
||||
*
|
||||
* @return string|null Reason phrase, or null if unknown.
|
||||
*/
|
||||
public function getReasonPhrase();
|
||||
}
|
@ -1,321 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Common\Transport;
|
||||
|
||||
/**
|
||||
* Represents a URL, containing its various syntax components. Please note that
|
||||
* this class does not validate input or enforce RFC3986 standards; instead it
|
||||
* is meant to serve as a usable model of a URL within our SDK.
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc3986
|
||||
*/
|
||||
class Url
|
||||
{
|
||||
private $scheme;
|
||||
private $host;
|
||||
private $port;
|
||||
private $user;
|
||||
private $password;
|
||||
private $path;
|
||||
private $query = [];
|
||||
private $fragment;
|
||||
|
||||
/**
|
||||
* @param array|string $value Either a string or array input value that
|
||||
* will be parsed and populated
|
||||
*
|
||||
* @throws \InvalidArgumentException If argument is not a string or array
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
if (is_string($value)) {
|
||||
$value = parse_url($value);
|
||||
} elseif (!is_array($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Url can only be populated with a string or array of values"
|
||||
);
|
||||
}
|
||||
|
||||
$this->populateFromArray($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method that allows for the hydration of this object with an
|
||||
* array. It iterates through each element and calls the necessary setter
|
||||
* method if it exists.
|
||||
*
|
||||
* @param array $array The input array
|
||||
*/
|
||||
private function populateFromArray(array $array)
|
||||
{
|
||||
foreach ($array as $key => $val) {
|
||||
if ($key == 'pass') {
|
||||
$key = 'password';
|
||||
}
|
||||
$method = 'set' . $key;
|
||||
if ($val && method_exists($this, $method)) {
|
||||
$this->$method($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $scheme
|
||||
*/
|
||||
public function setScheme($scheme)
|
||||
{
|
||||
$this->scheme = (string)$scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getScheme()
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
*/
|
||||
public function setHost($host)
|
||||
{
|
||||
$this->host = (string)$host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $port
|
||||
*/
|
||||
public function setPort($port)
|
||||
{
|
||||
$this->port = (int)$port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPort()
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
*/
|
||||
public function setUser($user)
|
||||
{
|
||||
$this->user = (string)$user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = (string)$password;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to a string value, ensuring that a trailing slash is always
|
||||
* added.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->path = rtrim((string)$path, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a string path to the existing path value.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function addPath($path)
|
||||
{
|
||||
$path = '/' . ltrim((string)$path, '/');
|
||||
$this->setPath($this->path . $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query value. If a string is provided, it is expanded according
|
||||
* to conventional key=pair representation, where `&' is a delimeter. An
|
||||
* array can also be provided.
|
||||
*
|
||||
* @param string|array $query
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setQuery($query)
|
||||
{
|
||||
if (is_string($query)) {
|
||||
$query = $this->expandQueryString($query);
|
||||
} elseif (!is_array($query)) {
|
||||
throw new \InvalidArgumentException("Query must be an array");
|
||||
}
|
||||
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for expanding a string representation of a query into an
|
||||
* array. The return value should be a simple key/value pair. Query arrays
|
||||
* are also supported.
|
||||
*
|
||||
* @param string $value A string based query representation, in the form of
|
||||
* ?foo=val&bar=val&baz[]=val_1&baz[]=val_2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function expandQueryString($value)
|
||||
{
|
||||
$parts = explode('&', $value);
|
||||
$array = [];
|
||||
foreach ($parts as $partArray) {
|
||||
$inner = explode('=', $partArray);
|
||||
$key = str_replace('[]', '', $inner[0]);
|
||||
$val = $inner[1];
|
||||
|
||||
if (isset($array[$key])) {
|
||||
$array[$key] = [$array[$key], $val];
|
||||
} else {
|
||||
$array[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
*/
|
||||
public function addQuery(array $query)
|
||||
{
|
||||
$this->setQuery((array)$this->query + $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fragment
|
||||
*/
|
||||
public function setFragment($fragment)
|
||||
{
|
||||
$this->fragment = (string)$fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFragment()
|
||||
{
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shrinks the query array and returns as a string representation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function shrinkQueryArray()
|
||||
{
|
||||
$url = '?';
|
||||
foreach ($this->query as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
foreach ($val as $subVal) {
|
||||
$url .= $key . '[]=' . $subVal . '&';
|
||||
}
|
||||
} else {
|
||||
$url .= $key . '=' . $val . '&';
|
||||
}
|
||||
}
|
||||
|
||||
return rtrim($url, '&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast this URL object into a string representation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$url = ($this->scheme) ? $this->scheme . '://' : '//';
|
||||
|
||||
if ($this->user && $this->password) {
|
||||
$url .= sprintf("%s:%s@", $this->user, $this->password);
|
||||
}
|
||||
|
||||
$url .= $this->host;
|
||||
|
||||
if ($this->port) {
|
||||
$url .= ':' . (int)$this->port;
|
||||
}
|
||||
|
||||
$url .= $this->path;
|
||||
|
||||
if (!empty($this->query)) {
|
||||
$url .= $this->shrinkQueryArray();
|
||||
}
|
||||
|
||||
if ($this->fragment) {
|
||||
$url .= '#' . $this->fragment;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
@ -1,740 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Identity\v2;
|
||||
use OpenStack\Common\Transport\ClientInterface;
|
||||
use OpenStack\Common\Transport\Guzzle\GuzzleAdapter;
|
||||
|
||||
/**
|
||||
* IdentityService provides authentication and authorization.
|
||||
*
|
||||
* IdentityService (a.k.a. Keystone) provides a central service for managing
|
||||
* other services. Through it, you can do the following:
|
||||
*
|
||||
* - Authenticate
|
||||
* - Obtain tokens valid accross services
|
||||
* - Obtain a list of the services currently available with a token
|
||||
* - Associate with tenants using tenant IDs.
|
||||
*
|
||||
* AUTHENTICATION
|
||||
*
|
||||
* The authentication process consists of a single transaction during which the
|
||||
* client (us) submits credentials and the server verifies those credentials,
|
||||
* returning a token (for subsequent requests), user information, and the
|
||||
* service catalog.
|
||||
*
|
||||
* Authentication credentials:
|
||||
*
|
||||
* - Username and password
|
||||
* - Account ID and Secret Key
|
||||
*
|
||||
* Other mechanisms may be supported in the future.
|
||||
*
|
||||
* TENANTS
|
||||
*
|
||||
* Services are associated with tenants. A token is returned when
|
||||
* authentication succeeds. It *may* be associated with a tenant. If it is not,
|
||||
* it is called "unscoped", and it will not have access to any services.
|
||||
*
|
||||
* A token that is associated with a tenant is considered "scoped". This token
|
||||
* can be used to access any of the services attached to that tenant.
|
||||
*
|
||||
* There are two different ways to attach a tenant to a token:
|
||||
*
|
||||
* - During authentication, provide a tenant ID. This will attach a tenant at
|
||||
* the outset.
|
||||
* - After authentication, "rescope" the token to attach it to a tenant. This
|
||||
* is done with either the rescopeUsingTenantId() or rescopeUsingTenantName()
|
||||
* method.
|
||||
*
|
||||
* Where do I get a tenant ID?
|
||||
*
|
||||
* There are two notable places to get this information:
|
||||
*
|
||||
* A list of tenants associated with this user can be obtain programatically
|
||||
* using the tenants() method on this object.
|
||||
*
|
||||
* OpenStack users can find their tenant ID in the console along with their
|
||||
* username and password.
|
||||
*
|
||||
* EXAMPLE
|
||||
*
|
||||
* The following example illustrates typical use of this class.
|
||||
*
|
||||
* <?php
|
||||
* // You may need to use \OpenStack\Bootstrap to set things up first.
|
||||
*
|
||||
* use \OpenStack\Identity\v2\IdentityService;
|
||||
*
|
||||
* // Create a new object with the endpoint URL (no version number)
|
||||
* $ident = new IdentityService('https://example.com:35357');
|
||||
*
|
||||
* // Authenticate and set the tenant ID simultaneously.
|
||||
* $ident->authenticateAsUser('me@example.com', 'password', '1234567');
|
||||
*
|
||||
* // The token to use when connecting to other services:
|
||||
* $token = $ident->token();
|
||||
*
|
||||
* // The tenant ID.
|
||||
* $tenant = $ident->tenantId();
|
||||
*
|
||||
* // Details about what services this token can access.
|
||||
* $services = $ident->serviceCatalog();
|
||||
*
|
||||
* // List all available tenants.
|
||||
* $tenants = $ident->tenants();
|
||||
*
|
||||
* // Switch to a different tenant.
|
||||
* $ident->rescopeUsingTenantId($tenants[0]['id']);
|
||||
*
|
||||
* ?>
|
||||
*
|
||||
* PERFORMANCE CONSIDERATIONS
|
||||
*
|
||||
* The following methods require network requests:
|
||||
*
|
||||
* - authenticate()
|
||||
* - authenticateAsUser()
|
||||
* - tenants()
|
||||
* - rescopeUsingTenantId()
|
||||
* - rescopeUsingTenantName()
|
||||
*
|
||||
* Serializing
|
||||
*
|
||||
* IdentityService has been intentionally built to serialize well.
|
||||
* This allows implementors to cache IdentityService objects rather
|
||||
* than make repeated requests for identity information.
|
||||
*
|
||||
*/
|
||||
class IdentityService
|
||||
{
|
||||
/**
|
||||
* The version of the API currently supported.
|
||||
*/
|
||||
const API_VERSION = '2.0';
|
||||
|
||||
/**
|
||||
* The full OpenStack accept type.
|
||||
*/
|
||||
const ACCEPT_TYPE = 'application/json';
|
||||
|
||||
// This is no longer supported.
|
||||
//const ACCEPT_TYPE = 'application/vnd.openstack.identity+json;version=2.0';
|
||||
|
||||
/**
|
||||
* The URL to the CS endpoint.
|
||||
*/
|
||||
protected $endpoint;
|
||||
|
||||
/**
|
||||
* The details sent with the token.
|
||||
*
|
||||
* The exact details of this array will differ depending on what type of
|
||||
* authentication is used. For example, authenticating by username and
|
||||
* password will set tenant information. Authenticating by username and
|
||||
* password, however, will leave the tenant section empty.
|
||||
*
|
||||
* This is an associative array looking like this:
|
||||
*
|
||||
* <?php
|
||||
* array(
|
||||
* 'id' => 'auth_123abc321defef99',
|
||||
* // Only non-empty for username/password auth.
|
||||
* 'tenant' => array(
|
||||
* 'id' => '123456',
|
||||
* 'name' => 'matt.butcher@hp.com',
|
||||
* ),
|
||||
* 'expires' => '2012-01-24T12:46:01.682Z'
|
||||
* );
|
||||
*/
|
||||
protected $tokenDetails;
|
||||
|
||||
/**
|
||||
* The service catalog.
|
||||
*/
|
||||
protected $catalog = [];
|
||||
|
||||
protected $userDetails;
|
||||
|
||||
/**
|
||||
* The HTTP Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Build a new IdentityService object.
|
||||
*
|
||||
* Each object is bound to a particular identity services endpoint.
|
||||
*
|
||||
* For the URL, you are advised to use the version without a
|
||||
* version number at the end, e.g. http://cs.example.com/ rather
|
||||
* than http://cs.example.com/v2.0. The version number must be
|
||||
* controlled by the library.
|
||||
*
|
||||
* If a version is included in the URI, the library will attempt to use
|
||||
* that URI.
|
||||
*
|
||||
* <?php
|
||||
* $cs = new \OpenStack\Identity\v2\IdentityService('http://example.com');
|
||||
* $token = $cs->authenticateAsUser($username, $password);
|
||||
* ?>
|
||||
*
|
||||
* @param string $url An URL pointing to the Identity Service endpoint.
|
||||
* Note that you do not need the version identifier in the URL, as version
|
||||
* information is sent in the HTTP headers rather than in the URL. The URL
|
||||
* should always be to an SSL/TLS encrypted endpoint.
|
||||
*
|
||||
* @param \OpenStack\Common\Transport\ClientInterface $client An optional HTTP client to use when making the requests.
|
||||
*/
|
||||
public function __construct($url, ClientInterface $client = null)
|
||||
{
|
||||
$parts = parse_url($url);
|
||||
|
||||
if (!empty($parts['path'])) {
|
||||
$this->endpoint = rtrim($url, '/');
|
||||
} else {
|
||||
$this->endpoint = rtrim($url, '/') . '/v' . self::API_VERSION;
|
||||
}
|
||||
|
||||
// Guzzle is the default client to use.
|
||||
if (is_null($client)) {
|
||||
$this->client = GuzzleAdapter::create();
|
||||
} else {
|
||||
$this->client = $client;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the endpoint URL.
|
||||
*
|
||||
* This includes version number, so in that regard it is not an identical
|
||||
* URL to the one passed into the constructor.
|
||||
*
|
||||
* @return string The complete URL to the identity services endpoint.
|
||||
*/
|
||||
public function url()
|
||||
{
|
||||
return $this->endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an authentication request.
|
||||
*
|
||||
* EXPERT: This allows authentication requests at a low level. For simple
|
||||
* authentication requests using a username, see the
|
||||
* authenticateAsUser() method.
|
||||
*
|
||||
* Here is an example of username/password-based authentication done with
|
||||
* the authenticate() method:
|
||||
*
|
||||
* <?php
|
||||
* $cs = new \OpenStack\Identity\v2\IdentityService($url);
|
||||
* $ops = array(
|
||||
* 'passwordCredentials' => array(
|
||||
* 'username' => $username,
|
||||
* 'password' => $password,
|
||||
* ),
|
||||
* 'tenantId' => $tenantId,
|
||||
* );
|
||||
* $token = $cs->authenticate($ops);
|
||||
* ?>
|
||||
*
|
||||
* Note that the same authentication can be done by authenticateAsUser().
|
||||
*
|
||||
* @param array $ops An associative array of authentication operations and
|
||||
* their respective parameters.
|
||||
*
|
||||
* @return string The token. This is returned for simplicity. The full
|
||||
* response is used to populate this object's service catalog, etc. The
|
||||
* token is also retrievable with token().
|
||||
*
|
||||
* @throws \OpenStack\Common\Transport\Exception\AuthorizationException If authentication failed.
|
||||
* @throws \OpenStack\Common\Exception For abnormal network conditions. The message
|
||||
* will give an indication as to the underlying problem.
|
||||
*/
|
||||
public function authenticate(array $ops)
|
||||
{
|
||||
$url = $this->url() . '/tokens';
|
||||
$envelope = [
|
||||
'auth' => $ops,
|
||||
];
|
||||
|
||||
$body = json_encode($envelope);
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => self::ACCEPT_TYPE,
|
||||
'Content-Length' => strlen($body),
|
||||
];
|
||||
|
||||
$response = $this->client->post($url, $body, ['headers' => $headers]);
|
||||
|
||||
$this->handleResponse($response);
|
||||
|
||||
return $this->token();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate to Identity Services with username, password, and either
|
||||
* tenant ID or tenant Name.
|
||||
*
|
||||
* Given a OpenStack username and password, authenticate to Identity Services.
|
||||
* Identity Services will then issue a token that can be used to access other
|
||||
* OpenStack services.
|
||||
*
|
||||
* If a tenant ID is provided, this will also associate the user with the
|
||||
* given tenant ID. If a tenant Name is provided, this will associate the user
|
||||
* with the given tenant Name. Only the tenant ID or tenant Name needs to be
|
||||
* given, not both.
|
||||
*
|
||||
* If no tenant ID or tenant Name is given, it will likely be necessary to
|
||||
* rescopeUsingTenantId() the request (See also tenants()).
|
||||
*
|
||||
* Other authentication methods:
|
||||
* - authenticate()
|
||||
*
|
||||
* @param string $username A valid username.
|
||||
* @param string $password A password string.
|
||||
* @param string $tenantId The tenant ID. This can be obtained through the
|
||||
* OpenStack console.
|
||||
* @param string $tenantName The tenant Name. This can be obtained through the
|
||||
* OpenStack console.
|
||||
*
|
||||
* @throws \OpenStack\Common\Transport\Exception\AuthorizationException If authentication failed.
|
||||
* @throws \OpenStack\Common\Exception For abnormal network conditions. The message will give an
|
||||
* indication as to the underlying problem.
|
||||
*/
|
||||
public function authenticateAsUser($username, $password, $tenantId = null, $tenantName = null)
|
||||
{
|
||||
$ops = [
|
||||
'passwordCredentials' => [
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
]
|
||||
];
|
||||
|
||||
// If a tenant ID is provided, added it to the auth array.
|
||||
if (!empty($tenantId)) {
|
||||
$ops['tenantId'] = $tenantId;
|
||||
} elseif (!empty($tenantName)) {
|
||||
$ops['tenantName'] = $tenantName;
|
||||
}
|
||||
|
||||
return $this->authenticate($ops);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token.
|
||||
*
|
||||
* This will not be populated until after one of the authentication
|
||||
* methods has been run.
|
||||
*
|
||||
* @return string The token ID to be used in subsequent calls.
|
||||
*/
|
||||
public function token()
|
||||
{
|
||||
return $this->tokenDetails['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tenant ID associated with this token.
|
||||
*
|
||||
* If this token has a tenant ID, the ID will be returned. Otherwise, this
|
||||
* will return null.
|
||||
*
|
||||
* This will not be populated until after an authentication method has been
|
||||
* run.
|
||||
*
|
||||
* @return string The tenant ID if available, or null.
|
||||
*/
|
||||
public function tenantId()
|
||||
{
|
||||
if (!empty($this->tokenDetails['tenant']['id'])) {
|
||||
return $this->tokenDetails['tenant']['id'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tenant name associated with this token.
|
||||
*
|
||||
* If this token has a tenant name, the name will be returned. Otherwise, this
|
||||
* will return null.
|
||||
*
|
||||
* This will not be populated until after an authentication method has been
|
||||
* run.
|
||||
*
|
||||
* @return string The tenant name if available, or null.
|
||||
*/
|
||||
public function tenantName()
|
||||
{
|
||||
if (!empty($this->tokenDetails['tenant']['name'])) {
|
||||
return $this->tokenDetails['tenant']['name'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token details.
|
||||
*
|
||||
* This returns an associative array with several pieces of information
|
||||
* about the token, including:
|
||||
*
|
||||
* - id: The token itself
|
||||
* - expires: When the token expires
|
||||
* - tenant_id: The tenant ID of the authenticated user.
|
||||
* - tenant_name: The username of the authenticated user.
|
||||
*
|
||||
* <?php
|
||||
* array(
|
||||
* 'id' => 'auth_123abc321defef99',
|
||||
* 'tenant' => array(
|
||||
* 'id' => '123456',
|
||||
* 'name' => 'matt.butcher@hp.com',
|
||||
* ),
|
||||
* 'expires' => '2012-01-24T12:46:01.682Z'
|
||||
* );
|
||||
*
|
||||
* This will not be populated until after authentication has been done.
|
||||
*
|
||||
* @return array An associative array of details.
|
||||
*/
|
||||
public function tokenDetails()
|
||||
{
|
||||
return $this->tokenDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current identity has an expired token.
|
||||
*
|
||||
* This does not perform a round-trip to the server. Instead, it compares the
|
||||
* machine's local timestamp with the server's expiration time stamp. A
|
||||
* mis-configured machine timestamp could give spurious results.
|
||||
*
|
||||
* @return boolean This will return false if there is a current token and it
|
||||
* has not yet expired (according to the date info). In all
|
||||
* other cases it returns true.
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
$details = $this->tokenDetails();
|
||||
|
||||
if (empty($details['expires'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$currentDateTime = new \DateTime('now');
|
||||
$expireDateTime = new \DateTime($details['expires']);
|
||||
|
||||
return $currentDateTime > $expireDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service catalog, optionaly filtering by type.
|
||||
*
|
||||
* This returns the service catalog (largely unprocessed) that
|
||||
* is returned during an authentication request. If a type is passed in,
|
||||
* only entries of that type are returned. If no type is passed in, the
|
||||
* entire service catalog is returned.
|
||||
*
|
||||
* The service catalog contains information about what services (if any) are
|
||||
* available for the present user. Object storage (Swift) Compute instances
|
||||
* (Nova) and other services will each be listed here if they are enabled
|
||||
* for your user in the current tenant. Only services that have been turned on
|
||||
* for the user on the tenant will be available. (That is, even if you *can*
|
||||
* create a compute instance, until you have actually created one, it will not
|
||||
* show up in this list.)
|
||||
*
|
||||
* One of the authentication methods MUST be run before obtaining the service
|
||||
* catalog.
|
||||
*
|
||||
* The return value is an indexed array of associative arrays, where each assoc
|
||||
* array describes an individual service.
|
||||
*
|
||||
* <?php
|
||||
* array(
|
||||
* array(
|
||||
* 'name' : 'Object Storage',
|
||||
* 'type' => 'object-store',
|
||||
* 'endpoints' => array(
|
||||
* 'tenantId' => '123456',
|
||||
* 'adminURL' => 'https://example.hpcloud.net/1.0',
|
||||
* 'publicURL' => 'https://example.hpcloud.net/1.0/123456',
|
||||
* 'region' => 'region-a.geo-1',
|
||||
* 'id' => '1.0',
|
||||
* ),
|
||||
* ),
|
||||
* array(
|
||||
* 'name' => 'Identity',
|
||||
* 'type' => 'identity'
|
||||
* 'endpoints' => array(
|
||||
* 'publicURL' => 'https://example.hpcloud.net/1.0/123456',
|
||||
* 'region' => 'region-a.geo-1',
|
||||
* 'id' => '2.0',
|
||||
* 'list' => 'http://example.hpcloud.net/extension',
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
* );
|
||||
* ?>
|
||||
*
|
||||
* This will not be populated until after authentication has been done.
|
||||
*
|
||||
* Types:
|
||||
*
|
||||
* While this is by no means an exhaustive list, here are a few types that
|
||||
* might appear in a service catalog (and upon which you can filter):
|
||||
*
|
||||
* - identity: Identity Services (i.e. Keystone)
|
||||
* - compute: Compute instance (Nova)
|
||||
* - object-store: Object Storage (Swift)
|
||||
*
|
||||
* Other services will be added.
|
||||
*
|
||||
* @todo Paging on the service catalog is not yet implemented.
|
||||
*
|
||||
* @return array An associative array representing the service catalog.
|
||||
*/
|
||||
public function serviceCatalog($type = null)
|
||||
{
|
||||
// If no type is specified, return the entire
|
||||
// catalog.
|
||||
if (empty($type)) {
|
||||
return $this->serviceCatalog;
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($this->serviceCatalog as $entry) {
|
||||
if ($entry['type'] == $type) {
|
||||
$list[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the currently authenticated user.
|
||||
*
|
||||
* This returns an associative array of information about the authenticated
|
||||
* user, including the user's username and roles.
|
||||
*
|
||||
* The returned data is structured like this:
|
||||
*
|
||||
* <?php
|
||||
* array(
|
||||
* 'name' => 'matthew.butcher@hp.com',
|
||||
* 'id' => '1234567890'
|
||||
* 'roles' => array(
|
||||
* array(
|
||||
* 'name' => 'domainuser',
|
||||
* 'serviceId' => '100',
|
||||
* 'id' => '000100400010011',
|
||||
* ),
|
||||
* // One array for each role...
|
||||
* ),
|
||||
* )
|
||||
* ?>
|
||||
*
|
||||
* This will not have data until after authentication has been done.
|
||||
*
|
||||
* @return array An associative array, as described above.
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->userDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all tenants associated with this account.
|
||||
*
|
||||
* If a valid token is passed into this object, the method can be invoked
|
||||
* before authentication. However, if no token is supplied, this attempts
|
||||
* to use the one returned by an authentication call.
|
||||
*
|
||||
* Returned data will follow this format:
|
||||
*
|
||||
* <?php
|
||||
* array(
|
||||
* array(
|
||||
* "id" => "395I91234514446",
|
||||
* "name" => "Banking Tenant Services",
|
||||
* "description" => "Banking Tenant Services for TimeWarner",
|
||||
* "enabled" => true,
|
||||
* "created" => "2011-11-29T16:59:52.635Z",
|
||||
* "updated" => "2011-11-29T16:59:52.635Z",
|
||||
* ),
|
||||
* );
|
||||
* ?>
|
||||
*
|
||||
* Note that this method invokes a new request against the remote server.
|
||||
*
|
||||
* @return array An indexed array of tenant info. Each entry will be an
|
||||
* associative array containing tenant details.
|
||||
*
|
||||
* @throws \OpenStack\Common\Transport\Exception\AuthorizationException If authentication failed.
|
||||
* @throws \OpenStack\Common\Exception For abnormal network conditions. The message will give an
|
||||
* indication as to the underlying problem.
|
||||
*/
|
||||
public function tenants($token = null)
|
||||
{
|
||||
$url = $this->url() . '/tenants';
|
||||
|
||||
if (empty($token)) {
|
||||
$token = $this->token();
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'X-Auth-Token' => $token,
|
||||
'Accept' => 'application/json'
|
||||
];
|
||||
|
||||
$response = $this->client->get($url, ['headers' => $headers]);
|
||||
|
||||
return $response->json()['tenants'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescope the authentication token to a different tenant.
|
||||
*
|
||||
* Note that this will rebuild the service catalog and user information for
|
||||
* the current object, since this information is sensitive to tenant info.
|
||||
*
|
||||
* An authentication token can be in one of two states:
|
||||
*
|
||||
* - unscoped: It has no associated tenant ID.
|
||||
* - scoped: It has a tenant ID, and can thus access that tenant's services.
|
||||
*
|
||||
* This method allows you to do any of the following:
|
||||
*
|
||||
* - Begin with an unscoped token, and assign it a tenant ID.
|
||||
* - Change a token from one tenant ID to another (re-scoping).
|
||||
* - Remove the tenant ID from a scoped token (unscoping).
|
||||
*
|
||||
* @param string $tenantId The tenant ID that this present token should be
|
||||
* bound to. If this is the empty string (`''`), the
|
||||
* present token will be "unscoped" and its tenant
|
||||
* ID will be removed.
|
||||
*
|
||||
* @return string The authentication token.
|
||||
*
|
||||
* @throws \OpenStack\Common\Transport\Exception\AuthorizationException If authentication failed.
|
||||
* @throws \OpenStack\Common\Exception For abnormal network conditions. The message will give an
|
||||
* indication as to the underlying problem.
|
||||
*/
|
||||
public function rescopeUsingTenantId($tenantId)
|
||||
{
|
||||
$url = $this->url() . '/tokens';
|
||||
|
||||
$body = json_encode([
|
||||
'auth' => [
|
||||
'tenantId' => $tenantId,
|
||||
'token' => [
|
||||
'id' => $this->token(),
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$headers = [
|
||||
'Accept' => self::ACCEPT_TYPE,
|
||||
'Content-Type' => 'application/json',
|
||||
'Content-Length' => strlen($body)
|
||||
];
|
||||
|
||||
$response = $this->client->post($url, $body, ['headers' => $headers]);
|
||||
|
||||
$this->handleResponse($response);
|
||||
|
||||
return $this->token();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescope the authentication token to a different tenant.
|
||||
*
|
||||
* Note that this will rebuild the service catalog and user information for
|
||||
* the current object, since this information is sensitive to tenant info.
|
||||
*
|
||||
* An authentication token can be in one of two states:
|
||||
*
|
||||
* - unscoped: It has no associated tenant ID.
|
||||
* - scoped: It has a tenant ID, and can thus access that tenant's services.
|
||||
*
|
||||
* This method allows you to do any of the following:
|
||||
*
|
||||
* - Begin with an unscoped token, and assign it a tenant ID.
|
||||
* - Change a token from one tenant ID to another (re-scoping).
|
||||
* - Remove the tenant ID from a scoped token (unscoping).
|
||||
*
|
||||
* @param string $tenantName The tenant name that this present token should be
|
||||
* bound to. If this is the empty string (`''`), the
|
||||
* present token will be "unscoped" and its tenant
|
||||
* name will be removed.
|
||||
*
|
||||
* @return string The authentication token.
|
||||
*
|
||||
* @throws \OpenStack\Common\Transport\Exception\AuthorizationException If authentication failed.
|
||||
* @throws \OpenStack\Common\Exception For abnormal network conditions. The message will
|
||||
* give an indication as to the underlying problem.
|
||||
*/
|
||||
public function rescopeUsingTenantName($tenantName)
|
||||
{
|
||||
$url = $this->url() . '/tokens';
|
||||
|
||||
$body = json_encode([
|
||||
'auth' => [
|
||||
'tenantName' => $tenantName,
|
||||
'token' => [
|
||||
'id' => $this->token()
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$headers = [
|
||||
'Accept' => self::ACCEPT_TYPE,
|
||||
'Content-Type' => 'application/json',
|
||||
'Content-Length' => strlen($body)
|
||||
];
|
||||
|
||||
$response = $this->client->post($url, $body, ['headers' => $headers]);
|
||||
|
||||
$this->handleResponse($response);
|
||||
|
||||
return $this->token();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a response object, populate this object.
|
||||
*
|
||||
* This parses the JSON data and parcels out the data to the appropriate
|
||||
* fields.
|
||||
*
|
||||
* @param \OpenStack\Common\Transport\ResponseInterface $response A response object.
|
||||
*
|
||||
* @return \OpenStack\Identity\v2\IdentityService $this for the current object so
|
||||
* it can be used in chaining.
|
||||
*/
|
||||
protected function handleResponse($response)
|
||||
{
|
||||
$json = $response->json();
|
||||
|
||||
$this->tokenDetails = $json['access']['token'];
|
||||
$this->userDetails = $json['access']['user'];
|
||||
$this->serviceCatalog = $json['access']['serviceCatalog'];
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 OpenStack\ObjectStore\v1\Exception;
|
||||
|
||||
/**
|
||||
* Indicatest that a container is not empty.
|
||||
*
|
||||
* Certain operations, notably container deletion, require that a
|
||||
* container be empty before the operation can be performed. This
|
||||
* exception is thrown when such an operation encounters an unempty
|
||||
* container when it requires an empty one.
|
||||
*/
|
||||
class ContainerNotEmptyException extends \OpenStack\Common\Transport\Exception\ServerException {}
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 OpenStack\ObjectStore\v1\Exception;
|
||||
|
||||
/**
|
||||
* Content Verification error condition.
|
||||
*
|
||||
* This occurs when the server sends content whose value does
|
||||
* not match the supplied checksum. See
|
||||
* RemoteObject::setContentVerification().
|
||||
*/
|
||||
class ContentVerificationException extends \OpenStack\Common\Exception {}
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 OpenStack\ObjectStore\v1\Exception;
|
||||
/**
|
||||
* Thrown if an object that is read only is modified.
|
||||
*/
|
||||
class ReadOnlyObjectException extends \OpenStack\Common\Exception {}
|
@ -1,493 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\ObjectStore\v1;
|
||||
|
||||
use OpenStack\Common\Exception;
|
||||
use OpenStack\Common\Transport\ClientInterface;
|
||||
use OpenStack\Common\Transport\Exception\ConflictException;
|
||||
use OpenStack\Common\Transport\Exception\ResourceNotFoundException;
|
||||
use OpenStack\Common\Transport\Guzzle\GuzzleAdapter;
|
||||
use OpenStack\ObjectStore\v1\Exception\ContainerNotEmptyException;
|
||||
use OpenStack\ObjectStore\v1\Resource\Container;
|
||||
use OpenStack\ObjectStore\v1\Resource\ACL;
|
||||
|
||||
/**
|
||||
* Access to ObjectStorage (Swift).
|
||||
*
|
||||
* This is the primary piece of the Object Oriented representation of
|
||||
* the Object Storage service. Developers wishing to work at a low level
|
||||
* should use this API.
|
||||
*
|
||||
* There is also a stream wrapper interface that exposes ObjectStorage
|
||||
* to PHP's streams system. For common use of an object store, you may
|
||||
* prefer to use that system. (@see \OpenStack\Bootstrap).
|
||||
*
|
||||
* To authenticate, use the IdentityService authentication mechanism (@see
|
||||
* \OpenStack\Identity\v2\IdentityService).
|
||||
*
|
||||
* Common Tasks
|
||||
*
|
||||
* - Create a new container with createContainer().
|
||||
* - List containers with containers().
|
||||
* - Remove a container with deleteContainer().
|
||||
*
|
||||
* @todo ObjectStorage is not yet constrained to a particular version
|
||||
* of the API. It attempts to use whatever version is passed in to the
|
||||
* URL. This is different from IdentityService, which uses a fixed version.
|
||||
*/
|
||||
class ObjectStorage
|
||||
{
|
||||
/**
|
||||
* The name of this service type in OpenStack.
|
||||
*
|
||||
* This is used with IdentityService::serviceCatalog().
|
||||
*/
|
||||
const SERVICE_TYPE = 'object-store';
|
||||
|
||||
const API_VERSION = '1';
|
||||
|
||||
/**
|
||||
* The authorization token.
|
||||
*/
|
||||
protected $token = null;
|
||||
/**
|
||||
* The URL to the Swift endpoint.
|
||||
*/
|
||||
protected $url = null;
|
||||
|
||||
/**
|
||||
* The HTTP Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Given an IdentityService instance, create an ObjectStorage instance.
|
||||
*
|
||||
* This constructs a new ObjectStorage from an authenticated instance
|
||||
* of an \OpenStack\Identity\v2\IdentityService object.
|
||||
*
|
||||
* @param \OpenStack\Identity\v2\IdentityService $identity An identity services object that already
|
||||
* has a valid token and a service catalog.
|
||||
* @param string $region The Object Storage region
|
||||
* @param \OpenStack\Common\Transport\ClientInterface $client The HTTP client
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\ObjectStorage A new ObjectStorage instance.
|
||||
*/
|
||||
public static function newFromIdentity($identity, $region, \OpenStack\Common\Transport\ClientInterface $client = null)
|
||||
{
|
||||
$cat = $identity->serviceCatalog();
|
||||
$tok = $identity->token();
|
||||
|
||||
return self::newFromServiceCatalog($cat, $tok, $region, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a service catalog and a token, create an ObjectStorage instance.
|
||||
*
|
||||
* The IdentityService object contains a service catalog listing all of the
|
||||
* services to which the present user has access.
|
||||
*
|
||||
* This builder can scan the catalog and generate a new ObjectStorage
|
||||
* instance pointed to the first object storage endpoint in the catalog
|
||||
* that matches the specified parameters.
|
||||
*
|
||||
* @param array $catalog The service catalog from IdentityService::serviceCatalog().
|
||||
* This can be either the entire catalog or a catalog
|
||||
* filtered to just ObjectStorage::SERVICE_TYPE.
|
||||
* @param string $authToken The auth token returned by IdentityService.
|
||||
* @param string $region The Object Storage region
|
||||
* @param \OpenStack\Common\Transport\ClientInterface $client The HTTP client
|
||||
*
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\ObjectStorage A new ObjectStorage instance.
|
||||
*/
|
||||
public static function newFromServiceCatalog($catalog, $authToken, $region, \OpenStack\Common\Transport\ClientInterface $client = null)
|
||||
{
|
||||
$c = count($catalog);
|
||||
for ($i = 0; $i < $c; ++$i) {
|
||||
if ($catalog[$i]['type'] == self::SERVICE_TYPE) {
|
||||
foreach ($catalog[$i]['endpoints'] as $endpoint) {
|
||||
if (isset($endpoint['publicURL']) && $endpoint['region'] == $region) {
|
||||
return new ObjectStorage($authToken, $endpoint['publicURL'], $client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ObjectStorage object.
|
||||
*
|
||||
* Use this if newFromServiceCatalog() does not meet your needs.
|
||||
*
|
||||
* @param string $authToken A token that will be included in subsequent
|
||||
* requests to validate that this client has authenticated
|
||||
* correctly.
|
||||
* @param string $url The URL to the endpoint. This typically is returned
|
||||
* after authentication.
|
||||
* @param \OpenStack\Common\Transport\ClientInterface $client The HTTP client
|
||||
*/
|
||||
public function __construct($authToken, $url, ClientInterface $client = null)
|
||||
{
|
||||
$this->token = $authToken;
|
||||
$this->url = $url;
|
||||
|
||||
// Guzzle is the default client to use.
|
||||
if (is_null($client)) {
|
||||
$this->client = GuzzleAdapter::create();
|
||||
} else {
|
||||
$this->client = $client;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authentication token.
|
||||
*
|
||||
* @return string The authentication token.
|
||||
*/
|
||||
public function token()
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL endpoint.
|
||||
*
|
||||
* @return string The URL that is the endpoint for this service.
|
||||
*/
|
||||
public function url()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of containers for this user.
|
||||
*
|
||||
* By default, this fetches the entire list of containers for the
|
||||
* given user. If you have more than 10,000 containers (who
|
||||
* wouldn't?), you will need to use $marker for paging.
|
||||
*
|
||||
* If you want more controlled paging, you can use $limit to indicate
|
||||
* the number of containers returned per page, and $marker to indicate
|
||||
* the last container retrieved.
|
||||
*
|
||||
* Containers are ordered. That is, they will always come back in the
|
||||
* same order. For that reason, the pager takes $marker (the name of
|
||||
* the last container) as a paging parameter, rather than an offset
|
||||
* number.
|
||||
*
|
||||
* @todo For some reason, ACL information does not seem to be returned
|
||||
* in the JSON data. Need to determine how to get that. As a
|
||||
* stop-gap, when a container object returned from here has its ACL
|
||||
* requested, it makes an additional round-trip to the server to
|
||||
* fetch that data.
|
||||
*
|
||||
* @param int $limit The maximum number to return at a time. The default is
|
||||
* -- brace yourself -- 10,000 (as determined by OpenStack. Implementations
|
||||
* may vary).
|
||||
* @param string $marker The name of the last object seen. Used when paging.
|
||||
*
|
||||
* @return array An associative array of containers, where the key is the
|
||||
* container's name and the value is an \OpenStack\ObjectStore\v1\ObjectStorage\Container
|
||||
* object. Results are ordered in server order (the order that the remote
|
||||
* host puts them in).
|
||||
*/
|
||||
public function containers($limit = 0, $marker = null)
|
||||
{
|
||||
$url = $this->url() . '?format=json';
|
||||
|
||||
if ($limit > 0) {
|
||||
$url .= sprintf('&limit=%d', $limit);
|
||||
}
|
||||
if (!empty($marker)) {
|
||||
$url .= sprintf('&marker=%d', $marker);
|
||||
}
|
||||
|
||||
$headers = ['X-Auth-Token' => $this->token];
|
||||
$response = $this->client->get($url, ['headers' => $headers]);
|
||||
$containers = $response->json();
|
||||
|
||||
$containerList = [];
|
||||
foreach ($containers as $container) {
|
||||
$cname = $container['name'];
|
||||
$containerList[$cname] = Container::newFromJSON($container, $this->token(), $this->url(), $this->client);
|
||||
}
|
||||
|
||||
return $containerList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single specific container.
|
||||
*
|
||||
* This loads only the named container from the remote server.
|
||||
*
|
||||
* @param string $name The name of the container to load.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Container A container.
|
||||
*
|
||||
* @throws \OpenStack\Common\Transport\Exception\ResourceNotFoundException if the named container is not
|
||||
* found on the remote server.
|
||||
*/
|
||||
public function container($name)
|
||||
{
|
||||
$url = $this->url() . '/' . rawurlencode($name);
|
||||
|
||||
$headers = ['X-Auth-Token' => $this->token()];
|
||||
$response = $this->client->head($url, ['headers' => $headers]);
|
||||
|
||||
$status = $response->getStatusCode();
|
||||
|
||||
if ($status == 204) {
|
||||
return Container::newFromResponse($name, $response, $this->token(), $this->url());
|
||||
}
|
||||
|
||||
// If we get here, it's not a 404 and it's not a 204.
|
||||
throw new Exception(sprintf("Unknown status: %d", $status));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this container name exists.
|
||||
*
|
||||
* This method directly checks the remote server. Calling container()
|
||||
* or containers() might be more efficient if you plan to work with
|
||||
* the resulting container.
|
||||
*
|
||||
* @param string $name The name of the container to test.
|
||||
*
|
||||
* @return boolean true if the container exists, false if it does not.
|
||||
*
|
||||
* @throws \OpenStack\Common\Exception If an unexpected network error occurs.
|
||||
*/
|
||||
public function hasContainer($name)
|
||||
{
|
||||
try {
|
||||
$container = $this->container($name);
|
||||
} catch (ResourceNotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a container with the given name.
|
||||
*
|
||||
* This creates a new container on the ObjectStorage
|
||||
* server with the name provided in $name.
|
||||
*
|
||||
* A boolean is returned when the operation did not generate an error
|
||||
* condition.
|
||||
*
|
||||
* - true means that the container was created.
|
||||
* - false means that the container was not created because it already
|
||||
* exists.
|
||||
*
|
||||
* Any actual error will cause an exception to be thrown. These will
|
||||
* be the HTTP-level exceptions.
|
||||
*
|
||||
* ACLs
|
||||
*
|
||||
* Swift supports an ACL stream that allows for specifying (with
|
||||
* certain caveats) various levels of read and write access. However,
|
||||
* there are two standard settings that cover the vast majority of
|
||||
* cases.
|
||||
*
|
||||
* - Make the resource private: This grants read and write access to
|
||||
* ONLY the creating user tenant. This is the default; it can also be
|
||||
* specified with ACL::makeNonPublic().
|
||||
* - Make the resource public: This grants READ permission to any
|
||||
* requesting host, yet only allows the creator to WRITE to the
|
||||
* object. This level can be granted by ACL::makePublic().
|
||||
*
|
||||
* Note that ACLs operate at a container level. Thus, marking a
|
||||
* container public will allow access to ALL objects inside of the
|
||||
* container.
|
||||
*
|
||||
* To find out whether an existing container is public, you can
|
||||
* write something like this:
|
||||
*
|
||||
* <?php
|
||||
* // Get the container.
|
||||
* $container = $objectStorage->container('my_container');
|
||||
*
|
||||
* //Check the permission on the ACL:
|
||||
* $boolean = $container->acl()->isPublic();
|
||||
* ?>
|
||||
*
|
||||
* For details on ACLs, see \OpenStack\ObjectStore\v1\Resource\ACL.
|
||||
*
|
||||
* @param string $name The name of the container.
|
||||
* @param object $acl \OpenStack\ObjectStore\v1\Resource\ACL An access control
|
||||
* list object. By default, a container is non-public
|
||||
* (private). To change this behavior, you can add a
|
||||
* custom ACL. To make the container publically
|
||||
* readable, you can use this: \OpenStack\ObjectStore\v1\Resource\ACL::makePublic().
|
||||
* @param array $metadata An associative array of metadata to attach to the
|
||||
* container.
|
||||
*
|
||||
* @return boolean true if the container was created, false if the container
|
||||
* was not created because it already exists.
|
||||
*/
|
||||
public function createContainer($name, ACL $acl = null, $metadata = [])
|
||||
{
|
||||
$url = $this->url() . '/' . rawurlencode($name);
|
||||
$headers = ['X-Auth-Token' => $this->token()];
|
||||
|
||||
if (!empty($metadata)) {
|
||||
$prefix = Container::CONTAINER_METADATA_HEADER_PREFIX;
|
||||
$headers += Container::generateMetadataHeaders($metadata, $prefix);
|
||||
}
|
||||
|
||||
// Add ACLs to header.
|
||||
if (!empty($acl)) {
|
||||
$headers += $acl->headers();
|
||||
}
|
||||
|
||||
$data = $this->client->put($url, null, ['headers' => $headers]);
|
||||
|
||||
$status = $data->getStatusCode();
|
||||
|
||||
if ($status == 201) {
|
||||
return true;
|
||||
} elseif ($status == 202) {
|
||||
return false;
|
||||
} else {
|
||||
// According to the OpenStack docs, there are no other return codes.
|
||||
throw new Exception('Server returned unexpected code: ' . $status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of createContainer().
|
||||
*
|
||||
* At present, there is no distinction in the Swift REST API between
|
||||
* creating an updating a container. In the future this may change, so
|
||||
* you are encouraged to use this alias in cases where you clearly intend
|
||||
* to update an existing container.
|
||||
*/
|
||||
public function updateContainer($name, ACL $acl = null, $metadata = [])
|
||||
{
|
||||
return $this->createContainer($name, $acl, $metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the container's ACL.
|
||||
*
|
||||
* This will attempt to change the ACL on a container. If the
|
||||
* container does not already exist, it will be created first, and
|
||||
* then the ACL will be set. (This is a relic of the OpenStack Swift
|
||||
* implementation, which uses the same HTTP verb to create a container
|
||||
* and to set the ACL.)
|
||||
*
|
||||
* @param string $name The name of the container.
|
||||
* @param object $acl \OpenStack\ObjectStore\v1\Resource\ACL An ACL. To make the
|
||||
* container publically readable, use ACL::makePublic().
|
||||
*
|
||||
* @return boolean true if the cointainer was created, false otherwise.
|
||||
*/
|
||||
public function changeContainerACL($name, ACL $acl)
|
||||
{
|
||||
// Oddly, the way to change an ACL is to issue the
|
||||
// same request as is used to create a container.
|
||||
return $this->createContainer($name, $acl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an empty container.
|
||||
*
|
||||
* Given a container name, this attempts to delete the container in
|
||||
* the object storage.
|
||||
*
|
||||
* The container MUST be empty before it can be deleted. If it is not,
|
||||
* an \OpenStack\ObjectStore\v1\Exception\ContainerNotEmptyException will
|
||||
* be thrown.
|
||||
*
|
||||
* @param string $name The name of the container.
|
||||
*
|
||||
* @return boolean true if the container was deleted, false if the container
|
||||
* was not found (and hence, was not deleted).
|
||||
*
|
||||
* @throws \OpenStack\ObjectStore\v1\Exception\ContainerNotEmptyException if the container is not empty.
|
||||
*
|
||||
* @throws \OpenStack\Common\Exception if an unexpected response code is returned. While this should never happen on
|
||||
* OpenStack servers, forks of OpenStack may choose to extend object storage in a way
|
||||
* that results in a non-standard code.
|
||||
*/
|
||||
public function deleteContainer($name)
|
||||
{
|
||||
$url = $this->url() . '/' . rawurlencode($name);
|
||||
|
||||
try {
|
||||
$headers = ['X-Auth-Token' => $this->token()];
|
||||
$data = $this->client->delete($url, ['headers' => $headers]);
|
||||
} catch (ResourceNotFoundException $e) {
|
||||
return false;
|
||||
} catch (ConflictException $e) {
|
||||
// XXX: I'm not terribly sure about this. Why not just throw the
|
||||
// ConflictException?
|
||||
throw new ContainerNotEmptyException(
|
||||
"Non-empty container cannot be deleted",
|
||||
$e->getRequest(),
|
||||
$e->getResponse()
|
||||
);
|
||||
}
|
||||
|
||||
$status = $data->getStatusCode();
|
||||
|
||||
// 204 indicates that the container has been deleted.
|
||||
if ($status == 204) {
|
||||
return true;
|
||||
} else {
|
||||
// OpenStacks documentation doesn't suggest any other return codes.
|
||||
throw new Exception('Server returned unexpected code: ' . $status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve account info.
|
||||
*
|
||||
* This returns information about:
|
||||
*
|
||||
* - The total bytes used by this Object Storage instance (`bytes`).
|
||||
* - The number of containers (`count`).
|
||||
*
|
||||
* @return array An associative array of account info. Typical keys are:
|
||||
* - bytes: Bytes consumed by existing content.
|
||||
* - containers: Number of containers.
|
||||
* - objects: Number of objects.
|
||||
*
|
||||
* @throws \OpenStack\Common\Transport\Exception\AuthorizationException if the user credentials
|
||||
* are invalid or have expired.
|
||||
*/
|
||||
public function accountInfo()
|
||||
{
|
||||
$headers = ['X-Auth-Token' => $this->token()];
|
||||
$response = $this->client->head($this->url(), ['headers' => $headers]);
|
||||
|
||||
return [
|
||||
'bytes' => $response->getHeader('X-Account-Bytes-Used', 0),
|
||||
'containers' => $response->getHeader('X-Account-Container-Count', 0),
|
||||
'objects' => $response->getHeader('X-Account-Container-Count', 0)
|
||||
];
|
||||
}
|
||||
}
|
@ -1,567 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\ObjectStore\v1\Resource;
|
||||
|
||||
/**
|
||||
* Access control list for object storage.
|
||||
*
|
||||
* EXPERIMENTAL: This is bassed on a feature of Swift that is likely to
|
||||
* change. Most of this is based on undocmented features of the API
|
||||
* discovered both in the Python docs and in discussions by various
|
||||
* members of the OpenStack community.
|
||||
*
|
||||
* Swift access control rules are broken into two permissions: READ and
|
||||
* WRITE. Read permissions grant the user the ability to access the file
|
||||
* (using verbs like GET and HEAD), while WRITE permissions allow any
|
||||
* modification operation. WRITE does not imply READ.
|
||||
*
|
||||
* In the current implementation of Swift, access can be assigned based
|
||||
* on two different factors:
|
||||
*
|
||||
* - Accounts: Access can be granted to specific accounts, and within
|
||||
* those accounts, can be further specified to specific users. See the
|
||||
* addAccount() method for details on this.
|
||||
* - Referrers: Access can be granted based on host names or host name
|
||||
* patterns. For example, only subdomains of *.example.com may be
|
||||
* granted READ access to a particular object.
|
||||
*
|
||||
* ACLs are transmitted within the HTTP headers for an object or
|
||||
* container. Two headers are used: `X-Container-Read` for READ rules, and
|
||||
* `X-Container-Write` for WRITE rules. Each header may have a chain of
|
||||
* rules.
|
||||
*
|
||||
* Examples
|
||||
*
|
||||
* For most casual cases, only the static constructor functions are
|
||||
* used. For example, an ACL that does not grant any public access can
|
||||
* be created with a single call:
|
||||
*
|
||||
* <?php
|
||||
* $acl = ACL::makeNonPublic();
|
||||
* ?>
|
||||
*
|
||||
* Public read access is granted like this:
|
||||
*
|
||||
* <?php
|
||||
* $acl = ACL::makePublic();
|
||||
* ?>
|
||||
*
|
||||
* (Note that in both cases, what is returned is an instance of an ACL with
|
||||
* all of the necessary configuration done.)
|
||||
*
|
||||
* Sometimes you will need more sophisticated access control rules. The
|
||||
* following grants READ access to anyone coming from an `example.com`
|
||||
* domain, but grants WRITE access only to the account `admins:`
|
||||
*
|
||||
* <?php
|
||||
* $acl = new ACL();
|
||||
*
|
||||
* // Grant READ to example.com users.
|
||||
* $acl->addReferrer(ACL::READ, '*.example.com');
|
||||
*
|
||||
* // Allow only people in the account 'admins' access to
|
||||
* // write.
|
||||
* $acl->addAccount(ACL::WRITE, 'admins');
|
||||
*
|
||||
* // Allow example.com users to view the container
|
||||
* // listings:
|
||||
* $acl->allowListings();
|
||||
*
|
||||
* ?>
|
||||
*
|
||||
* Notes
|
||||
*
|
||||
* - The current implementation does not do any validation of rules.
|
||||
* This will likely change in the future.
|
||||
* - There is discussion in OpenStack about providing a different or
|
||||
* drastically improved ACL mechanism. This class would then be
|
||||
* replaced by a new mechanism.
|
||||
*
|
||||
* For a detailed description of the rules for ACL creation,
|
||||
* @see http://swift.openstack.org/misc.html#acls
|
||||
*/
|
||||
class ACL
|
||||
{
|
||||
/**
|
||||
* Read flag.
|
||||
*
|
||||
* This is for an ACL of the READ type.
|
||||
*/
|
||||
const READ = 1;
|
||||
/**
|
||||
* Write flag.
|
||||
*
|
||||
* This is for an ACL of the WRITE type.
|
||||
*/
|
||||
const WRITE = 2;
|
||||
/**
|
||||
* Flag for READ and WRITE.
|
||||
*
|
||||
* This is equivalent to `ACL::READ | ACL::WRITE`
|
||||
*/
|
||||
const READ_WRITE = 3; // self::READ | self::WRITE;
|
||||
|
||||
/**
|
||||
* Header string for a read flag.
|
||||
*/
|
||||
const HEADER_READ = 'X-Container-Read';
|
||||
/**
|
||||
* Header string for a write flag.
|
||||
*/
|
||||
const HEADER_WRITE = 'X-Container-Write';
|
||||
|
||||
protected $rules = [];
|
||||
|
||||
/**
|
||||
* Allow READ access to the public.
|
||||
*
|
||||
* This grants the following:
|
||||
*
|
||||
* - READ to any host, with container listings.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\ACL an ACL object with the
|
||||
* appopriate permissions set.
|
||||
*/
|
||||
public static function makePublic()
|
||||
{
|
||||
$acl = new ACL();
|
||||
$acl->addReferrer(self::READ, '*');
|
||||
$acl->allowListings();
|
||||
|
||||
return $acl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallow all public access.
|
||||
*
|
||||
* Non-public is the same as private. Private, however, is a reserved
|
||||
* word in PHP.
|
||||
*
|
||||
* This does not grant any permissions. OpenStack interprets an object
|
||||
* with no permissions as a private object.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\ACL an ACL object with the
|
||||
* appopriate permissions set.
|
||||
*/
|
||||
public static function makeNonPublic()
|
||||
{
|
||||
// Default ACL is private.
|
||||
return new ACL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of ACL::makeNonPublic().
|
||||
*/
|
||||
public static function makePrivate()
|
||||
{
|
||||
return self::makeNonPublic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of headers, get the ACL info.
|
||||
*
|
||||
* This is a utility for processing headers and discovering any ACLs embedded
|
||||
* inside the headers.
|
||||
*
|
||||
* @param array $headers An associative array of headers.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\ACL A new ACL.
|
||||
*/
|
||||
public static function newFromHeaders($headers)
|
||||
{
|
||||
$acl = new ACL();
|
||||
|
||||
// READ rules.
|
||||
$rules = [];
|
||||
if (!empty($headers[self::HEADER_READ])) {
|
||||
$read = $headers[self::HEADER_READ];
|
||||
$rules = explode(',', $read);
|
||||
foreach ($rules as $rule) {
|
||||
$ruleArray = self::parseRule(self::READ, $rule);
|
||||
if (!empty($ruleArray)) {
|
||||
$acl->rules[] = $ruleArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WRITE rules.
|
||||
$rules = [];
|
||||
if (!empty($headers[self::HEADER_WRITE])) {
|
||||
$write = $headers[self::HEADER_WRITE];
|
||||
$rules = explode(',', $write);
|
||||
foreach ($rules as $rule) {
|
||||
$ruleArray = self::parseRule(self::WRITE, $rule);
|
||||
if (!empty($ruleArray)) {
|
||||
$acl->rules[] = $ruleArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//throw new \Exception(print_r($acl->rules(), true));
|
||||
return $acl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a rule.
|
||||
*
|
||||
* This attempts to parse an ACL rule. It is not particularly
|
||||
* fault-tolerant.
|
||||
*
|
||||
* @param int $perm The permission (ACL::READ, ACL::WRITE).
|
||||
* @param string $rule The string rule to parse.
|
||||
*
|
||||
* @return array The rule as an array.
|
||||
*/
|
||||
public static function parseRule($perm, $rule)
|
||||
{
|
||||
// This regular expression generates the following:
|
||||
//
|
||||
// array(
|
||||
// 0 => ENTIRE RULE
|
||||
// 1 => WHOLE EXPRESSION, no whitespace
|
||||
// 2 => domain compontent
|
||||
// 3 => 'rlistings', set if .rincludes is the directive
|
||||
// 4 => account name
|
||||
// 5 => :username
|
||||
// 6 => username
|
||||
// );
|
||||
$exp = '/^\s*(.r:([a-zA-Z0-9\*\-\.]+)|\.(rlistings)|([a-zA-Z0-9]+)(\:([a-zA-Z0-9]+))?)\s*$/';
|
||||
|
||||
$matches = [];
|
||||
preg_match($exp, $rule, $matches);
|
||||
|
||||
$entry = ['mask' => $perm];
|
||||
if (!empty($matches[2])) {
|
||||
$entry['host'] = $matches[2];
|
||||
} elseif (!empty($matches[3])) {
|
||||
$entry['rlistings'] = true;
|
||||
} elseif (!empty($matches[4])) {
|
||||
$entry['account'] = $matches[4];
|
||||
if (!empty($matches[6])) {
|
||||
$entry['user'] = $matches[6];
|
||||
}
|
||||
}
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ACL.
|
||||
*
|
||||
* This creates an empty ACL with no permissions granted. When no
|
||||
* permissions are granted, the file is effectively private
|
||||
* (nonPublic()).
|
||||
*
|
||||
* Use add* methods to add permissions.
|
||||
*/
|
||||
public function __construct() {}
|
||||
|
||||
/**
|
||||
* Grant ACL access to an account.
|
||||
*
|
||||
* Optionally, a user may be given to further limit access.
|
||||
*
|
||||
* This is used to restrict access to a particular account and, if so
|
||||
* specified, a specific user on that account.
|
||||
*
|
||||
* If just an account is given, any user on that account will be
|
||||
* automatically granted access.
|
||||
*
|
||||
* If an account and a user is given, only that user of the account is
|
||||
* granted access.
|
||||
*
|
||||
* If $user is an array, every user in the array will be granted
|
||||
* access under the provided account. That is, for each user in the
|
||||
* array, an entry of the form `account:user` will be generated in the
|
||||
* final ACL.
|
||||
*
|
||||
* At this time there does not seem to be a way to grant global write
|
||||
* access to an object.
|
||||
*
|
||||
* @param int $perm ACL::READ, ACL::WRITE or ACL::READ_WRITE (which is the
|
||||
* same as ACL::READ|ACL::WRITE).
|
||||
* @param string $account The name of the account.
|
||||
* @param mixed $user The name of the user, or optionally an indexed array of
|
||||
* user names.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\ACL $this for current object so
|
||||
* the method can be used in chaining.
|
||||
*/
|
||||
public function addAccount($perm, $account, $user = null)
|
||||
{
|
||||
$rule = ['account' => $account];
|
||||
|
||||
if (!empty($user)) {
|
||||
$rule['user'] = $user;
|
||||
}
|
||||
|
||||
$this->addRule($perm, $rule);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow (or deny) a hostname or host pattern.
|
||||
*
|
||||
* In current Swift implementations, only READ rules can have host
|
||||
* patterns. WRITE permissions cannot be granted to hostnames.
|
||||
*
|
||||
* Formats:
|
||||
* - Allow any host: '*'
|
||||
* - Allow exact host: 'www.example.com'
|
||||
* - Allow hosts in domain: '.example.com'
|
||||
* - Disallow exact host: '-www.example.com'
|
||||
* - Disallow hosts in domain: '-.example.com'
|
||||
*
|
||||
* Note that a simple minus sign ('-') is illegal, though it seems it
|
||||
* should be "disallow all hosts."
|
||||
*
|
||||
* @param string $perm The permission being granted. One of ACL:READ,
|
||||
* ACL::WRITE, or ACL::READ_WRITE.
|
||||
* @param string $host A host specification string as described above.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\ACL $this for current object so
|
||||
* the method can be used in chaining.
|
||||
*/
|
||||
public function addReferrer($perm, $host = '*')
|
||||
{
|
||||
$this->addRule($perm, ['host' => $host]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a rule to the appropriate stack of rules.
|
||||
*
|
||||
* @param int $perm One of the predefined permission constants.
|
||||
* @param array $rule A rule array.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\ACL $this for current object so
|
||||
* the method can be used in chaining.
|
||||
*/
|
||||
protected function addRule($perm, $rule)
|
||||
{
|
||||
$rule['mask'] = $perm;
|
||||
|
||||
$this->rules[] = $rule;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow hosts with READ permissions to list a container's content.
|
||||
*
|
||||
* By default, granting READ permission on a container does not grant
|
||||
* permission to list the contents of a container. Setting the
|
||||
* ACL::allowListings() permission will allow matching hosts to also list
|
||||
* the contents of a container.
|
||||
*
|
||||
* In the current Swift implementation, there is no mechanism for
|
||||
* allowing some hosts to get listings, while denying others.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\ACL $this for current object so
|
||||
* the method can be used in chaining.
|
||||
*/
|
||||
public function allowListings()
|
||||
{
|
||||
$this->rules[] = [
|
||||
'mask' => self::READ,
|
||||
'rlistings' => true,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rules array for this ACL.
|
||||
*
|
||||
* @return array An array of associative arrays of rules.
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return $this->rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTTP headers for this ACL.
|
||||
*
|
||||
* If this is called on an empty object, an empty set of headers is
|
||||
* returned.
|
||||
*
|
||||
* @return array Array of headers
|
||||
*/
|
||||
public function headers()
|
||||
{
|
||||
$headers = [];
|
||||
$readers = [];
|
||||
$writers = [];
|
||||
|
||||
// Create the rule strings. We need two copies, one for READ and
|
||||
// one for WRITE.
|
||||
foreach ($this->rules as $rule) {
|
||||
// We generate read and write rules separately so that the
|
||||
// generation logic has a chance to respond to the differences
|
||||
// allowances for READ and WRITE ACLs.
|
||||
if (self::READ & $rule['mask']) {
|
||||
$ruleStr = $this->ruleToString(self::READ, $rule);
|
||||
if (!empty($ruleStr)) {
|
||||
$readers[] = $ruleStr;
|
||||
}
|
||||
}
|
||||
if (self::WRITE & $rule['mask']) {
|
||||
$ruleStr = $this->ruleToString(self::WRITE, $rule);
|
||||
if (!empty($ruleStr)) {
|
||||
$writers[] = $ruleStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the HTTP headers.
|
||||
if (!empty($readers)) {
|
||||
$headers[self::HEADER_READ] = implode(',', $readers);
|
||||
}
|
||||
if (!empty($writers)) {
|
||||
$headers[self::HEADER_WRITE] = implode(',', $writers);
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a rule to a string.
|
||||
*
|
||||
* @param int $perm The permission for which to generate the rule.
|
||||
* @param array $rule A rule array.
|
||||
*/
|
||||
protected function ruleToString($perm, $rule)
|
||||
{
|
||||
// Some rules only apply to READ.
|
||||
if (self::READ & $perm) {
|
||||
|
||||
// Host rule.
|
||||
if (!empty($rule['host'])) {
|
||||
return '.r:' . $rule['host'];
|
||||
}
|
||||
|
||||
// Listing rule.
|
||||
if (!empty($rule['rlistings'])) {
|
||||
return '.rlistings';
|
||||
}
|
||||
}
|
||||
|
||||
// READ and WRITE both allow account/user rules.
|
||||
if (!empty($rule['account'])) {
|
||||
|
||||
// Just an account name.
|
||||
if (empty($rule['user'])) {
|
||||
return $rule['account'];
|
||||
}
|
||||
|
||||
// Account + multiple users.
|
||||
elseif (is_array($rule['user'])) {
|
||||
$buffer = [];
|
||||
foreach ($rule['user'] as $user) {
|
||||
$buffer[] = $rule['account'] . ':' . $user;
|
||||
}
|
||||
|
||||
return implode(',', $buffer);
|
||||
|
||||
}
|
||||
|
||||
// Account + one user.
|
||||
else {
|
||||
return $rule['account'] . ':' . $rule['user'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the ACL marks this private.
|
||||
*
|
||||
* This returns true only if this ACL does not grant any permissions
|
||||
* at all.
|
||||
*
|
||||
* @return boolean true if this is private (non-public), false if any
|
||||
* permissions are granted via this ACL.
|
||||
*/
|
||||
public function isNonPublic()
|
||||
{
|
||||
return empty($this->rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of isNonPublic().
|
||||
*/
|
||||
public function isPrivate()
|
||||
{
|
||||
return $this->isNonPublic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this object allows public reading.
|
||||
*
|
||||
* This will return true the ACL allows (a) any host to access
|
||||
* the item, and (b) it allows container listings.
|
||||
*
|
||||
* This checks whether the object allows public reading,
|
||||
* not whether it is ONLY allowing public reads.
|
||||
*
|
||||
* @see ACL::makePublic().
|
||||
*
|
||||
* @return boolean Whether or not the object allows public reading.
|
||||
*/
|
||||
public function isPublic()
|
||||
{
|
||||
$allowsAllHosts = false;
|
||||
$allowsRListings = false;
|
||||
foreach ($this->rules as $rule) {
|
||||
if (self::READ & $rule['mask']) {
|
||||
if (!empty($rule['rlistings'])) {
|
||||
$allowsRListings = true;
|
||||
} elseif (!empty($rule['host']) && trim($rule['host']) == '*') {
|
||||
$allowsAllHosts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $allowsAllHosts && $allowsRListings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the magic `__toString()` PHP function.
|
||||
*
|
||||
* This allows you to `print $acl` and get back
|
||||
* a pretty string.
|
||||
*
|
||||
* @return string The ACL represented as a string.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$headers = $this->headers();
|
||||
|
||||
$buffer = [];
|
||||
foreach ($headers as $k => $v) {
|
||||
$buffer[] = $k . ': ' . $v;
|
||||
}
|
||||
|
||||
return implode("\t", $buffer);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,524 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\ObjectStore\v1\Resource;
|
||||
|
||||
/**
|
||||
* An object for ObjectStorage.
|
||||
*
|
||||
* The OpenStack ObjectStorage system provides a method for storing
|
||||
* complete chunks of data (objects) in the cloud. This class describes
|
||||
* such a chunk of data.
|
||||
*
|
||||
* An object has the following basic components:
|
||||
*
|
||||
* - Name: A filename (which may be pathlike, subject to OpenStack's
|
||||
* pathing rules).
|
||||
* - Content: The content of the object.
|
||||
* - Content type: The MIME type of the object. Examples:
|
||||
* - text/plain; charset=UTF-8
|
||||
* - image/png
|
||||
* - application/x-my-custom-mime
|
||||
* - Metadata: File attributes that are stored along with the file on
|
||||
* object store.
|
||||
*
|
||||
* Objects are stored and retrieved by name. So it is assumed
|
||||
* that, per container, no more than one file with a given name exists.
|
||||
*
|
||||
* You may create Object instance and then store them in Containers.
|
||||
* Likewise, a Container instance can retrieve Object instances from the
|
||||
* remote object store.
|
||||
*/
|
||||
class Object
|
||||
{
|
||||
const DEFAULT_CONTENT_TYPE = 'application/octet-stream';
|
||||
|
||||
/**
|
||||
* The name of the object.
|
||||
*
|
||||
* This can be path-like, subject to OpenStack's definition
|
||||
* of "path-like".
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The content.
|
||||
*
|
||||
* Subclasses needn't use this to store an object's content,
|
||||
* as they may prefer filesystem backing.
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* The content type.
|
||||
*
|
||||
* The default type is 'application/octet-stream', which marks this as
|
||||
* a generic byte stream.
|
||||
*/
|
||||
protected $contentType = self::DEFAULT_CONTENT_TYPE;
|
||||
|
||||
/**
|
||||
* Associative array of stored metadata.
|
||||
*/
|
||||
protected $metadata = [];
|
||||
|
||||
protected $contentEncoding;
|
||||
protected $contentDisposition;
|
||||
|
||||
/**
|
||||
* Extension mechanism for new headers.
|
||||
*/
|
||||
protected $additionalHeaders = [];
|
||||
|
||||
/**
|
||||
* Construct a new object for storage.
|
||||
*
|
||||
* @param string $name A name (may be pathlike) for the object.
|
||||
* @param string $content Optional content to store in this object. This is
|
||||
* the same as calling setContent().
|
||||
* @param string $type Optional content type for this content. This is the
|
||||
* same as calling setContentType().
|
||||
*/
|
||||
public function __construct($name, $content = null, $type = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
if (!is_null($content)) {
|
||||
$this->content = $content;
|
||||
}
|
||||
if (!empty($type)) {
|
||||
$this->contentType = $type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the metadata.
|
||||
*
|
||||
* OpenStack allows you to specify metadata for a file. Metadata items
|
||||
* must follow these conventions:
|
||||
*
|
||||
* - names must contain only letters, numbers, and short dashes. Since
|
||||
* OpenStack normalizes the name to begin with uppercase, it is
|
||||
* suggested that you follow this convetion: Foo, not foo. Or you
|
||||
* can do your own normalizing (such as converting all to lowercase.
|
||||
* OpenStack limits the name length to 126 unicode chars.
|
||||
* - values must be encoded if they contain newlines or binary data.
|
||||
* While the exact encoding is up to you, Base-64 encoding is probably
|
||||
* your best bet. OpenStack limits the value to 256 unicode chars.
|
||||
*
|
||||
* (The docs are ambiguous -- they say chars, but they may mean
|
||||
* bytes.)
|
||||
*
|
||||
* This library does only minimal processing of metadata, and does no
|
||||
* error checking, escaping, etc. This is up to the implementor. The
|
||||
* OpenStack Swift implementation does not dictate what encoding is
|
||||
* used, though it suggests url encoding of both name and values.
|
||||
*
|
||||
* Currently, no length checking is performed in the library, nor is
|
||||
* any encoding of the data performed.
|
||||
*
|
||||
* IMPORTANT: Current versions of OpenStack Swift normalize metadata
|
||||
* names so that the name is always given an initial capital leter.
|
||||
* That is, `foo` becomes `Foo`.
|
||||
*
|
||||
* @param array $array An associative array of metadata names to values.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Object $this so the method can be
|
||||
* used in chaining.
|
||||
*/
|
||||
public function setMetadata(array $array)
|
||||
{
|
||||
$this->metadata = $array;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any associated metadata.
|
||||
*
|
||||
* This returns an associative array of all metadata for this object.
|
||||
*
|
||||
* @return array An associative array of metadata. This may be empty.
|
||||
*/
|
||||
public function metadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override (change) the name of an object.
|
||||
*
|
||||
* Note that this changes only the local copy of an object. It
|
||||
* does not rename the remote copy. In fact, changing the local name
|
||||
* and then saving it will result in a new object being created in the
|
||||
* object store.
|
||||
*
|
||||
* To copy an object:
|
||||
* @see \OpenStack\ObjectStore\v1\Resource\Container::copyObject().
|
||||
*
|
||||
* @param string $name A file or object name.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Object $this so the method can be
|
||||
* used in chaining.
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name.
|
||||
*
|
||||
* Returns the name of an object. If the name has been overwritten
|
||||
* using setName(), this will return the latest (overwritten) name.
|
||||
*
|
||||
* @return string The name of the object.
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content type (MIME type) for the object.
|
||||
*
|
||||
* Object storage is, to a certain degree, content-type aware. For
|
||||
* that reason, a content type is mandatory.
|
||||
*
|
||||
* The default MIME type used is `application/octet-stream`, which is
|
||||
* the generic content type for a byte stream. Where possible, you
|
||||
* should set a more accurate content type.
|
||||
*
|
||||
* All HTTP type options are allowed. So, for example, you can add a
|
||||
* charset to a text type:
|
||||
*
|
||||
* <?php
|
||||
* $o = new Object('my.html');
|
||||
* $o->setContentType('text/html; charset=iso-8859-13');
|
||||
* ?>
|
||||
*
|
||||
* Content type is not parsed or verified locally (though it is
|
||||
* remotely). It can be dangerous, too, to allow users to specify a
|
||||
* content type.
|
||||
*
|
||||
* @param string $type A valid content type.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Object $this so the method can be
|
||||
* used in chaining.
|
||||
*/
|
||||
public function setContentType($type)
|
||||
{
|
||||
$this->contentType = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content type.
|
||||
*
|
||||
* This returns the currently set content type.
|
||||
*
|
||||
* @return string The content type, including any additional options.
|
||||
*/
|
||||
public function contentType()
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content for this object.
|
||||
*
|
||||
* Place the content into the object. Typically, this is string
|
||||
* content that will be stored remotely.
|
||||
*
|
||||
* PHP's string is backed by a robust system that can accomodate
|
||||
* moderately sized files. However, it is best to keep strings short
|
||||
* (<2MB, for example -- test for your own system's sweet spot).
|
||||
* Larger data may be better handled with file system entries or
|
||||
* database storage.
|
||||
*
|
||||
* Note that the OpenStack will not allow files larger than 5G, and
|
||||
* PHP will likely croak well before that marker. So use discretion.
|
||||
*
|
||||
* @param string $content The content of the object.
|
||||
* @param string $type The content type (MIME type). This can be set here for
|
||||
* convenience, or you can call setContentType() directly.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Object $this so the method can be
|
||||
* used in chaining.
|
||||
*/
|
||||
public function setContent($content, $type = null)
|
||||
{
|
||||
$this->content = $content;
|
||||
if (!empty($type)) {
|
||||
$this->contentType = $type;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the content.
|
||||
*
|
||||
* Retrieve the ENTIRE content of an object.
|
||||
*
|
||||
* Note that this may be binary data (depending on what the original
|
||||
* content is). PHP strings are generally binary safe, but use this
|
||||
* with caution if you do not know what kind of data is stored in an
|
||||
* object.
|
||||
*
|
||||
* OpenStack does not do anything to validate that the content type is
|
||||
* accurate. While contentType() is intended to provide useful
|
||||
* information, poorly managed data can be written with the wrong
|
||||
* content type.
|
||||
*
|
||||
* When extending this class, you should make sure that this function
|
||||
* returns the entire contents of an object.
|
||||
*
|
||||
* @return string The content of the file.
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the content length.
|
||||
*
|
||||
* This returns the number of bytes in a piece of content (not
|
||||
* the number of characters). Among other things, it is used to let
|
||||
* the remote object store know how big of an object to expect when
|
||||
* transmitting data.
|
||||
*
|
||||
* When extending this class, you should make sure to calculate the
|
||||
* content length appropriately.
|
||||
*
|
||||
* @return int The length of the content, in bytes.
|
||||
*/
|
||||
public function contentLength()
|
||||
{
|
||||
// strlen() is binary safe (or at least it seems to be).
|
||||
return strlen($this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an ETag for the ObjectStorage server.
|
||||
*
|
||||
* OpenStack uses ETag to pass validation data. This generates an ETag
|
||||
* using an MD5 hash of the content.
|
||||
*
|
||||
* When extending this class, generate an ETag by creating an MD5 of
|
||||
* the entire object's content (but not the metadata or name).
|
||||
*
|
||||
* @return string An MD5 value as a string of 32 hex digits (0-9a-f).
|
||||
*/
|
||||
public function eTag()
|
||||
{
|
||||
return md5($this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encoding for a file.
|
||||
*
|
||||
* You can use content encoding on compressed content to indicate to
|
||||
* the receiving agent that a file is encoded using a specific
|
||||
* compression type.
|
||||
*
|
||||
* Typical compression types are 'gzip', 'zip', and 'compress', though
|
||||
* many others exist.
|
||||
*
|
||||
* This allows you, for example, to save a zipped file, yet preserve
|
||||
* its underlying content type. For example, for a gzipped text/plain
|
||||
* file, you can set the content type to "text/plain" and the encoding
|
||||
* to "gzip". This allows many user agents to receive the compressed
|
||||
* data and automatically decompress them and display them correctly.
|
||||
*
|
||||
* @param string $encoding A valid encoding type.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Object $this so the method can be
|
||||
* used in chaining.
|
||||
*/
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->contentEncoding = $encoding;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encoding (if any) for this object.
|
||||
*
|
||||
* Encoding is used to indicate how a file was encoded or compressed.
|
||||
* See setEncoding() for more information.
|
||||
*
|
||||
* @return string The encoding type.
|
||||
*/
|
||||
public function encoding()
|
||||
{
|
||||
return $this->contentEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content disposition.
|
||||
*
|
||||
* This makes it possible to have the file act like a download (in a
|
||||
* browser or similar agent), even if the MIME type normally triggers
|
||||
* a display.
|
||||
*
|
||||
* The typical value for this is:
|
||||
*
|
||||
* <?php
|
||||
* $object->setDisposition('attachment; filename=foo.png');
|
||||
* ?>
|
||||
*
|
||||
* A disposition string should not include any newline characters or
|
||||
* binary data.
|
||||
*
|
||||
* @param string $disposition A valid disposition declaration. These are
|
||||
* defined in various HTTP specifications.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Object $this so the method can be
|
||||
* used in chaining.
|
||||
*/
|
||||
public function setDisposition($disposition)
|
||||
{
|
||||
$this->contentDisposition = $disposition;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current disposition string, if any.
|
||||
*
|
||||
* See setDisposition() for discussion.
|
||||
*
|
||||
* @return string The disposition string, or null if none is set.
|
||||
*/
|
||||
public function disposition()
|
||||
{
|
||||
return $this->contentDisposition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional headers for storage.
|
||||
*
|
||||
* EXPERT: You will need to understand OpenStack internals to use this
|
||||
* effectively.
|
||||
*
|
||||
* Headers set here will be added to the HTTP request during save
|
||||
* operations. They are not merged into existing headers until
|
||||
* save-time.
|
||||
*
|
||||
* This provides a mechanism for adding extension headers. CORS
|
||||
* headers and possibly others are stored by Swift, but have no
|
||||
* semantic value to Swift or to popular user agents.
|
||||
*
|
||||
* There are a few things to note about this mechanism:
|
||||
*
|
||||
* - Existing headers cannot be overwritten. Only new headers can be
|
||||
* added.
|
||||
* - Headers are not merged. They are simply sent to the remote
|
||||
* server. A new object must be retrieved from the server before
|
||||
* these headers will be accessible.
|
||||
* - Swift only stores certain headers. If you supply an unrecognized
|
||||
* header to Swift, it may simply ignore it.
|
||||
* - The RemoteObject::headers() method provides access to all of the
|
||||
* headers returned from Swift.
|
||||
* - Headers are merged in as they are, with no cleaning, encoding, or
|
||||
* checking. You must ensure that the headers are in the proper
|
||||
* format.
|
||||
*
|
||||
* @param array $headers An associative array where each name is an HTTP
|
||||
* header name, and each value is the HTTP header value. No encoding or
|
||||
* escaping is done.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Object $this so the method can be
|
||||
* used in chaining.
|
||||
*/
|
||||
public function setAdditionalHeaders($headers)
|
||||
{
|
||||
$this->additionalHeaders = $headers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return additional headers.
|
||||
*
|
||||
* Headers here have likely not been stored remotely until
|
||||
* Container::save() is called on the object.
|
||||
*/
|
||||
public function additionalHeaders()
|
||||
{
|
||||
return $this->additionalHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove headers.
|
||||
*
|
||||
* This takes an array of header names, and removes
|
||||
* any matching headers. Typically, only headers set
|
||||
* by setAdditionalHeaders() are removed from an Object.
|
||||
* (RemoteObject works differently).
|
||||
*
|
||||
* Many headers are generated automatically, such as
|
||||
* Content-Type and Content-Length. Removing these
|
||||
* will simply result in their being regenerated.
|
||||
*
|
||||
* @param array $keys The header names to be removed.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\Object $this for the current
|
||||
* object so it can be used in chaining methods.
|
||||
*/
|
||||
public function removeHeaders($keys)
|
||||
{
|
||||
foreach ($keys as $k) {
|
||||
unset($this->additionalHeaders[$k]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This object should be transmitted in chunks.
|
||||
*
|
||||
* Indicates whether or not this object should be transmitted as
|
||||
* chunked data (in HTTP).
|
||||
*
|
||||
* This should be used when (a) the file size is large, or (b) the
|
||||
* exact size of the file is unknown.
|
||||
*
|
||||
* If this returns true, it does not guarantee that the data
|
||||
* will be transmitted in chunks. But it recommends that the
|
||||
* underlying transport layer use chunked encoding.
|
||||
*
|
||||
* The contentLength() method is not called for chunked transfers. So
|
||||
* if this returns true, contentLength() is ignored.
|
||||
*
|
||||
* @return boolean true to recommend chunked transfer, false otherwise.
|
||||
*/
|
||||
public function isChunked()
|
||||
{
|
||||
// Currently, this value is hard-coded. The default Object
|
||||
// implementation does not get chunked.
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,669 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\ObjectStore\v1\Resource;
|
||||
|
||||
use OpenStack\Common\Transport\ClientInterface;
|
||||
use OpenStack\Common\Transport\Guzzle\GuzzleAdapter;
|
||||
use OpenStack\ObjectStore\v1\Exception;
|
||||
|
||||
/**
|
||||
* A representation of an object stored in remote Object Storage.
|
||||
*
|
||||
* A remote object is one whose canonical copy is stored in a remote
|
||||
* object storage. It represents a local (and possibly partial) copy of
|
||||
* an object. (Contrast this with \OpenStack\ObjectStore\v1\Resource\Object)
|
||||
*
|
||||
* Depending on how the object was constructed, it may or may not have a
|
||||
* local copy of the entire contents of the file. It may only have the
|
||||
* object's "metadata" (information such as name, type, modification
|
||||
* date, and length of the object). Or it may have all of that in
|
||||
* addition to the entire content of the file.
|
||||
*
|
||||
* Remote objects can be modified locally. Simply modifying an object
|
||||
* will not result in those modifications being stored on the remote
|
||||
* server. The object must be saved (see
|
||||
* \OpenStack\ObjectStore\v1\Resource\Container::save()). When an
|
||||
* object is modified so that its local contents differ from the remote
|
||||
* stored copy, it is marked dirty (see isDirty()).
|
||||
*/
|
||||
class RemoteObject extends Object
|
||||
{
|
||||
protected $contentLength = 0;
|
||||
protected $etag = '';
|
||||
protected $lastModified = 0;
|
||||
|
||||
protected $contentVerification = true;
|
||||
protected $caching = false;
|
||||
|
||||
/**
|
||||
* All headers received from a remote are stored in this array.
|
||||
* Implementing subclasses can access this array for complete access
|
||||
* to the HTTP headers.
|
||||
*
|
||||
* This will be empty if the object was constructed from JSON, and may
|
||||
* serve as a good indicator that the object does not have all
|
||||
* attributes set.
|
||||
*/
|
||||
protected $allHeaders = [];
|
||||
|
||||
/**
|
||||
* The HTTP Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Create a new RemoteObject from JSON data.
|
||||
*
|
||||
* @param array $data The JSON data as an array.
|
||||
* @param string $token The authentication token.
|
||||
* @param $url The URL to the object on the remote server
|
||||
* @param \OpenStack\Common\Transport\ClientInterface $client A HTTP transport client.
|
||||
*/
|
||||
public static function newFromJSON($data, $token, $url, ClientInterface $client = null)
|
||||
{
|
||||
$object = new RemoteObject($data['name']);
|
||||
$object->setContentType($data['content_type']);
|
||||
|
||||
$object->contentLength = (int) $data['bytes'];
|
||||
$object->etag = (string) $data['hash'];
|
||||
$object->lastModified = strtotime($data['last_modified']);
|
||||
|
||||
$object->token = $token;
|
||||
$object->url = $url;
|
||||
|
||||
// FIXME: What do we do about HTTP header data that doesn't come
|
||||
// back in JSON?
|
||||
|
||||
if (is_null($client)) {
|
||||
$client = GuzzleAdapter::create();
|
||||
}
|
||||
$object->setClient($client);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteObject from HTTP headers.
|
||||
*
|
||||
* This is used to create objects from GET and HEAD requests, which
|
||||
* return all of the metadata inside of the headers.
|
||||
*
|
||||
* @param string $name The name of the object.
|
||||
* @param array $headers An associative array of HTTP headers in the exact
|
||||
* format documented by OpenStack's API docs.
|
||||
* @param string $token The current auth token (used for issuing subsequent
|
||||
* requests).
|
||||
* @param string $url The URL to the object in the object storage. Used for
|
||||
* issuing subsequent requests.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\RemoteObject A new RemoteObject.
|
||||
*/
|
||||
public static function newFromHeaders($name, $headers, $token, $url, ClientInterface $client = null)
|
||||
{
|
||||
$object = new RemoteObject($name);
|
||||
|
||||
//$object->allHeaders = $headers;
|
||||
$object->setHeaders($headers);
|
||||
|
||||
//throw new \Exception(print_r($headers, true));
|
||||
|
||||
// Fix inconsistant header.
|
||||
if (isset($headers['ETag'])) {
|
||||
$headers['Etag'] = $headers['ETag'];
|
||||
}
|
||||
|
||||
$object->setContentType($headers['Content-Type']);
|
||||
$object->contentLength = empty($headers['Content-Length']) ? 0 : (int) $headers['Content-Length'];
|
||||
$object->etag = (string) $headers['Etag']; // ETag is now Etag.
|
||||
$object->lastModified = strtotime($headers['Last-Modified']);
|
||||
|
||||
// Set the metadata, too.
|
||||
$object->setMetadata(Container::extractHeaderAttributes($headers));
|
||||
|
||||
|
||||
// If content encoding and disposition exist, set them on the
|
||||
// object.
|
||||
if (!empty($headers['Content-Disposition'])) {
|
||||
$object->setDisposition($headers['Content-Disposition']);
|
||||
|
||||
}
|
||||
if (!empty($headers['Content-Encoding'])) {
|
||||
$object->setEncoding($headers['Content-Encoding']);
|
||||
}
|
||||
|
||||
$object->token = $token;
|
||||
$object->url = $url;
|
||||
|
||||
if (is_null($client)) {
|
||||
$client = GuzzleAdapter::create();
|
||||
}
|
||||
$object->setClient($client);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HTTP Client to use.
|
||||
*
|
||||
* @param OpenStackTransportClientInterface $client The HTTP Client
|
||||
*/
|
||||
public function setClient(ClientInterface $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to this object.
|
||||
*
|
||||
* If this object has been stored remotely, it will have
|
||||
* a valid URL.
|
||||
*
|
||||
* @return string A URL to the object. The following considerations apply:
|
||||
* - If the container is public, this URL can be loaded without
|
||||
* authentication. You can, for example, pass the URL to a browser
|
||||
* user agent.
|
||||
* - If this object has never been saved remotely, then there will be
|
||||
* no URL, and this will return null.
|
||||
*/
|
||||
public function url()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
|
||||
public function contentLength()
|
||||
{
|
||||
if (!empty($this->content)) {
|
||||
return parent::contentLength();
|
||||
}
|
||||
|
||||
return $this->contentLength;
|
||||
}
|
||||
|
||||
public function eTag()
|
||||
{
|
||||
if (!empty($this->content)) {
|
||||
return parent::eTag();
|
||||
}
|
||||
|
||||
return $this->etag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the modification time, as reported by the server.
|
||||
*
|
||||
* This returns an integer timestamp indicating when the server's
|
||||
* copy of this file was last modified.
|
||||
*/
|
||||
public function lastModified()
|
||||
{
|
||||
return $this->lastModified;
|
||||
}
|
||||
|
||||
public function metadata()
|
||||
{
|
||||
// How do we get this?
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the headers
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\RemoteObject $this for the current object so it can be used in chaining
|
||||
* methods.
|
||||
*/
|
||||
public function setHeaders($headers)
|
||||
{
|
||||
$this->allHeaders = [];
|
||||
|
||||
foreach ($headers as $name => $value) {
|
||||
if (strpos($name, Container::METADATA_HEADER_PREFIX) !== 0) {
|
||||
$this->allHeaders[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP headers sent by the server.
|
||||
*
|
||||
* EXPERT.
|
||||
*
|
||||
* This returns the array of minimally processed HTTP headers that
|
||||
* were sent from the server.
|
||||
*
|
||||
* @return array An associative array of header names and values.
|
||||
*/
|
||||
public function headers()
|
||||
{
|
||||
return $this->allHeaders;
|
||||
}
|
||||
|
||||
public function additionalHeaders($mergeAll = false)
|
||||
{
|
||||
// Any additional headers will be set. Note that $this->headers will contain
|
||||
// some headers that are NOT additional. But we do not know which headers are
|
||||
// additional and which are from Swift because Swift does not commit to using
|
||||
// a specific set of headers.
|
||||
if ($mergeAll) {
|
||||
$additionalHeaders = parent::additionalHeaders() + $this->allHeaders;
|
||||
$this->filterHeaders($additionalHeaders);
|
||||
} else {
|
||||
$additionalHeaders = parent::additionalHeaders();
|
||||
}
|
||||
|
||||
return $additionalHeaders;
|
||||
}
|
||||
|
||||
protected $reservedHeaders = [
|
||||
'etag' => true, 'content-length' => true,
|
||||
'x-auth-token' => true,
|
||||
'transfer-encoding' => true,
|
||||
'x-trans-id' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* Filter the headers.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\RemoteObject $this for the current object so it can be used in chaining
|
||||
* methods.
|
||||
*/
|
||||
public function filterHeaders(&$headers)
|
||||
{
|
||||
$unset = [];
|
||||
foreach ($headers as $name => $value) {
|
||||
$lower = strtolower($name);
|
||||
if (isset($this->reservedHeaders[$lower])) {
|
||||
$unset[] = $name;
|
||||
}
|
||||
}
|
||||
foreach ($unset as $u) {
|
||||
unset($headers[$u]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of header names.
|
||||
*
|
||||
* This will remove the given headers from the existing headers.
|
||||
* Both additional headers and the original headers from the
|
||||
* server are affected here.
|
||||
*
|
||||
* Note that you cannot remove metadata through this mechanism,
|
||||
* as it is managed using the metadata() methods.
|
||||
*
|
||||
* Many headers are generated automatically, such as
|
||||
* Content-Type and Content-Length. Removing these
|
||||
* will simply result in their being regenerated.
|
||||
*
|
||||
* @param array $keys The header names to be removed.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\RemoteObject $this for the current object so it can be used in chaining
|
||||
* methods.
|
||||
*/
|
||||
public function removeHeaders($keys)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
unset($this->allHeaders[$key]);
|
||||
unset($this->additionalHeaders[$key]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of this object.
|
||||
*
|
||||
* Since this is a proxy object, calling content() will cause the
|
||||
* object to be fetched from the remote data storage. The result will
|
||||
* be delivered as one large string.
|
||||
*
|
||||
* The file size, content type, etag, and modification date of the
|
||||
* object are all updated during this command, too. This accounts for
|
||||
* the possibility that the content was modified externally between
|
||||
* the time this object was constructed and the time this method was
|
||||
* executed.
|
||||
*
|
||||
* Be wary of using this method with large files.
|
||||
*
|
||||
* @return string The contents of the file as a string.
|
||||
*
|
||||
* @throws \OpenStack\Common\Transport\Exception\ResourceNotFoundException when the requested content cannot be
|
||||
* located on the remote server.
|
||||
* @throws \OpenStack\Common\Exception when an unknown exception (usually an
|
||||
* abnormal network condition) occurs.
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
// XXX: This allows local overwrites. Is this a good idea?
|
||||
if (!empty($this->content)) {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
// Get the object, content included.
|
||||
$response = $this->fetchObject(true);
|
||||
|
||||
$content = $response->getBody();
|
||||
|
||||
// Checksum the content.
|
||||
// XXX: Right now the md5 is done even if checking is turned off.
|
||||
// Should fix that.
|
||||
$check = md5($content);
|
||||
if ($this->isVerifyingContent() && $check != $this->etag()) {
|
||||
throw new Exception\ContentVerificationException("Checksum $check does not match Etag " . $this->etag());
|
||||
}
|
||||
|
||||
// If we are caching, set the content locally when we retrieve
|
||||
// remotely.
|
||||
if ($this->isCaching()) {
|
||||
$this->setContent($content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of this object as a file stream.
|
||||
*
|
||||
* This is useful for large objects. Such objects should not be read
|
||||
* into memory all at once (as content() does), but should instead be
|
||||
* made available as an input stream.
|
||||
*
|
||||
* PHP offers low-level stream support in the form of PHP stream
|
||||
* wrappers, and this mechanism is used internally whenever available.
|
||||
*
|
||||
* If there is a local copy of the content, the stream will be read
|
||||
* out of the content as if it were a temp-file backed in-memory
|
||||
* resource. To ignore the local version, pass in true for the
|
||||
* $refresh parameter.
|
||||
*
|
||||
* If the content is coming from a remote copy, the stream will be
|
||||
* read directly from the underlying IO stream.
|
||||
*
|
||||
* Each time stream() is called, a new stream is created. In most
|
||||
* cases, this results in a new HTTP transaction (unless $refresh is
|
||||
* false and the content is already stored locally).
|
||||
*
|
||||
* The stream is read-only.
|
||||
*
|
||||
* @param boolean $refresh If this is set to true, any existing local
|
||||
* modifications will be ignored and the content will
|
||||
* be refreshed from the server. Any local changes to
|
||||
* the object will be discarded.
|
||||
*
|
||||
* @return resource A handle to the stream, which is already opened and
|
||||
* positioned at the beginning of the stream.
|
||||
*/
|
||||
public function stream($refresh = false)
|
||||
{
|
||||
// If we're working on local content, return that content wrapped in
|
||||
// a fake IO stream.
|
||||
if (!$refresh && isset($this->content)) {
|
||||
return $this->localFileStream();
|
||||
}
|
||||
|
||||
// Otherwise, we fetch a fresh version from the remote server and
|
||||
// return its stream handle.
|
||||
$response = $this->fetchObject(true);
|
||||
|
||||
// Write to in-mem handle backed by a temp file.
|
||||
$out = fopen('php://temp', 'rb+');
|
||||
fwrite($out, $response->getBody());
|
||||
rewind($out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a local copy of content into a file stream.
|
||||
*
|
||||
* This buffers the content into a stream resource and then returns
|
||||
* the stream resource. The resource is not used internally, and its
|
||||
* data is never written back to the remote object storage.
|
||||
*/
|
||||
protected function localFileStream()
|
||||
{
|
||||
$tmp = fopen('php://temp', 'rw');
|
||||
fwrite($tmp, $this->content(), $this->contentLength());
|
||||
rewind($tmp);
|
||||
|
||||
return $tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable content caching.
|
||||
*
|
||||
* If a RemoteObject is set to cache then the first time content() is
|
||||
* called, its results will be cached locally. This is very useful for
|
||||
* small files whose content is accessed repeatedly, but can be a
|
||||
* cause of memory consumption for larger files.
|
||||
*
|
||||
* If caching settings are changed after content is retrieved, the
|
||||
* already retrieved content will not be affected, though any
|
||||
* subsequent requests will use the new caching settings. That is,
|
||||
* existing cached content will not be removed if caching is turned
|
||||
* off.
|
||||
*
|
||||
* @param boolean $enabled If this is true, caching will be enabled. If this
|
||||
* is false, caching will be disabled.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\RemoteObject $this so the method can be used in chaining.
|
||||
*/
|
||||
public function setCaching($enabled)
|
||||
{
|
||||
$this->caching = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this object caches content.
|
||||
*
|
||||
* Importantly, this indicates whether the object will cache
|
||||
* its contents, not whether anything is actually cached.
|
||||
*
|
||||
* @return boolean true if caching is enabled, false otherwise.
|
||||
*/
|
||||
public function isCaching()
|
||||
{
|
||||
return $this->caching;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable content verification (checksum/md5).
|
||||
*
|
||||
* The default behavior of a RemoteObject is to verify that the MD5
|
||||
* provided by the server matches the locally generated MD5 of the
|
||||
* file contents.
|
||||
*
|
||||
* If content verification is enabled, then whenever the content is
|
||||
* fetched from the remote server, its checksum is calculated and
|
||||
* tested against the ETag value. This provides a layer of assurance
|
||||
* that the payload of the HTTP request was not altered during
|
||||
* transmission.
|
||||
*
|
||||
* This featured can be turned off, which is sometimes necessary on
|
||||
* systems that do not correctly produce MD5s. Turning this off might
|
||||
* also provide a small performance improvement on large files, but at
|
||||
* the expense of security.
|
||||
*
|
||||
* @param boolean $enabled If this is true, content verification is performed.
|
||||
* The content is hashed and checked against a
|
||||
* server-supplied MD5 hashcode. If this is false,
|
||||
* no checking is done.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\RemoteObject $this so the method can be used in chaining.
|
||||
*/
|
||||
public function setContentVerification($enabled)
|
||||
{
|
||||
$this->contentVerification = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether this object verifies content (checksum).
|
||||
*
|
||||
* When content verification is on, RemoteObject attemts to perform a
|
||||
* checksum on the object, calculating the MD5 hash of the content
|
||||
* returned by the remote server, and comparing that to the server's
|
||||
* supplied ETag hash.
|
||||
*
|
||||
* @return boolean true if this is verifying, false otherwise.
|
||||
*/
|
||||
public function isVerifyingContent()
|
||||
{
|
||||
return $this->contentVerification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether there are unsaved changes.
|
||||
*
|
||||
* An object is marked "dirty" if it has been altered
|
||||
* locally in such a way that it no longer matches the
|
||||
* remote version.
|
||||
*
|
||||
* The practical definition of dirtiness, for us, is this: An object
|
||||
* is dirty if and only if (a) it has locally buffered content AND (b)
|
||||
* the checksum of the local content does not match the checksom of
|
||||
* the remote content.
|
||||
*
|
||||
* Not that minor differences, such as altered character encoding, may
|
||||
* change the checksum value, and thus (correctly) mark the object as
|
||||
* dirty.
|
||||
*
|
||||
* The RemoteObject implementation does not internally check dirty
|
||||
* markers. It is left to implementors to ensure that dirty content is
|
||||
* written to the remote server when desired.
|
||||
*
|
||||
* To replace dirty content with a clean copy, see refresh().
|
||||
*
|
||||
* @return boolean Whether or not there are unsaved changes.
|
||||
*/
|
||||
public function isDirty()
|
||||
{
|
||||
// If there is no content, the object can't be dirty.
|
||||
if (!isset($this->content)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Content is dirty iff content is set, and it is
|
||||
// different from the original content. Note that
|
||||
// we are using the etag from the original headers.
|
||||
if ($this->etag != md5($this->content)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the local object from the remote.
|
||||
*
|
||||
* This refetches the object from the object store and then
|
||||
* reconstructs the present object based on the refreshed data.
|
||||
*
|
||||
* WARNING: This will destroy any unsaved local changes. You can use
|
||||
* isDirty() to determine whether or not a local change has been made.
|
||||
*
|
||||
* @param boolean $fetchContent If this is true, the content will be
|
||||
* downloaded as well.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\RemoteObject $this for the current object so it
|
||||
* can be used in chaining methods.
|
||||
*/
|
||||
public function refresh($fetchContent = false)
|
||||
{
|
||||
// Kill old content.
|
||||
unset($this->content);
|
||||
|
||||
$response = $this->fetchObject($fetchContent);
|
||||
|
||||
|
||||
if ($fetchContent) {
|
||||
$this->setContent($response->getBody());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for fetching an object.
|
||||
*
|
||||
* @param boolean $fetchContent If this is set to true, a GET request will be
|
||||
* issued, which will cause the remote host to
|
||||
* return the object in the response body. The
|
||||
* response body is not handled, though. If this
|
||||
* is set to false, a HEAD request is sent, and
|
||||
* no body is returned.
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\Response containing the object metadata and (depending
|
||||
* on the $fetchContent flag) optionally the data.
|
||||
*/
|
||||
protected function fetchObject($fetchContent = false)
|
||||
{
|
||||
$method = $fetchContent ? 'GET' : 'HEAD';
|
||||
|
||||
$headers = ['X-Auth-Token' => $this->token];
|
||||
|
||||
$response = $this->client->send(
|
||||
$this->client->createRequest($method, $this->url, null, ['headers' => $headers])
|
||||
);
|
||||
|
||||
if ($response->getStatusCode() != 200) {
|
||||
throw new \OpenStack\Common\Exception('An unknown exception occurred during transmission.');
|
||||
}
|
||||
|
||||
$this->extractFromHeaders($response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract information from HTTP headers.
|
||||
*
|
||||
* This is used internally to set object properties from headers.
|
||||
*
|
||||
* @return \OpenStack\ObjectStore\v1\Resource\RemoteObject $this for the current object so it
|
||||
* can be used in chaining methods.
|
||||
*/
|
||||
protected function extractFromHeaders($response)
|
||||
{
|
||||
$this->setContentType($response->getHeader('Content-Type') ? $response->getHeader('Content-Type') : $this->contentType());
|
||||
$this->lastModified = strtotime($response->getHeader('Last-Modified') ? $response->getHeader('Last-Modified') : 0);
|
||||
$this->etag = $response->getHeader('Etag') ? $response->getHeader('Etag') : $this->etag;
|
||||
$this->contentLength = (int) ($response->getHeader('Content-Length') ? $response->getHeader('Content-Length') : 0);
|
||||
|
||||
$this->setDisposition($response->getHeader('Content-Disposition', null));
|
||||
$this->setEncoding($response->getHeader('Content-Encoding', null));
|
||||
|
||||
// Reset the metadata, too:
|
||||
$headers = [];
|
||||
foreach ($response->getHeaders() as $name => $header) {
|
||||
$headers[$name] = $header[0];
|
||||
}
|
||||
$this->setMetadata(Container::extractHeaderAttributes($headers));
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,218 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains the stream wrapper for `swiftfs://` URLs.
|
||||
*
|
||||
* Note, this stream wrapper is in early testing.
|
||||
*
|
||||
* The stream wrapper implemented in \OpenStack\ObjectStore\v1\Resource\StreamWrapper
|
||||
* only supports the elements of a stream that are implemented by object
|
||||
* storage. This is how the PHP documentation states a stream wrapper should be
|
||||
* created. Because some features do not exist, attempting to treat a stream
|
||||
* wrapper as if it were a file system will not entirely work. For example,
|
||||
* while there are not directories objects have pathy names (with / separators).
|
||||
* Directory calls to object storage with the default stream wrappers will not
|
||||
* operate how they would for a file system.
|
||||
*
|
||||
* StreamWrapperFS is an attempt to make a filesystem like stream wrapper.
|
||||
* Hence the protocol is swiftfs standing for swift file system.
|
||||
*
|
||||
* To understand how this stream wrapper works start by first reading the
|
||||
* documentation on the \OpenStack\ObjectStore\v1\Resource\StreamWrapper.
|
||||
*
|
||||
* DIRECTORIES
|
||||
*
|
||||
* Because OpenStack Swift does not support directories the swift:// stream
|
||||
* wrapper does not support them. This stream wrapper attempts to fake them by
|
||||
* faking directory stats, mkdir, and rmdir. By default (see the options below
|
||||
* for how to change these) directories have permissions of 777, timestamps
|
||||
* close to that of the request, and the user and group called by php. We mock
|
||||
* these on the fly though information is stored in the PHP stat cache.
|
||||
*
|
||||
* In addition to the parameters supported by StreamWrapper, the following
|
||||
* parameters may be set either in the stream context or through
|
||||
* OpenStack\Bootstrap::setConfiguration():
|
||||
* - swiftfs_fake_stat_mode: Directories don't exist in swift. When stat() is
|
||||
* is called on a directory we mock the stat information so functions like
|
||||
* is_dir will work. The default file permissions is 0777. Though this
|
||||
* parameter you can pass is a different set of file permissions to use
|
||||
* for these mock stats.
|
||||
* - swiftfs_fake_isdir_true: Directory functions like mkdir and is_dir (stat)
|
||||
* check to see if there are objects with the the passed in directory as a
|
||||
* prefix to see if it already exists. If you want is_dir to always return
|
||||
* true even if it is not an existing prefix set this to true. Defaults to
|
||||
* false.
|
||||
*/
|
||||
|
||||
namespace OpenStack\ObjectStore\v1\Resource;
|
||||
|
||||
use \OpenStack\Bootstrap;
|
||||
use \OpenStack\ObjectStore\v1\ObjectStorage;
|
||||
|
||||
/**
|
||||
* Provides stream wrapping for Swift like a file system.
|
||||
*
|
||||
* This provides a full stream wrapper to expose `swiftfs://` URLs to the
|
||||
* PHP stream system.
|
||||
*
|
||||
* @see http://us3.php.net/manual/en/class.streamwrapper.php
|
||||
*/
|
||||
class StreamWrapperFS extends StreamWrapper
|
||||
{
|
||||
const DEFAULT_SCHEME = 'swiftfs';
|
||||
protected $schemeName = self::DEFAULT_SCHEME;
|
||||
|
||||
/**
|
||||
* Fake a make a dir.
|
||||
*
|
||||
* ObjectStorage has pathy objects not directories. If no objects with a path
|
||||
* prefix exist we can pass creating a directory. If objects with a path
|
||||
* prefix exist adding the directory will fail.
|
||||
*/
|
||||
public function mkdir($uri, $mode, $options)
|
||||
{
|
||||
return ($this->cxt('swiftfs_fake_isdir_true', false) || !($this->testDirectoryExists($uri)));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fake Remove a directory.
|
||||
*
|
||||
* ObjectStorage has pathy objects not directories. If no objects with a path
|
||||
* prefix exist we can pass removing it. If objects with a path prefix exist
|
||||
* removing the directory will fail.
|
||||
*/
|
||||
public function rmdir($path, $options)
|
||||
{
|
||||
return !($this->testDirectoryExists($path));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see stream_stat().
|
||||
*/
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$stat = parent::url_stat($path, $flags);
|
||||
|
||||
// If the file stat setup returned anything return it.
|
||||
if ($stat) {
|
||||
return $stat;
|
||||
}
|
||||
// When false is returned there is no file to stat. So, we attempt to handle
|
||||
// it like a directory.
|
||||
else {
|
||||
if ($this->cxt('swiftfs_fake_isdir_true', false) || $this->testDirectoryExists($path)) {
|
||||
// The directory prefix exists. Fake the directory file permissions.
|
||||
return $this->fakeStat(true);
|
||||
} else {
|
||||
// The directory does not exist as a prefix.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// INTERNAL METHODS
|
||||
// All methods beneath this line are not part of the Stream API.
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Test if a path prefix (directory like) esits.
|
||||
*
|
||||
* ObjectStorage has pathy objects not directories. If objects exist with a
|
||||
* path prefix we can consider that the directory exists. For example, if
|
||||
* we have an object at foo/bar/baz.txt and test the existance of the
|
||||
* directory foo/bar/ we sould see it.
|
||||
*
|
||||
* @param string $path The directory path to test.
|
||||
*
|
||||
* @return boolean true if the directory prefix exists and false otherwise.
|
||||
*/
|
||||
protected function testDirectoryExists($path)
|
||||
{
|
||||
$url = $this->parseUrl($path);
|
||||
|
||||
if (empty($url['host'])) {
|
||||
trigger_error('Container name is required.' , E_USER_WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->initializeObjectStorage();
|
||||
$container = $this->store->container($url['host']);
|
||||
|
||||
if (empty($url['path'])) {
|
||||
$this->dirPrefix = '';
|
||||
} else {
|
||||
$this->dirPrefix = $url['path'];
|
||||
}
|
||||
|
||||
$sep = '/';
|
||||
|
||||
|
||||
$dirListing = $container->objectsWithPrefix($this->dirPrefix, $sep);
|
||||
|
||||
return !empty($dirListing);
|
||||
} catch (\OpenStack\Common\Exception $e) {
|
||||
trigger_error('Path could not be opened: ' . $e->getMessage(), E_USER_WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fake stat data.
|
||||
*
|
||||
* Under certain conditions we have to return totally trumped-up
|
||||
* stats. This generates those.
|
||||
*/
|
||||
protected function fakeStat($dir = false)
|
||||
{
|
||||
$request_time = time();
|
||||
|
||||
// Set inode type to directory or file.
|
||||
$type = $dir ? 040000 : 0100000;
|
||||
// Fake world-readible
|
||||
$mode = $type + $this->cxt('swiftfs_fake_stat_mode', 0777);
|
||||
|
||||
$values = [
|
||||
'dev' => 0,
|
||||
'ino' => 0,
|
||||
'mode' => $mode,
|
||||
'nlink' => 0,
|
||||
'uid' => posix_getuid(),
|
||||
'gid' => posix_getgid(),
|
||||
'rdev' => 0,
|
||||
'size' => 0,
|
||||
'atime' => $request_time,
|
||||
'mtime' => $request_time,
|
||||
'ctime' => $request_time,
|
||||
'blksize' => -1,
|
||||
'blocks' => -1,
|
||||
];
|
||||
|
||||
$final = array_values($values) + $values;
|
||||
|
||||
return $final;
|
||||
}
|
||||
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 OpenStack\ObjectStore\v1\Resource;
|
||||
|
||||
/**
|
||||
* Represent a subdirectory (subdir) entry.
|
||||
*
|
||||
* Depending on the method with which Swift container requests are
|
||||
* executed, Swift may return subdir entries instead of Objects.
|
||||
*
|
||||
* Subdirs are used for things that are directory-like.
|
||||
*/
|
||||
class Subdir
|
||||
{
|
||||
/**
|
||||
* @var string The path string that this subdir describes
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var string The delimiter used in this path
|
||||
*/
|
||||
protected $delimiter;
|
||||
|
||||
/**
|
||||
* Create a new subdirectory.
|
||||
*
|
||||
* This represents a remote response's tag for a subdirectory.
|
||||
*
|
||||
* @param string $path The path string that this subdir describes.
|
||||
* @param string $delimiter The delimiter used in this path.
|
||||
*/
|
||||
public function __construct($path, $delimiter = '/')
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path.
|
||||
*
|
||||
* The path is delimited using the string returned by delimiter().
|
||||
*
|
||||
* @return string The path
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
/**
|
||||
* Get the delimiter used by the server.
|
||||
*
|
||||
* @return string The value used as a delimiter.
|
||||
*/
|
||||
public function delimiter()
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
}
|
105
tests/README.md
105
tests/README.md
@ -1,105 +0,0 @@
|
||||
# Running Tests for the PHP-Client bindings
|
||||
|
||||
This file explains how to configured your environment for running the
|
||||
PHP-Client automated testing.
|
||||
|
||||
The OpenStack bindings offer a few stand-alone tests for testing basic
|
||||
connectivity to OpenStack services, but most tests are of the
|
||||
automated variety.
|
||||
|
||||
*IMPORTANT*: Make sure your settings.ini file is up-to-date! Options
|
||||
have changed!
|
||||
|
||||
## Stand-alone Tests
|
||||
|
||||
Stand-alone tests are designed to verify that certain preconditions of
|
||||
the libary are met.
|
||||
|
||||
### AuthTest.php
|
||||
|
||||
The AuthTest test is a simple commandline program that allows you to
|
||||
verify that your PHP client can successfully connect to OpenStack. To
|
||||
run this test, do the following:
|
||||
|
||||
1. Begin from the root directory of this project, where you should see
|
||||
the directories `tests/` and `src/`, among others.
|
||||
2. Execute the following command on the commandline:
|
||||
|
||||
```
|
||||
$ php tests/AuthTest.php
|
||||
```
|
||||
|
||||
This will instruct you to use a more complete version of the command,
|
||||
including:
|
||||
|
||||
* USERNAME: The username given to you.
|
||||
* PASSWORD: The password associated with the username.
|
||||
* URL: The Endpoint URL.
|
||||
* TENANT ID: Your users's tenant ID.
|
||||
|
||||
All four pieces of information can be found by logging into the
|
||||
console. From there, you can execute a command like this:
|
||||
|
||||
```
|
||||
$ php tests/AuthTest.php myusername apassword https://region-a.geo-1.identity.hpcloudsvc.com:35357/v2.0/ 1234567
|
||||
|
||||
```
|
||||
|
||||
If successfull, it should return details about your username, token, and
|
||||
the services in your service catalog.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Unit and behavioral tests are built using [PHPUnit](http://www.phpunit.de/). Before you can
|
||||
test this package, you will need to [install that tool](http://www.phpunit.de/manual/3.7/en/installation.html).
|
||||
|
||||
Next, you need to create your own `settings.ini` file to contain your HP
|
||||
Cloud credentials, along with your preferred testing parameters.
|
||||
|
||||
### Creating settings.ini
|
||||
|
||||
The easiest way to do this is to copy the example settings file, and
|
||||
then make the necessary changes:
|
||||
|
||||
$ cd tests/
|
||||
$ cp example.settings.ini settings.ini
|
||||
$ edit settings.ini
|
||||
|
||||
### Running Tests
|
||||
|
||||
The test suite uses PHPUnit and can generate a code coverage report if
|
||||
xdebug is installed. To run the test suite make sure PHPUnit is installed
|
||||
via composer by using `composer install` or `composer update`. Once PHPUnit is
|
||||
installed execute the following command from the root of the project.
|
||||
|
||||
$ ./vendor/bin/phpunit
|
||||
|
||||
This should generate output looking something like this:
|
||||
|
||||
PHPUnit 4.0.13 by Sebastian Bergmann.
|
||||
|
||||
Configuration read from /path/to/openstack-sdk-php/phpunit.xml.dist
|
||||
|
||||
............................................................... 63 / 146 ( 43%)
|
||||
............................................................... 126 / 146 ( 86%)
|
||||
....................
|
||||
|
||||
Time: 4.94 minutes, Memory: 17.50Mb
|
||||
|
||||
OK (146 tests, 413 assertions)
|
||||
|
||||
Generating code coverage report in Clover XML format ... done
|
||||
|
||||
Generating code coverage report in HTML format ... done
|
||||
|
||||
If the tests fail, detailed information about the failure will be
|
||||
displayed.
|
||||
|
||||
PHPUnit has a wide variety of commandline options. Other sorts of
|
||||
reports and analyses can be done using those.
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Tests should be written according to the PHPUnit documentation. Tests
|
||||
should follow the same coding standards as all other parts of the
|
||||
library.
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* 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 OpenStack\Tests;
|
||||
|
||||
class BootstrapTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Canary test.
|
||||
*/
|
||||
public function testSettings()
|
||||
{
|
||||
$this->assertTrue(!empty(self::$settings));
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\Common\Transport;
|
||||
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
class AbstractClientTest extends TestCase
|
||||
{
|
||||
const URI = 'http://openstack.org';
|
||||
|
||||
private $client;
|
||||
private $request;
|
||||
private $options = ['foo' => 'bar'];
|
||||
private $body = 'baz';
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->request = $this->getMockBuilder('OpenStack\Common\Transport\RequestInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->client = $this->getMockForAbstractClass('OpenStack\Common\Transport\AbstractClient');
|
||||
|
||||
$this->client->expects($this->once())
|
||||
->method('send')
|
||||
->with($this->request);
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$this->client->expects($this->once())
|
||||
->method('createRequest')
|
||||
->with('GET', self::URI, null, $this->options)
|
||||
->will($this->returnValue($this->request));
|
||||
|
||||
$this->client->get(self::URI, $this->options);
|
||||
}
|
||||
|
||||
public function testHead()
|
||||
{
|
||||
$this->client->expects($this->once())
|
||||
->method('createRequest')
|
||||
->with('HEAD', self::URI, null, $this->options)
|
||||
->will($this->returnValue($this->request));
|
||||
|
||||
$this->client->head(self::URI, $this->options);
|
||||
}
|
||||
|
||||
public function testPost()
|
||||
{
|
||||
$this->client->expects($this->once())
|
||||
->method('createRequest')
|
||||
->with('POST', self::URI, $this->body, $this->options)
|
||||
->will($this->returnValue($this->request));
|
||||
|
||||
$this->client->post(self::URI, $this->body, $this->options);
|
||||
}
|
||||
|
||||
public function testPut()
|
||||
{
|
||||
$this->client->expects($this->once())
|
||||
->method('createRequest')
|
||||
->with('PUT', self::URI, $this->body, $this->options)
|
||||
->will($this->returnValue($this->request));
|
||||
|
||||
$this->client->put(self::URI, $this->body, $this->options);
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
{
|
||||
$this->client->expects($this->once())
|
||||
->method('createRequest')
|
||||
->with('DELETE', self::URI, null, $this->options)
|
||||
->will($this->returnValue($this->request));
|
||||
|
||||
$this->client->delete(self::URI, $this->options);
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\Common\Transport\Guzzle;
|
||||
|
||||
use OpenStack\Common\Transport\Guzzle\GuzzleAdapter;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
class GuzzleClientTest extends TestCase
|
||||
{
|
||||
const TEST_URL = 'http://openstack.org';
|
||||
|
||||
private $mockClient;
|
||||
private $adapter;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->mockClient = $this->getMockBuilder('GuzzleHttp\Client')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->adapter = new GuzzleAdapter($this->mockClient);
|
||||
}
|
||||
|
||||
public function testFactoryReturnsInstance()
|
||||
{
|
||||
$this->assertInstanceOf(
|
||||
'OpenStack\Common\Transport\Guzzle\GuzzleAdapter',
|
||||
$this->adapter
|
||||
);
|
||||
}
|
||||
|
||||
public function testFactoryMethod()
|
||||
{
|
||||
$this->assertInstanceOf(
|
||||
'OpenStack\Common\Transport\Guzzle\GuzzleAdapter',
|
||||
GuzzleAdapter::create()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCreateRequestCallsClientAndReturnsAdapter()
|
||||
{
|
||||
$this->mockClient
|
||||
->expects($this->once())
|
||||
->method('createRequest')
|
||||
->with('GET')
|
||||
->will($this->returnValue(
|
||||
$this->getMock('GuzzleHttp\Message\RequestInterface')
|
||||
));
|
||||
|
||||
$adapter = (new GuzzleAdapter($this->mockClient))->createRequest('GET');
|
||||
$this->assertInstanceOf('OpenStack\Common\Transport\Guzzle\RequestAdapter', $adapter);
|
||||
$this->assertInstanceOf('GuzzleHttp\Message\RequestInterface', $adapter->getMessage());
|
||||
}
|
||||
|
||||
public function testSetOptionCallsClient()
|
||||
{
|
||||
$key = 'foo';
|
||||
$value = 'bar';
|
||||
$this->mockClient->expects($this->once())->method('setDefaultOption')->with($key, $value);
|
||||
|
||||
(new GuzzleAdapter($this->mockClient))->setOption($key, $value);
|
||||
}
|
||||
|
||||
public function testGetBaseUrlWithOption()
|
||||
{
|
||||
$this->mockClient->expects($this->once())->method('getBaseUrl');
|
||||
(new GuzzleAdapter($this->mockClient))->getOption('base_url');
|
||||
}
|
||||
|
||||
public function testGetOption()
|
||||
{
|
||||
$this->mockClient->expects($this->once())->method('getDefaultOption')->with('foo');
|
||||
(new GuzzleAdapter($this->mockClient))->getOption('foo');
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\Common\Transport\Guzzle;
|
||||
|
||||
use GuzzleHttp\Adapter\Transaction;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Event\CompleteEvent;
|
||||
use GuzzleHttp\Message\Request;
|
||||
use GuzzleHttp\Message\Response;
|
||||
use OpenStack\Common\Transport\Guzzle\HttpError;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
class HttpErrorTest extends TestCase
|
||||
{
|
||||
public function testInheritance()
|
||||
{
|
||||
$sub = new HttpError();
|
||||
$this->assertInstanceOf('OpenStack\Common\Transport\Guzzle\HttpError', $sub);
|
||||
}
|
||||
|
||||
private function getEvent()
|
||||
{
|
||||
return new CompleteEvent(new Transaction(new Client(), new Request('GET', '/')));
|
||||
}
|
||||
|
||||
public function testSuccessfulResponsesThrowNothing()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(200));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Transport\Exception\ConflictException
|
||||
*/
|
||||
public function testConflictExceptionRaisedFor409Error()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(409));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Transport\Exception\ForbiddenException
|
||||
*/
|
||||
public function testConflictExceptionRaisedFor403Error()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(403));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Transport\Exception\LengthRequiredException
|
||||
*/
|
||||
public function testConflictExceptionRaisedFor411Error()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(411));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Transport\Exception\MethodNotAllowedException
|
||||
*/
|
||||
public function testConflictExceptionRaisedFor405Error()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(405));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Transport\Exception\ResourceNotFoundException
|
||||
*/
|
||||
public function testConflictExceptionRaisedFor404Error()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(404));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Transport\Exception\ServerException
|
||||
*/
|
||||
public function testConflictExceptionRaisedFor500Error()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(500));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Transport\Exception\UnauthorizedException
|
||||
*/
|
||||
public function testConflictExceptionRaisedFor401Error()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(401));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Transport\Exception\UnprocessableEntityException
|
||||
*/
|
||||
public function testConflictExceptionRaisedFor422Error()
|
||||
{
|
||||
$event = $this->getEvent();
|
||||
$event->intercept(new Response(422));
|
||||
(new HttpError())->onComplete($event);
|
||||
}
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\Common\Transport\Guzzle;
|
||||
|
||||
use OpenStack\Common\Transport\Guzzle\MessageAdapter;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
class MessageAdapterTest extends TestCase
|
||||
{
|
||||
const REQUEST_CLASS = 'GuzzleHttp\Message\Request';
|
||||
const RESPONSE_CLASS = 'GuzzleHttp\Message\Response';
|
||||
|
||||
private $mock;
|
||||
private $adapter;
|
||||
|
||||
private function getStub($class)
|
||||
{
|
||||
return $this->getMockBuilder($class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->mock = $this->getStub(self::REQUEST_CLASS);
|
||||
$this->adapter = new MessageAdapter($this->mock);
|
||||
}
|
||||
|
||||
public function testConstructorSetsMessage()
|
||||
{
|
||||
$this->assertInstanceOf(self::REQUEST_CLASS, $this->adapter->getMessage());
|
||||
}
|
||||
|
||||
public function testSettingMessage()
|
||||
{
|
||||
$this->adapter->setMessage($this->getStub(self::RESPONSE_CLASS));
|
||||
$this->assertInstanceOf(self::RESPONSE_CLASS, $this->adapter->getMessage());
|
||||
}
|
||||
|
||||
public function testGetProtocol()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('getProtocolVersion');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->getProtocolVersion();
|
||||
}
|
||||
|
||||
public function testSetBody()
|
||||
{
|
||||
$body = $this->getMock('GuzzleHttp\Stream\StreamInterface');
|
||||
$this->mock->expects($this->once())->method('setBody')->with($body);
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->setBody($body);
|
||||
}
|
||||
|
||||
public function testGetBody()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('getBody');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->getBody();
|
||||
}
|
||||
|
||||
public function testGetHeaders()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('getHeaders');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->getHeaders();
|
||||
}
|
||||
|
||||
public function testHasHeader()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('hasHeader')->with('foo');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->hasHeader('foo');
|
||||
}
|
||||
|
||||
public function testSetHeader()
|
||||
{
|
||||
$header = 'foo';
|
||||
$value = 'bar';
|
||||
$this->mock->expects($this->once())->method('setHeader')->with($header, $value);
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->setHeader($header, $value);
|
||||
}
|
||||
|
||||
public function testGetHeader()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('getHeader')->with('foo');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->getHeader('foo');
|
||||
}
|
||||
|
||||
public function testSetHeaders()
|
||||
{
|
||||
$headers = ['foo' => 'bar'];
|
||||
$this->mock->expects($this->once())->method('setHeaders')->with($headers);
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->setHeaders($headers);
|
||||
}
|
||||
|
||||
public function testAddHeader()
|
||||
{
|
||||
$header = 'foo';
|
||||
$value = 'bar';
|
||||
$this->mock->expects($this->once())->method('addHeader')->with($header, $value);
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->addHeader($header, $value);
|
||||
}
|
||||
|
||||
public function testAddHeaders()
|
||||
{
|
||||
$headers = ['foo' => 'bar'];
|
||||
$this->mock->expects($this->once())->method('addHeaders')->with($headers);
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->addHeaders($headers);
|
||||
}
|
||||
|
||||
public function testRemoveHeader()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('removeHeader')->with('foo');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->removeHeader('foo');
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\Common\Transport\Guzzle;
|
||||
|
||||
use OpenStack\Common\Transport\Guzzle\RequestAdapter;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
class RequestAdapterTest extends TestCase
|
||||
{
|
||||
private $mock;
|
||||
private $adapter;
|
||||
|
||||
private function getStub($class)
|
||||
{
|
||||
return $this->getMockBuilder($class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->mock = $this->getStub('GuzzleHttp\Message\Request');
|
||||
$this->adapter = new RequestAdapter($this->mock);
|
||||
}
|
||||
|
||||
public function testGetMethod()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('getMethod');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->getMethod();
|
||||
}
|
||||
|
||||
public function testSetMethod()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('setMethod')->with('foo');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->setMethod('foo');
|
||||
}
|
||||
|
||||
public function testGetUrl()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('getUrl');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->getUrl();
|
||||
}
|
||||
|
||||
public function testSetUrl()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('setUrl')->with('foo');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->setUrl('foo');
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\Common\Transport\Guzzle;
|
||||
|
||||
use OpenStack\Common\Transport\Guzzle\ResponseAdapter;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
class ResponseAdapterTest extends TestCase
|
||||
{
|
||||
private $mock;
|
||||
private $adapter;
|
||||
|
||||
private function getStub($class)
|
||||
{
|
||||
return $this->getMockBuilder($class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->mock = $this->getStub('GuzzleHttp\Message\Response');
|
||||
$this->adapter = new ResponseAdapter($this->mock);
|
||||
}
|
||||
|
||||
public function testGetStatusCode()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('getStatusCode');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->getStatusCode();
|
||||
}
|
||||
|
||||
public function testGetReasonPhrase()
|
||||
{
|
||||
$this->mock->expects($this->once())->method('getReasonPhrase');
|
||||
$this->adapter->setMessage($this->mock);
|
||||
$this->adapter->getReasonPhrase();
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\Common\Transport;
|
||||
|
||||
use OpenStack\Common\Transport\Url;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
class UrlTest extends TestCase
|
||||
{
|
||||
const URL_STRING = 'https://username:password@openstack.org:80/community/members#anchor';
|
||||
|
||||
private $url;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->url = new Url(self::URL_STRING);
|
||||
}
|
||||
|
||||
public function testIsConstructedWithProperties()
|
||||
{
|
||||
$this->assertEquals('https', $this->url->getScheme());
|
||||
$this->assertEquals('openstack.org', $this->url->getHost());
|
||||
$this->assertEquals('80', $this->url->getPort());
|
||||
$this->assertEquals('/community/members', $this->url->getPath());
|
||||
$this->assertEquals('username', $this->url->getUser());
|
||||
$this->assertEquals('password', $this->url->getPassword());
|
||||
$this->assertEquals('anchor', $this->url->getFragment());
|
||||
}
|
||||
|
||||
public function testSettingStringUrlResultsInArrayBasedQuery()
|
||||
{
|
||||
$url = new Url('//foo.com?bar=a&baz=b');
|
||||
$this->assertEquals(['bar' => 'a', 'baz' => 'b'], $url->getQuery());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testExceptionIsThrownWhenPopulatingWithInvalidDataType()
|
||||
{
|
||||
$value = (object) ['path' => 'https', 'host' => 'openstack.org'];
|
||||
new Url($value);
|
||||
}
|
||||
|
||||
public function testSettingQueryWithString()
|
||||
{
|
||||
$this->url->setQuery('foo=bar&baz=boo');
|
||||
$this->assertEquals(['foo' => 'bar', 'baz' => 'boo'], $this->url->getQuery());
|
||||
}
|
||||
|
||||
public function testSettingQueryWithStringArray()
|
||||
{
|
||||
$this->url->setQuery('foo[]=bar&foo[]=baz');
|
||||
$this->assertEquals(['foo' => ['bar', 'baz']], $this->url->getQuery());
|
||||
}
|
||||
|
||||
public function testSettingQueryWithArray()
|
||||
{
|
||||
$query = ['foo' => 'bar'];
|
||||
$this->url->setQuery($query);
|
||||
$this->assertEquals($query, $this->url->getQuery());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testExceptionIsThrownWhenSettingQueryWithInvalidDataType()
|
||||
{
|
||||
$this->url->setQuery(false);
|
||||
}
|
||||
|
||||
public function testAddPath()
|
||||
{
|
||||
$this->url->addPath('foo');
|
||||
$this->assertEquals('/community/members/foo', $this->url->getPath());
|
||||
}
|
||||
|
||||
public function testAddQuery()
|
||||
{
|
||||
$this->url->setQuery(['foo' => 'bar']);
|
||||
$this->url->addQuery(['baz' => 'boo']);
|
||||
$this->assertEquals(['foo' => 'bar', 'baz' => 'boo'], $this->url->getQuery());
|
||||
}
|
||||
|
||||
public function testCastingToString()
|
||||
{
|
||||
$this->assertEquals(self::URL_STRING, (string) $this->url);
|
||||
}
|
||||
|
||||
public function testCastingToStringForQueryArrays()
|
||||
{
|
||||
$url = new Url('http://openstack.org');
|
||||
$url->setQuery(['foo' => ['val1', 'val2'], 'bar' => 'val3']);
|
||||
|
||||
$this->assertEquals('http://openstack.org?foo[]=val1&foo[]=val2&bar=val3', (string) $url);
|
||||
}
|
||||
}
|
@ -1,439 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\Identity\v2;
|
||||
|
||||
use \OpenStack\Identity\v2\IdentityService;
|
||||
use \OpenStack\Bootstrap;
|
||||
|
||||
class IdentityServicesTest extends \OpenStack\Tests\TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$endpoint = self::conf('openstack.identity.url');
|
||||
$this->assertNotEmpty($endpoint);
|
||||
|
||||
$service = new IdentityService($endpoint, $this->getTransportClient());
|
||||
|
||||
$this->assertInstanceOf('\OpenStack\Identity\v2\IdentityService', $service);
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testConstructor
|
||||
*/
|
||||
public function testUrl()
|
||||
{
|
||||
$endpoint = self::conf('openstack.identity.url');
|
||||
$service = new IdentityService($endpoint, $this->getTransportClient());
|
||||
|
||||
// If there is a trailing / we remove that from the endpoint. Our calls add
|
||||
// the / back where appropriate.
|
||||
$this->assertStringStartsWith(rtrim($endpoint, '/'), $service->url());
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUrl
|
||||
*/
|
||||
public function testAuthenticate($service)
|
||||
{
|
||||
// Canary: Make sure all the required params are declared.
|
||||
$settings = [
|
||||
'openstack.identity.username',
|
||||
'openstack.identity.password',
|
||||
'openstack.identity.tenantId',
|
||||
];
|
||||
foreach ($settings as $setting) {
|
||||
$this->assertNotEmpty(self::conf($setting), "Required param: " . $setting);
|
||||
}
|
||||
|
||||
// Test username/password auth.
|
||||
$auth = [
|
||||
'passwordCredentials' => [
|
||||
'username' => self::conf('openstack.identity.username'),
|
||||
'password' => self::conf('openstack.identity.password'),
|
||||
],
|
||||
'tenantId' => self::conf('openstack.identity.tenantId'),
|
||||
];
|
||||
$tok = $service->authenticate($auth);
|
||||
$this->assertNotEmpty($tok);
|
||||
|
||||
// Again with no tenant ID.
|
||||
$auth = [
|
||||
'passwordCredentials' => [
|
||||
'username' => self::conf('openstack.identity.username'),
|
||||
'password' => self::conf('openstack.identity.password'),
|
||||
],
|
||||
//'tenantId' => self::conf('openstack.identity.tenantId'),
|
||||
];
|
||||
$tok = $service->authenticate($auth);
|
||||
$this->assertNotEmpty($tok);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticate
|
||||
*/
|
||||
public function testAuthenticateAsUser()
|
||||
{
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
|
||||
$user = self::conf('openstack.identity.username');
|
||||
$pass = self::conf('openstack.identity.password');
|
||||
$tenantId = self::conf('openstack.identity.tenantId');
|
||||
|
||||
$tok = $service->authenticateAsUser($user, $pass, $tenantId);
|
||||
$this->assertNotEmpty($tok);
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
public function testAuthenticatingAsUserWithoutTenant()
|
||||
{
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
|
||||
$username = self::conf('openstack.identity.username');
|
||||
$password = self::conf('openstack.identity.password');
|
||||
|
||||
$this->assertNotEmpty($service->authenticateAsUser($username, $password));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticateAsUser
|
||||
*/
|
||||
public function testToken($service)
|
||||
{
|
||||
$this->assertNotEmpty($service->token());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticateAsUser
|
||||
*/
|
||||
public function testIsExpired($service)
|
||||
{
|
||||
$this->assertFalse($service->isExpired());
|
||||
|
||||
$service2 = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$this->assertTrue($service2->isExpired());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticateAsUser
|
||||
*/
|
||||
public function testTenantName()
|
||||
{
|
||||
$user = self::conf('openstack.identity.username');
|
||||
$pass = self::conf('openstack.identity.password');
|
||||
$tenantName = self::conf('openstack.identity.tenantName');
|
||||
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$this->assertNull($service->tenantName());
|
||||
|
||||
$service->authenticateAsUser($user, $pass);
|
||||
$this->assertEmpty($service->tenantName());
|
||||
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$ret = $service->authenticateAsUser($user, $pass, null, $tenantName);
|
||||
$this->assertNotEmpty($service->tenantName());
|
||||
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$this->assertNull($service->tenantName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticateAsUser
|
||||
*/
|
||||
public function testTenantId()
|
||||
{
|
||||
$user = self::conf('openstack.identity.username');
|
||||
$pass = self::conf('openstack.identity.password');
|
||||
$tenantId = self::conf('openstack.identity.tenantId');
|
||||
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$this->assertNull($service->tenantId());
|
||||
|
||||
$service->authenticateAsUser($user, $pass);
|
||||
$this->assertEmpty($service->tenantId());
|
||||
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$service->authenticateAsUser($user, $pass, $tenantId);
|
||||
$this->assertNotEmpty($service->tenantId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticateAsUser
|
||||
*/
|
||||
public function testTokenDetails()
|
||||
{
|
||||
$now = time();
|
||||
$user = self::conf('openstack.identity.username');
|
||||
$pass = self::conf('openstack.identity.password');
|
||||
$tenantId = self::conf('openstack.identity.tenantId');
|
||||
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$service->authenticateAsUser($user, $pass);
|
||||
|
||||
// Details for user auth.
|
||||
$details = $service->tokenDetails();
|
||||
$this->assertNotEmpty($details['id']);
|
||||
$this->assertFalse(isset($details['tenant']));
|
||||
|
||||
$ts = strtotime($details['expires']);
|
||||
$this->assertGreaterThan($now, $ts);
|
||||
|
||||
// Test details for username auth.
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$service->authenticateAsUser($user, $pass, $tenantId);
|
||||
|
||||
$details = $service->tokenDetails();
|
||||
|
||||
$expectUser = self::conf('openstack.identity.username');
|
||||
|
||||
$this->assertStringStartsWith($expectUser, $details['tenant']['name']);
|
||||
$this->assertNotEmpty($details['id']);
|
||||
$this->assertNotEmpty($details['tenant']['id']);
|
||||
|
||||
$this->assertEquals($tenantId, $details['tenant']['id']);
|
||||
|
||||
$ts = strtotime($details['expires']);
|
||||
$this->assertGreaterThan($now, $ts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticateAsUser
|
||||
*/
|
||||
public function testServiceCatalog($service)
|
||||
{
|
||||
$catalog = $service->serviceCatalog();
|
||||
|
||||
$this->assertGreaterThan(0, count($catalog));
|
||||
|
||||
$idService = null;
|
||||
foreach ($catalog as $item) {
|
||||
if ($item['type'] == 'identity') {
|
||||
$idService = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertNotEmpty($idService['endpoints']);
|
||||
$this->assertNotEmpty($idService['endpoints'][0]['publicURL']);
|
||||
|
||||
// Test filters.
|
||||
$justID = $service->serviceCatalog('identity');
|
||||
$this->assertEquals(1, count($justID));
|
||||
|
||||
$idService = $justID[0];
|
||||
$this->assertNotEmpty($idService['endpoints']);
|
||||
$this->assertNotEmpty($idService['endpoints'][0]['publicURL']);
|
||||
|
||||
// Make sure a missed filter returns an empty set.
|
||||
$expectEmpty = $service->serviceCatalog('no-such-servicename');
|
||||
$this->assertEmpty($expectEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticateAsUser
|
||||
*/
|
||||
public function testUser($service)
|
||||
{
|
||||
$user = $service->user();
|
||||
|
||||
$this->assertEquals(self::conf('openstack.identity.username'), $user['name']);
|
||||
$this->assertNotEmpty($user['roles']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testAuthenticateAsUser
|
||||
* @group serialize
|
||||
*/
|
||||
public function testSerialization($service)
|
||||
{
|
||||
$ser = serialize($service);
|
||||
|
||||
$this->assertNotEmpty($ser);
|
||||
|
||||
$again = unserialize($ser);
|
||||
|
||||
$this->assertInstanceOf('\OpenStack\Identity\v2\IdentityService', $again);
|
||||
|
||||
$this->assertEquals($service->tenantId(), $again->tenantId());
|
||||
$this->assertEquals($service->serviceCatalog(), $again->serviceCatalog());
|
||||
$this->assertEquals($service->tokenDetails(), $again->tokenDetails());
|
||||
$this->assertEquals($service->user(), $again->user());
|
||||
$this->assertFalse($again->isExpired());
|
||||
|
||||
$tenantId = $again->tenantId();
|
||||
|
||||
$newTok = $again->rescopeUsingTenantId($tenantId);
|
||||
|
||||
$this->assertNotEmpty($newTok);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group tenant
|
||||
*/
|
||||
public function testTenants()
|
||||
{
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$service2 = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$user = self::conf('openstack.identity.username');
|
||||
$pass = self::conf('openstack.identity.password');
|
||||
$tenantId = self::conf('openstack.identity.tenantId');
|
||||
$service->authenticateAsUser($user, $pass, $tenantId);
|
||||
|
||||
$tenants = $service2->tenants($service->token());
|
||||
|
||||
$this->assertGreaterThan(0, count($tenants));
|
||||
$this->assertNotEmpty($tenants[0]['name']);
|
||||
$this->assertNotEmpty($tenants[0]['id']);
|
||||
|
||||
$tenants = $service->tenants();
|
||||
$this->assertGreaterThan(0, count($tenants));
|
||||
$this->assertNotEmpty($tenants[0]['name']);
|
||||
$this->assertNotEmpty($tenants[0]['id']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @group tenant
|
||||
* @depends testTenants
|
||||
*/
|
||||
public function testRescopeUsingTenantId()
|
||||
{
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$user = self::conf('openstack.identity.username');
|
||||
$pass = self::conf('openstack.identity.password');
|
||||
$tenantId = self::conf('openstack.identity.tenantId');
|
||||
|
||||
// Authenticate without a tenant ID.
|
||||
$token = $service->authenticateAsUser($user, $pass);
|
||||
|
||||
$this->assertNotEmpty($token);
|
||||
|
||||
$details = $service->tokenDetails();
|
||||
$this->assertFalse(isset($details['tenant']));
|
||||
|
||||
$service->rescopeUsingTenantId($tenantId);
|
||||
|
||||
$details = $service->tokenDetails();
|
||||
$this->assertEquals($tenantId, $details['tenant']['id']);
|
||||
|
||||
// Test unscoping
|
||||
$service->rescopeUsingTenantId('');
|
||||
$details = $service->tokenDetails();
|
||||
$this->assertFalse(isset($details['tenant']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group tenant
|
||||
* @depends testTenants
|
||||
*/
|
||||
public function testRescopeByTenantName()
|
||||
{
|
||||
$service = new IdentityService(self::conf('openstack.identity.url'), $this->getTransportClient());
|
||||
$user = self::conf('openstack.identity.username');
|
||||
$pass = self::conf('openstack.identity.password');
|
||||
$tenantName = self::conf('openstack.identity.tenantName');
|
||||
|
||||
// Authenticate without a tenant ID.
|
||||
$token = $service->authenticateAsUser($user, $pass);
|
||||
|
||||
$this->assertNotEmpty($token);
|
||||
|
||||
$details = $service->tokenDetails();
|
||||
$this->assertFalse(isset($details['tenant']));
|
||||
|
||||
$service->rescopeUsingTenantName($tenantName);
|
||||
|
||||
$details = $service->tokenDetails();
|
||||
$this->assertEquals($tenantName, $details['tenant']['name']);
|
||||
|
||||
// Test unscoping
|
||||
$service->rescopeUsingTenantName('');
|
||||
$details = $service->tokenDetails();
|
||||
$this->assertFalse(isset($details['tenant']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the bootstrap identity factory.
|
||||
* @depends testAuthenticateAsUser
|
||||
*/
|
||||
public function testBootstrap()
|
||||
{
|
||||
// We need to save the config settings and reset the bootstrap to this.
|
||||
// It does not remove the old settings. The means the identity fall through
|
||||
// for different settings may not happen because of ordering. So, we cache
|
||||
// and reset back to the default for each test.
|
||||
$reset = Bootstrap::$config;
|
||||
|
||||
// Test authenticating as a user.
|
||||
$settings = [
|
||||
'username' => self::conf('openstack.identity.username'),
|
||||
'password' => self::conf('openstack.identity.password'),
|
||||
'endpoint' => self::conf('openstack.identity.url'),
|
||||
'tenantid' => self::conf('openstack.identity.tenantId'),
|
||||
'transport' => self::conf('transport'),
|
||||
'transport.debug' => self::conf('transport.debug', false),
|
||||
'transport.ssl_verify' => self::conf('transport.ssl', true),
|
||||
];
|
||||
if (self::conf('transport.timeout')) {
|
||||
$setting['transport.timeout'] = self::conf('transport.timeout');
|
||||
}
|
||||
if (self::conf('transport.proxy')) {
|
||||
$setting['transport.proxy'] = self::conf('transport.proxy');
|
||||
}
|
||||
Bootstrap::setConfiguration($settings);
|
||||
|
||||
$is = Bootstrap::identity(true);
|
||||
$this->assertInstanceOf('\OpenStack\Identity\v2\IdentityService', $is);
|
||||
|
||||
// Test getting a second instance from the cache.
|
||||
$is2 = Bootstrap::identity();
|
||||
$this->assertEquals($is, $is2);
|
||||
|
||||
// Test that forcing a refresh does so.
|
||||
$is2 = Bootstrap::identity(true);
|
||||
$this->assertNotEquals($is, $is2);
|
||||
|
||||
Bootstrap::$config = $reset;
|
||||
|
||||
// Test with tenant name
|
||||
$settings = [
|
||||
'username' => self::conf('openstack.identity.username'),
|
||||
'password' => self::conf('openstack.identity.password'),
|
||||
'endpoint' => self::conf('openstack.identity.url'),
|
||||
'tenantname' => self::conf('openstack.identity.tenantName'),
|
||||
'transport' => self::conf('transport'),
|
||||
'transport.debug' => self::conf('transport.debug', false),
|
||||
'transport.ssl_verify' => self::conf('transport.ssl', true),
|
||||
];
|
||||
if (self::conf('transport.timeout')) {
|
||||
$setting['transport.timeout'] = self::conf('transport.timeout');
|
||||
}
|
||||
if (self::conf('transport.proxy')) {
|
||||
$setting['transport.proxy'] = self::conf('transport.proxy');
|
||||
}
|
||||
Bootstrap::setConfiguration($settings);
|
||||
|
||||
$is = Bootstrap::identity(true);
|
||||
$this->assertInstanceOf('\OpenStack\Identity\v2\IdentityService', $is);
|
||||
}
|
||||
}
|
@ -1,287 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\ObjectStore\v1\Resource;
|
||||
|
||||
use \OpenStack\ObjectStore\v1\Resource\Object;
|
||||
use \OpenStack\ObjectStore\v1\Resource\ACL;
|
||||
|
||||
class ObjectStorageTest extends \OpenStack\Tests\TestCase
|
||||
{
|
||||
|
||||
public function testSettings()
|
||||
{
|
||||
$this->assertTrue(!empty(self::$settings));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group auth
|
||||
*/
|
||||
public function testConstructor()
|
||||
{
|
||||
$ident = $this->identity();
|
||||
|
||||
$services = $ident->serviceCatalog(\OpenStack\ObjectStore\v1\ObjectStorage::SERVICE_TYPE);
|
||||
|
||||
if (empty($services)) {
|
||||
throw new \Exception('No object-store service found.');
|
||||
}
|
||||
|
||||
//$serviceURL = $services[0]['endpoints'][0]['adminURL'];
|
||||
$serviceURL = $services[0]['endpoints'][0]['publicURL'];
|
||||
|
||||
$ostore = new \OpenStack\ObjectStore\v1\ObjectStorage($ident->token(), $serviceURL, $this->getTransportClient());
|
||||
|
||||
$this->assertInstanceOf('\OpenStack\ObjectStore\v1\ObjectStorage', $ostore);
|
||||
$this->assertTrue(strlen($ostore->token()) > 0);
|
||||
|
||||
}
|
||||
|
||||
public function testNewFromServiceCatalog()
|
||||
{
|
||||
$ident = $this->identity();
|
||||
$tok = $ident->token();
|
||||
$cat = $ident->serviceCatalog();
|
||||
$region = self::$settings['openstack.swift.region'];
|
||||
$client = $this->getTransportClient();
|
||||
$ostore = \OpenStack\ObjectStore\v1\ObjectStorage::newFromServiceCatalog($cat, $tok, $region, $client);
|
||||
$this->assertInstanceOf('\OpenStack\ObjectStore\v1\ObjectStorage', $ostore);
|
||||
$this->assertTrue(strlen($ostore->token()) > 0);
|
||||
}
|
||||
|
||||
public function testFailedNewFromServiceCatalog()
|
||||
{
|
||||
$ident = $this->identity();
|
||||
$tok = $ident->token();
|
||||
$cat = $ident->serviceCatalog();
|
||||
$client = $this->getTransportClient();
|
||||
$ostore = \OpenStack\ObjectStore\v1\ObjectStorage::newFromServiceCatalog($cat, $tok, 'region-w.geo-99999.fake');
|
||||
$this->assertEmpty($ostore);
|
||||
}
|
||||
|
||||
public function testNewFromIdentity()
|
||||
{
|
||||
$ident = $this->identity();
|
||||
$region = self::$settings['openstack.swift.region'];
|
||||
$client = $this->getTransportClient();
|
||||
$ostore = \OpenStack\ObjectStore\v1\ObjectStorage::newFromIdentity($ident, $region, $client);
|
||||
$this->assertInstanceOf('\OpenStack\ObjectStore\v1\ObjectStorage', $ostore);
|
||||
$this->assertTrue(strlen($ostore->token()) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group auth
|
||||
* @group acl
|
||||
*/
|
||||
public function testCreateContainer()
|
||||
{
|
||||
$testCollection = self::$settings['openstack.swift.container'];
|
||||
|
||||
$this->assertNotEmpty($testCollection, "Canary: container name must be in settings file.");
|
||||
|
||||
$store = $this->objectStore();
|
||||
|
||||
$this->destroyContainerFixture();
|
||||
/*
|
||||
if ($store->hasContainer($testCollection)) {
|
||||
$store->deleteContainer($testCollection);
|
||||
}
|
||||
*/
|
||||
|
||||
$md = ['Foo' => 1234];
|
||||
|
||||
$ret = $store->createContainer($testCollection, null, $md);
|
||||
$this->assertTrue($ret, "Create container");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @group auth
|
||||
* @depends testCreateContainer
|
||||
*/
|
||||
public function testAccountInfo()
|
||||
{
|
||||
$store = $this->objectStore();
|
||||
|
||||
$info = $store->accountInfo();
|
||||
|
||||
$this->assertGreaterThan(0, $info['containers']);
|
||||
$this->assertGreaterThanOrEqual(0, $info['bytes']);
|
||||
$this->assertGreaterThanOrEqual(0, $info['objects']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateContainer
|
||||
*/
|
||||
public function testContainers()
|
||||
{
|
||||
$store = $this->objectStore();
|
||||
$containers = $store->containers();
|
||||
|
||||
$this->assertNotEmpty($containers);
|
||||
|
||||
//$first = array_shift($containers);
|
||||
|
||||
$testCollection = self::conf('openstack.swift.container');
|
||||
$testContainer = $containers[$testCollection];
|
||||
$this->assertEquals($testCollection, $testContainer->name());
|
||||
$this->assertEquals(0, $testContainer->bytes());
|
||||
$this->assertEquals(0, $testContainer->count());
|
||||
|
||||
// Make sure we get back an ACL:
|
||||
$this->assertInstanceOf('\OpenStack\ObjectStore\v1\Resource\ACL', $testContainer->acl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateContainer
|
||||
*/
|
||||
public function testContainer()
|
||||
{
|
||||
$testCollection = self::$settings['openstack.swift.container'];
|
||||
$store = $this->objectStore();
|
||||
|
||||
$container = $store->container($testCollection);
|
||||
|
||||
$this->assertEquals(0, $container->bytes());
|
||||
$this->assertEquals(0, $container->count());
|
||||
$this->assertEquals($testCollection, $container->name());
|
||||
|
||||
$md = $container->metadata();
|
||||
$this->assertEquals(1, count($md));
|
||||
$this->assertEquals('1234', $md['Foo']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateContainer
|
||||
*/
|
||||
public function testHasContainer()
|
||||
{
|
||||
$testCollection = self::$settings['openstack.swift.container'];
|
||||
$store = $this->objectStore();
|
||||
|
||||
$this->assertTrue($store->hasContainer($testCollection));
|
||||
$this->assertFalse($store->hasContainer('nihil'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testHasContainer
|
||||
*/
|
||||
public function testDeleteContainer()
|
||||
{
|
||||
$testCollection = self::$settings['openstack.swift.container'];
|
||||
|
||||
$store = $this->objectStore();
|
||||
//$ret = $store->createContainer($testCollection);
|
||||
//$this->assertTrue($store->hasContainer($testCollection));
|
||||
|
||||
$ret = $store->deleteContainer($testCollection);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
|
||||
// Now we try to delete a container that does not exist.
|
||||
$ret = $store->deleteContainer('nihil');
|
||||
$this->assertFalse($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\ObjectStore\v1\Exception\ContainerNotEmptyException
|
||||
*/
|
||||
public function testDeleteNonEmptyContainer()
|
||||
{
|
||||
$testCollection = self::$settings['openstack.swift.container'];
|
||||
|
||||
$this->assertNotEmpty($testCollection);
|
||||
|
||||
$store = $this->objectStore();
|
||||
$store->createContainer($testCollection);
|
||||
|
||||
$container = $store->container($testCollection);
|
||||
$container->save(new Object('test', 'test', 'text/plain'));
|
||||
|
||||
try {
|
||||
$ret = $store->deleteContainer($testCollection);
|
||||
} catch (\Exception $e) {
|
||||
$container->delete('test');
|
||||
$store->deleteContainer($testCollection);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
$container->delete('test');
|
||||
}
|
||||
// Skip 404s.
|
||||
catch (\Exception $e) {}
|
||||
|
||||
$store->deleteContainer($testCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateContainer
|
||||
* @group acl
|
||||
*/
|
||||
public function testCreateContainerPublic()
|
||||
{
|
||||
$testCollection = self::$settings['openstack.swift.container'] . 'PUBLIC';
|
||||
$store = $this->objectStore();
|
||||
if ($store->hasContainer($testCollection)) {
|
||||
$store->deleteContainer($testCollection);
|
||||
}
|
||||
|
||||
$ret = $store->createContainer($testCollection, ACL::makePublic());
|
||||
$container = $store->container($testCollection);
|
||||
|
||||
// Now test that we can get the container contents. Since there is
|
||||
// no content in the container, we use the format=xml to make sure
|
||||
// we get some data back.
|
||||
$url = $container->url() . '?format=xml';
|
||||
|
||||
$data = file_get_contents($url);
|
||||
$this->assertNotEmpty($data, $url);
|
||||
|
||||
$containers = $store->containers();
|
||||
|
||||
$store->deleteContainer($testCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateContainerPublic
|
||||
*/
|
||||
public function testChangeContainerACL()
|
||||
{
|
||||
$testCollection = self::$settings['openstack.swift.container'] . 'PUBLIC';
|
||||
$store = $this->objectStore();
|
||||
if ($store->hasContainer($testCollection)) {
|
||||
$store->deleteContainer($testCollection);
|
||||
}
|
||||
$ret = $store->createContainer($testCollection);
|
||||
|
||||
$acl = ACL::makePublic();
|
||||
$ret = $store->changeContainerACL($testCollection, $acl);
|
||||
|
||||
$this->assertFalse($ret);
|
||||
|
||||
$container = $store->container($testCollection);
|
||||
$url = $container->url() . '?format=xml';
|
||||
|
||||
$data = file_get_contents($url);
|
||||
$this->assertNotEmpty($data, $url);
|
||||
|
||||
$store->deleteContainer($testCollection);
|
||||
}
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\ObjectStore\v1\Resource;
|
||||
|
||||
use \OpenStack\ObjectStore\v1\Resource\ACL;
|
||||
/**
|
||||
* @ingroup Tests
|
||||
*/
|
||||
class ACLTest extends \OpenStack\Tests\TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$acl = new ACL();
|
||||
$this->assertEmpty($acl->rules());
|
||||
|
||||
}
|
||||
|
||||
public function testAddAccount()
|
||||
{
|
||||
$acl = new ACL();
|
||||
|
||||
$acl->addAccount(ACL::READ, 'test');
|
||||
|
||||
$rules = $acl->rules();
|
||||
|
||||
$this->assertEquals(1, count($rules));
|
||||
|
||||
$rule = array_shift($rules);
|
||||
|
||||
$this->assertEquals(ACL::READ, $rule['mask']);
|
||||
$this->assertEquals('test', $rule['account']);
|
||||
|
||||
// Test with user
|
||||
$acl = new ACL();
|
||||
$acl->addAccount(ACL::WRITE, 'admin', 'earnie');
|
||||
$rules = $acl->rules();
|
||||
$rule = array_shift($rules);
|
||||
|
||||
$this->assertEquals(ACL::WRITE, $rule['mask']);
|
||||
$this->assertEquals('admin', $rule['account']);
|
||||
$this->assertEquals('earnie', $rule['user']);
|
||||
|
||||
// Test with multiple users:
|
||||
$acl = new ACL();
|
||||
$acl->addAccount(ACL::WRITE, 'admin', ['earnie', 'bert']);
|
||||
$rules = $acl->rules();
|
||||
$rule = array_shift($rules);
|
||||
|
||||
$this->assertEquals(ACL::WRITE, $rule['mask']);
|
||||
$this->assertEquals('admin', $rule['account']);
|
||||
$this->assertEquals('earnie', $rule['user'][0]);
|
||||
$this->assertEquals('bert', $rule['user'][1]);
|
||||
|
||||
}
|
||||
|
||||
public function testAddReferrer()
|
||||
{
|
||||
$acl = new ACL();
|
||||
$acl->addReferrer(ACL::READ, '.example.com');
|
||||
$acl->addReferrer(ACL::READ_WRITE, '-bad.example.com');
|
||||
|
||||
$rules = $acl->rules();
|
||||
|
||||
$this->assertEquals(2, count($rules));
|
||||
|
||||
$first = array_shift($rules);
|
||||
$this->assertEquals(ACL::READ, $first['mask']);
|
||||
$this->assertEquals('.example.com', $first['host']);
|
||||
}
|
||||
|
||||
public function testAllowListings()
|
||||
{
|
||||
$acl = new ACL();
|
||||
$acl->allowListings();
|
||||
$rules = $acl->rules();
|
||||
|
||||
$this->assertEquals(1, count($rules));
|
||||
$this->assertTrue($rules[0]['rlistings']);
|
||||
$this->assertEquals(ACL::READ, $rules[0]['mask']);
|
||||
}
|
||||
|
||||
public function testHeaders()
|
||||
{
|
||||
$acl = new ACL();
|
||||
$acl->addAccount(ACL::READ_WRITE, 'test');
|
||||
|
||||
$headers = $acl->headers();
|
||||
|
||||
$this->assertEquals(2, count($headers));
|
||||
$read = $headers[ACL::HEADER_READ];
|
||||
$write = $headers[ACL::HEADER_WRITE];
|
||||
|
||||
$this->assertEquals('test', $read);
|
||||
$this->assertEquals('test', $write);
|
||||
|
||||
// Test hostname rules, which should only appear in READ.
|
||||
$acl = new ACL();
|
||||
$acl->addReferrer(ACL::READ_WRITE, '.example.com');
|
||||
$headers = $acl->headers();
|
||||
|
||||
$this->assertEquals(1, count($headers), print_r($headers, true));
|
||||
$read = $headers[ACL::HEADER_READ];
|
||||
|
||||
$this->assertEquals('.r:.example.com', $read);
|
||||
}
|
||||
|
||||
public function testToString()
|
||||
{
|
||||
$acl = new ACL();
|
||||
$acl->addReferrer(ACL::READ_WRITE, '.example.com');
|
||||
|
||||
$str = (string) $acl;
|
||||
|
||||
$this->assertEquals('X-Container-Read: .r:.example.com', $str);
|
||||
}
|
||||
|
||||
public function testMakePublic()
|
||||
{
|
||||
$acl = (string) ACL::makePublic();
|
||||
|
||||
$this->assertEquals('X-Container-Read: .r:*,.rlistings', $acl);
|
||||
}
|
||||
|
||||
public function testMakeNonPublic()
|
||||
{
|
||||
$acl = (string) ACL::makeNonPublic();
|
||||
|
||||
$this->assertEmpty($acl);
|
||||
}
|
||||
|
||||
public function testNewFromHeaders()
|
||||
{
|
||||
$headers = [
|
||||
ACL::HEADER_READ => '.r:.example.com,.rlistings,.r:-*.evil.net',
|
||||
ACL::HEADER_WRITE => 'testact2, testact3:earnie, .rlistings ',
|
||||
];
|
||||
|
||||
$acl = ACL::newFromHeaders($headers);
|
||||
|
||||
$rules = $acl->rules();
|
||||
|
||||
$this->assertEquals(6, count($rules));
|
||||
|
||||
// Yay, now we get to test each one.
|
||||
|
||||
$this->assertEquals(ACL::READ, $rules[0]['mask']);
|
||||
$this->assertEquals('.example.com', $rules[0]['host']);
|
||||
$this->assertTrue($rules[1]['rlistings']);
|
||||
$this->assertEquals('-*.evil.net', $rules[2]['host']);
|
||||
|
||||
$this->assertEquals(ACL::WRITE, $rules[3]['mask']);
|
||||
$this->assertEquals('testact2', $rules[3]['account']);
|
||||
$this->assertEquals('testact3', $rules[4]['account']);
|
||||
$this->assertEquals('earnie', $rules[4]['user']);
|
||||
$this->assertTrue($rules[5]['rlistings']);
|
||||
|
||||
// Final canary:
|
||||
$headers = $acl->headers();
|
||||
$read = $headers[ACL::HEADER_READ];
|
||||
$write = $headers[ACL::HEADER_WRITE];
|
||||
|
||||
$this->assertEquals('.r:.example.com,.rlistings,.r:-*.evil.net', $read);
|
||||
// Note that the spurious .rlistings was removed.
|
||||
$this->assertEquals('testact2,testact3:earnie', $write);
|
||||
|
||||
}
|
||||
|
||||
public function testIsNonPublic()
|
||||
{
|
||||
$acl = new ACL();
|
||||
|
||||
$this->assertTrue($acl->isNonPublic());
|
||||
|
||||
$acl->addReferrer(ACL::READ, '*.evil.net');
|
||||
$this->assertFalse($acl->isNonPublic());
|
||||
|
||||
$acl = ACL::makeNonPublic();
|
||||
$this->assertTrue($acl->isNonPublic());
|
||||
}
|
||||
|
||||
public function testIsPublic()
|
||||
{
|
||||
$acl = new ACL();
|
||||
|
||||
$this->assertFalse($acl->isPublic());
|
||||
$acl->allowListings();
|
||||
$acl->addReferrer(ACL::READ, '*');
|
||||
|
||||
$this->assertTrue($acl->isPublic());
|
||||
|
||||
$acl->addAccount(ACL::WRITE, 'foo', 'bar');
|
||||
$this->assertTrue($acl->isPublic());
|
||||
|
||||
$acl = ACL::makePublic();
|
||||
$this->assertTrue($acl->isPublic());
|
||||
}
|
||||
|
||||
}
|
@ -1,393 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\ObjectStore\v1\Resource;
|
||||
|
||||
use OpenStack\Bootstrap;
|
||||
use \OpenStack\ObjectStore\v1\Resource\Container;
|
||||
use \OpenStack\ObjectStore\v1\Resource\Object;
|
||||
use \OpenStack\ObjectStore\v1\Resource\ACL;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
class ContainerTest extends TestCase
|
||||
{
|
||||
const FILENAME = 'unit-test-dummy.txt';
|
||||
const FILESTR = 'This is a test.';
|
||||
const FNAME = 'testSave';
|
||||
const FCONTENT = 'This is a test.';
|
||||
const FTYPE = 'application/x-monkey-file';
|
||||
|
||||
public function testConstructorSetsName()
|
||||
{
|
||||
$container = new Container('foo');
|
||||
$this->assertEquals('foo', $container->name());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OpenStack\Common\Exception
|
||||
*/
|
||||
public function testExceptionIsThrownWhenContainerNotFound()
|
||||
{
|
||||
$container = new Container('foo');
|
||||
$container->bytes();
|
||||
}
|
||||
|
||||
public function testCountable()
|
||||
{
|
||||
// Verify that the interface Countable is properly implemented.
|
||||
|
||||
$mockJSON = ['count' => 5, 'bytes' => 128, 'name' => 'foo'];
|
||||
$container = Container::newFromJSON($mockJSON, 'fake', 'fake');
|
||||
$this->assertCount(5, $container);
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
// Clean up anything left.
|
||||
$this->destroyContainerFixture();
|
||||
|
||||
$container = $this->containerFixture();
|
||||
|
||||
$object = new Object(self::FNAME, self::FCONTENT, self::FTYPE);
|
||||
$object->setMetadata(['foo' => '1234']);
|
||||
|
||||
$this->assertEquals(self::FCONTENT, $object->content());
|
||||
|
||||
try {
|
||||
$ret = $container->save($object);
|
||||
} catch (\Exception $e) {
|
||||
$this->destroyContainerFixture();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->assertTrue($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSave
|
||||
*/
|
||||
public function testProxyObject()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$object = $container->proxyObject(self::FNAME);
|
||||
|
||||
$this->assertEquals(self::FNAME, $object->name());
|
||||
$this->assertEquals(self::FTYPE, $object->contentType());
|
||||
|
||||
$etag = md5(self::FCONTENT);
|
||||
$this->assertEquals($etag, $object->eTag());
|
||||
|
||||
$md = $object->metadata();
|
||||
$this->assertEquals(1, count($md));
|
||||
|
||||
// Note that headers are normalized remotely to have initial
|
||||
// caps. Since we have no way of knowing what the original
|
||||
// metadata casing is, we leave it with initial caps.
|
||||
$this->assertEquals('1234', $md['Foo']);
|
||||
|
||||
$content = $object->content();
|
||||
$this->assertEquals(self::FCONTENT, $content);
|
||||
|
||||
// Make sure I can do this twice (regression).
|
||||
// Note that this SHOULD perform another request.
|
||||
$this->assertEquals(self::FCONTENT, $object->content());
|
||||
|
||||
// Overwrite the copy:
|
||||
$object->setContent('HI');
|
||||
$this->assertEquals('HI', $object->content());
|
||||
|
||||
// Make sure I can do this twice (regression check).
|
||||
$this->assertEquals('HI', $object->content());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testProxyObject
|
||||
*/
|
||||
public function testRefresh()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$object = $container->proxyObject(self::FNAME);
|
||||
|
||||
$content = (string) $object->content();
|
||||
$object->setContent('FOO');
|
||||
$this->assertEquals('FOO', $object->content());
|
||||
|
||||
$object->refresh(true);
|
||||
$this->assertEquals($content, (string) $object->content());
|
||||
|
||||
$object->refresh(false);
|
||||
$this->assertEquals($content, (string) $object->content());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testProxyObject
|
||||
*/
|
||||
public function testObject()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$object = $container->object(self::FNAME);
|
||||
|
||||
$this->assertEquals(self::FNAME, $object->name());
|
||||
$this->assertEquals(self::FTYPE, $object->contentType());
|
||||
|
||||
$etag = md5(self::FCONTENT);
|
||||
$this->assertEquals($etag, $object->eTag());
|
||||
|
||||
$md = $object->metadata();
|
||||
$this->assertEquals(1, count($md));
|
||||
|
||||
// Note that headers are normalized remotely to have initial
|
||||
// caps. Since we have no way of knowing what the original
|
||||
// metadata casing is, we leave it with initial caps.
|
||||
$this->assertEquals('1234', $md['Foo']);
|
||||
|
||||
$content = $object->content();
|
||||
|
||||
$this->assertEquals(self::FCONTENT, $content);
|
||||
|
||||
// Overwrite the copy:
|
||||
$object->setContent('HI');
|
||||
$this->assertEquals('HI', $object->content());
|
||||
|
||||
// Make sure this throws a 404.
|
||||
try {
|
||||
$foo = $container->object('no/such');
|
||||
} catch (\OpenStack\Common\Exception $e) {
|
||||
$this->assertInstanceOf('OpenStack\Common\Transport\Exception\ResourceNotFoundException', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSave
|
||||
*/
|
||||
public function testObjects()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$obj1 = new Object('a/' . self::FNAME, self::FCONTENT, self::FTYPE);
|
||||
$obj2 = new Object('a/b/' . self::FNAME, self::FCONTENT, self::FTYPE);
|
||||
|
||||
$container->save($obj1);
|
||||
$container->save($obj2);
|
||||
|
||||
// Now we have a container with three items.
|
||||
$objects = $container->objects();
|
||||
|
||||
$this->assertEquals(3, count($objects));
|
||||
|
||||
$objects = $container->objects(1, 'a/' . self::FNAME);
|
||||
|
||||
$this->assertEquals(1, count($objects));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testObjects
|
||||
*/
|
||||
public function testGetIterator()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
|
||||
$it = $container->getIterator();
|
||||
$this->assertInstanceOf('Traversable', $it);
|
||||
|
||||
$i = 0;
|
||||
foreach ($container as $item) {
|
||||
++$i;
|
||||
}
|
||||
$this->assertEquals(3, $i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testObjects
|
||||
*/
|
||||
public function testObjectsWithPrefix()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
|
||||
$objects = $container->objectsWithPrefix('a/');
|
||||
$this->assertEquals(2, count($objects));
|
||||
|
||||
foreach ($objects as $o) {
|
||||
if ($o instanceof Object) {
|
||||
$this->assertEquals('a/' . self::FNAME, $o->name());
|
||||
} else {
|
||||
$this->assertEquals('a/b/', $o->path());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Since we set the delimiter to ':' we will get back
|
||||
// all of the objects in a/. This is because none of
|
||||
// the objects contain ':' in their names.
|
||||
$objects = $container->objectsWithPrefix('a/', ':');
|
||||
$this->assertEquals(2, count($objects));
|
||||
|
||||
foreach ($objects as $o) {
|
||||
$this->assertInstanceOf('\OpenStack\ObjectStore\v1\Resource\Object', $o);
|
||||
}
|
||||
|
||||
// This should give us one file and one subdir.
|
||||
$objects = $container->objectsWithPrefix('', '/');
|
||||
$this->assertEquals(2, count($objects));
|
||||
|
||||
foreach ($objects as $o) {
|
||||
if ($o instanceof Object) {
|
||||
$this->assertEquals(self::FNAME, $o->name());
|
||||
} else {
|
||||
$this->assertEquals('a/', $o->path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testObjects
|
||||
*/
|
||||
public function testObjectsWithPath()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$objects = $container->objectsByPath('a/b/');
|
||||
|
||||
$this->assertEquals(1, count($objects));
|
||||
|
||||
$o = array_shift($objects);
|
||||
$this->assertEquals('a/b/' . self::FNAME, $o->name());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testProxyObject
|
||||
*/
|
||||
public function testUpdateMetadata()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$object = $container->proxyObject(self::FNAME);
|
||||
|
||||
$md = $object->metadata();
|
||||
|
||||
$this->assertEquals('1234', $md['Foo']);
|
||||
|
||||
$md['Foo'] = 456;
|
||||
$md['Bar'] = 'bert';
|
||||
$object->setMetadata($md);
|
||||
|
||||
$container->updateMetadata($object);
|
||||
|
||||
$copy = $container->proxyObject(self::FNAME);
|
||||
|
||||
$this->assertEquals('456', $md['Foo']);
|
||||
$this->assertEquals('bert', $md['Bar']);
|
||||
|
||||
// Now we need to canary test:
|
||||
$this->assertEquals($object->contentType(), $copy->contentType());
|
||||
$this->assertEquals($object->contentLength(), $copy->contentLength());
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testProxyObject
|
||||
*/
|
||||
public function testCopy()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$object = $container->proxyObject(self::FNAME);
|
||||
|
||||
$container->copy($object, 'FOO-1.txt');
|
||||
|
||||
$copy = $container->proxyObject('FOO-1.txt');
|
||||
|
||||
$this->assertEquals($object->contentType(), $copy->contentType());
|
||||
$this->assertEquals($object->etag(), $copy->etag());
|
||||
|
||||
$container->delete('foo-1.txt');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCopy
|
||||
*/
|
||||
public function testCopyAcrossContainers()
|
||||
{
|
||||
// Create a new container.
|
||||
$store = $this->objectStore();
|
||||
$cname = self::$settings['openstack.swift.container'] . 'COPY';
|
||||
if ($store->hasContainer($cname)) {
|
||||
$this->eradicateContainer($cname);
|
||||
}
|
||||
|
||||
$store->createContainer($cname);
|
||||
$newContainer = $store->container($cname);
|
||||
|
||||
// Get teh old container and its object.
|
||||
$container = $this->containerFixture();
|
||||
$object = $container->proxyObject(self::FNAME);
|
||||
|
||||
$ret = $container->copy($object, 'foo-1.txt', $cname);
|
||||
|
||||
$this->assertTrue($ret);
|
||||
|
||||
$copy = $newContainer->proxyObject('foo-1.txt');
|
||||
|
||||
$this->assertEquals($object->etag(), $copy->etag());
|
||||
|
||||
$this->eradicateContainer($cname);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSave
|
||||
*/
|
||||
public function testDelete()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
|
||||
$ret = $container->delete(self::FNAME);
|
||||
|
||||
$fail = $container->delete('no_such_file.txt');
|
||||
|
||||
$this->destroyContainerFixture();
|
||||
$this->assertTrue($ret);
|
||||
$this->assertFalse($fail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group public
|
||||
*/
|
||||
public function testAcl()
|
||||
{
|
||||
$store = $this->objectStore();
|
||||
$cname = self::$settings['openstack.swift.container'] . 'PUBLIC';
|
||||
|
||||
if ($store->hasContainer($cname)) {
|
||||
$store->deleteContainer($cname);
|
||||
}
|
||||
|
||||
$store->createContainer($cname, ACL::makePublic());
|
||||
|
||||
$store->containers();
|
||||
$container = $store->container($cname);
|
||||
|
||||
$acl = $container->acl();
|
||||
|
||||
$this->assertInstanceOf('\OpenStack\ObjectStore\v1\Resource\ACL', $acl);
|
||||
$this->assertTrue($acl->isPublic());
|
||||
|
||||
$store->deleteContainer($cname);
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\ObjectStore\v1\Resource;
|
||||
|
||||
use \OpenStack\ObjectStore\v1\Resource\Object;
|
||||
|
||||
class ObjectTest extends \OpenStack\Tests\TestCase
|
||||
{
|
||||
const FNAME = 'descartes.txt';
|
||||
const FCONTENT = 'Cogito ergo sum.';
|
||||
const FTYPE = 'text/plain; charset=ISO-8859-1';
|
||||
|
||||
/**
|
||||
* Set up a basic object fixture.
|
||||
*
|
||||
* This provides an Object initialized with the main constants defined
|
||||
* for this class. Use this as a fixture to avoid repetition.
|
||||
*
|
||||
* @return Object An initialized object.
|
||||
*/
|
||||
public function basicObjectFixture()
|
||||
{
|
||||
$o = new Object(self::FNAME);
|
||||
$o->setContent(self::FCONTENT, self::FTYPE);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
public function testConstructor()
|
||||
{
|
||||
$o = $this->basicObjectFixture();
|
||||
|
||||
$this->assertEquals(self::FNAME, $o->name());
|
||||
|
||||
$o = new Object('a', 'b', 'text/plain');
|
||||
|
||||
$this->assertEquals('a', $o->name());
|
||||
$this->assertEquals('b', $o->content());
|
||||
$this->assertEquals('text/plain', $o->contentType());
|
||||
}
|
||||
|
||||
public function testContentType()
|
||||
{
|
||||
// Don't use the fixture, we want to test content
|
||||
// type in its raw state.
|
||||
$o = new Object('foo.txt');
|
||||
|
||||
$this->assertEquals('application/octet-stream', $o->contentType());
|
||||
|
||||
$o->setContentType('text/plain; charset=UTF-8');
|
||||
$this->assertEquals('text/plain; charset=UTF-8', $o->contentType());
|
||||
}
|
||||
|
||||
public function testContent()
|
||||
{
|
||||
$o = $this->basicObjectFixture();
|
||||
|
||||
$this->assertEquals(self::FCONTENT, $o->content());
|
||||
|
||||
// Test binary data.
|
||||
$bin = sha1(self::FCONTENT, true);
|
||||
$o->setContent($bin, 'application/octet-stream');
|
||||
|
||||
$this->assertEquals($bin, $o->content());
|
||||
}
|
||||
|
||||
public function testEtag()
|
||||
{
|
||||
$o = $this->basicObjectFixture();
|
||||
$md5 = md5(self::FCONTENT);
|
||||
|
||||
$this->assertEquals($md5, $o->eTag());
|
||||
}
|
||||
|
||||
public function testIsChunked()
|
||||
{
|
||||
$o = $this->basicObjectFixture();
|
||||
$this->assertFalse($o->isChunked());
|
||||
}
|
||||
|
||||
public function testContentLength()
|
||||
{
|
||||
$o = $this->basicObjectFixture();
|
||||
$this->assertEquals(strlen(self::FCONTENT), $o->contentLength());
|
||||
|
||||
// Test on binary data.
|
||||
$bin = sha1(self::FCONTENT, true);
|
||||
|
||||
$o->setContent($bin);
|
||||
$this->assertFalse($o->contentLength() == 0);
|
||||
$this->assertEquals(strlen($bin), $o->contentLength());
|
||||
}
|
||||
|
||||
public function testMetadata()
|
||||
{
|
||||
$md = [
|
||||
'Immanuel' => 'Kant',
|
||||
'David' => 'Hume',
|
||||
'Gottfried' => 'Leibniz',
|
||||
'Jean-Jaques' => 'Rousseau',
|
||||
];
|
||||
|
||||
$o = $this->basicObjectFixture();
|
||||
$o->setMetadata($md);
|
||||
|
||||
$got = $o->metadata();
|
||||
|
||||
$this->assertEquals(4, count($got));
|
||||
$this->assertArrayHasKey('Immanuel', $got);
|
||||
$this->assertEquals('Leibniz', $got['Gottfried']);
|
||||
|
||||
}
|
||||
|
||||
public function testAdditionalHeaders()
|
||||
{
|
||||
$o = $this->basicObjectFixture();
|
||||
|
||||
$extra = [
|
||||
'a' => 'b',
|
||||
'aaa' => 'bbb',
|
||||
'ccc' => 'bbb',
|
||||
];
|
||||
$o->setAdditionalHeaders($extra);
|
||||
|
||||
$got = $o->additionalHeaders();
|
||||
$this->assertEquals(3, count($got));
|
||||
|
||||
$o->removeHeaders(['ccc']);
|
||||
|
||||
$got = $o->additionalHeaders();
|
||||
$this->assertEquals(2, count($got));
|
||||
}
|
||||
}
|
@ -1,330 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\ObjectStore\v1\Resource;
|
||||
|
||||
use \OpenStack\ObjectStore\v1\Resource\RemoteObject;
|
||||
use \OpenStack\ObjectStore\v1\Resource\Object;
|
||||
use \OpenStack\ObjectStore\v1\Resource\Container;
|
||||
|
||||
class RemoteObjectTest extends \OpenStack\Tests\TestCase
|
||||
{
|
||||
const FNAME = 'RemoteObjectTest';
|
||||
//const FTYPE = 'text/plain; charset=UTF-8';
|
||||
const FTYPE = 'application/octet-stream; charset=UTF-8';
|
||||
const FCONTENT = 'Rah rah ah ah ah. Roma roma ma. Gaga oh la la.';
|
||||
const FMETA_NAME = 'Foo';
|
||||
const FMETA_VALUE = 'Bar';
|
||||
const FDISPOSITION = 'attachment; roma.gaga';
|
||||
const FENCODING = 'gzip';
|
||||
const FCORS_NAME = 'Access-Control-Max-Age';
|
||||
const FCORS_VALUE = '2000';
|
||||
|
||||
protected function createAnObject()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
|
||||
$object = new Object(self::FNAME, self::FCONTENT, self::FTYPE);
|
||||
$object->setMetadata([self::FMETA_NAME => self::FMETA_VALUE]);
|
||||
$object->setDisposition(self::FDISPOSITION);
|
||||
$object->setEncoding(self::FENCODING);
|
||||
$object->setAdditionalHeaders([
|
||||
'Access-Control-Allow-Origin' => 'http://example.com',
|
||||
'Access-control-allow-origin' => 'http://example.com',
|
||||
]);
|
||||
|
||||
// Need some headers that Swift actually stores and returns. This
|
||||
// one does not seem to be returned ever.
|
||||
//$object->setAdditionalHeaders(array(self::FCORS_NAME => self::FCORS_VALUE));
|
||||
|
||||
$container->save($object);
|
||||
}
|
||||
|
||||
public function testNewFromHeaders()
|
||||
{
|
||||
// This is tested via the container.
|
||||
|
||||
$this->destroyContainerFixture();
|
||||
$container = $this->containerFixture();
|
||||
$this->createAnObject();
|
||||
|
||||
$obj = $container->proxyObject(self::FNAME);
|
||||
|
||||
$this->assertInstanceOf('\OpenStack\ObjectStore\v1\Resource\RemoteObject', $obj);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
/**
|
||||
* @depends testNewFromHeaders
|
||||
*/
|
||||
public function testContentLength($obj)
|
||||
{
|
||||
$len = strlen(self::FCONTENT);
|
||||
|
||||
$this->assertEquals($len, $obj->contentLength());
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testContentLength
|
||||
*/
|
||||
public function testContentType($obj)
|
||||
{
|
||||
$this->assertEquals(self::FTYPE, $obj->contentType());
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testContentType
|
||||
*/
|
||||
public function testEtag($obj)
|
||||
{
|
||||
$hash = md5(self::FCONTENT);
|
||||
|
||||
$this->assertEquals($hash, $obj->eTag());
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testContentType
|
||||
*/
|
||||
public function testLastModified($obj)
|
||||
{
|
||||
$date = $obj->lastModified();
|
||||
|
||||
$this->assertTrue(is_int($date));
|
||||
$this->assertTrue($date > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testNewFromHeaders
|
||||
*/
|
||||
public function testMetadata($obj)
|
||||
{
|
||||
$md = $obj->metadata();
|
||||
|
||||
$this->assertArrayHasKey(self::FMETA_NAME, $md);
|
||||
$this->assertEquals(self::FMETA_VALUE, $md[self::FMETA_NAME]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testNewFromHeaders
|
||||
*/
|
||||
public function testDisposition($obj)
|
||||
{
|
||||
$this->assertEquals(self::FDISPOSITION, $obj->disposition());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testNewFromHeaders
|
||||
*/
|
||||
public function testEncoding($obj)
|
||||
{
|
||||
$this->assertEquals(self::FENCODING, $obj->encoding());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testNewFromHeaders
|
||||
*/
|
||||
public function testHeaders($obj)
|
||||
{
|
||||
$headers = $obj->headers();
|
||||
$this->assertTrue(count($headers) > 1);
|
||||
|
||||
//fwrite(STDOUT, print_r($headers, true));
|
||||
|
||||
$this->assertNotEmpty($headers['Date']);
|
||||
|
||||
$obj->removeHeaders(['Date']);
|
||||
|
||||
$headers = $obj->headers();
|
||||
$this->assertFalse(isset($headers['Date']));
|
||||
|
||||
// Swift doesn't return CORS headers even though it is supposed to.
|
||||
//$this->assertEquals(self::FCORS_VALUE, $headers[self::FCORS_NAME]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testNewFromHeaders
|
||||
*/
|
||||
public function testUrl($obj)
|
||||
{
|
||||
$url = $obj->url();
|
||||
|
||||
$this->assertTrue(strpos($obj->url(), $obj->name())> 0);
|
||||
}
|
||||
/**
|
||||
* @depends testNewFromHeaders
|
||||
*/
|
||||
public function testStream($obj)
|
||||
{
|
||||
$res = $obj->stream();
|
||||
|
||||
$this->assertTrue(is_resource($res));
|
||||
|
||||
$res_md = stream_get_meta_data($res);
|
||||
|
||||
$content = fread($res, $obj->contentLength());
|
||||
|
||||
fclose($res);
|
||||
|
||||
$this->assertEquals(self::FCONTENT, $content);
|
||||
|
||||
// Now repeat the tests, only with a local copy of the data.
|
||||
// This allows us to test the local tempfile buffering.
|
||||
|
||||
$obj->setContent($content);
|
||||
|
||||
$res2 = $obj->stream();
|
||||
$res_md = stream_get_meta_data($res2);
|
||||
|
||||
$this->assertEquals('PHP', $res_md['wrapper_type']);
|
||||
|
||||
$content = fread($res2, $obj->contentLength());
|
||||
|
||||
fclose($res2);
|
||||
|
||||
$this->assertEquals(self::FCONTENT, $content);
|
||||
|
||||
// Finally, we redo the first part of the test to make sure that
|
||||
// refreshing gets us a new copy:
|
||||
|
||||
$res3 = $obj->stream(true);
|
||||
$res_md = stream_get_meta_data($res3);
|
||||
$this->assertEquals('PHP', $res_md['wrapper_type']);
|
||||
fclose($res3);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
// To avoid test tainting from testStream(), we start over.
|
||||
public function testContent()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$obj = $container->object(self::FNAME);
|
||||
|
||||
$content = $obj->content();
|
||||
$this->assertEquals(self::FCONTENT, $content);
|
||||
|
||||
// Make sure proxyObject retrieves the same content.
|
||||
$obj = $container->proxyObject(self::FNAME);
|
||||
$content = $obj->content();
|
||||
$this->assertEquals(self::FCONTENT, $content);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testStream
|
||||
*/
|
||||
public function testCaching()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$obj = $container->proxyObject(self::FNAME);
|
||||
|
||||
$this->assertFalse($obj->isCaching());
|
||||
|
||||
$content = $obj->content();
|
||||
|
||||
$res1 = $obj->stream();
|
||||
$md = stream_get_meta_data($res1);
|
||||
$this->assertEquals('PHP', $md['wrapper_type']);
|
||||
|
||||
fclose($res1);
|
||||
|
||||
// Enable caching and retest.
|
||||
$obj->setCaching(true);
|
||||
$this->assertTrue($obj->isCaching());
|
||||
|
||||
// This will cache the content.
|
||||
$content = $obj->content();
|
||||
|
||||
$res2 = $obj->stream();
|
||||
$md = stream_get_meta_data($res2);
|
||||
|
||||
// If this is using the PHP version, it built content from the
|
||||
// cached version.
|
||||
$this->assertEquals('PHP', $md['wrapper_type']);
|
||||
|
||||
fclose($res2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testNewFromHeaders
|
||||
*/
|
||||
public function testContentVerification($obj)
|
||||
{
|
||||
$this->assertTrue($obj->isVerifyingContent());
|
||||
$obj->setContentVerification(false);
|
||||
$this->assertfalse($obj->isVerifyingContent());
|
||||
$obj->setContentVerification(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCaching
|
||||
*/
|
||||
public function testIsDirty()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$obj = $container->proxyObject(self::FNAME);
|
||||
|
||||
// THere is no content. Assert false.
|
||||
$this->assertFalse($obj->isDirty());
|
||||
|
||||
$obj->setCaching(true);
|
||||
$obj->content();
|
||||
|
||||
// THere is content, but it is unchanged.
|
||||
$this->assertFalse($obj->isDirty());
|
||||
|
||||
// Change content and retest.
|
||||
$obj->setContent('foo');
|
||||
|
||||
$this->assertTrue($obj->isDirty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testIsDirty
|
||||
*/
|
||||
public function testRefresh()
|
||||
{
|
||||
$container = $this->containerFixture();
|
||||
$obj = $container->proxyObject(self::FNAME);
|
||||
|
||||
$obj->setContent('foo');
|
||||
$this->assertTrue($obj->isDirty());
|
||||
|
||||
$obj->refresh(false);
|
||||
$this->assertFalse($obj->isDirty());
|
||||
$this->assertEquals(self::FCONTENT, $obj->content());
|
||||
|
||||
$obj->setContent('foo');
|
||||
$this->assertTrue($obj->isDirty());
|
||||
|
||||
$obj->refresh(true);
|
||||
$this->assertFalse($obj->isDirty());
|
||||
$this->assertEquals(self::FCONTENT, $obj->content());
|
||||
|
||||
$this->destroyContainerFixture();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,353 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\ObjectStore\v1\Resource;
|
||||
|
||||
use OpenStack\ObjectStore\v1\Resource\StreamWrapperFS;
|
||||
|
||||
/**
|
||||
* @group streamWrapper
|
||||
*/
|
||||
class StreamWrapperFSTest extends StreamWrapperTestCase
|
||||
{
|
||||
const SCHEME = StreamWrapperFS::DEFAULT_SCHEME;
|
||||
|
||||
public function testStreamContext()
|
||||
{
|
||||
$context = stream_context_get_options($this->context)['swiftfs'];
|
||||
$this->assertNotEmpty($context['token']);
|
||||
$this->assertNotEmpty($context['swift_endpoint']);
|
||||
$this->assertEquals(self::FTYPE, $context['content_type']);
|
||||
}
|
||||
|
||||
public function testRegister()
|
||||
{
|
||||
$this->assertNotEmpty(StreamWrapperFS::DEFAULT_SCHEME);
|
||||
$this->assertContains(StreamWrapperFS::DEFAULT_SCHEME, stream_get_wrappers());
|
||||
}
|
||||
|
||||
public function testOpenFailureWithoutContext()
|
||||
{
|
||||
$url = $this->createNewUrl('non_existent_container/foo.txt');
|
||||
$this->assertFalse(@fopen($url, 'r'));
|
||||
}
|
||||
|
||||
public function testResourceType()
|
||||
{
|
||||
$this->assertInternalType('resource', $this->resource);
|
||||
}
|
||||
|
||||
public function testCreatingResourceInWriteMode()
|
||||
{
|
||||
$resource = $this->createNewResource($this->createNewUrl(), 'w+');
|
||||
$this->assertInternalType('resource', $resource);
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
public function testCreatingResourceInCreateMode()
|
||||
{
|
||||
$resource = $this->createNewResource($this->createNewUrl(), 'c+');
|
||||
$this->assertInternalType('resource', $resource);
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
public function testTell()
|
||||
{
|
||||
// Sould be at the beginning of the buffer.
|
||||
$this->assertEquals(0, ftell($this->resource));
|
||||
}
|
||||
|
||||
public function testWrite()
|
||||
{
|
||||
$string = 'To be is to be the value of a bound variable. -- Quine';
|
||||
fwrite($this->resource, $string);
|
||||
$this->assertGreaterThan(0, ftell($this->resource));
|
||||
}
|
||||
|
||||
public function testStat()
|
||||
{
|
||||
$this->assertEquals(0, fstat($this->resource)['size']);
|
||||
|
||||
fwrite($this->resource, 'foo');
|
||||
fflush($this->resource);
|
||||
$this->assertGreaterThan(0, fstat($this->resource)['size']);
|
||||
}
|
||||
|
||||
public function testSeek()
|
||||
{
|
||||
$text = 'Foo bar';
|
||||
fwrite($this->resource, $text);
|
||||
|
||||
fseek($this->resource, 0, SEEK_END);
|
||||
$pointer = ftell($this->resource);
|
||||
|
||||
$this->assertGreaterThan(0, $pointer);
|
||||
}
|
||||
|
||||
public function testEof()
|
||||
{
|
||||
$this->assertFalse(feof($this->resource));
|
||||
|
||||
fwrite($this->resource, 'foo');
|
||||
rewind($this->resource);
|
||||
stream_get_contents($this->resource);
|
||||
|
||||
$this->assertTrue(feof($this->resource));
|
||||
}
|
||||
|
||||
public function testFlush()
|
||||
{
|
||||
$content = str_repeat('foo', 50);
|
||||
|
||||
fwrite($this->resource, $content);
|
||||
fflush($this->resource);
|
||||
rewind($this->resource);
|
||||
|
||||
$this->assertEquals($content, stream_get_contents($this->resource));
|
||||
}
|
||||
|
||||
public function testStreamGetMetadata()
|
||||
{
|
||||
$object = stream_get_meta_data($this->resource)['wrapper_data']->object();
|
||||
$this->assertInstanceOf('OpenStack\ObjectStore\v1\Resource\Object', $object);
|
||||
$this->assertEquals(self::FTYPE, $object->contentType());
|
||||
}
|
||||
|
||||
public function testClose()
|
||||
{
|
||||
fclose($this->resource);
|
||||
$this->assertFalse(is_resource($this->resource));
|
||||
}
|
||||
|
||||
public function testCast()
|
||||
{
|
||||
$read = [$this->resource];
|
||||
$write = [];
|
||||
$except = [];
|
||||
$this->assertGreaterThan(0, stream_select($read, $write, $except, 0));
|
||||
}
|
||||
|
||||
public function testUrlStat()
|
||||
{
|
||||
$stat = stat($this->url);
|
||||
|
||||
// Check that the array looks right.
|
||||
$this->assertCount(26, $stat);
|
||||
$this->assertEquals(0, $stat[3]);
|
||||
$this->assertEquals($stat[2], $stat['mode']);
|
||||
}
|
||||
|
||||
public function testFileExists()
|
||||
{
|
||||
$this->assertTrue(file_exists($this->url));
|
||||
}
|
||||
|
||||
public function testFileIsReadable()
|
||||
{
|
||||
$this->assertTrue(is_readable($this->url));
|
||||
}
|
||||
|
||||
public function testFileIsWritable()
|
||||
{
|
||||
$this->assertTrue(is_writeable($this->url));
|
||||
}
|
||||
|
||||
public function testFileModifyTime()
|
||||
{
|
||||
$this->assertGreaterThan(0, filemtime($this->url));
|
||||
}
|
||||
|
||||
public function testFileSize()
|
||||
{
|
||||
$url = $this->createNewUrl('file_size_test');
|
||||
|
||||
$resource = $this->createNewResource($url, 'w+');
|
||||
fwrite($resource, '!');
|
||||
fclose($resource);
|
||||
|
||||
$this->assertEquals(1, filesize($url));
|
||||
unlink($url);
|
||||
}
|
||||
|
||||
public function testPermissions()
|
||||
{
|
||||
$perm = fileperms($this->url);
|
||||
|
||||
// Assert that this is a file. Objects are *always* marked as files.
|
||||
$this->assertEquals(0x8000, $perm & 0x8000);
|
||||
|
||||
// Assert writeable by owner.
|
||||
$this->assertEquals(0x0080, $perm & 0x0080);
|
||||
|
||||
// Assert not world writable.
|
||||
$this->assertEquals(0, $perm & 0x0002);
|
||||
}
|
||||
|
||||
public function testFileGetContents()
|
||||
{
|
||||
$url = $this->createNewUrl('get_contents');
|
||||
$resource = $this->createNewResource($url, 'w+');
|
||||
|
||||
fwrite($resource, '!');
|
||||
fclose($resource);
|
||||
|
||||
$contents = file_get_contents($url, null, $this->context);
|
||||
$this->assertEquals('!', $contents);
|
||||
unlink($url);
|
||||
}
|
||||
|
||||
public function testCopy()
|
||||
{
|
||||
$newUrl = '/tmp/new_file_from_swift.txt';
|
||||
copy($this->url, $newUrl, $this->context);
|
||||
|
||||
$this->assertTrue(file_exists($newUrl));
|
||||
unlink($newUrl);
|
||||
}
|
||||
|
||||
public function testUnlink()
|
||||
{
|
||||
unlink($this->url, $this->context);
|
||||
$this->assertFalse(file_exists($this->url));
|
||||
}
|
||||
|
||||
public function testSetOption()
|
||||
{
|
||||
$this->assertTrue(stream_set_blocking($this->resource, 1));
|
||||
|
||||
// Returns 0 on success.
|
||||
$this->assertEquals(0, stream_set_write_buffer($this->resource, 8192));
|
||||
|
||||
// Cannot set a timeout on a tmp storage:
|
||||
$this->assertFalse(stream_set_timeout($this->resource, 10));
|
||||
}
|
||||
|
||||
public function testRename()
|
||||
{
|
||||
$oldUrl = $this->createNewUrl('old');
|
||||
$newUrl = $this->createNewUrl('new');
|
||||
|
||||
$original = $this->createNewResource($oldUrl, 'w+');
|
||||
fwrite($original, 'fooooo');
|
||||
fclose($original);
|
||||
|
||||
rename($oldUrl, $newUrl, $this->context);
|
||||
|
||||
$this->assertTrue(file_exists($newUrl));
|
||||
$this->assertFalse(file_exists($this->url));
|
||||
|
||||
unlink($newUrl, $this->context);
|
||||
}
|
||||
|
||||
public function testOpenDir()
|
||||
{
|
||||
$baseDirectory = opendir($this->createNewUrl(''), $this->context);
|
||||
$this->assertInternalType('resource', $baseDirectory);
|
||||
closedir($baseDirectory);
|
||||
}
|
||||
|
||||
public function testReadDir()
|
||||
{
|
||||
$paths = ['test1.txt', 'foo/test2.txt', 'foo/test3.txt', 'bar/test4.txt'];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$file = fopen($this->createNewUrl($path), 'c+', false, $this->context);
|
||||
fwrite($file, 'Test.');
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
$baseDirectory = opendir($this->createNewUrl(''), $this->context);
|
||||
|
||||
$expectedPaths = ['bar/', 'foo/', 'test1.txt'];
|
||||
while (false !== ($currentEntry = readdir($baseDirectory))) {
|
||||
$nextPath = array_shift($expectedPaths);
|
||||
$this->assertEquals($nextPath, $currentEntry);
|
||||
}
|
||||
|
||||
$this->assertFalse(readdir($baseDirectory));
|
||||
|
||||
closedir($baseDirectory);
|
||||
}
|
||||
|
||||
public function testRewindDir()
|
||||
{
|
||||
$baseDirectory = opendir($this->createNewUrl(''), $this->context);
|
||||
rewinddir($baseDirectory);
|
||||
|
||||
$this->assertEquals('bar/', readdir($baseDirectory));
|
||||
|
||||
closedir($baseDirectory);
|
||||
}
|
||||
|
||||
public function testCloseDir()
|
||||
{
|
||||
$baseDirectory = opendir($this->createNewUrl(''), $this->context);
|
||||
closedir($baseDirectory);
|
||||
$this->assertFalse(is_resource($baseDirectory));
|
||||
}
|
||||
|
||||
public function testOpenSubdir()
|
||||
{
|
||||
// Opening foo we should find test2.txt and test3.txt.
|
||||
$url = $this->createNewUrl('foo/');
|
||||
$dir = opendir($url, $this->context);
|
||||
|
||||
$this->assertEquals('test2.txt', readdir($dir));
|
||||
$this->assertEquals('test3.txt', readdir($dir));
|
||||
|
||||
$array = scandir($url, -1, $this->context);
|
||||
$this->assertEquals(2, count($array));
|
||||
$this->assertEquals('test3.txt', $array[0]);
|
||||
}
|
||||
|
||||
public function testIsDir()
|
||||
{
|
||||
// Object names are pathy. If objects exist starting with this path we can
|
||||
// consider the directory to exist.
|
||||
$url = $this->createNewUrl('baz/');
|
||||
$this->assertFalse(is_dir($url));
|
||||
|
||||
$url = $this->createNewUrl('foo/');
|
||||
$this->assertTrue(is_dir($url));
|
||||
}
|
||||
|
||||
public function testMkdir()
|
||||
{
|
||||
// Object names are pathy. If no object names start with the a path we can
|
||||
// consider mkdir passed. If object names exist we should fail mkdir.
|
||||
$url = $this->createNewUrl('baz/');
|
||||
$this->assertTrue(mkdir($url, 0700, true, $this->context));
|
||||
|
||||
// Test the case for an existing directory.
|
||||
$url = $this->createNewUrl('foo/');
|
||||
$this->assertFalse(mkdir($url, 0700, true, $this->context));
|
||||
}
|
||||
|
||||
public function testRmdir()
|
||||
{
|
||||
// Object names are pathy. If no object names start with the a path we can
|
||||
// consider rmdir passed. If object names exist we should fail rmdir.
|
||||
$url = $this->createNewUrl('baz/');
|
||||
$this->assertTrue(rmdir($url, $this->context));
|
||||
|
||||
// Test the case for an existing directory.
|
||||
$url = $this->createNewUrl('foo/');
|
||||
$this->assertFalse(rmdir($url, $this->context));
|
||||
}
|
||||
}
|
@ -1,314 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\ObjectStore\v1\Resource;
|
||||
|
||||
use OpenStack\Bootstrap;
|
||||
use OpenStack\ObjectStore\v1\Resource\StreamWrapper;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @group streamWrapper
|
||||
*/
|
||||
class StreamWrapperTest extends StreamWrapperTestCase
|
||||
{
|
||||
const SCHEME = StreamWrapper::DEFAULT_SCHEME;
|
||||
|
||||
public function testStreamContext()
|
||||
{
|
||||
$context = stream_context_get_options($this->context)['swift'];
|
||||
$this->assertNotEmpty($context['token']);
|
||||
$this->assertNotEmpty($context['swift_endpoint']);
|
||||
$this->assertEquals(self::FTYPE, $context['content_type']);
|
||||
}
|
||||
|
||||
public function testOpenFailureWithoutContext()
|
||||
{
|
||||
$url = $this->createNewUrl('non_existent_container/foo.txt');
|
||||
$this->assertFalse(@fopen($url, 'r'));
|
||||
}
|
||||
|
||||
public function testResourceType()
|
||||
{
|
||||
$this->assertInternalType('resource', $this->resource);
|
||||
}
|
||||
|
||||
public function testCreatingResourceInWriteMode()
|
||||
{
|
||||
$resource = $this->createNewResource($this->createNewUrl(), 'w+');
|
||||
$this->assertInternalType('resource', $resource);
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
public function testCreatingResourceInCreateMode()
|
||||
{
|
||||
$resource = $this->createNewResource($this->createNewUrl(), 'c+');
|
||||
$this->assertInternalType('resource', $resource);
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
public function testTell()
|
||||
{
|
||||
// Sould be at the beginning of the buffer.
|
||||
$this->assertEquals(0, ftell($this->resource));
|
||||
}
|
||||
|
||||
public function testWrite()
|
||||
{
|
||||
$string = 'To be is to be the value of a bound variable. -- Quine';
|
||||
fwrite($this->resource, $string);
|
||||
$this->assertGreaterThan(0, ftell($this->resource));
|
||||
}
|
||||
|
||||
public function testStat()
|
||||
{
|
||||
$this->assertEquals(0, fstat($this->resource)['size']);
|
||||
|
||||
fwrite($this->resource, 'foo');
|
||||
fflush($this->resource);
|
||||
$this->assertGreaterThan(0, fstat($this->resource)['size']);
|
||||
}
|
||||
|
||||
public function testSeek()
|
||||
{
|
||||
$text = 'Foo bar';
|
||||
fwrite($this->resource, $text);
|
||||
|
||||
fseek($this->resource, 0, SEEK_END);
|
||||
$pointer = ftell($this->resource);
|
||||
|
||||
$this->assertGreaterThan(0, $pointer);
|
||||
}
|
||||
|
||||
public function testEof()
|
||||
{
|
||||
$this->assertFalse(feof($this->resource));
|
||||
|
||||
fwrite($this->resource, 'foo');
|
||||
rewind($this->resource);
|
||||
stream_get_contents($this->resource);
|
||||
|
||||
$this->assertTrue(feof($this->resource));
|
||||
}
|
||||
|
||||
public function testFlush()
|
||||
{
|
||||
$content = str_repeat('foo', 50);
|
||||
|
||||
fwrite($this->resource, $content);
|
||||
fflush($this->resource);
|
||||
rewind($this->resource);
|
||||
|
||||
$this->assertEquals($content, stream_get_contents($this->resource));
|
||||
}
|
||||
|
||||
public function testStreamGetMetadata()
|
||||
{
|
||||
$object = stream_get_meta_data($this->resource)['wrapper_data']->object();
|
||||
$this->assertInstanceOf('OpenStack\ObjectStore\v1\Resource\Object', $object);
|
||||
$this->assertEquals(self::FTYPE, $object->contentType());
|
||||
}
|
||||
|
||||
public function testClose()
|
||||
{
|
||||
fclose($this->resource);
|
||||
$this->assertFalse(is_resource($this->resource));
|
||||
}
|
||||
|
||||
public function testCast()
|
||||
{
|
||||
$read = [$this->resource];
|
||||
$write = [];
|
||||
$except = [];
|
||||
$this->assertGreaterThan(0, stream_select($read, $write, $except, 0));
|
||||
}
|
||||
|
||||
public function testUrlStat()
|
||||
{
|
||||
$stat = stat($this->url);
|
||||
|
||||
// Check that the array looks right.
|
||||
$this->assertCount(26, $stat);
|
||||
$this->assertEquals(0, $stat[3]);
|
||||
$this->assertEquals($stat[2], $stat['mode']);
|
||||
}
|
||||
|
||||
public function testFileExists()
|
||||
{
|
||||
$this->assertTrue(file_exists($this->url));
|
||||
}
|
||||
|
||||
public function testFileIsReadable()
|
||||
{
|
||||
$this->assertTrue(is_readable($this->url));
|
||||
}
|
||||
|
||||
public function testFileIsWritable()
|
||||
{
|
||||
$this->assertTrue(is_writeable($this->url));
|
||||
}
|
||||
|
||||
public function testFileModifyTime()
|
||||
{
|
||||
$this->assertGreaterThan(0, filemtime($this->url));
|
||||
}
|
||||
|
||||
public function testFileSize()
|
||||
{
|
||||
$url = $this->createNewUrl('file_size_test');
|
||||
|
||||
$resource = $this->createNewResource($url, 'w+');
|
||||
fwrite($resource, '!');
|
||||
fclose($resource);
|
||||
|
||||
$this->assertEquals(1, filesize($url));
|
||||
unlink($url);
|
||||
}
|
||||
|
||||
public function testPermissions()
|
||||
{
|
||||
$perm = fileperms($this->url);
|
||||
|
||||
// Assert that this is a file. Objects are *always* marked as files.
|
||||
$this->assertEquals(0x8000, $perm & 0x8000);
|
||||
|
||||
// Assert writeable by owner.
|
||||
$this->assertEquals(0x0080, $perm & 0x0080);
|
||||
|
||||
// Assert not world writable.
|
||||
$this->assertEquals(0, $perm & 0x0002);
|
||||
}
|
||||
|
||||
public function testFileGetContents()
|
||||
{
|
||||
$url = $this->createNewUrl('get_contents');
|
||||
$resource = $this->createNewResource($url, 'w+');
|
||||
|
||||
fwrite($resource, '!');
|
||||
fclose($resource);
|
||||
|
||||
$contents = file_get_contents($url, null, $this->context);
|
||||
$this->assertEquals('!', $contents);
|
||||
unlink($url);
|
||||
}
|
||||
|
||||
public function testCopy()
|
||||
{
|
||||
$newUrl = '/tmp/new_file_from_swift.txt';
|
||||
copy($this->url, $newUrl, $this->context);
|
||||
|
||||
$this->assertTrue(file_exists($newUrl));
|
||||
unlink($newUrl);
|
||||
}
|
||||
|
||||
public function testUnlink()
|
||||
{
|
||||
unlink($this->url, $this->context);
|
||||
$this->assertFalse(file_exists($this->url));
|
||||
}
|
||||
|
||||
public function testSetOption()
|
||||
{
|
||||
$this->assertTrue(stream_set_blocking($this->resource, 1));
|
||||
|
||||
// Returns 0 on success.
|
||||
$this->assertEquals(0, stream_set_write_buffer($this->resource, 8192));
|
||||
|
||||
// Cannot set a timeout on a tmp storage:
|
||||
$this->assertFalse(stream_set_timeout($this->resource, 10));
|
||||
}
|
||||
|
||||
public function testRename()
|
||||
{
|
||||
$oldUrl = $this->createNewUrl('old');
|
||||
$newUrl = $this->createNewUrl('new');
|
||||
|
||||
$original = $this->createNewResource($oldUrl, 'w+');
|
||||
fwrite($original, 'fooooo');
|
||||
fclose($original);
|
||||
|
||||
rename($oldUrl, $newUrl, $this->context);
|
||||
|
||||
$this->assertTrue(file_exists($newUrl));
|
||||
$this->assertFalse(file_exists($this->url));
|
||||
|
||||
unlink($newUrl, $this->context);
|
||||
}
|
||||
|
||||
public function testOpenDir()
|
||||
{
|
||||
$baseDirectory = opendir($this->createNewUrl(''), $this->context);
|
||||
$this->assertInternalType('resource', $baseDirectory);
|
||||
closedir($baseDirectory);
|
||||
}
|
||||
|
||||
public function testReadDir()
|
||||
{
|
||||
$paths = ['test1.txt', 'foo/test2.txt', 'foo/test3.txt', 'bar/test4.txt'];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$file = fopen($this->createNewUrl($path), 'c+', false, $this->context);
|
||||
fwrite($file, 'Test.');
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
$baseDirectory = opendir($this->createNewUrl(''), $this->context);
|
||||
|
||||
$expectedPaths = ['bar/', 'foo/', 'test1.txt'];
|
||||
while (false !== ($currentEntry = readdir($baseDirectory))) {
|
||||
$nextPath = array_shift($expectedPaths);
|
||||
$this->assertEquals($nextPath, $currentEntry);
|
||||
}
|
||||
|
||||
$this->assertFalse(readdir($baseDirectory));
|
||||
|
||||
closedir($baseDirectory);
|
||||
}
|
||||
|
||||
public function testRewindDir()
|
||||
{
|
||||
$baseDirectory = opendir($this->createNewUrl(''), $this->context);
|
||||
rewinddir($baseDirectory);
|
||||
|
||||
$this->assertEquals('bar/', readdir($baseDirectory));
|
||||
|
||||
closedir($baseDirectory);
|
||||
}
|
||||
|
||||
public function testCloseDir()
|
||||
{
|
||||
$baseDirectory = opendir($this->createNewUrl(''), $this->context);
|
||||
closedir($baseDirectory);
|
||||
$this->assertFalse(is_resource($baseDirectory));
|
||||
}
|
||||
|
||||
public function testOpenSubdir()
|
||||
{
|
||||
// Opening foo we should find test2.txt and test3.txt.
|
||||
$url = $this->createNewUrl('foo/');
|
||||
$dir = opendir($url, $this->context);
|
||||
|
||||
$this->assertEquals('test2.txt', readdir($dir));
|
||||
$this->assertEquals('test3.txt', readdir($dir));
|
||||
|
||||
$array = scandir($url, -1, $this->context);
|
||||
$this->assertEquals(2, count($array));
|
||||
$this->assertEquals('test3.txt', $array[0]);
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests\ObjectStore\v1\Resource;
|
||||
|
||||
use OpenStack\Bootstrap;
|
||||
use OpenStack\ObjectStore\v1\Resource\StreamWrapper;
|
||||
use OpenStack\Tests\TestCase;
|
||||
|
||||
abstract class StreamWrapperTestCase extends TestCase
|
||||
{
|
||||
const FTYPE = 'application/foo-bar; charset=iso-8859-13';
|
||||
const DEFAULT_MODE = 'nope';
|
||||
const FILE_PATH = 'foo→/test.csv';
|
||||
const SCHEME = StreamWrapper::DEFAULT_SCHEME;
|
||||
|
||||
protected static $container;
|
||||
protected $resource;
|
||||
protected $context;
|
||||
protected $url;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
self::setConfiguration();
|
||||
|
||||
$service = self::createObjectStoreService();
|
||||
$containerName = self::$settings['openstack.swift.container'];
|
||||
|
||||
$service->createContainer($containerName);
|
||||
|
||||
try {
|
||||
self::$container = $service->container($containerName);
|
||||
} catch (\Exception $e) {
|
||||
$service->deleteContainer($containerName);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
self::$settings += [
|
||||
'username' => self::$settings['openstack.identity.username'],
|
||||
'password' => self::$settings['openstack.identity.password'],
|
||||
'endpoint' => self::$settings['openstack.identity.url'],
|
||||
'tenantid' => self::$settings['openstack.identity.tenantId'],
|
||||
'token' => $service->token(),
|
||||
'swift_endpoint' => $service->url(),
|
||||
];
|
||||
Bootstrap::setConfiguration(self::$settings);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
if (!self::$container) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (self::$container as $object) {
|
||||
try {
|
||||
self::$container->delete($object->name());
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
$service = self::createObjectStoreService();
|
||||
$service->deleteContainer(self::$container->name());
|
||||
}
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
Bootstrap::useStreamWrappers();
|
||||
|
||||
$this->url = $this->createNewUrl();
|
||||
$this->context = $this->createStreamContext();
|
||||
$this->resource = $this->createNewResource($this->url);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
if (is_resource($this->resource)) {
|
||||
fclose($this->resource);
|
||||
}
|
||||
|
||||
$this->resource = null;
|
||||
stream_wrapper_unregister(static::SCHEME);
|
||||
}
|
||||
|
||||
protected function createNewResource($url, $mode = self::DEFAULT_MODE)
|
||||
{
|
||||
return fopen($url, $mode, false, $this->context);
|
||||
}
|
||||
|
||||
protected function createNewUrl($objectName = self::FILE_PATH)
|
||||
{
|
||||
return sprintf("%s://%s/%s",
|
||||
static::SCHEME,
|
||||
urlencode(self::$settings['openstack.swift.container']),
|
||||
join('/', array_map('urlencode', explode('/', $objectName)))
|
||||
);
|
||||
}
|
||||
|
||||
private function createStreamContext(array $params = [], $scheme = null)
|
||||
{
|
||||
if (!$scheme) {
|
||||
$scheme = static::SCHEME;
|
||||
}
|
||||
|
||||
if (!($objectStore = $this->objectStore())) {
|
||||
throw new \Exception('Object storage service could not be created');
|
||||
}
|
||||
|
||||
$params += [
|
||||
'token' => $objectStore->token(),
|
||||
'swift_endpoint' => $objectStore->url(),
|
||||
'content_type' => self::FTYPE,
|
||||
'transport_client' => $this->getTransportClient(),
|
||||
];
|
||||
|
||||
return stream_context_create([
|
||||
$scheme => $params
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,240 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
|
||||
* (c) Copyright 2014 Rackspace US, 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 OpenStack\Tests;
|
||||
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use OpenStack\Bootstrap;
|
||||
use OpenStack\Common\Transport\Exception\ResourceNotFoundException;
|
||||
use OpenStack\Identity\v2\IdentityService;
|
||||
use OpenStack\ObjectStore\v1\ObjectStorage;
|
||||
use OpenStack\Common\Transport\Guzzle\GuzzleAdapter;
|
||||
|
||||
/**
|
||||
* @ingroup Tests
|
||||
*/
|
||||
abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public static $settings = [];
|
||||
|
||||
protected $objectStoreService;
|
||||
|
||||
/**
|
||||
* The IdentityService instance.
|
||||
*/
|
||||
public static $ident;
|
||||
|
||||
public static $httpClient = null;
|
||||
|
||||
protected $containerFixture = null;
|
||||
|
||||
protected static function setConfiguration()
|
||||
{
|
||||
if (file_exists('tests/settings.ini')) {
|
||||
self::$settings = parse_ini_file('tests/settings.ini');
|
||||
} else {
|
||||
throw new \Exception('Could not access test/settings.ini');
|
||||
}
|
||||
|
||||
Bootstrap::setConfiguration(self::$settings);
|
||||
}
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
self::setConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration value.
|
||||
*
|
||||
* Optionally, specify a default value to be used
|
||||
* if none was found.
|
||||
*/
|
||||
public static function conf($name, $default = null)
|
||||
{
|
||||
if (isset(self::$settings[$name])) {
|
||||
return self::$settings[$name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
protected static function createIdentityService()
|
||||
{
|
||||
$username = self::conf('openstack.identity.username');
|
||||
$password = self::conf('openstack.identity.password');
|
||||
$url = self::conf('openstack.identity.url');
|
||||
$tenantId = self::conf('openstack.identity.tenantId');
|
||||
|
||||
$service = new IdentityService($url);
|
||||
$service->authenticateAsUser($username, $password, $tenantId);
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
protected static function createObjectStoreService()
|
||||
{
|
||||
return ObjectStorage::newFromIdentity(
|
||||
self::createIdentityService(),
|
||||
self::$settings['openstack.swift.region'],
|
||||
self::getTransportClient()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a handle to an IdentityService object.
|
||||
*
|
||||
* Authentication is performed, and the returned
|
||||
* service has its tenant ID set already.
|
||||
*
|
||||
* <?php
|
||||
* // Get the current token.
|
||||
* $this->identity()->token();
|
||||
* ?>
|
||||
*/
|
||||
protected function identity($reset = false)
|
||||
{
|
||||
if ($reset || empty(self::$ident)) {
|
||||
self::$ident = self::createIdentityService();
|
||||
}
|
||||
|
||||
return self::$ident;
|
||||
}
|
||||
|
||||
protected function objectStore($reset = false)
|
||||
{
|
||||
if ($reset || !$this->objectStoreService) {
|
||||
$this->objectStoreService = self::createObjectStoreService();
|
||||
}
|
||||
|
||||
return $this->objectStoreService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a container from the server.
|
||||
*/
|
||||
protected function containerFixture()
|
||||
{
|
||||
if (empty($this->containerFixture)) {
|
||||
$store = $this->objectStore();
|
||||
$cname = self::$settings['openstack.swift.container'];
|
||||
|
||||
try {
|
||||
$store->createContainer($cname);
|
||||
$this->containerFixture = $store->container($cname);
|
||||
} catch (\Exception $e) {
|
||||
// Delete the container.
|
||||
$store->deleteContainer($cname);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->containerFixture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear and destroy a container.
|
||||
*
|
||||
* Destroy all of the files in a container, then destroy the
|
||||
* container.
|
||||
*
|
||||
* If the container doesn't exist, this will silently return.
|
||||
*
|
||||
* @param string $cname The name of the container.
|
||||
*/
|
||||
protected function eradicateContainer($cname)
|
||||
{
|
||||
$store = $this->objectStore();
|
||||
try {
|
||||
$container = $store->container($cname);
|
||||
} catch (ResourceNotFoundException $e) {
|
||||
// The container was never created.
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($container as $object) {
|
||||
try {
|
||||
$container->delete($object->name());
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
$store->deleteContainer($cname);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the HTTP Transport Client
|
||||
*
|
||||
* @return \OpenStack\Common\Transport\ClientInterface A transport client.
|
||||
*/
|
||||
public static function getTransportClient()
|
||||
{
|
||||
if (is_null(self::$httpClient)) {
|
||||
$options = [];
|
||||
|
||||
if (isset(self::$settings['transport.proxy'])) {
|
||||
$options['proxy'] = self::$settings['transport.proxy'];
|
||||
}
|
||||
if (isset(self::$settings['transport.debug'])) {
|
||||
$options['debug'] = self::$settings['transport.debug'];
|
||||
}
|
||||
if (isset(self::$settings['transport.ssl.verify'])) {
|
||||
$options['ssl_verify'] = self::$settings['transport.ssl.verify'];
|
||||
}
|
||||
if (isset(self::$settings['transport.timeout'])) {
|
||||
$options['timeout'] = self::$settings['transport.timeout'];
|
||||
}
|
||||
|
||||
self::$httpClient = GuzzleAdapter::create([
|
||||
'defaults' => $options
|
||||
]);
|
||||
}
|
||||
|
||||
return self::$httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a container fixture.
|
||||
*
|
||||
* This should be called in any method that uses containerFixture().
|
||||
*/
|
||||
protected function destroyContainerFixture()
|
||||
{
|
||||
$store = $this->objectStore();
|
||||
$cname = self::$settings['openstack.swift.container'];
|
||||
|
||||
try {
|
||||
$container = $store->container($cname);
|
||||
}
|
||||
// The container was never created.
|
||||
catch (ResourceNotFoundException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($container as $object) {
|
||||
try {
|
||||
$container->delete($object->name());
|
||||
} catch (\Exception $e) {
|
||||
syslog(LOG_WARNING, $e);
|
||||
}
|
||||
}
|
||||
|
||||
$store->deleteContainer($cname);
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* (c) Copyright 2014 Rackspace US, 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.
|
||||
*/
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
@ -1,53 +0,0 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
; Identity Services ;
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; This is the default Identity Service URL.
|
||||
openstack.identity.url = https://region-a.geo-1.identity.hpcloudsvc.com:35357/v2.0
|
||||
|
||||
; Set the tenant ID
|
||||
openstack.identity.tenantId =
|
||||
openstack.identity.tenantName =
|
||||
|
||||
; For authentication by username.
|
||||
openstack.identity.username =
|
||||
openstack.identity.password =
|
||||
|
||||
;;;;;;;;;;;;;;;;;;
|
||||
; Object Storage ;
|
||||
;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; Settings to work with swift:
|
||||
; Account is the tenandId:console username.
|
||||
openstack.swift.account = 12345678:87654321
|
||||
; Key is the console account password.
|
||||
openstack.swift.key = abcdef123456
|
||||
; URL is the same as used for identity services calls (including port) except
|
||||
; with /auth/v1.0/ appended to the end.
|
||||
openstack.swift.url = https://region-a.geo-1.identity.hpcloudsvc.com:35357/auth/v1.0/
|
||||
|
||||
; Container used for testing.
|
||||
openstack.swift.container = "test"
|
||||
|
||||
; Specified region name to test against.
|
||||
openstack.swift.region = "Region1"
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Configuration Parameters ;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; The HTTP Transport Client to use.
|
||||
transport = "OpenStack\Common\Transport\Guzzle\GuzzleAdapter"
|
||||
|
||||
; If behind a proxy set to the https proxy server communications need
|
||||
; to pass through.
|
||||
; transport.proxy = "https://username:password@foobar.com:1234"
|
||||
|
||||
; Turn on verbose debugging of the transport.
|
||||
; transport.debug = 1
|
||||
|
||||
; Site the max time (in seconds) a connection will wait for the transaction to complete.
|
||||
; transport.timeout = .5
|
||||
|
||||
; Tell SSL not to worry about certs that can be verified.
|
||||
; transport.ssl.verify = 0
|
Loading…
x
Reference in New Issue
Block a user