diff --git a/src/main/java/io/github/devatherock/artifactory/config/AppConfig.java b/src/main/java/io/github/devatherock/artifactory/config/AppConfig.java index 279d0e0..d6414fc 100644 --- a/src/main/java/io/github/devatherock/artifactory/config/AppConfig.java +++ b/src/main/java/io/github/devatherock/artifactory/config/AppConfig.java @@ -13,6 +13,12 @@ @Factory public class AppConfig { + /** + * HTTP client for interacting with artifactory APIs + * + * @param artifactoryClient + * @return a http client + */ @Singleton public BlockingHttpClient httpClient(@Client("${artifactory.url}") HttpClient artifactoryClient) { return artifactoryClient.toBlocking(); diff --git a/src/main/java/io/github/devatherock/artifactory/service/DockerBadgeService.java b/src/main/java/io/github/devatherock/artifactory/service/DockerBadgeService.java index 55fb53b..fd54bc4 100644 --- a/src/main/java/io/github/devatherock/artifactory/service/DockerBadgeService.java +++ b/src/main/java/io/github/devatherock/artifactory/service/DockerBadgeService.java @@ -13,6 +13,7 @@ import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; import io.micronaut.http.client.BlockingHttpClient; +import io.micronaut.http.client.exceptions.HttpClientResponseException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,9 +28,8 @@ /** * Service class to fetch information required for the badges and then generate * them - * - * @author devaprasadh * + * @author devaprasadh */ @Slf4j @Blocking @@ -139,7 +139,7 @@ public String getLatestVersionBadge(String packageName, String badgeLabel) { if (null == latestVersion || Instant.from(MODIFIED_TIME_PARSER.parse(currentVersion.getLastModified())).compareTo( - Instant.from(MODIFIED_TIME_PARSER.parse(latestVersion.getLastModified()))) > 0) { + Instant.from(MODIFIED_TIME_PARSER.parse(latestVersion.getLastModified()))) > 0) { latestVersion = currentVersion; } } @@ -188,10 +188,19 @@ private String generateNotFoundBadge(String badgeLabel) { */ private DockerManifest readManifest(String packageName, String tag) { String fullPackageName = packageName + "/" + tag; + DockerManifest manifest = null; + HttpRequest manifestRequest = HttpRequest .create(HttpMethod.GET, artifactoryConfig.getUrlPrefix() + fullPackageName + FILE_NAME_MANIFEST) .header(HDR_API_KEY, artifactoryConfig.getApiKey()); - return artifactoryClient.retrieve(manifestRequest, DockerManifest.class); + + try { + manifest = artifactoryClient.retrieve(manifestRequest, DockerManifest.class); + } catch (HttpClientResponseException exception) { + LOGGER.warn("Exception when reading manifest.json of {}", fullPackageName, exception); + } + + return manifest; } /** diff --git a/src/test/groovy/io/github/devatherock/artifactory/controllers/DockerControllerSpec.groovy b/src/test/groovy/io/github/devatherock/artifactory/controllers/DockerControllerSpec.groovy index 95e4acc..bbf6ea8 100644 --- a/src/test/groovy/io/github/devatherock/artifactory/controllers/DockerControllerSpec.groovy +++ b/src/test/groovy/io/github/devatherock/artifactory/controllers/DockerControllerSpec.groovy @@ -2,6 +2,7 @@ package io.github.devatherock.artifactory.controllers import com.github.tomakehurst.wiremock.WireMockServer import com.github.tomakehurst.wiremock.client.WireMock +import io.github.devatherock.artifactory.service.DockerBadgeService import io.github.devatherock.test.TestUtil import io.micronaut.http.HttpRequest import io.micronaut.http.client.HttpClient @@ -49,7 +50,7 @@ class DockerControllerSpec extends Specification { and: WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack','2020-10-01T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack', '2020-10-01T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/1.1.0/manifest.json?stats") .willReturn(WireMock.okJson(TestUtil.getManifestStats('1.1.0', 10)))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/1.1.2/manifest.json?stats") @@ -70,15 +71,21 @@ class DockerControllerSpec extends Specification { .queryParam('package', packageName).build())) then: - WireMock.verify(1, WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}"))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.0/manifest.json?stats"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) + WireMock.verify(1, + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.0/manifest.json?stats")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.2/manifest.json?stats"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.2/manifest.json?stats")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/latest/manifest.json?stats"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/latest/manifest.json?stats")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/abcdefgh/manifest.json?stats"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/abcdefgh/manifest.json?stats")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/static/v1"))) badge == 'dummyBadge' } @@ -112,15 +119,21 @@ class DockerControllerSpec extends Specification { .queryParam('label', 'downloads').build())) then: - WireMock.verify(1, WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}"))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.0/manifest.json?stats"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) + WireMock.verify(1, + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.0/manifest.json?stats")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.2/manifest.json?stats"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.2/manifest.json?stats")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/latest/manifest.json?stats"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/latest/manifest.json?stats")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/abcdefgh/manifest.json?stats"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/abcdefgh/manifest.json?stats")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/static/v1"))) badge == 'dummyBadge' } @@ -145,7 +158,8 @@ class DockerControllerSpec extends Specification { then: WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/latest/manifest.json"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/latest/manifest.json")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/static/v1"))) badge == 'dummyBadge' } @@ -172,7 +186,8 @@ class DockerControllerSpec extends Specification { then: WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/1.2.0/manifest.json"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/1.2.0/manifest.json")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/static/v1"))) badge == 'dummyBadge' } @@ -197,7 +212,8 @@ class DockerControllerSpec extends Specification { then: WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/latest/manifest.json"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/latest/manifest.json")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/static/v1"))) badge == 'dummyBadge' } @@ -224,7 +240,8 @@ class DockerControllerSpec extends Specification { then: WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/1.2.0/manifest.json"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/1.2.0/manifest.json")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/static/v1"))) badge == 'dummyBadge' } diff --git a/src/test/groovy/io/github/devatherock/artifactory/controllers/VersionControllerSpec.groovy b/src/test/groovy/io/github/devatherock/artifactory/controllers/VersionControllerSpec.groovy index 36754db..95de52d 100644 --- a/src/test/groovy/io/github/devatherock/artifactory/controllers/VersionControllerSpec.groovy +++ b/src/test/groovy/io/github/devatherock/artifactory/controllers/VersionControllerSpec.groovy @@ -2,6 +2,7 @@ package io.github.devatherock.artifactory.controllers import com.github.tomakehurst.wiremock.WireMockServer import com.github.tomakehurst.wiremock.client.WireMock +import io.github.devatherock.artifactory.service.DockerBadgeService import io.github.devatherock.test.TestUtil import io.micronaut.http.HttpRequest import io.micronaut.http.client.HttpClient @@ -49,19 +50,19 @@ class VersionControllerSpec extends Specification { and: WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack','2020-10-01T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack', '2020-10-01T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/1.1.0") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack/1.1.0','2020-10-08T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack/1.1.0', '2020-10-08T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/1.1.2") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack/1.1.2','2020-10-15T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack/1.1.2', '2020-10-15T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/latest") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack/latest','2020-10-01T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack/latest', '2020-10-01T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/abcdefgh") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack/abcdefgh','2020-10-22T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack/abcdefgh', '2020-10-22T00:00:00.000Z')))) WireMock.givenThat(WireMock.get(WireMock.urlPathEqualTo('/static/v1')) .withQueryParam('label', equalTo('version')) .withQueryParam('message', equalTo('abcdefgh')) @@ -74,15 +75,21 @@ class VersionControllerSpec extends Specification { .queryParam('package', packageName).build())) then: - WireMock.verify(1, WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}"))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.0"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.2"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.0")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/latest"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.2")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/abcdefgh"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/latest")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) + WireMock.verify(1, + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/abcdefgh")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/static/v1"))) badge == 'dummyBadge' } @@ -94,19 +101,19 @@ class VersionControllerSpec extends Specification { and: WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack','2020-10-01T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack', '2020-10-01T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/1.1.0") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack/1.1.0','2020-10-08T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack/1.1.0', '2020-10-08T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/1.1.2") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack/1.1.2','2020-10-15T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack/1.1.2', '2020-10-15T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/latest") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack/latest','2020-10-01T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack/latest', '2020-10-01T00:00:00.000Z')))) WireMock.givenThat(WireMock.get("/artifactory/api/storage/${packageName}/abcdefgh") .willReturn(WireMock.okJson( - TestUtil.getFoldersResponse('/devatherock/simple-slack/abcdefgh','2020-10-22T00:00:00.000Z')))) + TestUtil.getFoldersResponse('/devatherock/simple-slack/abcdefgh', '2020-10-22T00:00:00.000Z')))) WireMock.givenThat(WireMock.get(WireMock.urlPathEqualTo('/static/v1')) .withQueryParam('label', equalTo('tag')) .withQueryParam('message', equalTo('abcdefgh')) @@ -120,15 +127,21 @@ class VersionControllerSpec extends Specification { .queryParam('label', 'tag').build())) then: - WireMock.verify(1, WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}"))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.0"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) + WireMock.verify(1, + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.0")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.2"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/1.1.2")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/latest"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/latest")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, - WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/abcdefgh"))) + WireMock.getRequestedFor(urlEqualTo("/artifactory/api/storage/${packageName}/abcdefgh")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) WireMock.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo("/static/v1"))) badge == 'dummyBadge' } diff --git a/src/test/groovy/io/github/devatherock/artifactory/service/DockerBadgeServiceSpec.groovy b/src/test/groovy/io/github/devatherock/artifactory/service/DockerBadgeServiceSpec.groovy index c523a8b..326b4e0 100644 --- a/src/test/groovy/io/github/devatherock/artifactory/service/DockerBadgeServiceSpec.groovy +++ b/src/test/groovy/io/github/devatherock/artifactory/service/DockerBadgeServiceSpec.groovy @@ -1,13 +1,19 @@ package io.github.devatherock.artifactory.service +import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.WireMock import io.github.devatherock.artifactory.config.ArtifactoryProperties import io.github.devatherock.artifactory.entities.ArtifactoryFolderInfo import io.github.devatherock.artifactory.util.BadgeGenerator import io.micronaut.http.client.BlockingHttpClient import io.micronaut.http.client.HttpClient +import spock.lang.Shared import spock.lang.Specification import spock.lang.Subject +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo + /** * Test class for {@link DockerBadgeService} */ @@ -15,15 +21,50 @@ class DockerBadgeServiceSpec extends Specification { @Subject DockerBadgeService dockerBadgeService + @Shared + WireMockServer mockServer = new WireMockServer(8081) + + void setupSpec() { + WireMock.configureFor(8081) + mockServer.start() + } + + void cleanupSpec() { + mockServer.stop() + } + + void cleanup() { + mockServer.resetRequests() + } + BlockingHttpClient httpClient = HttpClient.create(new URL('http://localhost:8081')).toBlocking() BadgeGenerator badgeGenerator = Mock() - ArtifactoryProperties config = new ArtifactoryProperties(url: 'http://localhost:8081') + ArtifactoryProperties config = new ArtifactoryProperties(url: 'http://localhost:8081', apiKey: 'dummyKey') void setup() { config.init() dockerBadgeService = new DockerBadgeService(httpClient, badgeGenerator, config) } + void 'test get image size badge - manifest not found'() { + given: + String packageName = 'docker/devatherock/simple-slack' + + and: + WireMock.givenThat(WireMock.get("/artifactory/${packageName}/latest/manifest.json") + .willReturn(WireMock.notFound())) + + when: + String badge = dockerBadgeService.getImageSizeBadge(packageName, 'latest', 'image size') + + then: + WireMock.verify(1, + WireMock.getRequestedFor(urlEqualTo("/artifactory/${packageName}/latest/manifest.json")) + .withHeader(DockerBadgeService.HDR_API_KEY, equalTo('dummyKey'))) + 1 * badgeGenerator.generateBadge('image size', 'Not Found') >> 'dummyBadge' + badge == 'dummyBadge' + } + void 'test get version badge value'() { expect: dockerBadgeService.getVersionBadgeValue(new ArtifactoryFolderInfo(path: path)) == outputVersion @@ -47,4 +88,13 @@ class DockerBadgeServiceSpec extends Specification { 1_100_000_000 | '1.1G' 1_100_000_000_000 | '1100G' } + + void 'test generate not found badge'() { + when: + String badge = dockerBadgeService.generateNotFoundBadge('layers') + + then: + 1 * badgeGenerator.generateBadge('layers', 'Not Found') >> 'dummyBadge' + badge == 'dummyBadge' + } }