package keywhiz.service.resources;
import com.codahale.metrics.annotation.ExceptionMetered;
import com.codahale.metrics.annotation.Timed;
import com.codahale.metrics.health.HealthCheck;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import io.dropwizard.setup.Environment;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import keywhiz.KeywhizConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.util.stream.Collectors.toList;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
/** Serve status information */
@Path("/_status")
@Produces(APPLICATION_JSON)
public class StatusResource {
private static final Logger logger = LoggerFactory.getLogger(SecretDeliveryResource.class);
Supplier<SortedMap<String, HealthCheck.Result>> memoizedCheck;
@Inject public StatusResource(KeywhizConfig keywhizConfig, Environment environment) {
Duration cacheExpiry = keywhizConfig.getStatusCacheExpiry();
memoizedCheck = Suppliers.memoizeWithExpiration(() -> environment.healthChecks().runHealthChecks(),
cacheExpiry.toMillis(), TimeUnit.MILLISECONDS);
}
@Timed @ExceptionMetered
@GET
public Response get() {
SortedMap<String, HealthCheck.Result> results = memoizedCheck.get();
List<String> failing = results.entrySet().stream()
.filter(r -> !r.getValue().isHealthy())
.map(Map.Entry::getKey)
.collect(toList());
if (!failing.isEmpty()) {
logger.warn("Health checks failed: {}", results);
String message = "failing health checks: " + Arrays.toString(failing.toArray());
StatusResponse sr = new StatusResponse("critical", message, results);
return Response.serverError().entity(sr).build();
}
StatusResponse sr = new StatusResponse("ok", "ok", results);
return Response.ok(sr).build();
}
public static class StatusResponse {
private String status;
private String message;
private SortedMap<String, HealthCheck.Result> results;
public SortedMap<String, HealthCheck.Result> getResults() {
return results;
}
public String getMessage() {
return message;
}
public String getStatus() {
return status;
}
StatusResponse(String status, String message, SortedMap<String, HealthCheck.Result> results) {
this.status = status;
this.message = message;
this.results = results;
}
@Override public String toString() {
return "StatusResponse{" +
"status='" + this.getStatus() + '\'' +
", message='" + this.getMessage() + '\'' +
", results=" + this.getResults() +
'}';
}
}
}