From a325c0147dc1c759d6bf5f0eae76cbca585fcb2b Mon Sep 17 00:00:00 2001 From: Dennis Grosch Date: Thu, 7 Jul 2022 21:36:06 +0200 Subject: [PATCH 1/2] Added MinIO Storage adapter --- src/Storage/Device/MinIO.php | 119 +++++++++++++++++++++++++++++ src/Storage/Device/S3.php | 5 +- src/Storage/Storage.php | 1 + tests/Storage/Device/MinIOTest.php | 32 ++++++++ 4 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 src/Storage/Device/MinIO.php create mode 100644 tests/Storage/Device/MinIOTest.php diff --git a/src/Storage/Device/MinIO.php b/src/Storage/Device/MinIO.php new file mode 100644 index 00000000..538f1162 --- /dev/null +++ b/src/Storage/Device/MinIO.php @@ -0,0 +1,119 @@ +protocol = $protocol; + $this->headers['host'] = $host; + } + + /** + * Get list of objects in the given path. + * + * @param string $path + * + * @throws \Exception + * + * @return array + */ + public function listObjects($prefix = '', $maxKeys = 1000, $continuationToken = '') + { + $uri = '/' . $this->getRoot(); + $this->headers['content-type'] = 'text/plain'; + $this->headers['content-md5'] = \base64_encode(md5('', true)); + + $parameters = [ + 'list-type' => 2, + 'prefix' => $prefix, + 'max-keys' => $maxKeys, + ]; + if(!empty($continuationToken)) { + $parameters['continuation-token'] = $continuationToken; + } + $response = parent::call(self::METHOD_GET, $uri, '', $parameters); + return $response->body; + } + + /** + * Delete files in given path, path must be a directory. Return true on success and false on failure. + * + * @param string $path + * + * @throws \Exception + * + * @return bool + */ + public function deletePath(string $path): bool + { + $uri = '/' . $this->getRoot(); + $continuationToken = ''; + do { + $objects = $this->listObjects($path, continuationToken: $continuationToken); + $count = (int) ($objects['KeyCount'] ?? 1); + if($count < 1) { + break; + } + $continuationToken = $objects['NextContinuationToken'] ?? ''; + $body = ''; + + if($count > 1) { + foreach ($objects['Contents'] as $object) { + $body .= "{$object['Key']}"; + } + } else { + $body .= "{$objects['Contents']['Key']}"; + } + $body .= 'true'; + $body .= ''; + $this->amzHeaders['x-amz-content-sha256'] = \hash('sha256', $body); + $this->headers['content-md5'] = \base64_encode(md5($body, true)); + parent::call(self::METHOD_POST, $uri, $body, ['delete'=>'']); + } while(!empty($continuationToken)); + + return true; + } + + /** + * @return string + */ + public function getName(): string + { + return 'MinIO Object Storage'; + } + + /** + * @return string + */ + public function getDescription(): string + { + return 'MinIO Object Storage'; + } +} \ No newline at end of file diff --git a/src/Storage/Device/S3.php b/src/Storage/Device/S3.php index 13080282..aecb45aa 100644 --- a/src/Storage/Device/S3.php +++ b/src/Storage/Device/S3.php @@ -116,6 +116,7 @@ public function __construct(string $root, string $accessKey, string $secretKey, $this->acl = $acl; $this->headers['host'] = $this->bucket . '.s3.'.$this->region.'.amazonaws.com'; $this->amzHeaders = []; + $this->protocol = 'https'; } /** @@ -659,9 +660,9 @@ private function getSignatureV4(string $method, string $uri, array $parameters = * * @return object */ - private function call(string $method, string $uri, string $data = '', array $parameters=[]) + public function call(string $method, string $uri, string $data = '', array $parameters=[]) { - $url = 'https://' . $this->headers['host'] . $uri . '?' . \http_build_query($parameters, '', '&', PHP_QUERY_RFC3986); + $url = $this->protocol . '://' . $this->headers['host'] . $uri . '?' . \http_build_query($parameters, '', '&', PHP_QUERY_RFC3986); $response = new \stdClass; $response->body = ''; $response->headers = []; diff --git a/src/Storage/Storage.php b/src/Storage/Storage.php index 4e075305..cd0de0e7 100644 --- a/src/Storage/Storage.php +++ b/src/Storage/Storage.php @@ -16,6 +16,7 @@ class Storage const DEVICE_WASABI = 'Wasabi'; const DEVICE_BACKBLAZE = 'Backblaze'; const DEVICE_LINODE= 'Linode'; + const DEVICE_MINIO= 'MinIO'; /** * Devices. diff --git a/tests/Storage/Device/MinIOTest.php b/tests/Storage/Device/MinIOTest.php new file mode 100644 index 00000000..bc5b5615 --- /dev/null +++ b/tests/Storage/Device/MinIOTest.php @@ -0,0 +1,32 @@ +root = 'minio-test-bucket'; + $key = $_SERVER['MINIO_ACCESS_KEY'] ?? ''; + $secret = $_SERVER['MINIO_SECRET'] ?? ''; + $protocol = $_SERVER['MINIO_PROTOCOL'] ?? ''; + $host = $_SERVER['MINIO_HOST'] ?? ''; + $bucket = 'minio-test-bucket'; + + $this->object = new MinIO($this->root, $key, $secret, $protocol, $host, $bucket, MinIO::EU_CENTRAL_1); + + } + + protected function getAdapterName(): string + { + return 'MinIO Object Storage'; + } + + protected function getAdapterDescription(): string + { + return 'MinIO Object Storage'; + } +} From 28c19774a63e916533d3b8949bb2b21c508d99b4 Mon Sep 17 00:00:00 2001 From: Dennis Grosch Date: Thu, 29 Sep 2022 14:10:01 +0200 Subject: [PATCH 2/2] change method accessibility --- src/Storage/Device/MinIO.php | 2 +- src/Storage/Device/S3.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Storage/Device/MinIO.php b/src/Storage/Device/MinIO.php index 538f1162..569a5320 100644 --- a/src/Storage/Device/MinIO.php +++ b/src/Storage/Device/MinIO.php @@ -44,7 +44,7 @@ public function __construct(string $root, string $accessKey, string $secretKey, * * @return array */ - public function listObjects($prefix = '', $maxKeys = 1000, $continuationToken = '') + private function listObjects($prefix = '', $maxKeys = 1000, $continuationToken = '') { $uri = '/' . $this->getRoot(); $this->headers['content-type'] = 'text/plain'; diff --git a/src/Storage/Device/S3.php b/src/Storage/Device/S3.php index aecb45aa..4477a09c 100644 --- a/src/Storage/Device/S3.php +++ b/src/Storage/Device/S3.php @@ -660,7 +660,7 @@ private function getSignatureV4(string $method, string $uri, array $parameters = * * @return object */ - public function call(string $method, string $uri, string $data = '', array $parameters=[]) + protected function call(string $method, string $uri, string $data = '', array $parameters=[]) { $url = $this->protocol . '://' . $this->headers['host'] . $uri . '?' . \http_build_query($parameters, '', '&', PHP_QUERY_RFC3986); $response = new \stdClass;