Mercurial > hg > orthanc-object-storage
annotate Aws/AwsS3StoragePlugin.cpp @ 32:8d2b29fd4de5
cont
author | Alain Mazy |
---|---|
date | Fri, 09 Oct 2020 10:16:50 +0200 |
parents | 2a02b21f0a19 |
children | 70da4ce5c7cc |
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 } | |
32 | 133 else if (objectList.size() > 1) |
134 { | |
135 throw StoragePluginException(std::string("error while reading file ") + path_ + ": multiple objet with same name !"); | |
136 } | |
137 throw StoragePluginException(std::string("error while reading file ") + path_ + ": object not found !"); | |
1 | 138 } |
139 else | |
140 { | |
141 throw StoragePluginException(std::string("error while reading file ") + path_ + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); | |
142 } | |
143 } | |
144 virtual void Read(char* data, size_t size) | |
145 { | |
146 Aws::S3::Model::GetObjectRequest getObjectRequest; | |
147 getObjectRequest.SetBucket(bucketName_.c_str()); | |
148 getObjectRequest.SetKey(path_.c_str()); | |
149 | |
150 getObjectRequest.SetResponseStreamFactory( | |
151 [data, size]() | |
152 { | |
153 std::unique_ptr<Aws::StringStream> | |
154 istream(Aws::New<Aws::StringStream>(ALLOCATION_TAG)); | |
155 | |
156 istream->rdbuf()->pubsetbuf(static_cast<char*>(data), | |
157 size); | |
158 | |
159 return istream.release(); | |
160 }); | |
161 | |
162 // Get the object | |
163 auto result = client_.GetObject(getObjectRequest); | |
164 if (result.IsSuccess()) | |
165 { | |
166 } | |
167 else | |
168 { | |
169 throw StoragePluginException(std::string("error while reading file ") + path_ + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); | |
170 } | |
171 } | |
172 | |
173 }; | |
174 | |
175 | |
176 | |
177 const char* AwsS3StoragePluginFactory::GetStoragePluginName() | |
178 { | |
179 return "AWS S3 Storage"; | |
180 } | |
181 | |
182 IStoragePlugin* AwsS3StoragePluginFactory::CreateStoragePlugin(const OrthancPlugins::OrthancConfiguration& orthancConfig) | |
183 { | |
15 | 184 bool enableLegacyStorageStructure; |
185 | |
1 | 186 if (!orthancConfig.IsSection(PLUGIN_SECTION)) |
187 { | |
188 OrthancPlugins::LogWarning(std::string(GetStoragePluginName()) + " plugin, section missing. Plugin is not enabled."); | |
189 return nullptr; | |
190 } | |
191 | |
192 OrthancPlugins::OrthancConfiguration pluginSection; | |
193 orthancConfig.GetSection(pluginSection, PLUGIN_SECTION); | |
194 | |
15 | 195 if (!BaseStoragePlugin::ReadCommonConfiguration(enableLegacyStorageStructure, pluginSection)) |
196 { | |
197 return nullptr; | |
198 } | |
199 | |
1 | 200 std::string bucketName; |
201 std::string region; | |
202 std::string accessKey; | |
203 std::string secretKey; | |
204 | |
205 if (!pluginSection.LookupStringValue(bucketName, "BucketName")) | |
206 { | |
207 OrthancPlugins::LogError("AwsS3Storage/BucketName configuration missing. Unable to initialize plugin"); | |
208 return nullptr; | |
209 } | |
210 | |
211 if (!pluginSection.LookupStringValue(region, "Region")) | |
212 { | |
213 OrthancPlugins::LogError("AwsS3Storage/Region configuration missing. Unable to initialize plugin"); | |
214 return nullptr; | |
215 } | |
216 | |
217 if (!pluginSection.LookupStringValue(accessKey, "AccessKey")) | |
218 { | |
219 OrthancPlugins::LogError("AwsS3Storage/AccessKey configuration missing. Unable to initialize plugin"); | |
220 return nullptr; | |
221 } | |
222 | |
223 if (!pluginSection.LookupStringValue(secretKey, "SecretKey")) | |
224 { | |
225 OrthancPlugins::LogError("AwsS3Storage/SecretKey configuration missing. Unable to initialize plugin"); | |
226 return nullptr; | |
227 } | |
228 | |
6
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
229 std::string endpoint = pluginSection.GetStringValue("Endpoint", ""); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
230 unsigned int connectTimeout = pluginSection.GetUnsignedIntegerValue("ConnectTimeout", 30); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
231 unsigned int requestTimeout = pluginSection.GetUnsignedIntegerValue("RequestTimeout", 1200); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
232 |
1 | 233 try |
234 { | |
235 Aws::SDKOptions options; | |
236 Aws::InitAPI(options); | |
237 | |
238 Aws::Auth::AWSCredentials credentials(accessKey.c_str(), secretKey.c_str()); | |
239 Aws::Client::ClientConfiguration configuration; | |
240 configuration.region = region.c_str(); | |
6
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
241 configuration.scheme = Aws::Http::Scheme::HTTPS; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
242 configuration.connectTimeoutMs = connectTimeout * 1000; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
243 configuration.requestTimeoutMs = requestTimeout * 1000; |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
244 |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
245 if (!endpoint.empty()) |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
246 { |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
247 configuration.endpointOverride = endpoint.c_str(); |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
248 } |
393fcf337462
AWS: added 3 configurations: Endpoint, ConnectionTimeout, RequestTimeout
Alain Mazy
parents:
1
diff
changeset
|
249 |
1 | 250 Aws::S3::S3Client client(credentials, configuration); |
251 | |
252 OrthancPlugins::LogInfo("AWS S3 storage initialized"); | |
253 | |
15 | 254 return new AwsS3StoragePlugin(client, bucketName, enableLegacyStorageStructure); |
1 | 255 } |
256 catch (const std::exception& e) | |
257 { | |
258 OrthancPlugins::LogError(std::string("AzureBlobStorage plugin: failed to initialize plugin: ") + e.what()); | |
259 return nullptr; | |
260 } | |
261 | |
262 } | |
263 | |
15 | 264 AwsS3StoragePlugin::AwsS3StoragePlugin(const Aws::S3::S3Client& client, const std::string& bucketName, bool enableLegacyStorageStructure) |
265 : BaseStoragePlugin(enableLegacyStorageStructure), | |
266 client_(client), | |
1 | 267 bucketName_(bucketName) |
268 { | |
269 | |
270 } | |
271 | |
272 IStoragePlugin::IWriter* AwsS3StoragePlugin::GetWriterForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
273 { | |
274 return new Writer(client_, bucketName_, GetPath(uuid, type, encryptionEnabled)); | |
275 } | |
276 | |
277 IStoragePlugin::IReader* AwsS3StoragePlugin::GetReaderForObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
278 { | |
279 return new Reader(client_, bucketName_, GetPath(uuid, type, encryptionEnabled)); | |
280 } | |
281 | |
282 void AwsS3StoragePlugin::DeleteObject(const char* uuid, OrthancPluginContentType type, bool encryptionEnabled) | |
283 { | |
284 std::string path = GetPath(uuid, type, encryptionEnabled); | |
285 | |
286 Aws::S3::Model::DeleteObjectRequest deleteObjectRequest; | |
287 deleteObjectRequest.SetBucket(bucketName_.c_str()); | |
288 deleteObjectRequest.SetKey(path.c_str()); | |
289 | |
290 auto result = client_.DeleteObject(deleteObjectRequest); | |
291 | |
292 if (!result.IsSuccess()) | |
293 { | |
294 throw StoragePluginException(std::string("error while deleting file ") + path + ": " + result.GetError().GetExceptionName().c_str() + " " + result.GetError().GetMessage().c_str()); | |
295 } | |
296 | |
297 } |