Propagate instanceId in Events
Add instanceId in the Events payload when defined. The value won't be present in the JSON payload if the value is not set in the Gerrit config Reference design: https://gerrit-review.googlesource.com/c/homepage/+/263710 Feature: Issue 12685 Change-Id: I507db3f0efba5649ad519361f88ee3f60ab27205
This commit is contained in:
@@ -1281,6 +1281,39 @@ public class MyListener implements GitReferenceUpdatedListener {
|
||||
}
|
||||
----
|
||||
|
||||
== Trace Event origin
|
||||
|
||||
When plugins are installed in a multi-master setups it can be useful to know
|
||||
the Gerrit `instanceId` of the server that has generated an Event.
|
||||
|
||||
E.g. A plugin that sends an instance message for every comment on a change may
|
||||
want to react only if the event is generated on the local Gerrit master, for
|
||||
avoiding duplicating the notifications.
|
||||
|
||||
If link:config-gerrit.html[instanceId] is set, each Event will contain its
|
||||
origin in the `instanceId` field.
|
||||
|
||||
Here and example of ref-updated JSON event payload with `instanceId`:
|
||||
|
||||
[source,json]
|
||||
---
|
||||
{
|
||||
"submitter": {
|
||||
"name": "Administrator",
|
||||
"email": "admin@example.com",
|
||||
"username": "admin"
|
||||
},
|
||||
"refUpdate": {
|
||||
"oldRev": "a69fc95c7aad5ad41c618d31548b8af835d2959a",
|
||||
"newRev": "31da6556d638a74e5370b62f83e8007f94abb7c6",
|
||||
"refName": "refs/changes/01/1/meta",
|
||||
"project": "test"
|
||||
},
|
||||
"type": "ref-updated",
|
||||
"eventCreatedOn": 1588849085,
|
||||
"instanceId": "instance1"
|
||||
}
|
||||
---
|
||||
|
||||
[[capabilities]]
|
||||
== Plugin Owned Capabilities
|
||||
|
@@ -40,7 +40,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
|
||||
|
||||
public class EventRecorder {
|
||||
private final RegistrationHandle eventListenerRegistration;
|
||||
private final ListMultimap<String, RefEvent> recordedEvents;
|
||||
private final ListMultimap<String, Event> recordedEvents;
|
||||
|
||||
@Singleton
|
||||
public static class Factory {
|
||||
@@ -79,6 +79,8 @@ public class EventRecorder {
|
||||
refEventKey(
|
||||
event.getType(), event.getProjectNameKey().get(), event.getRefName());
|
||||
recordedEvents.put(key, event);
|
||||
} else {
|
||||
recordedEvents.put(e.type, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +160,17 @@ public class EventRecorder {
|
||||
return events;
|
||||
}
|
||||
|
||||
public ImmutableList<Event> getGenericEvents(String type, int expectedSize) {
|
||||
if (expectedSize == 0) {
|
||||
assertThat(recordedEvents).doesNotContainKey(type);
|
||||
return ImmutableList.of();
|
||||
}
|
||||
assertThat(recordedEvents).containsKey(type);
|
||||
ImmutableList<Event> events = FluentIterable.from(recordedEvents.get(type)).toList();
|
||||
assertThat(events).hasSize(expectedSize);
|
||||
return events;
|
||||
}
|
||||
|
||||
public void assertNoRefUpdatedEvents(String project, String branch) throws Exception {
|
||||
getRefUpdatedEvents(project, branch, 0);
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ import com.google.gerrit.server.util.time.TimeUtil;
|
||||
public abstract class Event {
|
||||
public final String type;
|
||||
public long eventCreatedOn = TimeUtil.nowMs() / 1000L;
|
||||
public String instanceId;
|
||||
|
||||
protected Event(String type) {
|
||||
this.type = type;
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.events;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.entities.BranchNameKey;
|
||||
import com.google.gerrit.entities.Change;
|
||||
import com.google.gerrit.entities.PatchSet;
|
||||
@@ -23,6 +24,7 @@ import com.google.gerrit.extensions.registration.DynamicItem;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.lifecycle.LifecycleModule;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.config.GerritInstanceId;
|
||||
import com.google.gerrit.server.notedb.ChangeNotes;
|
||||
import com.google.gerrit.server.permissions.ChangePermission;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
@@ -65,18 +67,22 @@ public class EventBroker implements EventDispatcher {
|
||||
|
||||
protected final ChangeNotes.Factory notesFactory;
|
||||
|
||||
protected final String gerritInstanceId;
|
||||
|
||||
@Inject
|
||||
public EventBroker(
|
||||
PluginSetContext<UserScopedEventListener> listeners,
|
||||
PluginSetContext<EventListener> unrestrictedListeners,
|
||||
PermissionBackend permissionBackend,
|
||||
ProjectCache projectCache,
|
||||
ChangeNotes.Factory notesFactory) {
|
||||
ChangeNotes.Factory notesFactory,
|
||||
@Nullable @GerritInstanceId String gerritInstanceId) {
|
||||
this.listeners = listeners;
|
||||
this.unrestrictedListeners = unrestrictedListeners;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.projectCache = projectCache;
|
||||
this.notesFactory = notesFactory;
|
||||
this.gerritInstanceId = gerritInstanceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,6 +111,7 @@ public class EventBroker implements EventDispatcher {
|
||||
}
|
||||
|
||||
protected void fireEvent(Change change, ChangeEvent event) throws PermissionBackendException {
|
||||
setInstanceId(event);
|
||||
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
|
||||
CurrentUser user = c.call(UserScopedEventListener::getUser);
|
||||
if (isVisibleTo(change, user)) {
|
||||
@@ -115,7 +122,9 @@ public class EventBroker implements EventDispatcher {
|
||||
}
|
||||
|
||||
protected void fireEvent(Project.NameKey project, ProjectEvent event) {
|
||||
setInstanceId(event);
|
||||
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
|
||||
|
||||
CurrentUser user = c.call(UserScopedEventListener::getUser);
|
||||
if (isVisibleTo(project, user)) {
|
||||
c.run(l -> l.onEvent(event));
|
||||
@@ -126,6 +135,7 @@ public class EventBroker implements EventDispatcher {
|
||||
|
||||
protected void fireEvent(BranchNameKey branchName, RefEvent event)
|
||||
throws PermissionBackendException {
|
||||
setInstanceId(event);
|
||||
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
|
||||
CurrentUser user = c.call(UserScopedEventListener::getUser);
|
||||
if (isVisibleTo(branchName, user)) {
|
||||
@@ -136,6 +146,7 @@ public class EventBroker implements EventDispatcher {
|
||||
}
|
||||
|
||||
protected void fireEvent(Event event) throws PermissionBackendException {
|
||||
setInstanceId(event);
|
||||
for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
|
||||
CurrentUser user = c.call(UserScopedEventListener::getUser);
|
||||
if (isVisibleTo(event, user)) {
|
||||
@@ -145,6 +156,10 @@ public class EventBroker implements EventDispatcher {
|
||||
fireEventForUnrestrictedListeners(event);
|
||||
}
|
||||
|
||||
protected void setInstanceId(Event event) {
|
||||
event.instanceId = gerritInstanceId;
|
||||
}
|
||||
|
||||
protected boolean isVisibleTo(Project.NameKey project, CurrentUser user) {
|
||||
try {
|
||||
Optional<ProjectState> state = projectCache.get(project);
|
||||
|
@@ -0,0 +1,85 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.acceptance.server.event;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.NoHttpd;
|
||||
import com.google.gerrit.acceptance.config.GerritConfig;
|
||||
import com.google.gerrit.extensions.registration.DynamicItem;
|
||||
import com.google.gerrit.server.events.Event;
|
||||
import com.google.gerrit.server.events.EventDispatcher;
|
||||
import com.google.gerrit.server.events.EventTypes;
|
||||
import com.google.inject.Inject;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@NoHttpd
|
||||
public class InstanceIdInEventIT extends AbstractDaemonTest {
|
||||
|
||||
public static class TestDispatcher {
|
||||
private final DynamicItem<EventDispatcher> eventDispatcher;
|
||||
|
||||
@Inject
|
||||
TestDispatcher(DynamicItem<EventDispatcher> eventDispatcher) {
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
}
|
||||
|
||||
public void postEvent(TestEvent event) {
|
||||
try {
|
||||
eventDispatcher.get().postEvent(event);
|
||||
} catch (Exception e) {
|
||||
fail("Exception raised when posting Event " + e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestEvent extends Event {
|
||||
private static final String TYPE = "test-event-instance-id";
|
||||
|
||||
public TestEvent() {
|
||||
super(TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject private DynamicItem<EventDispatcher> eventDispatcher;
|
||||
TestDispatcher testDispatcher;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testDispatcher = new TestDispatcher(eventDispatcher);
|
||||
EventTypes.register(TestEvent.TYPE, TestEvent.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@GerritConfig(name = "gerrit.instanceId", value = "testInstanceId")
|
||||
public void shouldSetInstanceIdWhenDefined() {
|
||||
testDispatcher.postEvent(new TestEvent());
|
||||
|
||||
ImmutableList<Event> events = eventRecorder.getGenericEvents(TestEvent.TYPE, 1);
|
||||
assertThat(events.get(0).instanceId).isEqualTo("testInstanceId");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotSetInstanceIdWhenNotDefined() {
|
||||
testDispatcher.postEvent(new TestEvent());
|
||||
|
||||
ImmutableList<Event> events = eventRecorder.getGenericEvents(TestEvent.TYPE, 1);
|
||||
assertThat(events.get(0).instanceId).isNull();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user