Support handling of 'Large CSARs' 43/122643/2
authorvasraz <vasyl.razinkov@est.tech>
Thu, 8 Jul 2021 17:54:19 +0000 (18:54 +0100)
committerMichael Morris <michael.morris@est.tech>
Wed, 21 Jul 2021 14:01:01 +0000 (14:01 +0000)
If artifact storage is enabled, stores original onboarded package,
leaving a reference in the VSP, instead of the original onboarded
package itself. Strips files from configured folders in order to reduce
the package size and onboard it. To retrieve the package, one needs to
read the reference and go to the artifact storage to retrieve.
If disabled, it just goes through the current onboarding process.

Change-Id: I3dce0ab8422ea736c8a1ffaeb1136cf8b12a2af4
Signed-off-by: Vasyl Razinkov <vasyl.razinkov@est.tech>
Signed-off-by: André Schmid <andre.schmid@est.tech>
Issue-ID: SDC-3635

34 files changed:
common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactInfo.java [new file with mode: 0644]
common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactStorageConfig.java [new file with mode: 0644]
common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactStorageManager.java [new file with mode: 0644]
common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/PackageSizeReducer.java [new file with mode: 0644]
common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/PackageSizeReducerConfig.java [new file with mode: 0644]
common-be/pom.xml
common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarPackageReducerConfiguration.java [new file with mode: 0644]
common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducer.java [new file with mode: 0644]
common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentStorageArtifactInfo.java [new file with mode: 0644]
common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageConfig.java [new file with mode: 0644]
common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageManager.java [new file with mode: 0644]
common-be/src/main/java/org/openecomp/sdc/be/csar/storage/exception/CsarSizeReducerException.java [new file with mode: 0644]
common-be/src/main/java/org/openecomp/sdc/be/csar/storage/exception/PersistentVolumeArtifactStorageException.java [new file with mode: 0644]
common-be/src/test/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducerTest.java [new file with mode: 0644]
common-be/src/test/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageManagerTest.java [new file with mode: 0644]
common-be/src/test/resources/csarSizeReducer/dummy.csar [new file with mode: 0644]
common-be/src/test/resources/persistentVolumeArtifactStorageManager/dummy.csar [new file with mode: 0644]
onboarding/pom.xml
openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateException.java
openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java
openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java
openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/onboarding/OnboardingPackageProcessor.java
openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/OrchestrationTemplateCSARHandler.java
openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/OrchestrationTemplateZipHandler.java
openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/types/OnboardPackageInfo.java
openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/onboarding/OnboardingPackageProcessorTest.java
openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/onboarding/OnboardingPackageProcessorUnitTest.java
openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java
openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/configuration.yaml.erb
openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java
openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-api/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/type/OrchestrationTemplateCandidateData.java
openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/impl/zusammen/OrchestrationTemplateCandidateDaoZusammenException.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/impl/zusammen/OrchestrationTemplateCandidateDaoZusammenImpl.java
pom.xml

diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactInfo.java
new file mode 100644 (file)
index 0000000..7584770
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import java.nio.file.Path;
+
+/**
+ * Represents the stored artifact
+ */
+public interface ArtifactInfo {
+
+    Path getPath();
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactStorageConfig.java b/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactStorageConfig.java
new file mode 100644 (file)
index 0000000..edac694
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+/**
+ * Configurations for the {@link ArtifactStorageManager}
+ */
+public interface ArtifactStorageConfig {
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactStorageManager.java b/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/ArtifactStorageManager.java
new file mode 100644 (file)
index 0000000..da06db0
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import java.io.InputStream;
+
+/**
+ * Manages the artifact storage and handles operations on the artifacts
+ */
+public interface ArtifactStorageManager {
+
+    /**
+     * Persists the uploaded artifact in the storage.
+     *
+     * @param vspId                the VSP id
+     * @param versionId            the VSP version id
+     * @param uploadedArtifactInfo the uploaded
+     * @return the information about the persisted artifact
+     */
+    ArtifactInfo persist(String vspId, String versionId, ArtifactInfo uploadedArtifactInfo);
+
+    /**
+     * Uploads a file to the Artifact Storage. This file will be temporary until persisted by {@link #persist(String, String, ArtifactInfo)}.
+     *
+     * @param vspId        the VSP id
+     * @param versionId    the VSP version id
+     * @param fileToUpload the file input stream
+     * @return the information about the uploaded artifact
+     */
+    ArtifactInfo upload(String vspId, String versionId, InputStream fileToUpload);
+
+    /**
+     * Checks if the Artifact Storage is enabled.
+     *
+     * @return {@code true} if enable, {@code false} otherwise
+     */
+    boolean isEnabled();
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/PackageSizeReducer.java b/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/PackageSizeReducer.java
new file mode 100644 (file)
index 0000000..290ca08
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import java.nio.file.Path;
+
+/**
+ * Handles operations to reduce the package size
+ */
+public interface PackageSizeReducer {
+
+    byte[] reduce(Path path);
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/PackageSizeReducerConfig.java b/common-app-api/src/main/java/org/openecomp/sdc/be/csar/storage/PackageSizeReducerConfig.java
new file mode 100644 (file)
index 0000000..b45396f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+/**
+ * Configuration for the {@link PackageSizeReducer}
+ */
+public interface PackageSizeReducerConfig {
+
+}
index 2f5464a..452ff4b 100644 (file)
       <version>${spring.version}</version>
       <scope>compile</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+      <version>${cxf.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.jboss.spec.javax.rmi</groupId>
+          <artifactId>jboss-rmi-api_1.0_spec</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarPackageReducerConfiguration.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarPackageReducerConfiguration.java
new file mode 100644 (file)
index 0000000..a14222a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import java.nio.file.Path;
+import java.util.Set;
+import lombok.Data;
+
+@Data
+public class CsarPackageReducerConfiguration implements PackageSizeReducerConfig {
+
+    private final Set<Path> foldersToStrip;
+    private final long sizeLimit;
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducer.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducer.java
new file mode 100644 (file)
index 0000000..cf35c8c
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.UUID;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+import org.openecomp.sdc.be.csar.storage.exception.CsarSizeReducerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CsarSizeReducer implements PackageSizeReducer {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(CsarSizeReducer.class);
+
+    private final CsarPackageReducerConfiguration configuration;
+
+    public CsarSizeReducer(final CsarPackageReducerConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    public byte[] reduce(final Path csarPackagePath) {
+        final var reducedCsarPath = Path.of(csarPackagePath + "." + UUID.randomUUID());
+
+        try (final var zf = new ZipFile(csarPackagePath.toString());
+            final var zos = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(reducedCsarPath)))) {
+
+            zf.entries().asIterator().forEachRemaining(entry -> {
+                final var entryName = entry.getName();
+                try {
+                    if (!entry.isDirectory()) {
+                        zos.putNextEntry(new ZipEntry(entryName));
+                        if (isCandidateToRemove(entry)) {
+                            // replace with EMPTY string to avoid package description inconsistency/validation errors
+                            zos.write("".getBytes());
+                        } else {
+                            zos.write(zf.getInputStream(entry).readAllBytes());
+                        }
+                    }
+                    zos.closeEntry();
+                } catch (final IOException ei) {
+                    final var errorMsg = String.format("Failed to extract '%s' from zip '%s'", entryName, csarPackagePath);
+                    throw new CsarSizeReducerException(errorMsg, ei);
+                }
+            });
+
+        } catch (final IOException ex1) {
+            rollback(reducedCsarPath);
+            final var errorMsg = String.format("An unexpected problem happened while reading the CSAR '%s'", csarPackagePath);
+            throw new CsarSizeReducerException(errorMsg, ex1);
+        }
+        final byte[] reducedCsarBytes;
+        try {
+            reducedCsarBytes = Files.readAllBytes(reducedCsarPath);
+        } catch (final IOException e) {
+            final var errorMsg = String.format("Could not read bytes of file '%s'", csarPackagePath);
+            throw new CsarSizeReducerException(errorMsg, e);
+        }
+        try {
+            Files.delete(reducedCsarPath);
+        } catch (final IOException e) {
+            final var errorMsg = String.format("Could not delete temporary file '%s'", reducedCsarPath);
+            throw new CsarSizeReducerException(errorMsg, e);
+        }
+
+        return reducedCsarBytes;
+    }
+
+    private void rollback(final Path reducedCsarPath) {
+        if (Files.exists(reducedCsarPath)) {
+            try {
+                Files.delete(reducedCsarPath);
+            } catch (final Exception ex2) {
+                LOGGER.warn("Could not delete temporary file '{}'", reducedCsarPath, ex2);
+            }
+        }
+    }
+
+    private boolean isCandidateToRemove(final ZipEntry zipEntry) {
+        final String zipEntryName = zipEntry.getName();
+        return configuration.getFoldersToStrip().stream().anyMatch(Path.of(zipEntryName)::startsWith)
+            || zipEntry.getSize() > configuration.getSizeLimit();
+    }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentStorageArtifactInfo.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentStorageArtifactInfo.java
new file mode 100644 (file)
index 0000000..0472661
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import java.nio.file.Path;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+public class PersistentStorageArtifactInfo implements ArtifactInfo {
+
+    @Getter
+    private final Path path;
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageConfig.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageConfig.java
new file mode 100644 (file)
index 0000000..d3cd6fb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import java.nio.file.Path;
+import lombok.Data;
+
+@Data
+public class PersistentVolumeArtifactStorageConfig implements ArtifactStorageConfig {
+
+    private final boolean isEnabled;
+    private final Path storagePath;
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageManager.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageManager.java
new file mode 100644 (file)
index 0000000..10629b3
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Optional;
+import java.util.UUID;
+import org.openecomp.sdc.be.csar.storage.exception.PersistentVolumeArtifactStorageException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PersistentVolumeArtifactStorageManager implements ArtifactStorageManager {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(PersistentVolumeArtifactStorageManager.class);
+
+    private final PersistentVolumeArtifactStorageConfig storageConfiguration;
+
+    public PersistentVolumeArtifactStorageManager(final ArtifactStorageConfig storageConfiguration) {
+        this.storageConfiguration = (PersistentVolumeArtifactStorageConfig) storageConfiguration;
+    }
+
+    @Override
+    public ArtifactInfo persist(final String vspId, final String versionId, final ArtifactInfo uploadedArtifactInfo) {
+        final var temporaryPath = uploadedArtifactInfo.getPath();
+        if (!Files.exists(temporaryPath)) {
+            throw new PersistentVolumeArtifactStorageException(String.format("Given artifact does not exist '%s'", uploadedArtifactInfo.getPath()));
+        }
+
+        final var filePath = buildFilePath(vspId, versionId);
+        final var backupPath = backupPreviousVersion(filePath).orElse(null);
+        try {
+            moveFile(temporaryPath, filePath);
+        } catch (final Exception e) {
+            rollback(backupPath, filePath);
+            final var errorMsg = String.format("Could not persist artifact for VSP '%s', version '%s'", vspId, versionId);
+            throw new PersistentVolumeArtifactStorageException(errorMsg, e);
+        }
+
+        removePreviousVersion(backupPath);
+
+        return new PersistentStorageArtifactInfo(filePath);
+    }
+
+    @Override
+    public ArtifactInfo upload(final String vspId, final String versionId, final InputStream artifactInputStream) {
+        final var destinationFolder = buildDestinationFolder(vspId, versionId);
+        try {
+            Files.createDirectories(destinationFolder);
+        } catch (final IOException e) {
+            throw new PersistentVolumeArtifactStorageException(String.format("Could not create directory '%s'", destinationFolder), e);
+        }
+
+        final var filePath = createTempFilePath(destinationFolder);
+        try {
+            persist(artifactInputStream, filePath);
+        } catch (final IOException e) {
+            throw new PersistentVolumeArtifactStorageException(String.format("Could not persist artifact '%s'", filePath), e);
+        }
+
+        return new PersistentStorageArtifactInfo(filePath);
+    }
+
+    private Path buildFilePath(final String vspId, final String versionId) {
+        return buildDestinationFolder(vspId, versionId).resolve(versionId);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return storageConfiguration != null && storageConfiguration.isEnabled();
+    }
+
+    private Optional<Path> backupPreviousVersion(final Path filePath) {
+        if (!Files.exists(filePath)) {
+            return Optional.empty();
+        }
+
+        final var backupPath = Path.of(filePath + UUID.randomUUID().toString());
+        moveFile(filePath, backupPath);
+        return Optional.ofNullable(backupPath);
+    }
+
+    private void rollback(final Path backupPath, final Path filePath) {
+        try {
+            moveFile(backupPath, filePath);
+        } catch (final Exception ex) {
+            LOGGER.warn("Could not rollback the backup file '{}' to the original '{}'", backupPath, filePath, ex);
+        }
+    }
+
+    private void removePreviousVersion(final Path filePath) {
+        if (filePath == null || !Files.exists(filePath)) {
+            return;
+        }
+
+        try {
+            Files.delete(filePath);
+        } catch (final IOException e) {
+            throw new PersistentVolumeArtifactStorageException(String.format("Could not delete previous version '%s'", filePath), e);
+        }
+    }
+
+    private Path createTempFilePath(final Path destinationFolder) {
+        final var retries = 10;
+        return createTempFilePath(destinationFolder, retries).orElseThrow(() -> {
+            throw new PersistentVolumeArtifactStorageException(String.format("Could not generate upload file path after '%s' retries", retries));
+        });
+    }
+
+    private Optional<Path> createTempFilePath(final Path destinationFolder, int retries) {
+        for (var i = 0; i < retries; i++) {
+            final var filePath = destinationFolder.resolve(UUID.randomUUID().toString());
+            if (Files.notExists(filePath)) {
+                return Optional.of(filePath);
+            }
+        }
+        return Optional.empty();
+    }
+
+    private Path buildDestinationFolder(final String vspId, final String versionId) {
+        return storageConfiguration.getStoragePath().resolve(vspId).resolve(versionId);
+    }
+
+    private void persist(final InputStream artifactInputStream, final Path filePath) throws IOException {
+        try (final var inputStream = artifactInputStream;
+            final var fileOutputStream = new FileOutputStream(filePath.toFile());) {
+            inputStream.transferTo(fileOutputStream);
+        }
+    }
+
+    private void moveFile(final Path from, final Path to) {
+        try {
+            Files.move(from, to, StandardCopyOption.REPLACE_EXISTING);
+        } catch (final IOException e) {
+            throw new PersistentVolumeArtifactStorageException(String.format("Could not move file '%s' to '%s'", from, to), e);
+        }
+    }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/exception/CsarSizeReducerException.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/exception/CsarSizeReducerException.java
new file mode 100644 (file)
index 0000000..f57666a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage.exception;
+
+import org.openecomp.sdc.be.exception.BusinessException;
+
+public class CsarSizeReducerException extends BusinessException {
+
+    public CsarSizeReducerException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/exception/PersistentVolumeArtifactStorageException.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/exception/PersistentVolumeArtifactStorageException.java
new file mode 100644 (file)
index 0000000..28fff65
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage.exception;
+
+import org.openecomp.sdc.be.exception.BusinessException;
+
+public class PersistentVolumeArtifactStorageException extends BusinessException {
+
+    public PersistentVolumeArtifactStorageException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    public PersistentVolumeArtifactStorageException(final String message) {
+        super(message);
+    }
+}
diff --git a/common-be/src/test/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducerTest.java b/common-be/src/test/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducerTest.java
new file mode 100644 (file)
index 0000000..c758644
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * -
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.common.zip.ZipUtils;
+import org.openecomp.sdc.common.zip.exception.ZipException;
+
+class CsarSizeReducerTest {
+
+    @Mock
+    private CsarPackageReducerConfiguration csarPackageReducerConfiguration;
+    @InjectMocks
+    private CsarSizeReducer csarSizeReducer;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+    }
+
+    @Test
+    void reduceByPathAndSizeTest() throws ZipException {
+        final var pathToReduce1 = Path.of("Files/images");
+        final var pathToReduce2 = Path.of("Files/Scripts/my_script.sh");
+        final var sizeLimit = 150000L;
+        when(csarPackageReducerConfiguration.getSizeLimit()).thenReturn(sizeLimit);
+        when(csarPackageReducerConfiguration.getFoldersToStrip()).thenReturn(Set.of(pathToReduce1, pathToReduce2));
+
+        final var csarPath = Path.of("src/test/resources/csarSizeReducer/dummy.csar");
+
+        final Map<String, byte[]> originalCsar = ZipUtils.readZip(csarPath.toFile(), false);
+
+        final byte[] reduce = csarSizeReducer.reduce(csarPath);
+
+        final Map<String, byte[]> reducedCsar = ZipUtils.readZip(reduce, false);
+
+        assertEquals(originalCsar.keySet().size(), reducedCsar.keySet().size(), "No file should be removed");
+        for (final Entry<String, byte[]> originalEntry : originalCsar.entrySet()) {
+            final var originalFilePath = originalEntry.getKey();
+            final byte[] originalBytes = originalEntry.getValue();
+            assertTrue(reducedCsar.containsKey(originalFilePath),
+                String.format("No file should be removed, but it is missing original file '%s'", originalFilePath));
+
+            if (originalFilePath.startsWith(pathToReduce1.toString()) || originalFilePath.startsWith(pathToReduce2.toString())
+                || originalBytes.length > sizeLimit) {
+                assertArrayEquals("".getBytes(StandardCharsets.UTF_8), reducedCsar.get(originalFilePath),
+                    String.format("File '%s' expected to be reduced to empty string", originalFilePath));
+            } else {
+                assertArrayEquals(originalBytes, reducedCsar.get(originalFilePath),
+                    String.format("File '%s' expected to be equal", originalFilePath));
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/common-be/src/test/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageManagerTest.java b/common-be/src/test/java/org/openecomp/sdc/be/csar/storage/PersistentVolumeArtifactStorageManagerTest.java
new file mode 100644 (file)
index 0000000..ab8c11c
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.csar.storage;
+
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+import javax.activation.DataHandler;
+import org.apache.commons.io.IOUtils;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
+
+@TestMethodOrder(OrderAnnotation.class)
+class PersistentVolumeArtifactStorageManagerTest {
+
+    private static final String SRC_TEST_RESOURCES = "src/test/resources/";
+
+    private PersistentVolumeArtifactStorageManager testSubject;
+
+    @BeforeEach
+    void setUp() {
+        testSubject = new PersistentVolumeArtifactStorageManager(new PersistentVolumeArtifactStorageConfig(true, Path.of(SRC_TEST_RESOURCES)));
+    }
+
+    @AfterAll
+    static void tearDown() throws IOException {
+        Files.move(Path.of(SRC_TEST_RESOURCES + "vspId/versionId/versionId"),
+            Path.of(SRC_TEST_RESOURCES + "persistentVolumeArtifactStorageManager/dummy.csar"));
+        Files.list(Path.of("src/test/resources/vspId/versionId/")).forEach(path -> {
+            try {
+                Files.deleteIfExists(path);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        });
+        Files.deleteIfExists(Path.of(SRC_TEST_RESOURCES + "vspId/versionId/"));
+        Files.deleteIfExists(Path.of(SRC_TEST_RESOURCES + "vspId/"));
+    }
+
+    @Test
+    @Order(1)
+    void testUpload() throws IOException {
+        final Attachment attachment = mockAttachment("dummy.csar", this.getClass().getResource("/persistentVolumeArtifactStorageManager/dummy.csar"));
+        final ArtifactInfo result = testSubject.upload("vspId", "versionId", attachment.getDataHandler().getInputStream());
+        Assertions.assertNotNull(result);
+        Assertions.assertNotNull(result.getPath());
+        Assertions.assertTrue(result.getPath().startsWith(Path.of(SRC_TEST_RESOURCES + "vspId/versionId/")));
+    }
+
+    @Test
+    @Order(2)
+    void testPersist() {
+        final ArtifactInfo result = testSubject.persist("vspId", "versionId",
+            new PersistentStorageArtifactInfo(Path.of(SRC_TEST_RESOURCES + "persistentVolumeArtifactStorageManager/dummy.csar")));
+        Assertions.assertNotNull(result);
+        Assertions.assertNotNull(result.getPath());
+        Assertions.assertTrue(result.getPath().startsWith(Path.of(SRC_TEST_RESOURCES + "vspId/versionId/")));
+    }
+
+    @Test
+    void testIsEnabled() {
+        Assertions.assertTrue(testSubject.isEnabled());
+    }
+
+    private Attachment mockAttachment(final String fileName, final URL fileToUpload) throws IOException {
+        final Attachment attachment = Mockito.mock(Attachment.class);
+        when(attachment.getContentDisposition()).thenReturn(new ContentDisposition("test"));
+        final DataHandler dataHandler = Mockito.mock(DataHandler.class);
+        when(dataHandler.getName()).thenReturn(fileName);
+        final InputStream inputStream = Mockito.mock(InputStream.class);
+        when(dataHandler.getInputStream()).thenReturn(inputStream);
+        when(attachment.getDataHandler()).thenReturn(dataHandler);
+        byte[] bytes = "upload package Test".getBytes();
+        if (Objects.nonNull(fileToUpload)) {
+            try {
+                bytes = IOUtils.toByteArray(fileToUpload);
+            } catch (final IOException e) {
+                fail("Not able to convert file to byte array");
+            }
+        }
+        when(attachment.getObject(ArgumentMatchers.any())).thenReturn(bytes);
+        return attachment;
+    }
+
+}
diff --git a/common-be/src/test/resources/csarSizeReducer/dummy.csar b/common-be/src/test/resources/csarSizeReducer/dummy.csar
new file mode 100644 (file)
index 0000000..73b28f5
Binary files /dev/null and b/common-be/src/test/resources/csarSizeReducer/dummy.csar differ
diff --git a/common-be/src/test/resources/persistentVolumeArtifactStorageManager/dummy.csar b/common-be/src/test/resources/persistentVolumeArtifactStorageManager/dummy.csar
new file mode 100644 (file)
index 0000000..73b28f5
Binary files /dev/null and b/common-be/src/test/resources/persistentVolumeArtifactStorageManager/dummy.csar differ
index e04f4b5..8cf7eb4 100644 (file)
@@ -65,7 +65,6 @@
     <commons.codec.version>1.10</commons.codec.version>
     <commons.digester.version>2.1</commons.digester.version>
     <commons.lang3.version>${lang3.version}</commons.lang3.version>
-    <cxf.version>3.4.3</cxf.version>
     <datastax.cassandra.version>3.8.0</datastax.cassandra.version>
     <groovy.minimal.version>1.5.8</groovy.minimal.version>
     <http.client.version>4.5.3</http.client.version>
index cd18cf9..10f6012 100644 (file)
  * limitations under the License.
  * ============LICENSE_END=========================================================
  * Modifications copyright (c) 2019 Nokia
+ * Modifications copyright (c) 2021 Nordix Foundation
  * ================================================================================
  */
 package org.openecomp.sdcrests.vsp.rest.services;
 
+import static javax.ws.rs.core.Response.Status.EXPECTATION_FAILED;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static javax.ws.rs.core.Response.Status.NOT_ACCEPTABLE;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
 import static org.openecomp.core.validation.errors.ErrorMessagesFormatBuilder.getErrorWithParameters;
+import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT;
+import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE;
+import static org.openecomp.sdc.common.errors.Messages.EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH;
+import static org.openecomp.sdc.common.errors.Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST;
+import static org.openecomp.sdc.common.errors.Messages.PACKAGE_PROCESS_ERROR;
+import static org.openecomp.sdc.common.errors.Messages.UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import javax.activation.DataHandler;
+import java.util.Set;
+import java.util.stream.Collectors;
 import javax.inject.Named;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
 import org.openecomp.sdc.activitylog.ActivityLogManager;
 import org.openecomp.sdc.activitylog.ActivityLogManagerFactory;
 import org.openecomp.sdc.activitylog.dao.type.ActivityLogEntity;
 import org.openecomp.sdc.activitylog.dao.type.ActivityType;
-import org.openecomp.sdc.common.errors.Messages;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
+import org.openecomp.sdc.be.csar.storage.ArtifactStorageConfig;
+import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager;
+import org.openecomp.sdc.be.csar.storage.CsarPackageReducerConfiguration;
+import org.openecomp.sdc.be.csar.storage.CsarSizeReducer;
+import org.openecomp.sdc.be.csar.storage.PackageSizeReducer;
+import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageConfig;
+import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageManager;
+import org.openecomp.sdc.be.exception.BusinessException;
+import org.openecomp.sdc.common.CommonConfigurationManager;
 import org.openecomp.sdc.common.util.ValidationUtils;
 import org.openecomp.sdc.common.utils.SdcCommon;
 import org.openecomp.sdc.datatypes.error.ErrorLevel;
@@ -76,49 +98,100 @@ import org.springframework.stereotype.Service;
 public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplateCandidate {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(OrchestrationTemplateCandidateImpl.class);
+    private static final String EXTERNAL_CSAR_STORE = "externalCsarStore";
     private final OrchestrationTemplateCandidateManager candidateManager;
     private final VendorSoftwareProductManager vendorSoftwareProductManager;
     private final ActivityLogManager activityLogManager;
+    private final ArtifactStorageManager artifactStorageManager;
+    private final PackageSizeReducer packageSizeReducer;
 
     public OrchestrationTemplateCandidateImpl() {
         this.candidateManager = OrchestrationTemplateCandidateManagerFactory.getInstance().createInterface();
         this.vendorSoftwareProductManager = VspManagerFactory.getInstance().createInterface();
         this.activityLogManager = ActivityLogManagerFactory.getInstance().createInterface();
+        LOGGER.info("Instantiating artifactStorageManager");
+        this.artifactStorageManager = new PersistentVolumeArtifactStorageManager(readArtifactStorageConfiguration());
+        LOGGER.info("Instantiating packageSizeReducer");
+        this.packageSizeReducer = new CsarSizeReducer(readPackageReducerConfiguration());
     }
 
     // Constructor used in test to avoid mock static
-    public OrchestrationTemplateCandidateImpl(OrchestrationTemplateCandidateManager candidateManager,
-                                              VendorSoftwareProductManager vendorSoftwareProductManager, ActivityLogManager activityLogManager) {
+    public OrchestrationTemplateCandidateImpl(final OrchestrationTemplateCandidateManager candidateManager,
+                                              final VendorSoftwareProductManager vendorSoftwareProductManager,
+                                              final ActivityLogManager activityLogManager,
+                                              final ArtifactStorageManager artifactStorageManager,
+                                              final PackageSizeReducer packageSizeReducer) {
         this.candidateManager = candidateManager;
         this.vendorSoftwareProductManager = vendorSoftwareProductManager;
         this.activityLogManager = activityLogManager;
+        this.artifactStorageManager = artifactStorageManager;
+        this.packageSizeReducer = packageSizeReducer;
+    }
+
+    private CsarPackageReducerConfiguration readPackageReducerConfiguration() {
+        final var commonConfigurationManager = CommonConfigurationManager.getInstance();
+        final List<String> foldersToStrip = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "foldersToStrip", new ArrayList<>());
+        final int sizeLimit = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "sizeLimit", 1000000);
+        LOGGER.info("Folders to strip: '{}'", String.join(", ", foldersToStrip));
+        final Set<Path> foldersToStripPathSet = foldersToStrip.stream().map(Path::of).collect(Collectors.toSet());
+        return new CsarPackageReducerConfiguration(foldersToStripPathSet, sizeLimit);
+    }
+
+    private ArtifactStorageConfig readArtifactStorageConfiguration() {
+        final var commonConfigurationManager = CommonConfigurationManager.getInstance();
+        final boolean isEnabled = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "storeCsarsExternally", false);
+        LOGGER.info("ArtifactConfig.isEnabled: '{}'", isEnabled);
+        final String storagePathString = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "fullPath", null);
+        LOGGER.info("ArtifactConfig.storagePath: '{}'", storagePathString);
+        if (isEnabled && storagePathString == null) {
+            throw new OrchestrationTemplateCandidateException(EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH.getErrorMessage());
+        }
+        final var storagePath = storagePathString == null ? null : Path.of(storagePathString);
+        return new PersistentVolumeArtifactStorageConfig(isEnabled, storagePath);
     }
 
     @Override
     public Response upload(final String vspId, final String versionId, final Attachment fileToUpload, final String user) {
-        final byte[] fileToUploadBytes = fileToUpload.getObject(byte[].class);
-        final DataHandler dataHandler = fileToUpload.getDataHandler();
-        final String filename = ValidationUtils.sanitizeInputString(dataHandler.getName());
-        final OnboardingPackageProcessor onboardingPackageProcessor =
-            new OnboardingPackageProcessor(filename, fileToUploadBytes, new CnfPackageValidator());
+        final byte[] fileToUploadBytes;
+        final var filename = ValidationUtils.sanitizeInputString(fileToUpload.getDataHandler().getName());
+        ArtifactInfo artifactInfo = null;
+        if (artifactStorageManager.isEnabled()) {
+            final InputStream packageInputStream;
+            try {
+                packageInputStream = fileToUpload.getDataHandler().getInputStream();
+            } catch (final IOException e) {
+                return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(new ErrorMessage(ErrorLevel.ERROR, UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING.formatMessage(filename)))).build();
+            }
+            try {
+                artifactInfo = artifactStorageManager.upload(vspId, versionId, packageInputStream);
+            } catch (final BusinessException e) {
+                return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT.formatMessage(filename)))).build();
+            }
+            try {
+                fileToUploadBytes = packageSizeReducer.reduce(artifactInfo.getPath());
+            } catch (final BusinessException e) {
+                return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(artifactInfo.getPath())))).build();
+            }
+        } else {
+            fileToUploadBytes = fileToUpload.getObject(byte[].class);
+        }
+
+        final var onboardingPackageProcessor = new OnboardingPackageProcessor(filename, fileToUploadBytes, new CnfPackageValidator(), artifactInfo);
         final ErrorMessage[] errorMessages = onboardingPackageProcessor.getErrorMessages().toArray(new ErrorMessage[0]);
         if (onboardingPackageProcessor.hasErrors()) {
-            final UploadFileResponseDto uploadFileResponseDto = buildUploadResponseWithError(
-                errorMessages);
-            return Response.status(Status.NOT_ACCEPTABLE).entity(uploadFileResponseDto).build();
+            return Response.status(NOT_ACCEPTABLE).entity(buildUploadResponseWithError(errorMessages)).build();
         }
-        final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
+        final var onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
         if (onboardPackageInfo == null) {
-            final UploadFileResponseDto uploadFileResponseDto = buildUploadResponseWithError(
-                new ErrorMessage(ErrorLevel.ERROR, Messages.PACKAGE_PROCESS_ERROR.formatMessage(filename)));
-            return Response.ok(uploadFileResponseDto).build();
+            return Response.ok(buildUploadResponseWithError(new ErrorMessage(ErrorLevel.ERROR, PACKAGE_PROCESS_ERROR.formatMessage(filename)))).build();
         }
-        final VspDetails vspDetails = new VspDetails(ValidationUtils.sanitizeInputString(vspId),
+        final var vspDetails = new VspDetails(ValidationUtils.sanitizeInputString(vspId),
             new Version(ValidationUtils.sanitizeInputString(versionId)));
         return processOnboardPackage(onboardPackageInfo, vspDetails, errorMessages);
     }
 
-    private Response processOnboardPackage(final OnboardPackageInfo onboardPackageInfo, final VspDetails vspDetails, final ErrorMessage... errorMessages) {
+    private Response processOnboardPackage(final OnboardPackageInfo onboardPackageInfo, final VspDetails vspDetails,
+                                           final ErrorMessage... errorMessages) {
         final UploadFileResponse uploadFileResponse = candidateManager.upload(vspDetails, onboardPackageInfo);
         final UploadFileResponseDto uploadFileResponseDto = new MapUploadFileResponseToUploadFileResponseDto()
             .applyMapping(uploadFileResponse, UploadFileResponseDto.class);
@@ -151,10 +224,9 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
         } else {
             zipFile = vendorSoftwareProductManager.get(vspId, new Version((versionId)));
             if (!zipFile.isPresent()) {
-                ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR,
-                    getErrorWithParameters(Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST.getErrorMessage(), ""));
+                ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR, getErrorWithParameters(NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST.getErrorMessage(), ""));
                 LOGGER.error(errorMessage.getMessage());
-                return Response.status(Response.Status.NOT_FOUND).build();
+                return Response.status(NOT_FOUND).build();
             }
             fileName = "Processed." + zipFile.get().getLeft();
         }
@@ -183,8 +255,7 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
         FilesDataStructure fileDataStructure = copyFilesDataStructureDtoToFilesDataStructure(fileDataStructureDto);
         ValidationResponse response = candidateManager.updateFilesDataStructure(vspId, new Version(versionId), fileDataStructure);
         if (!response.isValid()) {
-            return Response.status(Response.Status.EXPECTATION_FAILED)
-                .entity(new MapValidationResponseToDto().applyMapping(response, ValidationResponseDto.class)).build();
+            return Response.status(EXPECTATION_FAILED).entity(new MapValidationResponseToDto().applyMapping(response, ValidationResponseDto.class)).build();
         }
         return Response.ok(fileDataStructureDto).build();
     }
index 41891de..b89756e 100644 (file)
@@ -7,9 +7,9 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
 package org.openecomp.sdcrests.vsp.rest.services;
 
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.MockitoAnnotations.initMocks;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
 import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Objects;
 import java.util.Optional;
@@ -40,14 +46,17 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
 import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
 import org.openecomp.sdc.activitylog.ActivityLogManager;
+import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager;
+import org.openecomp.sdc.be.csar.storage.PackageSizeReducer;
+import org.openecomp.sdc.be.csar.storage.PersistentStorageArtifactInfo;
 import org.openecomp.sdc.logging.api.Logger;
 import org.openecomp.sdc.logging.api.LoggerFactory;
 import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager;
@@ -62,17 +71,20 @@ import org.openecomp.sdcrests.vendorsoftwareproducts.types.FileDataStructureDto;
 import org.openecomp.sdcrests.vendorsoftwareproducts.types.OrchestrationTemplateActionResponseDto;
 import org.openecomp.sdcrests.vendorsoftwareproducts.types.UploadFileResponseDto;
 
-public class OrchestrationTemplateCandidateImplTest {
+class OrchestrationTemplateCandidateImplTest {
 
-    private Logger logger = LoggerFactory.getLogger(OrchestrationTemplateCandidateImplTest.class);
+    private final Logger logger = LoggerFactory.getLogger(OrchestrationTemplateCandidateImplTest.class);
 
     @Mock
     private OrchestrationTemplateCandidateManager candidateManager;
     @Mock
     private VendorSoftwareProductManager vendorSoftwareProductManager;
-
     @Mock
     private ActivityLogManager activityLogManager;
+    @Mock
+    private ArtifactStorageManager artifactStorageManager;
+    @Mock
+    private PackageSizeReducer packageSizeReducer;
 
     private OrchestrationTemplateCandidateImpl orchestrationTemplateCandidate;
 
@@ -82,64 +94,59 @@ public class OrchestrationTemplateCandidateImplTest {
 
     private final String user = "cs0008";
 
-    @Before
-    public void setUp(){
+    @BeforeEach
+    public void setUp() {
         try {
-            initMocks(this);
+            MockitoAnnotations.openMocks(this);
             UploadFileResponse uploadFileResponse = new UploadFileResponse();
             uploadFileResponse.setOnboardingType(OnboardingTypesEnum.ZIP);
             uploadFileResponse.setNetworkPackageName("test");
             when(candidateManager.upload(any(), any())).thenReturn(uploadFileResponse);
 
-
             // get using the candidate manager.
-            Optional<Pair<String,byte[]>> zipFile =
-                    Optional.of(Pair.of("Hello", "World".getBytes()));
+            Optional<Pair<String, byte[]>> zipFile = Optional.of(Pair.of("Hello", "World".getBytes()));
 
             when(candidateManager.get(
-                    ArgumentMatchers.eq(candidateId),
-                    ArgumentMatchers.any())).thenReturn(zipFile);
+                ArgumentMatchers.eq(candidateId),
+                ArgumentMatchers.any())).thenReturn(zipFile);
 
             when(vendorSoftwareProductManager.get(
-                    ArgumentMatchers.eq(softwareProductId),
-                    ArgumentMatchers.any())).thenReturn(zipFile);
-
+                ArgumentMatchers.eq(softwareProductId),
+                ArgumentMatchers.any())).thenReturn(zipFile);
 
-            OrchestrationTemplateActionResponse processResponse =
-                    new OrchestrationTemplateActionResponse();
+            OrchestrationTemplateActionResponse processResponse = new OrchestrationTemplateActionResponse();
             processResponse.setStatus(UploadFileStatus.Success);
             when(candidateManager.process(
-                    ArgumentMatchers.eq(candidateId),
-                    ArgumentMatchers.any())).thenReturn(processResponse);
-
+                ArgumentMatchers.eq(candidateId),
+                ArgumentMatchers.any())).thenReturn(processResponse);
 
             ValidationResponse vr = new ValidationResponse();
             when(candidateManager.updateFilesDataStructure(
-                    ArgumentMatchers.eq(candidateId),
-                    ArgumentMatchers.any(),
-                    ArgumentMatchers.any())).thenReturn(vr);
+                ArgumentMatchers.eq(candidateId),
+                ArgumentMatchers.any(),
+                ArgumentMatchers.any())).thenReturn(vr);
 
             FilesDataStructure fds = new FilesDataStructure();
-            fds.setArtifacts(Arrays.asList("a","b"));
+            fds.setArtifacts(Arrays.asList("a", "b"));
             fds.setNested(Arrays.asList("foo", "bar"));
             fds.setUnassigned(Arrays.asList("c", "d"));
             fds.setModules(Arrays.asList(new Module(), new Module()));
 
             when(candidateManager.getFilesDataStructure(
-                    ArgumentMatchers.eq(candidateId),
-                    ArgumentMatchers.any())).thenReturn(Optional.of(fds));
+                ArgumentMatchers.eq(candidateId),
+                ArgumentMatchers.any())).thenReturn(Optional.of(fds));
 
             orchestrationTemplateCandidate =
-                new OrchestrationTemplateCandidateImpl(candidateManager, vendorSoftwareProductManager, activityLogManager);
-
+                new OrchestrationTemplateCandidateImpl(candidateManager, vendorSoftwareProductManager, activityLogManager,
+                    artifactStorageManager, packageSizeReducer);
 
-        }catch (Exception e){
-           logger.error(e.getMessage(), e);
+        } catch (Exception e) {
+            logger.error(e.getMessage(), e);
         }
     }
 
     @Test
-    public void uploadSignedTest() {
+    void uploadSignedTest() {
         Response response = orchestrationTemplateCandidate
             .upload("1", "1", mockAttachment("filename.zip", this.getClass().getResource("/files/sample-signed.zip")),
                 "1");
@@ -148,7 +155,21 @@ public class OrchestrationTemplateCandidateImplTest {
     }
 
     @Test
-    public void uploadNotSignedTest() {
+    void uploadNotSignedTest() throws IOException {
+        Response response = orchestrationTemplateCandidate.upload("1", "1",
+            mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), "1");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty());
+    }
+
+    @Test
+    void uploadNotSignedArtifactStorageManagerIsEnabledTest() throws IOException {
+        when(artifactStorageManager.isEnabled()).thenReturn(true);
+        final Path path = Path.of("src/test/resources/files/sample-not-signed.csar");
+        when(artifactStorageManager.upload(anyString(), anyString(), any())).thenReturn(new PersistentStorageArtifactInfo(path));
+        final byte[] bytes = Files.readAllBytes(path);
+        when(packageSizeReducer.reduce(any())).thenReturn(bytes);
+
         Response response = orchestrationTemplateCandidate.upload("1", "1",
             mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")), "1");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
@@ -167,7 +188,7 @@ public class OrchestrationTemplateCandidateImplTest {
                 bytes = IOUtils.toByteArray(fileToUpload);
             } catch (final IOException e) {
                 logger.error("unexpected exception", e);
-                Assert.fail("Not able to convert file to byte array");
+                fail("Not able to convert file to byte array");
             }
         }
         when(attachment.getObject(ArgumentMatchers.any())).thenReturn(bytes);
@@ -175,7 +196,7 @@ public class OrchestrationTemplateCandidateImplTest {
     }
 
     @Test
-    public void uploadSignNotValidTest() {
+    void uploadSignNotValidTest() {
         Response response = orchestrationTemplateCandidate
             .upload("1", "1", mockAttachment("filename.zip", null), "1");
         assertEquals(Status.NOT_ACCEPTABLE.getStatusCode(), response.getStatus());
@@ -183,82 +204,78 @@ public class OrchestrationTemplateCandidateImplTest {
     }
 
     @Test
-    public void testCandidateGet() throws IOException {
+    void testCandidateGet() throws IOException {
         Response rsp = orchestrationTemplateCandidate.get(candidateId, versionId, user);
-        Assert.assertEquals("Response status equals", Response.Status.OK.getStatusCode(), rsp.getStatus());
-        Assert.assertNotEquals(rsp.getHeaderString("Content-Disposition").indexOf("Candidate"),-1);
-        byte[] content = (byte[])rsp.getEntity();
-        Assert.assertEquals("World", new String(content));
+        assertEquals(Response.Status.OK.getStatusCode(), rsp.getStatus(), "Response status equals");
+        assertNotEquals(rsp.getHeaderString("Content-Disposition").indexOf("Candidate"), -1);
+        byte[] content = (byte[]) rsp.getEntity();
+        assertEquals("World", new String(content));
     }
 
     @Test
-    public void testVendorSoftwareProductGet() throws IOException {
+    void testVendorSoftwareProductGet() throws IOException {
         Response rsp = orchestrationTemplateCandidate.get(softwareProductId, versionId, user);
-        Assert.assertEquals("Response status equals", Response.Status.OK.getStatusCode(), rsp.getStatus());
-        Assert.assertNotEquals(rsp.getHeaderString("Content-Disposition").indexOf("Processed"),-1);
-        byte[] content = (byte[])rsp.getEntity();
-        Assert.assertEquals("World", new String(content));
+        assertEquals(Response.Status.OK.getStatusCode(), rsp.getStatus(), "Response status equals");
+        assertNotEquals(rsp.getHeaderString("Content-Disposition").indexOf("Processed"), -1);
+        byte[] content = (byte[]) rsp.getEntity();
+        assertEquals("World", new String(content));
     }
 
     @Test
-    public void testMissingGet() throws IOException {
+    void testMissingGet() throws IOException {
         Response rsp = orchestrationTemplateCandidate.get(UUID.randomUUID().toString(), versionId, user);
-        Assert.assertEquals("Response status equals", Response.Status.NOT_FOUND.getStatusCode(), rsp.getStatus());
+        assertEquals(Response.Status.NOT_FOUND.getStatusCode(), rsp.getStatus(), "Response status equals");
     }
 
     @Test
-    public void testAbort() {
+    void testAbort() {
         try {
             Response rsp = orchestrationTemplateCandidate.abort(candidateId, versionId);
-            Assert.assertEquals("Response status equals", Response.Status.OK.getStatusCode(), rsp.getStatus());
-            Assert.assertNull(rsp.getEntity());
-        }
-        catch (Exception ex) {
+            assertEquals(Response.Status.OK.getStatusCode(), rsp.getStatus(), "Response status equals");
+            assertNull(rsp.getEntity());
+        } catch (Exception ex) {
             logger.error("unexpected exception", ex);
-            Assert.fail("abort should not throw an exception");
+            fail("abort should not throw an exception");
         }
     }
 
     @Test
-    public void testProcess() {
+    void testProcess() {
         try {
             Response rsp = orchestrationTemplateCandidate.process(candidateId, versionId, user);
-            Assert.assertEquals("Response status equals", Response.Status.OK.getStatusCode(), rsp.getStatus());
-            Assert.assertNotNull(rsp.getEntity());
-            OrchestrationTemplateActionResponseDto dto = (OrchestrationTemplateActionResponseDto)rsp.getEntity();
-            Assert.assertEquals("status check", UploadFileStatus.Success, dto.getStatus());
-        }
-        catch (Exception ex) {
+            assertEquals(Response.Status.OK.getStatusCode(), rsp.getStatus(), "Response status equals");
+            assertNotNull(rsp.getEntity());
+            OrchestrationTemplateActionResponseDto dto = (OrchestrationTemplateActionResponseDto) rsp.getEntity();
+            assertEquals(UploadFileStatus.Success, dto.getStatus(), "status check");
+        } catch (Exception ex) {
             logger.error("unexpected exception", ex);
-            Assert.fail("abort should not throw an exception");
+            fail("abort should not throw an exception");
         }
     }
 
     @Test
-    public void testFilesDataStructureUpload() {
+    void testFilesDataStructureUpload() {
         try {
             FileDataStructureDto dto = new FileDataStructureDto();
             dto.setArtifacts(Arrays.asList("a", "b", "c"));
             Response rsp = orchestrationTemplateCandidate.updateFilesDataStructure(candidateId, versionId, dto, user);
-            Assert.assertEquals("Response status equals", Response.Status.OK.getStatusCode(), rsp.getStatus());
-        }
-        catch (Exception ex) {
+            assertEquals(Response.Status.OK.getStatusCode(), rsp.getStatus(), "Response status equals");
+        } catch (Exception ex) {
             logger.error("unexpected exception", ex);
-            Assert.fail("abort should not throw an exception");
+            fail("abort should not throw an exception");
         }
     }
 
     @Test
-    public void testFilesDataStructureGet() {
+    void testFilesDataStructureGet() {
         try {
             FileDataStructureDto dto = new FileDataStructureDto();
             dto.setArtifacts(Arrays.asList("a", "b", "c"));
             Response rsp = orchestrationTemplateCandidate.getFilesDataStructure(candidateId, versionId, user);
-            Assert.assertEquals("Response status equals", Response.Status.OK.getStatusCode(), rsp.getStatus());
-        }
-        catch (Exception ex) {
+            assertEquals(Response.Status.OK.getStatusCode(), rsp.getStatus(), "Response status equals");
+        } catch (Exception ex) {
             logger.error("unexpected exception", ex);
-            Assert.fail("abort should not throw an exception");
+            fail("abort should not throw an exception");
         }
     }
 
index e5b68cb..14b839e 100644 (file)
@@ -49,6 +49,7 @@ import org.apache.commons.io.FilenameUtils;
 import org.openecomp.core.utilities.file.FileContentHandler;
 import org.openecomp.core.utilities.json.JsonUtil;
 import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
 import org.openecomp.sdc.common.CommonConfigurationManager;
 import org.openecomp.sdc.common.utils.CommonUtil;
 import org.openecomp.sdc.common.utils.SdcCommon;
@@ -76,12 +77,15 @@ public class OnboardingPackageProcessor {
     private final CnfPackageValidator cnfPackageValidator;
     private FileContentHandler packageContent;
 
-    public OnboardingPackageProcessor(final String packageFileName, final byte[] packageFileContent,
-        final CnfPackageValidator cnfPackageValidator) {
+    public OnboardingPackageProcessor(final String packageFileName, final byte[] packageFileContent, final CnfPackageValidator cnfPackageValidator,
+                                      final ArtifactInfo artifactInfo) {
         this.packageFileName = packageFileName;
         this.packageFileContent = packageFileContent;
         this.cnfPackageValidator = cnfPackageValidator;
         onboardPackageInfo = processPackage();
+        if (onboardPackageInfo != null) {
+            onboardPackageInfo.setArtifactInfo(artifactInfo);
+        }
     }
 
     public Optional<OnboardPackageInfo> getOnboardPackageInfo() {
index e7367c2..f106239 100644 (file)
@@ -42,7 +42,7 @@ import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo;
 import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage;
 import org.openecomp.sdc.vendorsoftwareproduct.types.UploadFileResponse;
 
-public class OrchestrationTemplateCSARHandler extends BaseOrchestrationTemplateHandler implements OrchestrationTemplateFileHandler {
+public class OrchestrationTemplateCSARHandler extends BaseOrchestrationTemplateHandler {
 
     @Override
     public UploadFileResponse validate(final OnboardPackageInfo onboardPackageInfo) {
@@ -100,9 +100,10 @@ public class OrchestrationTemplateCSARHandler extends BaseOrchestrationTemplateH
         final OnboardPackage csarPackage = onboardPackageInfo.getOnboardPackage();
         final OnboardPackage originalOnboardPackage = onboardPackageInfo.getOriginalOnboardPackage();
         try {
-            candidateService.updateCandidateUploadData(vspDetails.getId(), vspDetails.getVersion(),
-                new OrchestrationTemplateCandidateData(csarPackage.getFileContent(), "", csarPackage.getFileExtension(), csarPackage.getFilename(),
-                    originalOnboardPackage.getFilename(), originalOnboardPackage.getFileExtension(), originalOnboardPackage.getFileContent()));
+            final var candidateData = new OrchestrationTemplateCandidateData(csarPackage.getFileContent(), csarPackage.getFileExtension(),
+                csarPackage.getFilename(), originalOnboardPackage.getFilename(), originalOnboardPackage.getFileExtension(),
+                originalOnboardPackage.getFileContent(), onboardPackageInfo.getArtifactInfo());
+            candidateService.updateCandidateUploadData(vspDetails.getId(), vspDetails.getVersion(), candidateData);
         } catch (final Exception exception) {
             logger.error(getErrorWithParameters(Messages.FILE_LOAD_CONTENT_ERROR.getErrorMessage(), getHandlerType().toString()), exception);
             uploadFileResponse.addStructureError(SdcCommon.UPLOAD_FILE, new ErrorMessage(ErrorLevel.ERROR, exception.getMessage()));
index 65ce2f5..e534ede 100644 (file)
@@ -34,7 +34,7 @@ import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackage;
 import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo;
 import org.openecomp.sdc.vendorsoftwareproduct.types.UploadFileResponse;
 
-public class OrchestrationTemplateZipHandler extends BaseOrchestrationTemplateHandler implements OrchestrationTemplateFileHandler {
+public class OrchestrationTemplateZipHandler extends BaseOrchestrationTemplateHandler {
 
     @Override
     public UploadFileResponse validate(final OnboardPackageInfo onboardPackageInfo) {
@@ -55,6 +55,7 @@ public class OrchestrationTemplateZipHandler extends BaseOrchestrationTemplateHa
                     uploadFileResponse.getErrors());
             candidateData.setFileName(zipPackage.getFilename());
             candidateData.setFileSuffix(zipPackage.getFileExtension());
+            candidateData.setArtifactInfo(onboardPackageInfo.getArtifactInfo());
             candidateService.updateCandidateUploadData(vspDetails.getId(), vspDetails.getVersion(), candidateData);
         } catch (final Exception exception) {
             logger.error(getErrorWithParameters(Messages.FILE_LOAD_CONTENT_ERROR.getErrorMessage(), getHandlerType().toString()), exception);
index a78185e..4bb1d98 100644 (file)
@@ -20,7 +20,9 @@ package org.openecomp.sdc.vendorsoftwareproduct.types;
 
 import java.nio.ByteBuffer;
 import lombok.Getter;
+import lombok.Setter;
 import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
 import org.openecomp.sdc.vendorsoftwareproduct.exception.OnboardPackageException;
 
 @Getter
@@ -29,6 +31,8 @@ public class OnboardPackageInfo {
     private final OnboardingTypesEnum packageType;
     private final OnboardPackage originalOnboardPackage;
     private final OnboardPackage onboardPackage;
+    @Setter
+    private ArtifactInfo artifactInfo;
 
     public OnboardPackageInfo(final OnboardPackage onboardPackage, final OnboardingTypesEnum packageType) {
         this(onboardPackage, onboardPackage, packageType);
index 6c847a7..80e257c 100644 (file)
@@ -118,7 +118,7 @@ public class OnboardingPackageProcessorTest {
     @Test
     public void processPackage() {
         final OnboardingPackageProcessor onboardingPackageProcessor = new OnboardingPackageProcessor(packageName,
-            packageBytes, cnfPackageValidator);
+            packageBytes, cnfPackageValidator, null);
         assertThat("Should contains errors", onboardingPackageProcessor.hasErrors(), is(!expectedErrorSet.isEmpty()));
         assertThat("Should have the same number of errors", onboardingPackageProcessor.getErrorMessages().size(),
             equalTo(expectedErrorSet.size()));
index 795da77..d9cd72e 100644 (file)
@@ -34,8 +34,7 @@ import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.validation.CnfPac
 
 public class OnboardingPackageProcessorUnitTest {
 
-    private OnboardingPackageProcessor processor = new OnboardingPackageProcessor("unitTestPackage",
-        null, new CnfPackageValidator());
+    private final OnboardingPackageProcessor processor = new OnboardingPackageProcessor("unitTestPackage", null, new CnfPackageValidator(), null);
 
     @Test
     public void shouldValidateZipPackage_helmWithoutHeat() {
index 27d8041..5f5f9eb 100644 (file)
@@ -90,7 +90,7 @@ public class CsarSecurityValidatorTest {
     private OnboardSignedPackage loadSignedPackage(final String packageName, final byte[] packageBytes,
         CnfPackageValidator cnfPackageValidator) {
         final OnboardingPackageProcessor onboardingPackageProcessor =
-            new OnboardingPackageProcessor(packageName, packageBytes, cnfPackageValidator);
+            new OnboardingPackageProcessor(packageName, packageBytes, cnfPackageValidator, null);
         final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
         if (onboardPackageInfo == null) {
             fail("Unexpected error. Could not load original package");
index 474dd48..5695137 100644 (file)
@@ -52,4 +52,11 @@ basicAuth:
   excludedUrls: "/v1.0/healthcheck"
 
 zipValidation:
-  ignoreManifest: false
\ No newline at end of file
+  ignoreManifest: false
+
+externalCsarStore:
+  storeCsarsExternally: false
+  fullPath: "/home/onap/temp/"
+  foldersToStrip:
+    - Files/images
+  sizeLimit: 10000000
\ No newline at end of file
index 91ed6ae..28ce5d2 100644 (file)
@@ -201,7 +201,12 @@ public enum Messages {
     /* Notifications */
     FAILED_TO_MARK_NOTIFICATION_AS_READ("Failed to mark notifications as read"),
     FAILED_TO_UPDATE_LAST_SEEN_NOTIFICATION("Failed to update last seen notification for user %s"),
-    FAILED_TO_VERIFY_SIGNATURE("Could not verify signature of signed package.");
+    FAILED_TO_VERIFY_SIGNATURE("Could not verify signature of signed package."),
+    EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH("externalCsarStore configuration failure, missing 'fullPath'"),
+    ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT("An error has occurred while persisting the artifact: %s"),
+    ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE("An error has occurred while reducing the artifact's size: %s"),
+    UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING("An unexpected problem happened while getting '%s'"),
+    PERSISTENCE_STORE_IS_DOWN_OR_NOT_AVAILABLE("Persistence store is down or not available. Error: '%s'");
     // @formatter:on
 
     private String errorMessage;
index fdd143d..b079474 100644 (file)
@@ -20,6 +20,7 @@ import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
 import org.openecomp.core.utilities.json.JsonUtil;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
 import org.openecomp.sdc.heat.datatypes.structure.ValidationStructureList;
 
 @Getter
@@ -35,17 +36,19 @@ public class OrchestrationTemplateCandidateData {
     private ByteBuffer originalFileContentData;
     private String originalFileName;
     private String originalFileSuffix;
+    private ArtifactInfo artifactInfo;
 
-    public OrchestrationTemplateCandidateData(final ByteBuffer contentData, final String dataStructureJson, final String fileSuffix,
-                                              final String fileName, final String originalFileName, final String originalFileSuffix,
-                                              final ByteBuffer originalFileContentData) {
+    public OrchestrationTemplateCandidateData(final ByteBuffer contentData, final String fileSuffix, final String fileName,
+                                              final String originalFileName, final String originalFileSuffix,
+                                              final ByteBuffer originalFileContentData, final ArtifactInfo artifactInfo) {
         this.contentData = contentData;
-        this.filesDataStructure = dataStructureJson;
+        this.filesDataStructure = "";
         this.fileSuffix = fileSuffix;
         this.fileName = fileName;
         this.originalFileName = originalFileName;
         this.originalFileSuffix = originalFileSuffix;
         this.originalFileContentData = originalFileContentData;
+        this.artifactInfo = artifactInfo;
     }
 
     public ValidationStructureList getValidationDataStructure() {
diff --git a/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/impl/zusammen/OrchestrationTemplateCandidateDaoZusammenException.java b/openecomp-be/lib/openecomp-sdc-vendor-software-product-lib/openecomp-sdc-vendor-software-product-core/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/dao/impl/zusammen/OrchestrationTemplateCandidateDaoZusammenException.java
new file mode 100644 (file)
index 0000000..5a0a53a
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.vendorsoftwareproduct.dao.impl.zusammen;
+
+public class OrchestrationTemplateCandidateDaoZusammenException extends RuntimeException {
+
+    public OrchestrationTemplateCandidateDaoZusammenException(final String message) {
+        super(message);
+    }
+}
index fb26714..e5c5996 100644 (file)
@@ -12,6 +12,9 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
+ * ============LICENSE_END=========================================================
+ * Modifications copyright (c) 2021 Nordix Foundation
+ * ================================================================================
  */
 package org.openecomp.sdc.vendorsoftwareproduct.dao.impl.zusammen;
 
@@ -27,10 +30,21 @@ import com.amdocs.zusammen.datatypes.item.ElementContext;
 import com.amdocs.zusammen.utils.fileutils.FileUtils;
 import java.io.ByteArrayInputStream;
 import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
 import java.util.Optional;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
 import org.openecomp.core.utilities.json.JsonUtil;
 import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
 import org.openecomp.core.zusammen.api.ZusammenAdaptor;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
+import org.openecomp.sdc.be.csar.storage.ArtifactStorageConfig;
+import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager;
+import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageConfig;
+import org.openecomp.sdc.be.csar.storage.PersistentVolumeArtifactStorageManager;
+import org.openecomp.sdc.common.CommonConfigurationManager;
+import org.openecomp.sdc.common.errors.Messages;
 import org.openecomp.sdc.datatypes.model.ElementType;
 import org.openecomp.sdc.heat.datatypes.structure.ValidationStructureList;
 import org.openecomp.sdc.logging.api.Logger;
@@ -44,10 +58,13 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat
 
     private static final Logger logger = LoggerFactory.getLogger(OrchestrationTemplateCandidateDaoZusammenImpl.class);
     private static final String EMPTY_DATA = "{}";
+    private static final String EXTERNAL_CSAR_STORE = "externalCsarStore";
     private final ZusammenAdaptor zusammenAdaptor;
+    private final ArtifactStorageManager artifactStorageManager;
 
-    public OrchestrationTemplateCandidateDaoZusammenImpl(ZusammenAdaptor zusammenAdaptor) {
+    public OrchestrationTemplateCandidateDaoZusammenImpl(final ZusammenAdaptor zusammenAdaptor) {
         this.zusammenAdaptor = zusammenAdaptor;
+        this.artifactStorageManager = new PersistentVolumeArtifactStorageManager(readArtifactStorageConfiguration());
     }
 
     @Override
@@ -139,11 +156,24 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat
         candidateContentElement.setData(new ByteArrayInputStream(candidateData.getContentData().array()));
         candidateContentElement.getInfo().addProperty(InfoPropertyName.FILE_SUFFIX.getVal(), candidateData.getFileSuffix());
         candidateContentElement.getInfo().addProperty(InfoPropertyName.FILE_NAME.getVal(), candidateData.getFileName());
+        final String versionId = version.getId();
         if (OnboardingTypesEnum.CSAR.toString().equalsIgnoreCase(candidateData.getFileSuffix())) {
             final ZusammenElement originalPackageElement = buildStructuralElement(ElementType.ORIGINAL_ONBOARDED_PACKAGE, Action.UPDATE);
-            originalPackageElement.getInfo().addProperty(InfoPropertyName.ORIGINAL_FILE_NAME.getVal(), candidateData.getOriginalFileName());
-            originalPackageElement.getInfo().addProperty(InfoPropertyName.ORIGINAL_FILE_SUFFIX.getVal(), candidateData.getOriginalFileSuffix());
-            originalPackageElement.setData(new ByteArrayInputStream(candidateData.getOriginalFileContentData().array()));
+            final String originalFileName = candidateData.getOriginalFileName();
+            final String originalFileSuffix = candidateData.getOriginalFileSuffix();
+            originalPackageElement.getInfo().addProperty(InfoPropertyName.ORIGINAL_FILE_NAME.getVal(), originalFileName);
+            originalPackageElement.getInfo().addProperty(InfoPropertyName.ORIGINAL_FILE_SUFFIX.getVal(), originalFileSuffix);
+            originalPackageElement.getInfo().addProperty("storeCsarsExternally", artifactStorageManager.isEnabled());
+            if (artifactStorageManager.isEnabled()) {
+                final ArtifactInfo candidateArtifactInfo = candidateData.getArtifactInfo();
+                if (candidateArtifactInfo == null) {
+                    throw new OrchestrationTemplateCandidateDaoZusammenException("No artifact info provided");
+                }
+                final ArtifactInfo artifactInfo = artifactStorageManager.persist(vspId, versionId, candidateArtifactInfo);
+                originalPackageElement.setData(new ByteArrayInputStream(artifactInfo.getPath().toString().getBytes(StandardCharsets.UTF_8)));
+            } else {
+                originalPackageElement.setData(new ByteArrayInputStream(candidateData.getOriginalFileContentData().array()));
+            }
             candidateElement.addSubElement(originalPackageElement);
         }
         final ZusammenElement validationData = buildStructuralElement(ElementType.OrchestrationTemplateCandidateValidationData, Action.UPDATE);
@@ -152,8 +182,8 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat
         }
         candidateElement.addSubElement(validationData);
         candidateElement.addSubElement(candidateContentElement);
-        SessionContext context = createSessionContext();
-        ElementContext elementContext = new ElementContext(vspId, version.getId());
+        final var context = createSessionContext();
+        final var elementContext = new ElementContext(vspId, versionId);
         zusammenAdaptor.saveElement(context, elementContext, candidateElement, "Update Orchestration Template Candidate");
         logger.info("Finished uploading candidate data entity for vsp id {}", vspId);
     }
@@ -197,16 +227,24 @@ public class OrchestrationTemplateCandidateDaoZusammenImpl implements Orchestrat
         return Optional.empty();
     }
 
-    public enum InfoPropertyName {
-        FILE_SUFFIX("fileSuffix"), FILE_NAME("fileName"), ORIGINAL_FILE_NAME("originalFilename"), ORIGINAL_FILE_SUFFIX("originalFileSuffix");
-        private final String val;
-
-        InfoPropertyName(String val) {
-            this.val = val;
+    private ArtifactStorageConfig readArtifactStorageConfiguration() {
+        final var commonConfigurationManager = CommonConfigurationManager.getInstance();
+        final boolean isEnabled = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "storeCsarsExternally", false);
+        logger.info("ArtifactConfig.isEnabled: '{}'", isEnabled);
+        final String storagePathString = commonConfigurationManager.getConfigValue(EXTERNAL_CSAR_STORE, "fullPath", null);
+        logger.info("ArtifactConfig.storagePath: '{}'", storagePathString);
+        if (isEnabled && storagePathString == null) {
+            throw new OrchestrationTemplateCandidateDaoZusammenException(
+                Messages.EXTERNAL_CSAR_STORE_CONFIGURATION_FAILURE_MISSING_FULL_PATH.getErrorMessage());
         }
+        final var storagePath = storagePathString == null ? null : Path.of(storagePathString);
+        return new PersistentVolumeArtifactStorageConfig(isEnabled, storagePath);
+    }
 
-        private String getVal() {
-            return val;
-        }
+    @Getter
+    @AllArgsConstructor
+    private enum InfoPropertyName {
+        FILE_SUFFIX("fileSuffix"), FILE_NAME("fileName"), ORIGINAL_FILE_NAME("originalFilename"), ORIGINAL_FILE_SUFFIX("originalFileSuffix");
+        private final String val;
     }
 }
diff --git a/pom.xml b/pom.xml
index b12ec7b..1d9a2c9 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,7 @@ Modifications copyright (c) 2018-2019 Nokia
         <ws.rs.version>2.1</ws.rs.version>
 
         <jetty.version>9.4.41.v20210516</jetty.version>
+        <cxf.version>3.4.3</cxf.version>
 
         <!-- JSON and YAML Parsing -->
         <jackson.version>2.12.1</jackson.version>