/*
* 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.ignite.internal.processors.igfs;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.FileSystemConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.igfs.IgfsGroupDataBlocksKeyMapper;
import org.apache.ignite.igfs.IgfsIpcEndpointConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.processors.cache.GridCacheDefaultAffinityKeyMapper;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
import static org.apache.ignite.igfs.IgfsMode.DUAL_ASYNC;
import static org.apache.ignite.igfs.IgfsMode.DUAL_SYNC;
import static org.apache.ignite.igfs.IgfsMode.PROXY;
/**
* Tests for node validation logic in {@link IgfsProcessor}.
* <p>
* Tests starting with "testLocal" are checking
* {@link IgfsUtils#validateLocalIgfsConfigurations(org.apache.ignite.configuration.IgniteConfiguration)}.
* <p>
* Tests starting with "testRemote" are checking {@link IgfsProcessor#checkIgfsOnRemoteNode(org.apache.ignite.cluster.ClusterNode)}.
*/
public class IgfsProcessorValidationSelfTest extends IgfsCommonAbstractTest {
/** IP finder. */
private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
/** Grid #1 config. */
private IgniteConfiguration g1Cfg;
/** First IGFS config in grid #1. */
private FileSystemConfiguration g1IgfsCfg1 = new FileSystemConfiguration();
/** Second IGFS config in grid#1. */
private FileSystemConfiguration g1IgfsCfg2 = new FileSystemConfiguration();
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
g1Cfg = getConfiguration("g1");
}
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
discoSpi.setIpFinder(IP_FINDER);
cfg.setDiscoverySpi(discoSpi);
g1IgfsCfg1.setName("g1IgfsCfg1");
g1IgfsCfg2.setName("g1IgfsCfg2");
cfg.setFileSystemConfiguration(g1IgfsCfg1, g1IgfsCfg2);
cfg.setLocalHost("127.0.0.1");
return cfg;
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
stopAllGrids();
}
/**
* Returns a new array that contains the concatenated contents of two arrays.
*
* @param first the first array of elements to concatenate.
* @param second the second array of elements to concatenate.
* @param cls Class of elements.
* @return Concatenated array.
*/
private <T> T[] concat(T[] first, T[] second, Class<?> cls) {
Collection<T> res = new ArrayList<>();
res.addAll(Arrays.asList(first));
res.addAll(Arrays.asList(second));
return res.toArray((T[]) Array.newInstance(cls, res.size()));
}
/**
* @throws Exception If failed.
*/
public void testLocalIfAffinityMapperIsWrongClass() throws Exception {
for (FileSystemConfiguration igfsCfg : g1Cfg.getFileSystemConfiguration()) {
igfsCfg.setDataCacheConfiguration(dataCache(1024));
igfsCfg.setMetaCacheConfiguration(metaCache());
igfsCfg.getMetaCacheConfiguration().setAffinityMapper(new GridCacheDefaultAffinityKeyMapper());
igfsCfg.getDataCacheConfiguration().setAffinityMapper(new GridCacheDefaultAffinityKeyMapper());
}
checkGridStartFails(g1Cfg, "Invalid IGFS data cache configuration (key affinity mapper class should be", true);
}
/**
* @throws Exception If failed.
*/
public void testLocalIfIgfsConfigsHaveDuplicatedNames() throws Exception {
String igfsCfgName = "igfs-cfg";
g1IgfsCfg1.setName(igfsCfgName);
g1IgfsCfg2.setName(igfsCfgName);
checkGridStartFails(g1Cfg, "Duplicate IGFS name found (check configuration and assign unique name", true);
}
/**
* @throws Exception If failed.
*/
public void testLocalIfQueryIndexingEnabledForDataCache() throws Exception {
g1IgfsCfg1.setDataCacheConfiguration(dataCache(1024));
g1IgfsCfg1.getDataCacheConfiguration().setIndexedTypes(Integer.class, String.class);
checkGridStartFails(g1Cfg, "IGFS data cache cannot start with enabled query indexing", true);
}
/**
* @throws Exception If failed.
*/
public void testLocalIfQueryIndexingEnabledForMetaCache() throws Exception {
g1IgfsCfg1.setMetaCacheConfiguration(metaCache());
g1IgfsCfg1.getMetaCacheConfiguration().setIndexedTypes(Integer.class, String.class);
checkGridStartFails(g1Cfg, "IGFS metadata cache cannot start with enabled query indexing", true);
}
/**
* @throws Exception If failed.
*/
@SuppressWarnings("NullableProblems")
public void testLocalNullIgfsNameIsNotSupported() throws Exception {
try {
g1IgfsCfg1.setName(null);
fail("IGFS name cannot be null");
}
catch (IllegalArgumentException e) {
// No-op.
}
ArrayList<FileSystemConfiguration> fsCfgs = new ArrayList<>(Arrays.asList(g1Cfg.getFileSystemConfiguration()));
fsCfgs.add(new FileSystemConfiguration()); // IGFS doesn't have default name (name == null).
g1Cfg.setFileSystemConfiguration(fsCfgs.toArray(new FileSystemConfiguration[fsCfgs.size()]));
checkGridStartFails(g1Cfg, "IGFS name cannot be null", true);
}
/**
* @throws Exception If failed.
*/
public void testLocalIfNonPrimaryModeAndHadoopFileSystemUriIsNull() throws Exception {
g1IgfsCfg2.setDefaultMode(PROXY);
checkGridStartFails(g1Cfg, "secondaryFileSystem cannot be null when mode is not PRIMARY", true);
}
/**
* @throws Exception If failed.
*/
public void testRemoteIfDataBlockSizeDiffers() throws Exception {
IgniteConfiguration g2Cfg = getConfiguration("g2");
FileSystemConfiguration g2IgfsCfg1 = new FileSystemConfiguration(g1IgfsCfg1);
g2IgfsCfg1.setBlockSize(g2IgfsCfg1.getBlockSize() + 100);
g2Cfg.setFileSystemConfiguration(g2IgfsCfg1, g1IgfsCfg2);
G.start(g1Cfg);
checkGridStartFails(g2Cfg, "Data block size should be the same on all nodes in grid for IGFS", false);
}
/**
* @throws Exception If failed.
*/
public void testRemoteIfAffinityMapperGroupSizeDiffers() throws Exception {
IgniteConfiguration g2Cfg = getConfiguration("g2");
G.start(g1Cfg);
for (FileSystemConfiguration igfsCfg : g2Cfg.getFileSystemConfiguration())
igfsCfg.setDataCacheConfiguration(dataCache(1000));
checkGridStartFails(g2Cfg, "Affinity mapper group size should be the same on all nodes in grid for IGFS",
false);
}
/**
* @throws Exception If failed.
*/
public void testRemoteIfDefaultModeDiffers() throws Exception {
IgniteConfiguration g2Cfg = getConfiguration("g2");
FileSystemConfiguration g2IgfsCfg1 = new FileSystemConfiguration(g1IgfsCfg1);
FileSystemConfiguration g2IgfsCfg2 = new FileSystemConfiguration(g1IgfsCfg2);
g1IgfsCfg1.setDefaultMode(DUAL_ASYNC);
g1IgfsCfg2.setDefaultMode(DUAL_ASYNC);
g2IgfsCfg1.setDefaultMode(DUAL_SYNC);
g2IgfsCfg2.setDefaultMode(DUAL_SYNC);
g2Cfg.setFileSystemConfiguration(g2IgfsCfg1, g2IgfsCfg2);
G.start(g1Cfg);
checkGridStartFails(g2Cfg, "Default mode should be the same on all nodes in grid for IGFS", false);
}
/**
* @throws Exception If failed.
*/
public void testRemoteIfPathModeDiffers() throws Exception {
IgniteConfiguration g2Cfg = getConfiguration("g2");
FileSystemConfiguration g2IgfsCfg1 = new FileSystemConfiguration(g1IgfsCfg1);
FileSystemConfiguration g2IgfsCfg2 = new FileSystemConfiguration(g1IgfsCfg2);
g2IgfsCfg1.setPathModes(Collections.singletonMap("/somePath", DUAL_SYNC));
g2IgfsCfg2.setPathModes(Collections.singletonMap("/somePath", DUAL_SYNC));
g2Cfg.setFileSystemConfiguration(g2IgfsCfg1, g2IgfsCfg2);
G.start(g1Cfg);
checkGridStartFails(g2Cfg, "Path modes should be the same on all nodes in grid for IGFS", false);
}
/**
* @throws Exception If failed.
*/
public void testZeroEndpointTcpPort() throws Exception {
checkInvalidPort(0);
}
/**
* @throws Exception If failed.
*/
public void testNegativeEndpointTcpPort() throws Exception {
checkInvalidPort(-1);
}
/**
* @throws Exception If failed.
*/
public void testTooBigEndpointTcpPort() throws Exception {
checkInvalidPort(65536);
}
/**
* @throws Exception If failed.
*/
public void testPreConfiguredCache() throws Exception {
FileSystemConfiguration igfsCfg1 = new FileSystemConfiguration(g1IgfsCfg1);
igfsCfg1.setName("igfs");
g1Cfg.setFileSystemConfiguration(igfsCfg1);
CacheConfiguration ccfgData = dataCache(1024);
ccfgData.setRebalanceTimeout(10001);
CacheConfiguration ccfgMeta = metaCache();
ccfgMeta.setRebalanceTimeout(10002);
igfsCfg1.setDataCacheConfiguration(ccfgData);
igfsCfg1.setMetaCacheConfiguration(ccfgMeta);
IgniteEx g = (IgniteEx)G.start(g1Cfg);
assertEquals(10001,
g.cachex(g.igfsx("igfs").configuration().getDataCacheConfiguration().getName())
.configuration().getRebalanceTimeout());
assertEquals(10002,
g.cachex(g.igfsx("igfs").configuration().getMetaCacheConfiguration().getName())
.configuration().getRebalanceTimeout());
}
/**
* Check invalid port handling.
*
* @param port Port.
* @throws Exception If failed.
*/
private void checkInvalidPort(int port) throws Exception {
final String failMsg = "IGFS endpoint TCP port is out of range";
final String igfsCfgName = "igfs-cfg";
final IgfsIpcEndpointConfiguration igfsEndpointCfg = new IgfsIpcEndpointConfiguration();
igfsEndpointCfg.setPort(port);
g1IgfsCfg1.setName(igfsCfgName);
g1IgfsCfg1.setIpcEndpointConfiguration(igfsEndpointCfg);
checkGridStartFails(g1Cfg, failMsg, true);
}
/**
* @throws Exception If failed.
*/
public void testInvalidEndpointThreadCount() throws Exception {
final String failMsg = "IGFS endpoint thread count must be positive";
final String igfsCfgName = "igfs-cfg";
final IgfsIpcEndpointConfiguration igfsEndpointCfg = new IgfsIpcEndpointConfiguration();
igfsEndpointCfg.setThreadCount(0);
g1IgfsCfg1.setName(igfsCfgName);
g1IgfsCfg1.setIpcEndpointConfiguration(igfsEndpointCfg);
checkGridStartFails(g1Cfg, failMsg, true);
}
/**
* Checks that the given grid configuration will lead to {@link IgniteCheckedException} upon grid startup.
*
* @param cfg Grid configuration to check.
* @param excMsgSnippet Root cause (assertion) exception message snippet.
* @param testLoc {@code True} if checking is done for "testLocal" tests.
*/
private void checkGridStartFails(IgniteConfiguration cfg, CharSequence excMsgSnippet, boolean testLoc) {
assertNotNull(cfg);
assertNotNull(excMsgSnippet);
try {
G.start(cfg);
fail("No exception has been thrown.");
}
catch (IgniteException e) {
if (testLoc) {
if (e.getMessage().contains(excMsgSnippet)
|| ("Failed to start processor: GridProcessorAdapter []".equals(e.getMessage()) &&
(e.getCause().getMessage().contains(excMsgSnippet) ||
e.getCause().getCause().getMessage().contains(excMsgSnippet))))
return; // Expected exception.
}
else if (e.getMessage().contains(excMsgSnippet))
return; // Expected exception.
error("Caught unexpected exception.", e);
fail();
}
}
/**
* @param grpSize Group size to use in {@link org.apache.ignite.igfs.IgfsGroupDataBlocksKeyMapper}.
* @return 2 preconfigured data cache.
*/
private CacheConfiguration dataCache(int grpSize) {
CacheConfiguration dataCache = defaultCacheConfiguration();
dataCache.setAffinityMapper(new IgfsGroupDataBlocksKeyMapper(grpSize));
dataCache.setAtomicityMode(TRANSACTIONAL);
return dataCache;
}
/**
* @return preconfigured meta cache.
*/
private CacheConfiguration metaCache() {
CacheConfiguration metaCache = defaultCacheConfiguration();
metaCache.setAtomicityMode(TRANSACTIONAL);
return metaCache;
}
}