endpoint to delete lookup tier and remove tier on last lookup deletion (#7852)

This commit is contained in:
Himanshu 2019-06-15 17:55:50 -07:00 committed by GitHub
parent f603498e11
commit b3328b2785
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 189 additions and 2 deletions

View File

@ -292,7 +292,10 @@ Using the prior example, a `GET` to `/druid/coordinator/v1/lookups/config/realti
```
## Delete Lookup
A `DELETE` to `/druid/coordinator/v1/lookups/config/{tier}/{id}` will remove that lookup from the cluster.
A `DELETE` to `/druid/coordinator/v1/lookups/config/{tier}/{id}` will remove that lookup from the cluster. If it was last lookup in the tier, then tier is deleted as well.
## Delete Tier
A `DELETE` to `/druid/coordinator/v1/lookups/config/{tier}` will remove that tier from the cluster.
## List tier names
A `GET` to `/druid/coordinator/v1/lookups/config` will return a list of known tier names in the dynamic configuration.

View File

@ -176,6 +176,35 @@ public class LookupCoordinatorResource
}
}
@DELETE
@Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE})
@Path("/config/{tier}")
public Response deleteTier(
@PathParam("tier") String tier,
@HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author,
@HeaderParam(AuditManager.X_DRUID_COMMENT) @DefaultValue("") final String comment,
@Context HttpServletRequest req
)
{
try {
if (Strings.isNullOrEmpty(tier)) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(ServletResourceUtils.sanitizeException(new NullPointerException("`tier` required")))
.build();
}
if (lookupCoordinatorManager.deleteTier(tier, new AuditInfo(author, comment, req.getRemoteAddr()))) {
return Response.status(Response.Status.ACCEPTED).build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
catch (Exception e) {
LOG.error(e, "Error deleting tier [%s]", tier);
return Response.serverError().entity(ServletResourceUtils.sanitizeException(e)).build();
}
}
@DELETE
@Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE})
@Path("/config/{tier}/{lookup}")

View File

@ -255,6 +255,27 @@ public class LookupCoordinatorManager
return lookupMapConfigRef.get();
}
public boolean deleteTier(final String tier, AuditInfo auditInfo)
{
Preconditions.checkState(lifecycleLock.awaitStarted(5, TimeUnit.SECONDS), "not started");
synchronized (this) {
final Map<String, Map<String, LookupExtractorFactoryMapContainer>> priorSpec = getKnownLookups();
if (priorSpec == null) {
LOG.warn("Requested delete tier [%s]. But no lookups exist!", tier);
return false;
}
final Map<String, Map<String, LookupExtractorFactoryMapContainer>> updateSpec = new HashMap<>(priorSpec);
if (updateSpec.remove(tier) == null) {
LOG.warn("Requested delete of tier [%s] that does not exist!", tier);
return false;
}
return configManager.set(LOOKUP_CONFIG_KEY, updateSpec, auditInfo).isOk();
}
}
public boolean deleteLookup(final String tier, final String lookup, AuditInfo auditInfo)
{
Preconditions.checkState(lifecycleLock.awaitStarted(5, TimeUnit.SECONDS), "not started");
@ -279,7 +300,12 @@ public class LookupCoordinatorManager
final Map<String, LookupExtractorFactoryMapContainer> updateTierSpec = new HashMap<>(priorTierSpec);
updateTierSpec.remove(lookup);
updateSpec.put(tier, updateTierSpec);
if (updateTierSpec.isEmpty()) {
updateSpec.remove(tier);
} else {
updateSpec.put(tier, updateTierSpec);
}
return configManager.set(LOOKUP_CONFIG_KEY, updateSpec, auditInfo).isOk();
}
}

View File

@ -286,6 +286,48 @@ public class LookupCoordinatorResourceTest
EasyMock.verify(lookupCoordinatorManager);
}
@Test
public void testSimpleDeleteTier()
{
final String author = "some author";
final String comment = "some comment";
final String ip = "127.0.0.1";
final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class);
EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once();
final Capture<AuditInfo> auditInfoCapture = Capture.newInstance();
final LookupCoordinatorManager lookupCoordinatorManager = EasyMock.createStrictMock(
LookupCoordinatorManager.class);
EasyMock.expect(lookupCoordinatorManager.deleteTier(
EasyMock.eq(LOOKUP_TIER),
EasyMock.capture(auditInfoCapture)
)).andReturn(true).once();
EasyMock.replay(lookupCoordinatorManager, request);
final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource(
lookupCoordinatorManager,
mapper,
mapper
);
final Response response = lookupCoordinatorResource.deleteTier(
LOOKUP_TIER,
author,
comment,
request
);
Assert.assertEquals(202, response.getStatus());
Assert.assertTrue(auditInfoCapture.hasCaptured());
final AuditInfo auditInfo = auditInfoCapture.getValue();
Assert.assertEquals(author, auditInfo.getAuthor());
Assert.assertEquals(comment, auditInfo.getComment());
Assert.assertEquals(ip, auditInfo.getIp());
EasyMock.verify(lookupCoordinatorManager, request);
}
@Test
public void testSimpleDelete()
{

View File

@ -846,6 +846,52 @@ public class LookupCoordinatorManagerTest
EasyMock.verify(configManager);
}
@Test
public void testDeleteTier()
{
final LookupExtractorFactoryMapContainer foo1 = new LookupExtractorFactoryMapContainer(
"v0",
ImmutableMap.of("lookup", "foo1")
);
final LookupExtractorFactoryMapContainer foo2 = new LookupExtractorFactoryMapContainer(
"v0",
ImmutableMap.of("lookup", "foo2")
);
final LookupCoordinatorManager manager = new LookupCoordinatorManager(
client,
druidNodeDiscoveryProvider,
mapper,
configManager,
lookupCoordinatorManagerConfig
)
{
@Override
public Map<String, Map<String, LookupExtractorFactoryMapContainer>> getKnownLookups()
{
return ImmutableMap.of(LOOKUP_TIER, ImmutableMap.of(
"foo1", foo1,
"foo2", foo2
));
}
};
manager.start();
final AuditInfo auditInfo = new AuditInfo("author", "comment", "localhost");
EasyMock.reset(configManager);
EasyMock.expect(
configManager.set(
EasyMock.eq(LookupCoordinatorManager.LOOKUP_CONFIG_KEY),
EasyMock.eq(
ImmutableMap.<String, Map<String, LookupExtractorFactoryMapContainer>>of()
),
EasyMock.eq(auditInfo)
)
).andReturn(SetResult.ok()).once();
EasyMock.replay(configManager);
Assert.assertTrue(manager.deleteTier(LOOKUP_TIER, auditInfo));
EasyMock.verify(configManager);
}
@Test
public void testDeleteLookup()
{
@ -896,6 +942,47 @@ public class LookupCoordinatorManagerTest
EasyMock.verify(configManager);
}
@Test
public void testDeleteLastLookup()
{
final LookupExtractorFactoryMapContainer lookup = new LookupExtractorFactoryMapContainer(
"v0",
ImmutableMap.of("lookup", "foo")
);
final LookupCoordinatorManager manager = new LookupCoordinatorManager(
client,
druidNodeDiscoveryProvider,
mapper,
configManager,
lookupCoordinatorManagerConfig
)
{
@Override
public Map<String, Map<String, LookupExtractorFactoryMapContainer>> getKnownLookups()
{
return ImmutableMap.of(LOOKUP_TIER, ImmutableMap.of(
"foo", lookup
));
}
};
manager.start();
final AuditInfo auditInfo = new AuditInfo("author", "comment", "localhost");
EasyMock.reset(configManager);
EasyMock.expect(
configManager.set(
EasyMock.eq(LookupCoordinatorManager.LOOKUP_CONFIG_KEY),
EasyMock.eq(
ImmutableMap.<String, Map<String, LookupExtractorFactoryMapContainer>>of()
),
EasyMock.eq(auditInfo)
)
).andReturn(SetResult.ok()).once();
EasyMock.replay(configManager);
Assert.assertTrue(manager.deleteLookup(LOOKUP_TIER, "foo", auditInfo));
EasyMock.verify(configManager);
}
@Test
public void testDeleteLookupIgnoresMissing()
{