Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/chainguard/dd-trace-java.release.sts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
issuer: https://gitlab.ddbuild.io

subject: repo:DataDog/dd-trace-java:ref:refs/heads/master

claim_pattern:
project_path: "DataDog/dd-trace-java"
ref: "master"
ref_type: "branch"
ref_path: "refs/heads/master"

permissions:
contents: "read,write"
78 changes: 61 additions & 17 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ variables:
BUILD_JOB_NAME: "build"
DEPENDENCY_CACHE_POLICY: pull
BUILD_CACHE_POLICY: pull
GRADLE_VERSION: "8.5" # must match gradle-wrapper.properties
GRADLE_VERSION: "8.14.3" # must match gradle-wrapper.properties
MAVEN_REPOSITORY_PROXY: "http://artifactual.artifactual.all-clusters.local-dc.fabric.dog:8081/repository/maven-central/"
GRADLE_PLUGIN_PROXY: "http://artifactual.artifactual.all-clusters.local-dc.fabric.dog:8081/repository/gradle-plugin-portal-proxy/"
BUILDER_IMAGE_VERSION_PREFIX: "v25.06-" # use either an empty string (e.g. "") for latest images or a version followed by a hyphen (e.g. "v25.05-")
BUILDER_IMAGE_VERSION_PREFIX: "v25.07-" # use either an empty string (e.g. "") for latest images or a version followed by a hyphen (e.g. "v25.05-")
REPO_NOTIFICATION_CHANNEL: "#apm-java-escalations"
DEFAULT_TEST_JVMS: /^(8|11|17|21|stable)$/
PROFILE_TESTS:
Expand Down Expand Up @@ -137,9 +137,12 @@ default:
KUBERNETES_MEMORY_REQUEST: 8Gi
KUBERNETES_MEMORY_LIMIT: 8Gi
CACHE_TYPE: lib #default
FF_USE_FASTZIP: "true"
CACHE_COMPRESSION_LEVEL: "slowest"

RUNTIME_AVAILABLE_PROCESSORS_OVERRIDE: 4 # Runtime.getRuntime().availableProcessors() returns incorrect or very high values in Kubernetes
cache:
- key: '$CI_SERVER_VERSION-$CACHE_TYPE' # Dependencies cache. Reset the cache every time gitlab is upgraded. ~Every couple months
- key: dependency-$CACHE_TYPE # Dependencies cache
paths:
# Cached dependencies and wrappers for gradle
- .gradle/wrapper
Expand All @@ -148,8 +151,8 @@ default:
policy: $DEPENDENCY_CACHE_POLICY
unprotect: true
fallback_keys: # Use fallback keys because all cache types are not populated. See note under: populate_dep_cache
- '$CI_SERVER_VERSION-base'
- '$CI_SERVER_VERSION-lib'
- 'dependency-base'
- 'dependency-lib'
- key: $CI_PIPELINE_ID-$CACHE_TYPE # Incremental build cache. Shared by all jobs in the pipeline of the same type
paths:
- .gradle/caches/$GRADLE_VERSION
Expand Down Expand Up @@ -188,6 +191,7 @@ default:
after_script:
- *cgroup_info

# TODO: Add a pre-release check to see if the dd-octo-sts token is working.
# Checks and fail early if central credentials are incorrect, indeed, when a new token is generated
# on the central publisher protal, it invalidates the old one. This checks prevents going further.
# See https://datadoghq.atlassian.net/wiki/x/Oog5OgE
Expand All @@ -200,11 +204,11 @@ pre-release-checks:
allow_failure: false
script:
- |
SONATYPE_USERNAME=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_username --with-decryption --query "Parameter.Value" --out text)
SONATYPE_PASSWORD=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_password --with-decryption --query "Parameter.Value" --out text)
MAVEN_CENTRAL_USERNAME=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_username --with-decryption --query "Parameter.Value" --out text)
MAVEN_CENTRAL_PASSWORD=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_password --with-decryption --query "Parameter.Value" --out text)
# See https://central.sonatype.org/publish/publish-portal-api/
# 15e0cbbb-deff-421e-9e02-296a24d0cada is deployment, any deployment id listed in central work, the idea is to check whether the token can authenticate
curl --request POST --include --fail https://central.sonatype.com/api/v1/publisher/status?id=15e0cbbb-deff-421e-9e02-296a24d0cada --header "Authorization: Bearer $(printf "$SONATYPE_USERNAME:$SONATYPE_PASSWORD" | base64)"
curl --request POST --include --fail https://central.sonatype.com/api/v1/publisher/status?id=15e0cbbb-deff-421e-9e02-296a24d0cada --header "Authorization: Bearer $(printf "$MAVEN_CENTRAL_USERNAME:$MAVEN_CENTRAL_PASSWORD" | base64)"
if [ $? -ne 0 ]; then
echo "Failed to authenticate against central. Check credentials, see https://datadoghq.atlassian.net/wiki/x/Oog5OgE"
exit 1
Expand Down Expand Up @@ -768,8 +772,8 @@ deploy_to_di_backend:manual:
UPSTREAM_COMMIT_AUTHOR: $CI_COMMIT_AUTHOR
UPSTREAM_COMMIT_SHORT_SHA: $CI_COMMIT_SHORT_SHA

# If the deploy_to_sonatype job is re-run, re-trigger the deploy_artifacts_to_github job as well so that the artifacts match.
deploy_to_sonatype:
# If the deploy_to_maven_central job is re-run, re-trigger the deploy_artifacts_to_github job as well so that the artifacts match.
deploy_to_maven_central:
extends: .gradle_build
stage: publish
needs: [ build ]
Expand All @@ -786,8 +790,8 @@ deploy_to_sonatype:
- when: manual
allow_failure: true
script:
- export SONATYPE_USERNAME=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_username --with-decryption --query "Parameter.Value" --out text)
- export SONATYPE_PASSWORD=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_password --with-decryption --query "Parameter.Value" --out text)
- export MAVEN_CENTRAL_USERNAME=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_username --with-decryption --query "Parameter.Value" --out text)
- export MAVEN_CENTRAL_PASSWORD=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.central_password --with-decryption --query "Parameter.Value" --out text)
- export GPG_PRIVATE_KEY=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.signing.gpg_private_key --with-decryption --query "Parameter.Value" --out text)
- export GPG_PASSWORD=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.signing.gpg_passphrase --with-decryption --query "Parameter.Value" --out text)
- ./gradlew -PbuildInfo.build.number=$CI_JOB_ID publishToSonatype closeSonatypeStagingRepository -PskipTests $GRADLE_ARGS
Expand All @@ -799,17 +803,57 @@ deploy_to_sonatype:

deploy_artifacts_to_github:
stage: publish
image: registry.ddbuild.io/github-cli:v27480869-eafb11d-2.43.0
image: registry.ddbuild.io/images/dd-octo-sts-ci-base:2025.06-1
tags: [ "arch:amd64" ]
id_tokens:
DDOCTOSTS_ID_TOKEN:
aud: dd-octo-sts
rules:
- if: '$POPULATE_CACHE'
when: never
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/'
when: on_success
# Requires the deploy_to_sonatype job to have run first (the UP-TO-DATE gradle check across jobs is broken)
# Requires the deploy_to_maven_central job to have run first (the UP-TO-DATE gradle check across jobs is broken)
# This will deploy the artifacts built from the publishToSonatype task to the GitHub release
needs:
- job: deploy_to_maven_central
# The deploy_to_maven_central job is not run for release candidate versions
optional: true
before_script:
# Get token
- dd-octo-sts version
- dd-octo-sts debug --scope DataDog/dd-trace-java --policy self.gitlab.release
- dd-octo-sts token --scope DataDog/dd-trace-java --policy self.gitlab.release > github-token.txt
script:
- gh auth login --with-token < github-token.txt
- gh auth status # Maybe helpful to have this output in logs?
- export VERSION=${CI_COMMIT_TAG##v} # remove "v" from front of tag to get version
- cp workspace/dd-java-agent/build/libs/dd-java-agent-${VERSION}.jar workspace/dd-java-agent/build/libs/dd-java-agent.jar # we upload two filenames
- gh release upload --clobber --repo DataDog/dd-trace-java $CI_COMMIT_TAG workspace/dd-java-agent/build/libs/dd-java-agent.jar
- gh release upload --clobber --repo DataDog/dd-trace-java $CI_COMMIT_TAG workspace/dd-java-agent/build/libs/dd-java-agent-${VERSION}.jar
- gh release upload --clobber --repo DataDog/dd-trace-java $CI_COMMIT_TAG workspace/dd-trace-api/build/libs/dd-trace-api-${VERSION}.jar
- gh release upload --clobber --repo DataDog/dd-trace-java $CI_COMMIT_TAG workspace/dd-trace-ot/build/libs/dd-trace-ot-${VERSION}.jar
after_script:
- dd-octo-sts revoke -t $(cat github-token.txt)
retry:
max: 2
when: always

# This is the original job that uses the AWS SSM token retrieval method. Allow manual triggering in case the dd-octo-sts token is not working.
# TODO: Remove this job once the dd-octo-sts token is provably working.
deploy_artifacts_to_github_old:
stage: publish
image: registry.ddbuild.io/github-cli:v27480869-eafb11d-2.43.0
rules:
- if: '$POPULATE_CACHE'
when: never
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/'
when: manual
# Requires the deploy_to_maven_central job to have run first (the UP-TO-DATE gradle check across jobs is broken)
# This will deploy the artifacts built from the publishToSonatype task to the GitHub release
needs:
- job: deploy_to_sonatype
# The deploy_to_sonatype job is not run for release candidate versions
- job: deploy_to_maven_central
# The deploy_to_maven_central job is not run for release candidate versions
optional: true
script:
- aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.gh_release_token --with-decryption --query "Parameter.Value" --out text > github-token.txt
Expand Down Expand Up @@ -837,7 +881,7 @@ package-oci:

configure_system_tests:
variables:
SYSTEM_TESTS_SCENARIOS_GROUPS: "simple_onboarding,simple_onboarding_profiling,docker-ssi,lib-injection"
SYSTEM_TESTS_SCENARIOS_GROUPS: "simple_onboarding,simple_onboarding_profiling,simple_onboarding_appsec,docker-ssi,lib-injection"

create_key:
stage: generate-signing-key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class SharedCommunicationObjects {
public OkHttpClient okHttpClient;
public HttpUrl agentUrl;
public Monitoring monitoring;
private DDAgentFeaturesDiscovery featuresDiscovery;
private volatile DDAgentFeaturesDiscovery featuresDiscovery;
private ConfigurationPoller configurationPoller;

public SharedCommunicationObjects() {
Expand Down Expand Up @@ -139,28 +139,34 @@ public void setFeaturesDiscovery(DDAgentFeaturesDiscovery featuresDiscovery) {
}

public DDAgentFeaturesDiscovery featuresDiscovery(Config config) {
if (featuresDiscovery == null) {
createRemaining(config);
featuresDiscovery =
new DDAgentFeaturesDiscovery(
okHttpClient,
monitoring,
agentUrl,
config.isTraceAgentV05Enabled(),
config.isTracerMetricsEnabled());

if (paused) {
// defer remote discovery until remote I/O is allowed
} else {
if (AGENT_THREAD_GROUP.equals(Thread.currentThread().getThreadGroup())) {
featuresDiscovery.discover(); // safe to run on same thread
} else {
// avoid performing blocking I/O operation on application thread
AgentTaskScheduler.INSTANCE.execute(featuresDiscovery::discover);
DDAgentFeaturesDiscovery ret = featuresDiscovery;
if (ret == null) {
synchronized (this) {
if (featuresDiscovery == null) {
createRemaining(config);
ret =
new DDAgentFeaturesDiscovery(
okHttpClient,
monitoring,
agentUrl,
config.isTraceAgentV05Enabled(),
config.isTracerMetricsEnabled());

if (paused) {
// defer remote discovery until remote I/O is allowed
} else {
if (AGENT_THREAD_GROUP.equals(Thread.currentThread().getThreadGroup())) {
ret.discover(); // safe to run on same thread
} else {
// avoid performing blocking I/O operation on application thread
AgentTaskScheduler.INSTANCE.execute(ret::discoverIfOutdated);
}
}
featuresDiscovery = ret;
}
}
}
return featuresDiscovery;
return ret;
}

private static final class FixedConfigUrlSupplier implements Supplier<String> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
private final ConfigurationPoller configurationPoller;
private WafBuilder wafBuilder;

private MergedAsmFeatures mergedAsmFeatures;
private volatile boolean initialized;
private final MergedAsmFeatures mergedAsmFeatures = new MergedAsmFeatures();

private final ConcurrentHashMap<String, SubconfigListener> subconfigListeners =
new ConcurrentHashMap<>();
Expand Down Expand Up @@ -173,9 +172,7 @@ private class AppSecConfigChangesListener implements ProductListener {
@Override
public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollingRateHinter)
throws IOException {
if (!initialized) {
throw new IllegalStateException();
}
maybeInitializeDefaultConfig();

if (content == null) {
try {
Expand Down Expand Up @@ -219,8 +216,8 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollin
}
defaultConfigActivated = false;
}
super.accept(configKey, content, pollingRateHinter);
usedDDWafConfigKeys.add(configKey.toString());
super.accept(configKey, content, pollingRateHinter);
}

@Override
Expand Down Expand Up @@ -282,13 +279,7 @@ private void subscribeAsmFeatures() {
Product.ASM_FEATURES,
AppSecFeaturesDeserializer.INSTANCE,
(configKey, newConfig, hinter) -> {
if (!hasUserWafConfig && !defaultConfigActivated) {
// features activated in runtime
init();
}
if (!initialized) {
throw new IllegalStateException();
}
maybeInitializeDefaultConfig();
if (newConfig == null) {
mergedAsmFeatures.removeConfig(configKey);
} else {
Expand All @@ -305,10 +296,7 @@ private void subscribeAsmFeatures() {

private void distributeSubConfigurations(
String key, AppSecModuleConfigurer.Reconfiguration reconfiguration) {
if (usedDDWafConfigKeys.isEmpty() && !defaultConfigActivated && !hasUserWafConfig) {
// no config left in the WAF builder, add the default config
init();
}
maybeInitializeDefaultConfig();
for (Map.Entry<String, SubconfigListener> entry : subconfigListeners.entrySet()) {
SubconfigListener listener = entry.getValue();
try {
Expand All @@ -320,6 +308,13 @@ private void distributeSubConfigurations(
}
}

private void maybeInitializeDefaultConfig() {
if (usedDDWafConfigKeys.isEmpty() && !hasUserWafConfig && !defaultConfigActivated) {
// no config left in the WAF builder, add the default config
init();
}
}

@Override
public void init() {
Map<String, Object> wafConfig;
Expand All @@ -341,8 +336,8 @@ public void init() {
} else {
hasUserWafConfig = true;
}
this.mergedAsmFeatures = new MergedAsmFeatures();
this.initialized = true;
this.mergedAsmFeatures.clear();
this.usedDDWafConfigKeys.clear();

if (wafConfig.isEmpty()) {
throw new IllegalStateException("Expected default waf config to be available");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ private void mergeAutoUserInstrum(
}
target.autoUserInstrum = newValue;
}

public void clear() {
mergedData = null;
configs.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,67 @@ class AppSecConfigServiceImplSpecification extends DDSpecification {
p.toFile().delete()
}

// https://github.com/DataDog/dd-trace-java/issues/9159
void 'test initialization issues while applying remote config'() {
setup:
final key = new ParsedConfigKey('Test', '1234', 1, 'ASM_DD', 'ID')
final service = new AppSecConfigServiceImpl(config, poller, reconf)
config.getAppSecActivation() >> ProductActivation.ENABLED_INACTIVE

when:
service.maybeSubscribeConfigPolling()

then:
1 * poller.addListener(Product.ASM_DD, _) >> {
listeners.savedWafDataChangesListener = it[1]
}

when:
listeners.savedWafDataChangesListener.accept(key, '''{"rules_override": [{"rules_target": [{"rule_id": "foo"}], "enabled": false}]}'''.getBytes(), NOOP)

then:
noExceptionThrown()
}

void 'config keys are added and removed to the set when receiving ASM_DD payloads'() {
setup:
final key = new ParsedConfigKey('Test', '1234', 1, 'ASM_DD', 'ID')
final service = new AppSecConfigServiceImpl(config, poller, reconf)
config.getAppSecActivation() >> ProductActivation.ENABLED_INACTIVE

when:
service.maybeSubscribeConfigPolling()

then:
1 * poller.addListener(Product.ASM_DD, _) >> {
listeners.savedWafDataChangesListener = it[1]
}
1 * poller.addListener(Product.ASM_FEATURES, _, _) >> {
listeners.savedFeaturesDeserializer = it[1]
listeners.savedFeaturesListener = it[2]
}

when:
listeners.savedFeaturesListener.accept('asm_features conf',
listeners.savedFeaturesDeserializer.deserialize('{"asm":{"enabled": true}}'.bytes),
NOOP)

then:
service.usedDDWafConfigKeys.empty

when:
listeners.savedWafDataChangesListener.accept(key, '''{"rules_override": [{"rules_target": [{"rule_id": "foo"}], "enabled": false}]}'''.getBytes(), NOOP)

then:
service.usedDDWafConfigKeys.toList() == [key.toString()]

when:
listeners.savedWafDataChangesListener.remove(key, NOOP)

then:
service.usedDDWafConfigKeys.empty
}

private static AppSecFeatures autoUserInstrum(String mode) {
return new AppSecFeatures().tap { features ->
features.autoUserInstrum = new AppSecFeatures.AutoUserInstrum().tap { instrum ->
Expand Down
Loading
Loading