Add filesystem artifact repository implementation (#336)

Signed-off-by: Michael Hirsch <michael.hirsch@bosch-si.com>
This commit is contained in:
Michael Hirsch
2016-11-14 11:23:50 +01:00
committed by Kai Zimmermann
parent 9b42c8cf57
commit 8be49a1184
48 changed files with 682 additions and 425 deletions

View File

@@ -1 +1,2 @@
/target/
/artifactrepo/*

View File

@@ -0,0 +1,23 @@
# Eclipse.IoT hawkBit - Artifact Repository MongoDB
HawkBit Artifact Repository is a library for storing binary artifacts and metadata into MongoDB.
## Using Artifact Repository MongoDB Extension
The module contains a spring-boot autoconfiguration for easily integration into spring-boot projects.
For using this extension in the hawkbit-example-application you just need to add the maven dependency.
```
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-extension-artifact-repository-mongo</artifactId>
<version>${project.version}</version>
</dependency>
```
If you do not have a mongoDB running you can use the the flapdoodle project to download and start an mongoDB on demand
```
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>${flapdoodle.version}</version>
</dependency>
```

View File

@@ -16,7 +16,7 @@
<version>0.2.0-SNAPSHOT</version>
<artifactId>hawkbit-parent</artifactId>
</parent>
<artifactId>hawkbit-artifact-repository-mongo</artifactId>
<artifactId>hawkbit-extension-artifact-repository-mongo</artifactId>
<name>hawkBit :: Artifact Repository Mongo</name>
@@ -70,7 +70,6 @@
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>

View File

@@ -44,13 +44,14 @@ import com.mongodb.gridfs.GridFSFile;
* The file management which looks up all the file in the file tore.
*
*/
public class ArtifactStore implements ArtifactRepository {
public class MongoDBArtifactStore implements ArtifactRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(ArtifactStore.class);
private static final Logger LOGGER = LoggerFactory.getLogger(MongoDBArtifactStore.class);
/**
* The mongoDB field which holds the filename of the file to download. SP
* Server uses the SHA hash as a filename and lookup in the mongoDB.
* The mongoDB field which holds the filename of the file to download.
* hawkBit update-server uses the SHA hash as a filename and lookup in the
* mongoDB.
*/
private static final String FILENAME = "filename";
@@ -206,7 +207,7 @@ public class ArtifactStore implements ArtifactRepository {
* @return a paged list of artifacts mapped from the given dbFiles
*/
private List<DbArtifact> map(final List<GridFSDBFile> dbFiles) {
return dbFiles.stream().map(ArtifactStore::map).collect(Collectors.toList());
return dbFiles.stream().map(MongoDBArtifactStore::map).collect(Collectors.toList());
}
/**

View File

@@ -13,17 +13,16 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto configuration for the {@link ArtifactStore}.
* Auto configuration for the {@link MongoDBArtifactStore}.
*/
@Configuration
@ConditionalOnMissingBean(value = ArtifactRepository.class)
public class ArtifactStoreAutoConfiguration {
public class MongoDBArtifactStoreAutoConfiguration {
/**
* @return Default {@link ArtifactRepository} implementation.
*/
@Bean
public ArtifactRepository artifactRepository() {
return new ArtifactStore();
return new MongoDBArtifactStore();
}
}

View File

@@ -1,3 +1,3 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.eclipse.hawkbit.artifact.repository.ArtifactStoreAutoConfiguration
org.eclipse.hawkbit.artifact.repository.MongoDBArtifactStoreAutoConfiguration

View File

@@ -34,12 +34,12 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository")
@Stories("Artifact Store MongoDB")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { ArtifactStoreAutoConfiguration.class, TestConfiguration.class })
@SpringApplicationConfiguration(classes = { MongoDBArtifactStoreAutoConfiguration.class, TestConfiguration.class })
@TestPropertySource(properties = { "spring.data.mongodb.port=0", "spring.mongodb.embedded.version=3.2.7" })
public class ArtifactStoreTest {
public class MongoDBArtifactStoreTest {
@Autowired
private ArtifactStore artifactStoreUnderTest;
private MongoDBArtifactStore artifactStoreUnderTest;
@Autowired
private GridFsOperations gridFs;

View File

@@ -22,6 +22,7 @@
<modules>
<module>hawkbit-extension-uaa</module>
<module>hawkbit-extension-artifact-repository-mongo</module>
</modules>
</project>

View File

@@ -0,0 +1 @@
/artifactrepo/*

View File

@@ -0,0 +1,17 @@
# Eclipse.IoT hawkBit - Artifact Repository File System
This module contains the implementation of the `ArtifactRepository` based on the file-system.
It's a very convenient and easy implementation of storing the artifact binaries into the file-system based on the SHA-1 hash naming.
Due the limit of many file-systems of files within one directory, the files
are stored in different sub-directories based on the last four digits of the
SHA1-hash `/basepath/[two digit sha1]/[two digit sha1/sha1-hash-filename]`.
# Compile
#### Build hawkbit-artifact-repository-filesystem
```
$ cd hawkbit/hawkbit-artifact-repository-filesystem
$ mvn clean install
```

View File

@@ -0,0 +1,69 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-parent</artifactId>
<version>0.2.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-artifact-repository-filesystem</artifactId>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import com.google.common.base.Throwables;
/**
* A {@link DbArtifact} implementation which dynamically creates a
* {@link FileInputStream} on calling {@link #getFileInputStream()}.
*/
public class ArtifactFilesystem extends DbArtifact {
private final File file;
ArtifactFilesystem(final File file) {
this.file = file;
}
@Override
// suppress warning, this InputStream needs to be closed by the caller, this
// cannot be closed in this method
@SuppressWarnings("squid:S2095")
public InputStream getFileInputStream() {
try {
return new BufferedInputStream(new FileInputStream(file));
} catch (final FileNotFoundException e) {
throw Throwables.propagate(e);
}
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for the file-system repository, e.g. the base-path
* to store the files.
*/
@ConfigurationProperties("org.eclipse.hawkbit.repository.file")
public class ArtifactFilesystemProperties {
/**
* The base-path of the directory to store the artifacts.
*/
private String path = "./artifactrepo";
public String getPath() {
return path;
}
public void setPath(final String path) {
this.path = path;
}
}

View File

@@ -0,0 +1,188 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import com.google.common.base.Splitter;
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteStreams;
/**
* Implementation of the {@link ArtifactRepository} to store artifacts on the
* file-system. The files are stored by their SHA1 hash of the artifact binary.
* Duplicate files with the same SHA1 hash will only stored once.
*
* All files are stored flat in one base directory configured in the
* {@link ArtifactFilesystemProperties#getPath()}.
*
* Due the limit of many file-systems of files within one directory, the files
* are stored in different sub-directories based on the last four digits of the
* SHA1-hash {@code (/basepath/[two digit sha1]/[two digit sha1])}.
*/
public class ArtifactFilesystemRepository implements ArtifactRepository {
private static final String TEMP_FILE_PREFIX = "tmp";
private static final String TEMP_FILE_SUFFIX = "artifactrepo";
private final ArtifactFilesystemProperties artifactResourceProperties;
/**
* Constructor.
*
* @param artifactResourceProperties
* the properties which holds the necessary configuration for the
* file-system repository
*/
public ArtifactFilesystemRepository(final ArtifactFilesystemProperties artifactResourceProperties) {
this.artifactResourceProperties = artifactResourceProperties;
}
@Override
public ArtifactFilesystem store(final InputStream content, final String filename, final String contentType) {
return store(content, filename, contentType, null);
}
@Override
// suppress warning, of not strong enough hashing algorithm, SHA-1 and MD5
// is not used security related
@SuppressWarnings("squid:S2070")
public ArtifactFilesystem store(final InputStream content, final String filename, final String contentType,
final DbArtifactHash hash) {
final MessageDigest mdSHA1;
final MessageDigest mdMD5;
try {
mdSHA1 = MessageDigest.getInstance("SHA1");
mdMD5 = MessageDigest.getInstance("MD5");
} catch (final NoSuchAlgorithmException e) {
throw new ArtifactStoreException(e.getMessage(), e);
}
final File file = createTempFile();
final DbArtifact artifact = store(content, contentType, hash, mdSHA1, mdMD5, file);
return renameFileToSHA1Naming(file, artifact);
}
@Override
public void deleteBySha1(final String sha1Hash) {
FileUtils.deleteQuietly(getFile(sha1Hash));
}
@Override
public ArtifactFilesystem getArtifactBySha1(final String sha1) {
final File file = getFile(sha1);
if (!file.exists()) {
return null;
}
final ArtifactFilesystem artifact = new ArtifactFilesystem(file);
artifact.setArtifactId(sha1);
artifact.setHashes(new DbArtifactHash(sha1, null));
artifact.setSize(file.length());
return artifact;
}
private DbArtifact store(final InputStream content, final String contentType, final DbArtifactHash hash,
final MessageDigest mdSHA1, final MessageDigest mdMD5, final File file) {
final DbArtifact artifact = new DbArtifact();
try (final DigestOutputStream outputstream = openFileOutputStream(file, mdSHA1, mdMD5)) {
final long artifactSize = ByteStreams.copy(content, outputstream);
outputstream.flush();
final String sha1Hash = BaseEncoding.base16().lowerCase().encode(mdSHA1.digest());
final String md5Hash = BaseEncoding.base16().lowerCase().encode(mdMD5.digest());
artifact.setArtifactId(sha1Hash);
artifact.setSize(artifactSize);
artifact.setContentType(contentType);
artifact.setHashes(new DbArtifactHash(sha1Hash, md5Hash));
checkHashes(artifact, hash);
} catch (final IOException e) {
throw new ArtifactStoreException(e.getMessage(), e);
} catch (final HashNotMatchException e) {
file.delete();
throw e;
}
return artifact;
}
private ArtifactFilesystem renameFileToSHA1Naming(final File file, final DbArtifact artifact) {
final File fileSHA1Naming = getFile(artifact.getHashes().getSha1());
final ArtifactFilesystem fileSystemArtifact = new ArtifactFilesystem(fileSHA1Naming);
if (fileSHA1Naming.exists()) {
FileUtils.deleteQuietly(file);
} else if (!file.renameTo(fileSHA1Naming)) {
throw new ArtifactStoreException("Could not store the file " + fileSHA1Naming);
}
file.delete();
fileSystemArtifact.setArtifactId(artifact.getArtifactId());
fileSystemArtifact.setContentType(artifact.getContentType());
fileSystemArtifact.setHashes(artifact.getHashes());
fileSystemArtifact.setSize(artifact.getSize());
return fileSystemArtifact;
}
private DbArtifact checkHashes(final DbArtifact artifact, final DbArtifactHash hash) {
if (hash == null) {
return artifact;
}
if (hash.getSha1() != null && !artifact.getHashes().getSha1().equals(hash.getSha1())) {
throw new HashNotMatchException("The given sh1 hash " + hash.getSha1()
+ " does not match with the calcualted sha1 hash " + artifact.getHashes().getSha1(),
HashNotMatchException.SHA1);
}
if (hash.getMd5() != null && !artifact.getHashes().getMd5().equals(hash.getMd5())) {
throw new HashNotMatchException("The given md5 hash " + hash.getMd5()
+ " does not match with the calcualted md5 hash " + artifact.getHashes().getMd5(),
HashNotMatchException.MD5);
}
return artifact;
}
private File createTempFile() {
try {
return File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX);
} catch (final IOException e) {
throw new ArtifactStoreException("Cannot create tempfile", e);
}
}
private File getFile(final String sha1) {
final File aritfactDirectory = getSha1DirectoryPath(sha1).toFile();
aritfactDirectory.mkdirs();
return new File(aritfactDirectory, sha1);
}
private Path getSha1DirectoryPath(final String sha1) {
final int length = sha1.length();
final List<String> folders = Splitter.fixedLength(2).splitToList(sha1.substring(length - 4, length));
final String folder1 = folders.get(0);
final String folder2 = folders.get(1);
return Paths.get(artifactResourceProperties.getPath(), folder1, folder2);
}
private DigestOutputStream openFileOutputStream(final File file, final MessageDigest mdSHA1,
final MessageDigest mdMD5) throws FileNotFoundException {
return new DigestOutputStream(
new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(file)), mdMD5), mdSHA1);
}
}

View File

@@ -0,0 +1,93 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import static org.fest.assertions.Assertions.assertThat;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Random;
import org.apache.commons.io.IOUtils;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.fest.assertions.api.Assertions;
import org.junit.Test;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Unit Tests - Artifact File System Repository")
@Stories("Test storing artifact binaries in the file-system")
public class ArtifactFilesystemRepositoryTest {
private final ArtifactFilesystemProperties artifactResourceProperties = new ArtifactFilesystemProperties();
private final ArtifactFilesystemRepository artifactFilesystemRepository = new ArtifactFilesystemRepository(
artifactResourceProperties);
@Test
@Description("Verfies that an artifact can be successfully stored in the file-system repository")
public void storeSuccessfully() throws IOException {
final byte[] fileContent = randomBytes();
final ArtifactFilesystem artifact = storeRandomArtifact(fileContent);
final byte[] readContent = new byte[fileContent.length];
IOUtils.read(artifact.getFileInputStream(), readContent);
assertThat(readContent).isEqualTo(fileContent);
}
@Test
@Description("Verfies that an artifact can be successfully stored in the file-system repository")
public void getStoredArtifactBasedOnSHA1Hash() {
final byte[] fileContent = randomBytes();
final ArtifactFilesystem artifact = storeRandomArtifact(fileContent);
final DbArtifact artifactBySha1 = artifactFilesystemRepository
.getArtifactBySha1(artifact.getHashes().getSha1());
assertThat(artifactBySha1).isNotNull();
}
@Test
@Description("Verfies that an artifact can be deleted in the file-system repository")
public void deleteStoredArtifactBySHA1Hash() {
final ArtifactFilesystem artifact = storeRandomArtifact(randomBytes());
artifactFilesystemRepository.deleteBySha1(artifact.getHashes().getSha1());
assertThat(artifactFilesystemRepository.getArtifactBySha1(artifact.getHashes().getSha1())).isNull();
}
@Test
@Description("Verfies that an artifact which does not exists is deleted quietly in the file-system repository")
public void deleteArtifactWhichDoesNotExistsBySHA1HashWithoutException() {
try {
artifactFilesystemRepository.deleteBySha1("sha1HashWhichDoesNotExists");
} catch (final Exception e) {
Assertions.fail("did not expect an exception while deleting a file which does not exists");
}
}
private ArtifactFilesystem storeRandomArtifact(final byte[] fileContent) {
final String fileName = "filename.tmp";
final ByteArrayInputStream inputStream = new ByteArrayInputStream(fileContent);
final ArtifactFilesystem store = artifactFilesystemRepository.store(inputStream, fileName, "application/txt");
return store;
}
private static byte[] randomBytes() {
final byte[] randomBytes = new byte[20];
final Random ran = new Random();
ran.nextBytes(randomBytes);
return randomBytes;
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.artifact.repository;
import static org.fest.assertions.Assertions.assertThat;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.fest.assertions.api.Assertions;
import org.junit.Test;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Unit Tests - Artifact File System Repository")
@Stories("Test storing artifact binaries in the file-system")
public class ArtifactFilesystemTest {
@Test
@Description("Verifies that an exception is thrown on opening an InputStream when file does not exists")
public void getInputStreamOfNonExistingFileThrowsException() {
final File file = new File("fileWhichTotalDoesNotExists");
final ArtifactFilesystem underTest = new ArtifactFilesystem(file);
try {
underTest.getFileInputStream();
Assertions.fail("Expected a FileNotFoundException because file does not exists");
} catch (final RuntimeException e) {
assertThat(e.getCause()).isInstanceOf(FileNotFoundException.class);
}
}
@Test
@Description("Verifies that an InputStream can be opened if file exists")
public void getInputStreamOfExistingFile() throws IOException {
final File createTempFile = File.createTempFile(ArtifactFilesystemTest.class.getSimpleName(), "");
createTempFile.deleteOnExit();
final ArtifactFilesystem underTest = new ArtifactFilesystem(createTempFile);
final byte[] buffer = new byte[1024];
IOUtils.read(underTest.getFileInputStream(), buffer);
}
}

View File

@@ -1,3 +0,0 @@
# HawkBit Artifact Repositoy
HawkBit Artifact Repository is library for storing binary artifacts and metadata into MongoDB.
It has an spring-boot autoconfiguration for easily integration into spring-boot projects.

View File

@@ -8,8 +8,7 @@
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
@@ -68,7 +67,13 @@
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-artifact-repository-filesystem</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.autoconfigure.repository;
import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemProperties;
import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemRepository;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto configuration for the {@link ArtifactFilesystemRepository}.
*/
@Configuration
@ConditionalOnMissingBean(ArtifactRepository.class)
@EnableConfigurationProperties(ArtifactFilesystemProperties.class)
public class ArtifactStoreAutoConfiguration {
/**
* @param artifactFilesystemProperties
* the artifact file system properties
* @return Default {@link ArtifactRepository} implementation.
*/
@Bean
public ArtifactRepository artifactRepository(final ArtifactFilesystemProperties artifactFilesystemProperties) {
return new ArtifactFilesystemRepository(artifactFilesystemProperties);
}
}

View File

@@ -11,4 +11,5 @@ org.eclipse.hawkbit.autoconfigure.event.EventPublisherAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.scheduling.AsyncConfigurerAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.scheduling.ExecutorAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.amqp.AmqpAutoConfiguration,\
org.eclipse.hawkbit.autoconfigure.security.InMemoryUserManagementConfiguration
org.eclipse.hawkbit.autoconfigure.security.InMemoryUserManagementConfiguration,\
org.eclipse.hawkbit.autoconfigure.repository.ArtifactStoreAutoConfiguration

View File

@@ -133,11 +133,6 @@
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>

View File

@@ -38,7 +38,7 @@ import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.test.SpringApplicationConfiguration;
@@ -62,7 +62,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Direct Device Integration API")
@Stories("Artifact Download Resource")
@SpringApplicationConfiguration(classes = DownloadTestConfiguration.class)
public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMongoDB {
public class DdiArtifactDownloadTest extends AbstractRestIntegrationTest {
private static final int ARTIFACT_SIZE = 5 * 1024 * 1024;

View File

@@ -37,7 +37,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.fest.assertions.core.Condition;
@@ -59,7 +59,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
*/
@Features("Component Tests - Direct Device Integration API")
@Stories("Deployment Action Resource")
public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoDB {
public class DdiDeploymentBaseTest extends AbstractRestIntegrationTest {
private static final String HTTP_LOCALHOST = "http://localhost:8080/";

View File

@@ -38,7 +38,7 @@ import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
@@ -57,7 +57,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
*/
@Features("Component Tests - Direct Device Integration API")
@Stories("Root Poll Resource")
public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoDB {
public class DdiRootControllerTest extends AbstractRestIntegrationTest {
@Autowired
private HawkbitSecurityProperties securityProperties;

View File

@@ -137,11 +137,6 @@
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>

View File

@@ -131,11 +131,6 @@
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.mgmt.rest.resource;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
@@ -82,8 +83,8 @@ public class MgmtDownloadResource implements MgmtDownloadRestApi {
artifactCache.getId(), artifactCache.getDownloadType());
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
try {
ByteStreams.copy(artifact.getFileInputStream(),
try (InputStream inputstream = artifact.getFileInputStream()) {
ByteStreams.copy(inputstream,
requestResponseContextHolder.getHttpServletResponse().getOutputStream());
} catch (final IOException e) {
LOGGER.error("Cannot copy streams", e);

View File

@@ -18,7 +18,7 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.Before;
import org.junit.Test;
@@ -30,7 +30,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Management API")
@Stories("Download Resource")
public class MgmtDownloadResourceTest extends AbstractRestIntegrationTestWithMongoDB {
public class MgmtDownloadResourceTest extends AbstractRestIntegrationTest {
@Autowired
private DownloadIdCache downloadIdCache;

View File

@@ -27,6 +27,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -43,7 +44,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
@@ -70,7 +71,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
*/
@Features("Component Tests - Management API")
@Stories("Software Module Resource")
public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestWithMongoDB {
public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTest {
@Before
public void assertPreparationOfRepo() {
@@ -165,12 +166,12 @@ public class MgmtSoftwareModuleResourceTest extends AbstractRestIntegrationTestW
assertThat(artifactManagement.countArtifactsAll()).as("Wrong artifact size").isEqualTo(1);
// binary
assertTrue("Wrong artifact content",
IOUtils.contentEquals(new ByteArrayInputStream(random),
artifactManagement
.loadArtifactBinary(
softwareManagement.findSoftwareModuleById(sm.getId()).getArtifacts().get(0))
.getFileInputStream()));
try (InputStream fileInputStream = artifactManagement
.loadArtifactBinary(softwareManagement.findSoftwareModuleById(sm.getId()).getArtifacts().get(0))
.getFileInputStream()) {
assertTrue("Wrong artifact content",
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream));
}
// hashes
assertThat(artifactManagement.findArtifactByFilename("origFilename").get(0).getSha1Hash()).as("Wrong sha1 hash")

View File

@@ -1,77 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.resource;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.After;
import org.junit.Test;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MvcResult;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Tests {@link MgmtSoftwareModuleResource} in case of missing MongoDB
* connection.
*
*/
@Features("Component Tests - Management API")
@Stories("Download Resource")
public class SMRessourceMisingMongoDbConnectionTest extends AbstractRestIntegrationTestWithMongoDB {
@Test
@Description("Ensures that the correct error code is returned in case MongoDB unavailable.")
public void missingMongoDbConnectionResultsInErrorAtUpload() throws Exception {
mongodExecutable.stop();
assertThat(softwareManagement.findSoftwareModulesAll(pageReq)).hasSize(0);
assertThat(artifactManagement.countArtifactsAll()).isEqualTo(0);
SoftwareModule sm = entityFactory.generateSoftwareModule(softwareManagement.findSoftwareModuleTypeByKey("os"),
"name 1", "version 1", null, null);
sm = softwareManagement.createSoftwareModule(sm);
assertThat(artifactManagement.countArtifactsAll()).isEqualTo(0);
// create test file
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, random);
// upload
final MvcResult mvcResult = mvc
.perform(fileUpload("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).file(file))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isInternalServerError()).andReturn();
// check error result
final ExceptionInfo exceptionInfo = ResourceUtility
.convertException(mvcResult.getResponse().getContentAsString());
assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_ARTIFACT_UPLOAD_FAILED.getKey());
assertThat(exceptionInfo.getMessage()).isEqualTo(SpServerError.SP_ARTIFACT_UPLOAD_FAILED.getMessage());
// ensure that the JPA transaction was rolled back
assertThat(artifactManagement.countArtifactsAll()).isEqualTo(0);
}
@Override
@After
public void cleanCurrentCollection() {
// not needed, mongodb is stopped already
}
}

View File

@@ -49,7 +49,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-artifact-repository-mongo</artifactId>
<artifactId>hawkbit-artifact-repository-filesystem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View File

@@ -20,12 +20,10 @@ import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSFile;
/**
* JPA implementation of {@link LocalArtifact}.
*
@@ -73,9 +71,9 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art
* Constructs artifact.
*
* @param gridFsFileName
* that is the link to the {@link GridFS} entity.
* that is the link to the {@link DbArtifact} entity.
* @param filename
* that is used by {@link GridFSFile} store.
* that is used by {@link DbArtifact} store.
* @param softwareModule
* of this artifact
*/

View File

@@ -15,7 +15,6 @@ import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
@SpringApplicationConfiguration(classes = { org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest {
@@ -62,9 +61,6 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired
protected TargetInfoRepository targetInfoRepository;
@Autowired
protected GridFsOperations operations;
@Autowired
protected RolloutGroupRepository rolloutGroupRepository;

View File

@@ -1,73 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
@SpringApplicationConfiguration(classes = { org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
public abstract class AbstractJpaIntegrationTestWithMongoDB extends AbstractIntegrationTest {
@PersistenceContext
protected EntityManager entityManager;
@Autowired
protected TargetRepository targetRepository;
@Autowired
protected ActionRepository actionRepository;
@Autowired
protected DistributionSetRepository distributionSetRepository;
@Autowired
protected SoftwareModuleRepository softwareModuleRepository;
@Autowired
protected TenantMetaDataRepository tenantMetaDataRepository;
@Autowired
protected DistributionSetTypeRepository distributionSetTypeRepository;
@Autowired
protected SoftwareModuleTypeRepository softwareModuleTypeRepository;
@Autowired
protected TargetTagRepository targetTagRepository;
@Autowired
protected DistributionSetTagRepository distributionSetTagRepository;
@Autowired
protected SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
@Autowired
protected ActionStatusRepository actionStatusRepository;
@Autowired
protected LocalArtifactRepository artifactRepository;
@Autowired
protected TargetInfoRepository targetInfoRepository;
@Autowired
protected RolloutGroupRepository rolloutGroupRepository;
@Autowired
protected RolloutRepository rolloutRepository;
@Autowired
protected TenantAwareCacheManager cacheManager;
}

View File

@@ -1,73 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.net.UnknownHostException;
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.junit.After;
import org.junit.Test;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository")
@Stories("Artifact Management")
public class ArtifactManagementFailedMongoDBTest extends AbstractJpaIntegrationTestWithMongoDB {
@Test
@Description("Trys and fails to delete or create local artifact with a down mongodb and checks if expected ArtifactDeleteFailedException is thrown.")
public void deleteArtifactsWithNoMongoDb() throws UnknownHostException, IOException {
// ensure baseline
assertThat(artifactRepository.findAll()).isEmpty();
// prepare test
JpaSoftwareModule sm = new JpaSoftwareModule(softwareManagement.findSoftwareModuleTypeByKey("os"), "name 1",
"version 1", null, null);
sm = softwareModuleRepository.save(sm);
final Artifact result = artifactManagement.createArtifact(new RandomGeneratedInputStream(5 * 1024), sm.getId(),
"file1", false);
assertThat(artifactRepository.findAll()).hasSize(1);
mongodExecutable.stop();
try {
artifactManagement.deleteArtifact(result.getId());
fail("deletion should have failed");
} catch (final ArtifactDeleteFailedException e) {
}
try {
artifactManagement.createArtifact(new RandomGeneratedInputStream(5 * 1024), sm.getId(), "file2", false);
fail("Should not have worked with MongoDb down.");
} catch (final ArtifactUploadFailedException e) {
}
assertThat(artifactRepository.findAll()).hasSize(1);
}
@Override
@After
public void cleanCurrentCollection() {
// no need to clean, is stopped already
}
}

View File

@@ -14,6 +14,7 @@ import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.io.IOUtils;
@@ -28,8 +29,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Test;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
@@ -44,7 +43,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
*/
@Features("Component Tests - Repository")
@Stories("Artifact Management")
public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoDB {
public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
/**
* Test method for
@@ -151,25 +150,17 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoD
assertThat(result2.getId()).isNotNull();
assertThat(((JpaArtifact) result).getGridFsFileName())
.isNotEqualTo(((JpaArtifact) result2).getGridFsFileName());
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName()))))
.isNotNull();
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result2).getGridFsFileName()))))
.isNotNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNotNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result2).getGridFsFileName())).isNotNull();
artifactManagement.deleteArtifact(result.getId());
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName()))))
.isNull();
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result2).getGridFsFileName()))))
.isNotNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result2).getGridFsFileName())).isNotNull();
artifactManagement.deleteArtifact(result2.getId());
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result2).getGridFsFileName()))))
.isNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result2).getGridFsFileName())).isNull();
assertThat(artifactRepository.findAll()).hasSize(0);
}
@@ -198,18 +189,12 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoD
assertThat(result2.getId()).isNotNull();
assertThat(((JpaArtifact) result).getGridFsFileName()).isEqualTo(((JpaArtifact) result2).getGridFsFileName());
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName()))))
.isNotNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNotNull();
artifactManagement.deleteArtifact(result.getId());
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName()))))
.isNotNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNotNull();
artifactManagement.deleteArtifact(result2.getId());
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName()))))
.isNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNull();
}
/**
@@ -253,8 +238,10 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoD
final Artifact result = artifactManagement.createArtifact(new ByteArrayInputStream(random), sm.getId(), "file1",
false);
assertTrue("The stored binary matches the given binary", IOUtils.contentEquals(new ByteArrayInputStream(random),
artifactManagement.loadArtifactBinary(result).getFileInputStream()));
try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result).getFileInputStream()) {
assertTrue("The stored binary matches the given binary",
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream));
}
}
@Test

View File

@@ -45,8 +45,6 @@ import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -57,7 +55,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository")
@Stories("Software Management")
public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoDB {
public class SoftwareManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Try to update non updatable fields results in repository doing nothing.")
@@ -387,7 +385,7 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD
}
@Test
@Description("Deletes an artifact, which is assigned to a Distribution Set")
@Description("Deletes an artifact, which is assigned to a DistributionSet")
public void softDeleteOfAssignedArtifact() {
// Init DistributionSet
@@ -464,12 +462,9 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD
}
@Test
@Description("Delete an softwaremodule with an artifact, which is also used by another softwaremodule.")
@Description("Delete an softwaremodule with an artifact, which is alsoused by another softwaremodule.")
public void deleteSoftwareModulesWithSharedArtifact() throws IOException {
// Precondition: Make sure MongoDB is Empty
assertThat(operations.find(new Query())).hasSize(0);
// Init artifact binary data, target and DistributionSets
final byte[] source = RandomUtils.nextBytes(1024);
@@ -489,9 +484,6 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD
moduleY = softwareManagement.findSoftwareModuleById(moduleY.getId());
final Artifact artifactY = moduleY.getArtifacts().iterator().next();
// verify: that only one entry was created in mongoDB
assertThat(operations.find(new Query())).hasSize(1);
// [STEP5]: Delete SoftwareModuleX
softwareManagement.deleteSoftwareModule(moduleX.getId());
@@ -515,9 +507,6 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD
@Description("Delete two assigned softwaremodules which share an artifact.")
public void deleteMultipleSoftwareModulesWhichShareAnArtifact() throws IOException {
// Precondition: Make sure MongoDB is Empty
assertThat(operations.find(new Query())).hasSize(0);
// Init artifact binary data, target and DistributionSets
final byte[] source = RandomUtils.nextBytes(1024);
final Target target = targetManagement.createTarget(new JpaTarget("test123"));
@@ -540,9 +529,6 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD
moduleY = softwareManagement.findSoftwareModuleById(moduleY.getId());
final Artifact artifactY = moduleY.getArtifacts().iterator().next();
// verify: that only one entry was created in mongoDB
assertThat(operations.find(new Query())).hasSize(1);
// [STEP3]: Assign SoftwareModuleX to DistributionSetX and to target
distributionSetManagement.assignSoftwareModules(disSetX, Sets.newHashSet(moduleX));
deploymentManagement.assignDistributionSet(disSetX, Lists.newArrayList(target));
@@ -611,17 +597,14 @@ public class SoftwareManagementTest extends AbstractJpaIntegrationTestWithMongoD
assertThat(artifactRepository.findAll()).hasSize(results.length);
for (final Artifact result : results) {
assertThat(result.getId()).isNotNull();
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName()))))
.isNotNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName()))
.isNotNull();
}
}
private void assertArtfiactNull(final Artifact... results) {
for (final Artifact result : results) {
assertThat(operations.findOne(
new Query().addCriteria(Criteria.where("filename").is(((JpaArtifact) result).getGridFsFileName()))))
.isNull();
assertThat(binaryArtifactRepository.getArtifactBySha1(((JpaArtifact) result).getGridFsFileName())).isNull();
}
}

View File

@@ -28,7 +28,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository")
@Stories("System Management")
public class SystemManagementTest extends AbstractJpaIntegrationTestWithMongoDB {
public class SystemManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Ensures that findTenants returns all tenants and not only restricted to the tenant which currently is logged in")

View File

@@ -30,7 +30,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Repository")
@Stories("Tenant Configuration Management")
public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTestWithMongoDB {
public class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Tests that tenant specific configuration can be persisted and in case the tenant does not have specific configuration the default from environment is used instead.")

View File

@@ -31,7 +31,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-artifact-repository-mongo</artifactId>
<artifactId>hawkbit-artifact-repository-filesystem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@@ -43,8 +43,8 @@
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>net._01001111</groupId>

View File

@@ -13,6 +13,9 @@ import java.util.concurrent.Executors;
import org.eclipse.hawkbit.api.ArtifactUrlHandlerProperties;
import org.eclipse.hawkbit.api.PropertyBasedArtifactUrlHandler;
import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemProperties;
import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemRepository;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.cache.DefaultDownloadIdCache;
import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
@@ -53,7 +56,6 @@ import org.springframework.security.concurrent.DelegatingSecurityContextExecutor
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.util.AntPathMatcher;
import com.mongodb.MongoClientOptions;
/**
* Spring context configuration required for Dev.Environment.
@@ -64,10 +66,16 @@ import com.mongodb.MongoClientOptions;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY, proxyTargetClass = false, securedEnabled = true)
@EnableConfigurationProperties({ HawkbitServerProperties.class, DdiSecurityProperties.class,
ArtifactUrlHandlerProperties.class })
ArtifactUrlHandlerProperties.class, ArtifactFilesystemProperties.class })
@Profile("test")
@EnableAutoConfiguration
public class TestConfiguration implements AsyncConfigurer {
@Bean
public ArtifactRepository artifactRepository(final ArtifactFilesystemProperties artifactFilesystemProperties) {
return new ArtifactFilesystemRepository(artifactFilesystemProperties);
}
@Bean
public TestRepositoryManagement testRepositoryManagement(final SystemSecurityContext systemSecurityContext,
final SystemManagement systemManagement) {
@@ -85,13 +93,6 @@ public class TestConfiguration implements AsyncConfigurer {
return new PropertyBasedArtifactUrlHandler(urlHandlerProperties);
}
@Bean
public MongoClientOptions options() {
return MongoClientOptions.builder().connectTimeout(500).maxWaitTime(500).connectionsPerHost(2)
.serverSelectionTimeout(500).build();
}
@Bean
public TenantAware tenantAware() {
return new SecurityContextTenantAware();

View File

@@ -12,8 +12,15 @@ import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpre
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.SYSTEM_ROLE;
import static org.junit.rules.RuleChain.outerRule;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter;
import org.eclipse.hawkbit.TestConfiguration;
import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystemProperties;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
@@ -54,13 +61,10 @@ import org.springframework.core.env.Environment;
import org.springframework.data.auditing.AuditingHandler;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
import org.springframework.hateoas.MediaTypes;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
@@ -68,8 +72,6 @@ import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import de.flapdoodle.embed.mongo.MongodExecutable;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ActiveProfiles({ "test" })
@@ -80,7 +82,6 @@ import de.flapdoodle.embed.mongo.MongodExecutable;
// refreshed we e.g. get two instances of CacheManager which leads to very
// strange test failures.
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
@TestPropertySource(properties = { "spring.data.mongodb.port=0", "spring.mongodb.embedded.version=3.2.7" })
public abstract class AbstractIntegrationTest implements EnvironmentAware {
private final static Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class);
@@ -154,6 +155,15 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
@Autowired
protected SystemSecurityContext systemSecurityContext;
@Autowired
private ArtifactFilesystemProperties artifactFilesystemProperties;
@Autowired
protected ArtifactRepository binaryArtifactRepository;
@Autowired
protected TenantAwareCacheManager cacheManager;
protected MockMvc mvc;
protected SoftwareModuleType osType;
@@ -165,12 +175,6 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
@Autowired
protected TestdataFactory testdataFactory;
@Autowired
protected GridFsOperations operations;
@Autowired
protected MongodExecutable mongodExecutable;
@Autowired
protected ServiceMatcher serviceMatcher;
@@ -232,8 +236,8 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware {
}
@After
public void cleanCurrentCollection() {
operations.delete(new Query());
public void cleanCurrentCollection() throws IOException {
FileUtils.deleteDirectory(new File(artifactFilesystemProperties.getPath()));
}
protected DefaultMockMvcBuilder createMvcWebAppContext() {

View File

@@ -180,9 +180,9 @@ public final class RestResourceConversionHelper {
response.setHeader(CONTENT_RANGE, "bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal());
response.setHeader(CONTENT_LENGTH, String.valueOf(r.getLength()));
try {
copyStreams(file.getFileInputStream(), response.getOutputStream(), controllerManagement, statusId,
r.getStart(), r.getLength());
try (InputStream inputStream = file.getFileInputStream()) {
copyStreams(inputStream, response.getOutputStream(), controllerManagement, statusId, r.getStart(),
r.getLength());
} catch (final IOException e) {
LOG.error("fullfileRequest of file ({}) failed!", artifact.getFilename(), e);
throw new FileSteamingFailedException(artifact.getFilename());
@@ -246,8 +246,9 @@ public final class RestResourceConversionHelper {
response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY);
response.setStatus(SC_PARTIAL_CONTENT);
try {
for (final ByteRange r : ranges) {
for (final ByteRange r : ranges) {
try (InputStream inputStream = file.getFileInputStream()) {
// Add multipart boundary and header fields for every range.
response.getOutputStream().println();
response.getOutputStream().println("--" + MULTIPART_BOUNDARY);
@@ -255,19 +256,26 @@ public final class RestResourceConversionHelper {
.println("Content-Range: bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal());
// Copy single part range of multi part range.
copyStreams(file.getFileInputStream(), response.getOutputStream(), controllerManagement, statusId,
r.getStart(), r.getLength());
copyStreams(inputStream, response.getOutputStream(), controllerManagement, statusId, r.getStart(),
r.getLength());
} catch (final IOException e) {
throwFileStreamingFailedException(artifact, e);
}
}
try {
// End with final multipart boundary.
response.getOutputStream().println();
response.getOutputStream().print("--" + MULTIPART_BOUNDARY + "--");
} catch (final IOException e) {
LOG.error("multipartRangeRequest of file ({}) failed!", artifact.getFilename(), e);
throw new FileSteamingFailedException(artifact.getFilename());
throwFileStreamingFailedException(artifact, e);
}
}
private static void throwFileStreamingFailedException(final Artifact artifact, final IOException e) {
LOG.error("multipartRangeRequest of file ({}) failed!", artifact.getFilename(), e);
throw new FileSteamingFailedException(artifact.getFilename());
}
private static void handleStandardRangeRequest(final Artifact artifact, final HttpServletResponse response,
final DbArtifact file, final ControllerManagement controllerManagement, final Long statusId,
final List<ByteRange> ranges) {
@@ -276,9 +284,9 @@ public final class RestResourceConversionHelper {
response.setHeader(CONTENT_LENGTH, String.valueOf(r.getLength()));
response.setStatus(SC_PARTIAL_CONTENT);
try {
copyStreams(file.getFileInputStream(), response.getOutputStream(), controllerManagement, statusId,
r.getStart(), r.getLength());
try (InputStream inputStream = file.getFileInputStream()) {
copyStreams(inputStream, response.getOutputStream(), controllerManagement, statusId, r.getStart(),
r.getLength());
} catch (final IOException e) {
LOG.error("standardRangeRequest of file ({}) failed!", artifact.getFilename(), e);
throw new FileSteamingFailedException(artifact.getFilename());

View File

@@ -1,33 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.rest;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.rest.configuration.RestConfiguration;
import org.eclipse.hawkbit.rest.util.FilterHttpResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
/**
* Abstract Test for Rest tests.
*/
@SpringApplicationConfiguration(classes = { RestConfiguration.class,
org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
public abstract class AbstractRestIntegrationTestWithMongoDB extends AbstractIntegrationTest {
@Autowired
private FilterHttpResponse filterHttpResponse;
@Override
protected DefaultMockMvcBuilder createMvcWebAppContext() {
final DefaultMockMvcBuilder createMvcWebAppContext = super.createMvcWebAppContext();
return createMvcWebAppContext.addFilter(filterHttpResponse);
}
}

View File

@@ -271,11 +271,6 @@
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>

View File

@@ -38,7 +38,7 @@
<module>hawkbit-security-integration</module>
<module>hawkbit-http-security</module>
<module>hawkbit-ui</module>
<module>hawkbit-artifact-repository-mongo</module>
<module>hawkbit-artifact-repository-filesystem</module>
<module>hawkbit-autoconfigure</module>
<module>hawkbit-test-report</module>
<module>examples</module>
@@ -670,11 +670,6 @@
<artifactId>json</artifactId>
<version>${json.version}</version>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>${embedded-mongo.version}</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>