@@ -1731,6 +1731,67 @@ func ValidateNodeProblemDetector(ctx context.Context, s *Scenario) {
17311731 execScriptOnVMForScenarioValidateExitCode (ctx , s , strings .Join (command , "\n " ), 0 , "Node Problem Detector (NPD) service validation failed" )
17321732}
17331733
1734+ func ValidateNodeExporter (ctx context.Context , s * Scenario ) {
1735+ s .T .Helper ()
1736+
1737+ skipFile := "/etc/node-exporter.d/skip_vhd_node_exporter"
1738+ serviceName := "node-exporter.service"
1739+
1740+ // Check if node-exporter is installed on this VHD by looking for the skip sentinel file.
1741+ // The skip file is only present on VHDs that have node-exporter installed (Ubuntu, Mariner, Azure Linux).
1742+ // Flatcar, OSGuard, and older VHDs do not have node-exporter installed and will not have the skip file.
1743+ if ! fileExist (ctx , s , skipFile ) {
1744+ s .T .Logf ("Skipping node-exporter validation: sentinel file %s not found (VHD does not have node-exporter installed)" , skipFile )
1745+ return
1746+ }
1747+
1748+ s .T .Logf ("skip_vhd_node_exporter sentinel file found, validating node-exporter installation" )
1749+
1750+ // Validate service is running
1751+ ValidateSystemdUnitIsRunning (ctx , s , serviceName )
1752+ ValidateSystemdUnitIsNotFailed (ctx , s , serviceName )
1753+
1754+ // Validate service is enabled
1755+ execScriptOnVMForScenarioValidateExitCode (ctx , s , fmt .Sprintf ("systemctl is-enabled %s" , serviceName ), 0 , fmt .Sprintf ("%s should be enabled" , serviceName ))
1756+
1757+ // Validate binary exists and is executable
1758+ // The binary is installed at /usr/bin and symlinked to /opt/bin for consistency with other binaries (kubelet, etc.)
1759+ ValidateFileExists (ctx , s , "/usr/bin/node-exporter" )
1760+ ValidateFileExists (ctx , s , "/opt/bin/node-exporter" )
1761+ ValidateFileExists (ctx , s , "/opt/bin/node-exporter-startup.sh" )
1762+
1763+ // Validate configuration files exist
1764+ ValidateFileExists (ctx , s , skipFile )
1765+ ValidateFileExists (ctx , s , "/etc/node-exporter.d/web-config.yml" )
1766+
1767+ // Validate that node-exporter is listening on port 19100
1768+ // We verify the port is open using ss/netstat rather than making a full mTLS request,
1769+ // since the e2e test environment may not have the correct client certs set up.
1770+ // The mTLS configuration is validated by checking that the web-config.yml exists
1771+ // and contains the expected TLS settings.
1772+ s .T .Logf ("Validating node-exporter is listening on port 19100" )
1773+ command := []string {
1774+ "set -ex" ,
1775+ "NODE_IP=$(hostname -I | awk '{print $1}')" ,
1776+ // Verify node-exporter is listening on port 19100
1777+ "ss -tlnp | grep -q ':19100' || netstat -tlnp | grep -q ':19100'" ,
1778+ }
1779+ execScriptOnVMForScenarioValidateExitCode (ctx , s , strings .Join (command , "\n " ), 0 , "node-exporter should be listening on port 19100" )
1780+
1781+ // Verify the web-config.yml has proper TLS configuration
1782+ s .T .Logf ("Validating node-exporter TLS configuration" )
1783+ tlsCommand := []string {
1784+ "set -ex" ,
1785+ // Verify web-config.yml contains TLS settings
1786+ "grep -q 'tls_server_config' /etc/node-exporter.d/web-config.yml" ,
1787+ "grep -q 'client_auth_type' /etc/node-exporter.d/web-config.yml" ,
1788+ "grep -q 'client_ca_file' /etc/node-exporter.d/web-config.yml" ,
1789+ }
1790+ execScriptOnVMForScenarioValidateExitCode (ctx , s , strings .Join (tlsCommand , "\n " ), 0 , "node-exporter TLS config should be properly configured" )
1791+
1792+ s .T .Logf ("node-exporter validation passed" )
1793+ }
1794+
17341795func ValidateNPDFilesystemCorruption (ctx context.Context , s * Scenario ) {
17351796 command := []string {
17361797 "set -ex" ,
0 commit comments