This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new d653807aee17 CAMEL-25773: camel-aws-secrets-manager - use one start
time for all CloudTrail lookups of a refresh check (#28256)
d653807aee17 is described below
commit d653807aee17d689450a6be5c6f4302ce1172d42
Author: allthingssecurity <[email protected]>
AuthorDate: Fri Oct 23 02:18:52 2026 +0530
CAMEL-25773: camel-aws-secrets-manager - use one start time for all
CloudTrail lookups of a refresh check (#28256)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../vault/CloudTrailReloadTriggerTask.java | 47 ++++++++++--------
.../CloudTrailReloadTriggerTaskLookupTest.java | 57 +++++++++++++++++++++-
2 files changed, 82 insertions(+), 22 deletions(-)
diff --git
a/components/camel-aws/camel-aws-secrets-manager/src/main/java/org/apache/camel/component/aws/secretsmanager/vault/CloudTrailReloadTriggerTask.java
b/components/camel-aws/camel-aws-secrets-manager/src/main/java/org/apache/camel/component/aws/secretsmanager/vault/CloudTrailReloadTriggerTask.java
index 060f5656cd00..0006ce6e966b 100644
---
a/components/camel-aws/camel-aws-secrets-manager/src/main/java/org/apache/camel/component/aws/secretsmanager/vault/CloudTrailReloadTriggerTask.java
+++
b/components/camel-aws/camel-aws-secrets-manager/src/main/java/org/apache/camel/component/aws/secretsmanager/vault/CloudTrailReloadTriggerTask.java
@@ -281,18 +281,21 @@ public class CloudTrailReloadTriggerTask extends
ServiceSupport implements Camel
}
/**
- * Adds every event with the given event name since the last check to the
list, reading all pages.
+ * Adds every event with the given event name since the given time to the
list, reading all pages.
+ *
+ * @param since the start time of the lookup, the same for every page and
every event name of one check; null to
+ * look up without a start time
*/
- private void lookupEvents(String eventName, List<Event> events) {
+ private void lookupEvents(String eventName, Instant since, List<Event>
events) {
String nextToken = null;
do {
LookupEventsRequest.Builder eventsRequestBuilder =
LookupEventsRequest.builder()
.maxResults(LOOKUP_MAX_RESULTS)
.lookupAttributes(LookupAttribute.builder().attributeKey(LookupAttributeKey.EVENT_NAME)
.attributeValue(eventName).build());
- if (lastTime != null) {
+ if (since != null) {
// inclusive, so an event in the same second as the newest
event seen is not skipped
- eventsRequestBuilder.startTime(lastTime);
+ eventsRequestBuilder.startTime(since);
}
if (nextToken != null) {
eventsRequestBuilder.nextToken(nextToken);
@@ -314,13 +317,15 @@ public class CloudTrailReloadTriggerTask extends
ServiceSupport implements Camel
// read every page of the update events since the last check.
LookupEvents takes a single lookup
// attribute, so there is one lookup per event name: filtering
on the event source instead would also
// return every read (GetSecretValue, DescribeSecret), and the
pages would grow with the read traffic
+ // read lastTime once: every page of both lookups of this
check uses the same start time
+ Instant since = lastTime;
List<Event> events = new ArrayList<>();
for (String eventName : UPDATE_EVENT_NAMES) {
- lookupEvents(eventName, events);
+ lookupEvents(eventName, since, events);
}
LOG.debug("Found {} events", events.size());
- Instant newest = lastTime;
+ Instant newest = since;
Set<String> newestEventIds = new HashSet<>();
for (Event event : events) {
if (lastTimeEventIds.contains(event.eventId())) {
@@ -335,25 +340,25 @@ public class CloudTrailReloadTriggerTask extends
ServiceSupport implements Camel
} else if (eventTime.equals(newest)) {
newestEventIds.add(event.eventId());
}
- if
(event.eventSource().equalsIgnoreCase(SECRETSMANAGER_AMAZONAWS_COM)) {
- if
(event.eventName().equalsIgnoreCase(SECRETSMANAGER_UPDATE_EVENT)
- ||
event.eventName().equalsIgnoreCase(SECRETSMANAGER_UPDATE_SECRET_EVENT)) {
- List<Resource> a = event.resources();
- for (Resource res : a) {
- String name = res.resourceName();
- if (matchSecret(name)) {
- updates.put(name, event.eventTime());
- if (isReloadEnabled()) {
- LOG.info("Update for AWS secret: {}
detected, triggering CamelContext reload", name);
- triggerReloading = true;
- }
- break;
- }
+ // the lookups are filtered by event name (PutSecretValue
or UpdateSecret), but event names are not
+ // unique across AWS services, so the event source is
still checked
+ if
(!SECRETSMANAGER_AMAZONAWS_COM.equalsIgnoreCase(event.eventSource())) {
+ continue;
+ }
+ List<Resource> a = event.resources();
+ for (Resource res : a) {
+ String name = res.resourceName();
+ if (matchSecret(name)) {
+ updates.put(name, event.eventTime());
+ if (isReloadEnabled()) {
+ LOG.info("Update for AWS secret: {} detected,
triggering CamelContext reload", name);
+ triggerReloading = true;
}
+ break;
}
}
}
- if (newest != null && newest.equals(lastTime)) {
+ if (newest != null && newest.equals(since)) {
// no newer event: the events at lastTime seen before are
still returned by the next lookup
newestEventIds.addAll(lastTimeEventIds);
}
diff --git
a/components/camel-aws/camel-aws-secrets-manager/src/test/java/org/apache/camel/component/aws/secretsmanager/vault/CloudTrailReloadTriggerTaskLookupTest.java
b/components/camel-aws/camel-aws-secrets-manager/src/test/java/org/apache/camel/component/aws/secretsmanager/vault/CloudTrailReloadTriggerTaskLookupTest.java
index 4888bf91084b..51161e2439ef 100644
---
a/components/camel-aws/camel-aws-secrets-manager/src/test/java/org/apache/camel/component/aws/secretsmanager/vault/CloudTrailReloadTriggerTaskLookupTest.java
+++
b/components/camel-aws/camel-aws-secrets-manager/src/test/java/org/apache/camel/component/aws/secretsmanager/vault/CloudTrailReloadTriggerTaskLookupTest.java
@@ -62,6 +62,8 @@ class CloudTrailReloadTriggerTaskLookupTest {
private final ContextReloadStrategy reload =
mock(ContextReloadStrategy.class);
private DefaultCamelContext context;
private int eventIds;
+ // run in each LookupEvents call, to change the task while its lookups run
+ private Runnable onLookup;
@BeforeEach
void setUp() throws Exception {
@@ -78,6 +80,9 @@ class CloudTrailReloadTriggerTaskLookupTest {
private LookupEventsResponse lookup(LookupEventsRequest request) {
requests.add(request);
+ if (onLookup != null) {
+ onLookup.run();
+ }
// the service takes at most one lookup attribute
assertTrue(request.lookupAttributes().size() <= 1, "LookupEvents takes
at most one lookup attribute");
LookupAttribute attribute = request.lookupAttributes().isEmpty() ?
null : request.lookupAttributes().get(0);
@@ -105,7 +110,11 @@ class CloudTrailReloadTriggerTaskLookupTest {
}
private void addEvent(String name, String secret, Instant time) {
- visibleEvents.add(Event.builder().eventId("id-" +
eventIds++).eventSource(SOURCE).eventName(name)
+ addEvent(SOURCE, name, secret, time);
+ }
+
+ private void addEvent(String source, String name, String secret, Instant
time) {
+ visibleEvents.add(Event.builder().eventId("id-" +
eventIds++).eventSource(source).eventName(name)
.eventTime(time).resources(Resource.builder().resourceName(secret).build()).build());
}
@@ -227,4 +236,50 @@ class CloudTrailReloadTriggerTaskLookupTest {
assertEquals(2, requests.size());
assertTrue(requests.stream().allMatch(r ->
t0.plusSeconds(5).equals(r.startTime())));
}
+
+ @Test
+ void everyPageOfBothLookupsOfOneCheckUsesTheSameStartTime() throws
Exception {
+ Instant t0 = Instant.parse("2026-10-17T10:00:00Z");
+ CloudTrailReloadTriggerTask task = task(t0);
+ // two pages of PutSecretValue events and one page of UpdateSecret
events
+ for (int i = 0; i < 60; i++) {
+ addEvent("PutSecretValue", "other-secret", t0.plusSeconds(1 + i %
20));
+ }
+ addEvent("UpdateSecret", "tracked-secret", t0.plusSeconds(5));
+ // lastTime changes while the first page is read: the other pages and
the other lookup keep the start time
+ onLookup = () -> {
+ if (requests.size() == 1) {
+ try {
+ setField(task, "lastTime", null);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ };
+
+ task.run();
+
+ assertEquals(3, requests.size(), "two pages of PutSecretValue and one
page of UpdateSecret");
+ for (LookupEventsRequest request : requests) {
+ assertEquals(t0, request.startTime(), "every page of both lookups
must use the start time of the check");
+ }
+ assertTrue(task.getUpdates().containsKey("tracked-secret"));
+ }
+
+ @Test
+ void updateEventOfAnotherServiceIsIgnored() throws Exception {
+ Instant t0 = Instant.parse("2026-10-17T10:00:00Z");
+ CloudTrailReloadTriggerTask task = task(t0);
+ // the lookup filters by event name only, and event names are not
unique across AWS services
+ addEvent("other-service.amazonaws.com", "UpdateSecret",
"tracked-secret", t0.plusSeconds(5));
+
+ task.run();
+
+ assertFalse(task.getUpdates().containsKey("tracked-secret"), "an event
of another service is not an update");
+ verify(reload, times(0)).onReload(task);
+ // the event still moves the start time of the next check
+ requests.clear();
+ task.run();
+ assertTrue(requests.stream().allMatch(r ->
t0.plusSeconds(5).equals(r.startTime())));
+ }
}