54a71d9b2a912508642974ca675c676dea04b90b
[sdc.git] / common-be / src / main / java / org / openecomp / sdc / be / csar / storage / MinIoStorageArtifactStorageManager.java
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation
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  *  SPDX-License-Identifier: Apache-2.0
18  *  ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.sdc.be.csar.storage;
22
23 import static org.openecomp.sdc.common.errors.Messages.EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING;
24
25 import io.minio.BucketExistsArgs;
26 import io.minio.CopyObjectArgs;
27 import io.minio.CopySource;
28 import io.minio.GetObjectArgs;
29 import io.minio.MakeBucketArgs;
30 import io.minio.MinioClient;
31 import io.minio.MinioClient.Builder;
32 import io.minio.PutObjectArgs;
33 import io.minio.RemoveObjectArgs;
34 import java.io.InputStream;
35 import java.util.Map;
36 import java.util.Optional;
37 import java.util.UUID;
38 import lombok.Getter;
39 import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig.Credentials;
40 import org.openecomp.sdc.be.csar.storage.MinIoStorageArtifactStorageConfig.EndPoint;
41 import org.openecomp.sdc.be.csar.storage.exception.ArtifactStorageException;
42 import org.openecomp.sdc.common.CommonConfigurationManager;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 public class MinIoStorageArtifactStorageManager implements ArtifactStorageManager {
47
48     private static final Logger LOGGER = LoggerFactory.getLogger(MinIoStorageArtifactStorageManager.class);
49     private static final String EXTERNAL_CSAR_STORE = "externalCsarStore";
50
51     @Getter
52     private final MinIoStorageArtifactStorageConfig storageConfiguration;
53     private final MinioClient minioClient;
54
55     public MinIoStorageArtifactStorageManager() {
56         this.storageConfiguration = readMinIoStorageArtifactStorageConfig();
57         minioClient = initMinioClient();
58     }
59
60     //for testing only
61     MinIoStorageArtifactStorageManager(final ArtifactStorageConfig storageConfiguration) {
62         this.storageConfiguration = (MinIoStorageArtifactStorageConfig) storageConfiguration;
63         minioClient = initMinioClient();
64     }
65
66     @Override
67     public ArtifactInfo persist(final String vspId, final String versionId, final ArtifactInfo uploadedArtifactInfo) {
68         final MinIoArtifactInfo minioObjectTemp = (MinIoArtifactInfo) uploadedArtifactInfo;
69         try {
70             minioClient.getObject(
71                 GetObjectArgs.builder()
72                     .bucket(minioObjectTemp.getBucket())
73                     .object(minioObjectTemp.getObjectName())
74                     .build()
75             );
76         } catch (final Exception e) {
77             LOGGER.error("Failed to retrieve uploaded artifact with bucket '{}' and name '{}' while persisting", minioObjectTemp.getBucket(),
78                 minioObjectTemp.getObjectName(), e);
79             throw new ArtifactStorageException(
80                 String.format("Failed to retrieve uploaded artifact with bucket '%s' and name '%s' while persisting",
81                     minioObjectTemp.getBucket(), minioObjectTemp.getObjectName()), e);
82         }
83
84         final var backupPath = backupPreviousVersion(vspId, versionId).orElse(null);
85         try {
86             moveFile(minioObjectTemp, vspId, versionId);
87         } catch (final Exception e) {
88             rollback(minioObjectTemp, vspId, versionId);
89             LOGGER.error("Could not persist artifact for bucket '{}', object '{}'", vspId, versionId, e);
90             final var errorMsg = String.format("Could not persist artifact for VSP '%s', version '%s'", vspId, versionId);
91             throw new ArtifactStorageException(errorMsg, e);
92         }
93
94         removePreviousVersion(backupPath);
95
96         return new MinIoArtifactInfo(vspId, versionId);
97     }
98
99     @Override
100     public ArtifactInfo upload(final String vspId, final String versionId, final InputStream fileToUpload) {
101
102         final String name = versionId + "--" + UUID.randomUUID();
103         try {
104             // Make bucket if not exist.
105             final boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(vspId).build());
106
107             if (!found) {
108                 // Make a new bucket ${vspId} .
109                 minioClient.makeBucket(MakeBucketArgs.builder().bucket(vspId).build());
110             } else {
111                 LOGGER.info("Bucket '{}' already exists.", vspId);
112             }
113
114             put(vspId, name, fileToUpload);
115
116         } catch (final Exception e) {
117             LOGGER.error("Failed to upload artifact - bucket: '{}', object: '{}'", vspId, name, e);
118             throw new ArtifactStorageException("Failed to upload artifact", e);
119         }
120
121         return new MinIoArtifactInfo(vspId, name);
122     }
123
124     @Override
125     public void put(final String vspId, final String name, final InputStream fileToUpload) {
126         try {
127             minioClient.putObject(
128                 PutObjectArgs.builder()
129                     .bucket(vspId)
130                     .object(name)
131                     .stream(fileToUpload, fileToUpload.available(), -1)
132                     .build()
133             );
134         } catch (final Exception e) {
135             LOGGER.error("Failed to put - bucket: '{}', object: '{}'", vspId, name, e);
136             throw new ArtifactStorageException("Failed to upload artifact", e);
137         }
138     }
139
140     @Override
141     public boolean isEnabled() {
142         return storageConfiguration != null && storageConfiguration.isEnabled();
143     }
144
145     @Override
146     public InputStream get(final ArtifactInfo artifactInfo) {
147         final MinIoArtifactInfo minioObject = (MinIoArtifactInfo) artifactInfo;
148         try {
149             return get(minioObject.getBucket(), minioObject.getObjectName());
150         } catch (final Exception e) {
151             LOGGER.error("Failed to get - bucket: '{}', object: '{}'", minioObject.getBucket(), minioObject.getObjectName(), e);
152             throw new ArtifactStorageException("Failed to get Object", e);
153         }
154     }
155
156     @Override
157     public InputStream get(final String bucketID, final String objectID) {
158         try {
159             return minioClient.getObject(GetObjectArgs.builder()
160                 .bucket(bucketID)
161                 .object(objectID)
162                 .build());
163         } catch (final Exception e) {
164             LOGGER.error("Failed to get - bucket: '{}', object: '{}'", bucketID, objectID, e);
165             throw new ArtifactStorageException("Failed to get Object", e);
166         }
167     }
168
169     @Override
170     public void delete(final ArtifactInfo artifactInfo) {
171         final MinIoArtifactInfo minioObject = (MinIoArtifactInfo) artifactInfo;
172         try {
173             minioClient.removeObject(RemoveObjectArgs.builder()
174                 .bucket(minioObject.getBucket())
175                 .object(minioObject.getObjectName())
176                 .bypassGovernanceMode(true)
177                 .build());
178         } catch (final Exception e) {
179             LOGGER.error("Failed to delete - bucket: '{}', object: '{}'", minioObject.getBucket(), minioObject.getObjectName(), e);
180             throw new ArtifactStorageException(String.format("Failed to delete '%s'", minioObject.getObjectName()), e);
181         }
182
183     }
184
185     private Optional<MinIoArtifactInfo> backupPreviousVersion(final String vspId, final String versionId) {
186
187         final String tempName = versionId + "--" + UUID.randomUUID().toString();
188         try {
189             copy(vspId, tempName, versionId);
190         } catch (final Exception e) {
191             LOGGER.error("Failed to copy - bucket: '{}', object: '{}'", vspId, versionId, e);
192             return Optional.empty();
193         }
194
195         return Optional.of(new MinIoArtifactInfo(vspId, tempName));
196     }
197
198     private void rollback(final MinIoArtifactInfo minioObject, final String vspId, final String versionId) {
199         try {
200             moveFile(minioObject, vspId, versionId);
201         } catch (final Exception ex) {
202             LOGGER.warn("Could not rollback the backup '{}' to the original '{}'", versionId, minioObject.getObjectName(), ex);
203         }
204     }
205
206     private void removePreviousVersion(final MinIoArtifactInfo minioObject) {
207         if (minioObject == null) {
208             return;
209         }
210         delete(minioObject);
211     }
212
213     private void moveFile(final MinIoArtifactInfo minioObject, final String vspId, final String versionId) {
214         try {
215             copy(vspId, versionId, minioObject.getObjectName());
216         } catch (final Exception e) {
217             LOGGER.error("Failed to copy - bucket: '{}', object: '{}'", vspId, versionId, e);
218             throw new ArtifactStorageException("Failed to move", e);
219         }
220         delete(minioObject);
221     }
222
223     private void copy(final String vspId, final String versionId, final String objectName) throws Exception {
224         minioClient.copyObject(
225             CopyObjectArgs.builder()
226                 .bucket(vspId)
227                 .object(versionId)
228                 .source(CopySource.builder()
229                     .bucket(vspId)
230                     .object(objectName)
231                     .build())
232                 .build());
233     }
234
235     private MinIoStorageArtifactStorageConfig readMinIoStorageArtifactStorageConfig() {
236         final var commonConfigurationManager = CommonConfigurationManager.getInstance();
237
238         final Map<String, Object> endpoint = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "endpoint", null);
239         final Map<String, Object> credentials = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "credentials", null);
240         final String tempPath = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "tempPath", null);
241
242         if (endpoint == null) {
243             LOGGER.error(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("endpoint"));
244             throw new ArtifactStorageException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("endpoint"));
245         }
246         if (credentials == null) {
247             LOGGER.error(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("credentials"));
248             throw new ArtifactStorageException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("credentials"));
249         }
250         if (tempPath == null) {
251             LOGGER.error(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("tempPath"));
252             throw new ArtifactStorageException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("tempPath"));
253         }
254         LOGGER.info("ArtifactConfig.endpoint: '{}'", endpoint);
255         LOGGER.info("ArtifactConfig.credentials: '{}'", credentials);
256         LOGGER.info("ArtifactConfig.tempPath: '{}'", tempPath);
257
258         final String host = (String) endpoint.getOrDefault("host", null);
259         final int port = (int) endpoint.getOrDefault("port", 0);
260         final boolean secure = (boolean) endpoint.getOrDefault("secure", false);
261
262         final String accessKey = (String) credentials.getOrDefault("accessKey", null);
263         final String secretKey = (String) credentials.getOrDefault("secretKey", null);
264
265         return new MinIoStorageArtifactStorageConfig(true, new EndPoint(host, port, secure), new Credentials(accessKey, secretKey), tempPath);
266     }
267
268     private MinioClient initMinioClient() {
269         final EndPoint endPoint = storageConfiguration.getEndPoint();
270         final Credentials credentials = storageConfiguration.getCredentials();
271
272         final Builder builder = MinioClient.builder();
273         return builder
274             .endpoint(endPoint.getHost(), endPoint.getPort(), endPoint.isSecure())
275             .credentials(credentials.getAccessKey(), credentials.getSecretKey())
276             .build();
277     }
278
279 }