Skip to content

Commit

Permalink
Replaced cluster/x endpoint with summary of runs
Browse files Browse the repository at this point in the history
  • Loading branch information
Bj0rnen committed Mar 13, 2015
1 parent 2f9e90e commit 0d6bf98
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 198 deletions.
154 changes: 32 additions & 122 deletions src/main/java/com/spotify/reaper/resources/ClusterResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,22 @@
package com.spotify.reaper.resources;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.spotify.reaper.AppContext;
import com.spotify.reaper.ReaperException;
import com.spotify.reaper.cassandra.JmxProxy;
import com.spotify.reaper.core.Cluster;
import com.spotify.reaper.core.RepairRun;
import com.spotify.reaper.resources.view.ClusterRun;
import com.spotify.reaper.resources.view.ClusterStatus;
import com.spotify.reaper.resources.view.KeyspaceStatus;
import com.spotify.reaper.resources.view.hierarchy.HCluster;
import com.spotify.reaper.storage.PostgresStorage;

import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sun.nio.cs.ISO_8859_2;

import java.net.URI;
import java.net.URL;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -83,70 +72,44 @@ public Response getClusterList() {

@GET
@Path("/{cluster_name}")
public Response getCluster(@PathParam("cluster_name") String clusterName) {
public Response getCluster(
@PathParam("cluster_name") String clusterName,
@QueryParam("limit") Optional<Integer> limit) {
LOG.info("get cluster called with cluster_name: {}", clusterName);
Optional<Cluster> cluster = context.storage.getCluster(clusterName);
if (cluster.isPresent()) {
return viewCluster(cluster.get(), Optional.<URI>absent());
} else {
return Response.status(Response.Status.NOT_FOUND)
.entity("cluster with name \"" + clusterName + "\" not found").build();
}
return viewCluster(clusterName, limit, Optional.<URI>absent());
}

@GET
@Path("/{cluster_name}/{keyspace_name}")
public Response getCluster(@PathParam("cluster_name") String clusterName,
@PathParam("keyspace_name") String keyspaceName) {
LOG.info("get cluster/keyspace called with cluster_name: {}, and keyspace_name: {}",
clusterName, keyspaceName);
Optional<Cluster> cluster = context.storage.getCluster(clusterName);
if (cluster.isPresent()) {
return viewKeyspace(cluster.get(), keyspaceName);
} else {
return Response.status(Response.Status.NOT_FOUND)
.entity("cluster with name \"" + clusterName + "\" not found").build();
}
}
private Response viewCluster(String clusterName, Optional<Integer> limit,
Optional<URI> createdURI) {
Collection<ClusterRun> view = context.storage.getClusterRunOverview(clusterName, limit.or(10));

@GET
@Path("/{cluster_name}/hierarchy")
public Response getClusterHierarchy(@PathParam("cluster_name") String clusterName) {
LOG.info("get cluster overview called with cluster_name: {}", clusterName);
Optional<Cluster> cluster = context.storage.getCluster(clusterName);
if (cluster.isPresent()) {
return viewClusterHierarchy(cluster.get());
} else {
if (view == null) {
return Response.status(Response.Status.NOT_FOUND)
.entity("cluster with name \"" + clusterName + "\" not found").build();
}
}

@GET
@Path("/{cluster_name}/runs")
public Response getClusterRunOverview(
@PathParam("cluster_name") String clusterName,
@QueryParam("limit") Optional<Integer> limit) {
PostgresStorage ps = (PostgresStorage) context.storage;

Collection<ClusterRun> view =
ps.getClusterRunOverview(clusterName, limit.or(10));

ObjectMapper objectMapper = new ObjectMapper()
.setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)
.registerModule(new JodaModule());
try {
return Response.ok().entity(objectMapper.writeValueAsString(view)).build();
} catch (JsonProcessingException e) {
return Response.serverError().entity("JSON processing failed").build();
} else {
ObjectMapper objectMapper = new ObjectMapper()
.setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)
.registerModule(new JodaModule());
try {
if (createdURI.isPresent()) {
return Response.created(createdURI.get())
.entity(objectMapper.writeValueAsString(view)).build();
} else {
return Response.ok()
.entity(objectMapper.writeValueAsString(view)).build();
}
} catch (JsonProcessingException e) {
return Response.serverError().entity("JSON processing failed").build();
}
}
}

@POST
public Response addCluster(
@Context UriInfo uriInfo,
@QueryParam("seedHost") Optional<String> seedHost) {
@QueryParam("seedHost") Optional<String> seedHost,
@QueryParam("limit") Optional<Integer> limit) {
if (!seedHost.isPresent()) {
LOG.error("POST on cluster resource called without seedHost");
return Response.status(400).entity("query parameter \"seedHost\" required").build();
Expand All @@ -173,15 +136,15 @@ public Response addCluster(

URI createdURI;
try {
createdURI = (new URL(uriInfo.getAbsolutePath().toURL(), newCluster.getName())).toURI();
createdURI = new URL(uriInfo.getAbsolutePath().toURL(), newCluster.getName()).toURI();
} catch (Exception e) {
String errMsg = "failed creating target URI for cluster: " + newCluster.getName();
LOG.error(errMsg);
e.printStackTrace();
return Response.status(400).entity(errMsg).build();
}

return viewCluster(newCluster, Optional.of(createdURI));
return viewCluster(newCluster.getName(), limit, Optional.of(createdURI));
}

public Cluster createClusterWithSeedHost(String seedHost)
Expand All @@ -199,61 +162,6 @@ public Cluster createClusterWithSeedHost(String seedHost)
return new Cluster(clusterName, partitioner, Collections.singleton(seedHost));
}

private Response viewCluster(Cluster cluster, Optional<URI> createdURI) {
ClusterStatus view = new ClusterStatus(cluster);
Collection<Collection<Object>> runIdTuples = Lists.newArrayList();
for (Long repairRunId : context.storage.getRepairRunIdsForCluster(cluster.getName())) {
Optional<RepairRun> repairRun = context.storage.getRepairRun(repairRunId);
if (repairRun.isPresent()) {
runIdTuples
.add(Lists.newArrayList(new Object[]{repairRunId, repairRun.get().getRunState()}));
}
}
view.setRepairRunIds(runIdTuples);
try (JmxProxy jmx = context.jmxConnectionFactory.connectAny(cluster)) {
view.setKeyspaces(jmx.getKeyspaces());
} catch (ReaperException e) {
e.printStackTrace();
LOG.error("failed connecting JMX", e);
return Response.status(500).entity("failed connecting given clusters JMX endpoint").build();
}
if (createdURI.isPresent()) {
return Response.created(createdURI.get()).entity(view).build();
} else {
return Response.ok().entity(view).build();
}
}

private Response viewKeyspace(Cluster cluster, String keyspaceName) {
KeyspaceStatus view = new KeyspaceStatus(cluster);
try (JmxProxy jmx = context.jmxConnectionFactory.connectAny(cluster)) {
if (jmx.getKeyspaces().contains(keyspaceName)) {
view.setTables(jmx.getTableNamesForKeyspace(keyspaceName));
} else {
return Response.status(Response.Status.NOT_FOUND)
.entity("cluster with name \"" + cluster.getName() + "\" does not contain keyspace \""
+ keyspaceName + "\"").build();
}
} catch (ReaperException e) {
e.printStackTrace();
LOG.error("failed connecting JMX", e);
return Response.status(500).entity("failed connecting given clusters JMX endpoint").build();
}
return Response.ok().entity(view).build();
}

private Response viewClusterHierarchy(Cluster cluster) {
HCluster view = new HCluster(context.storage, cluster.getName());
try {
ObjectMapper objectMapper = new ObjectMapper()
.setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
return Response.ok().entity(objectMapper.writeValueAsString(view)).build();
} catch (JsonProcessingException e) {
return Response.serverError().entity("JSON processing failed").build();
}
}

/**
* Delete a Cluster object with given name.
*
Expand All @@ -265,7 +173,9 @@ private Response viewClusterHierarchy(Cluster cluster) {
*/
@DELETE
@Path("/{cluster_name}")
public Response deleteCluster(@PathParam("cluster_name") String clusterName) {
public Response deleteCluster(
@PathParam("cluster_name") String clusterName,
@QueryParam("limit") Optional<Integer> limit) {
LOG.info("delete cluster called with clusterName: {}", clusterName);
Optional<Cluster> clusterToDelete = context.storage.getCluster(clusterName);
if (!clusterToDelete.isPresent()) {
Expand All @@ -284,7 +194,7 @@ public Response deleteCluster(@PathParam("cluster_name") String clusterName) {
}
Optional<Cluster> deletedCluster = context.storage.deleteCluster(clusterName);
if (deletedCluster.isPresent()) {
return Response.ok().entity(new ClusterStatus(deletedCluster.get())).build();
return Response.ok().build();
}
return Response.serverError().entity("delete failed for schedule with name \""
+ clusterName + "\"").build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

public class ClusterRun {

public final int runId;
public final long runId;
public final String cluserName;
public final String keyspaceName;
public final String[] columnFamilies;
Expand All @@ -54,7 +54,7 @@ public String getEstimatedTimeOfArrival() {
return CommonTools.dateTimeToISO8601(estimatedTimeOfArrival);
}

public ClusterRun(int runId, String clusterName, String keyspaceName, String[] columnFamilies,
public ClusterRun(long runId, String clusterName, String keyspaceName, String[] columnFamilies,
int segmentsRepaired, int totalSegments, RepairRun.RunState state, DateTime startTime,
DateTime endTime, String cause, String owner, String lastEvent) {
this.runId = runId;
Expand Down Expand Up @@ -92,7 +92,7 @@ public static class Mapper implements ResultSetMapper<ClusterRun> {

@Override
public ClusterRun map(int index, ResultSet r, StatementContext ctx) throws SQLException {
int runId = r.getInt("id");
long runId = r.getLong("id");
String clusterName = r.getString("cluster_name");
String keyspaceName = r.getString("keyspace_name");
String[] columnFamilies = (String[]) r.getArray("column_families").getArray();
Expand Down
70 changes: 0 additions & 70 deletions src/main/java/com/spotify/reaper/resources/view/ClusterStatus.java

This file was deleted.

2 changes: 2 additions & 0 deletions src/main/java/com/spotify/reaper/storage/IStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.spotify.reaper.core.RepairSchedule;
import com.spotify.reaper.core.RepairSegment;
import com.spotify.reaper.core.RepairUnit;
import com.spotify.reaper.resources.view.ClusterRun;
import com.spotify.reaper.service.RingRange;

import java.util.Collection;
Expand Down Expand Up @@ -124,4 +125,5 @@ Optional<RepairUnit> getRepairUnit(String cluster, String keyspace,
*/
Optional<RepairSchedule> deleteRepairSchedule(long id);

Collection<ClusterRun> getClusterRunOverview(String clusterName, int limit);
}
26 changes: 26 additions & 0 deletions src/main/java/com/spotify/reaper/storage/MemoryStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.spotify.reaper.core.RepairSchedule;
import com.spotify.reaper.core.RepairSegment;
import com.spotify.reaper.core.RepairUnit;
import com.spotify.reaper.resources.view.ClusterRun;
import com.spotify.reaper.service.RingRange;

import java.util.ArrayList;
Expand Down Expand Up @@ -375,6 +376,31 @@ public Optional<RepairSchedule> deleteRepairSchedule(long id) {
return Optional.fromNullable(deletedSchedule);
}

@Override
public Collection<ClusterRun> getClusterRunOverview(String clusterName, int limit) {
Optional<Cluster> cluster = getCluster(clusterName);
if (!cluster.isPresent()) {
return null;
} else {
List<ClusterRun> clusterRuns = Lists.newArrayList();
Collection<RepairRun> runs = getRepairRunsForCluster(clusterName);
for (RepairRun run : runs) {
RepairUnit unit = getRepairUnit(run.getRepairUnitId()).get();
String[] tables =
unit.getColumnFamilies().toArray(new String[unit.getColumnFamilies().size()]);
int segmentsRepaired =
getSegmentAmountForRepairRunWithState(run.getId(), RepairSegment.State.DONE);
int totalSegments = getSegmentAmountForRepairRun(run.getId());
clusterRuns.add(new ClusterRun(
run.getId(), clusterName, unit.getKeyspaceName(),
tables, segmentsRepaired, totalSegments,
run.getRunState(), run.getStartTime(), run.getEndTime(), run.getCause(),
run.getOwner(), run.getLastEvent()));
}
return clusterRuns;
}
}

public static class RepairUnitKey {

public final String cluster;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ public void deleting_cluster_called_fails(String clusterName) throws Throwable {
@And("^cluster called \"([^\"]*)\" is deleted$")
public void cluster_called_is_deleted(String clusterName) throws Throwable {
callAndExpect("DELETE", "/cluster/" + clusterName,
EMPTY_PARAMS, Response.Status.OK, Optional.of("\"" + clusterName + "\""));
EMPTY_PARAMS, Response.Status.OK, Optional.<String>absent());
}

@Then("^reaper has no cluster called \"([^\"]*)\" in storage$")
Expand Down
Loading

0 comments on commit 0d6bf98

Please sign in to comment.