ArtifactRepository tenant aware. (#539)
* ArtifactRepository tenant aware. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * No need to have this protected. Updated event to boot > 1.3 Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Remove conditional. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Remove Debug log. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Cleanup Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Missing validation and readability. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix test after change. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix computation is DosFilter Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fix session state on RESTful APIs. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Performance improvement controllermanagement Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Added cross tenant test. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Typos. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -26,6 +26,7 @@ import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
@@ -44,6 +45,7 @@ import com.google.common.io.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])}.
|
||||
*/
|
||||
@Validated
|
||||
public class ArtifactFilesystemRepository implements ArtifactRepository {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ArtifactFilesystemRepository.class);
|
||||
@@ -64,16 +66,17 @@ public class ArtifactFilesystemRepository implements ArtifactRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArtifactFilesystem store(final InputStream content, final String filename, final String contentType) {
|
||||
return store(content, filename, contentType, null);
|
||||
public ArtifactFilesystem store(final String tenant, final InputStream content, final String filename,
|
||||
final String contentType) {
|
||||
return store(tenant, 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) {
|
||||
public ArtifactFilesystem store(final String tenant, final InputStream content, final String filename,
|
||||
final String contentType, final DbArtifactHash hash) {
|
||||
|
||||
final MessageDigest mdSHA1;
|
||||
final MessageDigest mdMD5;
|
||||
@@ -86,17 +89,17 @@ public class ArtifactFilesystemRepository implements ArtifactRepository {
|
||||
|
||||
final File file = createTempFile();
|
||||
final DbArtifact artifact = store(content, contentType, hash, mdSHA1, mdMD5, file);
|
||||
return renameFileToSHA1Naming(file, artifact);
|
||||
return renameFileToSHA1Naming(tenant, file, artifact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteBySha1(final String sha1Hash) {
|
||||
FileUtils.deleteQuietly(getFile(sha1Hash));
|
||||
public void deleteBySha1(final String tenant, final String sha1Hash) {
|
||||
FileUtils.deleteQuietly(getFile(tenant, sha1Hash));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArtifactFilesystem getArtifactBySha1(final String sha1) {
|
||||
final File file = getFile(sha1);
|
||||
public ArtifactFilesystem getArtifactBySha1(final String tenant, final String sha1) {
|
||||
final File file = getFile(tenant, sha1);
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
@@ -131,8 +134,8 @@ public class ArtifactFilesystemRepository implements ArtifactRepository {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
private ArtifactFilesystem renameFileToSHA1Naming(final File file, final DbArtifact artifact) {
|
||||
final File fileSHA1Naming = getFile(artifact.getHashes().getSha1());
|
||||
private ArtifactFilesystem renameFileToSHA1Naming(final String tenant, final File file, final DbArtifact artifact) {
|
||||
final File fileSHA1Naming = getFile(tenant, artifact.getHashes().getSha1());
|
||||
final ArtifactFilesystem fileSystemArtifact = new ArtifactFilesystem(fileSHA1Naming);
|
||||
if (fileSHA1Naming.exists()) {
|
||||
FileUtils.deleteQuietly(file);
|
||||
@@ -179,18 +182,18 @@ public class ArtifactFilesystemRepository implements ArtifactRepository {
|
||||
}
|
||||
}
|
||||
|
||||
private File getFile(final String sha1) {
|
||||
final File aritfactDirectory = getSha1DirectoryPath(sha1).toFile();
|
||||
private File getFile(final String tenant, final String sha1) {
|
||||
final File aritfactDirectory = getSha1DirectoryPath(tenant, sha1).toFile();
|
||||
aritfactDirectory.mkdirs();
|
||||
return new File(aritfactDirectory, sha1);
|
||||
}
|
||||
|
||||
private Path getSha1DirectoryPath(final String sha1) {
|
||||
private Path getSha1DirectoryPath(final String tenant, 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);
|
||||
return Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant), folder1, folder2);
|
||||
}
|
||||
|
||||
private DigestOutputStream openFileOutputStream(final File file, final MessageDigest mdSHA1,
|
||||
@@ -198,4 +201,13 @@ public class ArtifactFilesystemRepository implements ArtifactRepository {
|
||||
return new DigestOutputStream(
|
||||
new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(file)), mdMD5), mdSHA1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByTenant(final String tenant) {
|
||||
FileUtils.deleteQuietly(Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant)).toFile());
|
||||
}
|
||||
|
||||
private static String sanitizeTenant(final String tenant) {
|
||||
return tenant.trim().toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ 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 static final String TENANT = "test_tenant";
|
||||
|
||||
private final ArtifactFilesystemProperties artifactResourceProperties = new ArtifactFilesystemProperties();
|
||||
|
||||
@@ -51,8 +52,8 @@ public class ArtifactFilesystemRepositoryTest {
|
||||
final byte[] fileContent = randomBytes();
|
||||
final ArtifactFilesystem artifact = storeRandomArtifact(fileContent);
|
||||
|
||||
final DbArtifact artifactBySha1 = artifactFilesystemRepository
|
||||
.getArtifactBySha1(artifact.getHashes().getSha1());
|
||||
final DbArtifact artifactBySha1 = artifactFilesystemRepository.getArtifactBySha1(TENANT,
|
||||
artifact.getHashes().getSha1());
|
||||
assertThat(artifactBySha1).isNotNull();
|
||||
}
|
||||
|
||||
@@ -61,16 +62,33 @@ public class ArtifactFilesystemRepositoryTest {
|
||||
public void deleteStoredArtifactBySHA1Hash() {
|
||||
final ArtifactFilesystem artifact = storeRandomArtifact(randomBytes());
|
||||
|
||||
artifactFilesystemRepository.deleteBySha1(artifact.getHashes().getSha1());
|
||||
artifactFilesystemRepository.deleteBySha1(TENANT, artifact.getHashes().getSha1());
|
||||
|
||||
assertThat(artifactFilesystemRepository.getArtifactBySha1(artifact.getHashes().getSha1())).isNull();
|
||||
assertThat(artifactFilesystemRepository.getArtifactBySha1(TENANT, artifact.getHashes().getSha1())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verfies that all artifacts of a tenant can be deleted in the file-system repository")
|
||||
public void deleteStoredArtifactOfTenant() {
|
||||
final ArtifactFilesystem artifact = storeRandomArtifact(randomBytes());
|
||||
|
||||
artifactFilesystemRepository.deleteByTenant(TENANT);
|
||||
|
||||
assertThat(artifactFilesystemRepository.getArtifactBySha1(TENANT, 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");
|
||||
artifactFilesystemRepository.deleteBySha1(TENANT, "sha1HashWhichDoesNotExists");
|
||||
} catch (final Exception e) {
|
||||
Assertions.fail("did not expect an exception while deleting a file which does not exists");
|
||||
}
|
||||
|
||||
final ArtifactFilesystem artifact = storeRandomArtifact(randomBytes());
|
||||
try {
|
||||
artifactFilesystemRepository.deleteBySha1("tenantWhichDoesNotExist", artifact.getHashes().getSha1());
|
||||
} catch (final Exception e) {
|
||||
Assertions.fail("did not expect an exception while deleting a file which does not exists");
|
||||
}
|
||||
@@ -79,7 +97,8 @@ public class ArtifactFilesystemRepositoryTest {
|
||||
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");
|
||||
final ArtifactFilesystem store = artifactFilesystemRepository.store(TENANT, inputStream, fileName,
|
||||
"application/txt");
|
||||
return store;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user