-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Add suspend|resume|terminate all supervisors endpoints. #6272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
45a4449
6b0f9c2
04b37fe
36db5b0
19c45db
cad44e6
bdd83a0
37ca9a3
1c8b26a
5248adc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -260,6 +260,35 @@ public Response terminate(@PathParam("id") final String id) | |
| ); | ||
| } | ||
|
|
||
| @POST | ||
| @Path("/suspendAll") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't feel like the |
||
| @Produces(MediaType.APPLICATION_JSON) | ||
| public Response suspendAll() | ||
| { | ||
| return suspendOrResumeAll(true); | ||
| } | ||
|
|
||
| @POST | ||
| @Path("/resumeAll") | ||
| @Produces(MediaType.APPLICATION_JSON) | ||
| public Response resumeAll() | ||
| { | ||
| return suspendOrResumeAll(false); | ||
| } | ||
|
|
||
| @POST | ||
| @Path("/terminateAll") | ||
| @Produces(MediaType.APPLICATION_JSON) | ||
| public Response terminateAll() | ||
| { | ||
| return asLeaderWithSupervisorManager( | ||
| manager -> { | ||
| manager.stopAndRemoveAllSupervisors(); | ||
| return Response.ok(ImmutableMap.of("status", "success")).build(); | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| @GET | ||
| @Path("/history") | ||
| @Produces(MediaType.APPLICATION_JSON) | ||
|
|
@@ -378,23 +407,34 @@ private Response specSuspendOrResume(final String id, boolean suspend) | |
| { | ||
| return asLeaderWithSupervisorManager( | ||
| manager -> { | ||
| Optional<SupervisorSpec> spec = manager.getSupervisorSpec(id); | ||
| if (!spec.isPresent()) { | ||
| return Response.status(Response.Status.NOT_FOUND) | ||
| .entity(ImmutableMap.of("error", StringUtils.format("[%s] does not exist", id))) | ||
| .build(); | ||
| } | ||
|
|
||
| if (spec.get().isSuspended() == suspend) { | ||
| final String errMsg = | ||
| StringUtils.format("[%s] is already %s", id, suspend ? "suspended" : "running"); | ||
| return Response.status(Response.Status.BAD_REQUEST) | ||
| if (manager.suspendOrResumeSupervisor(id, suspend)) { | ||
| Optional<SupervisorSpec> spec = manager.getSupervisorSpec(id); | ||
| return Response.ok(spec.get()).build(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calls Optional.get() without a check
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no need to do so, because |
||
| } else { | ||
| Optional<SupervisorSpec> spec = manager.getSupervisorSpec(id); | ||
| Response.Status status; | ||
| String errMsg; | ||
| if (spec.isPresent()) { | ||
| status = Response.Status.BAD_REQUEST; | ||
| errMsg = StringUtils.format("[%s] is already %s", id, suspend ? "suspended" : "running"); | ||
| } else { | ||
| status = Response.Status.NOT_FOUND; | ||
| errMsg = StringUtils.format("[%s] does not exist", id); | ||
| } | ||
| return Response.status(status) | ||
| .entity(ImmutableMap.of("error", errMsg)) | ||
| .build(); | ||
| } | ||
| manager.suspendOrResumeSupervisor(id, suspend); | ||
| spec = manager.getSupervisorSpec(id); | ||
| return Response.ok(spec.get()).build(); | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| private Response suspendOrResumeAll(boolean suspend) | ||
| { | ||
| return asLeaderWithSupervisorManager( | ||
| manager -> { | ||
| manager.suspendOrResumeAllSupervisors(suspend); | ||
| return Response.ok(ImmutableMap.of("status", "success")).build(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like it could be useful to return the list of spec ids which successfully had a state change for this method and terminate all, but I don't feel strongly about it
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have stated the reason why I did not return these ids above. For your convenience, I paste here:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| } | ||
| ); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -292,14 +292,6 @@ public void testSpecGetStatus() | |
| @Test | ||
| public void testSpecSuspend() | ||
| { | ||
|
|
||
| TestSupervisorSpec running = new TestSupervisorSpec("my-id", null, null, false) { | ||
| @Override | ||
| public List<String> getDataSources() | ||
| { | ||
| return Collections.singletonList("datasource1"); | ||
| } | ||
| }; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this should be removed, previously this test was first ensuring that a running spec would correctly be suspended, and then that an already suspended spec would result an error, now this only tests the latter behavior.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason why I removed this is that I have moved the condition
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see now 👍 |
||
| TestSupervisorSpec suspended = new TestSupervisorSpec("my-id", null, null, true) { | ||
| @Override | ||
| public List<String> getDataSources() | ||
|
|
@@ -309,11 +301,8 @@ public List<String> getDataSources() | |
| }; | ||
|
|
||
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.of(supervisorManager)); | ||
| EasyMock.expect(supervisorManager.getSupervisorSpec("my-id")) | ||
| .andReturn(Optional.of(running)).times(1) | ||
| .andReturn(Optional.of(suspended)).times(1); | ||
| EasyMock.expect(supervisorManager.suspendOrResumeSupervisor("my-id", true)).andReturn(true); | ||
| EasyMock.expectLastCall().anyTimes(); | ||
| EasyMock.expect(supervisorManager.getSupervisorSpec("my-id")).andReturn(Optional.of(suspended)); | ||
| replayAll(); | ||
|
|
||
| Response response = supervisorResource.specSuspend("my-id"); | ||
|
|
@@ -326,7 +315,8 @@ public List<String> getDataSources() | |
| resetAll(); | ||
|
|
||
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.of(supervisorManager)); | ||
| EasyMock.expect(supervisorManager.getSupervisorSpec("my-id")).andReturn(Optional.of(suspended)).atLeastOnce(); | ||
| EasyMock.expect(supervisorManager.suspendOrResumeSupervisor("my-id", true)).andReturn(false); | ||
| EasyMock.expect(supervisorManager.getSupervisorSpec("my-id")).andReturn(Optional.of(suspended)); | ||
| replayAll(); | ||
|
|
||
| response = supervisorResource.specSuspend("my-id"); | ||
|
|
@@ -336,18 +326,9 @@ public List<String> getDataSources() | |
| Assert.assertEquals(ImmutableMap.of("error", "[my-id] is already suspended"), response.getEntity()); | ||
| } | ||
|
|
||
|
|
||
|
|
||
| @Test | ||
| public void testSpecResume() | ||
| { | ||
| TestSupervisorSpec suspended = new TestSupervisorSpec("my-id", null, null, true) { | ||
| @Override | ||
| public List<String> getDataSources() | ||
| { | ||
| return Collections.singletonList("datasource1"); | ||
| } | ||
| }; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same |
||
| TestSupervisorSpec running = new TestSupervisorSpec("my-id", null, null, false) { | ||
| @Override | ||
| public List<String> getDataSources() | ||
|
|
@@ -357,11 +338,8 @@ public List<String> getDataSources() | |
| }; | ||
|
|
||
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.of(supervisorManager)); | ||
| EasyMock.expect(supervisorManager.getSupervisorSpec("my-id")) | ||
| .andReturn(Optional.of(suspended)).times(1) | ||
| .andReturn(Optional.of(running)).times(1); | ||
| EasyMock.expect(supervisorManager.suspendOrResumeSupervisor("my-id", false)).andReturn(true); | ||
| EasyMock.expectLastCall().anyTimes(); | ||
| EasyMock.expect(supervisorManager.getSupervisorSpec("my-id")).andReturn(Optional.of(running)); | ||
| replayAll(); | ||
|
|
||
| Response response = supervisorResource.specResume("my-id"); | ||
|
|
@@ -374,7 +352,8 @@ public List<String> getDataSources() | |
| resetAll(); | ||
|
|
||
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.of(supervisorManager)); | ||
| EasyMock.expect(supervisorManager.getSupervisorSpec("my-id")).andReturn(Optional.of(running)).atLeastOnce(); | ||
| EasyMock.expect(supervisorManager.suspendOrResumeSupervisor("my-id", false)).andReturn(false); | ||
| EasyMock.expect(supervisorManager.getSupervisorSpec("my-id")).andReturn(Optional.of(running)); | ||
| replayAll(); | ||
|
|
||
| response = supervisorResource.specResume("my-id"); | ||
|
|
@@ -385,19 +364,19 @@ public List<String> getDataSources() | |
| } | ||
|
|
||
| @Test | ||
| public void testShutdown() | ||
| public void testTerminate() | ||
| { | ||
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.of(supervisorManager)).times(2); | ||
| EasyMock.expect(supervisorManager.stopAndRemoveSupervisor("my-id")).andReturn(true); | ||
| EasyMock.expect(supervisorManager.stopAndRemoveSupervisor("my-id-2")).andReturn(false); | ||
| replayAll(); | ||
|
|
||
| Response response = supervisorResource.shutdown("my-id"); | ||
| Response response = supervisorResource.terminate("my-id"); | ||
|
|
||
| Assert.assertEquals(200, response.getStatus()); | ||
| Assert.assertEquals(ImmutableMap.of("id", "my-id"), response.getEntity()); | ||
|
|
||
| response = supervisorResource.shutdown("my-id-2"); | ||
| response = supervisorResource.terminate("my-id-2"); | ||
|
|
||
| Assert.assertEquals(404, response.getStatus()); | ||
| verifyAll(); | ||
|
|
@@ -407,12 +386,54 @@ public void testShutdown() | |
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.absent()); | ||
| replayAll(); | ||
|
|
||
| response = supervisorResource.shutdown("my-id"); | ||
| response = supervisorResource.terminate("my-id"); | ||
| verifyAll(); | ||
|
|
||
| Assert.assertEquals(503, response.getStatus()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSuspendAll() | ||
| { | ||
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.of(supervisorManager)); | ||
| supervisorManager.suspendOrResumeAllSupervisors(true); | ||
| EasyMock.expectLastCall(); | ||
| replayAll(); | ||
|
|
||
| Response response = supervisorResource.suspendAll(); | ||
| Assert.assertEquals(200, response.getStatus()); | ||
| Assert.assertEquals(ImmutableMap.of("status", "success"), response.getEntity()); | ||
| verifyAll(); | ||
| } | ||
|
|
||
| @Test | ||
| public void testResumeAll() | ||
| { | ||
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.of(supervisorManager)); | ||
| supervisorManager.suspendOrResumeAllSupervisors(false); | ||
| EasyMock.expectLastCall(); | ||
| replayAll(); | ||
|
|
||
| Response response = supervisorResource.resumeAll(); | ||
| Assert.assertEquals(200, response.getStatus()); | ||
| Assert.assertEquals(ImmutableMap.of("status", "success"), response.getEntity()); | ||
| verifyAll(); | ||
| } | ||
|
|
||
| @Test | ||
| public void testTerminateAll() | ||
| { | ||
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.of(supervisorManager)); | ||
| supervisorManager.stopAndRemoveAllSupervisors(); | ||
| EasyMock.expectLastCall(); | ||
| replayAll(); | ||
|
|
||
| Response response = supervisorResource.terminateAll(); | ||
| Assert.assertEquals(200, response.getStatus()); | ||
| Assert.assertEquals(ImmutableMap.of("status", "success"), response.getEntity()); | ||
| verifyAll(); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSpecGetAllHistory() | ||
| { | ||
|
|
@@ -872,7 +893,7 @@ public void testReset() | |
| EasyMock.expect(taskMaster.getSupervisorManager()).andReturn(Optional.absent()); | ||
| replayAll(); | ||
|
|
||
| response = supervisorResource.shutdown("my-id"); | ||
| response = supervisorResource.terminate("my-id"); | ||
|
|
||
| Assert.assertEquals(503, response.getStatus()); | ||
| verifyAll(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I still aesthetically prefer
/druid/indexer/v1/supervisor/suspend, etc. over/druid/indexer/v1/supervisor/suspendAll, etc, but that's just a matter of personal preference, up to you whether or not to change it 😄There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine for me if change to
/druid/indexer/v1/supervisors/suspend, but just as you said before, that seems more painful than useful to change. So I keep thisAllsuffix. Besides, in PR #6185Allis also be used in endpoint path.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I wasn't suggesting supervisors with the s since yeah that's a more dramatic change, just leaving out the All, but looking around at the rest of the APIs some more, and things already aren't terribly consistent with things like pluralization, etc., anyway so I think basically whatever is probably fine as long as there are docs and stuff does what is intended 😜