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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions AzureMonitorAgent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2206,7 +2206,13 @@ def generate_localsyslog_configs(uses_gcs = False, uses_mcs = False):
useSyslogTcp = False

if syslog_port == MDSDSyslogPort:
return
# If none of the syslog config files exist, we should not return early
# as we need to regenerate them
if (os.path.exists('/etc/rsyslog.d/10-azuremonitoragent-omfwd.conf') or
os.path.exists('/etc/rsyslog.d/10-azuremonitoragent.conf') or
os.path.exists('/etc/syslog-ng/conf.d/azuremonitoragent-tcp.conf') or
os.path.exists('/etc/syslog-ng/conf.d/azuremonitoragent.conf')):
return

# always use syslog tcp port, unless
# - the distro is Red Hat based and doesn't have semanage
Expand Down Expand Up @@ -2323,7 +2329,10 @@ def generate_localsyslog_configs(uses_gcs = False, uses_mcs = False):
def remove_localsyslog_configs():
"""
Remove local syslog configuration files if present and restart syslog
"""
"""
global MDSDSyslogPort
MDSDSyslogPort = 0

if os.path.exists('/etc/rsyslog.d/10-azuremonitoragent.conf') or os.path.exists('/etc/rsyslog.d/10-azuremonitoragent-omfwd.conf'):
if os.path.exists('/etc/rsyslog.d/10-azuremonitoragent-omfwd.conf'):
os.remove("/etc/rsyslog.d/10-azuremonitoragent-omfwd.conf")
Expand Down
105 changes: 105 additions & 0 deletions AzureMonitorAgent/tests/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,5 +214,110 @@ def test_config_keys(self):
self.assertEqual(agent.AzureMonitorConfigKey, "azureMonitorConfiguration")


class TestGenerateLocalsyslogConfigsEarlyReturn(unittest.TestCase):
"""Tests for generate_localsyslog_configs early-return logic when syslog_port == MDSDSyslogPort."""

def setUp(self):
self._orig_port = agent.MDSDSyslogPort
# Patch dependencies used by generate_localsyslog_configs
self._patches = [
patch('agent.get_settings', return_value=({}, {})),
patch('agent.hutil_log_info'),
patch('agent.hutil_log_error'),
patch('agent.run_command_and_log', return_value=(0, '')),
]
for p in self._patches:
p.start()

def tearDown(self):
agent.MDSDSyslogPort = self._orig_port
for p in self._patches:
p.stop()

@patch('agent.validate_port_number', return_value='28330')
@patch('os.path.isfile', return_value=True)
@patch('builtins.open', MagicMock())
def test_returns_early_when_port_matches_and_configs_exist(self, mock_isfile, mock_validate):
"""When port matches AND at least one syslog config file exists, should return early."""
agent.MDSDSyslogPort = '28330'
config_files = {
'/etc/rsyslog.d/10-azuremonitoragent-omfwd.conf',
'/etc/rsyslog.d/10-azuremonitoragent.conf',
'/etc/syslog-ng/conf.d/azuremonitoragent-tcp.conf',
'/etc/syslog-ng/conf.d/azuremonitoragent.conf',
}

def exists_side_effect(path):
return path in config_files

with patch('os.path.exists', side_effect=exists_side_effect):
result = agent.generate_localsyslog_configs(uses_gcs=True)
# Function returns early (None), meaning it did not proceed to syslog setup
self.assertIsNone(result)

@patch('agent.validate_port_number', return_value='28330')
@patch('os.path.isfile', return_value=True)
@patch('builtins.open', MagicMock())
def test_does_not_return_early_when_port_matches_but_no_configs_exist(self, mock_isfile, mock_validate):
"""When port matches but NO syslog config files exist, should NOT return early (regenerate configs)."""
agent.MDSDSyslogPort = '28330'

with patch('os.path.exists', return_value=False):
# The function should proceed past the early-return check.
# It will eventually try file operations that we haven't fully mocked,
# so we just verify it doesn't return immediately like the early-return case.
# We patch copyfile to prevent actual file ops.
with patch('agent.copyfile'):
try:
agent.generate_localsyslog_configs(uses_gcs=True)
except Exception:
pass
# Verify MDSDSyslogPort was updated (happens past the early-return)
self.assertEqual(agent.MDSDSyslogPort, '28330')

def test_returns_early_when_no_control_plane(self):
"""When neither uses_gcs nor uses_mcs, should return early regardless."""
result = agent.generate_localsyslog_configs(uses_gcs=False, uses_mcs=False)
self.assertIsNone(result)


class TestRemoveLocalsyslogConfigsResetsPort(unittest.TestCase):
"""Tests for remove_localsyslog_configs resetting MDSDSyslogPort to 0."""

def setUp(self):
self._orig_port = agent.MDSDSyslogPort

def tearDown(self):
agent.MDSDSyslogPort = self._orig_port

@patch('os.path.exists', return_value=False)
def test_resets_port_to_zero(self, mock_exists):
"""remove_localsyslog_configs should reset MDSDSyslogPort to 0."""
agent.MDSDSyslogPort = '28330'
agent.remove_localsyslog_configs()
self.assertEqual(agent.MDSDSyslogPort, 0)

@patch('agent.run_command_and_log', return_value=(0, ''))
@patch('agent.hutil_log_info')
@patch('agent.get_service_command', return_value='systemctl restart rsyslog')
@patch('os.remove')
@patch('os.path.exists', return_value=True)
def test_resets_port_to_zero_with_existing_configs(self, mock_exists, mock_remove,
mock_svc_cmd, mock_log, mock_run):
"""Port should be reset even when config files exist and are removed."""
agent.MDSDSyslogPort = '28330'
agent.remove_localsyslog_configs()
self.assertEqual(agent.MDSDSyslogPort, 0)

@patch('os.path.exists', return_value=False)
def test_port_reset_allows_regeneration(self, mock_exists):
"""After remove resets port to 0, a subsequent generate should not short-circuit on port match."""
agent.MDSDSyslogPort = '28330'
agent.remove_localsyslog_configs()
# Port is now 0; a new syslog_port read of '28330' should NOT match MDSDSyslogPort
self.assertNotEqual(agent.MDSDSyslogPort, '28330')
self.assertEqual(agent.MDSDSyslogPort, 0)


if __name__ == '__main__':
unittest.main()
Loading