From 891c7120ef1990e9fec01e5070344f05d2a7326a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 6 Nov 2019 14:45:59 +0000 Subject: [PATCH] Wait for distribution to reach Bintray before checking its completeness Previously, as soon as the distribution of a release from Artifactory to Bintray had been initiated we would start checking if it was complete. This created a race condition between the distribution being created and us checking if it was complete. If the check won the race and happened before the creation, Bintray would respond with a 404. This commit updates BintrayService to wait for up to 5 minutes for the distribution to be created on Bintray. Once it has been created we then wait for up to 40 minutes for it to be complete as we did before. The use of Awaitility has been introduced in this commit to simplify the logic required to wait for the distribution's creation and completion. Closes gh-18902 --- ci/images/releasescripts/pom.xml | 4 ++ .../bintray/BintrayService.java | 38 ++++++++++++------- .../bintray/BintrayServiceTests.java | 7 ++++ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/ci/images/releasescripts/pom.xml b/ci/images/releasescripts/pom.xml index 6dc3a70680c..05bd6dde8e9 100644 --- a/ci/images/releasescripts/pom.xml +++ b/ci/images/releasescripts/pom.xml @@ -44,6 +44,10 @@ com.fasterxml.jackson.module jackson-module-parameter-names + + org.awaitility + awaitility + org.springframework.boot spring-boot-starter-test diff --git a/ci/images/releasescripts/src/main/java/io/spring/concourse/releasescripts/bintray/BintrayService.java b/ci/images/releasescripts/src/main/java/io/spring/concourse/releasescripts/bintray/BintrayService.java index f3ee0d9cd41..c5b38b74bb2 100644 --- a/ci/images/releasescripts/src/main/java/io/spring/concourse/releasescripts/bintray/BintrayService.java +++ b/ci/images/releasescripts/src/main/java/io/spring/concourse/releasescripts/bintray/BintrayService.java @@ -17,19 +17,25 @@ package io.spring.concourse.releasescripts.bintray; import java.net.URI; +import java.util.Objects; +import java.util.concurrent.TimeUnit; import io.spring.concourse.releasescripts.ReleaseInfo; import io.spring.concourse.releasescripts.sonatype.SonatypeProperties; import io.spring.concourse.releasescripts.sonatype.SonatypeService; import io.spring.concourse.releasescripts.system.ConsoleLogger; +import org.awaitility.core.ConditionTimeoutException; import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import static org.awaitility.Awaitility.waitAtMost; + /** * Central class for interacting with Bintray's REST API. * @@ -64,25 +70,29 @@ public class BintrayService { } public boolean isDistributionComplete(ReleaseInfo releaseInfo) { - RequestEntity publishedFilesRequest = getRequest(releaseInfo, 0); RequestEntity allFilesRequest = getRequest(releaseInfo, 1); - Object[] allFiles = this.restTemplate.exchange(allFilesRequest, Object[].class).getBody(); - int count = 0; - while (count < 120) { - Object[] publishedFiles = this.restTemplate.exchange(publishedFilesRequest, Object[].class).getBody(); - int unpublished = allFiles.length - publishedFiles.length; - if (unpublished == 0) { - return true; - } - count++; + Object[] allFiles = waitAtMost(5, TimeUnit.MINUTES).with().pollDelay(20, TimeUnit.SECONDS).until(() -> { try { - Thread.sleep(20000); + return this.restTemplate.exchange(allFilesRequest, Object[].class).getBody(); } - catch (InterruptedException e) { - + catch (HttpClientErrorException ex) { + if (ex.getStatusCode() != HttpStatus.NOT_FOUND) { + throw ex; + } + return null; } + }, Objects::nonNull); + RequestEntity publishedFilesRequest = getRequest(releaseInfo, 0); + try { + waitAtMost(40, TimeUnit.MINUTES).with().pollDelay(20, TimeUnit.SECONDS).until(() -> { + Object[] publishedFiles = this.restTemplate.exchange(publishedFilesRequest, Object[].class).getBody(); + return allFiles.length == publishedFiles.length; + }); + } + catch (ConditionTimeoutException ex) { + return false; } - return false; + return true; } private RequestEntity getRequest(ReleaseInfo releaseInfo, int includeUnpublished) { diff --git a/ci/images/releasescripts/src/test/java/io/spring/concourse/releasescripts/bintray/BintrayServiceTests.java b/ci/images/releasescripts/src/test/java/io/spring/concourse/releasescripts/bintray/BintrayServiceTests.java index 0cbccbe1494..e622ed3adb4 100644 --- a/ci/images/releasescripts/src/test/java/io/spring/concourse/releasescripts/bintray/BintrayServiceTests.java +++ b/ci/images/releasescripts/src/test/java/io/spring/concourse/releasescripts/bintray/BintrayServiceTests.java @@ -28,6 +28,7 @@ import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.web.client.ExpectedCount; import org.springframework.test.web.client.MockRestServiceServer; @@ -40,6 +41,7 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers. import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; /** @@ -73,6 +75,11 @@ class BintrayServiceTests { @Test void isDistributionComplete() throws Exception { + this.server + .expect(requestTo(String.format( + "https://api.bintray.com/packages/%s/%s/%s/versions/%s/files?include_unpublished=%s", + this.properties.getSubject(), this.properties.getRepo(), "example", "1.1.0.RELEASE", 1))) + .andRespond(withStatus(HttpStatus.NOT_FOUND)); setupGetPackageFiles(1, "all-package-files.json"); setupGetPackageFiles(0, "published-files.json"); setupGetPackageFiles(0, "all-package-files.json");