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

Implement endpoint for readiness check #7

Merged
merged 1 commit into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Garden Linux Authors;

== Lorem Ipsum

=== Check running app

include::{snippets}/readiness/curl-request.adoc[]

include::{snippets}/readiness/http-response.adoc[]

=== Get a CVE by id

Request
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/io/gardenlinux/glvd/AppController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.gardenlinux.glvd;

import io.gardenlinux.glvd.dto.Readiness;
import io.gardenlinux.glvd.exceptions.DbNotConnectedException;
import jakarta.annotation.Nonnull;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AppController {

@Nonnull
private final GlvdService glvdService;

public AppController(@Nonnull GlvdService glvdService) {
this.glvdService = glvdService;
}

@GetMapping("/readiness")
public Readiness readiness() throws DbNotConnectedException {
return glvdService.getReadiness();
}

}
3 changes: 2 additions & 1 deletion src/main/java/io/gardenlinux/glvd/GlvdController.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ ResponseEntity<Cve> getCveId(@PathVariable("cveId") final String cveId) throws N
}

@GetMapping("/{vendor}/{product}/{codename}")
ResponseEntity<List<String>> getCveDistro(@PathVariable final String vendor, @PathVariable final String product, @PathVariable final String codename) {
ResponseEntity<List<String>> getCveDistro(@PathVariable final String vendor, @PathVariable final String product,
@PathVariable final String codename) {
return ResponseEntity.ok().body(glvdService.getCveForDistribution(vendor, product, codename));
}

Expand Down
19 changes: 18 additions & 1 deletion src/main/java/io/gardenlinux/glvd/GlvdService.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package io.gardenlinux.glvd;

import io.gardenlinux.glvd.db.CveRepository;
import io.gardenlinux.glvd.db.HealthCheckRepository;
import io.gardenlinux.glvd.dto.Cve;
import io.gardenlinux.glvd.dto.Readiness;
import io.gardenlinux.glvd.exceptions.DbNotConnectedException;
import io.gardenlinux.glvd.exceptions.NotFoundException;
import jakarta.annotation.Nonnull;
import org.springframework.stereotype.Service;
Expand All @@ -14,8 +17,21 @@ public class GlvdService {
@Nonnull
private final CveRepository cveRepository;

public GlvdService(@Nonnull CveRepository cveRepository) {
@Nonnull
private final HealthCheckRepository healthCheckRepository;

public GlvdService(@Nonnull CveRepository cveRepository, @Nonnull HealthCheckRepository healthCheckRepository) {
this.cveRepository = cveRepository;
this.healthCheckRepository = healthCheckRepository;
}

public Readiness getReadiness() throws DbNotConnectedException {
try {
var connection = healthCheckRepository.checkDbConnection();
return new Readiness(connection);
} catch (Exception e) {
throw new DbNotConnectedException(e);
}
}

public Cve getCve(String cveId) throws NotFoundException {
Expand All @@ -28,4 +44,5 @@ public Cve getCve(String cveId) throws NotFoundException {
public List<String> getCveForDistribution(String vendor, String product, String codename) {
return cveRepository.cvesForDistribution(vendor, product, codename);
}

}
11 changes: 8 additions & 3 deletions src/main/java/io/gardenlinux/glvd/db/CveEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

@Entity(name = "all_cve")
public class CveEntity {

@Id
@Column(name = "cve_id", nullable = false)
private String id;
Expand Down Expand Up @@ -46,11 +47,14 @@ public String getData() {

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;

CveEntity cveEntity = (CveEntity) o;
return Objects.equals(id, cveEntity.id) && lastModified.equals(cveEntity.lastModified) && data.equals(cveEntity.data);
return Objects.equals(id, cveEntity.id) && lastModified.equals(cveEntity.lastModified)
&& data.equals(cveEntity.data);
}

@Override
Expand All @@ -60,4 +64,5 @@ public int hashCode() {
result = 31 * result + data.hashCode();
return result;
}

}
1 change: 1 addition & 0 deletions src/main/java/io/gardenlinux/glvd/db/CveRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ INNER JOIN dist_cpe ON (deb_cve.dist_id = dist_cpe.id)
all_cve.cve_id
""", nativeQuery = true)
List<String> cvesForDistribution(String vendor, String product, String codename);

}
12 changes: 12 additions & 0 deletions src/main/java/io/gardenlinux/glvd/db/HealthCheckEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.gardenlinux.glvd.db;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public class HealthCheckEntity {

@Id
private String id;

}
11 changes: 11 additions & 0 deletions src/main/java/io/gardenlinux/glvd/db/HealthCheckRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.gardenlinux.glvd.db;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface HealthCheckRepository extends JpaRepository<HealthCheckEntity, String> {

@Query(value = "SELECT TRUE", nativeQuery = true)
String checkDbConnection();

}
10 changes: 8 additions & 2 deletions src/main/java/io/gardenlinux/glvd/dto/Cve.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import java.util.Objects;

public class Cve {

private String id;

@Nonnull
private String lastModified;

@Nonnull
private String data;

Expand Down Expand Up @@ -36,8 +39,10 @@ public String getData() {

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;

Cve Cve = (Cve) o;
return Objects.equals(id, Cve.id) && lastModified.equals(Cve.lastModified) && data.equals(Cve.data);
Expand All @@ -50,4 +55,5 @@ public int hashCode() {
result = 31 * result + data.hashCode();
return result;
}

}
33 changes: 33 additions & 0 deletions src/main/java/io/gardenlinux/glvd/dto/Readiness.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.gardenlinux.glvd.dto;

import java.util.Objects;

public class Readiness {

private final String dbCheck;

public Readiness(String dbCheck) {
this.dbCheck = dbCheck;
}

public String getDbCheck() {
return dbCheck;
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;

Readiness readiness = (Readiness) o;
return Objects.equals(dbCheck, readiness.dbCheck);
}

@Override
public int hashCode() {
return Objects.hashCode(dbCheck);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.gardenlinux.glvd.exceptions;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "Could not connect to Database. Is it running and configured correctly?")
public class DbNotConnectedException extends Throwable {
public DbNotConnectedException(Exception e) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Not Found")
public class NotFoundException extends Exception{
public class NotFoundException extends Exception {

}
1 change: 1 addition & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/glvd
spring.datasource.username=glvd
spring.datasource.password=glvd
spring.sql.init.mode=never
spring.jpa.properties.javax.persistence.query.timeout=5000
6 changes: 3 additions & 3 deletions src/test/java/io/gardenlinux/glvd/GlvdApplicationTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
class GlvdApplicationTests {

static DockerImageName glvdPostgresImage = DockerImageName.parse("ghcr.io/gardenlinux/glvd-postgres:edgesampledata")
.asCompatibleSubstituteFor("postgres");

@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
glvdPostgresImage
).withDatabaseName("glvd").withUsername("glvd").withPassword("glvd");
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(glvdPostgresImage).withDatabaseName("glvd")
.withUsername("glvd").withPassword("glvd");

@Test
void contextLoads() {
Expand Down
71 changes: 30 additions & 41 deletions src/test/java/io/gardenlinux/glvd/GlvdControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.testcontainers.utility.DockerImageName;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyUris;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
Expand All @@ -43,25 +42,17 @@ class GlvdControllerTest {

@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
glvdPostgresImage
).withDatabaseName("glvd").withUsername("glvd").withPassword("glvd");
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(glvdPostgresImage).withDatabaseName("glvd")
.withUsername("glvd").withPassword("glvd");

@Autowired
CveRepository cveRepository;

@LocalServerPort
private Integer port;

private RequestSpecification spec;

@BeforeEach
void setUp(RestDocumentationContextProvider restDocumentation) {
this.spec = new RequestSpecBuilder()
.addFilter(documentationConfiguration(restDocumentation)).build();

RestAssured.baseURI = "http://localhost:" + port;
}

@BeforeAll
static void beforeAll() {
postgres.start();
Expand All @@ -79,47 +70,45 @@ static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.password", postgres::getPassword);
}

@BeforeEach
void setUp(RestDocumentationContextProvider restDocumentation) {
this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(restDocumentation)).build();

RestAssured.baseURI = "http://localhost:" + port;
}

@Test
public void shouldGetCveById() {
given(this.spec)
.accept("application/json")
given(this.spec).accept("application/json")
.filter(document("getCve",
preprocessRequest(modifyUris()
.scheme("https")
.host("glvd.gardenlinux.io")
.removePort())))
.when()
.port(this.port)
.get("/v1/cves/CVE-2024-1549")
.then()
.statusCode(HttpStatus.SC_OK)
.body("id", containsString("CVE-2024-1549"));
preprocessRequest(modifyUris().scheme("https").host("glvd.gardenlinux.io").removePort())))
.when().port(this.port).get("/v1/cves/CVE-2024-1549")
.then().statusCode(HttpStatus.SC_OK).body("id", containsString("CVE-2024-1549"));
}

@Test
void tryGetNonExistingCveById() {
given()
.contentType(ContentType.JSON)
.when()
.get("/v1/cves/CVE-1989-1234")
.then()
.statusCode(HttpStatus.SC_NOT_FOUND);
given().contentType(ContentType.JSON)
.when().get("/v1/cves/CVE-1989-1234")
.then().statusCode(HttpStatus.SC_NOT_FOUND);
}

@Test
public void shouldReturnCvesForBookworm() {
given(this.spec)
.accept("application/json")
given(this.spec).accept("application/json")
.filter(document("getCveForDistro",
preprocessRequest(modifyUris()
.scheme("https")
.host("glvd.gardenlinux.io")
.removePort())))
.when()
.port(this.port)
.get("/v1/cves/debian/debian_linux/bookworm")
.then()
.statusCode(HttpStatus.SC_OK);
preprocessRequest(modifyUris().scheme("https").host("glvd.gardenlinux.io").removePort())))
.when().port(this.port).get("/v1/cves/debian/debian_linux/bookworm")
.then().statusCode(HttpStatus.SC_OK);
}

@Test
public void shouldBeReady() {
given(this.spec)
.filter(document("readiness",
preprocessRequest(modifyUris().scheme("https").host("glvd.gardenlinux.io").removePort())))
.when().port(this.port).get("/readiness")
.then().statusCode(HttpStatus.SC_OK).body("dbCheck", containsString("true"));
}

}
Loading