1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.terminationstate.endpoint.aws;
22
23 import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
24 import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
25 import static javax.servlet.http.HttpServletResponse.SC_OK;
26 import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
27
28 import org.opencastproject.terminationstate.api.TerminationStateService;
29 import org.opencastproject.terminationstate.endpoint.api.TerminationStateRestService;
30 import org.opencastproject.util.doc.rest.RestParameter;
31 import org.opencastproject.util.doc.rest.RestParameter.Type;
32 import org.opencastproject.util.doc.rest.RestQuery;
33 import org.opencastproject.util.doc.rest.RestResponse;
34 import org.opencastproject.util.doc.rest.RestService;
35
36 import org.json.simple.JSONObject;
37 import org.osgi.service.component.annotations.Component;
38 import org.osgi.service.component.annotations.Reference;
39 import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import javax.ws.rs.FormParam;
44 import javax.ws.rs.GET;
45 import javax.ws.rs.PUT;
46 import javax.ws.rs.Path;
47 import javax.ws.rs.Produces;
48 import javax.ws.rs.core.MediaType;
49 import javax.ws.rs.core.Response;
50
51 @Path("/termination/aws/autoscaling")
52 @RestService(
53 name = "terminationstateservice",
54 title = "Termination State Service: AWS Auto Scaling",
55 abstractText = "This service responds to notifications from an AWS AutoScaling Group that the "
56 + "underlying EC2 instance is terminating. When put into a termination 'wait' state, it "
57 + "stops the node accepting further jobs, and will inform AWS AutoScaling, once any "
58 + "running jobs complete, that the instance can be terminated. NOTE: The service does not "
59 + "actually shut down the node or instance.",
60 notes = {
61 "All paths above are relative to the REST endpoint base (something like "
62 + "http://your.server/termination/aws/autoscaling)",
63 "If the service is down or not working it will return a status 503, this means the the "
64 + "underlying service is not working and is either restarting or has failed",
65 "A status code 500 means a general failure has occurred which is not recoverable and was "
66 + "not anticipated. In other words, there is a bug! You should file an error report "
67 + "with your server logs from the time when the error occurred: "
68 + "<a href=\"https://github.com/opencast/opencast/issues\">Opencast Issue Tracker</a>"
69 }
70 )
71 @Component(
72 immediate = true,
73 service = AutoScalingTerminationStateRestService.class,
74 property = {
75 "service.description=Termination State Rest Service: AWS Auto Scaling",
76 "opencast.service.type=org.opencastproject.terminationstate.aws.autoscaling",
77 "opencast.service.path=/termination/aws/autoscaling"
78 }
79 )
80 @JaxrsResource
81 public class AutoScalingTerminationStateRestService implements TerminationStateRestService {
82
83 private static final Logger logger = LoggerFactory.getLogger(AutoScalingTerminationStateRestService.class);
84
85 private TerminationStateService service;
86
87 @Override
88 @GET
89 @Path("/state")
90 @Produces(MediaType.APPLICATION_JSON)
91 @RestQuery(
92 name = "stateasjson",
93 description = "Returns the Termination State as JSON. Possible termination states are none, wait and ready.",
94 returnDescription = "A JSON representation of the termination state.",
95 responses = {
96 @RestResponse(
97 responseCode = SC_OK,
98 description = "A JSON representation of the termination state."
99 ),
100 @RestResponse(
101 responseCode = SC_SERVICE_UNAVAILABLE,
102 description = "The AWS Autoscaling Termination State Service is disabled or unavailable"
103 )
104 }
105 )
106 public Response getState() {
107 if (service != null) {
108 JSONObject json = new JSONObject();
109 String state = service.getState().toString();
110 json.put("state", state);
111 return Response.ok(json.toJSONString()).build();
112 } else {
113 logger.error("TerminationStateService is not available");
114 return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
115 }
116 }
117
118 @Override
119 @PUT
120 @Path("/state")
121 @RestQuery(
122 name = "setstate",
123 description = "Set the termination state. The only permissable value to write to the state is 'wait'",
124 returnDescription = "Whether the termination state was set successfully",
125 restParameters = {
126 @RestParameter(
127 name = "state",
128 type = Type.STRING,
129 defaultValue = "wait",
130 description = "The termination state, the only valid value is 'wait'",
131 isRequired = false
132 )
133 },
134 responses = {
135 @RestResponse(
136 responseCode = SC_NO_CONTENT,
137 description = "The node is preparing to terminate"
138 ),
139 @RestResponse(
140 responseCode = SC_BAD_REQUEST,
141 description = "The state was not 'wait'"
142 ),
143 @RestResponse(
144 responseCode = SC_SERVICE_UNAVAILABLE,
145 description = "The AWS Autoscaling Termination State Service is disabled or unavailable"
146 ),
147 }
148 )
149 public Response setState(@FormParam("state") String state) {
150 if (service != null) {
151 if (TerminationStateService.TerminationState.WAIT.toString().equalsIgnoreCase(state)) {
152 service.setState(TerminationStateService.TerminationState.WAIT);
153
154
155 if (service.getState() != TerminationStateService.TerminationState.NONE) {
156 return Response.noContent().build();
157 }
158 } else {
159 logger.error("state must be 'wait'");
160 return Response.status(Response.Status.BAD_REQUEST).build();
161 }
162 }
163
164 logger.error("AWS Autoscaling Termination State Serice is not available");
165 return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
166 }
167
168
169
170
171
172 @Reference(
173 target = "(&(vendor.name=aws)(vendor.service=autoscaling))"
174 )
175 public void setService(TerminationStateService service) {
176 this.service = service;
177 }
178 }