Overview
  • Namespace
  • Class

Namespaces

  • OpenCloud
    • Autoscale
      • Resource
    • CloudMonitoring
      • Collection
      • Exception
      • Resource
    • Common
      • Collection
      • Constants
      • Exceptions
      • Http
        • Message
      • Log
      • Resource
      • Service
    • Compute
      • Constants
      • Exception
      • Resource
    • Database
      • Resource
    • DNS
      • Collection
      • Resource
    • Identity
      • Constants
      • Resource
    • Image
      • Enum
      • Resource
        • JsonPatch
        • Schema
    • LoadBalancer
      • Collection
      • Enum
      • Resource
    • Networking
      • Resource
    • ObjectStore
      • Constants
      • Exception
      • Resource
      • Upload
    • Orchestration
      • Resource
    • Queues
      • Collection
      • Exception
      • Resource
    • Volume
      • Resource

Classes

  • OpenCloud\Volume\Resource\Snapshot
  • OpenCloud\Volume\Resource\Volume
  • OpenCloud\Volume\Resource\VolumeType
  1 <?php
  2 /**
  3  * Copyright 2012-2014 Rackspace US, Inc.
  4  *
  5  * Licensed under the Apache License, Version 2.0 (the "License");
  6  * you may not use this file except in compliance with the License.
  7  * You may obtain a copy of the License at
  8  *
  9  * http://www.apache.org/licenses/LICENSE-2.0
 10  *
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an "AS IS" BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  */
 17 
 18 namespace OpenCloud\ObjectStore\Upload;
 19 
 20 use DirectoryIterator;
 21 use Guzzle\Http\EntityBody;
 22 use OpenCloud\Common\Collection\ResourceIterator;
 23 use OpenCloud\Common\Exceptions\InvalidArgumentError;
 24 use OpenCloud\ObjectStore\Resource\Container;
 25 
 26 /**
 27  * DirectorySync upload class, in charge of creating, replacing and delete data objects on the API. The goal of
 28  * this execution is to sync local directories with remote CloudFiles containers so that they are consistent.
 29  *
 30  * @package OpenCloud\ObjectStore\Upload
 31  */
 32 class DirectorySync
 33 {
 34     /**
 35      * @var string The path to the directory you're syncing.
 36      */
 37     private $basePath;
 38     /**
 39      * @var ResourceIterator A collection of remote files in Swift.
 40      */
 41     private $remoteFiles;
 42     /**
 43      * @var AbstractContainer The Container object you are syncing.
 44      */
 45     private $container;
 46 
 47     /**
 48      * Basic factory method to instantiate a new DirectorySync object with all the appropriate properties.
 49      *
 50      * @param           $path      The local path
 51      * @param Container $container The container you're syncing
 52      * @return DirectorySync
 53      */
 54     public static function factory($path, Container $container)
 55     {
 56         $transfer = new self();
 57         $transfer->setBasePath($path);
 58         $transfer->setContainer($container);
 59         $transfer->setRemoteFiles($container->objectList());
 60 
 61         return $transfer;
 62     }
 63 
 64     /**
 65      * @param $path
 66      * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
 67      */
 68     public function setBasePath($path)
 69     {
 70         if (!file_exists($path)) {
 71             throw new InvalidArgumentError(sprintf('%s does not exist', $path));
 72         }
 73 
 74         $this->basePath = $path;
 75     }
 76 
 77     /**
 78      * @param ResourceIterator $remoteFiles
 79      */
 80     public function setRemoteFiles(ResourceIterator $remoteFiles)
 81     {
 82         $this->remoteFiles = $remoteFiles;
 83     }
 84 
 85     /**
 86      * @param Container $container
 87      */
 88     public function setContainer(Container $container)
 89     {
 90         $this->container = $container;
 91     }
 92 
 93     /**
 94      * Execute the sync process. This will collect all the remote files from the API and do a comparison. There are
 95      * four scenarios that need to be dealt with:
 96      *
 97      * - Exists locally, exists remotely (identical checksum) = no action
 98      * - Exists locally, exists remotely (diff checksum) = local overwrites remote
 99      * - Exists locally, not exists remotely = local is written to remote
100      * - Not exists locally, exists remotely = remote file is deleted
101      */
102     public function execute()
103     {
104         $localFiles = $this->traversePath($this->basePath);
105 
106         $this->remoteFiles->rewind();
107         $this->remoteFiles->populateAll();
108 
109         $entities = array();
110         $requests = array();
111         $deletePaths = array();
112 
113         // Handle PUT requests (create/update files)
114         foreach ($localFiles as $filename) {
115             $callback = $this->getCallback($filename);
116             $filePath = rtrim($this->basePath, '/') . '/' . $filename;
117 
118             if (!is_readable($filePath)) {
119                 continue;
120             }
121 
122             $entities[] = $entityBody = EntityBody::factory(fopen($filePath, 'r+'));
123 
124             if (false !== ($remoteFile = $this->remoteFiles->search($callback))) {
125                 // if different, upload updated version
126                 if ($remoteFile->getEtag() != $entityBody->getContentMd5()) {
127                     $requests[] = $this->container->getClient()->put(
128                         $remoteFile->getUrl(),
129                         $remoteFile->getMetadata()->toArray(),
130                         $entityBody
131                     );
132                 }
133             } else {
134                 // upload new file
135                 $url = clone $this->container->getUrl();
136                 $url->addPath($filename);
137 
138                 $requests[] = $this->container->getClient()->put($url, array(), $entityBody);
139             }
140         }
141 
142         // Handle DELETE requests
143         foreach ($this->remoteFiles as $remoteFile) {
144             $remoteName = $remoteFile->getName();
145             if (!in_array($remoteName, $localFiles)) {
146                 $deletePaths[] = sprintf('/%s/%s', $this->container->getName(), $remoteName);
147             }
148         }
149 
150         // send update/create requests
151         if (count($requests)) {
152             $this->container->getClient()->send($requests);
153         }
154 
155         // bulk delete
156         if (count($deletePaths)) {
157             $this->container->getService()->bulkDelete($deletePaths);
158         }
159 
160         // close all streams
161         if (count($entities)) {
162             foreach ($entities as $entity) {
163                 $entity->close();
164             }
165         }
166     }
167 
168     /**
169      * Given a path, traverse it recursively for nested files.
170      *
171      * @param $path
172      * @return array
173      */
174     private function traversePath($path)
175     {
176         $filenames = array();
177 
178         $directory = new DirectoryIterator($path);
179 
180         foreach ($directory as $file) {
181             if ($file->isDot()) {
182                 continue;
183             }
184             if ($file->isDir()) {
185                 $filenames = array_merge($filenames, $this->traversePath($file->getPathname()));
186             } else {
187                 $filenames[] = $this->trimFilename($file);
188             }
189         }
190 
191         return $filenames;
192     }
193 
194     /**
195      * Given a path, trim away leading slashes and strip the base path.
196      *
197      * @param $file
198      * @return string
199      */
200     private function trimFilename($file)
201     {
202         return ltrim(str_replace($this->basePath, '', $file->getPathname()), '/');
203     }
204 
205     /**
206      * Get the callback used to do a search function on the remote iterator.
207      *
208      * @param $name     The name of the file we're looking for.
209      * @return callable
210      */
211     private function getCallback($name)
212     {
213         $name = trim($name, '/');
214 
215         return function ($remoteFile) use ($name) {
216             if ($remoteFile->getName() == $name) {
217                 return true;
218             }
219 
220             return false;
221         };
222     }
223 }
224 
API documentation generated by ApiGen