blob: 6ae45819ec1814b6056e454969e6a6cea2d31268 [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.perftest.jvms;
import static java.util.concurrent.TimeUnit.DAYS;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.X500Name;
import org.apache.geode.perftest.infrastructure.Infrastructure;
import org.apache.geode.perftest.infrastructure.InfrastructureFactory;
import org.apache.geode.perftest.jvms.classpath.ClassPathCopier;
import org.apache.geode.perftest.jvms.rmi.Controller;
import org.apache.geode.perftest.jvms.rmi.ControllerFactory;
import org.apache.geode.perftest.runner.SharedContext;
/**
* Factory for launching JVMs and a given infrastructure and setting up RMI
* access to all JVMs.
*/
public class RemoteJVMFactory {
private static final Logger logger = LoggerFactory.getLogger(RemoteJVMFactory.class);
public static final String RMI_HOST = "RMI_HOST";
public static final String RMI_PORT_PROPERTY = "RMI_PORT";
public static final String CONTROLLER = "CONTROLLER";
public static final String OUTPUT_DIR = "OUTPUT_DIR";
public static final String ROLE = "ROLE";
public static final String JVM_ID = "JVM_ID";
public static final int RMI_PORT = 33333;
private static final String CLASSPATH = System.getProperty("java.class.path");
private static final String JAVA_HOME = System.getProperty("java.home");
private final JVMLauncher jvmLauncher;
private final ClassPathCopier classPathCopier;
private final ControllerFactory controllerFactory;
private final InfrastructureFactory infrastructureFactory;
public RemoteJVMFactory(InfrastructureFactory infrastructureFactory,
JVMLauncher jvmLauncher,
ClassPathCopier classPathCopier,
ControllerFactory controllerFactory) {
this.infrastructureFactory = infrastructureFactory;
this.jvmLauncher = jvmLauncher;
this.classPathCopier = classPathCopier;
this.controllerFactory = controllerFactory;
}
public RemoteJVMFactory(InfrastructureFactory infrastructureFactory) {
this(infrastructureFactory, new JVMLauncher(), new ClassPathCopier(CLASSPATH, JAVA_HOME),
new ControllerFactory());
}
/**
* Start all requested JVMs on the infrastructure
*
* @param roles The JVMs to start. Keys a roles and values are the number
* of JVMs in that role.
*
* @return a {@link RemoteJVMs} object used to access the JVMs through RMI
*/
public RemoteJVMs launch(Map<String, Integer> roles,
Map<String, List<String>> jvmArgs) throws Exception {
int numWorkers = roles.values().stream().mapToInt(Integer::intValue).sum();
Infrastructure infra = infrastructureFactory.create(numWorkers);
Set<Infrastructure.Node> nodes = infra.getNodes();
if (nodes.size() < numWorkers) {
throw new IllegalStateException(
"Too few nodes for test. Need " + numWorkers + ", have " + nodes.size());
}
List<JVMMapping> mapping = mapRolesToNodes(roles, nodes, jvmArgs);
Controller controller =
controllerFactory.createController(new SharedContext(mapping), numWorkers);
classPathCopier.copyToNodes(infra, node -> getLibDir(mapping, node));
final List<File> files = new ArrayList<>();
files.add(createKeystore());
final File securityJsonFile = new File("security.json");
FileUtils.copyURLToFile(getClass().getClassLoader().getResource("security.json"),
securityJsonFile);
files.add(securityJsonFile);
if (JavaVersion.current().atLeast(JavaVersion.v17)) {
final File javaArgsFile = new File("java.args");
FileUtils.copyURLToFile(
getClass().getClassLoader().getResource("open-all-jdk-packages-linux-openjdk-17"),
javaArgsFile);
files.add(javaArgsFile);
}
infra.copyToNodes(files, node -> getLibDir(mapping, node), false);
CompletableFuture<Void> processesExited = jvmLauncher.launchProcesses(infra, RMI_PORT, mapping);
if (!controller.waitForWorkers(5, TimeUnit.MINUTES)) {
throw new IllegalStateException("Workers failed to start in 5 minute");
}
return new RemoteJVMs(infra, mapping, controller, processesExited);
}
private JVMMapping getJvmMapping(List<JVMMapping> mapping, Infrastructure.Node node) {
return mapping.stream()
.filter(entry -> entry.getNode().equals(node))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Could not find node dir " + node));
}
private String getLibDir(List<JVMMapping> mapping, Infrastructure.Node node) {
return getJvmMapping(mapping, node)
.getLibDir();
}
private File createKeystore()
throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException,
NoSuchProviderException, InvalidKeyException, SignatureException {
CertAndKeyGen keyGen = new CertAndKeyGen("RSA", "SHA1WithRSA", null);
keyGen.generate(1024);
char[] password = "123456".toCharArray();
PrivateKey privateKey = keyGen.getPrivateKey();
// Generate self signed certificate
X509Certificate[] chain = new X509Certificate[1];
chain[0] = keyGen.getSelfCertificate(new X500Name("CN=ROOT"), DAYS.toSeconds(365));
logger.debug("Certificate : {}", chain[0]);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setKeyEntry("default", privateKey, password, chain);
File jksFile = new File("temp-self-signed.jks");
FileOutputStream fos = new FileOutputStream(jksFile);
ks.store(fos, password);
fos.close();
return jksFile;
}
public InfrastructureFactory getInfrastructureFactory() {
return infrastructureFactory;
}
private List<JVMMapping> mapRolesToNodes(Map<String, Integer> roles,
Set<Infrastructure.Node> nodes,
Map<String, List<String>> jvmArgs) {
List<JVMMapping> mapping = new ArrayList<>();
Iterator<Infrastructure.Node> nodeItr = nodes.iterator();
int id = 0;
for (Map.Entry<String, Integer> roleEntry : roles.entrySet()) {
for (int i = 0; i < roleEntry.getValue(); i++) {
Infrastructure.Node node = nodeItr.next();
String role = roleEntry.getKey();
List<String> roleArgs = jvmArgs.getOrDefault(role, Collections.emptyList());
mapping.add(new JVMMapping(node, role, id++, roleArgs));
}
}
return mapping;
}
}