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
This commit is contained in:
Andy Wilkinson 2019-11-06 14:45:59 +00:00
parent 8b1ff0a1a9
commit 891c7120ef
3 changed files with 35 additions and 14 deletions

View File

@ -44,6 +44,10 @@
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@ -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<Void> publishedFilesRequest = getRequest(releaseInfo, 0);
RequestEntity<Void> 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<Void> 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;
});
}
return false;
catch (ConditionTimeoutException ex) {
return false;
}
return true;
}
private RequestEntity<Void> getRequest(ReleaseInfo releaseInfo, int includeUnpublished) {

View File

@ -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");