diff --git a/.ci/shared.sh b/.ci/shared.sh index 0c6ff0e46..d3440caf8 100644 --- a/.ci/shared.sh +++ b/.ci/shared.sh @@ -23,6 +23,8 @@ export ELASTIC_APM_PHP_TESTS_APP_CODE_HOST_KINDS_SHORT_NAMES=("${ELASTIC_APM_PHP export ELASTIC_APM_PHP_TESTS_LEAF_GROUPS_SHORT_NAMES=(no_ext_svc with_ext_svc) export ELASTIC_APM_PHP_TESTS_GROUPS_SHORT_NAMES=("${ELASTIC_APM_PHP_TESTS_LEAF_GROUPS_SHORT_NAMES[@]}" smoke) +export ELASTIC_APM_PHP_TESTS_AGENT_ENABLED_CONFIG_DEFAULT=false + function ensureSyslogIsRunningImpl () { if ps -ef | grep -v 'grep' | grep -q 'syslogd' ; then echo 'Syslog is already started.' diff --git a/.ci/static-check-unit-test.sh b/.ci/static-check-unit-test.sh index d2c485f69..9c4ada0c4 100755 --- a/.ci/static-check-unit-test.sh +++ b/.ci/static-check-unit-test.sh @@ -42,8 +42,14 @@ runPhpCoposerInstall composer run-script static_check # Run unit tests +composerCommandToRunUnitTests=(composer run-script --) phpUnitConfigFile=$(php ./tests/ElasticApmTests/Util/runSelectPhpUnitConfigFile.php --tests-type=unit) -composer run-script -- run_unit_tests_custom_config -c "${phpUnitConfigFile}" +composerCommandToRunUnitTests=("${composerCommandToRunUnitTests[@]}" run_unit_tests_custom_config -c "${phpUnitConfigFile}") +if [ "${ELASTIC_APM_PHP_TESTS_AGENT_ENABLED_CONFIG_DEFAULT:?}" == "false" ]; then + composerCommandToRunUnitTests=("${composerCommandToRunUnitTests[@]}" --filter AgentEnabledConfigUnitTest) +fi +echo "composerCommandToRunUnitTests: ${composerCommandToRunUnitTests[@]}" +"${composerCommandToRunUnitTests[@]}" ls -l ./build/unit-tests-phpunit-junit.xml # Generate junit output for phpstan diff --git a/.ci/validate_agent_installation.sh b/.ci/validate_agent_installation.sh index 03e78a845..23d6f7309 100755 --- a/.ci/validate_agent_installation.sh +++ b/.ci/validate_agent_installation.sh @@ -22,12 +22,16 @@ function runComponentTests () { phpUnitConfigFile=$(php ./tests/ElasticApmTests/Util/runSelectPhpUnitConfigFile.php --tests-type=component) composerCommand=("${composerCommand[@]}" -c "${phpUnitConfigFile}") - if [ -n "${ELASTIC_APM_PHP_TESTS_GROUP}" ] ; then - composerCommand=("${composerCommand[@]}" --group "${ELASTIC_APM_PHP_TESTS_GROUP}") - fi - - if [ -n "${ELASTIC_APM_PHP_TESTS_FILTER}" ] ; then - composerCommand=("${composerCommand[@]}" --filter "${ELASTIC_APM_PHP_TESTS_FILTER}") + if [ "${ELASTIC_APM_PHP_TESTS_AGENT_ENABLED_CONFIG_DEFAULT:?}" == "true" ]; then + if [ -n "${ELASTIC_APM_PHP_TESTS_GROUP}" ] ; then + composerCommand=("${composerCommand[@]}" --group "${ELASTIC_APM_PHP_TESTS_GROUP}") + fi + + if [ -n "${ELASTIC_APM_PHP_TESTS_FILTER}" ] ; then + composerCommand=("${composerCommand[@]}" --filter "${ELASTIC_APM_PHP_TESTS_FILTER}") + fi + else + composerCommand=("${composerCommand[@]}" --filter AgentEnabledConfigComponentTest) fi local initialTimeoutInMinutes=30 diff --git a/agent/native/ext/ConfigManager.cpp b/agent/native/ext/ConfigManager.cpp index 2f7005e73..60f975af0 100644 --- a/agent/native/ext/ConfigManager.cpp +++ b/agent/native/ext/ConfigManager.cpp @@ -1034,7 +1034,7 @@ static void initOptionsMetadata( OptionMetadata* optsMeta ) buildBoolOptionMetadata, enabled, ELASTIC_APM_CFG_OPT_NAME_ENABLED, - /* defaultValue: */ true ); + /* defaultValue: */ false ); ELASTIC_APM_INIT_METADATA( buildStringOptionMetadata, diff --git a/agent/native/ext/lifecycle.cpp b/agent/native/ext/lifecycle.cpp index bb0429527..873165c5c 100644 --- a/agent/native/ext/lifecycle.cpp +++ b/agent/native/ext/lifecycle.cpp @@ -500,6 +500,22 @@ static void unregisterErrorAndExceptionHooks() { } +void logImportantAgentInfo( const ConfigSnapshot* config, String calledFromFunc ) +{ + ELASTIC_APM_LOG_INFO( + "Custom build based on version: %s" + "; Custom changes:" + " * agent is disabled by default (enabled: false)" + "; config->enabled: %s." + "; SAPI module name: %s" + "; Called from: %s" + , PHP_ELASTIC_APM_VERSION + , boolToString( config->enabled ) + , getPhpSapiModuleName() + , calledFromFunc + ); +} + void elasticApmModuleInit( int moduleType, int moduleNumber ) { ELASTIC_APM_LOG_DIRECT_DEBUG( "%s entered: moduleType: %d, moduleNumber: %d, parent PID: %d", __FUNCTION__, moduleType, moduleNumber, (int)(getParentProcessId()) ); @@ -526,10 +542,11 @@ void elasticApmModuleInit( int moduleType, int moduleNumber ) ELASTIC_APM_CALL_IF_FAILED_GOTO( ensureLoggerInitialConfigIsLatest( tracer ) ); ELASTIC_APM_CALL_IF_FAILED_GOTO( ensureAllComponentsHaveLatestConfig( tracer ) ); - logSupportabilityInfo( logLevel_debug ); - config = getTracerCurrentConfigSnapshot( tracer ); + logImportantAgentInfo( config, __FUNCTION__ ); + logSupportabilityInfo( logLevel_debug ); + if ( ! config->enabled ) { resultCode = resultSuccess; @@ -580,6 +597,8 @@ void elasticApmModuleShutdown( int moduleType, int moduleNumber ) Tracer* const tracer = getGlobalTracer(); const ConfigSnapshot* const config = getTracerCurrentConfigSnapshot( tracer ); + logImportantAgentInfo( config, __FUNCTION__ ); + if ( ! config->enabled ) { resultCode = resultSuccess; diff --git a/agent/native/ext/tests/config_defaults.phpt b/agent/native/ext/tests/config_defaults.phpt index d949cbaae..dbc3618c4 100644 --- a/agent/native/ext/tests/config_defaults.phpt +++ b/agent/native/ext/tests/config_defaults.phpt @@ -25,9 +25,11 @@ elasticApmAssertSame("getenv('ELASTIC_APM_ENABLED')", getenv('ELASTIC_APM_ENABLE elasticApmAssertEqual("ini_get('elastic_apm.enabled')", ini_get('elastic_apm.enabled'), false); -elasticApmAssertSame("elastic_apm_is_enabled()", elastic_apm_is_enabled(), true); - -elasticApmAssertSame("elastic_apm_get_config_option_by_name('enabled')", elastic_apm_get_config_option_by_name('enabled'), true); +if (($agentEnabledConfigDefaultEnvVar = getenv('ELASTIC_APM_PHP_TESTS_AGENT_ENABLED_CONFIG_DEFAULT')) !== false) { + $agentEnabledConfigDefault = json_decode($agentEnabledConfigDefaultEnvVar); + elasticApmAssertSame("elastic_apm_is_enabled()", elastic_apm_is_enabled(), $agentEnabledConfigDefault); + elasticApmAssertSame("elastic_apm_get_config_option_by_name('enabled')", elastic_apm_get_config_option_by_name('enabled'), $agentEnabledConfigDefault); +} ////////////////////////////////////////////// /////////////// log_file diff --git a/agent/native/ext/tests/config_setting_to_invalid_values_using_env_vars.phpt b/agent/native/ext/tests/config_setting_to_invalid_values_using_env_vars.phpt index 78ee5e4bb..cdc65f351 100644 --- a/agent/native/ext/tests/config_setting_to_invalid_values_using_env_vars.phpt +++ b/agent/native/ext/tests/config_setting_to_invalid_values_using_env_vars.phpt @@ -19,9 +19,11 @@ require __DIR__ . '/../tests_util/tests_util.php'; elasticApmAssertSame("getenv('ELASTIC_APM_ENABLED')", getenv('ELASTIC_APM_ENABLED'), 'not_valid_boolean_value'); -elasticApmAssertSame("elastic_apm_is_enabled()", elastic_apm_is_enabled(), true); - -elasticApmAssertSame("elastic_apm_get_config_option_by_name('enabled')", elastic_apm_get_config_option_by_name('enabled'), true); +if (($agentEnabledConfigDefaultEnvVar = getenv('ELASTIC_APM_PHP_TESTS_AGENT_ENABLED_CONFIG_DEFAULT')) !== false) { + $agentEnabledConfigDefault = json_decode($agentEnabledConfigDefaultEnvVar); + elasticApmAssertSame("elastic_apm_is_enabled()", elastic_apm_is_enabled(), $agentEnabledConfigDefault); + elasticApmAssertSame("elastic_apm_get_config_option_by_name('enabled')", elastic_apm_get_config_option_by_name('enabled'), $agentEnabledConfigDefault); +} ////////////////////////////////////////////// /////////////// assert_level diff --git a/agent/native/ext/tests/config_setting_to_invalid_values_using_ini.phpt b/agent/native/ext/tests/config_setting_to_invalid_values_using_ini.phpt index df8e03fb1..99321c964 100644 --- a/agent/native/ext/tests/config_setting_to_invalid_values_using_ini.phpt +++ b/agent/native/ext/tests/config_setting_to_invalid_values_using_ini.phpt @@ -19,9 +19,11 @@ require __DIR__ . '/../tests_util/tests_util.php'; elasticApmAssertEqual("ini_get('elastic_apm.enabled')", ini_get('elastic_apm.enabled'), 'not valid boolean value'); -elasticApmAssertSame("elastic_apm_get_config_option_by_name('enabled')", elastic_apm_get_config_option_by_name('enabled'), true); - -elasticApmAssertSame("elastic_apm_is_enabled()", elastic_apm_is_enabled(), true); +if (($agentEnabledConfigDefaultEnvVar = getenv('ELASTIC_APM_PHP_TESTS_AGENT_ENABLED_CONFIG_DEFAULT')) !== false) { + $agentEnabledConfigDefault = json_decode($agentEnabledConfigDefaultEnvVar); + elasticApmAssertSame("elastic_apm_is_enabled()", elastic_apm_is_enabled(), $agentEnabledConfigDefault); + elasticApmAssertSame("elastic_apm_get_config_option_by_name('enabled')", elastic_apm_get_config_option_by_name('enabled'), $agentEnabledConfigDefault); +} ////////////////////////////////////////////// /////////////// assert_level diff --git a/agent/native/ext/util_for_PHP.cpp b/agent/native/ext/util_for_PHP.cpp index 450e91eca..725cb2b05 100644 --- a/agent/native/ext/util_for_PHP.cpp +++ b/agent/native/ext/util_for_PHP.cpp @@ -275,6 +275,11 @@ ResultCode callPhpFunctionRetZval( StringView phpFunctionName, uint32_t argsCoun return callPhpFunction( phpFunctionName, argsCount, args, consumeZvalRetVal, retVal ); } +String getPhpSapiModuleName() +{ + return sapi_module.name; +} + bool isPhpRunningAsCliScript() { return strcmp( sapi_module.name, "cli" ) == 0; diff --git a/agent/native/ext/util_for_PHP.h b/agent/native/ext/util_for_PHP.h index 7c6c59442..bab557ef2 100644 --- a/agent/native/ext/util_for_PHP.h +++ b/agent/native/ext/util_for_PHP.h @@ -85,6 +85,7 @@ ResultCode callPhpFunctionRetZval( StringView phpFunctionName, uint32_t argsCoun void getArgsFromZendExecuteData( zend_execute_data *execute_data, size_t dstArraySize, zval dstArray[], uint32_t* argsCount ); +String getPhpSapiModuleName(); bool isPhpRunningAsCliScript(); bool detectOpcachePreload(); bool isScriptRestricedByOpcacheAPI(); diff --git a/agent/php/ElasticApm/Impl/Config/AllOptionsMetadata.php b/agent/php/ElasticApm/Impl/Config/AllOptionsMetadata.php index e3bd9ecd3..90cbbfbf0 100644 --- a/agent/php/ElasticApm/Impl/Config/AllOptionsMetadata.php +++ b/agent/php/ElasticApm/Impl/Config/AllOptionsMetadata.php @@ -93,7 +93,7 @@ public static function get(): array OptionNames::DEV_INTERNAL => new NullableWildcardListOptionMetadata(), OptionNames::DISABLE_INSTRUMENTATIONS => new NullableWildcardListOptionMetadata(), OptionNames::DISABLE_SEND => new BoolOptionMetadata(/* default */ false), - OptionNames::ENABLED => new BoolOptionMetadata(/* default */ true), + OptionNames::ENABLED => new BoolOptionMetadata(/* default */ false), OptionNames::ENVIRONMENT => new NullableStringOptionMetadata(), OptionNames::GLOBAL_LABELS => new NullableLabelsOptionMetadata(), OptionNames::HOSTNAME => new NullableStringOptionMetadata(), diff --git a/tests/ElasticApmTests/ComponentTests/AgentEnabledConfigComponentTest.php b/tests/ElasticApmTests/ComponentTests/AgentEnabledConfigComponentTest.php new file mode 100644 index 000000000..d6414c445 --- /dev/null +++ b/tests/ElasticApmTests/ComponentTests/AgentEnabledConfigComponentTest.php @@ -0,0 +1,112 @@ +getBool(OptionNames::ENABLED); + + /** + * elastic_apm_* functions are provided by the elastic_apm extension + * + * @noinspection PhpFullyQualifiedNameUsageInspection, PhpUndefinedFunctionInspection + * @phpstan-ignore-next-line + */ + $agentEnabledAsSeenByNativePart = \elastic_apm_is_enabled(); + self::assertSame($agentEnabledExpected, $agentEnabledAsSeenByNativePart); + } + + /** + * @return iterable + */ + public static function dataProviderForTestAgentEnabledConfig(): iterable + { + $result = (new DataProviderForTestBuilder()) + ->addKeyedDimensionAllValuesCombinable(OptionNames::ENABLED, [null, true, false]) + ->build(); + + return DataProviderForTestBuilder::convertEachDataSetToMixedMap($result); + } + + /** + * @dataProvider dataProviderForTestAgentEnabledConfig + */ + public function testAgentEnabledConfig(MixedMap $testArgs): void + { + AssertMessageStack::newScope(/* out */ $dbgCtx, AssertMessageStack::funcArgs()); + + $testCaseHandle = $this->getTestCaseHandle(); + $appCodeHost = $testCaseHandle->ensureMainAppCodeHost( + function (AppCodeHostParams $appCodeParams) use ($testArgs): void { + self::setConfigIfNotNull($testArgs, OptionNames::ENABLED, $appCodeParams); + } + ); + + $agentEnabledTestArg = $testArgs->getNullableBool(OptionNames::ENABLED); + $agentEnabledActual = $appCodeHost->appCodeHostParams->getEffectiveAgentConfig()->enabled(); + if (($agentEnabledExpected = $agentEnabledTestArg ?? AmbientContextForTests::testConfig()->agentEnabledConfigDefault) !== null) { + self::assertSame($agentEnabledExpected, $agentEnabledActual); + } + + $appCodeHost->sendRequest( + AppCodeTarget::asRouted([__CLASS__, 'appCodeForTestAgentEnabledConfig']), + function (AppCodeRequestParams $appCodeRequestParams) use ($agentEnabledActual): void { + $appCodeRequestParams->setAppCodeArgs([OptionNames::ENABLED => $agentEnabledActual]); + } + ); + + if ($agentEnabledActual) { + $dataFromAgent = $this->waitForOneEmptyTransaction($testCaseHandle); + self::assertCount(1, $dataFromAgent->idToTransaction); + self::assertCount(1, $dataFromAgent->metadatas); + } else { + $logger = self::getLoggerStatic(__NAMESPACE__, __CLASS__, __FILE__); + $waitTimeSeconds = 5; + ($loggerProxy = $logger->ifInfoLevelEnabled(__LINE__, __FUNCTION__)) + && $loggerProxy->log('Sleeping ' . $waitTimeSeconds . ' seconds to give agent enough time to send data (which it should not since it is disabled)...'); + sleep($waitTimeSeconds); + $dataFromAgent = $testCaseHandle->getDataFromAgentWithoutWaiting(); + self::assertCount(0, $dataFromAgent->idToTransaction); + self::assertCount(0, $dataFromAgent->metadatas); + } + self::assertCount(0, $dataFromAgent->idToSpan); + self::assertCount(0, $dataFromAgent->idToError); + } +} diff --git a/tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php b/tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php index c39134f6e..45a5d2399 100644 --- a/tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php +++ b/tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php @@ -567,8 +567,10 @@ protected static function disableTimingDependentFeatures(AppCodeHostParams $appC protected static function setConfigIfNotNull(MixedMap $testArgs, string $optName, AppCodeHostParams $appCodeParams): void { - $optVal = $testArgs->getNullableString($optName); + $optVal = $testArgs->get($optName); if ($optVal !== null) { + self::assertTrue(is_string($optVal) || is_int($optVal) || is_float($optVal) || is_bool($optVal)); + /** @var string|int|float|bool $optVal */ $appCodeParams->setAgentOption($optName, $optVal); } } diff --git a/tests/ElasticApmTests/ComponentTests/Util/ConfigSnapshotForTests.php b/tests/ElasticApmTests/ComponentTests/Util/ConfigSnapshotForTests.php index fd36bc698..b662cc229 100644 --- a/tests/ElasticApmTests/ComponentTests/Util/ConfigSnapshotForTests.php +++ b/tests/ElasticApmTests/ComponentTests/Util/ConfigSnapshotForTests.php @@ -60,6 +60,9 @@ final class ConfigSnapshotForTests implements LoggableInterface /** @var bool */ public $deleteTempPhpIni; + /** @var ?bool */ + public $agentEnabledConfigDefault = null; + /** @var ?WildcardListMatcher */ public $envVarsToPassThrough; diff --git a/tests/ElasticApmTests/ComponentTests/Util/ExpectedEventCounts.php b/tests/ElasticApmTests/ComponentTests/Util/ExpectedEventCounts.php index 6f4b1443d..bff87a168 100644 --- a/tests/ElasticApmTests/ComponentTests/Util/ExpectedEventCounts.php +++ b/tests/ElasticApmTests/ComponentTests/Util/ExpectedEventCounts.php @@ -43,7 +43,7 @@ public function __construct() { $this->perDataKind = []; $this->errors(0); - $this->setValueForKind(ApmDataKind::metadata(), 1); + $this->metadatas(1); $this->metricSets(0); $this->spans(0); $this->transactions(1); @@ -65,6 +65,11 @@ private function setValueForKind(ApmDataKind $apmDataKind, ?int $count): self return $this; } + public function metadatas(int $count): self + { + return $this->setValueForKind(ApmDataKind::metadata(), $count); + } + public function errors(int $count): self { return $this->setValueForKind(ApmDataKind::error(), $count); diff --git a/tests/ElasticApmTests/ComponentTests/Util/TestCaseHandle.php b/tests/ElasticApmTests/ComponentTests/Util/TestCaseHandle.php index 5f6ea5c75..f46391bac 100644 --- a/tests/ElasticApmTests/ComponentTests/Util/TestCaseHandle.php +++ b/tests/ElasticApmTests/ComponentTests/Util/TestCaseHandle.php @@ -156,6 +156,13 @@ function (HttpAppCodeHostParams $appCodeHostParams) use ($setParamsFunc): void { return $this->additionalHttpAppCodeHost; } + public function getDataFromAgentWithoutWaiting(): DataFromAgentPlusRaw + { + $dataFromAgentAccumulator = new DataFromAgentPlusRawAccumulator(); + $dataFromAgentAccumulator->addReceiverEvents($this->mockApmServer->fetchNewData(/* shouldWait */ false)); + return $dataFromAgentAccumulator->getAccumulatedData(); + } + public function waitForDataFromAgent(ExpectedEventCounts $expectedEventCounts, bool $shouldValidate = true): DataFromAgentPlusRaw { TestCaseBase::assertNotEmpty($this->appCodeInvocations); diff --git a/tests/ElasticApmTests/UnitTests/AgentEnabledConfigUnitTest.php b/tests/ElasticApmTests/UnitTests/AgentEnabledConfigUnitTest.php new file mode 100644 index 000000000..6ecef8e6f --- /dev/null +++ b/tests/ElasticApmTests/UnitTests/AgentEnabledConfigUnitTest.php @@ -0,0 +1,45 @@ +agentEnabledConfigDefault === null) { + self::dummyAssert(); + return; + } + self::assertSame($configForTests->agentEnabledConfigDefault, AllOptionsMetadata::get()[OptionNames::ENABLED]->defaultValue()); + } +} diff --git a/tests/ElasticApmTests/Util/MixedMap.php b/tests/ElasticApmTests/Util/MixedMap.php index 6815f8bff..75c9a0b3f 100644 --- a/tests/ElasticApmTests/Util/MixedMap.php +++ b/tests/ElasticApmTests/Util/MixedMap.php @@ -101,13 +101,33 @@ public function getIfKeyExistsElse(string $key, $fallbackValue) * @param string $key * @param array $from * - * @return bool + * @return ?bool */ - public static function getBoolFrom(string $key, array $from): bool + public static function getNullableBoolFrom(string $key, array $from): ?bool { AssertMessageStack::newScope(/* out */ $dbgCtx, AssertMessageStack::funcArgs()); $value = self::getFrom($key, $from); - TestCaseBase::assertIsBool($value); + if ($value !== null) { + TestCaseBase::assertIsBool($value); + } + return $value; + } + + public function getNullableBool(string $key): ?bool + { + return self::getNullableBoolFrom($key, $this->map); + } + + /** + * @param string $key + * @param array $from + * + * @return bool + */ + public static function getBoolFrom(string $key, array $from): bool + { + $value = self::getNullableBoolFrom($key, $from); + TestCaseBase::assertNotNull($value); return $value; }