Mercurial > hg > orthanc-object-storage
annotate Aws/AwsS3StoragePlugin.cpp @ 11:1b85dc5eb372
more logs for azure plugin
author | Alain Mazy |
---|---|
date | Wed, 26 Aug 2020 11:15:14 +0200 |
parents | 393fcf337462 |
children | 2a02b21f0a19 |
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"; | |
35 | |
36 class AwsS3StoragePlugin : public IStoragePlugin | |
37 { | |
38 public: | |
39 | |
40 Aws::S3::S3Client client_; | |
41 std::string bucketName_; | |
42 | |
43 public: | |
44 | |
45 AwsS3StoragePlugin(const Aws::S3::S3Client& client, const std::string& bucketName); | |
46 | |
47 virtual IWriter* GetWriterForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); | |
48 virtual IReader* GetReaderForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); | |
49 virtual void DeleteObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); | |
50 private: | |
51 virtual std::string GetPath(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled); | |
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 { | |
181 static const char* const PLUGIN_SECTION = "AwsS3Storage"; | |
182 if (!orthancConfig.IsSection(PLUGIN_SECTION)) | |
183 { | |
184 OrthancPlugins::LogWarning(std::string(GetStoragePluginName()) + " plugin, section missing. Plugin is not enabled."); | |
185 return nullptr; | |
186 } | |
187 | |
188 OrthancPlugins::OrthancConfiguration pluginSection; | |
189 orthancConfig.GetSection(pluginSection, PLUGIN_SECTION); | |
190 | |
191 std::string bucketName; | |
192 std::string region; | |
193 std::string accessKey; | |
194 std::string secretKey; | |
195 | |
196 if (!pluginSection.LookupStringValue(bucketName, "BucketName")) | |
197 { | |
198 OrthancPlugins::LogError("AwsS3Storage/BucketName configuration missing. Unable to initialize plugin"); | |
199 return nullptr; | |
200 } | |
201 | |
202 if (!pluginSection.LookupStringValue(region, "Region")) | |
203 { | |
204 OrthancPlugins::LogError("AwsS3Storage/Region configuration missing. Unable to initialize plugin"); | |
205 return nullptr; | |
206 } | |
207 | |
208 if (!pluginSection.LookupStringValue(accessKey, "AccessKey")) | |
209 { | |
210 OrthancPlugins::LogError("AwsS3Storage/AccessKey configuration missing. Unable to initialize plugin"); | |
211 return nullptr; | |
212 } | |
213 | |
214 if (!pluginSection.LookupStringValue(secretKey, "SecretKey")) | |
215 { | |
216 OrthancPlugins::LogError("AwsS3Storage/SecretKey configuration missing. Unable to initialize plugin"); | |
217 return nullptr; | |
218 } | |
219 | |
6
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
220 std::string endpoint = pluginSection.GetStringValue("Endpoint", ""); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
221 unsigned int connectTimeout = pluginSection.GetUnsignedIntegerValue("ConnectTimeout", 30); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
222 unsigned int requestTimeout = pluginSection.GetUnsignedIntegerValue("RequestTimeout", 1200); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
223 |
1 | 224 try |
225 { | |
226 Aws::SDKOptions options; | |
227 Aws::InitAPI(options); | |
228 | |
229 Aws::Auth::AWSCredentials credentials(accessKey.c_str(), secretKey.c_str()); | |
230 Aws::Client::ClientConfiguration configuration; | |
231 configuration.region = region.c_str(); | |
6
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
232 configuration.scheme = Aws::Http::Scheme::HTTPS; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
233 configuration.connectTimeoutMs = connectTimeout * 1000; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
234 configuration.requestTimeoutMs = requestTimeout * 1000; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
235 |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
236 if (!endpoint.empty()) |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
237 { |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
238 configuration.endpointOverride = endpoint.c_str(); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
239 } |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
240 |
1 | 241 Aws::S3::S3Client client(credentials, configuration); |
242 | |
243 OrthancPlugins::LogInfo("AWS S3 storage initialized"); | |
244 | |
245 return new AwsS3StoragePlugin(client, bucketName); | |
246 } | |
247 catch (const std::exception& e) | |
248 { | |
249 OrthancPlugins::LogError(std::string("AzureBlobStorage plugin: failed to initialize plugin: ") + e.what()); | |
250 return nullptr; | |
251 } | |
252 | |
253 } | |
254 | |
255 AwsS3StoragePlugin::AwsS3StoragePlugin(const Aws::S3::S3Client& client, const std::string& bucketName) | |
256 : client_(client), | |
257 bucketName_(bucketName) | |
258 { | |
259 | |
260 } | |
261 | |
262 IStoragePlugin::IWriter* AwsS3StoragePlugin::GetWriterForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
263 { | |
264 return new Writer(client_, bucketName_, GetPath(uuid, type, encryptionEnabled)); | |
265 } | |
266 | |
267 IStoragePlugin::IReader* AwsS3StoragePlugin::GetReaderForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
268 { | |
269 return new Reader(client_, bucketName_, GetPath(uuid, type, encryptionEnabled)); | |
270 } | |
271 | |
272 void AwsS3StoragePlugin::DeleteObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
273 { | |
274 std::string path = GetPath(uuid, type, encryptionEnabled); | |
275 | |
276 Aws::S3::Model::DeleteObjectRequest deleteObjectRequest; | |
277 deleteObjectRequest.SetBucket(bucketName_.c_str()); | |
278 deleteObjectRequest.SetKey(path.c_str()); | |
279 | |
280 auto result = client_.DeleteObject(deleteObjectRequest); | |
281 | |
282 if (!result.IsSuccess()) | |
283 { | |
284 throw StoragePluginException(std::string("error while deleting file ") + path + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); | |
285 } | |
286 | |
287 } | |
288 | |
289 std::string AwsS3StoragePlugin::GetPath(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
290 { | |
291 std::string path = std::string(uuid); | |
292 | |
293 if (type == OrthancPluginContentType_Dicom) | |
294 { | |
295 path += ".dcm"; | |
296 } | |
297 else if (type == OrthancPluginContentType_DicomAsJson) | |
298 { | |
299 path += ".json"; | |
300 } | |
301 else | |
302 { | |
303 path += ".unk"; | |
304 } | |
305 | |
306 if (encryptionEnabled) | |
307 { | |
308 path += ".enc"; | |
309 } | |
310 return path; | |
311 } |