r106624 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r106623‎ | r106624 | r106625 >
Date:01:43, 19 December 2011
Author:nelson
Status:deferred
Tags:
Comment:
new filebackend for Swift
Modified paths:
  • /branches/FileBackend/phase3/includes/filerepo/backend/SwiftFileBackend.php (added) (history)

Diff [purge]

Index: branches/FileBackend/phase3/includes/filerepo/backend/SwiftFileBackend.php
@@ -0,0 +1,415 @@
 2+<?php
 3+/**
 4+ * @file
 5+ * @ingroup FileBackend
 6+ */
 7+
 8+/**
 9+ * Class for a Swift based file backend.
 10+ * Status messages should avoid mentioning the Swift account name
 11+ * Likewise, error suppression should be used to avoid path disclosure.
 12+ *
 13+ * @ingroup FileBackend
 14+ */
 15+class SwiftFileBackend extends FileBackend {
 16+ protected $swiftuser,
 17+ $swiftkey,
 18+ $authurl,
 19+ $container;
 20+
 21+ function __construct( array $config ) {
 22+ parent::__construct( $config );
 23+
 24+ // Required settings
 25+ $this->swiftuser = $config['user'];
 26+ $this->swiftkey = $config['key'];
 27+ $this->authurl = $config['authurl'];
 28+ $this->container = $config['container'];
 29+ }
 30+
 31+ /**
 32+ * Get a connection to the swift proxy.
 33+ *
 34+ * @return CF_Connection
 35+ */
 36+ protected function connect() {
 37+ $auth = new CF_Authentication( $this->swiftuser, $this->swiftkey, NULL, $this->authurl );
 38+ try {
 39+ $auth->authenticate();
 40+ } catch ( AuthenticationException $e ) {
 41+ throw new MWException( "We can't authenticate ourselves." );
 42+ # } catch (InvalidResponseException $e) {
 43+ # throw new MWException( __METHOD__ . "unexpected response '$e'" );
 44+ }
 45+ return new CF_Connection( $auth );
 46+ }
 47+
 48+ /**
 49+ * Given a connection and container name, return the container.
 50+ * We KNOW the container should exist, so puke if it doesn't.
 51+ *
 52+ * @param $conn CF_Connection
 53+ *
 54+ * @return CF_Container
 55+ */
 56+ protected function get_container( $conn, $cont ) {
 57+ try {
 58+ return $conn->get_container( $cont );
 59+ } catch ( NoSuchContainerException $e ) {
 60+ throw new MWException( "A container we thought existed, doesn't." );
 61+ # } catch (InvalidResponseException $e) {
 62+ # throw new MWException( __METHOD__ . "unexpected response '$e'" );
 63+ }
 64+ }
 65+
 66+ /**
 67+ * Copy a file from one place to another place
 68+ * @param $srcContainer CF_Container
 69+ * @param $srcRel String: relative path to the source file.
 70+ * @param $dstContainer CF_Container
 71+ * @param $dstRel String: relative path to the destination.
 72+ */
 73+ protected function swiftcopy( $srcContainer, $srcRel, $dstContainer, $dstRel ) {
 74+ // The destination must exist already.
 75+ $obj = $dstContainer->create_object( $dstRel );
 76+ $obj->content_type = 'text/plain'; // overwritten by source object.
 77+
 78+ try {
 79+ $obj->write( '.' );
 80+ } catch ( SyntaxException $e ) {
 81+ throw new MWException( "Write failed: $e" );
 82+ } catch ( BadContentTypeException $e ) {
 83+ throw new MWException( "Missing Content-Type: $e" );
 84+ } catch ( MisMatchedChecksumException $e ) {
 85+ throw new MWException( __METHOD__ . "should not happen: '$e'" );
 86+ }
 87+
 88+ try {
 89+ $obj = $dstContainer->get_object( $dstRel );
 90+ } catch ( NoSuchObjectException $e ) {
 91+ throw new MWException( 'The object we just created does not exist: ' . $dstContainer->name . "/$dstRel: $e" );
 92+ }
 93+
 94+ try {
 95+ $srcObj = $srcContainer->get_object( $srcRel );
 96+ } catch ( NoSuchObjectException $e ) {
 97+ throw new MWException( 'Source file does not exist: ' . $srcContainer->name . "/$srcRel: $e" );
 98+ }
 99+
 100+ try {
 101+ $dstContainer->copy_object_from($srcObj,$srcContainer,$dstRel);
 102+ } catch ( SyntaxException $e ) {
 103+ throw new MWException( 'Source file does not exist: ' . $srcContainer->name . "/$srcRel: $e" );
 104+ } catch ( MisMatchedChecksumException $e ) {
 105+ throw new MWException( "Checksums do not match: $e" );
 106+ }
 107+ }
 108+
 109+ function store( array $params ) {
 110+ $status = Status::newGood();
 111+
 112+ list( $destc, $dest ) = $this->resolveStoragePath( $params['dst'] );
 113+ if ( $dest === null ) {
 114+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 115+ return $status;
 116+ }
 117+ $conn = $this->connect();
 118+ $dstc = $this->get_container( $conn, $destc );
 119+ try {
 120+ $objd = $dstc->get_object( $dest );
 121+ // if we are still here, it exists.
 122+ if ( empty( $params['overwriteDest'] ) ) {
 123+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
 124+ return $status;
 125+ }
 126+ $exists = true;
 127+ } catch (NoSuchObjectException $e) {
 128+ $exists = false;
 129+ }
 130+
 131+ try {
 132+ $obj = $dstc->create_object( $dest);
 133+ $obj->load_from_filename( $params['src'], True );
 134+ } catch ( SyntaxException $e ) {
 135+ throw new MWException( 'missing required parameters' );
 136+ } catch ( BadContentTypeException $e ) {
 137+ throw new MWException( 'No Content-Type was/could be set' );
 138+ } catch (InvalidResponseException $e) {
 139+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
 140+ } catch ( IOException $e ) {
 141+ throw new MWException( "error opening file '$e'" );
 142+ }
 143+ return $status;
 144+ }
 145+
 146+ function copy( array $params ) {
 147+ $status = Status::newGood();
 148+
 149+ list( $sourcec, $source ) = $this->resolveStoragePath( $params['src'] );
 150+ if ( $source === null ) {
 151+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
 152+ return $status;
 153+ }
 154+
 155+ list( $destc, $dest ) = $this->resolveStoragePath( $params['dst'] );
 156+ if ( $dest === null ) {
 157+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 158+ return $status;
 159+ }
 160+
 161+ $conn = $this->connect();
 162+ $srcc = $this->get_container( $conn, $sourcec );
 163+ $dstc = $this->get_container( $conn, $destc );
 164+ try {
 165+ $objd = $dstc->get_object( $dest );
 166+ // if we are still here, it exists.
 167+ if ( empty( $params['overwriteDest'] ) ) {
 168+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
 169+ return $status;
 170+ }
 171+ $exists = true;
 172+ } catch (NoSuchObjectException $e) {
 173+ $exists = false;
 174+ }
 175+ try {
 176+ $this->swiftcopy( $srcc, $source, $dstc, $dest );
 177+ } catch (InvalidResponseException $e ) {
 178+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
 179+ }
 180+ return $status;
 181+ }
 182+
 183+ function canMove( array $params ) {
 184+ return false;
 185+ }
 186+
 187+ function delete( array $params ) {
 188+ $status = Status::newGood();
 189+
 190+ list( $sourcec, $source ) = $this->resolveStoragePath( $params['src'] );
 191+ if ( $source === null ) {
 192+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
 193+ return $status;
 194+ }
 195+
 196+ $conn = $this->connect();
 197+ $container = $this->get_container( $conn, $sourcec );
 198+
 199+ try {
 200+ $obj = $container->get_object( $source);
 201+ $exists = true;
 202+ } catch (NoSuchObjectException $e) {
 203+ if ( empty( $params['ignoreMissingSource'] ) ) {
 204+ $status->fatal( 'backend-fail-delete', $params['src'] );
 205+ }
 206+ $exists = false;
 207+ }
 208+
 209+ if ($exists) {
 210+ try {
 211+ $container->delete_object( $source );
 212+ } catch ( SyntaxException $e ) {
 213+ throw new MWException( "Swift object name not well-formed: '$e'" );
 214+ } catch ( NoSuchObjectException $e ) {
 215+ throw new MWException( "Swift object we are trying to delete does not exist: '$e'" );
 216+ } catch (InvalidResponseException $e) {
 217+ $status->fatal( 'backend-fail-delete', $params['src'] );
 218+ }
 219+ }
 220+ return $status; // do nothing; either OK or bad status
 221+ }
 222+
 223+ function concatenate( array $params ) {
 224+ $status = Status::newGood();
 225+
 226+ list( $destc, $dest ) = $this->resolveStoragePath( $params['dst'] );
 227+ if ( $dest === null ) {
 228+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 229+ return $status;
 230+ }
 231+
 232+ $conn = $this->connect();
 233+ $dstc = $this->get_container( $conn, $destc );
 234+ try {
 235+ $objd = $dstc->get_object( $dest );
 236+ // if we are still here, it exists.
 237+ if ( empty( $params['overwriteDest'] ) ) {
 238+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
 239+ return $status;
 240+ }
 241+ $exists = true;
 242+ } catch (NoSuchObjectException $e) {
 243+ $exists = false;
 244+ }
 245+
 246+ try {
 247+ $biggie = $dstc->create_object( $dest );
 248+ } catch (InvalidResponseException $e) {
 249+ $status->fatal( 'backend-fail-opentemp', $tmpPath );
 250+ return $status;
 251+ }
 252+
 253+ foreach ( $params['srcs'] as $virtualSource ) {
 254+ list( $sourcec, $source ) = $this->resolveStoragePath( $virtualSource );
 255+ if ( $source === null ) {
 256+ $status->fatal( 'backend-fail-invalidpath', $virtualSource );
 257+ return $status;
 258+ }
 259+ $srcc = $this->get_container( $conn, $sourcec );
 260+ $obj = $srcc->get_object( $source);
 261+ $biggie->write( $obj->read() );
 262+ }
 263+ return $status;
 264+ }
 265+
 266+ function create( array $params ) {
 267+ $status = Status::newGood();
 268+
 269+ list( $destc, $dest ) = $this->resolveStoragePath( $params['dst'] );
 270+ if ( $dest === null ) {
 271+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 272+ return $status;
 273+ }
 274+
 275+ $conn = $this->connect();
 276+ $dstc = $this->get_container( $conn, $destc );
 277+ try {
 278+ $objd = $dstc->get_object( $dest );
 279+ // if we are still here, it exists.
 280+ if ( empty( $params['overwriteDest'] ) ) {
 281+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
 282+ return $status;
 283+ }
 284+ $exists = true;
 285+ } catch (NoSuchObjectException $e) {
 286+ $exists = false;
 287+ }
 288+
 289+ $obj = $dstc->create_object( $dest );
 290+ //FIXME how do we know what the content type is?
 291+ $obj->content_type = 'text/plain';
 292+
 293+ try {
 294+ $obj->write( $params['content'] );
 295+ } catch (InvalidResponseException $e ) {
 296+ $status->fatal( 'backend-fail-create', $params['dst'] );
 297+ return $status;
 298+ }
 299+
 300+ return $status;
 301+ }
 302+
 303+ function prepare( array $params ) {
 304+ $status = Status::newGood();
 305+ return $status; // we good. we so good, we BAD.
 306+ }
 307+
 308+ function secure( array $params ) {
 309+ $status = Status::newGood();
 310+ return $status; // badgers? We don't need no steenking badgers!
 311+ }
 312+
 313+ function clean( array $params ) {
 314+ $status = Status::newGood();
 315+ return $status; // can't delete what doesn't exist.
 316+ }
 317+
 318+ function fileExists( array $params ) {
 319+ list( $sourcec, $source ) = $this->resolveStoragePath( $params['src'] );
 320+ if ( $source === null ) {
 321+ return false; // invalid storage path
 322+ }
 323+ $conn = $this->connect();
 324+ $container = $this->get_container( $conn, $sourcec );
 325+ try {
 326+ $obj = $container->get_object( $source );
 327+ $exists = true;
 328+ } catch ( NoSuchObjectException $e ) {
 329+ $exists = false;
 330+ }
 331+ return $exists;
 332+ }
 333+
 334+ function getFileTimestamp( array $params ) {
 335+ list( $sourcec, $source ) = $this->resolveStoragePath( $params['src'] );
 336+ if ( $source === null ) {
 337+ return false; // invalid storage path
 338+ }
 339+
 340+ $conn = $this->connect();
 341+ $container = $this->get_container( $conn, $sourcec);
 342+ try {
 343+ $obj = $container->get_object( $source );
 344+ } catch ( NoSuchObjectException $e ) {
 345+ $obj = NULL;
 346+ }
 347+ if ( $obj ) {
 348+ $thumbTime = $obj->last_modified;
 349+ $tm = strptime( $thumbTime, '%a, %d %b %Y %H:%M:%S GMT' );
 350+ $thumbGMT = gmmktime( $tm['tm_hour'], $tm['tm_min'], $tm['tm_sec'], $tm['tm_mon'] + 1, $tm['tm_mday'], $tm['tm_year'] + 1900 );
 351+ return ( gmdate( 'YmdHis', $thumbGMT ) );
 352+ } else {
 353+ return false; // file not found.
 354+ }
 355+ }
 356+
 357+ function getFileList( array $params ) {
 358+ list( $dirc, $dir ) = $this->resolveStoragePath( $params['dir'] );
 359+ if ( $dir === null ) { // invalid storage path
 360+ return array(); // empty result
 361+ }
 362+
 363+ $conn = $this->connect();
 364+ $container = $this->get_container( $conn, $dirc );
 365+ $files = $container->list_objects( 0, NULL, $dir);
 366+ // if there are no files matching the prefix, return empty array
 367+ return $files;
 368+ }
 369+
 370+ function getLocalReference( array $params ) {
 371+ list( $c, $source ) = $this->resolveStoragePath( $params['src'] );
 372+ if ( $source === null ) {
 373+ return null;
 374+ }
 375+ // FIXME!
 376+ return new FSFile( $source );
 377+ }
 378+
 379+ function getLocalCopy( array $params ) {
 380+ list( $sourcec, $source ) = $this->resolveStoragePath( $params['src'] );
 381+ if ( $source === null ) {
 382+ return null;
 383+ }
 384+
 385+ // Get source file extension
 386+ $i = strrpos( $source, '.' );
 387+ $ext = strtolower( $i ? substr( $source, $i + 1 ) : '' );
 388+ // Create a new temporary file...
 389+ $tmpFile = TempFSFile::factory( wfBaseName( $source ) . '_', $ext );
 390+ if ( !$tmpFile ) {
 391+ return null;
 392+ }
 393+ $tmpPath = $tmpFile->getPath();
 394+
 395+ $conn = $this->connect();
 396+ $cont = $this->get_container( $conn, $sourcec);
 397+
 398+ try {
 399+ $obj = $cont->get_object( $source );
 400+ } catch ( NoSuchObjectException $e ) {
 401+ throw new MWException( "Unable to open original file at", $params['src'] );
 402+ }
 403+
 404+ try {
 405+ $obj->save_to_filename( $tmpPath );
 406+ } catch ( IOException $e ) {
 407+ // throw new MWException( __METHOD__ . ": error opening '$e'" );
 408+ return null;
 409+ } catch ( InvalidResponseException $e ) {
 410+ // throw new MWException( __METHOD__ . "unexpected response '$e'" );
 411+ return null;
 412+ }
 413+ return $tmpFile;
 414+ }
 415+
 416+}

Status & tagging log