Mercurial > hg > orthanc-object-storage
annotate Aws/AwsS3StoragePlugin.cpp @ 15:2a02b21f0a19
migration + storage structure
author | Alain Mazy |
---|---|
date | Tue, 01 Sep 2020 13:08:49 +0200 |
parents | 393fcf337462 |
children | e1f52b851827 8d2b29fd4de5 |
rev | line source |
---|---|
1 | 1 /** |
2 * Cloud storage plugins for Orthanc | |
3 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
4 * | |
5 * This program is free software: you can redistribute it and/or | |
6 * modify it under the terms of the GNU Affero General Public License | |
7 * as published by the Free Software Foundation, either version 3 of | |
8 * the License, or (at your option) any later version. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, but | |
11 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Affero General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Affero General Public License | |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 **/ | |
18 | |
19 #include "AwsS3StoragePlugin.h" | |
20 | |
21 #include <aws/core/Aws.h> | |
22 #include <aws/s3/S3Client.h> | |
23 #include <aws/s3/model/PutObjectRequest.h> | |
24 #include <aws/s3/model/GetObjectRequest.h> | |
25 #include <aws/s3/model/ListObjectsRequest.h> | |
26 #include <aws/s3/model/DeleteObjectRequest.h> | |
27 #include <aws/core/auth/AWSCredentialsProvider.h> | |
28 #include <aws/core/utils/memory/stl/AWSStringStream.h> | |
29 | |
30 #include <boost/lexical_cast.hpp> | |
31 #include <iostream> | |
32 #include <fstream> | |
33 | |
34 const char* ALLOCATION_TAG = "OrthancS3"; | |
15 | 35 static const char* const PLUGIN_SECTION = "AwsS3Storage"; |
1 | 36 |
15 | 37 class AwsS3StoragePlugin : public BaseStoragePlugin |
1 | 38 { |
39 public: | |
40 | |
41 Aws::S3::S3Client client_; | |
42 std::string bucketName_; | |
43 | |
44 public: | |
45 | |
15 | 46 AwsS3StoragePlugin(const Aws::S3::S3Client& client, const std::string& bucketName, bool enableLegacyStorageStructure); |
1 | 47 |
48 virtual IWriter* GetWriterForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); | |
49 virtual IReader* GetReaderForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); | |
50 virtual void DeleteObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); | |
15 | 51 virtual const char* GetConfigurationSectionName() {return PLUGIN_SECTION;}; |
1 | 52 }; |
53 | |
54 | |
55 class Writer : public IStoragePlugin::IWriter | |
56 { | |
57 std::string path_; | |
58 Aws::S3::S3Client client_; | |
59 std::string bucketName_; | |
60 | |
61 public: | |
62 Writer(const Aws::S3::S3Client& client, const std::string& bucketName, const std::string& path) | |
63 : path_(path), | |
64 client_(client), | |
65 bucketName_(bucketName) | |
66 { | |
67 } | |
68 | |
69 virtual ~Writer() | |
70 { | |
71 } | |
72 | |
73 virtual void Write(const char* data, size_t size) | |
74 { | |
75 Aws::S3::Model::PutObjectRequest putObjectRequest; | |
76 | |
77 putObjectRequest.SetBucket(bucketName_.c_str()); | |
78 putObjectRequest.SetKey(path_.c_str()); | |
79 | |
80 std::shared_ptr<Aws::StringStream> stream = Aws::MakeShared<Aws::StringStream>(ALLOCATION_TAG, std::ios_base::in | std::ios_base::binary); | |
81 | |
82 stream->rdbuf()->pubsetbuf(const_cast<char*>(data), size); | |
83 stream->rdbuf()->pubseekpos(size); | |
84 stream->seekg(0); | |
85 | |
86 putObjectRequest.SetBody(stream); | |
87 | |
88 auto result = client_.PutObject(putObjectRequest); | |
89 | |
90 if (!result.IsSuccess()) | |
91 { | |
92 throw StoragePluginException(std::string("error while writing file ") + path_ + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); | |
93 } | |
94 } | |
95 }; | |
96 | |
97 | |
98 class Reader : public IStoragePlugin::IReader | |
99 { | |
100 std::string path_; | |
101 Aws::S3::S3Client client_; | |
102 std::string bucketName_; | |
103 | |
104 public: | |
105 Reader(const Aws::S3::S3Client& client, const std::string& bucketName, const std::string& path) | |
106 : path_(path), | |
107 client_(client), | |
108 bucketName_(bucketName) | |
109 { | |
110 } | |
111 | |
112 virtual ~Reader() | |
113 { | |
114 | |
115 } | |
116 virtual size_t GetSize() | |
117 { | |
118 Aws::S3::Model::ListObjectsRequest listObjectRequest; | |
119 listObjectRequest.SetBucket(bucketName_.c_str()); | |
120 listObjectRequest.SetPrefix(path_.c_str()); | |
121 | |
122 auto result = client_.ListObjects(listObjectRequest); | |
123 | |
124 if (result.IsSuccess()) | |
125 { | |
126 Aws::Vector<Aws::S3::Model::Object> objectList = | |
127 result.GetResult().GetContents(); | |
128 | |
129 if (objectList.size() == 1) | |
130 { | |
131 return objectList[0].GetSize(); | |
132 } | |
133 throw StoragePluginException(std::string("error while reading file ") + path_ + ": multiple objet with same name !"); | |
134 } | |
135 else | |
136 { | |
137 throw StoragePluginException(std::string("error while reading file ") + path_ + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); | |
138 } | |
139 } | |
140 | |
141 virtual void Read(char* data, size_t size) | |
142 { | |
143 Aws::S3::Model::GetObjectRequest getObjectRequest; | |
144 getObjectRequest.SetBucket(bucketName_.c_str()); | |
145 getObjectRequest.SetKey(path_.c_str()); | |
146 | |
147 getObjectRequest.SetResponseStreamFactory( | |
148 [data, size]() | |
149 { | |
150 std::unique_ptr<Aws::StringStream> | |
151 istream(Aws::New<Aws::StringStream>(ALLOCATION_TAG)); | |
152 | |
153 istream->rdbuf()->pubsetbuf(static_cast<char*>(data), | |
154 size); | |
155 | |
156 return istream.release(); | |
157 }); | |
158 | |
159 // Get the object | |
160 auto result = client_.GetObject(getObjectRequest); | |
161 if (result.IsSuccess()) | |
162 { | |
163 } | |
164 else | |
165 { | |
166 throw StoragePluginException(std::string("error while reading file ") + path_ + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); | |
167 } | |
168 } | |
169 | |
170 }; | |
171 | |
172 | |
173 | |
174 const char* AwsS3StoragePluginFactory::GetStoragePluginName() | |
175 { | |
176 return "AWS S3 Storage"; | |
177 } | |
178 | |
179 IStoragePlugin* AwsS3StoragePluginFactory::CreateStoragePlugin(const OrthancPlugins::OrthancConfiguration& orthancConfig) | |
180 { | |
15 | 181 bool enableLegacyStorageStructure; |
182 | |
1 | 183 if (!orthancConfig.IsSection(PLUGIN_SECTION)) |
184 { | |
185 OrthancPlugins::LogWarning(std::string(GetStoragePluginName()) + " plugin, section missing. Plugin is not enabled."); | |
186 return nullptr; | |
187 } | |
188 | |
189 OrthancPlugins::OrthancConfiguration pluginSection; | |
190 orthancConfig.GetSection(pluginSection, PLUGIN_SECTION); | |
191 | |
15 | 192 if (!BaseStoragePlugin::ReadCommonConfiguration(enableLegacyStorageStructure, pluginSection)) |
193 { | |
194 return nullptr; | |
195 } | |
196 | |
1 | 197 std::string bucketName; |
198 std::string region; | |
199 std::string accessKey; | |
200 std::string secretKey; | |
201 | |
202 if (!pluginSection.LookupStringValue(bucketName, "BucketName")) | |
203 { | |
204 OrthancPlugins::LogError("AwsS3Storage/BucketName configuration missing. Unable to initialize plugin"); | |
205 return nullptr; | |
206 } | |
207 | |
208 if (!pluginSection.LookupStringValue(region, "Region")) | |
209 { | |
210 OrthancPlugins::LogError("AwsS3Storage/Region configuration missing. Unable to initialize plugin"); | |
211 return nullptr; | |
212 } | |
213 | |
214 if (!pluginSection.LookupStringValue(accessKey, "AccessKey")) | |
215 { | |
216 OrthancPlugins::LogError("AwsS3Storage/AccessKey configuration missing. Unable to initialize plugin"); | |
217 return nullptr; | |
218 } | |
219 | |
220 if (!pluginSection.LookupStringValue(secretKey, "SecretKey")) | |
221 { | |
222 OrthancPlugins::LogError("AwsS3Storage/SecretKey configuration missing. Unable to initialize plugin"); | |
223 return nullptr; | |
224 } | |
225 | |
6
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
226 std::string endpoint = pluginSection.GetStringValue("Endpoint", ""); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
227 unsigned int connectTimeout = pluginSection.GetUnsignedIntegerValue("ConnectTimeout", 30); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
228 unsigned int requestTimeout = pluginSection.GetUnsignedIntegerValue("RequestTimeout", 1200); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
229 |
1 | 230 try |
231 { | |
232 Aws::SDKOptions options; | |
233 Aws::InitAPI(options); | |
234 | |
235 Aws::Auth::AWSCredentials credentials(accessKey.c_str(), secretKey.c_str()); | |
236 Aws::Client::ClientConfiguration configuration; | |
237 configuration.region = region.c_str(); | |
6
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
238 configuration.scheme = Aws::Http::Scheme::HTTPS; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
239 configuration.connectTimeoutMs = connectTimeout * 1000; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
240 configuration.requestTimeoutMs = requestTimeout * 1000; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
241 |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
242 if (!endpoint.empty()) |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
243 { |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
244 configuration.endpointOverride = endpoint.c_str(); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
245 } |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
246 |
1 | 247 Aws::S3::S3Client client(credentials, configuration); |
248 | |
249 OrthancPlugins::LogInfo("AWS S3 storage initialized"); | |
250 | |
15 | 251 return new AwsS3StoragePlugin(client, bucketName, enableLegacyStorageStructure); |
1 | 252 } |
253 catch (const std::exception& e) | |
254 { | |
255 OrthancPlugins::LogError(std::string("AzureBlobStorage plugin: failed to initialize plugin: ") + e.what()); | |
256 return nullptr; | |
257 } | |
258 | |
259 } | |
260 | |
15 | 261 AwsS3StoragePlugin::AwsS3StoragePlugin(const Aws::S3::S3Client& client, const std::string& bucketName, bool enableLegacyStorageStructure) |
262 : BaseStoragePlugin(enableLegacyStorageStructure), | |
263 client_(client), | |
1 | 264 bucketName_(bucketName) |
265 { | |
266 | |
267 } | |
268 | |
269 IStoragePlugin::IWriter* AwsS3StoragePlugin::GetWriterForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
270 { | |
271 return new Writer(client_, bucketName_, GetPath(uuid, type, encryptionEnabled)); | |
272 } | |
273 | |
274 IStoragePlugin::IReader* AwsS3StoragePlugin::GetReaderForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
275 { | |
276 return new Reader(client_, bucketName_, GetPath(uuid, type, encryptionEnabled)); | |
277 } | |
278 | |
279 void AwsS3StoragePlugin::DeleteObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
280 { | |
281 std::string path = GetPath(uuid, type, encryptionEnabled); | |
282 | |
283 Aws::S3::Model::DeleteObjectRequest deleteObjectRequest; | |
284 deleteObjectRequest.SetBucket(bucketName_.c_str()); | |
285 deleteObjectRequest.SetKey(path.c_str()); | |
286 | |
287 auto result = client_.DeleteObject(deleteObjectRequest); | |
288 | |
289 if (!result.IsSuccess()) | |
290 { | |
291 throw StoragePluginException(std::string("error while deleting file ") + path + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); | |
292 } | |
293 | |
294 } |