Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

- fix bug: change sql-query in localArtifactRepository #820

Merged

Conversation

nazifebasbaz
Copy link
Contributor

  • tests added in ArtifactManagementTest:
    -ensure that an Artifact can not deleted when the Artifact with the
    same hash exist
    -ensure that Artifacts with the same hash from one tenant can not
    deleted by an other tenant

Signed-off-by: Nazife Basbaz [email protected]

@hawkbit
Copy link

hawkbit bot commented Apr 9, 2019

Thanks for taking the time to contribute to hawkBit! We really appreciate this. Make yourself comfortable while I'm looking for a committer to help you with your contribution.
Please make sure you read the contribution guide and signed the Eclipse Contributor Agreement (ECA).

@schabdo schabdo added this to the 0.3.0M3 milestone Apr 9, 2019

createArtifactForTenant(tenant1, "myInput", module.getId(), "myFirstFile");
createArtifactForTenant(tenant2, "myInput", module2.getId(), "mySecondFile");
final Artifact artifactTenant2 = createArtifactForTenant(tenant2, "myInput", module2.getId(), "myThirdFile");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already tested with the previous test case

@nazifebasbaz
Copy link
Contributor Author

check other artifact storage extensions if file handling is covered as well

@nazifebasbaz nazifebasbaz force-pushed the bugfix_uploadingIdenticalArtifacts branch from 20199f2 to 2b738c3 Compare May 14, 2019 12:32
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaArtifact a WHERE a.sha1Hash = :sha1 AND a.softwareModule.id != :moduleId AND a.softwareModule.deleted = 0")
boolean existsWithSha1HashAndSoftwareModuleIdIsNot(@Param("sha1") String sha1, @Param("moduleId") Long moduleId);
@Query("SELECT CASE WHEN COUNT (a)>1 THEN 'true' ELSE 'false' END FROM JpaArtifact a WHERE a.sha1Hash = :sha1 AND a.tenant = :tenant AND a.softwareModule.deleted = 0")
Copy link
Contributor

@a-sayyed a-sayyed May 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can get rid of the @Query annotation and use the Jpa support for Entities:

long countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(@Param("sha1") String sha1, @Param("tenant") String tenant);

We can later just check the count if greater than 1

* Verifies if an artifact exists that has given hash and is still related
* to a {@link SoftwareModule} other than a given one and not
* {@link SoftwareModule#isDeleted()}.
* Verifies if in the same tenant one then more {@link Artifact}s with the
Copy link
Contributor

@a-sayyed a-sayyed May 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be adapted:

    /**
     * Counts current elements based on the sha1 and tenant, as well as having
     * the {@link SoftwareModule} property 'deleted' with value 'false'
     * 
     * @param sha1
     *            the sha1 of the {@link Artifact}
     * @param tenant
     *            the current tenant
     * 
     * @return the count of the elements
     */

@@ -166,8 +165,8 @@ private void assertMaxArtifactStorageQuota(final String filename, final long art
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public boolean clearArtifactBinary(final String sha1Hash, final long moduleId) {

if (localArtifactRepository.existsWithSha1HashAndSoftwareModuleIdIsNot(sha1Hash, moduleId)) {
if (localArtifactRepository.existsForMoreThenOneArtifactInTheSameTenantWithSha1HashAndSoftwareModuleIsNotDeleted(sha1Hash,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be refactored:

        final long count = localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash,
                tenantAware.getCurrentTenant());
        if (count > 1) {
            // there are still other artifacts that need the binary
            return false;
        }

@Test
@Description("Verifies that you cannot delete an artifact which exists with the same hash, in the same tenant and the SoftwareModule is not deleted .")
public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants()
throws NoSuchAlgorithmException, IOException {
Copy link
Contributor

@a-sayyed a-sayyed May 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the throws NoSuchAlgorithmException, not thrown in this context.
Could you please also double check in the whole file, there are many redundant throws declarations like this (from before) that need to be cleaned up.

.existsForMoreThenOneArtifactInTheSameTenantWithSha1HashAndSoftwareModuleIsNotDeleted(
artifact1.getSha1Hash(), artifact1.getTenant())).isTrue();

artifactRepository.deleteById(artifact1.getId());
Copy link
Contributor

@a-sayyed a-sayyed May 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe here we can add another assertion for the binary file contents


            try (final InputStream inputStream = artifactManagement.loadArtifactBinary(artifact2.getSha1Hash()).get()
                    .getFileInputStream()) {
                assertTrue("The stored binary matches the given binary",
                        IOUtils.contentEquals(new ByteArrayInputStream(randomBytes), inputStream));
            }

Btw, this assertion is used in other places in this class, so maybe you can extract it to an own method, maybe with the name assertEqualFileContents(Optional<AbstractDBArtifact> artifact, byte[] randomBytes)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaArtifact a WHERE a.sha1Hash = :sha1 AND a.softwareModule.id != :moduleId AND a.softwareModule.deleted = 0")
boolean existsWithSha1HashAndSoftwareModuleIdIsNot(@Param("sha1") String sha1, @Param("moduleId") Long moduleId);
default boolean existsForMoreThenOneArtifactInTheSameTenantWithSha1HashAndSoftwareModuleIsNotDeleted(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this method used anywhere or can it be removed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

*
* @return the boolean whether the atrifact exists or not
*/
boolean existsByTenantAndSha1(@NotEmpty String Tenant, @NotEmpty String sha1Hash);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR Document the parameter(s): Tenant rule
MINOR Rename this local variable to match the regular expression '^[a-z][a-zA-Z0-9]*$'. rule

@hawkbit-bot
Copy link

SonarQube analysis reported 2 issues

  • MINOR 2 minor

Watch the comments in this conversation to review them.

@nazifebasbaz nazifebasbaz force-pushed the bugfix_uploadingIdenticalArtifacts branch from 6b15408 to 3080740 Compare May 17, 2019 12:59
@schabdo schabdo modified the milestones: 0.3.0M3, 0.3.0M4 May 21, 2019

assertArtifactQuota(moduleId, 1);
assertMaxArtifactSizeQuota(filename, moduleId, artifactUpload.getFilesize());
assertMaxArtifactStorageQuota(filename, artifactUpload.getFilesize());

AbstractDbArtifact result = getOrCreateArtifact(artifactUpload);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use the Optional in order to get rid of null checks:

Suggested change
AbstractDbArtifact result = getOrCreateArtifact(artifactUpload);
return getOrCreateArtifact(artifactUpload)
.map(artifact -> storeArtifactMetadata(softwareModule, filename, artifact, existing)).orElse(null);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, looks much cleaner indeed, thank you for the hint! 👍

return result == null ? null : storeArtifactMetadata(softwareModule, filename, result, existing);
}

private AbstractDbArtifact getOrCreateArtifact(final ArtifactUpload artifactUpload) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you need to change this method correspondingly:

Suggested change
private AbstractDbArtifact getOrCreateArtifact(final ArtifactUpload artifactUpload) {
private Optional<AbstractDbArtifact> getOrCreateArtifact(final ArtifactUpload artifactUpload) {
final String providedSha1Sum = artifactUpload.getProvidedSha1Sum();
if (!StringUtils.isEmpty(providedSha1Sum)) {
final AbstractDbArtifact artifact = artifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(),
providedSha1Sum);
if (artifact != null) {
return Optional.of(artifact);
}
}
return Optional.ofNullable(storeArtifact(artifactUpload));
}

if (!StringUtils.isEmpty(providedSha1Sum)) {
artifact = artifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), providedSha1Sum);
}
return Optional.ofNullable(artifact == null ? storeArtifact(artifactUpload) : artifact);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still propose my previous solution (with 2 return statements and final AbstractDbArtifact) in order to get rid of conditional here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but then we have "multiple return statements" and we still have to do the null check anyway.
I could move the check to an own line to make it simpler maybe. What do you think?

artifact = (artifact == null) ? storeArtifact(artifactUpload) : artifact;
return Optional.ofNullable(artifact);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let it be our consensus for this question :)

@a-sayyed a-sayyed force-pushed the bugfix_uploadingIdenticalArtifacts branch from 39a0126 to 06a198f Compare May 23, 2019 14:28
Copy link
Contributor

@bogdan-bondar bogdan-bondar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks Good!

nazifebasbaz and others added 8 commits May 27, 2019 12:36
- tests added in ArtifactManagementTest:
  -ensure that an Artifact can not deleted when the Artifact with the
same hash exist
  -ensure that Artifacts with the same hash from one tenant can not
deleted by an other tenant

Signed-off-by: Nazife Basbaz <[email protected]>
-replace the @query annotation with the Jpa support for entities
-adapt tests
-remove unused exceptions

Signed-off-by: Nazife Basbaz <[email protected]>
Signed-off-by: Ahmed Sayed <[email protected]>
a-sayyed and others added 8 commits May 27, 2019 12:36
@a-sayyed a-sayyed force-pushed the bugfix_uploadingIdenticalArtifacts branch from 06a198f to 6f0a0fc Compare May 27, 2019 10:43
@laverman laverman merged commit fde0cbd into eclipse-hawkbit:master Jun 5, 2019
@laverman laverman deleted the bugfix_uploadingIdenticalArtifacts branch June 5, 2019 14:07
AmmarBikic pushed a commit to bosch-io/hawkbit that referenced this pull request Oct 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants