blob: a14c26ff246f210f5e96d16006f1f9f432102a50 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.management.internal.api;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import com.google.common.collect.Sets;
import org.junit.Before;
import org.junit.Test;
import org.apache.geode.cache.configuration.CacheConfig;
import org.apache.geode.cache.configuration.GatewayReceiverConfig;
import org.apache.geode.cache.configuration.RegionConfig;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.config.JAXBService;
import org.apache.geode.management.api.ClusterManagementException;
import org.apache.geode.management.api.ClusterManagementOperation;
import org.apache.geode.management.api.ClusterManagementOperationResult;
import org.apache.geode.management.api.ClusterManagementRealizationResult;
import org.apache.geode.management.api.ClusterManagementResult;
import org.apache.geode.management.api.RealizationResult;
import org.apache.geode.management.configuration.Index;
import org.apache.geode.management.configuration.Member;
import org.apache.geode.management.configuration.Region;
import org.apache.geode.management.configuration.RegionType;
import org.apache.geode.management.internal.CacheElementOperation;
import org.apache.geode.management.internal.ClusterManagementOperationStatusResult;
import org.apache.geode.management.internal.configuration.mutators.ConfigurationManager;
import org.apache.geode.management.internal.configuration.mutators.GatewayReceiverConfigManager;
import org.apache.geode.management.internal.configuration.mutators.RegionConfigManager;
import org.apache.geode.management.internal.configuration.validators.CommonConfigurationValidator;
import org.apache.geode.management.internal.configuration.validators.ConfigurationValidator;
import org.apache.geode.management.internal.configuration.validators.MemberValidator;
import org.apache.geode.management.internal.configuration.validators.RegionConfigValidator;
import org.apache.geode.management.internal.operation.OperationHistoryManager.OperationInstance;
import org.apache.geode.management.internal.operation.OperationManager;
import org.apache.geode.management.runtime.OperationResult;
import org.apache.geode.management.runtime.RuntimeInfo;
import org.apache.geode.management.runtime.RuntimeRegionInfo;
public class LocatorClusterManagementServiceTest {
private LocatorClusterManagementService service;
private InternalCache cache;
private InternalConfigurationPersistenceService persistenceService;
private Region regionConfig;
private ClusterManagementResult result;
private Map<Class, ConfigurationValidator> validators = new HashMap<>();
private Map<Class, ConfigurationManager> managers = new HashMap<>();
private OperationManager executorManager;
private ConfigurationValidator<Region> regionValidator;
private CommonConfigurationValidator cacheElementValidator;
private ConfigurationManager<Region> regionManager;
private MemberValidator memberValidator;
@Before
public void before() throws Exception {
cache = mock(InternalCache.class);
regionValidator = mock(RegionConfigValidator.class);
doCallRealMethod().when(regionValidator).validate(eq(CacheElementOperation.DELETE), any());
regionManager = spy(RegionConfigManager.class);
cacheElementValidator = spy(CommonConfigurationValidator.class);
validators.put(Region.class, regionValidator);
managers.put(Region.class, regionManager);
managers.put(GatewayReceiverConfig.class, new GatewayReceiverConfigManager());
memberValidator = mock(MemberValidator.class);
persistenceService = spy(new InternalConfigurationPersistenceService(
JAXBService.create(CacheConfig.class)));
Set<String> groups = new HashSet<>();
groups.add("cluster");
doReturn(groups).when(persistenceService).getGroups();
doReturn(new CacheConfig()).when(persistenceService).getCacheConfig(any(), anyBoolean());
doReturn(true).when(persistenceService).lockSharedConfiguration();
doNothing().when(persistenceService).unlockSharedConfiguration();
executorManager = mock(OperationManager.class);
service =
spy(new LocatorClusterManagementService(persistenceService, managers, validators,
memberValidator, cacheElementValidator, executorManager));
regionConfig = new Region();
regionConfig.setName("region1");
}
@Test
public void create_persistenceIsNull() {
service = new LocatorClusterManagementService(cache, null);
assertThatThrownBy(() -> service.create(regionConfig))
.hasMessageContaining("Cluster configuration service needs to be enabled");
}
@Test
public void create_validatorIsCalledCorrectly() {
doReturn(Collections.emptySet()).when(memberValidator).findServers(anyString());
doNothing().when(persistenceService).updateCacheConfig(any(), any());
service.create(regionConfig);
verify(cacheElementValidator).validate(CacheElementOperation.CREATE, regionConfig);
verify(regionValidator).validate(CacheElementOperation.CREATE, regionConfig);
verify(memberValidator).validateCreate(regionConfig, regionManager);
}
@Test
public void delete_validatorIsCalledCorrectly() {
doReturn(Collections.emptySet()).when(memberValidator).findServers(anyString());
doReturn(new String[] {"cluster"}).when(memberValidator).findGroupsWithThisElement(
regionConfig.getId(),
regionManager);
doNothing().when(persistenceService).updateCacheConfig(any(), any());
service.delete(regionConfig);
verify(cacheElementValidator).validate(CacheElementOperation.DELETE, regionConfig);
verify(regionValidator).validate(CacheElementOperation.DELETE, regionConfig);
verify(memberValidator).findGroupsWithThisElement(regionConfig.getId(), regionManager);
verify(memberValidator).findServers("cluster");
}
@Test
public void create_partialFailureOnMembers() {
List<RealizationResult> functionResults = new ArrayList<>();
functionResults.add(new RealizationResult().setMemberName("member1"));
functionResults.add(
new RealizationResult().setMemberName("member2").setSuccess(false).setMessage("failed"));
doReturn(functionResults).when(service).executeAndGetFunctionResult(any(), any(), any());
doReturn(Collections.singleton(mock(DistributedMember.class))).when(memberValidator)
.findServers();
when(persistenceService.getCacheConfig("cluster", true)).thenReturn(new CacheConfig());
regionConfig.setName("test");
assertThatThrownBy(() -> service.create(regionConfig))
.hasMessageContaining("Failed to create on all members");
}
@Test
public void create_succeedsOnAllMembers() {
List<RealizationResult> functionResults = new ArrayList<>();
functionResults.add(new RealizationResult().setMemberName("member1"));
functionResults.add(new RealizationResult().setMemberName("member2"));
doReturn(functionResults).when(service).executeAndGetFunctionResult(any(), any(), any());
doReturn(Collections.singleton(mock(DistributedMember.class))).when(memberValidator)
.findServers();
CacheConfig cacheConfig = new CacheConfig();
when(persistenceService.getCacheConfig("cluster", true)).thenReturn(cacheConfig);
doReturn(null).when(persistenceService).getConfiguration(any());
org.apache.geode.cache.Region mockRegion = mock(org.apache.geode.cache.Region.class);
doReturn(mockRegion).when(persistenceService).getConfigurationRegion();
regionConfig.setName("test");
regionConfig.setType(RegionType.REPLICATE);
result = service.create(regionConfig);
assertThat(result.isSuccessful()).isTrue();
assertThat(cacheConfig.getRegions()).hasSize(1);
}
@Test
public void create_non_supportedConfigObject() {
Member config = new Member();
assertThatThrownBy(() -> service.create(config)).isInstanceOf(ClusterManagementException.class)
.hasMessageContaining("ILLEGAL_ARGUMENT: Member is not supported.");
}
@Test
public void list_oneGroup() {
regionConfig.setGroup("cluster");
doReturn(Sets.newHashSet("cluster", "group1")).when(persistenceService).getGroups();
service.list(regionConfig);
verify(persistenceService).getCacheConfig("cluster", true);
verify(regionManager).list(any(), any());
}
@Test
public void list_oneGroupCaseInsensitive() {
regionConfig.setGroup("CLUSTER");
doReturn(Sets.newHashSet("cluster", "group1")).when(persistenceService).getGroups();
service.list(regionConfig);
verify(persistenceService).getCacheConfig("cluster", true);
verify(regionManager).list(any(), any());
}
@Test
public void list_aRegionInMultipleGroups() {
doReturn(Sets.newHashSet("group1", "group2")).when(persistenceService).getGroups();
Region region1group2 = new Region();
region1group2.setName("region1");
region1group2.setType(RegionType.REPLICATE);
Region region1group1 = new Region();
region1group1.setName("region1");
region1group1.setType(RegionType.REPLICATE);
List group2Regions = Arrays.asList(region1group2);
List group1Regions = Arrays.asList(region1group1);
CacheConfig mockCacheConfigGroup2 = mock(CacheConfig.class);
CacheConfig mockCacheConfigGroup1 = mock(CacheConfig.class);
doReturn(mockCacheConfigGroup2).when(persistenceService).getCacheConfig(eq("group2"),
anyBoolean());
doReturn(mockCacheConfigGroup1).when(persistenceService).getCacheConfig(eq("group1"),
anyBoolean());
doReturn(group2Regions).when(regionManager).list(any(), same(mockCacheConfigGroup2));
doReturn(group1Regions).when(regionManager).list(any(), same(mockCacheConfigGroup1));
List<Region> results = service.list(new Region()).getConfigResult();
assertThat(results).hasSize(2);
Region result1 = results.get(0);
assertThat(result1.getName()).isEqualTo("region1");
Region result2 = results.get(1);
assertThat(result2.getName()).isEqualTo("region1");
assertThat(results).extracting(Region::getGroup).containsExactlyInAnyOrder("group1", "group2");
}
@Test
public void delete_unknownRegionFails() {
Region config = new Region();
config.setName("unknown");
doReturn(new String[] {}).when(memberValidator).findGroupsWithThisElement(any(), any());
assertThatThrownBy(() -> service.delete(config))
.isInstanceOf(ClusterManagementException.class)
.hasMessage("ENTITY_NOT_FOUND: Region 'unknown' does not exist.");
}
@Test
public void delete_usingGroupFails() {
Region config = new Region();
config.setName("test");
config.setGroup("group1");
assertThatThrownBy(() -> service.delete(config))
.isInstanceOf(ClusterManagementException.class)
.hasMessage("ILLEGAL_ARGUMENT: Group is an invalid option when deleting region.");
}
@Test
public void delete_partialFailureOnMembers() {
List<RealizationResult> functionResults = new ArrayList<>();
functionResults.add(new RealizationResult().setMemberName("member1"));
functionResults.add(
new RealizationResult().setMemberName("member2").setSuccess(false).setMessage("failed"));
doReturn(functionResults).when(service).executeAndGetFunctionResult(any(), any(), any());
doReturn(new String[] {"cluster"}).when(memberValidator).findGroupsWithThisElement(any(),
any());
doReturn(Collections.singleton(mock(DistributedMember.class))).when(memberValidator)
.findServers();
CacheConfig config = new CacheConfig();
RegionConfig regionConfig = new RegionConfig();
regionConfig.setName("test");
config.getRegions().add(regionConfig);
doReturn(config).when(persistenceService).getCacheConfig(eq("cluster"), anyBoolean());
Region region = new Region();
region.setName("test");
result = service.delete(region);
assertThat(result.isSuccessful()).isFalse();
assertThat(result.getStatusMessage())
.contains("Failed to delete on all members.");
assertThat(config.getRegions()).hasSize(1);
}
@Test
public void delete_succeedsOnAllMembers() {
List<RealizationResult> functionResults = new ArrayList<>();
functionResults.add(new RealizationResult().setMemberName("member1"));
functionResults.add(new RealizationResult().setMemberName("member2"));
doReturn(functionResults).when(service).executeAndGetFunctionResult(any(), any(), any());
doReturn(new String[] {"cluster"}).when(memberValidator).findGroupsWithThisElement(any(),
any());
doReturn(Collections.singleton(mock(DistributedMember.class))).when(memberValidator)
.findServers();
CacheConfig config = new CacheConfig();
RegionConfig regionConfig = new RegionConfig();
regionConfig.setName("test");
config.getRegions().add(regionConfig);
doReturn(config).when(persistenceService).getCacheConfig(eq("cluster"), anyBoolean());
doReturn(null).when(persistenceService).getConfiguration(any());
org.apache.geode.cache.Region mockRegion = mock(org.apache.geode.cache.Region.class);
doReturn(mockRegion).when(persistenceService).getConfigurationRegion();
Region region = new Region();
region.setName("test");
result = service.delete(region);
assertThat(result.isSuccessful()).isTrue();
assertThat(config.getRegions()).isEmpty();
}
@Test
public void deleteWithNoMember() {
// region exists in cluster configuration
doReturn(new String[] {"cluster"}).when(memberValidator).findGroupsWithThisElement(any(),
any());
// no members found in any group
doReturn(Collections.emptySet()).when(memberValidator).findServers();
doReturn(null).when(persistenceService).getConfiguration(any());
org.apache.geode.cache.Region mockRegion = mock(org.apache.geode.cache.Region.class);
doReturn(mockRegion).when(persistenceService).getConfigurationRegion();
ClusterManagementRealizationResult result = service.delete(regionConfig);
verify(regionManager).delete(eq(regionConfig), any());
assertThat(result.isSuccessful()).isTrue();
assertThat(result.getMemberStatuses()).hasSize(0);
assertThat(result.getStatusMessage())
.contains("Successfully removed configuration for [cluster]");
}
@Test
public void startOperation() {
final String URI = "/test/uri";
ClusterManagementOperation<OperationResult> operation = mock(ClusterManagementOperation.class);
when(operation.getEndpoint()).thenReturn(URI);
when(executorManager.submit(any()))
.thenReturn(new OperationInstance<>(null, "42", operation, new Date()));
ClusterManagementOperationResult<?> result = service.start(operation);
assertThat(result.getStatusCode()).isEqualTo(ClusterManagementResult.StatusCode.ACCEPTED);
assertThat(result.getStatusMessage()).contains("Operation started");
}
@Test
public void checkStatusForNotFound() {
assertThatThrownBy(() -> service.checkStatus("123"))
.isInstanceOf(ClusterManagementException.class);
}
@Test
public void checkStatus() {
CompletableFuture future = mock(CompletableFuture.class);
OperationInstance operationInstance = mock(OperationInstance.class);
when(operationInstance.getFutureResult()).thenReturn(future);
when(operationInstance.getFutureOperationEnded()).thenReturn(future);
when(executorManager.getOperationInstance(any())).thenReturn(operationInstance);
when(future.isDone()).thenReturn(false);
ClusterManagementOperationStatusResult<OperationResult> result = service.checkStatus("456");
assertThat(result.getStatusCode()).isEqualTo(ClusterManagementResult.StatusCode.IN_PROGRESS);
assertThat(result.getResult()).isNull();
when(future.isDone()).thenReturn(true);
result = service.checkStatus("456");
assertThat(result.getStatusCode()).isEqualTo(ClusterManagementResult.StatusCode.OK);
}
@Test
public void getRuntimeClass() throws Exception {
assertThat(service.getRuntimeClass(Region.class)).isEqualTo(RuntimeRegionInfo.class);
assertThat(service.hasRuntimeInfo(Region.class)).isTrue();
assertThat(service.getRuntimeClass(Index.class)).isEqualTo(RuntimeInfo.class);
assertThat(service.hasRuntimeInfo(Index.class)).isFalse();
}
}