850b4e1ddac75d10c7545172f286ebe3eb3839bc
[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             throw new ArtifactStorageException(
78                 String.format("Failed to retrieve uploaded artifact with bucket '%s' and name '%s' while persisting",
79                     minioObjectTemp.getBucket(), minioObjectTemp.getObjectName()), e);
80         }
81
82         final var backupPath = backupPreviousVersion(vspId, versionId).orElse(null);
83         try {
84             moveFile(minioObjectTemp, vspId, versionId);
85         } catch (final Exception e) {
86             rollback(minioObjectTemp, vspId, versionId);
87             final var errorMsg = String.format("Could not persist artifact for VSP '%s', version '%s'", vspId, versionId);
88             throw new ArtifactStorageException(errorMsg, e);
89         }
90
91         removePreviousVersion(backupPath);
92
93         return new MinIoArtifactInfo(vspId, versionId);
94     }
95
96     @Override
97     public ArtifactInfo upload(final String vspId, final String versionId, final InputStream fileToUpload) {
98
99         final String name = versionId + "--" + UUID.randomUUID();
100         try {
101             // Make bucket if not exist.
102             final boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(vspId).build());
103
104             if (!found) {
105                 // Make a new bucket ${vspId} .
106                 minioClient.makeBucket(MakeBucketArgs.builder().bucket(vspId).build());
107             } else {
108                 LOGGER.info("Bucket '{}' already exists.", vspId);
109             }
110
111             put(vspId, name, fileToUpload);
112
113         } catch (final Exception e) {
114             throw new ArtifactStorageException("Failed to upload artifact", e);
115         }
116
117         return new MinIoArtifactInfo(vspId, name);
118     }
119
120     @Override
121     public void put(final String vspId, final String name, final InputStream fileToUpload) {
122         try {
123             minioClient.putObject(
124                 PutObjectArgs.builder()
125                     .bucket(vspId)
126                     .object(name)
127                     .stream(fileToUpload, fileToUpload.available(), -1)
128                     .build()
129             );
130         } catch (final Exception e) {
131             throw new ArtifactStorageException("Failed to upload artifact", e);
132         }
133     }
134
135     @Override
136     public boolean isEnabled() {
137         return storageConfiguration != null && storageConfiguration.isEnabled();
138     }
139
140     @Override
141     public InputStream get(final ArtifactInfo artifactInfo) {
142         final MinIoArtifactInfo minioObject = (MinIoArtifactInfo) artifactInfo;
143         try {
144             return get(minioObject.getBucket(), minioObject.getObjectName());
145         } catch (final Exception e) {
146             throw new ArtifactStorageException("Failed to get Object", e);
147         }
148     }
149
150     @Override
151     public InputStream get(final String bucketID, final String objectID) {
152         try {
153             return minioClient.getObject(GetObjectArgs.builder()
154                 .bucket(bucketID)
155                 .object(objectID)
156                 .build());
157         } catch (final Exception e) {
158             throw new ArtifactStorageException("Failed to get Object", e);
159         }
160     }
161
162     @Override
163     public void delete(final ArtifactInfo artifactInfo) {
164         final MinIoArtifactInfo minioObject = (MinIoArtifactInfo) artifactInfo;
165         try {
166             minioClient.removeObject(RemoveObjectArgs.builder()
167                 .bucket(minioObject.getBucket())
168                 .object(minioObject.getObjectName())
169                 .bypassGovernanceMode(true)
170                 .build());
171         } catch (final Exception e) {
172             throw new ArtifactStorageException(String.format("Failed to delete '%s'", minioObject.getObjectName()), e);
173         }
174
175     }
176
177     private Optional<MinIoArtifactInfo> backupPreviousVersion(final String vspId, final String versionId) {
178
179         final String tempName = versionId + "--" + UUID.randomUUID().toString();
180         try {
181             copy(vspId, tempName, versionId);
182         } catch (final Exception e) {
183             return Optional.empty();
184         }
185
186         return Optional.of(new MinIoArtifactInfo(vspId, tempName));
187     }
188
189     private void rollback(final MinIoArtifactInfo minioObject, final String vspId, final String versionId) {
190         try {
191             moveFile(minioObject, vspId, versionId);
192         } catch (final Exception ex) {
193             LOGGER.warn("Could not rollback the backup '{}' to the original '{}'", versionId, minioObject.getObjectName(), ex);
194         }
195     }
196
197     private void removePreviousVersion(final MinIoArtifactInfo minioObject) {
198         if (minioObject == null) {
199             return;
200         }
201         delete(minioObject);
202     }
203
204     private void moveFile(final MinIoArtifactInfo minioObject, final String vspId, final String versionId) {
205         try {
206             copy(vspId, versionId, minioObject.getObjectName());
207         } catch (final Exception e) {
208             throw new ArtifactStorageException("Failed to move", e);
209         }
210         delete(minioObject);
211     }
212
213     private void copy(final String vspId, final String versionId, final String objectName) throws Exception {
214         minioClient.copyObject(
215             CopyObjectArgs.builder()
216                 .bucket(vspId)
217                 .object(versionId)
218                 .source(CopySource.builder()
219                     .bucket(vspId)
220                     .object(objectName)
221                     .build())
222                 .build());
223     }
224
225     private MinIoStorageArtifactStorageConfig readMinIoStorageArtifactStorageConfig() {
226         final var commonConfigurationManager = CommonConfigurationManager.getInstance();
227
228         final Map<String, Object> endpoint = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "endpoint", null);
229         final Map<String, Object> credentials = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "credentials", null);
230         final String tempPath = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "tempPath", null);
231         LOGGER.info("ArtifactConfig.endpoint: '{}'", endpoint);
232         LOGGER.info("ArtifactConfig.credentials: '{}'", credentials);
233         LOGGER.info("ArtifactConfig.tempPath: '{}'", tempPath);
234
235         if (endpoint == null) {
236             throw new RuntimeException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("endpoint"));
237         }
238         if (credentials == null) {
239             throw new RuntimeException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("credentials"));
240         }
241         if (tempPath == null) {
242             throw new RuntimeException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING.formatMessage("tempPath"));
243         }
244         final String host = (String) endpoint.getOrDefault("host", null);
245         final int port = (int) endpoint.getOrDefault("port", 0);
246         final boolean secure = (boolean) endpoint.getOrDefault("secure", false);
247
248         final String accessKey = (String) credentials.getOrDefault("accessKey", null);
249         final String secretKey = (String) credentials.getOrDefault("secretKey", null);
250
251         return new MinIoStorageArtifactStorageConfig(true, new EndPoint(host, port, secure), new Credentials(accessKey, secretKey), tempPath);
252     }
253
254     private MinioClient initMinioClient() {
255         final EndPoint endPoint = storageConfiguration.getEndPoint();
256         final Credentials credentials = storageConfiguration.getCredentials();
257
258         final Builder builder = MinioClient.builder();
259         return builder
260             .endpoint(endPoint.getHost(), endPoint.getPort(), endPoint.isSecure())
261             .credentials(credentials.getAccessKey(), credentials.getSecretKey())
262             .build();
263     }
264
265 }