diff --git a/common/src/main/java/io/druid/audit/AuditManager.java b/common/src/main/java/io/druid/audit/AuditManager.java index 6540fac8a0f..0513d596004 100644 --- a/common/src/main/java/io/druid/audit/AuditManager.java +++ b/common/src/main/java/io/druid/audit/AuditManager.java @@ -54,4 +54,12 @@ public interface AuditManager */ public List fetchAuditHistory(String key, String type, Interval interval); + /** + * provides audit history for given type and interval + * @param type type of auditEntry + * @param interval interval for which to fetch auditHistory + * @return list of AuditEntries satisfying the passed parameters + */ + public List fetchAuditHistory(String type, Interval interval); + } diff --git a/docs/content/design/coordinator.md b/docs/content/design/coordinator.md index 4fe48a95fc6..0dedde1d2f8 100644 --- a/docs/content/design/coordinator.md +++ b/docs/content/design/coordinator.md @@ -183,10 +183,13 @@ Returns all rules for a specified datasource. Returns all rules for a specified datasource and includes default datasource. +* `/druid/coordinator/v1/rules/history?interval=` + + Returns audit history of rules for all datasources. default value of interval can be specified by setting `druid.audit.manager.auditHistoryMillis` (1 week if not configured) in coordinator runtime.properties * `/druid/coordinator/v1/rules/{dataSourceName}/history?interval=` - Returns audit history of rules. default value of interval can be specified by setting `druid.audit.manager.auditHistoryMillis` (1 week if not configured) in coordinator runtime.properties + Returns audit history of rules for a specified datasource. default value of interval can be specified by setting `druid.audit.manager.auditHistoryMillis` (1 week if not configured) in coordinator runtime.properties ### POST diff --git a/server/src/main/java/io/druid/server/audit/SQLAuditManager.java b/server/src/main/java/io/druid/server/audit/SQLAuditManager.java index 246b318a917..84bbe53a140 100644 --- a/server/src/main/java/io/druid/server/audit/SQLAuditManager.java +++ b/server/src/main/java/io/druid/server/audit/SQLAuditManager.java @@ -116,13 +116,7 @@ public class SQLAuditManager implements AuditManager @Override public List fetchAuditHistory(final String key, final String type, Interval interval) { - final Interval theInterval; - if (interval == null) { - DateTime now = new DateTime(); - theInterval = new Interval(now.minus(config.getAuditHistoryMillis()), now); - } else { - theInterval = interval; - } + final Interval theInterval = getIntervalOrDefault(interval); return dbi.withHandle( new HandleCallback>() { @@ -160,4 +154,57 @@ public class SQLAuditManager implements AuditManager ); } + private Interval getIntervalOrDefault(Interval interval) + { + final Interval theInterval; + if (interval == null) { + DateTime now = new DateTime(); + theInterval = new Interval(now.minus(config.getAuditHistoryMillis()), now); + } else { + theInterval = interval; + } + return theInterval; + } + + @Override + public List fetchAuditHistory(final String type, Interval interval) + { + final Interval theInterval = getIntervalOrDefault(interval); + return dbi.withHandle( + new HandleCallback>() + { + @Override + public List withHandle(Handle handle) throws Exception + { + return handle.createQuery( + String.format( + "SELECT payload FROM %s WHERE type = :type and created_date between :start_date and :end_date ORDER BY created_date", + getAuditTable() + ) + ) + .bind("type", type) + .bind("start_date", theInterval.getStart().toString()) + .bind("end_date", theInterval.getEnd().toString()) + .map( + new ResultSetMapper() + { + @Override + public AuditEntry map(int index, ResultSet r, StatementContext ctx) + throws SQLException + { + try { + return jsonMapper.readValue(r.getBytes("payload"), AuditEntry.class); + } + catch (IOException e) { + throw new SQLException(e); + } + } + } + ) + .list(); + } + } + ); + } + } diff --git a/server/src/main/java/io/druid/server/http/RulesResource.java b/server/src/main/java/io/druid/server/http/RulesResource.java index e3ea5c444bb..b5dbc3fb9a4 100644 --- a/server/src/main/java/io/druid/server/http/RulesResource.java +++ b/server/src/main/java/io/druid/server/http/RulesResource.java @@ -17,6 +17,7 @@ package io.druid.server.http; +import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import io.druid.audit.AuditInfo; import io.druid.audit.AuditManager; @@ -79,6 +80,7 @@ public class RulesResource return Response.ok(databaseRuleManager.getRules(dataSourceName)) .build(); } + // default value is used for backwards compatibility @POST @Path("/{dataSourceName}") @@ -114,4 +116,21 @@ public class RulesResource .build(); } + @GET + @Path("/history") + @Produces(MediaType.APPLICATION_JSON) + public Response getDatasourceRuleHistory( + @QueryParam("interval") final String interval + ) + { + try { + Interval theInterval = interval == null ? null : new Interval(interval); + return Response.ok(auditManager.fetchAuditHistory("rules", theInterval)) + .build(); + } + catch (IllegalArgumentException e) { + return Response.serverError().entity(ImmutableMap.of("error", e.getMessage())).build(); + } + } + } diff --git a/server/src/test/java/io/druid/metadata/SQLMetadataRuleManagerTest.java b/server/src/test/java/io/druid/metadata/SQLMetadataRuleManagerTest.java index 02b144ab805..6b1a94dbccd 100644 --- a/server/src/test/java/io/druid/metadata/SQLMetadataRuleManagerTest.java +++ b/server/src/test/java/io/druid/metadata/SQLMetadataRuleManagerTest.java @@ -17,6 +17,8 @@ package io.druid.metadata; + +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; @@ -127,11 +129,61 @@ public class SQLMetadataRuleManagerTest List auditEntries = auditManager.fetchAuditHistory("test_dataSource", "rules", null); Assert.assertEquals(1, auditEntries.size()); AuditEntry entry = auditEntries.get(0); - Assert.assertEquals(mapper.writeValueAsString(rules), entry.getPayload()); + + Assert.assertEquals( + rules, mapper.readValue( + entry.getPayload(), new TypeReference>() + { + } + ) + ); Assert.assertEquals(auditInfo, entry.getAuditInfo()); Assert.assertEquals("test_dataSource", entry.getKey()); } + @Test + public void testFetchAuditEntriesForAllDataSources() throws Exception + { + List rules = Arrays.asList( + new IntervalLoadRule( + new Interval("2015-01-01/2015-02-01"), ImmutableMap.of( + DruidServer.DEFAULT_TIER, + DruidServer.DEFAULT_NUM_REPLICANTS + ) + ) + ); + AuditInfo auditInfo = new AuditInfo("test_author", "test_comment", "127.0.0.1"); + ruleManager.overrideRule( + "test_dataSource", + rules, + auditInfo + ); + ruleManager.overrideRule( + "test_dataSource2", + rules, + auditInfo + ); + // fetch rules from metadata storage + ruleManager.poll(); + + Assert.assertEquals(rules, ruleManager.getRules("test_dataSource")); + Assert.assertEquals(rules, ruleManager.getRules("test_dataSource2")); + + // test fetch audit entries + List auditEntries = auditManager.fetchAuditHistory("rules", null); + Assert.assertEquals(2, auditEntries.size()); + for (AuditEntry entry : auditEntries) { + Assert.assertEquals( + rules, mapper.readValue( + entry.getPayload(), new TypeReference>() + { + } + ) + ); + Assert.assertEquals(auditInfo, entry.getAuditInfo()); + } + } + @After public void cleanup() {