/**
* 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.Map;
import java.util.Objects;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.apache.aurora.common.collections.Pair;
import static javax.servlet.http.HttpServletResponse.SC_BAD_GATEWAY;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
import static org.apache.aurora.scheduler.http.LeaderRedirect.LeaderStatus;
import static org.apache.aurora.scheduler.http.LeaderRedirect.LeaderStatus.LEADING;
import static org.apache.aurora.scheduler.http.LeaderRedirect.LeaderStatus.NOT_LEADING;
import static org.apache.aurora.scheduler.http.LeaderRedirect.LeaderStatus.NO_LEADER;
/**
* Servlet that exposes the leader health. It is meant to be used by load balancers such as
* ELB to always forward to the leading scheduler.
*/
@Path("/leaderhealth")
public class LeaderHealth {
private static final Map<LeaderStatus, Pair<Integer, String>> RESPONSE_CODES = ImmutableMap.of(
LEADING, Pair.of(SC_OK, "This is the leading scheduler."),
NOT_LEADING, Pair.of(SC_SERVICE_UNAVAILABLE, "Another instance is the leading scheduler."),
NO_LEADER, Pair.of(SC_BAD_GATEWAY, "There is no leading scheduler or the leader is unknown.")
);
private static final String HELP = generateHelpText();
private final LeaderRedirect leaderRedirect;
@Inject
LeaderHealth(LeaderRedirect leaderRedirect) {
this.leaderRedirect = Objects.requireNonNull(leaderRedirect);
}
@VisibleForTesting
static String getHelpText(LeaderStatus status) {
return "Current status: " + RESPONSE_CODES.get(status).getSecond()
+ "\n\n"
+ HELP;
}
/**
* Gets the leadership status of the scheduler.
*
* @return HTTP response.
*/
@GET
public Response get() {
LeaderStatus leaderStatus = leaderRedirect.getLeaderStatus();
return Response.status(RESPONSE_CODES.get(leaderStatus).getFirst())
.type(MediaType.TEXT_PLAIN_TYPE)
.entity(getHelpText(leaderStatus))
.build();
}
private static String generateHelpText() {
Map<Integer, String> details = Maps.transformValues(
Maps.uniqueIndex(RESPONSE_CODES.values(), Pair::getFirst),
Pair::getSecond);
return "This endpoint can indicate to a load balancer which among a group of schedulers "
+ "is leading.\n\nThe response codes are:\n"
+ Joiner.on("\n").withKeyValueSeparator(": ").join(details);
}
}