Skip to content

Commit 7aa2181

Browse files
Merge branch 'master' into leo.romanovsky/ffl-2693-java-agentless-configuration-source
2 parents 5057539 + d3d2e12 commit 7aa2181

91 files changed

Lines changed: 10577 additions & 107 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitlab/collect-result/CollectResults.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33

44
class CollectResults {
55
public static void main(String[] args) throws Exception {
6+
// Detect flaky jobs
7+
var continueOnFailure = Boolean.parseBoolean(System.getenv("CONTINUE_ON_FAILURE"));
8+
// Run collector
69
var collector =
710
new ResultCollector(
811
Path.of("results"),
912
Path.of("workspace"),
10-
List.of(Path.of("workspace"), Path.of("buildSrc")));
11-
13+
List.of(Path.of("workspace"), Path.of("buildSrc")),
14+
continueOnFailure);
1215
collector.collect();
1316
}
1417
}

.gitlab/collect-result/JUnitReport.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,29 @@ void tagFinalStatuses() {
153153
}
154154
}
155155

156+
/// Marks every testcase as `test.final_status=skip` for jobs that tolerate failures.
157+
///
158+
/// The flaky test jobs (`test_flaky`, `test_flaky_inst`) run with `CONTINUE_ON_FAILURE=true`, so
159+
/// their result never gates the pipeline: it runs to completion regardless of pass or fail.
160+
/// `test.final_status` records that CI impact rather than the raw outcome, so every test in these
161+
/// jobs is `skip` — a failure is a non-blocking failure and a pass is a non-blocking pass. This
162+
/// keeps flaky failures from creating false-positive notifications and skewing SLIs, while the
163+
/// real per-test outcome stays available in `test.status` (derived from the `<failure>`,
164+
/// `<error>`, and `<skipped>` children, which are left in place). Always-green tests that could
165+
/// leave the flaky pipeline are then found with `@test.status:pass @test.final_status:skip`.
166+
///
167+
/// **Must run before {@link #tagFinalStatuses()}** so the natural pass/fail status is never
168+
/// assigned. Testcases already tagged by {@link #tagRetriedAttempts()} or
169+
/// {@link #tagSyntheticFailures()} are left untouched (already `skip`).
170+
void tagAllAsSkipped() {
171+
for (var testcase : testcases()) {
172+
if (hasFinalStatusProperty(testcase)) {
173+
continue;
174+
}
175+
addFinalStatusProperty(testcase, "skip", MissingPropertiesPlacement.FIRST_CHILD);
176+
}
177+
}
178+
156179
void write(Path xmlFile) throws Exception {
157180
Files.createDirectories(xmlFile.getParent());
158181
var tmpFile = Files.createTempFile(xmlFile.getParent(), "collect-results-", ".xml");

.gitlab/collect-result/ResultCollector.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ final class ResultCollector {
1313
private final Path resultsDir;
1414
private final Path workspaceDir;
1515
private final List<Path> searchDirs;
16+
private final boolean continueOnFailure;
1617
private final SourceFileResolver sourceFileResolver;
1718

18-
ResultCollector(Path resultsDir, Path workspaceDir, List<Path> searchDirs) {
19+
ResultCollector(
20+
Path resultsDir, Path workspaceDir, List<Path> searchDirs, boolean continueOnFailure) {
1921
this.resultsDir = resultsDir;
2022
this.workspaceDir = workspaceDir;
2123
this.searchDirs = searchDirs;
24+
this.continueOnFailure = continueOnFailure;
2225
this.sourceFileResolver = new SourceFileResolver(workspaceDir);
2326
}
2427

@@ -32,6 +35,10 @@ void collect() throws Exception {
3235
return;
3336
}
3437

38+
if (continueOnFailure) {
39+
System.out.println("CONTINUE_ON_FAILURE=true: reporting all tests as skip");
40+
}
41+
3542
System.out.println("Saving test results:");
3643
for (var sourceXml : findXmlFiles(testResultDirs)) {
3744
collect(sourceXml);
@@ -51,6 +58,11 @@ private void collect(Path sourceXml) throws Exception {
5158
report.tagRetriedAttempts();
5259
reportChangedBeforeFinalStatus |= report.normalizeStableTestNames();
5360
report.tagSyntheticFailures();
61+
// Flaky jobs (CONTINUE_ON_FAILURE=true) never gate CI, so record every test as skip before
62+
// assigning natural statuses (APMLP-1267).
63+
if (continueOnFailure) {
64+
report.tagAllAsSkipped();
65+
}
5466
report.tagFinalStatuses();
5567
report.write(targetXml);
5668

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ docs/ Developer documentation (see below)
6868

6969
- Title: imperative verb sentence describing user-visible change (e.g. "Fix span sampling rule parsing")
7070
- Labels: always add `tag: ai generated` and at least one `comp:` or `inst:` label + one `type:` label
71-
- Use `tag: no release note` for internal/refactoring changes
71+
- Use `tag: no release notes` for internal/refactoring changes
7272
- Open as draft first, convert to ready when reviewable
7373

7474
## Review Guidelines

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ Both pull requests and issues should be labelled with at least a component or an
118118
119119
Labels are used to not only categorize but also alter the continuous integration behavior:
120120

121-
* `tag: no release note` to exclude a pull request from the next release changelog. Use it when changes are not relevant to the users like:
121+
* `tag: no release notes` to exclude a pull request from the next release changelog. Use it when changes are not relevant to the users like:
122122
* Internal features changes
123123
* Refactoring pull requests
124124
* CI and build tools improvements

dd-java-agent/instrumentation/akka/akka-actor-2.5/build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,19 @@ apply from: "$rootDir/gradle/java.gradle"
1616
apply from: "$rootDir/gradle/test-with-scala.gradle"
1717

1818
addTestSuite('akka23Test')
19+
addTestSuiteExtendingForDir('akka23ForkedTest', 'akka23Test', 'akka23Test')
1920
addTestSuiteForDir('latestDepTest', 'test')
21+
addTestSuiteExtendingForDir('latestDepForkedTest', 'latestDepTest', 'akka23Test')
2022

2123
tasks.named("compileAkka23TestGroovy", GroovyCompile) {
2224
classpath += files(sourceSets.akka23Test.scala.classesDirectory)
2325
}
26+
tasks.named("compileAkka23ForkedTestGroovy", GroovyCompile) {
27+
classpath += files(sourceSets.akka23ForkedTest.scala.classesDirectory)
28+
}
29+
tasks.named("compileLatestDepForkedTestGroovy", GroovyCompile) {
30+
classpath += files(sourceSets.latestDepForkedTest.scala.classesDirectory)
31+
}
2432

2533
// Run the akka23Test against the normal and latestDep akka version as well
2634
sourceSets {

dd-java-agent/instrumentation/akka/akka-actor-2.5/src/akka23Test/groovy/AkkaActorTest.groovy

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import datadog.trace.agent.test.InstrumentationSpecification
22
import datadog.trace.api.config.TraceInstrumentationConfig
33
import datadog.trace.bootstrap.instrumentation.api.Tags
4+
import spock.lang.AutoCleanup
45
import spock.lang.Shared
56

67
class AkkaActorTest extends InstrumentationSpecification {
78
@Shared
9+
@AutoCleanup
810
def akkaTester = new AkkaActors()
911

1012
def "akka actor send #name #iterations"() {
@@ -84,7 +86,7 @@ class AkkaActorTest extends InstrumentationSpecification {
8486
}
8587
}
8688

87-
class AkkaActorContextSwapTest extends AkkaActorTest {
89+
class AkkaActorContextSwapForkedTest extends AkkaActorTest {
8890
@Override
8991
void configurePreAgent() {
9092
super.configurePreAgent()

dd-java-agent/instrumentation/akka/akka-actor-2.5/src/akka23Test/scala/AkkaActors.scala

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentTracer.{
1111
startSpan
1212
}
1313

14+
import scala.concurrent.Await
1415
import scala.concurrent.duration._
1516

1617
class AkkaActors extends AutoCloseable {
@@ -87,24 +88,18 @@ object AkkaActors {
8788
}
8889

8990
// The way to terminate an actor system has changed between versions
90-
val terminate: (ActorSystem) => Unit = {
91-
val t =
92-
try {
93-
ActorSystem.getClass.getMethod("terminate")
94-
} catch {
95-
case _: Throwable =>
96-
try {
97-
ActorSystem.getClass.getMethod("awaitTermination")
98-
} catch {
99-
case _: Throwable => null
100-
}
101-
}
102-
if (t ne null) {
103-
{ system: ActorSystem =>
104-
t.invoke(system)
105-
}
106-
} else {
107-
{ _ => ??? }
91+
val terminate: (ActorSystem) => Unit = { system =>
92+
try {
93+
classOf[ActorSystem].getMethod("terminate").invoke(system)
94+
val whenTerminated = classOf[ActorSystem]
95+
.getMethod("whenTerminated")
96+
.invoke(system)
97+
.asInstanceOf[scala.concurrent.Future[AnyRef]]
98+
Await.ready(whenTerminated, 30.seconds)
99+
} catch {
100+
case _: NoSuchMethodException =>
101+
classOf[ActorSystem].getMethod("shutdown").invoke(system)
102+
classOf[ActorSystem].getMethod("awaitTermination").invoke(system)
108103
}
109104
}
110105
}

dd-java-agent/instrumentation/akka/akka-actor-2.5/src/main/java/datadog/trace/instrumentation/akka/concurrent/AkkaMailboxInstrumentation.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,18 @@
44
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.checkpointActiveForRollback;
55
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.rollbackActiveToCheckpoint;
66
import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.currentContext;
7-
import static java.util.Collections.singletonList;
87
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
98

109
import com.google.auto.service.AutoService;
1110
import datadog.context.Context;
12-
import datadog.trace.agent.tooling.ExcludeFilterProvider;
1311
import datadog.trace.agent.tooling.Instrumenter;
1412
import datadog.trace.agent.tooling.InstrumenterModule;
1513
import datadog.trace.api.InstrumenterConfig;
16-
import datadog.trace.bootstrap.instrumentation.java.concurrent.ExcludeFilter;
17-
import java.util.Collection;
18-
import java.util.EnumMap;
19-
import java.util.List;
20-
import java.util.Map;
2114
import net.bytebuddy.asm.Advice;
2215

2316
@AutoService(InstrumenterModule.class)
2417
public class AkkaMailboxInstrumentation extends InstrumenterModule.ContextTracking
25-
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice, ExcludeFilterProvider {
18+
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
2619

2720
public AkkaMailboxInstrumentation() {
2821
super("akka_actor_mailbox", "akka_actor", "akka_concurrent", "java_concurrent");
@@ -39,16 +32,6 @@ public void methodAdvice(MethodTransformer transformer) {
3932
isMethod().and(named("run")), getClass().getName() + "$SuppressMailboxRunAdvice");
4033
}
4134

42-
@Override
43-
public Map<ExcludeFilter.ExcludeType, ? extends Collection<String>> excludedClasses() {
44-
List<String> excludedClass = singletonList("akka.dispatch.MailBox");
45-
EnumMap<ExcludeFilter.ExcludeType, Collection<String>> excludedTypes =
46-
new EnumMap<>(ExcludeFilter.ExcludeType.class);
47-
excludedTypes.put(ExcludeFilter.ExcludeType.RUNNABLE, excludedClass);
48-
excludedTypes.put(ExcludeFilter.ExcludeType.FORK_JOIN_TASK, excludedClass);
49-
return excludedTypes;
50-
}
51-
5235
/**
5336
* This instrumentation is defensive and closes all scopes on the scope stack that were not there
5437
* when we started processing this actor mailbox. The reason for that is twofold.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package datadog.trace.instrumentation.karate2;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
4+
import static net.bytebuddy.matcher.ElementMatchers.isBridge;
5+
import static net.bytebuddy.matcher.ElementMatchers.not;
6+
import static net.bytebuddy.matcher.ElementMatchers.returns;
7+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
8+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
9+
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
10+
11+
import com.google.auto.service.AutoService;
12+
import datadog.trace.agent.tooling.Instrumenter;
13+
import datadog.trace.agent.tooling.InstrumenterModule;
14+
import datadog.trace.api.Config;
15+
import java.util.Collections;
16+
import java.util.Map;
17+
18+
/**
19+
* Drives test-retry execution policies (ATR / EFD / attempt-to-fix) and failure suppression for
20+
* Karate v2.
21+
*
22+
* <ul>
23+
* <li>{@code ScenarioRuntime#call()} is advised to re-run the scenario while the execution policy
24+
* is applicable, overriding the returned {@code ScenarioResult} with the final attempt.
25+
* <li>{@code ScenarioResult#addStepResult(StepResult)} is advised to replace a failing step with
26+
* a skipped one when the policy requests failure suppression.
27+
* </ul>
28+
*
29+
* Compiled for Java 8 (see {@link KarateInstrumentation}); the advice lives in the {@code java21}
30+
* source set.
31+
*/
32+
@AutoService(InstrumenterModule.class)
33+
public class KarateExecutionInstrumentation extends InstrumenterModule.CiVisibility
34+
implements Instrumenter.ForKnownTypes, Instrumenter.HasMethodAdvice {
35+
36+
public KarateExecutionInstrumentation() {
37+
super("ci-visibility", "karate", "test-retry");
38+
}
39+
40+
@Override
41+
public boolean isEnabled() {
42+
return super.isEnabled() && Config.get().isCiVisibilityExecutionPoliciesEnabled();
43+
}
44+
45+
@Override
46+
public String[] knownMatchingTypes() {
47+
return new String[] {"io.karatelabs.core.ScenarioRuntime", "io.karatelabs.core.ScenarioResult"};
48+
}
49+
50+
@Override
51+
public String[] helperClassNames() {
52+
return new String[] {
53+
packageName + ".KarateUtils",
54+
packageName + ".TestEventsHandlerHolder",
55+
packageName + ".ExecutionContext",
56+
packageName + ".KarateTracingListener",
57+
packageName + ".KarateScenarioAdvice",
58+
packageName + ".KarateScenarioAdvice$RetryAdvice",
59+
packageName + ".KarateScenarioAdvice$SuppressErrorAdvice"
60+
};
61+
}
62+
63+
@Override
64+
public Map<String, String> contextStore() {
65+
return Collections.singletonMap(
66+
"io.karatelabs.gherkin.Scenario", packageName + ".ExecutionContext");
67+
}
68+
69+
@Override
70+
public void methodAdvice(MethodTransformer transformer) {
71+
// ScenarioRuntime#call() is the run()-equivalent; match the concrete ScenarioResult-returning
72+
// method, not the synthetic Callable#call() bridge.
73+
transformer.applyAdvice(
74+
named("call")
75+
.and(takesNoArguments())
76+
.and(returns(named("io.karatelabs.core.ScenarioResult")))
77+
.and(not(isBridge())),
78+
packageName + ".KarateScenarioAdvice$RetryAdvice");
79+
80+
// ScenarioResult#addStepResult(StepResult)
81+
transformer.applyAdvice(
82+
named("addStepResult")
83+
.and(takesArguments(1))
84+
.and(takesArgument(0, named("io.karatelabs.core.StepResult"))),
85+
packageName + ".KarateScenarioAdvice$SuppressErrorAdvice");
86+
}
87+
}

0 commit comments

Comments
 (0)