From 94d72ec4c291c1b4ccbc8aa9b2ae36ea1f165af3 Mon Sep 17 00:00:00 2001 From: Nikita Tkachenko Date: Tue, 28 Mar 2023 14:13:00 +0200 Subject: [PATCH 1/3] Implement embedded git metadata extraction --- .../agent-ci-visibility/build.gradle | 6 +- .../trace/civisibility/AppVeyorInfo.java | 10 +- .../civisibility/AzurePipelinesInfo.java | 14 +- .../trace/civisibility/BitBucketInfo.java | 10 +- .../trace/civisibility/BitriseInfo.java | 12 +- .../datadog/trace/civisibility/BuddyInfo.java | 12 +- .../trace/civisibility/BuildkiteInfo.java | 12 +- .../civisibility/CITagsProviderImpl.java | 248 ++++-------------- .../civisibility/CiVisibilitySystem.java | 8 + .../trace/civisibility/CircleCIInfo.java | 10 +- .../trace/civisibility/GitLabInfo.java | 14 +- .../trace/civisibility/GithubActionsInfo.java | 10 +- .../trace/civisibility/JenkinsInfo.java | 12 +- .../trace/civisibility/TravisInfo.java | 10 +- .../trace/civisibility/UnknownCIInfo.java | 2 +- .../git/CILocalGitInfoBuilder.java | 24 ++ .../git/CIProviderGitInfoBuilder.java | 18 ++ .../civisibility/git/GitInfoExtractor.java | 2 +- .../git/LocalFSGitInfoExtractor.java | 8 +- .../git/info/CILocalGitInfoBuilder.java | 15 -- .../git/info/UserSuppliedGitInfoBuilder.java | 57 ---- .../source/MethodLinesResolverImpl.java | 2 +- .../CITagsProviderImplTest.groovy | 12 +- .../civisibility/UnknownCIInfoTest.groovy | 12 +- .../git/CILocalGitInfoBuilderTest.groovy | 49 ++++ .../git/CIProviderGitInfoBuilderTest.groovy | 30 +++ .../git/LocalFSGitInfoExtractorTest.groovy | 6 +- .../source/MethodLinesResolverImplTest.groovy | 75 +++++- .../civisibility/source/UtilsTest.groovy | 32 +++ .../trace/agent/jmxfetch/JMXFetch.java | 2 +- ...ServiceNameCollectingTraceInterceptor.java | 19 +- .../profiling/uploader/ProfileUploader.java | 12 +- .../main/java/datadog/trace/api/DDTags.java | 3 + .../trace/api/config/TracerConfig.java | 1 + .../interceptor/AbstractTraceInterceptor.java | 38 +++ .../AbstractTraceInterceptorTest.groovy | 20 ++ .../CiVisibilityApmProtocolInterceptor.java | 15 +- .../CiVisibilityTraceInterceptor.java | 16 +- .../common/GitMetadataTraceInterceptor.java | 38 +++ .../ddintake/DDIntakeTraceInterceptor.java | 16 +- .../java/datadog/trace/core/CoreTracer.java | 66 +++-- .../trace/core/TraceInterceptorTest.groovy | 8 +- internal-api/build.gradle | 32 ++- .../main/java/datadog/trace/api/Config.java | 9 + .../api/civisibility/CIProviderInfo.java | 2 +- .../{civisibility => }/git/CommitInfo.java | 2 +- .../trace/api/git/EmbeddedGitInfoBuilder.java | 70 +++++ .../api/{civisibility => }/git/GitInfo.java | 2 +- .../datadog/trace/api/git/GitInfoBuilder.java | 7 + .../trace/api/git/GitInfoProvider.java | 106 ++++++++ .../java/datadog/trace/api}/git/GitUtils.java | 9 +- .../{civisibility => }/git/PersonInfo.java | 2 +- .../datadog/trace/api}/git/RawParseUtils.java | 2 +- .../api/git/UserSuppliedGitInfoBuilder.java | 96 +++++++ .../api/git/EmbeddedGitInfoBuilderTest.groovy | 53 ++++ .../trace/api/git/GitInfoProviderTest.groovy | 127 +++++++++ .../{civisibility => }/git/GitInfoTest.groovy | 2 +- .../trace/api}/git/GitUtilsTest.groovy | 8 +- .../trace/api}/git/RawParseUtilsTest.groovy | 6 +- .../UserSuppliedGitInfoBuilderTest.groovy | 15 +- .../trace/bootstrap/git/gradle-git.properties | 19 ++ .../trace/bootstrap/git/maven-git.properties | 26 ++ 62 files changed, 1137 insertions(+), 444 deletions(-) create mode 100644 dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CILocalGitInfoBuilder.java create mode 100644 dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CIProviderGitInfoBuilder.java delete mode 100644 dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/info/CILocalGitInfoBuilder.java delete mode 100644 dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/info/UserSuppliedGitInfoBuilder.java create mode 100644 dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CILocalGitInfoBuilderTest.groovy create mode 100644 dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CIProviderGitInfoBuilderTest.groovy create mode 100644 dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/source/UtilsTest.groovy create mode 100644 dd-trace-api/src/main/java/datadog/trace/api/interceptor/AbstractTraceInterceptor.java create mode 100644 dd-trace-api/src/test/groovy/datadog/trace/api/interceptor/AbstractTraceInterceptorTest.groovy create mode 100644 dd-trace-core/src/main/java/datadog/trace/common/GitMetadataTraceInterceptor.java rename internal-api/src/main/java/datadog/trace/api/{civisibility => }/git/CommitInfo.java (98%) create mode 100644 internal-api/src/main/java/datadog/trace/api/git/EmbeddedGitInfoBuilder.java rename internal-api/src/main/java/datadog/trace/api/{civisibility => }/git/GitInfo.java (98%) create mode 100644 internal-api/src/main/java/datadog/trace/api/git/GitInfoBuilder.java create mode 100644 internal-api/src/main/java/datadog/trace/api/git/GitInfoProvider.java rename {dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility => internal-api/src/main/java/datadog/trace/api}/git/GitUtils.java (95%) rename internal-api/src/main/java/datadog/trace/api/{civisibility => }/git/PersonInfo.java (98%) rename {dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility => internal-api/src/main/java/datadog/trace/api}/git/RawParseUtils.java (99%) create mode 100644 internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java create mode 100644 internal-api/src/test/groovy/datadog/trace/api/git/EmbeddedGitInfoBuilderTest.groovy create mode 100644 internal-api/src/test/groovy/datadog/trace/api/git/GitInfoProviderTest.groovy rename internal-api/src/test/groovy/datadog/trace/api/{civisibility => }/git/GitInfoTest.groovy (96%) rename {dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility => internal-api/src/test/groovy/datadog/trace/api}/git/GitUtilsTest.groovy (86%) rename {dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility => internal-api/src/test/groovy/datadog/trace/api}/git/RawParseUtilsTest.groovy (89%) rename {dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility => internal-api/src/test/groovy/datadog/trace/api/git}/UserSuppliedGitInfoBuilderTest.groovy (85%) create mode 100644 internal-api/src/test/resources/datadog/trace/bootstrap/git/gradle-git.properties create mode 100644 internal-api/src/test/resources/datadog/trace/bootstrap/git/maven-git.properties diff --git a/dd-java-agent/agent-ci-visibility/build.gradle b/dd-java-agent/agent-ci-visibility/build.gradle index ee881968bc6..400492fa1f5 100644 --- a/dd-java-agent/agent-ci-visibility/build.gradle +++ b/dd-java-agent/agent-ci-visibility/build.gradle @@ -8,11 +8,7 @@ apply from: "$rootDir/gradle/version.gradle" minimumBranchCoverage = 0.7 minimumInstructionCoverage = 0.8 -excludedClassesCoverage += [ - "datadog.trace.civisibility.CiVisibilitySystem", - "datadog.trace.civisibility.git.GitObject", - "datadog.trace.civisibility.source.*" -] +excludedClassesCoverage += ["datadog.trace.civisibility.CiVisibilitySystem", "datadog.trace.civisibility.git.GitObject",] dependencies { api deps.slf4j diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/AppVeyorInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/AppVeyorInfo.java index b8aafde0461..90916e24e8c 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/AppVeyorInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/AppVeyorInfo.java @@ -1,14 +1,14 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.PersonInfo; class AppVeyorInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/AzurePipelinesInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/AzurePipelinesInfo.java index 6e1d49f7c18..4e329ee07f5 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/AzurePipelinesInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/AzurePipelinesInfo.java @@ -1,16 +1,16 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.filterSensitiveInfo; -import static datadog.trace.civisibility.git.GitUtils.isTagReference; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.filterSensitiveInfo; +import static datadog.trace.api.git.GitUtils.isTagReference; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.PersonInfo; class AzurePipelinesInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BitBucketInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BitBucketInfo.java index 6443326f57a..88187c76d5f 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BitBucketInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BitBucketInfo.java @@ -1,14 +1,14 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.filterSensitiveInfo; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.filterSensitiveInfo; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; import datadog.trace.util.Strings; class BitBucketInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BitriseInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BitriseInfo.java index 2323e8a2dc8..4e6201ff101 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BitriseInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BitriseInfo.java @@ -1,15 +1,15 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.filterSensitiveInfo; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.filterSensitiveInfo; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.PersonInfo; class BitriseInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BuddyInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BuddyInfo.java index b52ad398c16..d1217c6e201 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BuddyInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BuddyInfo.java @@ -1,14 +1,14 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.filterSensitiveInfo; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.filterSensitiveInfo; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.PersonInfo; class BuddyInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BuildkiteInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BuildkiteInfo.java index d98b2806b88..b5f68ea8233 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BuildkiteInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/BuildkiteInfo.java @@ -1,15 +1,15 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.filterSensitiveInfo; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.filterSensitiveInfo; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.PersonInfo; class BuildkiteInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CITagsProviderImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CITagsProviderImpl.java index 74608e1a5c4..2682ea52d94 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CITagsProviderImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CITagsProviderImpl.java @@ -6,93 +6,53 @@ import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; import datadog.trace.api.civisibility.CITagsProvider; -import datadog.trace.api.civisibility.git.GitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.GitInfoProvider; import datadog.trace.bootstrap.instrumentation.api.Tags; -import datadog.trace.civisibility.git.GitUtils; -import datadog.trace.civisibility.git.info.CILocalGitInfoBuilder; -import datadog.trace.civisibility.git.info.UserSuppliedGitInfoBuilder; import java.util.HashMap; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class CITagsProviderImpl implements CITagsProvider { - private static final Logger log = LoggerFactory.getLogger(CITagsProviderImpl.class); - - private static final String GIT_FOLDER_NAME = ".git"; - - private final String gitFolder; + private final GitInfoProvider gitInfoProvider; public CITagsProviderImpl() { - this(GIT_FOLDER_NAME); + this(GitInfoProvider.INSTANCE); } - CITagsProviderImpl(String gitFolder) { - this.gitFolder = gitFolder; + CITagsProviderImpl(GitInfoProvider gitInfoProvider) { + this.gitInfoProvider = gitInfoProvider; } @Override public Map getCiTags(CIProviderInfo ciProviderInfo) { CIInfo ciInfo = ciProviderInfo.buildCIInfo(); - GitInfo ciGitInfo = ciProviderInfo.buildCIGitInfo(); - String repoRoot = ciInfo.getCiWorkspace(); - - CILocalGitInfoBuilder ciLocalGitInfoBuilder = new CILocalGitInfoBuilder(); - GitInfo localGitInfo = ciLocalGitInfoBuilder.build(repoRoot, gitFolder); - - UserSuppliedGitInfoBuilder userSuppliedGitInfoBuilder = new UserSuppliedGitInfoBuilder(); - GitInfo userSuppliedGitInfo = userSuppliedGitInfoBuilder.build(); - - Map ciTags = - new CITagsBuilder() - .withCiProviderName(ciInfo.getCiProviderName()) - .withCiPipelineId(ciInfo.getCiPipelineId()) - .withCiPipelineName(ciInfo.getCiPipelineName()) - .withCiStageName(ciInfo.getCiStageName()) - .withCiJobName(ciInfo.getCiJobName()) - .withCiPipelineNumber(ciInfo.getCiPipelineNumber()) - .withCiPipelineUrl(ciInfo.getCiPipelineUrl()) - .withCiJorUrl(ciInfo.getCiJobUrl()) - .withCiWorkspacePath(ciInfo.getCiWorkspace()) - .withCiEnvVars(ciInfo.getCiEnvVars()) - .withGitRepositoryUrl(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitCommit(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitBranch(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitTag(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitCommitAuthorName(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitCommitAuthorEmail(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitCommitAuthorDate(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitCommitCommitterName(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitCommitCommitterEmail(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitCommitCommitterDate(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .withGitCommitMessage(userSuppliedGitInfo, ciGitInfo, localGitInfo) - .build(); - - if (!userSuppliedGitInfo.isEmpty()) { - // if there is any git metadata supplied by the user, we want to check that repo URL and - // commit SHA are valid - String resolvedRepoUrl = ciTags.get(Tags.GIT_REPOSITORY_URL); - if (resolvedRepoUrl == null || resolvedRepoUrl.isEmpty()) { - log.error( - "Could not resolve git repository URL (can be provided via " - + GitInfo.DD_GIT_REPOSITORY_URL - + " env var)"); - } - - String resolvedCommitSha = ciTags.get(Tags.GIT_COMMIT_SHA); - if (!GitUtils.isValidCommitSha(resolvedCommitSha)) { - log.error( - "Git commit SHA could not be resolved or is invalid: " - + resolvedCommitSha - + " (can be provided via " - + GitInfo.DD_GIT_COMMIT_SHA - + " env var, and must be a full-length git SHA)"); - } - } - - return ciTags; + GitInfo gitInfo = gitInfoProvider.getGitInfo(repoRoot); + + return new CITagsBuilder() + .withCiProviderName(ciInfo.getCiProviderName()) + .withCiPipelineId(ciInfo.getCiPipelineId()) + .withCiPipelineName(ciInfo.getCiPipelineName()) + .withCiStageName(ciInfo.getCiStageName()) + .withCiJobName(ciInfo.getCiJobName()) + .withCiPipelineNumber(ciInfo.getCiPipelineNumber()) + .withCiPipelineUrl(ciInfo.getCiPipelineUrl()) + .withCiJorUrl(ciInfo.getCiJobUrl()) + .withCiWorkspacePath(ciInfo.getCiWorkspace()) + .withCiEnvVars(ciInfo.getCiEnvVars()) + .withGitRepositoryUrl(gitInfo) + .withGitCommit(gitInfo) + .withGitBranch(gitInfo) + .withGitTag(gitInfo) + .withGitCommitAuthorName(gitInfo) + .withGitCommitAuthorEmail(gitInfo) + .withGitCommitAuthorDate(gitInfo) + .withGitCommitCommitterName(gitInfo) + .withGitCommitCommitterEmail(gitInfo) + .withGitCommitCommitterDate(gitInfo) + .withGitCommitMessage(gitInfo) + .build(); } public static class CITagsBuilder { @@ -142,148 +102,52 @@ public CITagsBuilder withCiEnvVars(final Map ciEnvVars) { return putTagValue(DDTags.CI_ENV_VARS, toJson(ciEnvVars)); } - public CITagsBuilder withGitRepositoryUrl( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - return putTagValue( - Tags.GIT_REPOSITORY_URL, - userSuppliedGitInfo.getRepositoryURL(), - ciGitInfo.getRepositoryURL(), - localGitInfo.getRepositoryURL()); + public CITagsBuilder withGitRepositoryUrl(final GitInfo gitInfo) { + return putTagValue(Tags.GIT_REPOSITORY_URL, gitInfo.getRepositoryURL()); } - public CITagsBuilder withGitCommit( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - return putTagValue( - Tags.GIT_COMMIT_SHA, - userSuppliedGitInfo.getCommit().getSha(), - ciGitInfo.getCommit().getSha(), - localGitInfo.getCommit().getSha()); + public CITagsBuilder withGitCommit(final GitInfo gitInfo) { + return putTagValue(Tags.GIT_COMMIT_SHA, gitInfo.getCommit().getSha()); } - public CITagsBuilder withGitBranch( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - return putTagValue( - Tags.GIT_BRANCH, - userSuppliedGitInfo.getBranch(), - ciGitInfo.getBranch(), - localGitInfo.getBranch()); + public CITagsBuilder withGitBranch(final GitInfo gitInfo) { + return putTagValue(Tags.GIT_BRANCH, gitInfo.getBranch()); } - public CITagsBuilder withGitTag( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - return putTagValue( - Tags.GIT_TAG, userSuppliedGitInfo.getTag(), ciGitInfo.getTag(), localGitInfo.getTag()); + public CITagsBuilder withGitTag(final GitInfo gitInfo) { + return putTagValue(Tags.GIT_TAG, gitInfo.getTag()); } - public CITagsBuilder withGitCommitAuthorName( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - final String userSuppliedGitAuthorName = - userSuppliedGitInfo.getCommit().getAuthor().getName(); - final String ciGitAuthorName = ciGitInfo.getCommit().getAuthor().getName(); - final String localGitAuthorName = - isCommitShaEquals(ciGitInfo, localGitInfo) - ? localGitInfo.getCommit().getAuthor().getName() - : null; - return putTagValue( - Tags.GIT_COMMIT_AUTHOR_NAME, - userSuppliedGitAuthorName, - ciGitAuthorName, - localGitAuthorName); + public CITagsBuilder withGitCommitAuthorName(final GitInfo gitInfo) { + return putTagValue(Tags.GIT_COMMIT_AUTHOR_NAME, gitInfo.getCommit().getAuthor().getName()); } - public CITagsBuilder withGitCommitAuthorEmail( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - final String userSuppliedGitAuthorEmail = - userSuppliedGitInfo.getCommit().getAuthor().getEmail(); - final String ciGitAuthorEmail = ciGitInfo.getCommit().getAuthor().getEmail(); - final String localGitAuthorEmail = - isCommitShaEquals(ciGitInfo, localGitInfo) - ? localGitInfo.getCommit().getAuthor().getEmail() - : null; - return putTagValue( - Tags.GIT_COMMIT_AUTHOR_EMAIL, - userSuppliedGitAuthorEmail, - ciGitAuthorEmail, - localGitAuthorEmail); + public CITagsBuilder withGitCommitAuthorEmail(final GitInfo gitInfo) { + return putTagValue(Tags.GIT_COMMIT_AUTHOR_EMAIL, gitInfo.getCommit().getAuthor().getEmail()); } - public CITagsBuilder withGitCommitAuthorDate( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - final String userSuppliedGitAuthorDate = - userSuppliedGitInfo.getCommit().getAuthor().getIso8601Date(); - final String ciGitAuthorDate = ciGitInfo.getCommit().getAuthor().getIso8601Date(); - final String localGitAuthorDate = - isCommitShaEquals(ciGitInfo, localGitInfo) - ? localGitInfo.getCommit().getAuthor().getIso8601Date() - : null; + public CITagsBuilder withGitCommitAuthorDate(final GitInfo gitInfo) { return putTagValue( - Tags.GIT_COMMIT_AUTHOR_DATE, - userSuppliedGitAuthorDate, - ciGitAuthorDate, - localGitAuthorDate); + Tags.GIT_COMMIT_AUTHOR_DATE, gitInfo.getCommit().getAuthor().getIso8601Date()); } - public CITagsBuilder withGitCommitCommitterName( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - final String userSuppliedGitCommitterName = - userSuppliedGitInfo.getCommit().getCommitter().getName(); - final String ciGitCommitterName = ciGitInfo.getCommit().getCommitter().getName(); - final String localGitCommitterName = - isCommitShaEquals(ciGitInfo, localGitInfo) - ? localGitInfo.getCommit().getCommitter().getName() - : null; + public CITagsBuilder withGitCommitCommitterName(final GitInfo gitInfo) { return putTagValue( - Tags.GIT_COMMIT_COMMITTER_NAME, - userSuppliedGitCommitterName, - ciGitCommitterName, - localGitCommitterName); + Tags.GIT_COMMIT_COMMITTER_NAME, gitInfo.getCommit().getCommitter().getName()); } - public CITagsBuilder withGitCommitCommitterEmail( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - final String userSuppliedGitCommitterEmail = - userSuppliedGitInfo.getCommit().getCommitter().getEmail(); - final String ciGitCommitterEmail = ciGitInfo.getCommit().getCommitter().getEmail(); - final String localCommitterEmail = - isCommitShaEquals(ciGitInfo, localGitInfo) - ? localGitInfo.getCommit().getCommitter().getEmail() - : null; + public CITagsBuilder withGitCommitCommitterEmail(final GitInfo gitInfo) { return putTagValue( - Tags.GIT_COMMIT_COMMITTER_EMAIL, - userSuppliedGitCommitterEmail, - ciGitCommitterEmail, - localCommitterEmail); + Tags.GIT_COMMIT_COMMITTER_EMAIL, gitInfo.getCommit().getCommitter().getEmail()); } - public CITagsBuilder withGitCommitCommitterDate( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - final String userSuppliedGitCommitterDate = - userSuppliedGitInfo.getCommit().getCommitter().getIso8601Date(); - final String ciGitCommitterDate = ciGitInfo.getCommit().getCommitter().getIso8601Date(); - final String localGitCommitterDate = - isCommitShaEquals(ciGitInfo, localGitInfo) - ? localGitInfo.getCommit().getCommitter().getIso8601Date() - : null; + public CITagsBuilder withGitCommitCommitterDate(final GitInfo gitInfo) { return putTagValue( - Tags.GIT_COMMIT_COMMITTER_DATE, - userSuppliedGitCommitterDate, - ciGitCommitterDate, - localGitCommitterDate); + Tags.GIT_COMMIT_COMMITTER_DATE, gitInfo.getCommit().getCommitter().getIso8601Date()); } - public CITagsBuilder withGitCommitMessage( - final GitInfo userSuppliedGitInfo, final GitInfo ciGitInfo, final GitInfo localGitInfo) { - final String userSuppliedGitCommitMessage = userSuppliedGitInfo.getCommit().getFullMessage(); - final String ciGitCommitMessage = ciGitInfo.getCommit().getFullMessage(); - final String localGitCommitMessage = - isCommitShaEquals(ciGitInfo, localGitInfo) - ? localGitInfo.getCommit().getFullMessage() - : null; - return putTagValue( - Tags.GIT_COMMIT_MESSAGE, - userSuppliedGitCommitMessage, - ciGitCommitMessage, - localGitCommitMessage); + public CITagsBuilder withGitCommitMessage(final GitInfo gitInfo) { + return putTagValue(Tags.GIT_COMMIT_MESSAGE, gitInfo.getCommit().getFullMessage()); } public Map build() { @@ -304,11 +168,5 @@ private CITagsBuilder putTagValue( } return this; } - - private boolean isCommitShaEquals(GitInfo ciGitInfo, GitInfo localGitInfo) { - final String ciGitCommit = ciGitInfo.getCommit().getSha(); - final String localFSGitCommit = localGitInfo.getCommit().getSha(); - return ciGitCommit == null || ciGitCommit.equalsIgnoreCase(localFSGitCommit); - } } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 7e1e96042b3..db996b650dd 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -6,7 +6,10 @@ import datadog.trace.api.civisibility.events.impl.BuildEventsHandlerImpl; import datadog.trace.api.civisibility.events.impl.TestEventsHandlerImpl; import datadog.trace.api.civisibility.source.SourcePathResolver; +import datadog.trace.api.git.GitInfoProvider; import datadog.trace.civisibility.codeowners.CodeownersProvider; +import datadog.trace.civisibility.git.CILocalGitInfoBuilder; +import datadog.trace.civisibility.git.CIProviderGitInfoBuilder; import datadog.trace.civisibility.source.BestEfforSourcePathResolver; import datadog.trace.civisibility.source.CompilerAidedSourcePathResolver; import datadog.trace.civisibility.source.MethodLinesResolverImpl; @@ -18,6 +21,8 @@ public class CiVisibilitySystem { private static final Logger LOGGER = LoggerFactory.getLogger(CiVisibilitySystem.class); + private static final String GIT_FOLDER_NAME = ".git"; + public static void start() { final Config config = Config.get(); if (!config.isCiVisibilityEnabled()) { @@ -45,5 +50,8 @@ public static void start() { InstrumentationBridge.setTestEventsHandlerFactory(TestEventsHandlerImpl::new); InstrumentationBridge.setBuildEventsHandlerFactory(BuildEventsHandlerImpl::new); + + GitInfoProvider.INSTANCE.registerGitInfoBuilder(new CIProviderGitInfoBuilder()); + GitInfoProvider.INSTANCE.registerGitInfoBuilder(new CILocalGitInfoBuilder(GIT_FOLDER_NAME)); } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CircleCIInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CircleCIInfo.java index b4096154a70..9ee8859335e 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CircleCIInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CircleCIInfo.java @@ -1,14 +1,14 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.filterSensitiveInfo; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.filterSensitiveInfo; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; class CircleCIInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/GitLabInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/GitLabInfo.java index 19ebae6b2bf..c5e66a1c95a 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/GitLabInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/GitLabInfo.java @@ -1,16 +1,16 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.filterSensitiveInfo; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.filterSensitiveInfo; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; -import datadog.trace.civisibility.git.GitUtils; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.GitUtils; +import datadog.trace.api.git.PersonInfo; import de.thetaphi.forbiddenapis.SuppressForbidden; @SuppressForbidden diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/GithubActionsInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/GithubActionsInfo.java index 31eff43ca79..c6172ef331d 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/GithubActionsInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/GithubActionsInfo.java @@ -1,14 +1,14 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.isTagReference; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.isTagReference; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; class GithubActionsInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/JenkinsInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/JenkinsInfo.java index cefd842075c..4be10e2af08 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/JenkinsInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/JenkinsInfo.java @@ -1,15 +1,15 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.filterSensitiveInfo; -import static datadog.trace.civisibility.git.GitUtils.isTagReference; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.filterSensitiveInfo; +import static datadog.trace.api.git.GitUtils.isTagReference; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; import de.thetaphi.forbiddenapis.SuppressForbidden; import java.util.HashMap; import java.util.Map; diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/TravisInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/TravisInfo.java index 7de51f27f5f..ef92381567e 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/TravisInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/TravisInfo.java @@ -1,14 +1,14 @@ package datadog.trace.civisibility; -import static datadog.trace.civisibility.git.GitUtils.normalizeBranch; -import static datadog.trace.civisibility.git.GitUtils.normalizeTag; +import static datadog.trace.api.git.GitUtils.normalizeBranch; +import static datadog.trace.api.git.GitUtils.normalizeTag; import static datadog.trace.civisibility.utils.PathUtils.expandTilde; import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.PersonInfo; class TravisInfo implements CIProviderInfo { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/UnknownCIInfo.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/UnknownCIInfo.java index 3ec3280092c..97b435c358e 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/UnknownCIInfo.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/UnknownCIInfo.java @@ -4,7 +4,7 @@ import datadog.trace.api.civisibility.CIInfo; import datadog.trace.api.civisibility.CIProviderInfo; -import datadog.trace.api.civisibility.git.GitInfo; +import datadog.trace.api.git.GitInfo; import java.nio.file.Path; /** diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CILocalGitInfoBuilder.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CILocalGitInfoBuilder.java new file mode 100644 index 00000000000..dd1a5927fd6 --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CILocalGitInfoBuilder.java @@ -0,0 +1,24 @@ +package datadog.trace.civisibility.git; + +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.GitInfoBuilder; +import java.nio.file.Paths; +import javax.annotation.Nullable; + +public class CILocalGitInfoBuilder implements GitInfoBuilder { + + private final String gitFolderName; + + public CILocalGitInfoBuilder(String gitFolderName) { + this.gitFolderName = gitFolderName; + } + + @Override + public GitInfo build(@Nullable String repositoryPath) { + if (repositoryPath == null) { + return GitInfo.NOOP; + } + return new LocalFSGitInfoExtractor() + .headCommit(Paths.get(repositoryPath, gitFolderName).toFile().getAbsolutePath()); + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CIProviderGitInfoBuilder.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CIProviderGitInfoBuilder.java new file mode 100644 index 00000000000..e055627c4fc --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/CIProviderGitInfoBuilder.java @@ -0,0 +1,18 @@ +package datadog.trace.civisibility.git; + +import datadog.trace.api.civisibility.CIProviderInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.GitInfoBuilder; +import datadog.trace.civisibility.CIProviderInfoFactory; +import java.nio.file.Path; +import java.nio.file.Paths; +import javax.annotation.Nullable; + +public class CIProviderGitInfoBuilder implements GitInfoBuilder { + @Override + public GitInfo build(@Nullable String repositoryPath) { + Path currentPath = repositoryPath != null ? Paths.get(repositoryPath) : null; + CIProviderInfo ciProviderInfo = CIProviderInfoFactory.createCIProviderInfo(currentPath); + return ciProviderInfo.buildCIGitInfo(); + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/GitInfoExtractor.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/GitInfoExtractor.java index 36682612059..4038fe0063e 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/GitInfoExtractor.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/GitInfoExtractor.java @@ -1,6 +1,6 @@ package datadog.trace.civisibility.git; -import datadog.trace.api.civisibility.git.GitInfo; +import datadog.trace.api.git.GitInfo; public interface GitInfoExtractor { diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/LocalFSGitInfoExtractor.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/LocalFSGitInfoExtractor.java index b0ae8b444ba..55832a06836 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/LocalFSGitInfoExtractor.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/LocalFSGitInfoExtractor.java @@ -1,8 +1,10 @@ package datadog.trace.civisibility.git; -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; +import datadog.trace.api.git.CommitInfo; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.GitUtils; +import datadog.trace.api.git.PersonInfo; +import datadog.trace.api.git.RawParseUtils; import datadog.trace.civisibility.git.pack.GitPackObject; import datadog.trace.civisibility.git.pack.GitPackUtils; import datadog.trace.civisibility.git.pack.V2PackGitInfoExtractor; diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/info/CILocalGitInfoBuilder.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/info/CILocalGitInfoBuilder.java deleted file mode 100644 index 3cb07c1fe2b..00000000000 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/info/CILocalGitInfoBuilder.java +++ /dev/null @@ -1,15 +0,0 @@ -package datadog.trace.civisibility.git.info; - -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.civisibility.git.LocalFSGitInfoExtractor; -import java.nio.file.Paths; - -public class CILocalGitInfoBuilder { - public GitInfo build(String ciWorkspace, String gitFolderName) { - if (ciWorkspace == null) { - return GitInfo.NOOP; - } - return new LocalFSGitInfoExtractor() - .headCommit(Paths.get(ciWorkspace, gitFolderName).toFile().getAbsolutePath()); - } -} diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/info/UserSuppliedGitInfoBuilder.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/info/UserSuppliedGitInfoBuilder.java deleted file mode 100644 index d1247f06419..00000000000 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/info/UserSuppliedGitInfoBuilder.java +++ /dev/null @@ -1,57 +0,0 @@ -package datadog.trace.civisibility.git.info; - -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_BRANCH; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_COMMIT_AUTHOR_DATE; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_COMMIT_AUTHOR_EMAIL; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_COMMIT_AUTHOR_NAME; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_COMMIT_COMMITTER_DATE; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_COMMIT_COMMITTER_EMAIL; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_COMMIT_COMMITTER_NAME; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_COMMIT_MESSAGE; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_COMMIT_SHA; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_REPOSITORY_URL; -import static datadog.trace.api.civisibility.git.GitInfo.DD_GIT_TAG; - -import datadog.trace.api.civisibility.git.CommitInfo; -import datadog.trace.api.civisibility.git.GitInfo; -import datadog.trace.api.civisibility.git.PersonInfo; -import datadog.trace.civisibility.git.GitUtils; - -public class UserSuppliedGitInfoBuilder { - public GitInfo build() { - final String gitRepositoryUrl = System.getenv(DD_GIT_REPOSITORY_URL); - - // The user can set the DD_GIT_BRANCH manually but - // using the value returned by the CI Provider, so - // we need to normalize the value. Also, it can contain - // the tag (e.g. origin/tags/0.1.0) - String gitTag = System.getenv(DD_GIT_TAG); - String gitBranch = null; - final String gitBranchOrTag = System.getenv(DD_GIT_BRANCH); - if (gitBranchOrTag != null) { - if (!GitUtils.isTagReference(gitBranchOrTag)) { - gitBranch = GitUtils.normalizeBranch(gitBranchOrTag); - } else if (gitTag == null) { - gitTag = GitUtils.normalizeTag(gitBranchOrTag); - } - } - final String gitCommitSha = System.getenv(DD_GIT_COMMIT_SHA); - final String gitCommitMessage = System.getenv(DD_GIT_COMMIT_MESSAGE); - final String gitCommitAuthorName = System.getenv(DD_GIT_COMMIT_AUTHOR_NAME); - final String gitCommitAuthorEmail = System.getenv(DD_GIT_COMMIT_AUTHOR_EMAIL); - final String gitCommitAuthorDate = System.getenv(DD_GIT_COMMIT_AUTHOR_DATE); - final String gitCommitCommitterName = System.getenv(DD_GIT_COMMIT_COMMITTER_NAME); - final String gitCommitCommitterEmail = System.getenv(DD_GIT_COMMIT_COMMITTER_EMAIL); - final String gitCommitCommitterDate = System.getenv(DD_GIT_COMMIT_COMMITTER_DATE); - - return new GitInfo( - gitRepositoryUrl, - gitBranch, - gitTag, - new CommitInfo( - gitCommitSha, - new PersonInfo(gitCommitAuthorName, gitCommitAuthorEmail, gitCommitAuthorDate), - new PersonInfo(gitCommitCommitterName, gitCommitCommitterEmail, gitCommitCommitterDate), - gitCommitMessage)); - } -} diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/MethodLinesResolverImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/MethodLinesResolverImpl.java index e3b7aff97bb..cc915f0366e 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/MethodLinesResolverImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/source/MethodLinesResolverImpl.java @@ -38,7 +38,7 @@ public MethodLines getLines(@Nonnull Method method) { } } - private static final class ClassMethodLines { + static final class ClassMethodLines { private final Map recordersByMethodFingerprint = new HashMap<>(); public MethodLinesRecorder createRecorder(String methodFingerprint) { diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/CITagsProviderImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/CITagsProviderImplTest.groovy index ef1cd112c4c..0f34f56d139 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/CITagsProviderImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/CITagsProviderImplTest.groovy @@ -2,8 +2,12 @@ package datadog.trace.civisibility import datadog.trace.api.civisibility.CIProviderInfo import datadog.trace.api.civisibility.CITagsProvider -import datadog.trace.api.civisibility.git.GitInfo +import datadog.trace.api.git.GitInfo +import datadog.trace.api.git.GitInfoProvider +import datadog.trace.api.git.UserSuppliedGitInfoBuilder import datadog.trace.bootstrap.instrumentation.api.Tags +import datadog.trace.civisibility.git.CILocalGitInfoBuilder +import datadog.trace.civisibility.git.CIProviderGitInfoBuilder import org.junit.Rule import org.junit.contrib.java.lang.system.EnvironmentVariables import org.junit.contrib.java.lang.system.RestoreSystemProperties @@ -157,7 +161,11 @@ abstract class CITagsProviderImplTest extends Specification { } CITagsProvider ciTagsProvider() { - return new CITagsProviderImpl(GIT_FOLDER_FOR_TESTS) + GitInfoProvider gitInfoProvider = new GitInfoProvider() + gitInfoProvider.registerGitInfoBuilder(new UserSuppliedGitInfoBuilder()) + gitInfoProvider.registerGitInfoBuilder(new CIProviderGitInfoBuilder()) + gitInfoProvider.registerGitInfoBuilder(new CILocalGitInfoBuilder(GIT_FOLDER_FOR_TESTS)) + return new CITagsProviderImpl(gitInfoProvider) } def "resolve"(workspace) { diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/UnknownCIInfoTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/UnknownCIInfoTest.groovy index 1c0042fba18..aceb28cc6f8 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/UnknownCIInfoTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/UnknownCIInfoTest.groovy @@ -1,7 +1,11 @@ package datadog.trace.civisibility import datadog.trace.api.civisibility.CIProviderInfo +import datadog.trace.api.git.GitInfoProvider +import datadog.trace.api.git.UserSuppliedGitInfoBuilder import datadog.trace.bootstrap.instrumentation.api.Tags +import datadog.trace.civisibility.git.CILocalGitInfoBuilder +import datadog.trace.civisibility.git.CIProviderGitInfoBuilder import java.nio.file.Paths @@ -47,7 +51,13 @@ class UnknownCIInfoTest extends CITagsProviderImplTest { def "test workspace is null if target folder does not exist"() { when: def ciProviderInfo = new UnknownCIInfo("this-target-folder-does-not-exist", workspaceForTests) - def ciTagsProvider = new CITagsProviderImpl(GIT_FOLDER_FOR_TESTS) + + GitInfoProvider gitInfoProvider = new GitInfoProvider() + gitInfoProvider.registerGitInfoBuilder(new UserSuppliedGitInfoBuilder()) + gitInfoProvider.registerGitInfoBuilder(new CIProviderGitInfoBuilder()) + gitInfoProvider.registerGitInfoBuilder(new CILocalGitInfoBuilder(GIT_FOLDER_FOR_TESTS)) + def ciTagsProvider = new CITagsProviderImpl(gitInfoProvider) + def ciTags = ciTagsProvider.getCiTags(ciProviderInfo) then: diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CILocalGitInfoBuilderTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CILocalGitInfoBuilderTest.groovy new file mode 100644 index 00000000000..d921242da9c --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CILocalGitInfoBuilderTest.groovy @@ -0,0 +1,49 @@ +package datadog.trace.civisibility.git + + +import spock.lang.Specification + +import java.nio.file.Paths + +class CILocalGitInfoBuilderTest extends Specification { + + def "returns empty git info when repository path is not specified"() { + setup: + def builder = new CILocalGitInfoBuilder(".git") + + when: + def gitInfo = builder.build(null) + + then: + gitInfo != null + gitInfo.isEmpty() + } + + def "parses git info"() { + setup: + def builder = new CILocalGitInfoBuilder("git_folder_for_tests") + def workspace = resolve("ci/ci_workspace_for_tests") + + when: + def gitInfo = builder.build(workspace) + + then: + gitInfo != null + !gitInfo.isEmpty() + gitInfo.repositoryURL == "https://some-host/some-user/some-repo.git" + gitInfo.branch == "master" + gitInfo.commit.sha == "0797c248e019314fc1d91a483e859b32f4509953" + gitInfo.commit.fullMessage == "This is a commit message\n" + gitInfo.commit.author.name == "John Doe" + gitInfo.commit.author.email == "john@doe.com" + gitInfo.commit.author.iso8601Date == "2021-02-12T13:47:48.000Z" + gitInfo.commit.committer.name == "Jane Doe" + gitInfo.commit.committer.email == "jane@doe.com" + gitInfo.commit.committer.iso8601Date == "2021-02-12T13:48:44.000Z" + } + + def resolve(workspace) { + def resolvedWS = Paths.get(getClass().getClassLoader().getResource(workspace).toURI()).toFile().getAbsolutePath() + return resolvedWS + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CIProviderGitInfoBuilderTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CIProviderGitInfoBuilderTest.groovy new file mode 100644 index 00000000000..1ad5ad74617 --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CIProviderGitInfoBuilderTest.groovy @@ -0,0 +1,30 @@ +package datadog.trace.civisibility.git + + +import org.junit.Rule +import org.junit.contrib.java.lang.system.EnvironmentVariables +import spock.lang.Specification + +class CIProviderGitInfoBuilderTest extends Specification { + + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables() + + def setup() { + // Clear all environment variables to avoid clashes between + // real CI/Git environment variables and the spec CI/Git + // environment variables. + environmentVariables.clear(System.getenv().keySet() as String[]) + } + + def "test builds empty git info in an unknown repository"() { + setup: + def builder = new CIProviderGitInfoBuilder() + + when: + def gitInfo = builder.build(null) + + then: + gitInfo.isEmpty() + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/LocalFSGitInfoExtractorTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/LocalFSGitInfoExtractorTest.groovy index 0031627dae6..5ae6cf5fbca 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/LocalFSGitInfoExtractorTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/LocalFSGitInfoExtractorTest.groovy @@ -1,8 +1,8 @@ package datadog.trace.civisibility.git -import datadog.trace.api.civisibility.git.CommitInfo -import datadog.trace.api.civisibility.git.GitInfo -import datadog.trace.api.civisibility.git.PersonInfo +import datadog.trace.api.git.CommitInfo +import datadog.trace.api.git.GitInfo +import datadog.trace.api.git.PersonInfo import datadog.trace.test.util.DDSpecification import java.nio.file.Paths diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/source/MethodLinesResolverImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/source/MethodLinesResolverImplTest.groovy index b10a5ac0a3a..4e7c8862496 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/source/MethodLinesResolverImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/source/MethodLinesResolverImplTest.groovy @@ -1,19 +1,11 @@ package datadog.trace.civisibility.source +import org.spockframework.util.IoUtil import spock.lang.Specification class MethodLinesResolverImplTest extends Specification { - private static abstract class NestedClass { - static double aTestMethod() { - def random = Math.random() - return random - } - - abstract void abstractMethod() - } - def "test method lines resolution"() { setup: def aTestMethod = NestedClass.getDeclaredMethod("aTestMethod") @@ -39,5 +31,70 @@ class MethodLinesResolverImplTest extends Specification { then: !methodLines.isValid() } + + def "test returns empty method lines when class cannot be loaded"() { + setup: + def misbehavingClassLoader = new MisbehavingClassLoader() + + Utils.getClassStream(NestedClass).withCloseable { stream -> + def baos = new ByteArrayOutputStream() + IoUtil.copyStream(stream, baos) + misbehavingClassLoader.putClass(NestedClass.name, baos.toByteArray()) + } + + def misbehavingClass = misbehavingClassLoader.loadClass(NestedClass.name) + def misbehavingMethod = misbehavingClass.getDeclaredMethod("aTestMethod") + + when: + def methodLinesResolver = new MethodLinesResolverImpl() + def methodLines = methodLinesResolver.getLines(misbehavingMethod) + + then: + !methodLines.isValid() + } + + def "test returns empty method lines when unknown method is attempted to be resolved"() { + setup: + def aTestMethod = NestedClass.getDeclaredMethod("abstractMethod") + def classMethodLines = new MethodLinesResolverImpl.ClassMethodLines() + + when: + def methodLines = classMethodLines.get(aTestMethod) + + then: + !methodLines.isValid() + } + + private static abstract class NestedClass { + static double aTestMethod() { + def random = Math.random() + return random + } + + abstract void abstractMethod() + } + + private static final class MisbehavingClassLoader extends ClassLoader { + + private final Map classes = new HashMap<>() + + @Override + InputStream getResourceAsStream(String name) { + throw new RuntimeException("Something went wrong") + } + + @Override + Class loadClass(String name) throws ClassNotFoundException { + def bytes = classes.get(name) + if (bytes != null) { + return defineClass(name, bytes, 0, bytes.length) + } + return super.loadClass(name) + } + + void putClass(String name, byte[] bytes) { + classes.put(name, bytes) + } + } } diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/source/UtilsTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/source/UtilsTest.groovy new file mode 100644 index 00000000000..ab5984617db --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/source/UtilsTest.groovy @@ -0,0 +1,32 @@ +package datadog.trace.civisibility.source + + +import spock.lang.Specification + +class UtilsTest extends Specification { + + def "test gets class stream for #clazz"() { + expect: + Utils.getClassStream(clazz).withCloseable { stream -> + def cafebabe = readMagicNumber(stream) + cafebabe[0] == (byte) 0xCA + cafebabe[1] == (byte) 0xFE + cafebabe[2] == (byte) 0xBA + cafebabe[3] == (byte) 0xBE + } + + where: + clazz << [UtilsTestClass, Object] + } + + private byte[] readMagicNumber(InputStream stream) { + def bytes = new byte[4] + def totalRead = 0 + while (totalRead != bytes.length) { + totalRead += stream.read(bytes, totalRead, bytes.length - totalRead) + } + bytes + } + + private static final class UtilsTestClass {} +} diff --git a/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/JMXFetch.java b/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/JMXFetch.java index 7575943f109..c573e60101a 100644 --- a/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/JMXFetch.java +++ b/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/JMXFetch.java @@ -108,7 +108,7 @@ private static void run(final StatsDClientManager statsDClientManager, final Con if (config.isJmxFetchMultipleRuntimeServicesEnabled()) { ServiceNameCollectingTraceInterceptor serviceNameProvider = - new ServiceNameCollectingTraceInterceptor(); + ServiceNameCollectingTraceInterceptor.INSTANCE; GlobalTracer.get().addTraceInterceptor(serviceNameProvider); configBuilder.serviceNameProvider(serviceNameProvider); diff --git a/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/ServiceNameCollectingTraceInterceptor.java b/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/ServiceNameCollectingTraceInterceptor.java index ba47bb79bce..442bea4e332 100644 --- a/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/ServiceNameCollectingTraceInterceptor.java +++ b/dd-java-agent/agent-jmxfetch/src/main/java/datadog/trace/agent/jmxfetch/ServiceNameCollectingTraceInterceptor.java @@ -2,8 +2,8 @@ import datadog.trace.api.Config; import datadog.trace.api.DDSpanTypes; +import datadog.trace.api.interceptor.AbstractTraceInterceptor; import datadog.trace.api.interceptor.MutableSpan; -import datadog.trace.api.interceptor.TraceInterceptor; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -12,8 +12,12 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import org.datadog.jmxfetch.service.ServiceNameProvider; -public class ServiceNameCollectingTraceInterceptor - implements TraceInterceptor, ServiceNameProvider { +public class ServiceNameCollectingTraceInterceptor extends AbstractTraceInterceptor + implements ServiceNameProvider { + + public static final ServiceNameCollectingTraceInterceptor INSTANCE = + new ServiceNameCollectingTraceInterceptor(Priority.SERVICE_NAME_COLLECTING); + /* * The other span types all set their own service names, so we ignore them. They should not have JVM * runtime metrics applied to their service names. @@ -30,6 +34,10 @@ public class ServiceNameCollectingTraceInterceptor private volatile int serviceNamesSize = 0; private final ConcurrentHashMap serviceNames = new ConcurrentHashMap<>(); + protected ServiceNameCollectingTraceInterceptor(Priority priority) { + super(priority); + } + @Override public Collection onTraceComplete( Collection trace) { @@ -47,11 +55,6 @@ public Collection onTraceComplete( return trace; } - @Override - public int priority() { - return Integer.MAX_VALUE; - } - @Override public Iterable getServiceNames() { return serviceNames.keySet(); diff --git a/dd-java-agent/agent-profiling/profiling-uploader/src/main/java/com/datadog/profiling/uploader/ProfileUploader.java b/dd-java-agent/agent-profiling/profiling-uploader/src/main/java/com/datadog/profiling/uploader/ProfileUploader.java index 22e25a60140..f850bb67f07 100644 --- a/dd-java-agent/agent-profiling/profiling-uploader/src/main/java/com/datadog/profiling/uploader/ProfileUploader.java +++ b/dd-java-agent/agent-profiling/profiling-uploader/src/main/java/com/datadog/profiling/uploader/ProfileUploader.java @@ -24,7 +24,10 @@ import datadog.communication.http.OkHttpUtils; import datadog.trace.api.Config; import datadog.trace.api.DDTags; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.GitInfoProvider; import datadog.trace.bootstrap.config.provider.ConfigProvider; +import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.relocate.api.IOLogger; import datadog.trace.util.AgentThreadFactory; import datadog.trace.util.PidHelper; @@ -151,9 +154,16 @@ public ProfileUploader(final Config config, final ConfigProvider configProvider) if (!PidHelper.getPid().isEmpty()) { tagsMap.put(DDTags.PID_TAG, PidHelper.getPid()); } + + if (config.isTraceGitMetadataEnabled()) { + GitInfo gitInfo = GitInfoProvider.INSTANCE.getGitInfo(); + tagsMap.put(Tags.GIT_REPOSITORY_URL, gitInfo.getRepositoryURL()); + tagsMap.put(Tags.GIT_COMMIT_SHA, gitInfo.getCommit().getSha()); + } + // Comma separated tags string for V2.4 format tags = String.join(",", tagsToList(tagsMap)); - this.uploadTimeout = Duration.ofSeconds(config.getProfilingUploadTimeout()); + uploadTimeout = Duration.ofSeconds(config.getProfilingUploadTimeout()); // This is the same thing OkHttp Dispatcher is doing except thread naming and daemonization okHttpExecutorService = diff --git a/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java b/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java index 7e2fde9c224..710f595ddef 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java @@ -44,4 +44,7 @@ public class DDTags { public static final String MEASURED = "_dd.measured"; public static final String PID_TAG = "process_id"; public static final String SCHEMA_VERSION_TAG_KEY = "_dd.trace_span_attribute_schema"; + + public static final String INTERNAL_GIT_REPOSITORY_URL = "_dd.git.repository_url"; + public static final String INTERNAL_GIT_COMMIT_SHA = "_dd.git.commit.sha"; } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java index 0f1456b5ca1..1659d57b80b 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TracerConfig.java @@ -48,6 +48,7 @@ public final class TracerConfig { public static final String TRACE_REPORT_HOSTNAME = "trace.report-hostname"; public static final String TRACE_CLIENT_IP_HEADER = "trace.client-ip-header"; public static final String TRACE_CLIENT_IP_RESOLVER_ENABLED = "trace.client-ip.resolver.enabled"; + public static final String TRACE_GIT_METADATA_ENABLED = "trace.git.metadata.enabled"; public static final String HEADER_TAGS = "trace.header.tags"; public static final String REQUEST_HEADER_TAGS = "trace.request_header.tags"; public static final String RESPONSE_HEADER_TAGS = "trace.response_header.tags"; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/interceptor/AbstractTraceInterceptor.java b/dd-trace-api/src/main/java/datadog/trace/api/interceptor/AbstractTraceInterceptor.java new file mode 100644 index 00000000000..c40bffc83d3 --- /dev/null +++ b/dd-trace-api/src/main/java/datadog/trace/api/interceptor/AbstractTraceInterceptor.java @@ -0,0 +1,38 @@ +package datadog.trace.api.interceptor; + +public abstract class AbstractTraceInterceptor implements TraceInterceptor { + + private final Priority priority; + + protected AbstractTraceInterceptor(Priority priority) { + this.priority = priority; + } + + @Override + public int priority() { + return priority.idx; + } + + public enum Priority { + // trace filtering + CI_VISIBILITY_TRACE(0), + CI_VISIBILITY_APM(1), + + // trace data enrichment + DD_INTAKE(2), + GIT_METADATA(3), + + // trace data collection + SERVICE_NAME_COLLECTING(Integer.MAX_VALUE); + + private final int idx; + + Priority(int idx) { + this.idx = idx; + } + + int getIdx() { + return idx; + } + } +} diff --git a/dd-trace-api/src/test/groovy/datadog/trace/api/interceptor/AbstractTraceInterceptorTest.groovy b/dd-trace-api/src/test/groovy/datadog/trace/api/interceptor/AbstractTraceInterceptorTest.groovy new file mode 100644 index 00000000000..904edf3d39f --- /dev/null +++ b/dd-trace-api/src/test/groovy/datadog/trace/api/interceptor/AbstractTraceInterceptorTest.groovy @@ -0,0 +1,20 @@ +package datadog.trace.api.interceptor + +import spock.lang.Specification + +class AbstractTraceInterceptorTest extends Specification { + + def "priority index is taken from enum"() { + given: + def priority = AbstractTraceInterceptor.Priority.values()[0] + def interceptor = new AbstractTraceInterceptor(priority) { + @Override + Collection onTraceComplete(Collection trace) { + return null + } + } + + expect: + interceptor.priority() == priority.idx + } +} diff --git a/dd-trace-core/src/main/java/datadog/trace/civisibility/interceptor/CiVisibilityApmProtocolInterceptor.java b/dd-trace-core/src/main/java/datadog/trace/civisibility/interceptor/CiVisibilityApmProtocolInterceptor.java index df1811ced14..589fba522db 100644 --- a/dd-trace-core/src/main/java/datadog/trace/civisibility/interceptor/CiVisibilityApmProtocolInterceptor.java +++ b/dd-trace-core/src/main/java/datadog/trace/civisibility/interceptor/CiVisibilityApmProtocolInterceptor.java @@ -1,8 +1,8 @@ package datadog.trace.civisibility.interceptor; import datadog.trace.api.DDSpanTypes; +import datadog.trace.api.interceptor.AbstractTraceInterceptor; import datadog.trace.api.interceptor.MutableSpan; -import datadog.trace.api.interceptor.TraceInterceptor; import datadog.trace.bootstrap.instrumentation.api.Tags; import java.util.Collection; import java.util.List; @@ -13,10 +13,14 @@ * and tags related to certain CI Visibility features, e.g. Test Suite Level Visibility, can only be * written with CI Test Cycle protocol) */ -public class CiVisibilityApmProtocolInterceptor implements TraceInterceptor { +public class CiVisibilityApmProtocolInterceptor extends AbstractTraceInterceptor { public static final CiVisibilityApmProtocolInterceptor INSTANCE = - new CiVisibilityApmProtocolInterceptor(); + new CiVisibilityApmProtocolInterceptor(Priority.CI_VISIBILITY_APM); + + protected CiVisibilityApmProtocolInterceptor(Priority priority) { + super(priority); + } @Override public Collection onTraceComplete( @@ -38,9 +42,4 @@ private boolean isSupportedByApmProtocol(MutableSpan span) { && !DDSpanTypes.TEST_MODULE_END.equals(spanType) && !DDSpanTypes.TEST_SUITE_END.equals(spanType); } - - @Override - public int priority() { - return 1; - } } diff --git a/dd-trace-core/src/main/java/datadog/trace/civisibility/interceptor/CiVisibilityTraceInterceptor.java b/dd-trace-core/src/main/java/datadog/trace/civisibility/interceptor/CiVisibilityTraceInterceptor.java index 09a142dbcd3..bec1bdcd64d 100644 --- a/dd-trace-core/src/main/java/datadog/trace/civisibility/interceptor/CiVisibilityTraceInterceptor.java +++ b/dd-trace-core/src/main/java/datadog/trace/civisibility/interceptor/CiVisibilityTraceInterceptor.java @@ -2,20 +2,25 @@ import datadog.trace.api.DDSpanTypes; import datadog.trace.api.DDTags; +import datadog.trace.api.interceptor.AbstractTraceInterceptor; import datadog.trace.api.interceptor.MutableSpan; -import datadog.trace.api.interceptor.TraceInterceptor; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.core.DDSpan; import datadog.trace.core.DDTraceCoreInfo; import java.util.Collection; import java.util.Collections; -public class CiVisibilityTraceInterceptor implements TraceInterceptor { +public class CiVisibilityTraceInterceptor extends AbstractTraceInterceptor { - public static final CiVisibilityTraceInterceptor INSTANCE = new CiVisibilityTraceInterceptor(); + public static final CiVisibilityTraceInterceptor INSTANCE = + new CiVisibilityTraceInterceptor(Priority.CI_VISIBILITY_TRACE); static final UTF8BytesString CIAPP_TEST_ORIGIN = UTF8BytesString.create("ciapp-test"); + protected CiVisibilityTraceInterceptor(Priority priority) { + super(priority); + } + @Override public Collection onTraceComplete( Collection trace) { @@ -48,9 +53,4 @@ public Collection onTraceComplete( return trace; } - - @Override - public int priority() { - return 0; - } } diff --git a/dd-trace-core/src/main/java/datadog/trace/common/GitMetadataTraceInterceptor.java b/dd-trace-core/src/main/java/datadog/trace/common/GitMetadataTraceInterceptor.java new file mode 100644 index 00000000000..584d79f8ffb --- /dev/null +++ b/dd-trace-core/src/main/java/datadog/trace/common/GitMetadataTraceInterceptor.java @@ -0,0 +1,38 @@ +package datadog.trace.common; + +import datadog.trace.api.DDTags; +import datadog.trace.api.git.GitInfo; +import datadog.trace.api.git.GitInfoProvider; +import datadog.trace.api.interceptor.AbstractTraceInterceptor; +import datadog.trace.api.interceptor.MutableSpan; +import datadog.trace.api.interceptor.TraceInterceptor; +import datadog.trace.bootstrap.instrumentation.api.Tags; +import datadog.trace.core.DDSpan; +import java.util.Collection; + +public class GitMetadataTraceInterceptor extends AbstractTraceInterceptor { + + public static final TraceInterceptor INSTANCE = + new GitMetadataTraceInterceptor(Priority.GIT_METADATA); + + protected GitMetadataTraceInterceptor(Priority priority) { + super(priority); + } + + @Override + public Collection onTraceComplete( + Collection trace) { + if (trace.isEmpty()) { + return trace; + } + + final DDSpan firstSpan = (DDSpan) trace.iterator().next(); + String ciWorkspacePath = (String) firstSpan.getTag(Tags.CI_WORKSPACE_PATH); + + GitInfo gitInfo = GitInfoProvider.INSTANCE.getGitInfo(ciWorkspacePath); + firstSpan.setTag(DDTags.INTERNAL_GIT_REPOSITORY_URL, gitInfo.getRepositoryURL()); + firstSpan.setTag(DDTags.INTERNAL_GIT_COMMIT_SHA, gitInfo.getCommit().getSha()); + + return trace; + } +} diff --git a/dd-trace-core/src/main/java/datadog/trace/common/writer/ddintake/DDIntakeTraceInterceptor.java b/dd-trace-core/src/main/java/datadog/trace/common/writer/ddintake/DDIntakeTraceInterceptor.java index 5d42fa8feea..5c2cfae9d40 100644 --- a/dd-trace-core/src/main/java/datadog/trace/common/writer/ddintake/DDIntakeTraceInterceptor.java +++ b/dd-trace-core/src/main/java/datadog/trace/common/writer/ddintake/DDIntakeTraceInterceptor.java @@ -5,8 +5,8 @@ import static datadog.trace.util.TraceUtils.normalizeServiceName; import static datadog.trace.util.TraceUtils.normalizeSpanType; +import datadog.trace.api.interceptor.AbstractTraceInterceptor; import datadog.trace.api.interceptor.MutableSpan; -import datadog.trace.api.interceptor.TraceInterceptor; import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.core.DDSpan; import datadog.trace.util.TraceUtils; @@ -14,12 +14,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class DDIntakeTraceInterceptor implements TraceInterceptor { +public class DDIntakeTraceInterceptor extends AbstractTraceInterceptor { - public static final DDIntakeTraceInterceptor INSTANCE = new DDIntakeTraceInterceptor(); + public static final DDIntakeTraceInterceptor INSTANCE = + new DDIntakeTraceInterceptor(Priority.DD_INTAKE); private static final Logger log = LoggerFactory.getLogger(DDIntakeTraceInterceptor.class); + protected DDIntakeTraceInterceptor(Priority priority) { + super(priority); + } + @Override public Collection onTraceComplete( Collection trace) { @@ -59,9 +64,4 @@ private void process(DDSpan span) { span.setHttpStatusCode(0); } } - - @Override - public int priority() { - return 1; - } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java index bb3a79baf2e..d675cd5823e 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java @@ -53,6 +53,7 @@ import datadog.trace.bootstrap.instrumentation.api.TagContext; import datadog.trace.civisibility.interceptor.CiVisibilityApmProtocolInterceptor; import datadog.trace.civisibility.interceptor.CiVisibilityTraceInterceptor; +import datadog.trace.common.GitMetadataTraceInterceptor; import datadog.trace.common.metrics.MetricsAggregator; import datadog.trace.common.sampling.PrioritySampler; import datadog.trace.common.sampling.Sampler; @@ -460,12 +461,12 @@ private CoreTracer( assert baggageMapping != null; this.timeSource = timeSource == null ? SystemTimeSource.INSTANCE : timeSource; - this.startTimeNano = this.timeSource.getCurrentTimeNanos(); - this.startNanoTicks = this.timeSource.getNanoTicks(); - this.clockSyncPeriod = Math.max(1_000_000L, SECONDS.toNanos(config.getClockSyncPeriod())); - this.lastSyncTicks = startNanoTicks; + startTimeNano = this.timeSource.getCurrentTimeNanos(); + startNanoTicks = this.timeSource.getNanoTicks(); + clockSyncPeriod = Math.max(1_000_000L, SECONDS.toNanos(config.getClockSyncPeriod())); + lastSyncTicks = startNanoTicks; - this.endpointCheckpointer = EndpointCheckpointerHolder.create(); + endpointCheckpointer = EndpointCheckpointerHolder.create(); this.serviceName = serviceName; this.sampler = sampler; this.injector = injector; @@ -488,21 +489,21 @@ private CoreTracer( this.statsDClient = StatsDClient.NO_OP; } - this.monitoring = + monitoring = config.isHealthMetricsEnabled() ? new MonitoringImpl(this.statsDClient, 10, SECONDS) : Monitoring.DISABLED; - this.healthMetrics = + healthMetrics = config.isHealthMetricsEnabled() ? new TracerHealthMetrics(this.statsDClient) : HealthMetrics.NO_OP; - this.healthMetrics.start(); - this.performanceMonitoring = + healthMetrics.start(); + performanceMonitoring = config.isPerfMetricsEnabled() ? new MonitoringImpl(this.statsDClient, 10, SECONDS) : Monitoring.DISABLED; - this.traceWriteTimer = performanceMonitoring.newThreadLocalTimer("trace.write"); + traceWriteTimer = performanceMonitoring.newThreadLocalTimer("trace.write"); if (scopeManager == null) { this.scopeManager = new ContinuableScopeManager( @@ -510,14 +511,14 @@ private CoreTracer( config.isScopeStrictMode(), config.isScopeInheritAsyncPropagation(), profilingContextIntegration, - this.healthMetrics); + healthMetrics); } else { this.scopeManager = scopeManager; } - this.externalAgentLauncher = new ExternalAgentLauncher(config); + externalAgentLauncher = new ExternalAgentLauncher(config); - this.disableSamplingMechanismValidation = config.isSamplingMechanismValidationDisabled(); + disableSamplingMechanismValidation = config.isSamplingMechanismValidationDisabled(); if (sharedCommunicationObjects == null) { sharedCommunicationObjects = new SharedCommunicationObjects(); @@ -533,7 +534,7 @@ private CoreTracer( this.writer = writer; } - this.pendingTraceBuffer = + pendingTraceBuffer = strictTraceWrites ? PendingTraceBuffer.discarding() : PendingTraceBuffer.delaying(this.timeSource); @@ -576,11 +577,14 @@ private CoreTracer( } } + if (config.isTraceGitMetadataEnabled()) { + addTraceInterceptor(GitMetadataTraceInterceptor.INSTANCE); + } + this.instrumentationGateway = instrumentationGateway; - this.callbackProviderAppSec = - instrumentationGateway.getCallbackProvider(RequestContextSlot.APPSEC); - this.callbackProviderIast = instrumentationGateway.getCallbackProvider(RequestContextSlot.IAST); - this.universalCallbackProvider = instrumentationGateway.getUniversalCallbackProvider(); + callbackProviderAppSec = instrumentationGateway.getCallbackProvider(RequestContextSlot.APPSEC); + callbackProviderIast = instrumentationGateway.getCallbackProvider(RequestContextSlot.IAST); + universalCallbackProvider = instrumentationGateway.getUniversalCallbackProvider(); injectors = HttpCodec.allInjectorsFor(config, invertMap(baggageMapping)); @@ -931,7 +935,6 @@ private List interceptCompleteTrace(List trace) { return trace; } - @SuppressWarnings("unchecked") void setSamplingPriorityIfNecessary(final DDSpan rootSpan) { // There's a race where multiple threads can see PrioritySampling.UNSET here // This check skips potential complex sampling priority logic when we know its redundant @@ -965,7 +968,24 @@ public String getSpanId() { @Override public boolean addTraceInterceptor(final TraceInterceptor interceptor) { - return interceptors.add(interceptor); + if (interceptors.add(interceptor)) { + return true; + } else { + Comparator interceptorComparator = interceptors.comparator(); + if (interceptorComparator != null) { + TraceInterceptor anotherInterceptor = + interceptors.stream() + .filter(i -> interceptorComparator.compare(i, interceptor) == 0) + .findFirst() + .orElse(null); + log.warn( + "Interceptor {} will NOT be registered with the tracer, " + + "as already registered interceptor {} is considered its duplicate", + interceptor, + anotherInterceptor); + } + return false; + } } @Override @@ -977,12 +997,12 @@ public void addScopeListener(final ScopeListener listener) { @Override public void registerCheckpointer(EndpointCheckpointer implementation) { - this.endpointCheckpointer.register(implementation); + endpointCheckpointer.register(implementation); } @Override public SubscriptionService getSubscriptionService(RequestContextSlot slot) { - return (SubscriptionService) this.instrumentationGateway.getCallbackProvider(slot); + return (SubscriptionService) instrumentationGateway.getCallbackProvider(slot); } @Override @@ -1050,7 +1070,7 @@ public Profiling getProfilingContext() { @Override public TraceSegment getTraceSegment() { - AgentSpan.Context ctx = this.activeSpan().context(); + AgentSpan.Context ctx = activeSpan().context(); if (ctx instanceof DDSpanContext) { return ((DDSpanContext) ctx).getTraceSegment(); } diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/TraceInterceptorTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/TraceInterceptorTest.groovy index b77e21e695a..e9bf46448f7 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/TraceInterceptorTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/TraceInterceptorTest.groovy @@ -2,6 +2,7 @@ package datadog.trace.core import datadog.trace.TestInterceptor import datadog.trace.api.GlobalTracer +import datadog.trace.api.config.TracerConfig import datadog.trace.api.interceptor.MutableSpan import datadog.trace.api.interceptor.TraceInterceptor import datadog.trace.common.writer.ListWriter @@ -16,7 +17,12 @@ import java.util.concurrent.atomic.AtomicBoolean class TraceInterceptorTest extends DDCoreSpecification { def writer = new ListWriter() - def tracer = tracerBuilder().writer(writer).build() + def tracer + + def setup() { + injectSysConfig(TracerConfig.TRACE_GIT_METADATA_ENABLED, "false") + tracer = tracerBuilder().writer(writer).build() + } def cleanup() { tracer?.close() diff --git a/internal-api/build.gradle b/internal-api/build.gradle index 7df93cb3efa..72cb983b514 100644 --- a/internal-api/build.gradle +++ b/internal-api/build.gradle @@ -68,11 +68,35 @@ excludedClassesCoverage += [ "datadog.trace.api.civisibility.CIInfo", "datadog.trace.api.civisibility.CIInfo.Builder", "datadog.trace.api.civisibility.InstrumentationBridge", - "datadog.trace.api.civisibility.events.*", - "datadog.trace.api.civisibility.git.GitInfo", - "datadog.trace.api.civisibility.git.CommitInfo", - "datadog.trace.api.civisibility.git.PersonInfo", "datadog.trace.api.civisibility.source.MethodLinesResolver.MethodLines", + "datadog.trace.api.civisibility.events.impl.AbstractTestContext", + // POJO + "datadog.trace.api.civisibility.events.impl.BuildEventsHandlerImpl", + // tested with integration tests in instr modules + "datadog.trace.api.civisibility.events.impl.BuildEventsHandlerImpl.SessionContext", + // POJO + "datadog.trace.api.civisibility.events.impl.ParentProcessTestContext", + // POJO + "datadog.trace.api.civisibility.events.impl.SpanTestContext", + // POJO + "datadog.trace.api.civisibility.events.impl.TestEventsHandlerImpl", + // tested with integration tests in instr modules + "datadog.trace.api.civisibility.events.impl.TestModuleDescriptor", + // POJO + "datadog.trace.api.civisibility.events.impl.TestSuiteDescriptor", + // POJO + "datadog.trace.api.civisibility.events.BuildEventsHandler.ModuleAndSessionId", + // POJO + "datadog.trace.api.git.GitInfo", + // POJO + "datadog.trace.api.git.PersonInfo", + // POJO + "datadog.trace.api.git.CommitInfo", + // POJO + "datadog.trace.api.git.GitUtils", + // tested indirectly by dependent modules + "datadog.trace.api.git.RawParseUtils", + // tested indirectly by dependent modules "datadog.trace.logging.LoggingSettingsDescription", "datadog.trace.util.AgentProxySelector", "datadog.trace.util.AgentTaskScheduler", diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index ae07b71078e..fb0e83ce1ae 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -283,6 +283,7 @@ import static datadog.trace.api.config.TracerConfig.TRACE_ANALYTICS_ENABLED; import static datadog.trace.api.config.TracerConfig.TRACE_CLIENT_IP_HEADER; import static datadog.trace.api.config.TracerConfig.TRACE_CLIENT_IP_RESOLVER_ENABLED; +import static datadog.trace.api.config.TracerConfig.TRACE_GIT_METADATA_ENABLED; import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_CLIENT_PATH_RESOURCE_NAME_MAPPING; import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_RESOURCE_REMOVE_TRAILING_SLASH; import static datadog.trace.api.config.TracerConfig.TRACE_HTTP_SERVER_PATH_RESOURCE_NAME_MAPPING; @@ -484,6 +485,8 @@ static class HostNameHolder { private final String traceClientIpHeader; private final boolean traceClientIpResolverEnabled; + private final boolean traceGitMetadataEnabled; + private final Map traceSamplingServiceRules; private final Map traceSamplingOperationRules; private final String traceSamplingRules; @@ -1082,6 +1085,8 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins traceClientIpResolverEnabled = configProvider.getBoolean(TRACE_CLIENT_IP_RESOLVER_ENABLED, true); + traceGitMetadataEnabled = configProvider.getBoolean(TRACE_GIT_METADATA_ENABLED, true); + traceSamplingServiceRules = configProvider.getMergedMap(TRACE_SAMPLING_SERVICE_RULES); traceSamplingOperationRules = configProvider.getMergedMap(TRACE_SAMPLING_OPERATION_RULES); traceSamplingRules = configProvider.getString(TRACE_SAMPLING_RULES); @@ -1773,6 +1778,10 @@ public boolean isTraceClientIpResolverEnabled() { return traceClientIpResolverEnabled; } + public boolean isTraceGitMetadataEnabled() { + return traceGitMetadataEnabled; + } + public Map getTraceSamplingServiceRules() { return traceSamplingServiceRules; } diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/CIProviderInfo.java b/internal-api/src/main/java/datadog/trace/api/civisibility/CIProviderInfo.java index ff8c7fdafd2..1c7b00f4a23 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/CIProviderInfo.java +++ b/internal-api/src/main/java/datadog/trace/api/civisibility/CIProviderInfo.java @@ -1,6 +1,6 @@ package datadog.trace.api.civisibility; -import datadog.trace.api.civisibility.git.GitInfo; +import datadog.trace.api.git.GitInfo; import java.nio.file.Path; public interface CIProviderInfo { diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/git/CommitInfo.java b/internal-api/src/main/java/datadog/trace/api/git/CommitInfo.java similarity index 98% rename from internal-api/src/main/java/datadog/trace/api/civisibility/git/CommitInfo.java rename to internal-api/src/main/java/datadog/trace/api/git/CommitInfo.java index 91d6b4145d6..3cd7182a2df 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/git/CommitInfo.java +++ b/internal-api/src/main/java/datadog/trace/api/git/CommitInfo.java @@ -1,4 +1,4 @@ -package datadog.trace.api.civisibility.git; +package datadog.trace.api.git; import java.util.Objects; diff --git a/internal-api/src/main/java/datadog/trace/api/git/EmbeddedGitInfoBuilder.java b/internal-api/src/main/java/datadog/trace/api/git/EmbeddedGitInfoBuilder.java new file mode 100644 index 00000000000..b40a399ab30 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/git/EmbeddedGitInfoBuilder.java @@ -0,0 +1,70 @@ +package datadog.trace.api.git; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class EmbeddedGitInfoBuilder implements GitInfoBuilder { + + private static final Logger log = LoggerFactory.getLogger(EmbeddedGitInfoBuilder.class); + + private static final String EMBEDDED_GIT_PROPERTIES_FILE_NAME = "git.properties"; + + private final String resourceName; + + public EmbeddedGitInfoBuilder() { + this(EMBEDDED_GIT_PROPERTIES_FILE_NAME); + } + + EmbeddedGitInfoBuilder(String resourceName) { + this.resourceName = resourceName; + } + + @Override + public GitInfo build(@Nullable String repositoryPath) { + Properties gitProperties = new Properties(); + try (InputStream is = ClassLoader.getSystemResourceAsStream(resourceName)) { + if (is != null) { + gitProperties.load(is); + } else { + log.debug("Could not find embedded Git properties resource: {}", resourceName); + } + } catch (IOException e) { + log.error("Error reading embedded Git properties from {}", resourceName, e); + } + + String commitSha = gitProperties.getProperty("git.commit.id"); + if (commitSha == null) { + commitSha = gitProperties.getProperty("git.commit.id.full"); + } + + String committerTime = gitProperties.getProperty("git.commit.committer.time"); + if (committerTime == null) { + committerTime = gitProperties.getProperty("git.commit.time"); + } + + String authorTime = gitProperties.getProperty("git.commit.author.time"); + if (authorTime == null) { + authorTime = gitProperties.getProperty("git.commit.time"); + } + + return new GitInfo( + gitProperties.getProperty("git.remote.origin.url"), + gitProperties.getProperty("git.branch"), + gitProperties.getProperty("git.tags"), + new CommitInfo( + commitSha, + new PersonInfo( + gitProperties.getProperty("git.commit.user.name"), + gitProperties.getProperty("git.commit.user.email"), + authorTime), + new PersonInfo( + gitProperties.getProperty("git.commit.user.name"), + gitProperties.getProperty("git.commit.user.email"), + committerTime), + gitProperties.getProperty("git.commit.message.full"))); + } +} diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/git/GitInfo.java b/internal-api/src/main/java/datadog/trace/api/git/GitInfo.java similarity index 98% rename from internal-api/src/main/java/datadog/trace/api/civisibility/git/GitInfo.java rename to internal-api/src/main/java/datadog/trace/api/git/GitInfo.java index 601aac6ac92..4708d3b5021 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/git/GitInfo.java +++ b/internal-api/src/main/java/datadog/trace/api/git/GitInfo.java @@ -1,4 +1,4 @@ -package datadog.trace.api.civisibility.git; +package datadog.trace.api.git; import java.util.Objects; diff --git a/internal-api/src/main/java/datadog/trace/api/git/GitInfoBuilder.java b/internal-api/src/main/java/datadog/trace/api/git/GitInfoBuilder.java new file mode 100644 index 00000000000..e92a9f5514e --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/git/GitInfoBuilder.java @@ -0,0 +1,7 @@ +package datadog.trace.api.git; + +import javax.annotation.Nullable; + +public interface GitInfoBuilder { + GitInfo build(@Nullable String repositoryPath); +} diff --git a/internal-api/src/main/java/datadog/trace/api/git/GitInfoProvider.java b/internal-api/src/main/java/datadog/trace/api/git/GitInfoProvider.java new file mode 100644 index 00000000000..8bdd7468591 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/git/GitInfoProvider.java @@ -0,0 +1,106 @@ +package datadog.trace.api.git; + +import datadog.trace.api.cache.DDCache; +import datadog.trace.api.cache.DDCaches; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +public class GitInfoProvider { + + public static final GitInfoProvider INSTANCE; + + static { + INSTANCE = new GitInfoProvider(); + INSTANCE.registerGitInfoBuilder(new UserSuppliedGitInfoBuilder()); + INSTANCE.registerGitInfoBuilder(new EmbeddedGitInfoBuilder()); + } + + private final Collection builders = new CopyOnWriteArrayList<>(); + + // in regular cases git info has to be built only once, + // but there is a rare exception: + // when attaching to a Gradle Daemon, + // it is possible to have builds from multiple repositories + // executed in the same process; + // 4 is chosen somewhat randomly, since we want to make memory footprint small + // and having more than 4 builds from different repos running in parallel + // in the same daemon is unlikely + private final DDCache gitInfoCache = DDCaches.newFixedSizeCache(4); + + public GitInfo getGitInfo() { + return getGitInfo(null); + } + + public GitInfo getGitInfo(@Nullable String repositoryPath) { + if (repositoryPath == null) { + repositoryPath = Paths.get("").toAbsolutePath().toString(); + } + return gitInfoCache.computeIfAbsent(repositoryPath, this::buildGitInfo); + } + + private GitInfo buildGitInfo(String repositoryPath) { + List infos = + builders.stream() + .map(builder -> builder.build(repositoryPath)) + .collect(Collectors.toList()); + + String commitSha = firstNonNull(infos, gi -> gi.getCommit().getSha()); + return new GitInfo( + firstNonNull(infos, GitInfo::getRepositoryURL), + firstNonNull(infos, GitInfo::getBranch), + firstNonNull(infos, GitInfo::getTag), + new CommitInfo( + commitSha, + new PersonInfo( + firstNonNullWithMatchingCommit( + infos, commitSha, gi -> gi.getCommit().getAuthor().getName()), + firstNonNullWithMatchingCommit( + infos, commitSha, gi -> gi.getCommit().getAuthor().getEmail()), + firstNonNullWithMatchingCommit( + infos, commitSha, gi -> gi.getCommit().getAuthor().getIso8601Date())), + new PersonInfo( + firstNonNullWithMatchingCommit( + infos, commitSha, gi -> gi.getCommit().getCommitter().getName()), + firstNonNullWithMatchingCommit( + infos, commitSha, gi -> gi.getCommit().getCommitter().getEmail()), + firstNonNullWithMatchingCommit( + infos, commitSha, gi -> gi.getCommit().getCommitter().getIso8601Date())), + firstNonNullWithMatchingCommit( + infos, commitSha, gi -> gi.getCommit().getFullMessage()))); + } + + private static String firstNonNull( + Iterable gitInfos, Function function) { + for (GitInfo gitInfo : gitInfos) { + String result = function.apply(gitInfo); + if (result != null) { + return result; + } + } + return null; + } + + private static String firstNonNullWithMatchingCommit( + Iterable gitInfos, String commitSha, Function function) { + for (GitInfo gitInfo : gitInfos) { + if (commitSha != null && !commitSha.equalsIgnoreCase(gitInfo.getCommit().getSha())) { + continue; + } + String result = function.apply(gitInfo); + if (result != null) { + return result; + } + } + return null; + } + + public void registerGitInfoBuilder(GitInfoBuilder builder) { + builders.add(builder); + gitInfoCache.clear(); + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/GitUtils.java b/internal-api/src/main/java/datadog/trace/api/git/GitUtils.java similarity index 95% rename from dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/GitUtils.java rename to internal-api/src/main/java/datadog/trace/api/git/GitUtils.java index a82428775ea..ed861b25f3c 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/GitUtils.java +++ b/internal-api/src/main/java/datadog/trace/api/git/GitUtils.java @@ -1,9 +1,8 @@ -package datadog.trace.civisibility.git; +package datadog.trace.api.git; -import static datadog.trace.civisibility.git.RawParseUtils.decode; -import static datadog.trace.civisibility.git.RawParseUtils.nextLF; +import static datadog.trace.api.git.RawParseUtils.decode; +import static datadog.trace.api.git.RawParseUtils.nextLF; -import datadog.trace.api.civisibility.git.PersonInfo; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URI; @@ -22,7 +21,7 @@ public class GitUtils { private static final Pattern REFS_TAGS_PATTERN = Pattern.compile("refs/tags/", Pattern.LITERAL); private static final Pattern TAGS_PATTERN = Pattern.compile("tags/", Pattern.LITERAL); - private static final Logger log = LoggerFactory.getLogger(LocalFSGitInfoExtractor.class); + private static final Logger log = LoggerFactory.getLogger(GitUtils.class); /** * Normalizes Git tag references: diff --git a/internal-api/src/main/java/datadog/trace/api/civisibility/git/PersonInfo.java b/internal-api/src/main/java/datadog/trace/api/git/PersonInfo.java similarity index 98% rename from internal-api/src/main/java/datadog/trace/api/civisibility/git/PersonInfo.java rename to internal-api/src/main/java/datadog/trace/api/git/PersonInfo.java index b7fd62b77b2..3b1019f894e 100644 --- a/internal-api/src/main/java/datadog/trace/api/civisibility/git/PersonInfo.java +++ b/internal-api/src/main/java/datadog/trace/api/git/PersonInfo.java @@ -1,4 +1,4 @@ -package datadog.trace.api.civisibility.git; +package datadog.trace.api.git; import java.text.SimpleDateFormat; import java.util.Date; diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/RawParseUtils.java b/internal-api/src/main/java/datadog/trace/api/git/RawParseUtils.java similarity index 99% rename from dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/RawParseUtils.java rename to internal-api/src/main/java/datadog/trace/api/git/RawParseUtils.java index 369152a95b1..40d91a2634f 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/git/RawParseUtils.java +++ b/internal-api/src/main/java/datadog/trace/api/git/RawParseUtils.java @@ -1,4 +1,4 @@ -package datadog.trace.civisibility.git; +package datadog.trace.api.git; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java b/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java new file mode 100644 index 00000000000..e4b12e2666f --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java @@ -0,0 +1,96 @@ +package datadog.trace.api.git; + +import static datadog.trace.api.git.GitInfo.DD_GIT_BRANCH; +import static datadog.trace.api.git.GitInfo.DD_GIT_COMMIT_AUTHOR_DATE; +import static datadog.trace.api.git.GitInfo.DD_GIT_COMMIT_AUTHOR_EMAIL; +import static datadog.trace.api.git.GitInfo.DD_GIT_COMMIT_AUTHOR_NAME; +import static datadog.trace.api.git.GitInfo.DD_GIT_COMMIT_COMMITTER_DATE; +import static datadog.trace.api.git.GitInfo.DD_GIT_COMMIT_COMMITTER_EMAIL; +import static datadog.trace.api.git.GitInfo.DD_GIT_COMMIT_COMMITTER_NAME; +import static datadog.trace.api.git.GitInfo.DD_GIT_COMMIT_MESSAGE; +import static datadog.trace.api.git.GitInfo.DD_GIT_COMMIT_SHA; +import static datadog.trace.api.git.GitInfo.DD_GIT_REPOSITORY_URL; +import static datadog.trace.api.git.GitInfo.DD_GIT_TAG; + +import datadog.trace.api.Config; +import datadog.trace.bootstrap.instrumentation.api.Tags; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class UserSuppliedGitInfoBuilder implements GitInfoBuilder { + + private static final Logger log = LoggerFactory.getLogger(UserSuppliedGitInfoBuilder.class); + + @Override + public GitInfo build(@Nullable String repositoryPath) { + String gitRepositoryUrl = System.getenv(DD_GIT_REPOSITORY_URL); + if (gitRepositoryUrl == null) { + gitRepositoryUrl = Config.get().getGlobalTags().get(Tags.GIT_REPOSITORY_URL); + } + + // The user can set the DD_GIT_BRANCH manually but + // using the value returned by the CI Provider, so + // we need to normalize the value. Also, it can contain + // the tag (e.g. origin/tags/0.1.0) + String gitTag = System.getenv(DD_GIT_TAG); + String gitBranch = null; + final String gitBranchOrTag = System.getenv(DD_GIT_BRANCH); + if (gitBranchOrTag != null) { + if (!GitUtils.isTagReference(gitBranchOrTag)) { + gitBranch = GitUtils.normalizeBranch(gitBranchOrTag); + } else if (gitTag == null) { + gitTag = GitUtils.normalizeTag(gitBranchOrTag); + } + } + + String gitCommitSha = System.getenv(DD_GIT_COMMIT_SHA); + if (gitCommitSha == null) { + gitCommitSha = Config.get().getGlobalTags().get(Tags.GIT_COMMIT_SHA); + } + + final String gitCommitMessage = System.getenv(DD_GIT_COMMIT_MESSAGE); + final String gitCommitAuthorName = System.getenv(DD_GIT_COMMIT_AUTHOR_NAME); + final String gitCommitAuthorEmail = System.getenv(DD_GIT_COMMIT_AUTHOR_EMAIL); + final String gitCommitAuthorDate = System.getenv(DD_GIT_COMMIT_AUTHOR_DATE); + final String gitCommitCommitterName = System.getenv(DD_GIT_COMMIT_COMMITTER_NAME); + final String gitCommitCommitterEmail = System.getenv(DD_GIT_COMMIT_COMMITTER_EMAIL); + final String gitCommitCommitterDate = System.getenv(DD_GIT_COMMIT_COMMITTER_DATE); + + GitInfo gitInfo = + new GitInfo( + gitRepositoryUrl, + gitBranch, + gitTag, + new CommitInfo( + gitCommitSha, + new PersonInfo(gitCommitAuthorName, gitCommitAuthorEmail, gitCommitAuthorDate), + new PersonInfo( + gitCommitCommitterName, gitCommitCommitterEmail, gitCommitCommitterDate), + gitCommitMessage)); + + if (!gitInfo.isEmpty()) { + // if there is any git metadata supplied by the user, we want to check that repo URL and + // commit SHA are valid + String repoUrl = gitInfo.getRepositoryURL(); + if (repoUrl == null || repoUrl.isEmpty()) { + log.error( + "Could not resolve git repository URL (can be provided via " + + GitInfo.DD_GIT_REPOSITORY_URL + + " env var)"); + } + + String commitSha = gitInfo.getCommit().getSha(); + if (!GitUtils.isValidCommitSha(commitSha)) { + log.error( + "Git commit SHA could not be resolved or is invalid: " + + commitSha + + " (can be provided via " + + GitInfo.DD_GIT_COMMIT_SHA + + " env var, and must be a full-length git SHA)"); + } + } + + return gitInfo; + } +} diff --git a/internal-api/src/test/groovy/datadog/trace/api/git/EmbeddedGitInfoBuilderTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/git/EmbeddedGitInfoBuilderTest.groovy new file mode 100644 index 00000000000..222c1715b07 --- /dev/null +++ b/internal-api/src/test/groovy/datadog/trace/api/git/EmbeddedGitInfoBuilderTest.groovy @@ -0,0 +1,53 @@ +package datadog.trace.api.git + + +import spock.lang.Specification + +class EmbeddedGitInfoBuilderTest extends Specification { + + def "test no embedded git info"() { + when: + def gitInfo = new EmbeddedGitInfoBuilder("non-existent-git.properties").build(null) + + then: + gitInfo.isEmpty() + } + + def "test maven-plugin-generated git info"() { + when: + def mavenGitProperties = "datadog/trace/bootstrap/git/maven-git.properties" + def gitInfo = new EmbeddedGitInfoBuilder(mavenGitProperties).build(null) + + then: + gitInfo.repositoryURL == "git@github.com:DataDog/ciapp-test-resources.git" + gitInfo.branch == "master" + gitInfo.tag == "test_tag" + gitInfo.commit.sha == "847416a0f14356609ef2a2e0bf19b8a7222c250a" + gitInfo.commit.fullMessage == "Add integration test in Java Gradle JUnit 5 project" + gitInfo.commit.author.name == "Nikita Tkachenko" + gitInfo.commit.author.email == "nikita.tkachenko@datadoghq.com" + gitInfo.commit.author.iso8601Date == "2023-03-22T14:43:22+0100" + gitInfo.commit.committer.name == "Nikita Tkachenko" + gitInfo.commit.committer.email == "nikita.tkachenko@datadoghq.com" + gitInfo.commit.committer.iso8601Date == "2023-03-22T14:43:23+0100" + } + + def "test gradle-plugin-generated git info"() { + when: + def gradleGitProperties = "datadog/trace/bootstrap/git/gradle-git.properties" + def gitInfo = new EmbeddedGitInfoBuilder(gradleGitProperties).build(null) + + then: + gitInfo.repositoryURL == "git@github.com:DataDog/ciapp-test-resources.git" + gitInfo.branch == "master" + gitInfo.tag == "test_tag" + gitInfo.commit.sha == "847416a0f14356609ef2a2e0bf19b8a7222c250a" + gitInfo.commit.fullMessage == "Add integration test in Java Gradle JUnit 5 project\n" + gitInfo.commit.author.name == "Nikita Tkachenko" + gitInfo.commit.author.email == "nikita.tkachenko@datadoghq.com" + gitInfo.commit.author.iso8601Date == "2023-03-22T14:43:21+0100" + gitInfo.commit.committer.name == "Nikita Tkachenko" + gitInfo.commit.committer.email == "nikita.tkachenko@datadoghq.com" + gitInfo.commit.committer.iso8601Date == "2023-03-22T14:43:21+0100" + } +} diff --git a/internal-api/src/test/groovy/datadog/trace/api/git/GitInfoProviderTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/git/GitInfoProviderTest.groovy new file mode 100644 index 00000000000..a8cf2c39226 --- /dev/null +++ b/internal-api/src/test/groovy/datadog/trace/api/git/GitInfoProviderTest.groovy @@ -0,0 +1,127 @@ +package datadog.trace.api.git + +import spock.lang.Specification + +class GitInfoProviderTest extends Specification { + + private static final String REPO_PATH = "/repo/path" + + def "test delegates to GitInfoBuilder"() { + setup: + def gitInfoBuilder = givenABuilderReturning( + new GitInfo("repoUrl", "branch", "tag", new CommitInfo("sha")) + ) + + def gitInfoProvider = new GitInfoProvider() + gitInfoProvider.registerGitInfoBuilder(gitInfoBuilder) + + when: + def actualGitInfo = gitInfoProvider.getGitInfo(REPO_PATH) + + then: + actualGitInfo.repositoryURL == "repoUrl" + actualGitInfo.branch == "branch" + actualGitInfo.tag == "tag" + actualGitInfo.commit.sha == "sha" + } + + def "test falls back to the second GitInfoBuilder"() { + setup: + def gitInfoBuilderA = givenABuilderReturning( + new GitInfo("repoUrl", null, null, new CommitInfo(null)) + ) + + def gitInfoBuilderB = givenABuilderReturning( + new GitInfo(null, "branch", "tag", new CommitInfo("sha")) + ) + + def gitInfoProvider = new GitInfoProvider() + gitInfoProvider.registerGitInfoBuilder(gitInfoBuilderA) + gitInfoProvider.registerGitInfoBuilder(gitInfoBuilderB) + + when: + def actualGitInfo = gitInfoProvider.getGitInfo(REPO_PATH) + + then: + actualGitInfo.repositoryURL == "repoUrl" + actualGitInfo.branch == "branch" + actualGitInfo.tag == "tag" + actualGitInfo.commit.sha == "sha" + } + + def "test falls back to the second GitInfoBuilder for commit info"() { + setup: + def gitInfoBuilderA = givenABuilderReturning( + new GitInfo("repoUrl", null, null, + new CommitInfo("sha", + PersonInfo.NOOP, + PersonInfo.NOOP, + "message"))) + + def gitInfoBuilderB = givenABuilderReturning( + new GitInfo("repoUrl", null, null, + new CommitInfo("sha", + new PersonInfo("author name", "author email", "author date"), + new PersonInfo("committer name", "committer email", "committer date"), + "message"))) + + def gitInfoProvider = new GitInfoProvider() + gitInfoProvider.registerGitInfoBuilder(gitInfoBuilderA) + gitInfoProvider.registerGitInfoBuilder(gitInfoBuilderB) + + when: + def actualGitInfo = gitInfoProvider.getGitInfo(REPO_PATH) + + then: + actualGitInfo.repositoryURL == "repoUrl" + actualGitInfo.commit.sha == "sha" + actualGitInfo.commit.fullMessage == "message" + actualGitInfo.commit.author.name == "author name" + actualGitInfo.commit.author.email == "author email" + actualGitInfo.commit.author.iso8601Date == "author date" + actualGitInfo.commit.committer.name == "committer name" + actualGitInfo.commit.committer.email == "committer email" + actualGitInfo.commit.committer.iso8601Date == "committer date" + } + + def "test does not falls back to the second GitInfoBuilder for commit info if SHAs do not match"() { + setup: + def gitInfoBuilderA = givenABuilderReturning( + new GitInfo("repoUrl", null, null, + new CommitInfo("sha", + PersonInfo.NOOP, + PersonInfo.NOOP, + "message"))) + + def gitInfoBuilderB = givenABuilderReturning( + new GitInfo("repoUrl", null, null, + new CommitInfo("different sha", + new PersonInfo("author name", "author email", "author date"), + new PersonInfo("committer name", "committer email", "committer date"), + "message"))) + + def gitInfoProvider = new GitInfoProvider() + gitInfoProvider.registerGitInfoBuilder(gitInfoBuilderA) + gitInfoProvider.registerGitInfoBuilder(gitInfoBuilderB) + + when: + def actualGitInfo = gitInfoProvider.getGitInfo(REPO_PATH) + + then: + actualGitInfo.repositoryURL == "repoUrl" + actualGitInfo.commit.sha == "sha" + actualGitInfo.commit.fullMessage == "message" + actualGitInfo.commit.author.name == null + actualGitInfo.commit.author.email == null + actualGitInfo.commit.author.iso8601Date == null + actualGitInfo.commit.committer.name == null + actualGitInfo.commit.committer.email == null + actualGitInfo.commit.committer.iso8601Date == null + } + + private GitInfoBuilder givenABuilderReturning(GitInfo gitInfo) { + def gitInfoBuilder = Stub(GitInfoBuilder) + gitInfoBuilder.build(REPO_PATH) >> gitInfo + return gitInfoBuilder + } +} diff --git a/internal-api/src/test/groovy/datadog/trace/api/civisibility/git/GitInfoTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/git/GitInfoTest.groovy similarity index 96% rename from internal-api/src/test/groovy/datadog/trace/api/civisibility/git/GitInfoTest.groovy rename to internal-api/src/test/groovy/datadog/trace/api/git/GitInfoTest.groovy index 903594dea0d..ed1510555f3 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/civisibility/git/GitInfoTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/git/GitInfoTest.groovy @@ -1,4 +1,4 @@ -package datadog.trace.api.civisibility.git +package datadog.trace.api.git import datadog.trace.test.util.DDSpecification diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/GitUtilsTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/git/GitUtilsTest.groovy similarity index 86% rename from dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/GitUtilsTest.groovy rename to internal-api/src/test/groovy/datadog/trace/api/git/GitUtilsTest.groovy index 63693b29ec1..6a0a1e0dc8c 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/GitUtilsTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/git/GitUtilsTest.groovy @@ -1,9 +1,9 @@ -package datadog.trace.civisibility.git +package datadog.trace.api.git -import datadog.trace.api.civisibility.git.PersonInfo -import datadog.trace.test.util.DDSpecification -class GitUtilsTest extends DDSpecification { +import spock.lang.Specification + +class GitUtilsTest extends Specification { static janeDoePersonInfo = new PersonInfo("Jane Doe", "jane.doe@email.com") diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/RawParseUtilsTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/git/RawParseUtilsTest.groovy similarity index 89% rename from dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/RawParseUtilsTest.groovy rename to internal-api/src/test/groovy/datadog/trace/api/git/RawParseUtilsTest.groovy index 019c2b58501..f262a5c127b 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/RawParseUtilsTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/git/RawParseUtilsTest.groovy @@ -1,11 +1,11 @@ -package datadog.trace.civisibility.git +package datadog.trace.api.git -import datadog.trace.test.util.DDSpecification +import spock.lang.Specification import java.nio.charset.StandardCharsets -class RawParseUtilsTest extends DDSpecification { +class RawParseUtilsTest extends Specification { def "test decode no fallback"() { setup: diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/UserSuppliedGitInfoBuilderTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/git/UserSuppliedGitInfoBuilderTest.groovy similarity index 85% rename from dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/UserSuppliedGitInfoBuilderTest.groovy rename to internal-api/src/test/groovy/datadog/trace/api/git/UserSuppliedGitInfoBuilderTest.groovy index 0f8f2bf0a17..1f962a2e81d 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/UserSuppliedGitInfoBuilderTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/git/UserSuppliedGitInfoBuilderTest.groovy @@ -1,7 +1,6 @@ -package datadog.trace.civisibility +package datadog.trace.api.git + -import datadog.trace.api.civisibility.git.GitInfo -import datadog.trace.civisibility.git.info.UserSuppliedGitInfoBuilder import org.junit.Rule import org.junit.contrib.java.lang.system.EnvironmentVariables import spock.lang.Specification @@ -18,7 +17,7 @@ class UserSuppliedGitInfoBuilderTest extends Specification { def "test no user supplied git info"() { when: - def gitInfo = new UserSuppliedGitInfoBuilder().build() + def gitInfo = new UserSuppliedGitInfoBuilder().build(null) then: gitInfo.isEmpty() @@ -29,7 +28,7 @@ class UserSuppliedGitInfoBuilderTest extends Specification { environmentVariables.set(envVariable, value) when: - def gitInfo = new UserSuppliedGitInfoBuilder().build() + def gitInfo = new UserSuppliedGitInfoBuilder().build(null) then: !gitInfo.isEmpty() @@ -55,7 +54,7 @@ class UserSuppliedGitInfoBuilderTest extends Specification { environmentVariables.set(GitInfo.DD_GIT_BRANCH, "origin/myBranch") when: - def gitInfo = new UserSuppliedGitInfoBuilder().build() + def gitInfo = new UserSuppliedGitInfoBuilder().build(null) then: !gitInfo.isEmpty() @@ -67,7 +66,7 @@ class UserSuppliedGitInfoBuilderTest extends Specification { environmentVariables.set(GitInfo.DD_GIT_BRANCH, "refs/tags/myTag") when: - def gitInfo = new UserSuppliedGitInfoBuilder().build() + def gitInfo = new UserSuppliedGitInfoBuilder().build(null) then: !gitInfo.isEmpty() @@ -81,7 +80,7 @@ class UserSuppliedGitInfoBuilderTest extends Specification { environmentVariables.set(GitInfo.DD_GIT_BRANCH, "refs/tags/myTag") when: - def gitInfo = new UserSuppliedGitInfoBuilder().build() + def gitInfo = new UserSuppliedGitInfoBuilder().build(null) then: !gitInfo.isEmpty() diff --git a/internal-api/src/test/resources/datadog/trace/bootstrap/git/gradle-git.properties b/internal-api/src/test/resources/datadog/trace/bootstrap/git/gradle-git.properties new file mode 100644 index 00000000000..1930b2d3288 --- /dev/null +++ b/internal-api/src/test/resources/datadog/trace/bootstrap/git/gradle-git.properties @@ -0,0 +1,19 @@ +git.branch=master +git.build.host=COMP-QG19F24WGY +git.build.user.email=nikita.tkachenko@datadoghq.com +git.build.user.name= +git.build.version=unspecified +git.closest.tag.commit.count=0 +git.closest.tag.name=test_taf +git.commit.id=847416a0f14356609ef2a2e0bf19b8a7222c250a +git.commit.id.abbrev=847416a +git.commit.id.describe=test_taf-dirty +git.commit.message.full=Add integration test in Java Gradle JUnit 5 project\n +git.commit.message.short=Add integration test in Java Gradle JUnit 5 project +git.commit.time=2023-03-22T14\:43\:21+0100 +git.commit.user.email=nikita.tkachenko@datadoghq.com +git.commit.user.name=Nikita Tkachenko +git.dirty=true +git.remote.origin.url=git@github.com\:DataDog/ciapp-test-resources.git +git.tags=test_tag +git.total.commit.count=8 diff --git a/internal-api/src/test/resources/datadog/trace/bootstrap/git/maven-git.properties b/internal-api/src/test/resources/datadog/trace/bootstrap/git/maven-git.properties new file mode 100644 index 00000000000..7c66513c6e5 --- /dev/null +++ b/internal-api/src/test/resources/datadog/trace/bootstrap/git/maven-git.properties @@ -0,0 +1,26 @@ +#Generated by Git-Commit-Id-Plugin +git.branch=master +git.build.host=COMP-QG19F24WGY +git.build.time=2023-03-23T16\:29\:19+0100 +git.build.user.email=nikita.tkachenko@datadoghq.com +git.build.user.name= +git.build.version=1.0-SNAPSHOT +git.closest.tag.commit.count= +git.closest.tag.name= +git.commit.author.time=2023-03-22T14\:43\:22+0100 +git.commit.committer.time=2023-03-22T14\:43\:23+0100 +git.commit.id.abbrev=847416a +git.commit.id.describe=847416a-dirty +git.commit.id.describe-short=847416a-dirty +git.commit.id.full=847416a0f14356609ef2a2e0bf19b8a7222c250a +git.commit.message.full=Add integration test in Java Gradle JUnit 5 project +git.commit.message.short=Add integration test in Java Gradle JUnit 5 project +git.commit.time=2023-03-22T14\:43\:21+0100 +git.commit.user.email=nikita.tkachenko@datadoghq.com +git.commit.user.name=Nikita Tkachenko +git.dirty=true +git.local.branch.ahead=0 +git.local.branch.behind=0 +git.remote.origin.url=git@github.com\:DataDog/ciapp-test-resources.git +git.tags=test_tag +git.total.commit.count=8 From 3932d406fdc3a05a142dfcf298942cd9fe3c2c46 Mon Sep 17 00:00:00 2001 From: Nikita Tkachenko Date: Fri, 31 Mar 2023 17:54:38 +0200 Subject: [PATCH 2/3] Address review comments --- .../git/CILocalGitInfoBuilderTest.groovy | 3 +- .../api/git/UserSuppliedGitInfoBuilder.java | 5 ++- .../git/UserSuppliedGitInfoBuilderTest.groovy | 41 +++++++++++++++---- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CILocalGitInfoBuilderTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CILocalGitInfoBuilderTest.groovy index d921242da9c..d484f28bfdc 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CILocalGitInfoBuilderTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/git/CILocalGitInfoBuilderTest.groovy @@ -43,7 +43,6 @@ class CILocalGitInfoBuilderTest extends Specification { } def resolve(workspace) { - def resolvedWS = Paths.get(getClass().getClassLoader().getResource(workspace).toURI()).toFile().getAbsolutePath() - return resolvedWS + Paths.get(getClass().getClassLoader().getResource(workspace).toURI()).toFile().getAbsolutePath() } } diff --git a/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java b/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java index e4b12e2666f..342a4f3930b 100644 --- a/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java +++ b/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java @@ -13,6 +13,7 @@ import static datadog.trace.api.git.GitInfo.DD_GIT_TAG; import datadog.trace.api.Config; +import datadog.trace.api.config.GeneralConfig; import datadog.trace.bootstrap.instrumentation.api.Tags; import javax.annotation.Nullable; import org.slf4j.Logger; @@ -77,7 +78,7 @@ public GitInfo build(@Nullable String repositoryPath) { log.error( "Could not resolve git repository URL (can be provided via " + GitInfo.DD_GIT_REPOSITORY_URL - + " env var)"); + + " env var, " + GeneralConfig.TAGS + " config property or by embedding git metadata at build time)"); } String commitSha = gitInfo.getCommit().getSha(); @@ -87,7 +88,7 @@ public GitInfo build(@Nullable String repositoryPath) { + commitSha + " (can be provided via " + GitInfo.DD_GIT_COMMIT_SHA - + " env var, and must be a full-length git SHA)"); + + " env var, " + GeneralConfig.TAGS + " config property or by embedding git metadata at build time; must be a full-length SHA_"); } } diff --git a/internal-api/src/test/groovy/datadog/trace/api/git/UserSuppliedGitInfoBuilderTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/git/UserSuppliedGitInfoBuilderTest.groovy index 1f962a2e81d..ae0ae4a09b5 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/git/UserSuppliedGitInfoBuilderTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/git/UserSuppliedGitInfoBuilderTest.groovy @@ -1,14 +1,11 @@ package datadog.trace.api.git +import datadog.trace.api.config.GeneralConfig +import datadog.trace.bootstrap.instrumentation.api.Tags +import datadog.trace.test.util.DDSpecification +import datadog.trace.util.Strings -import org.junit.Rule -import org.junit.contrib.java.lang.system.EnvironmentVariables -import spock.lang.Specification - -class UserSuppliedGitInfoBuilderTest extends Specification { - - @Rule - public final EnvironmentVariables environmentVariables = new EnvironmentVariables() +class UserSuppliedGitInfoBuilderTest extends DDSpecification { def setup() { // Clear all environment variables to avoid clashes between @@ -87,4 +84,32 @@ class UserSuppliedGitInfoBuilderTest extends Specification { gitInfo.branch == null gitInfo.tag == "myProvidedTag" } + + def "git info is extracted from global tags"() { + setup: + injectEnvConfig(Strings.toEnvVar(GeneralConfig.TAGS), Tags.GIT_REPOSITORY_URL + ":repo_url," + Tags.GIT_COMMIT_SHA + ":commit_sha") + + when: + def gitInfo = new UserSuppliedGitInfoBuilder().build(null) + + then: + !gitInfo.isEmpty() + gitInfo.repositoryURL == "repo_url" + gitInfo.commit.sha == "commit_sha" + } + + def "global tags have lower priority than dedicated environment variables"() { + setup: + injectEnvConfig(Strings.toEnvVar(GeneralConfig.TAGS), Tags.GIT_REPOSITORY_URL + ":repo_url," + Tags.GIT_COMMIT_SHA + ":commit_sha") + injectEnvConfig(GitInfo.DD_GIT_REPOSITORY_URL, "overridden_repo_url") + injectEnvConfig(GitInfo.DD_GIT_COMMIT_SHA, "overridden_commit_sha") + + when: + def gitInfo = new UserSuppliedGitInfoBuilder().build(null) + + then: + !gitInfo.isEmpty() + gitInfo.repositoryURL == "overridden_repo_url" + gitInfo.commit.sha == "overridden_commit_sha" + } } From 18d5aa7b0d91cd0e1037d9b7fceef56862a2fd0a Mon Sep 17 00:00:00 2001 From: Nikita Tkachenko Date: Fri, 31 Mar 2023 17:55:29 +0200 Subject: [PATCH 3/3] automated formatting updates --- .../datadog/trace/api/git/UserSuppliedGitInfoBuilder.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java b/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java index 342a4f3930b..faf9be9f86f 100644 --- a/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java +++ b/internal-api/src/main/java/datadog/trace/api/git/UserSuppliedGitInfoBuilder.java @@ -78,7 +78,9 @@ public GitInfo build(@Nullable String repositoryPath) { log.error( "Could not resolve git repository URL (can be provided via " + GitInfo.DD_GIT_REPOSITORY_URL - + " env var, " + GeneralConfig.TAGS + " config property or by embedding git metadata at build time)"); + + " env var, " + + GeneralConfig.TAGS + + " config property or by embedding git metadata at build time)"); } String commitSha = gitInfo.getCommit().getSha(); @@ -88,7 +90,9 @@ public GitInfo build(@Nullable String repositoryPath) { + commitSha + " (can be provided via " + GitInfo.DD_GIT_COMMIT_SHA - + " env var, " + GeneralConfig.TAGS + " config property or by embedding git metadata at build time; must be a full-length SHA_"); + + " env var, " + + GeneralConfig.TAGS + + " config property or by embedding git metadata at build time; must be a full-length SHA_"); } }