org.apache.aurora.scheduler.http.Mname.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.aurora.scheduler.http.Mname.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 org.apache.aurora.scheduler.http;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.inject.Inject;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

import org.apache.aurora.scheduler.base.JobKeys;
import org.apache.aurora.scheduler.base.Query;
import org.apache.aurora.scheduler.storage.Storage;
import org.apache.aurora.scheduler.storage.entities.IAssignedTask;
import org.apache.aurora.scheduler.storage.entities.IScheduledTask;

import static java.util.Objects.requireNonNull;

import static javax.ws.rs.core.Response.Status.NOT_FOUND;

import static org.apache.aurora.gen.ScheduleStatus.RUNNING;

/**
 * Servlet implementing HTTP redirects to task endpoints.
 */
@Path("/mname")
public class Mname {

    private static final String USAGE = "<html>HTTP redirector from the canonical name of a task to its current HTTP endpoint. "
            + "<p>Forwards GET, PUT, POST and DELETE requests using HTTP status code 307. "
            + "This allows compliant clients to seamlessly perform re-directed mutations.</p>"
            + "Usage: /mname/{role}/{env}/{job}/{instance}</html>";

    private static final Set<String> HTTP_PORT_NAMES = ImmutableSet.of("health", "http", "HTTP", "web", "admin");

    private final Storage storage;

    @Inject
    public Mname(Storage storage) {
        this.storage = requireNonNull(storage);
    }

    @GET
    public Response getUsage() {
        return Response.status(Status.BAD_REQUEST).entity(USAGE).build();
    }

    @GET
    @Path("/{role}/{env}/{job}/{instance}/{forward:.+}")
    public Response getWithForwardRequest(@PathParam("role") String role, @PathParam("env") String env,
            @PathParam("job") String job, @PathParam("instance") int instanceId,
            @PathParam("forward") String forward, @Context UriInfo uriInfo) {

        return get(role, env, job, instanceId, uriInfo, Optional.of(forward));
    }

    @PUT
    @Path("/{role}/{env}/{job}/{instance}/{forward:.+}")
    public Response putWithForwardRequest(@PathParam("role") String role, @PathParam("env") String env,
            @PathParam("job") String job, @PathParam("instance") int instanceId,
            @PathParam("forward") String forward, @Context UriInfo uriInfo) {

        return get(role, env, job, instanceId, uriInfo, Optional.of(forward));
    }

    @POST
    @Path("/{role}/{env}/{job}/{instance}/{forward:.+}")
    public Response postWithForwardRequest(@PathParam("role") String role, @PathParam("env") String env,
            @PathParam("job") String job, @PathParam("instance") int instanceId,
            @PathParam("forward") String forward, @Context UriInfo uriInfo) {

        return get(role, env, job, instanceId, uriInfo, Optional.of(forward));
    }

    @DELETE
    @Path("/{role}/{env}/{job}/{instance}/{forward:.+}")
    public Response deleteWithForwardRequest(@PathParam("role") String role, @PathParam("env") String env,
            @PathParam("job") String job, @PathParam("instance") int instanceId,
            @PathParam("forward") String forward, @Context UriInfo uriInfo) {

        return get(role, env, job, instanceId, uriInfo, Optional.of(forward));
    }

    @GET
    @Path("/{role}/{env}/{job}/{instance}")
    public Response get(@PathParam("role") String role, @PathParam("env") String env, @PathParam("job") String job,
            @PathParam("instance") int instanceId, @Context UriInfo uriInfo) {

        return get(role, env, job, instanceId, uriInfo, Optional.absent());
    }

    @PUT
    @Path("/{role}/{env}/{job}/{instance}")
    public Response put(@PathParam("role") String role, @PathParam("env") String env, @PathParam("job") String job,
            @PathParam("instance") int instanceId, @Context UriInfo uriInfo) {

        return get(role, env, job, instanceId, uriInfo, Optional.absent());
    }

    @POST
    @Path("/{role}/{env}/{job}/{instance}")
    public Response post(@PathParam("role") String role, @PathParam("env") String env, @PathParam("job") String job,
            @PathParam("instance") int instanceId, @Context UriInfo uriInfo) {

        return get(role, env, job, instanceId, uriInfo, Optional.absent());
    }

    @DELETE
    @Path("/{role}/{env}/{job}/{instance}")
    public Response delete(@PathParam("role") String role, @PathParam("env") String env,
            @PathParam("job") String job, @PathParam("instance") int instanceId, @Context UriInfo uriInfo) {

        return get(role, env, job, instanceId, uriInfo, Optional.absent());
    }

    private Response get(String role, String env, String job, int instanceId, UriInfo uriInfo,
            Optional<String> forwardRequest) {

        IScheduledTask task = Iterables.getOnlyElement(Storage.Util.fetchTasks(storage,
                Query.instanceScoped(JobKeys.from(role, env, job), instanceId).active()), null);
        if (task == null) {
            return respond(NOT_FOUND, "No such live instance found.");
        }

        if (task.getStatus() != RUNNING) {
            return respond(NOT_FOUND, "The selected instance is currently in state " + task.getStatus());
        }

        IAssignedTask assignedTask = task.getAssignedTask();
        Optional<Integer> port = getRedirectPort(assignedTask);
        if (!port.isPresent()) {
            return respond(NOT_FOUND, "The task does not have a registered http port.");
        }

        UriBuilder redirect = UriBuilder.fromPath(forwardRequest.or("/")).scheme("http")
                .host(assignedTask.getSlaveHost()).port(port.get());
        for (Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
            for (String value : entry.getValue()) {
                redirect.queryParam(entry.getKey(), value);
            }
        }

        return Response.temporaryRedirect(redirect.build()).build();
    }

    @VisibleForTesting
    static Optional<Integer> getRedirectPort(IAssignedTask task) {
        Map<String, Integer> ports = task.getAssignedPorts();
        for (String httpPortName : HTTP_PORT_NAMES) {
            Integer port = ports.get(httpPortName);
            if (port != null) {
                return Optional.of(port);
            }
        }
        return Optional.absent();
    }

    private Response respond(Status status, String message) {
        return Response.status(status).entity(message).build();
    }
}