Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…ackport/8.x/pr-117201
  • Loading branch information
astefan committed Nov 22, 2024
2 parents 9a6320e + 519057d commit faf0302
Show file tree
Hide file tree
Showing 51 changed files with 1,510 additions and 261 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.compile.CompileOptions;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.api.tasks.testing.Test;
import org.gradle.external.javadoc.CoreJavadocOptions;
import org.gradle.jvm.tasks.Jar;
import org.gradle.jvm.toolchain.JavaLanguageVersion;
import org.gradle.jvm.toolchain.JavaToolchainService;
Expand Down Expand Up @@ -79,6 +82,7 @@ public void apply(Project project) {
String mainSourceSetName = SourceSet.MAIN_SOURCE_SET_NAME + javaVersion;
SourceSet mainSourceSet = addSourceSet(project, javaExtension, mainSourceSetName, mainSourceSets, javaVersion);
configureSourceSetInJar(project, mainSourceSet, javaVersion);
addJar(project, mainSourceSet, javaVersion);
mainSourceSets.add(mainSourceSetName);
testSourceSets.add(mainSourceSetName);

Expand Down Expand Up @@ -142,6 +146,29 @@ private SourceSet addSourceSet(
return sourceSet;
}

private void addJar(Project project, SourceSet sourceSet, int javaVersion) {
project.getConfigurations().register("java" + javaVersion);
TaskProvider<Jar> jarTask = project.getTasks().register("java" + javaVersion + "Jar", Jar.class, task -> {
task.from(sourceSet.getOutput());
});
project.getArtifacts().add("java" + javaVersion, jarTask);
}

private void configurePreviewFeatures(Project project, SourceSet sourceSet, int javaVersion) {
project.getTasks().withType(JavaCompile.class).named(sourceSet.getCompileJavaTaskName()).configure(compileTask -> {
CompileOptions compileOptions = compileTask.getOptions();
compileOptions.getCompilerArgs().add("--enable-preview");
compileOptions.getCompilerArgs().add("-Xlint:-preview");

compileTask.doLast(t -> { stripPreviewFromFiles(compileTask.getDestinationDirectory().getAsFile().get().toPath()); });
});
project.getTasks().withType(Javadoc.class).named(name -> name.equals(sourceSet.getJavadocTaskName())).configureEach(javadocTask -> {
CoreJavadocOptions options = (CoreJavadocOptions) javadocTask.getOptions();
options.addBooleanOption("-enable-preview", true);
options.addStringOption("-release", String.valueOf(javaVersion));
});
}

private void configureSourceSetInJar(Project project, SourceSet sourceSet, int javaVersion) {
var jarTask = project.getTasks().withType(Jar.class).named(JavaPlugin.JAR_TASK_NAME);
jarTask.configure(task -> task.into("META-INF/versions/" + javaVersion, copySpec -> copySpec.from(sourceSet.getOutput())));
Expand Down
5 changes: 5 additions & 0 deletions docs/changelog/115836.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 115836
summary: Catch and handle disconnect exceptions in search
area: Search
type: bug
issues: []
6 changes: 6 additions & 0 deletions docs/changelog/116060.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 116060
summary: Fix leak in `DfsQueryPhase` and introduce search disconnect stress test
area: Search
type: bug
issues:
- 115056
5 changes: 5 additions & 0 deletions docs/changelog/117297.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 117297
summary: Fix CCS exchange when multi cluster aliases point to same cluster
area: ES|QL
type: bug
issues: []
2 changes: 2 additions & 0 deletions docs/reference/esql/functions/mv-functions.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* <<esql-mv_median>>
* <<esql-mv_median_absolute_deviation>>
* <<esql-mv_min>>
* <<esql-mv_percentile>>
* <<esql-mv_pseries_weighted_sum>>
* <<esql-mv_sort>>
* <<esql-mv_slice>>
Expand All @@ -37,6 +38,7 @@ include::layout/mv_max.asciidoc[]
include::layout/mv_median.asciidoc[]
include::layout/mv_median_absolute_deviation.asciidoc[]
include::layout/mv_min.asciidoc[]
include::layout/mv_percentile.asciidoc[]
include::layout/mv_pseries_weighted_sum.asciidoc[]
include::layout/mv_slice.asciidoc[]
include::layout/mv_sort.asciidoc[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,29 @@

package org.elasticsearch.entitlement.instrumentation.impl;

import org.elasticsearch.entitlement.instrumentation.CheckerMethod;
import org.elasticsearch.entitlement.instrumentation.InstrumentationService;
import org.elasticsearch.entitlement.instrumentation.Instrumenter;
import org.elasticsearch.entitlement.instrumentation.MethodKey;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Stream;

public class InstrumentationServiceImpl implements InstrumentationService {

@Override
public Instrumenter newInstrumenter(String classNameSuffix, Map<MethodKey, Method> instrumentationMethods) {
public Instrumenter newInstrumenter(String classNameSuffix, Map<MethodKey, CheckerMethod> instrumentationMethods) {
return new InstrumenterImpl(classNameSuffix, instrumentationMethods);
}

Expand All @@ -33,9 +43,97 @@ public MethodKey methodKeyForTarget(Method targetMethod) {
return new MethodKey(
Type.getInternalName(targetMethod.getDeclaringClass()),
targetMethod.getName(),
Stream.of(actualType.getArgumentTypes()).map(Type::getInternalName).toList(),
Modifier.isStatic(targetMethod.getModifiers())
Stream.of(actualType.getArgumentTypes()).map(Type::getInternalName).toList()
);
}

@Override
public Map<MethodKey, CheckerMethod> lookupMethodsToInstrument(String entitlementCheckerClassName) throws ClassNotFoundException,
IOException {
var methodsToInstrument = new HashMap<MethodKey, CheckerMethod>();
var checkerClass = Class.forName(entitlementCheckerClassName);
var classFileInfo = InstrumenterImpl.getClassFileInfo(checkerClass);
ClassReader reader = new ClassReader(classFileInfo.bytecodes());
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9) {
@Override
public MethodVisitor visitMethod(
int access,
String checkerMethodName,
String checkerMethodDescriptor,
String signature,
String[] exceptions
) {
var mv = super.visitMethod(access, checkerMethodName, checkerMethodDescriptor, signature, exceptions);

var checkerMethodArgumentTypes = Type.getArgumentTypes(checkerMethodDescriptor);
var methodToInstrument = parseCheckerMethodSignature(checkerMethodName, checkerMethodArgumentTypes);

var checkerParameterDescriptors = Arrays.stream(checkerMethodArgumentTypes).map(Type::getDescriptor).toList();
var checkerMethod = new CheckerMethod(Type.getInternalName(checkerClass), checkerMethodName, checkerParameterDescriptors);

methodsToInstrument.put(methodToInstrument, checkerMethod);

return mv;
}
};
reader.accept(visitor, 0);
return methodsToInstrument;
}

private static final Type CLASS_TYPE = Type.getType(Class.class);

static MethodKey parseCheckerMethodSignature(String checkerMethodName, Type[] checkerMethodArgumentTypes) {
var classNameStartIndex = checkerMethodName.indexOf('$');
var classNameEndIndex = checkerMethodName.lastIndexOf('$');

if (classNameStartIndex == -1 || classNameStartIndex >= classNameEndIndex) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Checker method %s has incorrect name format. "
+ "It should be either check$$methodName (instance) or check$package_ClassName$methodName (static)",
checkerMethodName
)
);
}

// No "className" (check$$methodName) -> method is static, and we'll get the class from the actual typed argument
final boolean targetMethodIsStatic = classNameStartIndex + 1 != classNameEndIndex;
final String targetMethodName = checkerMethodName.substring(classNameEndIndex + 1);

final String targetClassName;
final List<String> targetParameterTypes;
if (targetMethodIsStatic) {
if (checkerMethodArgumentTypes.length < 1 || CLASS_TYPE.equals(checkerMethodArgumentTypes[0]) == false) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Checker method %s has incorrect argument types. " + "It must have a first argument of Class<?> type.",
checkerMethodName
)
);
}

targetClassName = checkerMethodName.substring(classNameStartIndex + 1, classNameEndIndex).replace('_', '/');
targetParameterTypes = Arrays.stream(checkerMethodArgumentTypes).skip(1).map(Type::getInternalName).toList();
} else {
if (checkerMethodArgumentTypes.length < 2
|| CLASS_TYPE.equals(checkerMethodArgumentTypes[0]) == false
|| checkerMethodArgumentTypes[1].getSort() != Type.OBJECT) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Checker method %s has incorrect argument types. "
+ "It must have a first argument of Class<?> type, and a second argument of the class containing the method to "
+ "instrument",
checkerMethodName
)
);
}
var targetClassType = checkerMethodArgumentTypes[1];
targetClassName = targetClassType.getInternalName();
targetParameterTypes = Arrays.stream(checkerMethodArgumentTypes).skip(2).map(Type::getInternalName).toList();
}
return new MethodKey(targetClassName, targetMethodName, targetParameterTypes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

package org.elasticsearch.entitlement.instrumentation.impl;

import org.elasticsearch.entitlement.instrumentation.CheckerMethod;
import org.elasticsearch.entitlement.instrumentation.Instrumenter;
import org.elasticsearch.entitlement.instrumentation.MethodKey;
import org.objectweb.asm.AnnotationVisitor;
Expand All @@ -23,7 +24,6 @@

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.stream.Stream;

Expand All @@ -36,13 +36,29 @@
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;

public class InstrumenterImpl implements Instrumenter {

private static final String checkerClassDescriptor;
private static final String handleClass;
static {
int javaVersion = Runtime.version().feature();
final String classNamePrefix;
if (javaVersion >= 23) {
classNamePrefix = "Java23";
} else {
classNamePrefix = "";
}
String checkerClass = "org/elasticsearch/entitlement/bridge/" + classNamePrefix + "EntitlementChecker";
handleClass = checkerClass + "Handle";
checkerClassDescriptor = Type.getObjectType(checkerClass).getDescriptor();
}

/**
* To avoid class name collisions during testing without an agent to replace classes in-place.
*/
private final String classNameSuffix;
private final Map<MethodKey, Method> instrumentationMethods;
private final Map<MethodKey, CheckerMethod> instrumentationMethods;

public InstrumenterImpl(String classNameSuffix, Map<MethodKey, Method> instrumentationMethods) {
public InstrumenterImpl(String classNameSuffix, Map<MethodKey, CheckerMethod> instrumentationMethods) {
this.classNameSuffix = classNameSuffix;
this.instrumentationMethods = instrumentationMethods;
}
Expand Down Expand Up @@ -138,12 +154,7 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str
var mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (isAnnotationPresent == false) {
boolean isStatic = (access & ACC_STATIC) != 0;
var key = new MethodKey(
className,
name,
Stream.of(Type.getArgumentTypes(descriptor)).map(Type::getInternalName).toList(),
isStatic
);
var key = new MethodKey(className, name, Stream.of(Type.getArgumentTypes(descriptor)).map(Type::getInternalName).toList());
var instrumentationMethod = instrumentationMethods.get(key);
if (instrumentationMethod != null) {
// LOGGER.debug("Will instrument method {}", key);
Expand Down Expand Up @@ -177,15 +188,15 @@ private void addClassAnnotationIfNeeded() {
class EntitlementMethodVisitor extends MethodVisitor {
private final boolean instrumentedMethodIsStatic;
private final String instrumentedMethodDescriptor;
private final Method instrumentationMethod;
private final CheckerMethod instrumentationMethod;
private boolean hasCallerSensitiveAnnotation = false;

EntitlementMethodVisitor(
int api,
MethodVisitor methodVisitor,
boolean instrumentedMethodIsStatic,
String instrumentedMethodDescriptor,
Method instrumentationMethod
CheckerMethod instrumentationMethod
) {
super(api, methodVisitor);
this.instrumentedMethodIsStatic = instrumentedMethodIsStatic;
Expand Down Expand Up @@ -262,22 +273,19 @@ private void forwardIncomingArguments() {
private void invokeInstrumentationMethod() {
mv.visitMethodInsn(
INVOKEINTERFACE,
Type.getInternalName(instrumentationMethod.getDeclaringClass()),
instrumentationMethod.getName(),
Type.getMethodDescriptor(instrumentationMethod),
instrumentationMethod.className(),
instrumentationMethod.methodName(),
Type.getMethodDescriptor(
Type.VOID_TYPE,
instrumentationMethod.parameterDescriptors().stream().map(Type::getType).toArray(Type[]::new)
),
true
);
}
}

protected void pushEntitlementChecker(MethodVisitor mv) {
mv.visitMethodInsn(
INVOKESTATIC,
"org/elasticsearch/entitlement/bridge/EntitlementCheckerHandle",
"instance",
"()Lorg/elasticsearch/entitlement/bridge/EntitlementChecker;",
false
);
mv.visitMethodInsn(INVOKESTATIC, handleClass, "instance", "()" + checkerClassDescriptor, false);
}

public record ClassFileInfo(String fileName, byte[] bytecodes) {}
Expand Down
Loading

0 comments on commit faf0302

Please sign in to comment.