com.spotify.reaper.resources.ClusterResource.java Source code

Java tutorial

Introduction

Here is the source code for com.spotify.reaper.resources.ClusterResource.java

Source

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.spotify.reaper.resources;

import com.google.common.base.Optional;

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.resources.view.ClusterStatus;
import com.spotify.reaper.resources.view.RepairRunStatus;
import com.spotify.reaper.resources.view.RepairScheduleStatus;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

@Path("/cluster")
@Produces(MediaType.APPLICATION_JSON)
public class ClusterResource {

    private static final Logger LOG = LoggerFactory.getLogger(ClusterResource.class);

    private final AppContext context;

    public ClusterResource(AppContext context) {
        this.context = context;
    }

    @GET
    public Response getClusterList(@QueryParam("seedHost") Optional<String> seedHost) {
        LOG.debug("get cluster list called");
        Collection<Cluster> clusters = context.storage.getClusters();
        List<String> clusterNames = new ArrayList<>();
        for (Cluster cluster : clusters) {
            if (seedHost.isPresent()) {
                if (cluster.getSeedHosts().contains(seedHost.get())) {
                    clusterNames.add(cluster.getName());
                }
            } else {
                clusterNames.add(cluster.getName());
            }
        }
        return Response.ok().entity(clusterNames).build();
    }

    @GET
    @Path("/{cluster_name}")
    public Response getCluster(@PathParam("cluster_name") String clusterName,
            @QueryParam("limit") Optional<Integer> limit) {
        LOG.debug("get cluster called with cluster_name: {}", clusterName);
        return viewCluster(clusterName, limit, Optional.<URI>absent());
    }

    private Response viewCluster(String clusterName, Optional<Integer> limit, Optional<URI> createdURI) {
        Optional<Cluster> cluster = context.storage.getCluster(clusterName);

        if (!cluster.isPresent()) {
            return Response.status(Response.Status.NOT_FOUND)
                    .entity("cluster with name \"" + clusterName + "\" not found").build();
        } else {
            ClusterStatus view = new ClusterStatus(cluster.get(),
                    context.storage.getClusterRunStatuses(clusterName, limit.or(Integer.MAX_VALUE)),
                    context.storage.getClusterScheduleStatuses(clusterName));
            if (createdURI.isPresent()) {
                return Response.created(createdURI.get()).entity(view).build();
            } else {
                return Response.ok().entity(view).build();
            }
        }
    }

    @POST
    public Response addCluster(@Context UriInfo uriInfo, @QueryParam("seedHost") Optional<String> seedHost) {
        if (!seedHost.isPresent()) {
            LOG.error("POST on cluster resource called without seedHost");
            return Response.status(400).entity("query parameter \"seedHost\" required").build();
        }
        LOG.debug("add cluster called with seedHost: {}", seedHost.get());

        Cluster newCluster;
        try {
            newCluster = createClusterWithSeedHost(seedHost.get());
        } catch (java.lang.SecurityException e) {
            return Response.status(400)
                    .entity("seed host \"" + seedHost.get() + "\" JMX threw security exception: " + e.getMessage())
                    .build();
        } catch (ReaperException e) {
            return Response.status(400).entity("failed to create cluster with seed host: " + seedHost.get())
                    .build();
        }
        Optional<Cluster> existingCluster = context.storage.getCluster(newCluster.getName());
        if (existingCluster.isPresent()) {
            LOG.info("cluster already stored with this name: {}", existingCluster);
            return Response.status(403)
                    .entity(String.format("cluster \"%s\" already exists", existingCluster.get().getName()))
                    .build();
        } else {
            LOG.info("creating new cluster based on given seed host: {}", newCluster.getName());
            context.storage.addCluster(newCluster);
        }

        URI createdURI;
        try {
            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.getName(), Optional.<Integer>absent(), Optional.of(createdURI));
    }

    public Cluster createClusterWithSeedHost(String seedHost) throws ReaperException {
        String clusterName;
        String partitioner;
        try (JmxProxy jmxProxy = context.jmxConnectionFactory.connect(seedHost)) {
            clusterName = jmxProxy.getClusterName();
            partitioner = jmxProxy.getPartitioner();
        } catch (ReaperException e) {
            LOG.error("failed to create cluster with seed host: " + seedHost);
            e.printStackTrace();
            throw e;
        }
        return new Cluster(clusterName, partitioner, Collections.singleton(seedHost));
    }

    /**
     * Delete a Cluster object with given name.
     *
     * Cluster can be only deleted when it hasn't any RepairRun or RepairSchedule instances under it,
     * i.e. you must delete all repair runs and schedules first.
     *
     * @param clusterName The name of the Cluster instance you are about to delete.
     * @return The deleted RepairRun instance, with state overwritten to string "DELETED".
     */
    @DELETE
    @Path("/{cluster_name}")
    public Response deleteCluster(@PathParam("cluster_name") String clusterName) {
        LOG.info("delete cluster called with clusterName: {}", clusterName);
        Optional<Cluster> clusterToDelete = context.storage.getCluster(clusterName);
        if (!clusterToDelete.isPresent()) {
            return Response.status(Response.Status.NOT_FOUND)
                    .entity("cluster with name \"" + clusterName + "\" not found").build();
        }
        if (!context.storage.getRepairSchedulesForCluster(clusterName).isEmpty()) {
            return Response.status(Response.Status.FORBIDDEN).entity(
                    "cluster with name \"" + clusterName + "\" cannot be deleted, as it " + "has repair schedules")
                    .build();
        }
        if (!context.storage.getRepairRunsForCluster(clusterName).isEmpty()) {
            return Response.status(Response.Status.FORBIDDEN).entity(
                    "cluster with name \"" + clusterName + "\" cannot be deleted, as it " + "has repair runs")
                    .build();
        }
        Optional<Cluster> deletedCluster = context.storage.deleteCluster(clusterName);
        if (deletedCluster.isPresent()) {
            return Response.ok(new ClusterStatus(deletedCluster.get(), Collections.<RepairRunStatus>emptyList(),
                    Collections.<RepairScheduleStatus>emptyList())).build();
        }
        return Response.serverError().entity("delete failed for schedule with name \"" + clusterName + "\"")
                .build();
    }

}