mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-04 00:02:37 +01:00
Merge pull request #1127 from greenqloud/pr-eventbus-uuids-4.6
Fix event UUIDS missing on event busSame as pull request #1111, but this time on the 4.6 branch for forward merging in accordance with [the wiki](https://cwiki.apache.org/confluence/display/CLOUDSTACK/Release+principles+for+Apache+CloudStack+4.6+and+up#ReleaseprinciplesforApacheCloudStack4.6andup-HowtomergeaPullRequest?). The fixing of CLOUDSTACK-8816 introduced a regression that removed the first class entities in the event bus description property. This is because everything was changed to use the Class as a key... Everything but the populateFirstClassEntities method in ActionEventUtils. This commit tries to load the class key instead of the String key, which re-enables the populateFirstClassEntities method. Likely this was not caught because of the trace exception handling... maybe some better logging/unit tests would be good for this PR. * pr/1127: Fix event UUIDS missing on event bus Signed-off-by: Daan Hoogland <daan@onecht.net>
This commit is contained in:
commit
9960593893
@ -293,8 +293,7 @@ public class ActionEventUtils {
|
||||
|
||||
for(Map.Entry<Object, Object> entry : contextMap.entrySet()){
|
||||
try{
|
||||
Object key = entry.getKey();
|
||||
Class<?> clz = Class.forName((String)key);
|
||||
Class<?> clz = (Class<?>)entry.getKey();
|
||||
if(clz != null && Identity.class.isAssignableFrom(clz)){
|
||||
String uuid = getEntityUuid(clz, entry.getValue());
|
||||
eventDescription.put(ReflectUtil.getEntityName(clz), uuid);
|
||||
|
||||
213
server/test/com/cloud/event/ActionEventUtilsTest.java
Normal file
213
server/test/com/cloud/event/ActionEventUtilsTest.java
Normal file
@ -0,0 +1,213 @@
|
||||
package com.cloud.event;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.framework.events.Event;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.events.EventBus;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.event.dao.EventDao;
|
||||
import com.cloud.network.IpAddress;
|
||||
import com.cloud.projects.dao.ProjectDao;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest(ComponentContext.class)
|
||||
public class ActionEventUtilsTest {
|
||||
//Predictable constants used throughout this test.
|
||||
public static final long EVENT_ID = 1;
|
||||
public static final long USER_ID = 1;
|
||||
public static final long ACCOUNT_ID = 1;
|
||||
|
||||
//Keep track of the static field values between tests.
|
||||
//A horrid abuse of reflection required due to the strange
|
||||
//static/inject pattern found in ActionEventUtils.
|
||||
protected Map<String, Object> staticFieldValues = new HashMap<>();
|
||||
|
||||
//List of events published on the event bus. Handled via a mocked method.
|
||||
//Cleared on every run.
|
||||
protected List<Event> publishedEvents = new ArrayList<>();
|
||||
|
||||
//Mock fields. These are injected into ActionEventUtils by the setup() method.
|
||||
@Mock
|
||||
protected EventDao eventDao;
|
||||
|
||||
@Mock
|
||||
protected AccountDao accountDao;
|
||||
|
||||
@Mock
|
||||
protected UserDao userDao;
|
||||
|
||||
@Mock
|
||||
protected ProjectDao projectDao;
|
||||
|
||||
@Mock
|
||||
protected EntityManager entityMgr;
|
||||
|
||||
@Mock
|
||||
protected ConfigurationDao configDao;
|
||||
|
||||
@Mock
|
||||
protected EventBus eventBus;
|
||||
|
||||
/**
|
||||
* This setup method injects the mocked beans into the ActionEventUtils class.
|
||||
* Because ActionEventUtils has static methods, we must also remember these fields
|
||||
* and restore them later, as otherwise strange behavior can result in other unit
|
||||
* tests due to the way the JVM handles static fields.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
publishedEvents = new ArrayList<>();
|
||||
staticFieldValues = new HashMap<>();
|
||||
setupCommonMocks();
|
||||
|
||||
ActionEventUtils utils = new ActionEventUtils();
|
||||
|
||||
for (Field field : ActionEventUtils.class.getDeclaredFields()) {
|
||||
if (field.getAnnotation(Inject.class) != null) {
|
||||
field.setAccessible(true);
|
||||
|
||||
try {
|
||||
//Inject the mocked field from this class into the ActionEventUtils
|
||||
//and keep track of its original value.
|
||||
Field mockField = this.getClass().getDeclaredField(field.getName());
|
||||
field.set(utils, mockField.get(this));
|
||||
Field staticField = ActionEventUtils.class.getDeclaredField("s_" + field.getName());
|
||||
staticFieldValues.put(field.getName(), staticField.get(null));
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore missing fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
utils.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the common specialized mocks that are needed to make the ActionEventUtils class behave in a
|
||||
* predictable way. This method only mocks things that are common to all the tests. Each individual test
|
||||
* also mocks some other methods (e.g. find user/account) by itself.
|
||||
*/
|
||||
public void setupCommonMocks() throws Exception {
|
||||
//Some basic mocks.
|
||||
Mockito.when(configDao.getValue(Config.PublishActionEvent.key())).thenReturn("true");
|
||||
PowerMockito.mockStatic(ComponentContext.class);
|
||||
Mockito.when(ComponentContext.getComponent(EventBus.class)).thenReturn(eventBus);
|
||||
|
||||
//Needed for persist to actually set an ID that can be returned from the ActionEventUtils
|
||||
//methods.
|
||||
Mockito.when(eventDao.persist(Mockito.any(EventVO.class))).thenAnswer(new Answer<EventVO>() {
|
||||
@Override
|
||||
public EventVO answer(InvocationOnMock invocation) throws Throwable {
|
||||
EventVO event = (EventVO)invocation.getArguments()[0];
|
||||
Field id = event.getClass().getDeclaredField("id");
|
||||
id.setAccessible(true);
|
||||
id.set(event, EVENT_ID);
|
||||
return event;
|
||||
}
|
||||
});
|
||||
|
||||
//Needed to record events published on the bus.
|
||||
Mockito.doAnswer(new Answer<Void>() {
|
||||
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
Event event = (Event)invocation.getArguments()[0];
|
||||
publishedEvents.add(event);
|
||||
return null;
|
||||
}
|
||||
|
||||
}).when(eventBus).publish(Mockito.any(Event.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* This teardown method restores the ActionEventUtils static field values to their original values,
|
||||
* keeping the mocked mess inside this class.
|
||||
*/
|
||||
@After
|
||||
public void teardown() {
|
||||
ActionEventUtils utils = new ActionEventUtils();
|
||||
|
||||
for (String fieldName : staticFieldValues.keySet()) {
|
||||
try {
|
||||
Field field = ActionEventUtils.class.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(utils, staticFieldValues.get(fieldName));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
utils.init();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulateFirstClassEntities() {
|
||||
AccountVO account = new AccountVO("testaccount", 1L, "networkdomain", (short) 0, "uuid");
|
||||
account.setId(ACCOUNT_ID);
|
||||
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone",
|
||||
UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||
|
||||
Mockito.when(accountDao.findById(ACCOUNT_ID)).thenReturn(account);
|
||||
Mockito.when(userDao.findById(USER_ID)).thenReturn(user);
|
||||
|
||||
CallContext.register(user, account);
|
||||
|
||||
//Inject some entity UUIDs into the call context
|
||||
String instanceUuid = UUID.randomUUID().toString();
|
||||
String ipUuid = UUID.randomUUID().toString();
|
||||
CallContext.current().putContextParameter(VirtualMachine.class, instanceUuid);
|
||||
CallContext.current().putContextParameter(IpAddress.class, ipUuid);
|
||||
|
||||
ActionEventUtils.onActionEvent(USER_ID, ACCOUNT_ID, account.getDomainId(), "StaticNat", "Test event");
|
||||
|
||||
//Assertions
|
||||
Assert.assertNotEquals(publishedEvents.size(), 0);
|
||||
Assert.assertEquals(publishedEvents.size(), 1);
|
||||
|
||||
Event event = publishedEvents.get(0);
|
||||
Assert.assertNotNull(event.getDescription());
|
||||
|
||||
JsonObject json = new JsonParser().parse(event.getDescription()).getAsJsonObject();
|
||||
|
||||
Assert.assertTrue(json.has("VirtualMachine"));
|
||||
Assert.assertTrue(json.has("IpAddress"));
|
||||
Assert.assertEquals(json.get("VirtualMachine").getAsString(), instanceUuid);
|
||||
Assert.assertEquals(json.get("IpAddress").getAsString(), ipUuid);
|
||||
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user