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.impl;
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")
52 @RestService(name = "terminationstateservice", title = "Termination State Service",
53 abstractText = "When put into a termination 'wait' state, this service stops the node accepting further jobs,"
54 + " and once any running jobs complete will change termination to 'ready'."
55 + " NOTE: The service does not actually shut down the node or instance.",
56 notes = {
57 "All paths above are relative to the REST endpoint base (something like http://your.server/termination/)",
58 "If the service is down or not working it will return a status 503, this means the the underlying service is "
59 + "not working and is either restarting or has failed",
60 "A status code 500 means a general failure has occurred which is not recoverable and was not anticipated. In "
61 + "other words, there is a bug! You should file an error report with your server logs from the time when the "
62 + "error occurred: <a href=\"https://github.com/opencast/opencast/issues\">Opencast Issue Tracker</a>"})
63 @Component(
64 immediate = true,
65 service = TerminationStateRestServiceImpl.class,
66 property = {
67 "service.description=Termination State Rest Service",
68 "opencast.service.type=org.opencastproject.terminationstate.impl",
69 "opencast.service.path=/termination"
70 }
71 )
72 @JaxrsResource
73 public class TerminationStateRestServiceImpl implements TerminationStateRestService {
74
75 private static final Logger logger = LoggerFactory.getLogger(TerminationStateRestServiceImpl.class);
76
77 private TerminationStateService service;
78
79 @Override
80 @GET
81 @Path("/state")
82 @Produces(MediaType.APPLICATION_JSON)
83 @RestQuery(
84 name = "stateasjson",
85 description = "Returns the Termination State as JSON. Possible termination states are none, wait and ready.",
86 returnDescription = "A JSON representation of the termination state.",
87 responses = {
88 @RestResponse(
89 responseCode = SC_OK,
90 description = "A JSON representation of the termination state."
91 ),
92 @RestResponse(
93 responseCode = SC_SERVICE_UNAVAILABLE,
94 description = "The Termination State Service is disabled or unavailable"
95 ),
96 }
97 )
98 public Response getState() {
99 if (service != null) {
100 JSONObject json = new JSONObject();
101 String state = service.getState().toString();
102 json.put("state", state);
103 return Response.ok(json.toJSONString()).build();
104 } else {
105 logger.error("TerminationStateService is not available");
106 return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
107 }
108 }
109
110 @Override
111 @PUT
112 @Path("/state")
113 @RestQuery(
114 name = "setstate",
115 description = "Set the termination state. The only permissable values are 'none', 'wait' or 'ready'",
116 returnDescription = "Whether the termination state was set successfully",
117 restParameters = {
118 @RestParameter(
119 name = "state",
120 type = Type.STRING,
121 defaultValue = "wait",
122 description = "The termination state, the only valid value is 'wait'",
123 isRequired = true
124 ),
125 },
126 responses = {
127 @RestResponse(
128 responseCode = SC_NO_CONTENT,
129 description = "The node is preparing to terminate"
130 ),
131 @RestResponse(
132 responseCode = SC_BAD_REQUEST,
133 description = "The state was not valid"
134 ),
135 @RestResponse(
136 responseCode = SC_SERVICE_UNAVAILABLE,
137 description = "The Termination State Service is disabled or unavailable"
138 ),
139 }
140 )
141 public Response setState(@FormParam("state") String state) {
142 if (service != null) {
143 TerminationStateService.TerminationState tState;
144 switch (state) {
145 case "none":
146 tState = TerminationStateService.TerminationState.NONE;
147 break;
148 case "wait":
149 tState = TerminationStateService.TerminationState.WAIT;
150 break;
151 case "ready":
152 tState = TerminationStateService.TerminationState.READY;
153 break;
154 default:
155 logger.error("state {} is invalid", state);
156 return Response.status(Response.Status.BAD_REQUEST).build();
157 }
158
159 service.setState(tState);
160 return Response.noContent().build();
161 }
162
163 logger.error("AWS Autoscaling Termination State Serice is not available");
164 return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
165 }
166
167
168
169
170
171 @Reference(
172 target = "(&(vendor.name=opencast)(vendor.service=basic))"
173 )
174 public void setService(TerminationStateService service) {
175 this.service = service;
176 }
177 }