mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-04-19 04:45:07 -04:00
Patcher for AWS SDKv2 locale-dependent formatting (#126326)
AWS SDK v2 has a bug (aws/aws-sdk-java-v2#5968) where PathResolver uses locale-dependent formatting. This PR adds a patcher to the discovery-ec2 build process to replace calls to String.format(<format>, <args>) with String.format(Locale.ROOT, <format>, <args>). Relates to ES-11279
This commit is contained in:
parent
07cb14e7a9
commit
2697a3a872
4 changed files with 187 additions and 2 deletions
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.dependencies.patches.awsv2sdk;
|
||||
|
||||
import org.elasticsearch.gradle.internal.dependencies.patches.PatcherInfo;
|
||||
import org.elasticsearch.gradle.internal.dependencies.patches.Utils;
|
||||
import org.gradle.api.artifacts.transform.CacheableTransform;
|
||||
import org.gradle.api.artifacts.transform.InputArtifact;
|
||||
import org.gradle.api.artifacts.transform.TransformAction;
|
||||
import org.gradle.api.artifacts.transform.TransformOutputs;
|
||||
import org.gradle.api.artifacts.transform.TransformParameters;
|
||||
import org.gradle.api.file.FileSystemLocation;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.gradle.internal.dependencies.patches.PatcherInfo.classPatcher;
|
||||
|
||||
@CacheableTransform
|
||||
public abstract class Awsv2ClassPatcher implements TransformAction<TransformParameters.None> {
|
||||
|
||||
private static final String JAR_FILE_TO_PATCH = "aws-query-protocol";
|
||||
|
||||
private static final List<PatcherInfo> CLASS_PATCHERS = List.of(
|
||||
// This patcher is needed because of this AWS bug: https://github.com/aws/aws-sdk-java-v2/issues/5968
|
||||
// As soon as the bug is resolved and we upgrade our AWS SDK v2 libraries, we can remove this.
|
||||
classPatcher(
|
||||
"software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.class",
|
||||
"213e84d9a745bdae4b844334d17aecdd6499b36df32aa73f82dc114b35043009",
|
||||
StringFormatInPathResolverPatcher::new
|
||||
)
|
||||
);
|
||||
|
||||
@Classpath
|
||||
@InputArtifact
|
||||
public abstract Provider<FileSystemLocation> getInputArtifact();
|
||||
|
||||
@Override
|
||||
public void transform(@NotNull TransformOutputs outputs) {
|
||||
File inputFile = getInputArtifact().get().getAsFile();
|
||||
|
||||
if (inputFile.getName().startsWith(JAR_FILE_TO_PATCH)) {
|
||||
System.out.println("Patching " + inputFile.getName());
|
||||
File outputFile = outputs.file(inputFile.getName().replace(".jar", "-patched.jar"));
|
||||
Utils.patchJar(inputFile, outputFile, CLASS_PATCHERS);
|
||||
} else {
|
||||
System.out.println("Skipping " + inputFile.getName());
|
||||
outputs.file(getInputArtifact());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
package org.elasticsearch.gradle.internal.dependencies.patches.awsv2sdk;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.ASM9;
|
||||
import static org.objectweb.asm.Opcodes.GETSTATIC;
|
||||
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
|
||||
|
||||
class StringFormatInPathResolverPatcher extends ClassVisitor {
|
||||
|
||||
StringFormatInPathResolverPatcher(ClassWriter classWriter) {
|
||||
super(ASM9, classWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
return new ReplaceCallMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces calls to String.format(format, args); with calls to String.format(Locale.ROOT, format, args);
|
||||
*/
|
||||
private static class ReplaceCallMethodVisitor extends MethodVisitor {
|
||||
private static final String CLASS_INTERNAL_NAME = Type.getInternalName(String.class);
|
||||
private static final String METHOD_NAME = "format";
|
||||
private static final String OLD_METHOD_DESCRIPTOR = Type.getMethodDescriptor(
|
||||
Type.getType(String.class),
|
||||
Type.getType(String.class),
|
||||
Type.getType(Object[].class)
|
||||
);
|
||||
private static final String NEW_METHOD_DESCRIPTOR = Type.getMethodDescriptor(
|
||||
Type.getType(String.class),
|
||||
Type.getType(Locale.class),
|
||||
Type.getType(String.class),
|
||||
Type.getType(Object[].class)
|
||||
);
|
||||
|
||||
private boolean foundFormatPattern = false;
|
||||
|
||||
ReplaceCallMethodVisitor(MethodVisitor methodVisitor) {
|
||||
super(ASM9, methodVisitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
if (value instanceof String s && s.startsWith("%s")) {
|
||||
if (foundFormatPattern) {
|
||||
throw new IllegalStateException(
|
||||
"A previous string format constant was not paired with a String.format() call. "
|
||||
+ "Patching would generate an unbalances stack"
|
||||
);
|
||||
}
|
||||
// Push the extra arg on the stack
|
||||
mv.visitFieldInsn(GETSTATIC, Type.getInternalName(Locale.class), "ROOT", Type.getDescriptor(Locale.class));
|
||||
foundFormatPattern = true;
|
||||
}
|
||||
super.visitLdcInsn(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
||||
if (opcode == INVOKESTATIC
|
||||
&& foundFormatPattern
|
||||
&& CLASS_INTERNAL_NAME.equals(owner)
|
||||
&& METHOD_NAME.equals(name)
|
||||
&& OLD_METHOD_DESCRIPTOR.equals(descriptor)) {
|
||||
// Replace the call with String.format(Locale.ROOT, format, args)
|
||||
mv.visitMethodInsn(INVOKESTATIC, CLASS_INTERNAL_NAME, METHOD_NAME, NEW_METHOD_DESCRIPTOR, false);
|
||||
foundFormatPattern = false;
|
||||
} else {
|
||||
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,31 @@ esplugin {
|
|||
classname ='org.elasticsearch.discovery.ec2.Ec2DiscoveryPlugin'
|
||||
}
|
||||
|
||||
def patched = Attribute.of('patched', Boolean)
|
||||
|
||||
configurations {
|
||||
compileClasspath {
|
||||
attributes {
|
||||
attribute(patched, true)
|
||||
}
|
||||
}
|
||||
runtimeClasspath {
|
||||
attributes {
|
||||
attribute(patched, true)
|
||||
}
|
||||
}
|
||||
testCompileClasspath {
|
||||
attributes {
|
||||
attribute(patched, true)
|
||||
}
|
||||
}
|
||||
testRuntimeClasspath {
|
||||
attributes {
|
||||
attribute(patched, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation "software.amazon.awssdk:annotations:${versions.awsv2sdk}"
|
||||
|
@ -65,6 +90,17 @@ dependencies {
|
|||
testImplementation project(':test:fixtures:ec2-imds-fixture')
|
||||
|
||||
internalClusterTestImplementation project(':test:fixtures:ec2-imds-fixture')
|
||||
|
||||
attributesSchema {
|
||||
attribute(patched)
|
||||
}
|
||||
artifactTypes.getByName("jar") {
|
||||
attributes.attribute(patched, false)
|
||||
}
|
||||
registerTransform(org.elasticsearch.gradle.internal.dependencies.patches.awsv2sdk.Awsv2ClassPatcher) {
|
||||
from.attribute(patched, false)
|
||||
to.attribute(patched, true)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("dependencyLicenses").configure {
|
||||
|
|
|
@ -103,8 +103,7 @@ public class Ec2DiscoveryTests extends AbstractEC2MockAPITestCase {
|
|||
final String[] params = request.split("&");
|
||||
Arrays.stream(params).filter(entry -> entry.startsWith("Filter.") && entry.contains("=tag%3A")).forEach(entry -> {
|
||||
final int startIndex = "Filter.".length();
|
||||
// TODO ensure the filterId is an ASCII int when https://github.com/aws/aws-sdk-java-v2/issues/5968 fixed
|
||||
final var filterId = entry.substring(startIndex, entry.indexOf(".", startIndex));
|
||||
final int filterId = Integer.parseInt(entry.substring(startIndex, entry.indexOf(".", startIndex)));
|
||||
tagsIncluded.put(
|
||||
entry.substring(entry.indexOf("=tag%3A") + "=tag%3A".length()),
|
||||
Arrays.stream(params)
|
||||
|
|
Loading…
Add table
Reference in a new issue