diff --git a/utils/config-utils/src/test/groovy/datadog/trace/api/ConfigSettingTest.groovy b/utils/config-utils/src/test/groovy/datadog/trace/api/ConfigSettingTest.groovy deleted file mode 100644 index 71a58e144d2..00000000000 --- a/utils/config-utils/src/test/groovy/datadog/trace/api/ConfigSettingTest.groovy +++ /dev/null @@ -1,85 +0,0 @@ -package datadog.trace.api - -import spock.lang.Specification - -class ConfigSettingTest extends Specification { - - def "supports equality check"() { - when: - def cs1 = ConfigSetting.of(key1, value1, origin1) - def cs2 = ConfigSetting.of(key2, value2, origin2) - - then: - if (key1 == key2 && value1 == value2 && origin1 == origin2) { - assert cs1.hashCode() == cs2.hashCode() - assert cs1 == cs2 - assert cs2 == cs1 - assert cs1.toString() == cs2.toString() - } else { - assert cs1.hashCode() != cs2.hashCode() - assert cs1 != cs2 - assert cs2 != cs1 - assert cs1.toString() != cs2.toString() - } - - where: - key1 | key2 | value1 | value2 | origin1 | origin2 - "key" | "key" | "value" | "value" | ConfigOrigin.DEFAULT | ConfigOrigin.DEFAULT - "key" | "key2" | "value" | "value" | ConfigOrigin.ENV | ConfigOrigin.ENV - "key" | "key" | "value2" | "value" | ConfigOrigin.JVM_PROP | ConfigOrigin.JVM_PROP - "key" | "key" | "value" | "value" | ConfigOrigin.ENV | ConfigOrigin.DEFAULT - } - - def "filters key values"() { - expect: - ConfigSetting.of(key, value, ConfigOrigin.DEFAULT).stringValue() == filteredValue - - where: - key | value | filteredValue - "DD_API_KEY" | "somevalue" | "" - "dd.api-key" | "somevalue" | "" - "dd.profiling.api-key" | "somevalue" | "" - "dd.profiling.apikey" | "somevalue" | "" - "some.other.key" | "somevalue" | "somevalue" - } - - def "support basic types"() { - expect: - ConfigSetting.of("key", value, ConfigOrigin.DEFAULT).stringValue() == rendered - - where: - value | rendered - null | null - true | "true" - false | "false" - 1 | "1" - 1.0 | "1.0" - 2.33f | "2.33" - "string" | "string" - } - - def "convert Iterable, Map, and BitSet to String"() { - expect: - ConfigSetting.of("key", value, ConfigOrigin.DEFAULT).stringValue() == rendered - - where: - value | rendered - ["1", "2", "3"] | "1,2,3" - [1, 2, 3] | "1,2,3" - [1.0f, 22.23d, 3.1415] | "1.0,22.23,3.1415" - [a: 1, b: 2] | "a:1,b:2" - [a: "1", b: "2"] | "a:1,b:2" - [:] | "" - [] | "" - bitSetIntervals() | "33,200-300,303,400-500" - } - - BitSet bitSetIntervals() { - def bitSetIntervals = new BitSet() - bitSetIntervals.set(33) - bitSetIntervals.set(200, 300) - bitSetIntervals.set(303) - bitSetIntervals.set(400, 500) - return bitSetIntervals - } -} diff --git a/utils/config-utils/src/test/groovy/datadog/trace/api/env/CapturedEnvironmentTest.groovy b/utils/config-utils/src/test/groovy/datadog/trace/api/env/CapturedEnvironmentTest.groovy deleted file mode 100644 index 78c52635898..00000000000 --- a/utils/config-utils/src/test/groovy/datadog/trace/api/env/CapturedEnvironmentTest.groovy +++ /dev/null @@ -1,151 +0,0 @@ -package datadog.trace.api.env - -import static java.io.File.separator - -import datadog.trace.api.config.GeneralConfig -import datadog.trace.test.util.DDSpecification - -class CapturedEnvironmentTest extends DDSpecification { - def "non autodetected service.name with null command"() { - when: - def serviceName = forkAndRunProperties('null') - - then: - serviceName == null - } - - def "non autodetected service.name with empty command"() { - when: - def serviceName = forkAndRunProperties('') - - then: - serviceName == null - } - - def "non autodetected service.name with all blanks command"() { - when: - def serviceName = forkAndRunProperties(' ') - - then: - serviceName == null - } - - def "set service.name by sysprop 'sun.java.command' with class"() { - when: - def serviceName = forkAndRunProperties('org.example.App -Dfoo=bar arg2 arg3') - - then: - serviceName == 'org.example.App' - } - - def "set service.name by sysprop 'sun.java.command' with jar"() { - when: - def serviceName = forkAndRunProperties('foo/bar/example.jar -Dfoo=bar arg2 arg3') - - then: - serviceName == 'example' - } - - def "set service.name with real 'sun.java.command' property"() { - when: - def serviceName = forkAndRunProperties(null) - - then: - serviceName == ServiceNamePrinter.name - } - - def "use Azure site name in Azure"() { - when: - def serviceName = forkAndRunProperties('foo/bar/example.jar -Dfoo=bar arg2 arg3', [ - 'DD_AZURE_APP_SERVICES': '1', - 'WEBSITE_SITE_NAME': 'siteService' - ]) - - then: - serviceName == 'siteService' - } - - def "dont use site name when not in azure"() { - when: - def serviceName = forkAndRunProperties('foo/bar/example.jar -Dfoo=bar arg2 arg3', [ - 'WEBSITE_SITE_NAME': 'siteService' - ]) - - then: - serviceName == 'example' - } - - def "dont use Azure site name when null"() { - when: - def serviceName = forkAndRunProperties('foo/bar/example.jar -Dfoo=bar arg2 arg3', [ - 'DD_AZURE_APP_SERVICES': 'true', - ]) - - then: - serviceName == 'example' - } - - private static String forkAndRunProperties(String arg, Map envVars = [:]) - throws IOException, InterruptedException { - // Build the command to run a new Java process - List command = [] - command += System.getProperty("java.home") + separator + "bin" + separator + "java" - command += '-cp' - command += System.getProperty("java.class.path") - command += ServiceNamePrinter.name - if (arg != null) { - command += arg - } - // Start the process - ProcessBuilder processBuilder = new ProcessBuilder(command) - processBuilder.environment().putAll(envVars) - Process process = processBuilder.start() - // Read and parse output and error streams - String serviceName = '' - try (BufferedReader reader = - new BufferedReader(new InputStreamReader(process.getInputStream()))) { - String line - while ((line = reader.readLine()) != null) { - if (serviceName != '') { - serviceName += '\n' - } - serviceName += line - } - } - if (serviceName == 'null') { - serviceName = null - } - String error = '' - try (BufferedReader reader = - new BufferedReader(new InputStreamReader(process.getErrorStream()))) { - String line - while ((line = reader.readLine()) != null) { - error += line + '\n' - } - } - // Wait for the process to complete - int exitCode = process.waitFor() - // Dumping state on error - if (exitCode != 0) { - println("Error printing service name. Exit code $exitCode with service name: '$serviceName' and error:\n$error") - throw new IllegalStateException('Process should exit without error') - } - return serviceName - } - - static class ServiceNamePrinter { - static void main(String[] args) { - if (args.length > 0) { - def sunJavaCommand = args[0] - if (sunJavaCommand == 'null') { - System.clearProperty('sun.java.command') - } else { - System.setProperty('sun.java.command', sunJavaCommand) - } - } - def capturedEnv = CapturedEnvironment.get() - def props = capturedEnv.properties - println props.get(GeneralConfig.SERVICE_NAME) - } - } -} diff --git a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/AgentArgsInjectorTest.groovy b/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/AgentArgsInjectorTest.groovy deleted file mode 100644 index 1833f785eba..00000000000 --- a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/AgentArgsInjectorTest.groovy +++ /dev/null @@ -1,18 +0,0 @@ -package datadog.trace.bootstrap.config.provider - -import datadog.trace.test.util.DDSpecification - -class AgentArgsInjectorTest extends DDSpecification { - - def "injects agent arguments as system properties"() { - given: - def agentArgs = "arg1=value1,arg2=value2" - - when: - AgentArgsInjector.injectAgentArgsConfig(agentArgs) - - then: - System.getProperty("arg1") == "value1" - System.getProperty("arg2") == "value2" - } -} diff --git a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/AgentArgsParserTest.groovy b/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/AgentArgsParserTest.groovy deleted file mode 100644 index 83cd29701fd..00000000000 --- a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/AgentArgsParserTest.groovy +++ /dev/null @@ -1,79 +0,0 @@ -package datadog.trace.bootstrap.config.provider - - -import spock.lang.Specification - -class AgentArgsParserTest extends Specification { - - def "parses a single argument"() { - given: - def args = "key1=value1" - - when: - def properties = AgentArgsParser.parseAgentArgs(args) - - then: - properties != null - properties.size() == 1 - properties.get("key1") == "value1" - } - - def "parses multiple arguments"() { - given: - def args = "key1=value1,key2=value2" - - when: - def properties = AgentArgsParser.parseAgentArgs(args) - - then: - properties != null - properties.size() == 2 - properties.get("key2") == "value2" - } - - def "returns null for null string"() { - given: - def args = null - - when: - def properties = AgentArgsParser.parseAgentArgs(args) - - then: - properties == null - } - - def "returns null for empty string"() { - given: - def args = "" - - when: - def properties = AgentArgsParser.parseAgentArgs(args) - - then: - properties == null - } - - def "returns null for malformed string"() { - given: - def args = "key=value,,,==" - - when: - def properties = AgentArgsParser.parseAgentArgs(args) - - then: - properties == null - } - - def "parses argument with spaces"() { - given: - def args = "key=value with spaces" - - when: - def properties = AgentArgsParser.parseAgentArgs(args) - - then: - properties != null - properties.size() == 1 - properties.get("key") == "value with spaces" - } -} diff --git a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/ConfigConverterTest.groovy b/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/ConfigConverterTest.groovy deleted file mode 100644 index 0f9e1dd752d..00000000000 --- a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/ConfigConverterTest.groovy +++ /dev/null @@ -1,195 +0,0 @@ -package datadog.trace.bootstrap.config.provider - -import datadog.trace.test.util.DDSpecification - -class ConfigConverterTest extends DDSpecification { - - def "Convert boolean properties"() { - when: - def value = ConfigConverter.valueOf(stringValue, Boolean) - - then: - value == expectedConvertedValue - - where: - stringValue | expectedConvertedValue - "true" | true - "TRUE" | true - "True" | true - "1" | true - "false" | false - null | null - "" | null - "0" | false - } - - def "Convert boolean properties throws exception for invalid values"() { - when: - ConfigConverter.valueOf(invalidValue, Boolean) - - then: - def exception = thrown(ConfigConverter.InvalidBooleanValueException) - exception.message.contains("Invalid boolean value:") - - where: - invalidValue << [ - "42.42", - "tru", - "truee", - "true ", - " true", - " true ", - " true ", - "notABool", - "yes", - "no", - "on", - "off" - ] - } - - def "parse map properly for #mapString"() { - when: - def result = ConfigConverter.parseMap(mapString, "test") - - then: - result == expected - - where: - // spotless:off - mapString | expected - "a:1, a:2, a:3" | [a: "3"] - "a:b,c:d,e:" | [a: "b", c: "d"] - // space separated - "a:1 a:2 a:3" | [a: "3"] - "a:b c:d e:" | [a: "b", c: "d"] - // More different string variants: - "a:a;" | [a: "a;"] - "a:1, a:2, a:3" | [a: "3"] - "a:1 a:2 a:3" | [a: "3"] - "a:b,c:d,e:" | [a: "b", c: "d"] - "a:b c:d e:" | [a: "b", c: "d"] - "key 1!:va|ue_1," | ["key 1!": "va|ue_1"] - "key 1!:va|ue_1 " | ["key 1!": "va|ue_1"] - " key1 :value1 ,\t key2: value2" | [key1: "value1", key2: "value2"] - 'a:b, b:c, c:d, d: e' | ['a': 'b', 'b': 'c', 'c': 'd', 'd': 'e'] - "key1 :value1 \t key2: value2" | [key1: "value1", key2: "value2"] - "dyno:web.1 dynotype:web appname:******" | ["dyno": "web.1", "dynotype": "web", "appname": "******"] - "is:val:id" | [is: "val:id"] - "a:b,is:val:id,x:y" | [a: "b", is: "val:id", x: "y"] - "a:b:c:d" | [a: "b:c:d"] - 'fooa:barb, foob:barc, fooc: bard, food: bare,' | ['fooa': 'barb', 'foob': 'barc', 'fooc': 'bard', 'food': 'bare'] - "a:b=c=d" | [a: "b=c=d"] - // Illegal - "a:" | [:] - "a:b,c,d" | [:] - "a:b,c,d,k:v" | [:] - "" | [:] - "1" | [:] - "a" | [:] - "a,1" | [:] - "!a" | [:] - " " | [:] - ",,,," | [:] - ":,:,:,:," | [:] - ": : : : " | [:] - "::::" | [:] - 'key1:val1 with_space:and_colon, key2:val2' | [:] - // spotless:on - } - - def "parse map for #mapString with separator #separator"() { - when: - def result = ConfigConverter.parseMap(mapString, "test", separator as char) - - then: - result == expected - - where: - // spotless:off - mapString | separator | expected - "a=1, a=2, a=3" | '=' | [a: "3"] - "a=b,c=d,e=" | '=' | [a: "b", c: "d"] - "a;b,c;d,e;" | ';' | [a: "b", c: "d"] - // space separated - "a=1 a=2 a=3" | '=' | [a: "3"] - "a=b c=d e=" | '=' | [a: "b", c: "d"] - // More different string variants - "a=b=c=d" | '=' | [a: "b=c=d"] - 'fooa=barb, foob=barc, fooc= bard, food= bare,' | '=' | ['fooa': 'barb', 'foob': 'barc', 'fooc': 'bard', 'food': 'bare'] - "a=b:c:d" | '=' | [a: "b:c:d"] - // Illegal - "a=" | '=' | [:] - "====" | '=' | [:] - // spotless:on - } - - def "parsing map #mapString with List of arg separators for with key value separator #separator"() { - //testing parsing for DD_TAGS - setup: - def separatorList = [',' as char, ' ' as char] - - when: - def result = ConfigConverter.parseTraceTagsMap(mapString, separator as char, separatorList as List) - - then: - result == expected - - where: - // spotless:off - mapString | separator | expected - "key1:value1,key2:value2" | ':' | [key1: "value1", key2: "value2"] - "key1:value1 key2:value2" | ':' | [key1: "value1", key2: "value2"] - "env:test aKey:aVal bKey:bVal cKey:" | ':' | [env: "test", aKey: "aVal", bKey: "bVal", cKey:""] - "env:test,aKey:aVal,bKey:bVal,cKey:" | ':' | [env: "test", aKey: "aVal", bKey: "bVal", cKey:""] - "env:test,aKey:aVal bKey:bVal cKey:" | ':' | [env: "test", aKey: "aVal bKey:bVal cKey:"] - "env:test bKey :bVal dKey: dVal cKey:" | ':' | [env: "test", bKey: "", dKey: "", dVal: "", cKey: ""] - 'env :test, aKey : aVal bKey:bVal cKey:' | ':' | [env: "test", aKey : "aVal bKey:bVal cKey:"] - "env:keyWithA:Semicolon bKey:bVal cKey" | ':' | [env: "keyWithA:Semicolon", bKey: "bVal", cKey: ""] - "env:keyWith: , , Lots:Of:Semicolons " | ':' | [env: "keyWith:", Lots: "Of:Semicolons"] - "a:b,c,d" | ':' | [a: "b", c: "", d: ""] - "a,1" | ':' | [a: "", "1": ""] - "a:b:c:d" | ':' | [a: "b:c:d"] - //edge cases - "noDelimiters" | ':' | [noDelimiters: ""] - " " | ':' | [:] - ",,,,,,,,,,,," | ':' | [:] - ", , , , , , " | ':' | [:] - // spotless:on - } - - def "test parseMapWithOptionalMappings"() { - when: - def result = ConfigConverter.parseMapWithOptionalMappings(mapString, "test", defaultPrefix, lowercaseKeys) - - then: - result == expected - - where: - mapString | expected | lowercaseKeys | defaultPrefix - "header1:one,header2:two" | [header1: "one", header2: "two"] | false | "" - "header1:one, header2:two" | [header1: "one", header2: "two"] | false | "" - "header1,header2:two" | [header1: "header1", header2: "two"] | false | "" - "Header1:one,header2:two" | [header1: "one", header2: "two"] | true | "" - "\"header1:one,header2:two\"" | ["\"header1": "one", header2: "two\""] | true | "" - "header1" | [header1: "header1"] | true | "" - ",header1:tag" | [header1: "tag"] | true | "" - "header1:tag," | [header1: "tag"] | true | "" - "header:tag:value" | [header: "tag:value"] | true | "" - "" | [:] | true | "" - null | [:] | true | "" - // Test for wildcard header tags - "*" | ["*":"datadog.response.headers."] | true | "datadog.response.headers" - "*:" | [:] | true | "datadog.response.headers" - "*,header1:tag" | ["*":"datadog.response.headers."] | true | "datadog.response.headers" - "header1:tag,*" | ["*":"datadog.response.headers."] | true | "datadog.response.headers" - // logs warning: Illegal key only tag starting with non letter '1header' - "1header,header2:two" | [:] | true | "" - // logs warning: Illegal tag starting with non letter for key 'header' - "header::tag" | [:] | true | "" - // logs warning: Illegal empty key at position 0 - ":tag" | [:] | true | "" - // logs warning: Illegal empty key at position 11 - "header:tag,:tag" | [:] | true | "" - } -} diff --git a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/PropertiesConfigSourceTest.groovy b/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/PropertiesConfigSourceTest.groovy deleted file mode 100644 index b8a4a15757c..00000000000 --- a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/PropertiesConfigSourceTest.groovy +++ /dev/null @@ -1,36 +0,0 @@ -package datadog.trace.bootstrap.config.provider - -import datadog.trace.test.util.DDSpecification - -class PropertiesConfigSourceTest extends DDSpecification { - - def "test null"() { - when: - new PropertiesConfigSource(null, true) - - then: - thrown(AssertionError) - } - - def "config pulled from properties"() { - setup: - def props = new Properties(["abc": "def", "dd.abc": "xyz"]) - def source = new PropertiesConfigSource(props, false) - - expect: - source.get("abc") == "def" - source.get("dd.abc") == "xyz" - source.get("missing") == null - } - - def "config pulled from properties with prefix"() { - setup: - def props = new Properties(["abc": "def", "dd.abc": "xyz"]) - def source = new PropertiesConfigSource(props, true) - - expect: - source.get("abc") == "xyz" - source.get("dd.abc") == null - source.get("missing") == null - } -} diff --git a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/StableConfigMappingExceptionTest.groovy b/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/StableConfigMappingExceptionTest.groovy deleted file mode 100644 index 2a1af178562..00000000000 --- a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/StableConfigMappingExceptionTest.groovy +++ /dev/null @@ -1,35 +0,0 @@ -package datadog.trace.bootstrap.config.provider - -import datadog.trace.bootstrap.config.provider.stableconfig.StableConfigMappingException -import spock.lang.Specification - -class StableConfigMappingExceptionTest extends Specification { - - def "constructors work as expected"() { - when: - def ex1 = new StableConfigMappingException("msg") - def ex2 = new StableConfigMappingException("msg2") - - then: - ex1.message == "msg" - ex1.cause == null - ex2.message == "msg2" - } - - def "safeToString handles null"() { - expect: - StableConfigMappingException.safeToString(null) == "null" - } - - def "safeToString handles short string"() { - expect: - StableConfigMappingException.safeToString("short string") == "short string" - } - - def "safeToString handles long string"() { - given: - def longStr = "a" * 101 - expect: - StableConfigMappingException.safeToString(longStr) == ("a" * 50) + "...(truncated)..." + ("a" * 51).substring(1) - } -} diff --git a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/StableConfigSourceTest.groovy b/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/StableConfigSourceTest.groovy deleted file mode 100644 index 84aeda8ebb0..00000000000 --- a/utils/config-utils/src/test/groovy/datadog/trace/bootstrap/config/provider/StableConfigSourceTest.groovy +++ /dev/null @@ -1,322 +0,0 @@ -package datadog.trace.bootstrap.config.provider - -import datadog.trace.api.ConfigCollector - -import static java.util.Collections.singletonMap - -import datadog.trace.api.ConfigOrigin -import datadog.trace.bootstrap.config.provider.stableconfig.Rule -import datadog.trace.bootstrap.config.provider.stableconfig.Selector -import datadog.trace.bootstrap.config.provider.stableconfig.StableConfig -import datadog.trace.test.util.DDSpecification -import org.snakeyaml.engine.v2.api.Dump -import org.snakeyaml.engine.v2.api.DumpSettings -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.read.ListAppender -import org.slf4j.LoggerFactory - -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardOpenOption - -class StableConfigSourceTest extends DDSpecification { - - def "test file doesn't exist"() { - setup: - StableConfigSource config = new StableConfigSource(StableConfigSource.LOCAL_STABLE_CONFIG_PATH, ConfigOrigin.LOCAL_STABLE_CONFIG) - - expect: - config.getKeys().size() == 0 - config.getConfigId() == null - } - - def "test empty file"() { - given: - Path filePath = Files.createTempFile("testFile_", ".yaml") - - when: - StableConfigSource config = new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG) - then: - config.getKeys().size() == 0 - config.getConfigId() == null - - cleanup: - Files.delete(filePath) - } - - def "test file invalid format"() { - // StableConfigSource must handle the exception thrown by StableConfigParser.parse(filePath) gracefully - when: - Path filePath = Files.createTempFile("testFile_", ".yaml") - then: - if (filePath == null) { - throw new AssertionError("Failed to create: " + filePath) - } - - when: - writeFileRaw(filePath, configId, data) - StableConfigSource stableCfg = new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG) - - then: - stableCfg.getConfigId() == null - stableCfg.getKeys().size() == 0 - - cleanup: - Files.delete(filePath) - - where: - configId | data - null | corruptYaml - "12345" | "this is not yaml format!" - } - - def "test null values in YAML"() { - when: - Path filePath = Files.createTempFile("testFile_", ".yaml") - then: - if (filePath == null) { - throw new AssertionError("Failed to create: " + filePath) - } - - when: - // Test the scenario where YAML contains null values for apm_configuration_default and apm_configuration_rules - String yaml = """ -config_id: "12345" -apm_configuration_default: -apm_configuration_rules: -""" - Files.write(filePath, yaml.getBytes()) - StableConfigSource stableCfg = new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG) - - then: - // Should not throw NullPointerException and should handle null values gracefully - stableCfg.getConfigId() == "12345" - stableCfg.getKeys().size() == 0 - Files.delete(filePath) - } - - def "test file valid format"() { - given: - Path filePath = Files.createTempFile("testFile_", ".yaml") - - when: - StableConfig stableConfigYaml = new StableConfig(configId, defaultConfigs) - writeFileYaml(filePath, stableConfigYaml) - StableConfigSource stableCfg = new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG) - - then: - for (key in defaultConfigs.keySet()) { - String keyString = (String) key - keyString = keyString.substring(4) // Cut `DD_` - stableCfg.get(keyString) == defaultConfigs.get(key) - } - // All configs from MatchingRule should be applied - if (ruleConfigs.contains(sampleMatchingRule)) { - for (key in sampleMatchingRule.getConfiguration().keySet()) { - String keyString = (String) key - keyString = keyString.substring(4) // Cut `DD_` - stableCfg.get(keyString) == defaultConfigs.get(key) - } - } - // None of the configs from NonMatchingRule should be applied - if (ruleConfigs.contains(sampleNonMatchingRule)) { - Set cfgKeys = stableCfg.getKeys() - for (key in sampleMatchingRule.getConfiguration().keySet()) { - String keyString = (String) key - keyString = keyString.substring(4) // Cut `DD_` - !cfgKeys.contains(keyString) - } - } - - cleanup: - Files.delete(filePath) - - where: - configId | defaultConfigs | ruleConfigs - "" | [:] | Arrays.asList(new Rule()) - "12345" | ["DD_KEY_ONE": "one", "DD_KEY_TWO": "two"] | Arrays.asList(sampleMatchingRule, sampleNonMatchingRule) - } - - def "test parse invalid logs mapping errors"() { - given: - Logger logbackLogger = (Logger) LoggerFactory.getLogger(StableConfigSource) - def listAppender = new ListAppender() - listAppender.start() - logbackLogger.addAppender(listAppender) - - def tempFile = File.createTempFile("testFile_", ".yaml") - tempFile.text = yaml - - when: - def stableCfg = new StableConfigSource(tempFile.absolutePath, ConfigOrigin.LOCAL_STABLE_CONFIG) - - then: - stableCfg.config == StableConfigSource.StableConfig.EMPTY - def warnLogs = listAppender.list.findAll { it.level.toString() == 'WARN' } - warnLogs.any { it.formattedMessage.contains(expectedLogSubstring) } - - cleanup: - tempFile.delete() - logbackLogger.detachAppender(listAppender) - - where: - yaml | expectedLogSubstring - '''apm_configuration_rules: - - selectors: - - key: "someKey" - matches: ["someValue"] - operator: equals - configuration: - DD_SERVICE: "test" - ''' | "Missing 'origin' in selector" - '''apm_configuration_rules: - - selectors: - - origin: process_arguments - key: "-Dfoo" - matches: ["bar"] - operator: equals - ''' | "Missing 'configuration' in rule" - '''apm_configuration_rules: - - configuration: - DD_SERVICE: "test" - ''' | "Missing 'selectors' in rule" - '''apm_configuration_rules: - - selectors: "not-a-list" - configuration: - DD_SERVICE: "test" - ''' | "'selectors' must be a list, but got: String" - '''apm_configuration_rules: - - selectors: - - "not-a-map" - configuration: - DD_SERVICE: "test" - ''' | "Each selector must be a map, but got: String" - '''apm_configuration_rules: - - selectors: - - origin: process_arguments - key: "-Dfoo" - matches: ["bar"] - operator: equals - configuration: "not-a-map" - ''' | "'configuration' must be a map, but got: String" - '''apm_configuration_rules: - - selectors: - - origin: process_arguments - key: "-Dfoo" - matches: ["bar"] - operator: equals - configuration: 12345 - ''' | "'configuration' must be a map, but got: Integer" - '''apm_configuration_rules: - - "not-a-map" - ''' | "Rule must be a map, but got: String" - '''apm_configuration_rules: - - selectors: - - origin: process_arguments - key: "-Dfoo" - matches: "not-a-list" - operator: equals - configuration: - DD_SERVICE: "test" - ''' | "'matches' must be a list, but got: String" - '''apm_configuration_rules: - - selectors: - - origin: process_arguments - key: "-Dfoo" - matches: ["bar"] - configuration: - DD_SERVICE: "test" - ''' | "Missing 'operator' in selector" - '''apm_configuration_rules: - - selectors: - - origin: process_arguments - key: "-Dfoo" - matches: ["bar"] - operator: 12345 - configuration: - DD_SERVICE: "test" - ''' | "'operator' must be a string, but got: Integer" - '''apm_configuration_rules: - - selectors: - # origin is missing entirely, should trigger NullPointerException - - key: "-Dfoo" - matches: ["bar"] - operator: equals - ''' | "YAML mapping error in stable configuration file" - } - - // Corrupt YAML string variable used for testing, defined outside the 'where' block for readability - static corruptYaml = ''' - abc: 123 - def: - ghi: "jkl" - lmn: 456 - ''' - - // Matching and non-matching Rules used for testing, defined outside the 'where' block for readability - static sampleMatchingRule = new Rule(Arrays.asList(new Selector("origin", "language", Arrays.asList("Java"), null)), singletonMap("DD_KEY_THREE", "three")) - static sampleNonMatchingRule = new Rule(Arrays.asList(new Selector("origin", "language", Arrays.asList("Golang"), null)), singletonMap("DD_KEY_FOUR", "four")) - - def writeFileYaml(Path filePath, StableConfig stableConfigs) { - Map yamlData = new HashMap<>() - - if (stableConfigs.getConfigId() != null && !stableConfigs.getConfigId().isEmpty()) { - yamlData.put("config_id", stableConfigs.getConfigId()) - } - - if (stableConfigs.getApmConfigurationDefault() != null && !stableConfigs.getApmConfigurationDefault().isEmpty()) { - yamlData.put("apm_configuration_default", stableConfigs.getApmConfigurationDefault()) - } - - DumpSettings settings = DumpSettings.builder().build() - Dump dump = new Dump(settings) - String yamlContent = dump.dumpToString(yamlData) - - try (FileWriter writer = new FileWriter(filePath.toFile())) { - writer.write(yamlContent) - } - } - - def "test config id exists in ConfigCollector when using StableConfigSource"() { - given: - Path filePath = Files.createTempFile("testFile_", ".yaml") - String expectedConfigId = "123" - - // Create YAML content with config_id and some configuration - def yamlContent = """ -config_id: ${expectedConfigId} -apm_configuration_default: - DD_SERVICE: test-service - DD_ENV: test-env -""" - Files.write(filePath, yamlContent.getBytes()) - - // Clear any existing collected config - ConfigCollector.get().collect().clear() - - when: - // Create StableConfigSource and ConfigProvider - StableConfigSource stableConfigSource = new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG) - ConfigProvider configProvider = new ConfigProvider(stableConfigSource) - - // Trigger config collection by getting a value - configProvider.getString("SERVICE", "default-service") - - then: - def collectedConfigs = ConfigCollector.get().collect() - def serviceSetting = collectedConfigs.get(ConfigOrigin.LOCAL_STABLE_CONFIG).("SERVICE") - serviceSetting.configId == expectedConfigId - serviceSetting.value == "test-service" - serviceSetting.origin == ConfigOrigin.LOCAL_STABLE_CONFIG - - cleanup: - Files.delete(filePath) - } - - def writeFileRaw(Path filePath, String configId, String data) { - data = configId + "\n" + data - StandardOpenOption[] openOpts = [StandardOpenOption.WRITE] as StandardOpenOption[] - Files.write(filePath, data.getBytes(), openOpts) - } -} diff --git a/utils/config-utils/src/test/java/datadog/trace/api/ConfigSettingTest.java b/utils/config-utils/src/test/java/datadog/trace/api/ConfigSettingTest.java new file mode 100644 index 00000000000..1f9563179c2 --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/api/ConfigSettingTest.java @@ -0,0 +1,114 @@ +package datadog.trace.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import datadog.trace.junit.utils.tabletest.BoxedValueConverter; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.provider.MethodSource; +import org.tabletest.junit.TableTest; + +public class ConfigSettingTest { + + @TableTest({ + "scenario | key1 | key2 | value1 | value2 | origin1 | origin2 ", + "equal | key | key | value | value | DEFAULT | DEFAULT ", + "different key | key | key2 | value | value | ENV | ENV ", + "different value | key | key | value2 | value | JVM_PROP | JVM_PROP", + "different origin | key | key | value | value | ENV | DEFAULT " + }) + void supportsEqualityCheck( + String key1, + String key2, + Object value1, + Object value2, + ConfigOrigin origin1, + ConfigOrigin origin2) { + // when + ConfigSetting cs1 = ConfigSetting.of(key1, value1, origin1); + ConfigSetting cs2 = ConfigSetting.of(key2, value2, origin2); + + // then + if (key1.equals(key2) && value1.equals(value2) && origin1 == origin2) { + assertEquals(cs1.hashCode(), cs2.hashCode()); + assertEquals(cs1, cs2); + assertEquals(cs2, cs1); + assertEquals(cs1.toString(), cs2.toString()); + } else { + assertNotEquals(cs1.hashCode(), cs2.hashCode()); + assertNotEquals(cs1, cs2); + assertNotEquals(cs2, cs1); + assertNotEquals(cs1.toString(), cs2.toString()); + } + } + + @TableTest({ + "scenario | key | value | filteredValue", + "DD_API_KEY | DD_API_KEY | somevalue | ", + "dd.api-key | dd.api-key | somevalue | ", + "dd.profiling.api-key | dd.profiling.api-key | somevalue | ", + "dd.profiling.apikey | dd.profiling.apikey | somevalue | ", + "some.other.key | some.other.key | somevalue | somevalue " + }) + void filtersKeyValues(String key, String value, String filteredValue) { + assertEquals(filteredValue, ConfigSetting.of(key, value, ConfigOrigin.DEFAULT).stringValue()); + } + + @TableTest({ + "scenario | value | rendered", + "null | | ", + "boolean | true | true ", + "boolean | false | false ", + "integer | 1 | 1 ", + "double | 1.0 | 1.0 ", + "float | 2.33f | 2.33 ", + "string | string | string " + }) + void supportBasicTypes(@ConvertWith(BoxedValueConverter.class) Object value, String rendered) { + assertEquals(rendered, ConfigSetting.of("key", value, ConfigOrigin.DEFAULT).stringValue()); + } + + @ParameterizedTest + @MethodSource("convertIterableMapAndBitSetArguments") + void convertIterableMapAndBitSetToString(Object value, String rendered) { + assertEquals(rendered, ConfigSetting.of("key", value, ConfigOrigin.DEFAULT).stringValue()); + } + + static Stream + convertIterableMapAndBitSetArguments() { + Map mapStringInt = new LinkedHashMap<>(); + mapStringInt.put("a", 1); + mapStringInt.put("b", 2); + + Map mapStringString = new LinkedHashMap<>(); + mapStringString.put("a", "1"); + mapStringString.put("b", "2"); + + return Stream.of( + arguments(Arrays.asList("1", "2", "3"), "1,2,3"), + arguments(Arrays.asList(1, 2, 3), "1,2,3"), + arguments(Arrays.asList(1.0f, 22.23d, 3.1415d), "1.0,22.23,3.1415"), + arguments(mapStringInt, "a:1,b:2"), + arguments(mapStringString, "a:1,b:2"), + arguments(Collections.emptyMap(), ""), + arguments(Arrays.asList(), ""), + arguments(bitSetIntervals(), "33,200-300,303,400-500")); + } + + private static BitSet bitSetIntervals() { + BitSet bitSet = new BitSet(); + bitSet.set(33); + bitSet.set(200, 300); + bitSet.set(303); + bitSet.set(400, 500); + return bitSet; + } +} diff --git a/utils/config-utils/src/test/java/datadog/trace/api/env/CapturedEnvironmentTest.java b/utils/config-utils/src/test/java/datadog/trace/api/env/CapturedEnvironmentTest.java new file mode 100644 index 00000000000..0e704f81d2e --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/api/env/CapturedEnvironmentTest.java @@ -0,0 +1,168 @@ +package datadog.trace.api.env; + +import static java.io.File.separator; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import datadog.trace.api.config.GeneralConfig; +import datadog.trace.test.util.DDJavaSpecification; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class CapturedEnvironmentTest extends DDJavaSpecification { + + @Test + void nonAutodetectedServiceNameWithNullCommand() throws IOException, InterruptedException { + String serviceName = forkAndRunProperties("null"); + + assertNull(serviceName); + } + + @Test + void nonAutodetectedServiceNameWithEmptyCommand() throws IOException, InterruptedException { + String serviceName = forkAndRunProperties(""); + + assertNull(serviceName); + } + + @Test + void nonAutodetectedServiceNameWithAllBlanksCommand() throws IOException, InterruptedException { + String serviceName = forkAndRunProperties(" "); + + assertNull(serviceName); + } + + @Test + void setServiceNameBySyspropSunJavaCommandWithClass() throws IOException, InterruptedException { + String serviceName = forkAndRunProperties("org.example.App -Dfoo=bar arg2 arg3"); + + assertEquals("org.example.App", serviceName); + } + + @Test + void setServiceNameBySyspropSunJavaCommandWithJar() throws IOException, InterruptedException { + String serviceName = forkAndRunProperties("foo/bar/example.jar -Dfoo=bar arg2 arg3"); + + assertEquals("example", serviceName); + } + + @Test + void setServiceNameWithRealSunJavaCommandProperty() throws IOException, InterruptedException { + String serviceName = forkAndRunProperties(null); + + assertEquals(ServiceNamePrinter.class.getName(), serviceName); + } + + @Test + void useAzureSiteNameInAzure() throws IOException, InterruptedException { + HashMap azureEnvVars = new HashMap<>(); + azureEnvVars.put("DD_AZURE_APP_SERVICES", "1"); + azureEnvVars.put("WEBSITE_SITE_NAME", "siteService"); + + String serviceName = + forkAndRunProperties("foo/bar/example.jar -Dfoo=bar arg2 arg3", azureEnvVars); + + assertEquals("siteService", serviceName); + } + + @Test + void dontUseSiteNameWhenNotInAzure() throws IOException, InterruptedException { + String serviceName = + forkAndRunProperties( + "foo/bar/example.jar -Dfoo=bar arg2 arg3", + Collections.singletonMap("WEBSITE_SITE_NAME", "siteService")); + + assertEquals("example", serviceName); + } + + @Test + void dontUseAzureSiteNameWhenNull() throws IOException, InterruptedException { + String serviceName = + forkAndRunProperties( + "foo/bar/example.jar -Dfoo=bar arg2 arg3", + Collections.singletonMap("DD_AZURE_APP_SERVICES", "true")); + + assertEquals("example", serviceName); + } + + private static String forkAndRunProperties(String arg) throws IOException, InterruptedException { + return forkAndRunProperties(arg, Collections.emptyMap()); + } + + private static String forkAndRunProperties(String arg, Map envVars) + throws IOException, InterruptedException { + // Build the command to run a new Java process + List command = new ArrayList<>(); + command.add(System.getProperty("java.home") + separator + "bin" + separator + "java"); + command.add("-cp"); + command.add(System.getProperty("java.class.path")); + command.add(ServiceNamePrinter.class.getName()); + if (arg != null) { + command.add(arg); + } + // Start the process + ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.environment().putAll(envVars); + Process process = processBuilder.start(); + // Read and parse output and error streams + String serviceName = ""; + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + if (!serviceName.isEmpty()) { + serviceName += "\n"; + } + serviceName += line; + } + } + if ("null".equals(serviceName)) { + serviceName = null; + } + String error = ""; + try (BufferedReader reader = + new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + while ((line = reader.readLine()) != null) { + error += line + "\n"; + } + } + // Wait for the process to complete + int exitCode = process.waitFor(); + // Dumping state on error + if (exitCode != 0) { + System.out.println( + "Error printing service name. Exit code " + + exitCode + + " with service name: '" + + serviceName + + "' and error:\n" + + error); + throw new IllegalStateException("Process should exit without error"); + } + return serviceName; + } + + public static class ServiceNamePrinter { + public static void main(String[] args) { + if (args.length > 0) { + String sunJavaCommand = args[0]; + if ("null".equals(sunJavaCommand)) { + System.clearProperty("sun.java.command"); + } else { + System.setProperty("sun.java.command", sunJavaCommand); + } + } + CapturedEnvironment capturedEnv = CapturedEnvironment.get(); + Map props = capturedEnv.getProperties(); + System.out.println(props.get(GeneralConfig.SERVICE_NAME)); + } + } +} diff --git a/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/AgentArgsInjectorTest.java b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/AgentArgsInjectorTest.java new file mode 100644 index 00000000000..9c762f96bbc --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/AgentArgsInjectorTest.java @@ -0,0 +1,26 @@ +package datadog.trace.bootstrap.config.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import datadog.trace.test.util.DDJavaSpecification; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +public class AgentArgsInjectorTest extends DDJavaSpecification { + + @AfterEach + void clearInjectedProperties() { + System.clearProperty("arg1"); + System.clearProperty("arg2"); + } + + @Test + void injectsAgentArgumentsAsSystemProperties() { + String agentArgs = "arg1=value1,arg2=value2"; + + AgentArgsInjector.injectAgentArgsConfig(agentArgs); + + assertEquals("value1", System.getProperty("arg1")); + assertEquals("value2", System.getProperty("arg2")); + } +} diff --git a/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/AgentArgsParserTest.java b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/AgentArgsParserTest.java new file mode 100644 index 00000000000..4c8575d81a1 --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/AgentArgsParserTest.java @@ -0,0 +1,65 @@ +package datadog.trace.bootstrap.config.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class AgentArgsParserTest { + + @Test + void parsesASingleArgument() { + String args = "key1=value1"; + + Map properties = AgentArgsParser.parseAgentArgs(args); + + assertNotNull(properties); + assertEquals(1, properties.size()); + assertEquals("value1", properties.get("key1")); + } + + @Test + void parsesMultipleArguments() { + String args = "key1=value1,key2=value2"; + + Map properties = AgentArgsParser.parseAgentArgs(args); + + assertNotNull(properties); + assertEquals(2, properties.size()); + assertEquals("value2", properties.get("key2")); + } + + @Test + void returnsNullForNullString() { + Map properties = AgentArgsParser.parseAgentArgs(null); + + assertNull(properties); + } + + @Test + void returnsNullForEmptyString() { + Map properties = AgentArgsParser.parseAgentArgs(""); + + assertNull(properties); + } + + @Test + void returnsNullForMalformedString() { + Map properties = AgentArgsParser.parseAgentArgs("key=value,,,=="); + + assertNull(properties); + } + + @Test + void parsesArgumentWithSpaces() { + String args = "key=value with spaces"; + + Map properties = AgentArgsParser.parseAgentArgs(args); + + assertNotNull(properties); + assertEquals(1, properties.size()); + assertEquals("value with spaces", properties.get("key")); + } +} diff --git a/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/ConfigConverterTest.java b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/ConfigConverterTest.java new file mode 100644 index 00000000000..2a7cfbb5467 --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/ConfigConverterTest.java @@ -0,0 +1,167 @@ +package datadog.trace.bootstrap.config.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.test.util.DDJavaSpecification; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.tabletest.junit.TableTest; + +public class ConfigConverterTest extends DDJavaSpecification { + + @TableTest({ + "scenario | stringValue | expectedConvertedValue", + "true | true | true ", + "TRUE | TRUE | true ", + "True | True | true ", + "1 | 1 | true ", + "false | false | false ", + "null | | ", + "empty string | '' | ", + "0 | 0 | false " + }) + void convertBooleanProperties(String stringValue, Boolean expectedConvertedValue) { + assertEquals(expectedConvertedValue, ConfigConverter.valueOf(stringValue, Boolean.class)); + } + + @TableTest({ + "invalidValue", + "42.42 ", + "tru ", + "truee ", + "'true ' ", + "' true' ", + "' true ' ", + "' true ' ", + "notABool ", + "yes ", + "no ", + "on ", + "off " + }) + void convertBooleanPropertiesThrowsExceptionForInvalidValues(String invalidValue) { + ConfigConverter.InvalidBooleanValueException exception = + assertThrows( + ConfigConverter.InvalidBooleanValueException.class, + () -> ConfigConverter.valueOf(invalidValue, Boolean.class)); + assertTrue(exception.getMessage().contains("Invalid boolean value:")); + } + + // spotless:off + @TableTest({ + "mapString | expected ", + "a:1, a:2, a:3 | [a: '3'] ", + "a:b,c:d,e: | [a: b, c: d] ", + "a:1 a:2 a:3 | [a: '3'] ", + "a:b c:d e: | [a: b, c: d] ", + "a:a; | [a: a;] ", + "a:1, a:2, a:3 | [a: '3'] ", + "a:1 a:2 a:3 | [a: '3'] ", + "a:b,c:d,e: | [a: b, c: d] ", + "a:b c:d e: | [a: b, c: d] ", + "'key 1!:va|ue_1,' | [key 1!: 'va|ue_1'] ", + "'key 1!:va|ue_1 ' | [key 1!: 'va|ue_1'] ", + "' key1 :value1 ,\t key2: value2' | [key1: value1, key2: value2] ", + "a:b, b:c, c:d, d: e | [a: b, b: c, c: d, d: e] ", + "key1 :value1 \t key2: value2 | [key1: value1, key2: value2] ", + "dyno:web.1 dynotype:web appname:****** | [dyno: web.1, dynotype: web, appname: ******] ", + "is:val:id | [is: 'val:id'] ", + "a:b,is:val:id,x:y | [a: b, is: 'val:id', x: y] ", + "a:b:c:d | [a: 'b:c:d'] ", + "fooa:barb, foob:barc, fooc: bard, food: bare, | [fooa: barb, foob: barc, fooc: bard, food: bare] ", + "a:b=c=d | [a: b=c=d] ", + "a: | [:] ", + "a:b,c,d | [:] ", + "a:b,c,d,k:v | [:] ", + "'' | [:] ", + "1 | [:] ", + "a | [:] ", + "a,1 | [:] ", + "!a | [:] ", + "' ' | [:] ", + ",,,, | [:] ", + ":,:,:,: | [:] ", + "': : : : ' | [:] ", + ":::: | [:] ", + "key1:val1 with_space:and_colon, key2:val2 | [:] " + }) + void parseMapProperly(String mapString, Map expected) { + assertEquals(expected, ConfigConverter.parseMap(mapString, "test")); + } + + @TableTest({ + "mapString | separator | expected ", + "a=1, a=2, a=3 | = | [a: '3'] ", + "a=b,c=d,e= | = | [a: b, c: d] ", + "a;b,c;d,e; | ; | [a: b, c: d] ", + "a=1 a=2 a=3 | = | [a: '3'] ", + "a=b c=d e= | = | [a: b, c: d] ", + "a=b=c=d | = | [a: b=c=d] ", + "fooa=barb, foob=barc, fooc= bard, food= bare, | = | [fooa: barb, foob: barc, fooc: bard, food: bare] ", + "a=b:c:d | = | [a: 'b:c:d'] ", + "a= | = | [:] ", + "==== | = | [:] " + }) + void parseMapWithSeparator(String mapString, char separator, Map expected) { + assertEquals(expected, ConfigConverter.parseMap(mapString, "test", separator)); + } + + @TableTest({ + "mapString | separator | expected ", + "key1:value1,key2:value2 | : | [key1: value1, key2: value2] ", + "key1:value1 key2:value2 | : | [key1: value1, key2: value2] ", + "env:test aKey:aVal bKey:bVal cKey: | : | [env: test, aKey: aVal, bKey: bVal, cKey: ''] ", + "env:test,aKey:aVal,bKey:bVal,cKey: | : | [env: test, aKey: aVal, bKey: bVal, cKey: ''] ", + "env:test,aKey:aVal bKey:bVal cKey: | : | [env: test, aKey: 'aVal bKey:bVal cKey:'] ", + "env:test bKey :bVal dKey: dVal cKey: | : | [env: test, bKey: '', dKey: '', dVal: '', cKey: '']", + "env :test, aKey : aVal bKey:bVal cKey: | : | [env: test, aKey: 'aVal bKey:bVal cKey:'] ", + "env:keyWithA:Semicolon bKey:bVal cKey | : | [env: 'keyWithA:Semicolon', bKey: bVal, cKey: ''] ", + "env:keyWith: , , Lots:Of:Semicolons | : | [env: 'keyWith:', Lots: 'Of:Semicolons'] ", + "a:b,c,d | : | [a: b, c: '', d: ''] ", + "a,1 | : | [a: '', 1: ''] ", + "a:b:c:d | : | [a: 'b:c:d'] ", + "noDelimiters | : | [noDelimiters: ''] ", + "' ' | : | [:] ", + ",,,,,,,,,,,, | : | [:] ", + "', , , , , , ' | : | [:] " + }) + void parsingMapWithListOfArgSeparatorsForWithKeyValueSeparator( + String mapString, char separator, Map expected) { + // testing parsing for DD_TAGS + List separatorList = Arrays.asList(',', ' '); + assertEquals(expected, ConfigConverter.parseTraceTagsMap(mapString, separator, separatorList)); + } + + @TableTest({ + "mapString | expected | lowercaseKeys | defaultPrefix ", + "header1:one,header2:two | [header1: one, header2: two] | false | '' ", + "header1:one, header2:two | [header1: one, header2: two] | false | '' ", + "header1,header2:two | [header1: header1, header2: two] | false | '' ", + "Header1:one,header2:two | [header1: one, header2: two] | true | '' ", + "'\"header1:one,header2:two\"' | ['\"header1': one, header2: 'two\"'] | true | '' ", + "header1 | [header1: header1] | true | '' ", + ",header1:tag | [header1: tag] | true | '' ", + "header1:tag, | [header1: tag] | true | '' ", + "header:tag:value | [header: 'tag:value'] | true | '' ", + "'' | [:] | true | '' ", + " | [:] | true | '' ", + "* | [*: 'datadog.response.headers.'] | true | datadog.response.headers ", + "*: | [:] | true | datadog.response.headers ", + "*,header1:tag | [*: 'datadog.response.headers.'] | true | datadog.response.headers ", + "header1:tag,* | [*: 'datadog.response.headers.'] | true | datadog.response.headers ", + "1header,header2:two | [:] | true | '' ", + "header::tag | [:] | true | '' ", + ":tag | [:] | true | '' ", + "header:tag,:tag | [:] | true | '' " + }) + void testParseMapWithOptionalMappings( + String mapString, Map expected, boolean lowercaseKeys, String defaultPrefix) { + assertEquals( + expected, + ConfigConverter.parseMapWithOptionalMappings(mapString, "test", defaultPrefix, lowercaseKeys)); + } + // spotless:on +} diff --git a/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/PropertiesConfigSourceTest.java b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/PropertiesConfigSourceTest.java new file mode 100644 index 00000000000..3a2aca5b006 --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/PropertiesConfigSourceTest.java @@ -0,0 +1,41 @@ +package datadog.trace.bootstrap.config.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import datadog.trace.test.util.DDJavaSpecification; +import java.util.Properties; +import org.junit.jupiter.api.Test; + +public class PropertiesConfigSourceTest extends DDJavaSpecification { + + @Test + void testNull() { + assertThrows(AssertionError.class, () -> new PropertiesConfigSource(null, true)); + } + + @Test + void configPulledFromProperties() { + Properties props = new Properties(); + props.put("abc", "def"); + props.put("dd.abc", "xyz"); + PropertiesConfigSource source = new PropertiesConfigSource(props, false); + + assertEquals("def", source.get("abc")); + assertEquals("xyz", source.get("dd.abc")); + assertNull(source.get("missing")); + } + + @Test + void configPulledFromPropertiesWithPrefix() { + Properties props = new Properties(); + props.put("abc", "def"); + props.put("dd.abc", "xyz"); + PropertiesConfigSource source = new PropertiesConfigSource(props, true); + + assertEquals("xyz", source.get("abc")); + assertNull(source.get("dd.abc")); + assertNull(source.get("missing")); + } +} diff --git a/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/StableConfigMappingExceptionTest.java b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/StableConfigMappingExceptionTest.java new file mode 100644 index 00000000000..8b2476197f3 --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/StableConfigMappingExceptionTest.java @@ -0,0 +1,60 @@ +package datadog.trace.bootstrap.config.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.bootstrap.config.provider.stableconfig.StableConfigMappingException; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +public class StableConfigMappingExceptionTest { + + @Test + void constructorsWorkAsExpected() { + StableConfigMappingException ex1 = new StableConfigMappingException("msg"); + StableConfigMappingException ex2 = new StableConfigMappingException("msg2"); + + assertEquals("msg", ex1.getMessage()); + assertNull(ex1.getCause()); + assertEquals("msg2", ex2.getMessage()); + } + + @Test + void safeToStringHandlesNull() { + StableConfigMappingException ex = + assertThrows( + StableConfigMappingException.class, + () -> StableConfigMappingException.throwStableConfigMappingException("msg", null)); + assertTrue(ex.getMessage().endsWith(" null")); + } + + @Test + void safeToStringHandlesShortString() { + StableConfigMappingException ex = + assertThrows( + StableConfigMappingException.class, + () -> + StableConfigMappingException.throwStableConfigMappingException( + "msg", "short string")); + assertTrue(ex.getMessage().endsWith(" short string")); + } + + @Test + void safeToStringHandlesLongString() { + char[] chars = new char[101]; + Arrays.fill(chars, 'a'); + String longStr = new String(chars); + + StableConfigMappingException ex = + assertThrows( + StableConfigMappingException.class, + () -> StableConfigMappingException.throwStableConfigMappingException("msg", longStr)); + + char[] halfChars = new char[50]; + Arrays.fill(halfChars, 'a'); + String half = new String(halfChars); + assertTrue(ex.getMessage().endsWith(" " + half + "...(truncated)..." + half)); + } +} diff --git a/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/StableConfigSourceTest.java b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/StableConfigSourceTest.java new file mode 100644 index 00000000000..38062a49be0 --- /dev/null +++ b/utils/config-utils/src/test/java/datadog/trace/bootstrap/config/provider/StableConfigSourceTest.java @@ -0,0 +1,316 @@ +package datadog.trace.bootstrap.config.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import datadog.trace.api.ConfigCollector; +import datadog.trace.api.ConfigOrigin; +import datadog.trace.api.ConfigSetting; +import datadog.trace.test.util.DDJavaSpecification; +import de.thetaphi.forbiddenapis.SuppressForbidden; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.LoggerFactory; +import org.snakeyaml.engine.v2.api.Dump; +import org.snakeyaml.engine.v2.api.DumpSettings; +import org.tabletest.junit.TableTest; + +@SuppressForbidden +public class StableConfigSourceTest extends DDJavaSpecification { + + @Test + void testFileDoesntExist() { + StableConfigSource config = + new StableConfigSource( + StableConfigSource.LOCAL_STABLE_CONFIG_PATH, ConfigOrigin.LOCAL_STABLE_CONFIG); + + assertEquals(0, config.getKeys().size()); + assertNull(config.getConfigId()); + } + + @Test + void testEmptyFile() throws IOException { + Path filePath = Files.createTempFile("testFile_", ".yaml"); + try { + StableConfigSource config = + new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG); + assertEquals(0, config.getKeys().size()); + assertNull(config.getConfigId()); + } finally { + Files.delete(filePath); + } + } + + @TableTest({ + "scenario | configId | data ", + "corrupt yaml | | not_valid_stable_config ", + "invalid format | 12345 | this is not yaml format!" + }) + void testFileInvalidFormat(String configId, String data) throws IOException { + Path filePath = Files.createTempFile("testFile_", ".yaml"); + assertNotNull(filePath, "Failed to create: " + filePath); + try { + writeFileRaw(filePath, configId, data); + StableConfigSource stableCfg = + new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG); + + assertNull(stableCfg.getConfigId()); + assertEquals(0, stableCfg.getKeys().size()); + } finally { + Files.delete(filePath); + } + } + + @Test + void testNullValuesInYaml() throws IOException { + Path filePath = Files.createTempFile("testFile_", ".yaml"); + assertNotNull(filePath, "Failed to create: " + filePath); + try { + // Test the scenario where YAML contains null values for apm_configuration_default and + // apm_configuration_rules + String yaml = "config_id: \"12345\"\napm_configuration_default:\napm_configuration_rules:\n"; + Files.write(filePath, yaml.getBytes()); + + StableConfigSource stableCfg = + new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG); + + // Should not throw NullPointerException and should handle null values gracefully + assertEquals("12345", stableCfg.getConfigId()); + assertEquals(0, stableCfg.getKeys().size()); + } finally { + Files.delete(filePath); + } + } + + @TableTest({ + "configId | defaultConfigs ", + "'' | [:] ", + "12345 | [DD_KEY_ONE: one, DD_KEY_TWO: two]" + }) + void testFileValidFormat(String configId, Map defaultConfigs) throws IOException { + Path filePath = Files.createTempFile("testFile_", ".yaml"); + try { + writeFileYaml(filePath, configId, defaultConfigs); + // verify parsing succeeds without throwing an exception + assertNotNull(new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG)); + } finally { + Files.delete(filePath); + } + } + + @ParameterizedTest + @MethodSource("testParseInvalidLogsMappingErrorsArguments") + void testParseInvalidLogsMappingErrors(String yaml, String expectedLogSubstring) + throws IOException { + Logger logbackLogger = (Logger) LoggerFactory.getLogger(StableConfigSource.class); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + logbackLogger.addAppender(listAppender); + + java.io.File tempFile = java.io.File.createTempFile("testFile_", ".yaml"); + try { + Files.write(tempFile.toPath(), yaml.getBytes()); + + StableConfigSource stableCfg = + new StableConfigSource(tempFile.getAbsolutePath(), ConfigOrigin.LOCAL_STABLE_CONFIG); + + assertNull(stableCfg.getConfigId()); + assertEquals(0, stableCfg.getKeys().size()); + boolean hasExpectedLog = + listAppender.list.stream() + .anyMatch( + event -> + "WARN".equals(event.getLevel().toString()) + && event.getFormattedMessage().contains(expectedLogSubstring)); + assertTrue(hasExpectedLog, "Expected WARN log containing: " + expectedLogSubstring); + } finally { + tempFile.delete(); + logbackLogger.detachAppender(listAppender); + } + } + + static Stream + testParseInvalidLogsMappingErrorsArguments() { + return Stream.of( + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " - key: \"someKey\"\n" + + " matches: [\"someValue\"]\n" + + " operator: equals\n" + + " configuration:\n" + + " DD_SERVICE: \"test\"\n", + "Missing 'origin' in selector"), + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " - origin: process_arguments\n" + + " key: \"-Dfoo\"\n" + + " matches: [\"bar\"]\n" + + " operator: equals\n", + "Missing 'configuration' in rule"), + arguments( + "apm_configuration_rules:\n" + + " - configuration:\n" + + " DD_SERVICE: \"test\"\n", + "Missing 'selectors' in rule"), + arguments( + "apm_configuration_rules:\n" + + " - selectors: \"not-a-list\"\n" + + " configuration:\n" + + " DD_SERVICE: \"test\"\n", + "'selectors' must be a list, but got: String"), + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " - \"not-a-map\"\n" + + " configuration:\n" + + " DD_SERVICE: \"test\"\n", + "Each selector must be a map, but got: String"), + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " - origin: process_arguments\n" + + " key: \"-Dfoo\"\n" + + " matches: [\"bar\"]\n" + + " operator: equals\n" + + " configuration: \"not-a-map\"\n", + "'configuration' must be a map, but got: String"), + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " - origin: process_arguments\n" + + " key: \"-Dfoo\"\n" + + " matches: [\"bar\"]\n" + + " operator: equals\n" + + " configuration: 12345\n", + "'configuration' must be a map, but got: Integer"), + arguments( + "apm_configuration_rules:\n" + " - \"not-a-map\"\n", + "Rule must be a map, but got: String"), + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " - origin: process_arguments\n" + + " key: \"-Dfoo\"\n" + + " matches: \"not-a-list\"\n" + + " operator: equals\n" + + " configuration:\n" + + " DD_SERVICE: \"test\"\n", + "'matches' must be a list, but got: String"), + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " - origin: process_arguments\n" + + " key: \"-Dfoo\"\n" + + " matches: [\"bar\"]\n" + + " configuration:\n" + + " DD_SERVICE: \"test\"\n", + "Missing 'operator' in selector"), + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " - origin: process_arguments\n" + + " key: \"-Dfoo\"\n" + + " matches: [\"bar\"]\n" + + " operator: 12345\n" + + " configuration:\n" + + " DD_SERVICE: \"test\"\n", + "'operator' must be a string, but got: Integer"), + arguments( + "apm_configuration_rules:\n" + + " - selectors:\n" + + " # origin is missing entirely, should trigger NullPointerException\n" + + " - key: \"-Dfoo\"\n" + + " matches: [\"bar\"]\n" + + " operator: equals\n", + "YAML mapping error in stable configuration file")); + } + + @Test + @SuppressForbidden + void testConfigIdExistsInConfigCollectorWhenUsingStableConfigSource() throws Exception { + Path filePath = Files.createTempFile("testFile_", ".yaml"); + String expectedConfigId = "123"; + + // Create YAML content with config_id and some configuration + String yamlContent = + "config_id: " + + expectedConfigId + + "\napm_configuration_default:\n DD_SERVICE: test-service\n DD_ENV: test-env\n"; + Files.write(filePath, yamlContent.getBytes()); + + // Clear any existing collected config + ConfigCollector.get().collect(); + + try { + StableConfigSource stableConfigSource = + new StableConfigSource(filePath.toString(), ConfigOrigin.LOCAL_STABLE_CONFIG); + + // Create ConfigProvider via reflection (constructor is private) + Constructor constructor = + ConfigProvider.class.getDeclaredConstructor(ConfigProvider.Source[].class); + constructor.setAccessible(true); + ConfigProvider configProvider = + constructor.newInstance((Object) new ConfigProvider.Source[] {stableConfigSource}); + + // Trigger config collection by getting a value + configProvider.getString("SERVICE", "default-service"); + + Map> collectedConfigs = + ConfigCollector.get().collect(); + Map localStableConfigs = + collectedConfigs.get(ConfigOrigin.LOCAL_STABLE_CONFIG); + assertNotNull(localStableConfigs, "No configs collected for LOCAL_STABLE_CONFIG origin"); + ConfigSetting serviceSetting = localStableConfigs.get("SERVICE"); + assertNotNull(serviceSetting, "No SERVICE setting collected"); + assertEquals(expectedConfigId, serviceSetting.configId); + assertEquals("test-service", serviceSetting.value); + assertEquals(ConfigOrigin.LOCAL_STABLE_CONFIG, serviceSetting.origin); + } finally { + Files.delete(filePath); + } + } + + private static void writeFileYaml( + Path filePath, String configId, Map defaultConfigs) throws IOException { + Map yamlData = new HashMap<>(); + + if (configId != null && !configId.isEmpty()) { + yamlData.put("config_id", configId); + } + + if (defaultConfigs != null && !defaultConfigs.isEmpty()) { + yamlData.put("apm_configuration_default", defaultConfigs); + } + + DumpSettings settings = DumpSettings.builder().build(); + Dump dump = new Dump(settings); + String yamlContent = dump.dumpToString(yamlData); + + try (FileWriter writer = new FileWriter(filePath.toFile())) { + writer.write(yamlContent); + } + } + + private static void writeFileRaw(Path filePath, String configId, String data) throws IOException { + String content = configId + "\n" + data; + Files.write(filePath, content.getBytes(), StandardOpenOption.WRITE); + } +} diff --git a/utils/container-utils/src/test/groovy/datadog/common/container/ContainerInfoTest.groovy b/utils/container-utils/src/test/groovy/datadog/common/container/ContainerInfoTest.groovy deleted file mode 100644 index 1b74763b5b3..00000000000 --- a/utils/container-utils/src/test/groovy/datadog/common/container/ContainerInfoTest.groovy +++ /dev/null @@ -1,365 +0,0 @@ -package datadog.common.container - -import datadog.trace.test.util.DDSpecification - -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.text.ParseException - -class ContainerInfoTest extends DDSpecification { - - def "CGroupInfo is parsed from individual lines"() { - when: - ContainerInfo.CGroupInfo cGroupInfo = ContainerInfo.parseLine(line) - - then: - cGroupInfo.getId() == id - cGroupInfo.getPath() == path - cGroupInfo.getControllers() == controllers - cGroupInfo.getContainerId() == containerId - cGroupInfo.podId == podId - - // Examples from container tagging rfc and Qard/container-info - where: - // spotless:off - id | controllers | path | containerId | podId | line - - // Docker examples - 13 | ["name=systemd"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "13:name=systemd:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 12 | ["pids"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "12:pids:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 11 | ["hugetlb"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "11:hugetlb:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 10 | ["net_prio"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "10:net_prio:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 9 | ["perf_event"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "9:perf_event:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 8 | ["net_cls"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "8:net_cls:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 7 | ["freezer"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "7:freezer:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 6 | ["devices"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "6:devices:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 5 | ["memory"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "5:memory:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 4 | ["blkio"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "4:blkio:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 3 | ["cpuacct"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "3:cpuacct:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 2 | ["cpu"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "2:cpu:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - 1 | ["cpuset"] | "/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | "1:cpuset:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" - - // Kubernates examples - 11 | ["perf_event"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "11:perf_event:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 10 | ["pids"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "10:pids:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 9 | ["memory"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "9:memory:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 8 | ["cpu", "cpuacct"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "8:cpu,cpuacct:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 7 | ["blkio"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "7:blkio:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 6 | ["cpuset"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "6:cpuset:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 5 | ["devices"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "5:devices:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 4 | ["freezer"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "4:freezer:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 3 | ["net_cls", "net_prio"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "3:net_cls,net_prio:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 2 | ["hugetlb"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "2:hugetlb:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - 1 | ["name=systemd"] | "/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | "1:name=systemd:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" - - //ECS examples - 9 | ["perf_event"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "9:perf_event:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - 8 | ["memory"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "8:memory:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - 7 | ["hugetlb"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "7:hugetlb:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - 6 | ["freezer"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "6:freezer:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - 5 | ["devices"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "5:devices:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - 4 | ["cpuset"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "4:cpuset:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - 3 | ["cpuacct"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "3:cpuacct:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - 2 | ["cpu"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "2:cpu:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - 1 | ["blkio"] | "/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | "1:blkio:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" - - //Fargate Examples - 11 | ["hugetlb"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "11:hugetlb:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 10 | ["pids"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "10:pids:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 9 | ["cpuset"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "9:cpuset:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 8 | ["net_cls", "net_prio"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "8:net_cls,net_prio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 7 | ["cpu", "cpuacct"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "7:cpu,cpuacct:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 6 | ["perf_event"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "6:perf_event:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 5 | ["freezer"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "5:freezer:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 4 | ["devices"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "4:devices:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 3 | ["blkio"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "3:blkio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 2 | ["memory"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "2:memory:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 1 | ["name=systemd"] | "/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | "1:name=systemd:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" - 1 | ["name=systemd"] | "/ecs/34dc0b5e626f2c5c4c5170e34b10e765-1234567890" | "34dc0b5e626f2c5c4c5170e34b10e765-1234567890" | null | "1:name=systemd:/ecs/34dc0b5e626f2c5c4c5170e34b10e765-1234567890" - - // PCF example - 1 | ["freezer"] | "/garden/6f265890-5165-7fab-6b52-18d1" | "6f265890-5165-7fab-6b52-18d1" | null | "1:freezer:/garden/6f265890-5165-7fab-6b52-18d1" - - //Reference impl examples - 1 | ["name=systemd"] | "/system.slice/docker-cde7c2bab394630a42d73dc610b9c57415dced996106665d427f6d0566594411.scope" | "cde7c2bab394630a42d73dc610b9c57415dced996106665d427f6d0566594411" | null | "1:name=systemd:/system.slice/docker-cde7c2bab394630a42d73dc610b9c57415dced996106665d427f6d0566594411.scope" - 1 | ["name=systemd"] | "/docker/051e2ee0bce99116029a13df4a9e943137f19f957f38ac02d6bad96f9b700f76/not_hex" | null | null | "1:name=systemd:/docker/051e2ee0bce99116029a13df4a9e943137f19f957f38ac02d6bad96f9b700f76/not_hex" - 1 | ["name=systemd"] | "/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod90d81341_92de_11e7_8cf2_507b9d4141fa.slice/crio-2227daf62df6694645fee5df53c1f91271546a9560e8600a525690ae252b7f63.scope" | "2227daf62df6694645fee5df53c1f91271546a9560e8600a525690ae252b7f63" | "90d81341_92de_11e7_8cf2_507b9d4141fa" | "1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod90d81341_92de_11e7_8cf2_507b9d4141fa.slice/crio-2227daf62df6694645fee5df53c1f91271546a9560e8600a525690ae252b7f63.scope" - // spotless:on - } - - def "Container info parsed from file content"() { - when: - ContainerInfo containerInfo = ContainerInfo.parse(content) - - then: - containerInfo.getContainerId() == containerId - containerInfo.getPodId() == podId - containerInfo.getCGroups().size() == size - - where: - // spotless:off - containerId | podId | size | content - // Docker - "3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860" | null | 13 | """13:name=systemd:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -12:pids:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -11:hugetlb:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -10:net_prio:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -9:perf_event:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -8:net_cls:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -7:freezer:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -6:devices:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -5:memory:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -4:blkio:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -3:cpuacct:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -2:cpu:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 -1:cpuset:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860""" - - // Kubernetes - "3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1" | "3d274242-8ee0-11e9-a8a6-1e68d864ef1a" | 11 | """11:perf_event:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -10:pids:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -9:memory:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -8:cpu,cpuacct:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -7:blkio:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -6:cpuset:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -5:devices:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -4:freezer:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -3:net_cls,net_prio:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -2:hugetlb:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 -1:name=systemd:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1""" - "7b8952daecf4c0e44bbcefe1b5c5ebc7b4839d4eefeccefe694709d3809b6199" | "2d3da189_6407_48e3_9ab6_78188d75e609" | 1 | "1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod2d3da189_6407_48e3_9ab6_78188d75e609.slice/docker-7b8952daecf4c0e44bbcefe1b5c5ebc7b4839d4eefeccefe694709d3809b6199.scope" - - // ECS - "38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce" | null | 9 | """9:perf_event:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce -8:memory:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce -7:hugetlb:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce -6:freezer:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce -5:devices:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce -4:cpuset:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce -3:cpuacct:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce -2:cpu:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce -1:blkio:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce""" - - // Fargate 1.3- - "432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da" | null | 11 | """11:hugetlb:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -10:pids:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -9:cpuset:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -8:net_cls,net_prio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -7:cpu,cpuacct:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -6:perf_event:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -5:freezer:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -4:devices:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -3:blkio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -2:memory:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -1:name=systemd:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da""" - - // Fargate 1.4+ - "34dc0b5e626f2c5c4c5170e34b10e765-1234567890" | null | 11 | """11:hugetlb:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -10:pids:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -9:cpuset:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -8:net_cls,net_prio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -7:cpu,cpuacct:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -6:perf_event:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -5:freezer:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -4:devices:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -3:blkio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -2:memory:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da -1:name=systemd:/ecs/34dc0b5e626f2c5c4c5170e34b10e765-1234567890""" - - // EKS Fargate cgroup with trailing cgroup v2 membership entry (0::...) - "cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc" | "defa568d-ff14-43d9-9a63-9e39ee9b39b4" | 13 | """12:misc:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -11:cpuset:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -10:perf_event:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -9:blkio:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -8:net_cls,net_prio:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -7:memory:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -6:cpu,cpuacct:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -5:pids:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -4:devices:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -3:hugetlb:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -2:freezer:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -1:name=systemd:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc -0::/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393""" - - // PCF file - "6f265890-5165-7fab-6b52-18d1" | null | 12 | """12:rdma:/ -11:net_cls,net_prio:/garden/6f265890-5165-7fab-6b52-18d1 -10:freezer:/garden/6f265890-5165-7fab-6b52-18d1 -9:devices:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1 -8:blkio:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1 -7:pids:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1 -6:memory:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1 -5:cpuset:/garden/6f265890-5165-7fab-6b52-18d1 -4:cpu,cpuacct:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1 -3:perf_event:/garden/6f265890-5165-7fab-6b52-18d1 -2:hugetlb:/garden/6f265890-5165-7fab-6b52-18d1 -1:name=systemd:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1""" - - // spotless:on - } - - def "ContainerInfo from empty file is empty"() { - when: - File f = File.createTempFile("container-info-test-", "-empty-file") - f.deleteOnExit() - Path p = Paths.get(f.path) - ContainerInfo containerInfo = ContainerInfo.fromProcFile(p) - - - then: - containerInfo.getContainerId() == null - containerInfo.getPodId() == null - containerInfo.getCGroups().size() == 0 - } - - def "ContainerInfo throws java.text.ParseException when given malformed procfile"() { - when: - File f = File.createTempFile("container-info-test-", "-malformed-file") - f.deleteOnExit() - f.write("This is not valid") - Path p = Paths.get(f.path) - ContainerInfo.fromProcFile(p) - - then: - thrown(ParseException) - } - - def "ContainerInfo tolerates missing container id and pod id in procfile"() { - when: - File f = File.createTempFile("container-info-test-", "-missing-container-id") - f.deleteOnExit() - f.write("1:cpuset:fake-path") - Path p = Paths.get(f.path) - ContainerInfo containerInfo = ContainerInfo.fromProcFile(p) - f.deleteOnExit() - - then: - containerInfo.getContainerId() == null - containerInfo.getPodId() == null - containerInfo.getCGroups().size() == 1 - } - - def "getIno(path) should return the same value as `ls -id path`"() { - when: - File f = File.createTempFile("container-info-test-", "-inode-file") - f.deleteOnExit() - Path path = f.toPath() - - then: - ContainerInfo.readInode(path) == readInode(path) - } - - private long readInode(Path path) { - ProcessBuilder pb = new ProcessBuilder("ls", "-id", path.toString()) - Process ps = pb.start() - BufferedReader reader = new BufferedReader(new InputStreamReader(ps.getInputStream())) - String line = reader.readLine() - reader.close() - ps.waitFor() - Long.parseLong(line.substring(0, line.indexOf(' '))) - } - - def "readEntityID return cid- if containerId is defined"() { - when: - ContainerInfo containerInfo = new ContainerInfo() - containerInfo.setContainerId(cid) - - then: - containerInfo.readEntityID(containerInfo, true, Paths.get("/sys/fs/cgroup")) == "cid-" + cid - - where: - cid | isHostCgroupNamespace - "cid" | true - "containerId" | false - } - - def "readEntityID return null if containerId is not defined and isHostCgroupNamespace"() { - when: - ContainerInfo containerInfo = new ContainerInfo() - containerInfo.setContainerId(cid) - - then: - containerInfo.readEntityID(containerInfo, true, Paths.get("/sys/fs/cgroup")) == null - - where: - cid << [null, ""] - } - - def "readEntityID return id- for '' controller"() { - setup: - File mountPath = File.createTempDir("container-info-test-", "-sys-fs-cgroup") - mountPath.deleteOnExit() - File file = File.createTempFile("container-info-test-", "-inode-file", mountPath) - file.deleteOnExit() - Path path = file.toPath() - Long ino = readInode(path) - - when: - ContainerInfo containerInfo = new ContainerInfo() - ContainerInfo.CGroupInfo cGroupInfo = new ContainerInfo.CGroupInfo() - cGroupInfo.setControllers(controllers) - cGroupInfo.setPath(file.getName()) - List cGroups = Arrays.asList(cGroupInfo) - containerInfo.setcGroups(cGroups) - - then: - containerInfo.readEntityID(containerInfo, false, mountPath.toPath()) == (hasEntityId ? "in-" + ino : null) - - where: - controllers | hasEntityId - Arrays.asList("", "memory") | true - Arrays.asList("memory", "") | true - Arrays.asList("") | true - Arrays.asList("memory") | false - } - - def "readEntityID return id- for 'memory' controller"() { - setup: - File mountPath = File.createTempDir("container-info-test-", "-sys-fs-cgroup") - mountPath.deleteOnExit() - File memoryController = Files.createDirectory(mountPath.toPath().resolve("memory")).toFile() - memoryController.deleteOnExit() - File file = File.createTempFile("container-info-test-", "-inode-file", memoryController) - file.deleteOnExit() - Path path = file.toPath() - Long ino = readInode(path) - - when: - ContainerInfo containerInfo = new ContainerInfo() - ContainerInfo.CGroupInfo cGroupInfo = new ContainerInfo.CGroupInfo() - cGroupInfo.setControllers(controllers) - cGroupInfo.setPath(file.getName()) - List cGroups = Arrays.asList(cGroupInfo) - containerInfo.setcGroups(cGroups) - - then: - containerInfo.readEntityID(containerInfo, false, mountPath.toPath()) == (hasEntityId ? "in-" + ino : null) - - where: - controllers | hasEntityId - Arrays.asList("", "memory") | true - Arrays.asList("memory", "") | true - Arrays.asList("memory") | true - Arrays.asList("") | false - } - - def "readEntityID return id- for a parent when path is /"() { - setup: - File mountPath = File.createTempDir("container-info-test-", "-sys-fs-cgroup") - mountPath.deleteOnExit() - File memoryController = Files.createDirectory(mountPath.toPath().resolve("memory")).toFile() - memoryController.deleteOnExit() - Long ino = readInode(memoryController.toPath()) - - when: - ContainerInfo containerInfo = new ContainerInfo() - ContainerInfo.CGroupInfo cGroupInfo = new ContainerInfo.CGroupInfo() - cGroupInfo.setControllers(Arrays.asList("memory")) - cGroupInfo.setPath("/") - List cGroups = Arrays.asList(cGroupInfo) - containerInfo.setcGroups(cGroups) - - then: - containerInfo.readEntityID(containerInfo, false, mountPath.toPath()) == "in-" + ino - } -} diff --git a/utils/container-utils/src/test/groovy/datadog/common/container/ServerlessInfoTest.groovy b/utils/container-utils/src/test/groovy/datadog/common/container/ServerlessInfoTest.groovy deleted file mode 100644 index 329fc8b2fc4..00000000000 --- a/utils/container-utils/src/test/groovy/datadog/common/container/ServerlessInfoTest.groovy +++ /dev/null @@ -1,47 +0,0 @@ -package datadog.common.container - -import datadog.trace.test.util.DDSpecification - -class ServerlessInfoTest extends DDSpecification { - - def "test serverless detection"() { - given: - environmentVariables.set(ServerlessInfo.AWS_FUNCTION_VARIABLE, functionName) - - when: - def info = new ServerlessInfo() - - then: - info.runningInServerlessEnvironment == serverlessEnv - info.functionName == functionName - - where: - functionName | serverlessEnv - null | false - "" | false - "someName" | true - } - - def "test serverless hasExtension false"() { - when: - def info = new ServerlessInfo() - then: - info.hasExtension() == false - } - - def "test serverless hasExtension false since the extension path is null"() { - when: - def info = new ServerlessInfo(null) - then: - info.hasExtension() == false - } - - def "test serverless hasExtension true"() { - when: - File f = File.createTempFile("fake-", "extension") - f.deleteOnExit() - def info = new ServerlessInfo(f.getAbsolutePath()) - then: - info.hasExtension() == true - } -} diff --git a/utils/container-utils/src/test/java/datadog/common/container/ContainerInfoTest.java b/utils/container-utils/src/test/java/datadog/common/container/ContainerInfoTest.java new file mode 100644 index 00000000000..6d45cb4a7bb --- /dev/null +++ b/utils/container-utils/src/test/java/datadog/common/container/ContainerInfoTest.java @@ -0,0 +1,372 @@ +package datadog.common.container; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import datadog.trace.test.util.DDJavaSpecification; +import de.thetaphi.forbiddenapis.SuppressForbidden; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.tabletest.junit.TableTest; + +@SuppressForbidden +public class ContainerInfoTest extends DDJavaSpecification { + + // spotless:off + @TableTest({ + "id | controllers | path | containerId | podId | line", + // Docker examples + "13 | [name=systemd] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 13:name=systemd:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + "12 | [pids] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 12:pids:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + "11 | [hugetlb] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 11:hugetlb:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + "10 | [net_prio] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 10:net_prio:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 9 | [perf_event] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 9:perf_event:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 8 | [net_cls] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 8:net_cls:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 7 | [freezer] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 7:freezer:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 6 | [devices] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 6:devices:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 5 | [memory] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 5:memory:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 4 | [blkio] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 4:blkio:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 3 | [cpuacct] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 3:cpuacct:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 2 | [cpu] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 2:cpu:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + " 1 | [cpuset] | /docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | 3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860 | | 1:cpuset:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", + // Kubernetes examples + "11 | [perf_event] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 11:perf_event:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + "10 | [pids] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 10:pids:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 9 | [memory] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 9:memory:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 8 | [cpu, cpuacct] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 8:cpu,cpuacct:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 7 | [blkio] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 7:blkio:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 6 | [cpuset] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 6:cpuset:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 5 | [devices] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 5:devices:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 4 | [freezer] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 4:freezer:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 3 | [net_cls, net_prio] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 3:net_cls,net_prio:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 2 | [hugetlb] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 2:hugetlb:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + " 1 | [name=systemd] | /kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1 | 3d274242-8ee0-11e9-a8a6-1e68d864ef1a | 1:name=systemd:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", + // ECS examples + " 9 | [perf_event] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 9:perf_event:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + " 8 | [memory] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 8:memory:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + " 7 | [hugetlb] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 7:hugetlb:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + " 6 | [freezer] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 6:freezer:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + " 5 | [devices] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 5:devices:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + " 4 | [cpuset] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 4:cpuset:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + " 3 | [cpuacct] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 3:cpuacct:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + " 2 | [cpu] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 2:cpu:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + " 1 | [blkio] | /ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | 38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce | | 1:blkio:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", + // Fargate examples + "11 | [hugetlb] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 11:hugetlb:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + "10 | [pids] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 10:pids:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 9 | [cpuset] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 9:cpuset:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 8 | [net_cls, net_prio] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 8:net_cls,net_prio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 7 | [cpu, cpuacct] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 7:cpu,cpuacct:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 6 | [perf_event] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 6:perf_event:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 5 | [freezer] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 5:freezer:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 4 | [devices] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 4:devices:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 3 | [blkio] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 3:blkio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 2 | [memory] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 2:memory:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 1 | [name=systemd] | /ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | 432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da | | 1:name=systemd:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", + " 1 | [name=systemd] | /ecs/34dc0b5e626f2c5c4c5170e34b10e765-1234567890 | 34dc0b5e626f2c5c4c5170e34b10e765-1234567890 | | 1:name=systemd:/ecs/34dc0b5e626f2c5c4c5170e34b10e765-1234567890", + // PCF example + " 1 | [freezer] | /garden/6f265890-5165-7fab-6b52-18d1 | 6f265890-5165-7fab-6b52-18d1 | | 1:freezer:/garden/6f265890-5165-7fab-6b52-18d1", + // Reference impl examples + " 1 | [name=systemd] | /system.slice/docker-cde7c2bab394630a42d73dc610b9c57415dced996106665d427f6d0566594411.scope | cde7c2bab394630a42d73dc610b9c57415dced996106665d427f6d0566594411 | | 1:name=systemd:/system.slice/docker-cde7c2bab394630a42d73dc610b9c57415dced996106665d427f6d0566594411.scope", + " 1 | [name=systemd] | /docker/051e2ee0bce99116029a13df4a9e943137f19f957f38ac02d6bad96f9b700f76/not_hex | | | 1:name=systemd:/docker/051e2ee0bce99116029a13df4a9e943137f19f957f38ac02d6bad96f9b700f76/not_hex", + " 1 | [name=systemd] | /kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod90d81341_92de_11e7_8cf2_507b9d4141fa.slice/crio-2227daf62df6694645fee5df53c1f91271546a9560e8600a525690ae252b7f63.scope | 2227daf62df6694645fee5df53c1f91271546a9560e8600a525690ae252b7f63 | 90d81341_92de_11e7_8cf2_507b9d4141fa | 1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod90d81341_92de_11e7_8cf2_507b9d4141fa.slice/crio-2227daf62df6694645fee5df53c1f91271546a9560e8600a525690ae252b7f63.scope" + }) + void cGroupInfoIsParsedFromIndividualLines( + int id, List controllers, String path, String containerId, String podId, String line) + throws ParseException { + ContainerInfo.CGroupInfo cGroupInfo = ContainerInfo.parseLine(line); + + assertEquals(id, cGroupInfo.getId()); + assertEquals(path, cGroupInfo.getPath()); + assertEquals(controllers, cGroupInfo.getControllers()); + assertEquals(containerId, cGroupInfo.getContainerId()); + assertEquals(podId, cGroupInfo.getPodId()); + } + // spotless:on + + @ParameterizedTest + @MethodSource("containerInfoParsedFromFileContentArguments") + void containerInfoParsedFromFileContent( + String containerId, String podId, int size, String content) throws Exception { + ContainerInfo containerInfo = ContainerInfo.parse(content); + + assertEquals(containerId, containerInfo.getContainerId()); + assertEquals(podId, containerInfo.getPodId()); + assertEquals(size, containerInfo.getCGroups().size()); + } + + static Stream + containerInfoParsedFromFileContentArguments() { + // spotless:off + return Stream.of( + // Docker + arguments("3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860", null, 13, + "13:name=systemd:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "12:pids:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "11:hugetlb:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "10:net_prio:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "9:perf_event:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "8:net_cls:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "7:freezer:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "6:devices:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "5:memory:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "4:blkio:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "3:cpuacct:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "2:cpu:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860\n" + + "1:cpuset:/docker/3726184226f5d3147c25fdeab5b60097e378e8a720503a5e19ecfdf29f869860"), + // Kubernetes + arguments("3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1", "3d274242-8ee0-11e9-a8a6-1e68d864ef1a", 11, + "11:perf_event:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "10:pids:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "9:memory:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "8:cpu,cpuacct:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "7:blkio:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "6:cpuset:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "5:devices:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "4:freezer:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "3:net_cls,net_prio:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "2:hugetlb:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1\n" + + "1:name=systemd:/kubepods/besteffort/pod3d274242-8ee0-11e9-a8a6-1e68d864ef1a/3e74d3fd9db4c9dd921ae05c2502fb984d0cde1b36e581b13f79c639da4518a1"), + arguments("7b8952daecf4c0e44bbcefe1b5c5ebc7b4839d4eefeccefe694709d3809b6199", "2d3da189_6407_48e3_9ab6_78188d75e609", 1, + "1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod2d3da189_6407_48e3_9ab6_78188d75e609.slice/docker-7b8952daecf4c0e44bbcefe1b5c5ebc7b4839d4eefeccefe694709d3809b6199.scope"), + // ECS + arguments("38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce", null, 9, + "9:perf_event:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce\n" + + "8:memory:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce\n" + + "7:hugetlb:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce\n" + + "6:freezer:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce\n" + + "5:devices:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce\n" + + "4:cpuset:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce\n" + + "3:cpuacct:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce\n" + + "2:cpu:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce\n" + + "1:blkio:/ecs/haissam-ecs-classic/5a0d5ceddf6c44c1928d367a815d890f/38fac3e99302b3622be089dd41e7ccf38aff368a86cc339972075136ee2710ce"), + // Fargate 1.3- + arguments("432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da", null, 11, + "11:hugetlb:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "10:pids:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "9:cpuset:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "8:net_cls,net_prio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "7:cpu,cpuacct:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "6:perf_event:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "5:freezer:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "4:devices:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "3:blkio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "2:memory:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "1:name=systemd:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da"), + // Fargate 1.4+ + arguments("34dc0b5e626f2c5c4c5170e34b10e765-1234567890", null, 11, + "11:hugetlb:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "10:pids:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "9:cpuset:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "8:net_cls,net_prio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "7:cpu,cpuacct:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "6:perf_event:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "5:freezer:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "4:devices:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "3:blkio:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "2:memory:/ecs/55091c13-b8cf-4801-b527-f4601742204d/432624d2150b349fe35ba397284dea788c2bf66b885d14dfc1569b01890ca7da\n" + + "1:name=systemd:/ecs/34dc0b5e626f2c5c4c5170e34b10e765-1234567890"), + // EKS Fargate with trailing cgroup v2 membership entry + arguments("cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc", "defa568d-ff14-43d9-9a63-9e39ee9b39b4", 13, + "12:misc:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "11:cpuset:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "10:perf_event:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "9:blkio:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "8:net_cls,net_prio:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "7:memory:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "6:cpu,cpuacct:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "5:pids:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "4:devices:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "3:hugetlb:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "2:freezer:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "1:name=systemd:/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393/kubepods/burstable/poddefa568d-ff14-43d9-9a63-9e39ee9b39b4/cf1241bbf80ea91eebdd28bf719057380997ca4b0cea16869393b905fb6d52bc\n" + + "0::/ecs/545b896a072744d186c7fb09a45ec172/545b896a072744d186c7fb09a45ec172-3057940393"), + // PCF + arguments("6f265890-5165-7fab-6b52-18d1", null, 12, + "12:rdma:/\n" + + "11:net_cls,net_prio:/garden/6f265890-5165-7fab-6b52-18d1\n" + + "10:freezer:/garden/6f265890-5165-7fab-6b52-18d1\n" + + "9:devices:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1\n" + + "8:blkio:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1\n" + + "7:pids:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1\n" + + "6:memory:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1\n" + + "5:cpuset:/garden/6f265890-5165-7fab-6b52-18d1\n" + + "4:cpu,cpuacct:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1\n" + + "3:perf_event:/garden/6f265890-5165-7fab-6b52-18d1\n" + + "2:hugetlb:/garden/6f265890-5165-7fab-6b52-18d1\n" + + "1:name=systemd:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1") + ); + // spotless:on + } + + @Test + void containerInfoFromEmptyFileIsEmpty() throws Exception { + File f = File.createTempFile("container-info-test-", "-empty-file"); + f.deleteOnExit(); + Path p = Paths.get(f.getPath()); + + ContainerInfo containerInfo = ContainerInfo.fromProcFile(p); + + assertNull(containerInfo.getContainerId()); + assertNull(containerInfo.getPodId()); + assertEquals(0, containerInfo.getCGroups().size()); + } + + @Test + void containerInfoThrowsParseExceptionWhenGivenMalformedProcfile() throws Exception { + File f = File.createTempFile("container-info-test-", "-malformed-file"); + f.deleteOnExit(); + Files.write(f.toPath(), "This is not valid".getBytes()); + Path p = Paths.get(f.getPath()); + + assertThrows(ParseException.class, () -> ContainerInfo.fromProcFile(p)); + } + + @Test + void containerInfoToleratesMissingContainerIdAndPodIdInProcfile() throws Exception { + File f = File.createTempFile("container-info-test-", "-missing-container-id"); + f.deleteOnExit(); + Files.write(f.toPath(), "1:cpuset:fake-path".getBytes()); + Path p = Paths.get(f.getPath()); + + ContainerInfo containerInfo = ContainerInfo.fromProcFile(p); + + assertNull(containerInfo.getContainerId()); + assertNull(containerInfo.getPodId()); + assertEquals(1, containerInfo.getCGroups().size()); + } + + @Test + void getInoPathShouldReturnSameValueAsLsIdPath() throws Exception { + File f = File.createTempFile("container-info-test-", "-inode-file"); + f.deleteOnExit(); + Path path = f.toPath(); + + assertEquals(readInode(path), ContainerInfo.readInode(path)); + } + + // spotless:off + @TableTest({ + "cid | isHostCgroupNamespace", + "cid | true ", + "containerId | false " + }) + void readEntityIDReturnCidContainerIdIfContainerIdIsDefined( + String cid, boolean isHostCgroupNamespace) { + ContainerInfo containerInfo = new ContainerInfo(); + containerInfo.setContainerId(cid); + + assertEquals("cid-" + cid, + ContainerInfo.readEntityID(containerInfo, true, Paths.get("/sys/fs/cgroup"))); + } + // spotless:on + + @TableTest({"cid", " ", "'' "}) + void readEntityIDReturnNullIfContainerIdIsNotDefinedAndIsHostCgroupNamespace(String cid) { + ContainerInfo containerInfo = new ContainerInfo(); + containerInfo.setContainerId(cid); + + assertNull(ContainerInfo.readEntityID(containerInfo, true, Paths.get("/sys/fs/cgroup"))); + } + + // spotless:off + @TableTest({ + "controllers | hasEntityId", + "['', memory] | true ", + "[memory, ''] | true ", + "[''] | true ", + "[memory] | false " + }) + void readEntityIDReturnIdInoForEmptyController( + List controllers, boolean hasEntityId) throws Exception { + File mountPath = createTempDir(); + File file = File.createTempFile("container-info-test-", "-inode-file", mountPath); + file.deleteOnExit(); + long ino = readInode(file.toPath()); + + ContainerInfo containerInfo = new ContainerInfo(); + ContainerInfo.CGroupInfo cGroupInfo = new ContainerInfo.CGroupInfo(); + cGroupInfo.setControllers(controllers); + cGroupInfo.setPath(file.getName()); + containerInfo.setcGroups(Arrays.asList(cGroupInfo)); + + String expected = hasEntityId ? "in-" + ino : null; + assertEquals(expected, ContainerInfo.readEntityID(containerInfo, false, mountPath.toPath())); + } + + @TableTest({ + "controllers | hasEntityId", + "['', memory] | true ", + "[memory, ''] | true ", + "[memory] | true ", + "[''] | false " + }) + void readEntityIDReturnIdInoForMemoryController( + List controllers, boolean hasEntityId) throws Exception { + File mountPath = createTempDir(); + File memoryController = + Files.createDirectory(mountPath.toPath().resolve("memory")).toFile(); + memoryController.deleteOnExit(); + File file = File.createTempFile("container-info-test-", "-inode-file", memoryController); + file.deleteOnExit(); + long ino = readInode(file.toPath()); + + ContainerInfo containerInfo = new ContainerInfo(); + ContainerInfo.CGroupInfo cGroupInfo = new ContainerInfo.CGroupInfo(); + cGroupInfo.setControllers(controllers); + cGroupInfo.setPath(file.getName()); + containerInfo.setcGroups(Arrays.asList(cGroupInfo)); + + String expected = hasEntityId ? "in-" + ino : null; + assertEquals(expected, ContainerInfo.readEntityID(containerInfo, false, mountPath.toPath())); + } + // spotless:on + + @Test + void readEntityIDReturnIdInoForParentWhenPathIsSlash() throws Exception { + File mountPath = createTempDir(); + File memoryController = Files.createDirectory(mountPath.toPath().resolve("memory")).toFile(); + memoryController.deleteOnExit(); + long ino = readInode(memoryController.toPath()); + + ContainerInfo containerInfo = new ContainerInfo(); + ContainerInfo.CGroupInfo cGroupInfo = new ContainerInfo.CGroupInfo(); + cGroupInfo.setControllers(Arrays.asList("memory")); + cGroupInfo.setPath("/"); + containerInfo.setcGroups(Arrays.asList(cGroupInfo)); + + assertEquals("in-" + ino, ContainerInfo.readEntityID(containerInfo, false, mountPath.toPath())); + } + + private static File createTempDir() throws IOException { + File dir = File.createTempFile("container-info-test-", "-sys-fs-cgroup"); + dir.delete(); + dir.mkdirs(); + dir.deleteOnExit(); + return dir; + } + + private static long readInode(Path path) throws IOException, InterruptedException { + ProcessBuilder pb = new ProcessBuilder("ls", "-id", path.toString()); + Process ps = pb.start(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(ps.getInputStream()))) { + String line = reader.readLine(); + ps.waitFor(); + return Long.parseLong(line.substring(0, line.indexOf(' '))); + } + } +} diff --git a/utils/container-utils/src/test/java/datadog/common/container/ServerlessInfoTest.java b/utils/container-utils/src/test/java/datadog/common/container/ServerlessInfoTest.java new file mode 100644 index 00000000000..65d0eb84118 --- /dev/null +++ b/utils/container-utils/src/test/java/datadog/common/container/ServerlessInfoTest.java @@ -0,0 +1,72 @@ +package datadog.common.container; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.test.util.ControllableEnvironmentVariables; +import datadog.trace.test.util.DDJavaSpecification; +import java.io.File; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.tabletest.junit.TableTest; + +public class ServerlessInfoTest extends DDJavaSpecification { + + // AWS_LAMBDA_FUNCTION_NAME is ServerlessInfo.AWS_FUNCTION_VARIABLE (private constant) + private static final String AWS_FUNCTION_VARIABLE = "AWS_LAMBDA_FUNCTION_NAME"; + + private static final ControllableEnvironmentVariables environmentVariables = + ControllableEnvironmentVariables.setup(); + + @AfterEach + void clearEnvVars() { + environmentVariables.clear(); + } + + @TableTest({ + "functionName | serverlessEnv", + " | false ", + "'' | false ", + "someName | true " + }) + void testServerlessDetection(String functionName, boolean serverlessEnv) { + environmentVariables.set(AWS_FUNCTION_VARIABLE, functionName); + + ServerlessInfo info = new ServerlessInfo(); + + assertEquals(serverlessEnv, info.isRunningInServerlessEnvironment()); + assertEquals(functionName, info.getFunctionName()); + } + + @Test + void testServerlessHasExtensionFalse() { + ServerlessInfo info = new ServerlessInfo(); + + assertFalse(info.hasExtension()); + } + + @Test + void testServerlessHasExtensionFalseSinceExtensionPathIsNull() throws Exception { + // ServerlessInfo(String extensionPath) is private — access via reflection + java.lang.reflect.Constructor constructor = + ServerlessInfo.class.getDeclaredConstructor(String.class); + constructor.setAccessible(true); + ServerlessInfo info = constructor.newInstance((Object) null); + + assertFalse(info.hasExtension()); + } + + @Test + void testServerlessHasExtensionTrue() throws Exception { + File f = File.createTempFile("fake-", "extension"); + f.deleteOnExit(); + + java.lang.reflect.Constructor constructor = + ServerlessInfo.class.getDeclaredConstructor(String.class); + constructor.setAccessible(true); + ServerlessInfo info = constructor.newInstance(f.getAbsolutePath()); + + assertTrue(info.hasExtension()); + } +} diff --git a/utils/junit-utils/src/main/java/datadog/trace/junit/utils/tabletest/BoxedValueConverter.java b/utils/junit-utils/src/main/java/datadog/trace/junit/utils/tabletest/BoxedValueConverter.java new file mode 100644 index 00000000000..185a1f55b7a --- /dev/null +++ b/utils/junit-utils/src/main/java/datadog/trace/junit/utils/tabletest/BoxedValueConverter.java @@ -0,0 +1,51 @@ +package datadog.trace.junit.utils.tabletest; + +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.params.converter.ArgumentConversionException; +import org.junit.jupiter.params.converter.ArgumentConverter; + +/** + * Converts a String cell value to the most specific boxed Java primitive type. Use with + * {@code @ConvertWith(BoxedValueConverter.class)} on {@code Object}-typed parameters when the test + * needs actual typed values (e.g. {@code Float} not {@code String "2.33f"}). + * + *

Conversion rules: + * + *

    + *
  • blank/null -> null + *
  • {@code "true"}/{@code "false"} -> {@link Boolean} + *
  • ends with {@code "f"} -> {@link Float} + *
  • contains {@code "."} -> {@link Double} + *
  • parseable as integer -> {@link Integer} + *
  • otherwise -> {@link String} + *
+ */ +public class BoxedValueConverter implements ArgumentConverter { + + @Override + public Object convert(Object source, ParameterContext context) + throws ArgumentConversionException { + if (source == null) return null; + String s = source.toString(); + if (s.isEmpty()) return null; + if ("true".equals(s)) return Boolean.TRUE; + if ("false".equals(s)) return Boolean.FALSE; + if (s.endsWith("f")) { + try { + return Float.parseFloat(s.substring(0, s.length() - 1)); + } catch (NumberFormatException ignored) { + } + } + if (s.contains(".")) { + try { + return Double.parseDouble(s); + } catch (NumberFormatException ignored) { + } + } + try { + return Integer.parseInt(s); + } catch (NumberFormatException ignored) { + } + return s; + } +} diff --git a/utils/time-utils/src/test/groovy/datadog/trace/api/time/TimeUtilsTest.groovy b/utils/time-utils/src/test/groovy/datadog/trace/api/time/TimeUtilsTest.groovy deleted file mode 100644 index 0a569b57c93..00000000000 --- a/utils/time-utils/src/test/groovy/datadog/trace/api/time/TimeUtilsTest.groovy +++ /dev/null @@ -1,50 +0,0 @@ -package datadog.trace.api.time - -import datadog.trace.test.util.DDSpecification - -class TimeUtilsTest extends DDSpecification { - - def "test simple delay parsing"() { - when: - long delay = TimeUtils.parseSimpleDelay(delayString) - - then: - delay == expected - - where: - delayString | expected - null | -1 - "" | -1 - "foo" | -1 - "-8" | -1 - "-1" | -1 - "0" | 0 - "1" | 1 - "2" | 2 - "3" | 3 - "0s" | 0 - "1s" | 1 - "2s" | 2 - "3s" | 3 - "0m" | 0 - "1m" | 60 - "2m" | 120 - "3m" | 180 - "0h" | 0 - "1h" | 3600 - "2h" | 7200 - "3h" | 10800 - "0S" | 0 - "1S" | 1 - "2S" | 2 - "3S" | 3 - "0M" | 0 - "1M" | 60 - "2M" | 120 - "3M" | 180 - "0H" | 0 - "1H" | 3600 - "2H" | 7200 - "3H" | 10800 - } -} diff --git a/utils/time-utils/src/test/java/datadog/trace/api/time/TimeUtilsTest.java b/utils/time-utils/src/test/java/datadog/trace/api/time/TimeUtilsTest.java new file mode 100644 index 00000000000..1b105d78f4d --- /dev/null +++ b/utils/time-utils/src/test/java/datadog/trace/api/time/TimeUtilsTest.java @@ -0,0 +1,51 @@ +package datadog.trace.api.time; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import datadog.trace.test.util.DDJavaSpecification; +import org.tabletest.junit.TableTest; + +public class TimeUtilsTest extends DDJavaSpecification { + + // spotless:off + @TableTest({ + "delayString | expected", + " | -1 ", + "'' | -1 ", + "foo | -1 ", + "-8 | -1 ", + "-1 | -1 ", + "0 | 0 ", + "1 | 1 ", + "2 | 2 ", + "3 | 3 ", + "0s | 0 ", + "1s | 1 ", + "2s | 2 ", + "3s | 3 ", + "0m | 0 ", + "1m | 60 ", + "2m | 120 ", + "3m | 180 ", + "0h | 0 ", + "1h | 3600 ", + "2h | 7200 ", + "3h | 10800 ", + "0S | 0 ", + "1S | 1 ", + "2S | 2 ", + "3S | 3 ", + "0M | 0 ", + "1M | 60 ", + "2M | 120 ", + "3M | 180 ", + "0H | 0 ", + "1H | 3600 ", + "2H | 7200 ", + "3H | 10800 " + }) + // spotless:on + void testSimpleDelayParsing(String delayString, long expected) { + assertEquals(expected, TimeUtils.parseSimpleDelay(delayString)); + } +}