@sonde/packs 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +6 -0
- package/.turbo/turbo-test.log +814 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/CHANGELOG.md +10 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -2
- package/dist/index.js.map +1 -1
- package/dist/integrations/citrix.d.ts +13 -0
- package/dist/integrations/citrix.d.ts.map +1 -0
- package/dist/integrations/citrix.js +420 -0
- package/dist/integrations/citrix.js.map +1 -0
- package/dist/integrations/citrix.test.d.ts +2 -0
- package/dist/integrations/citrix.test.d.ts.map +1 -0
- package/dist/integrations/citrix.test.js +464 -0
- package/dist/integrations/citrix.test.js.map +1 -0
- package/dist/integrations/graph.d.ts +9 -0
- package/dist/integrations/graph.d.ts.map +1 -0
- package/dist/integrations/graph.js +290 -0
- package/dist/integrations/graph.js.map +1 -0
- package/dist/integrations/graph.test.d.ts +2 -0
- package/dist/integrations/graph.test.d.ts.map +1 -0
- package/dist/integrations/graph.test.js +356 -0
- package/dist/integrations/graph.test.js.map +1 -0
- package/dist/integrations/httpbin.d.ts +3 -0
- package/dist/integrations/httpbin.d.ts.map +1 -0
- package/dist/integrations/httpbin.js +70 -0
- package/dist/integrations/httpbin.js.map +1 -0
- package/dist/integrations/nutanix.d.ts +18 -0
- package/dist/integrations/nutanix.d.ts.map +1 -0
- package/dist/integrations/nutanix.js +1121 -0
- package/dist/integrations/nutanix.js.map +1 -0
- package/dist/integrations/nutanix.test.d.ts +2 -0
- package/dist/integrations/nutanix.test.d.ts.map +1 -0
- package/dist/integrations/nutanix.test.js +978 -0
- package/dist/integrations/nutanix.test.js.map +1 -0
- package/dist/integrations/proxmox.d.ts +12 -0
- package/dist/integrations/proxmox.d.ts.map +1 -0
- package/dist/integrations/proxmox.js +733 -0
- package/dist/integrations/proxmox.js.map +1 -0
- package/dist/integrations/proxmox.test.d.ts +2 -0
- package/dist/integrations/proxmox.test.d.ts.map +1 -0
- package/dist/integrations/proxmox.test.js +697 -0
- package/dist/integrations/proxmox.test.js.map +1 -0
- package/dist/integrations/servicenow.d.ts +3 -0
- package/dist/integrations/servicenow.d.ts.map +1 -0
- package/dist/integrations/servicenow.js +257 -0
- package/dist/integrations/servicenow.js.map +1 -0
- package/dist/integrations/servicenow.test.d.ts +2 -0
- package/dist/integrations/servicenow.test.d.ts.map +1 -0
- package/dist/integrations/servicenow.test.js +217 -0
- package/dist/integrations/servicenow.test.js.map +1 -0
- package/dist/integrations/splunk.d.ts +9 -0
- package/dist/integrations/splunk.d.ts.map +1 -0
- package/dist/integrations/splunk.js +242 -0
- package/dist/integrations/splunk.js.map +1 -0
- package/dist/integrations/splunk.test.d.ts +2 -0
- package/dist/integrations/splunk.test.d.ts.map +1 -0
- package/dist/integrations/splunk.test.js +323 -0
- package/dist/integrations/splunk.test.js.map +1 -0
- package/dist/mysql/index.d.ts +3 -0
- package/dist/mysql/index.d.ts.map +1 -0
- package/dist/mysql/index.js +13 -0
- package/dist/mysql/index.js.map +1 -0
- package/dist/mysql/manifest.d.ts +3 -0
- package/dist/mysql/manifest.d.ts.map +1 -0
- package/dist/mysql/manifest.js +69 -0
- package/dist/mysql/manifest.js.map +1 -0
- package/dist/mysql/probes/databases-list.d.ts +13 -0
- package/dist/mysql/probes/databases-list.d.ts.map +1 -0
- package/dist/mysql/probes/databases-list.js +31 -0
- package/dist/mysql/probes/databases-list.js.map +1 -0
- package/dist/mysql/probes/databases-list.test.d.ts +2 -0
- package/dist/mysql/probes/databases-list.test.d.ts.map +1 -0
- package/dist/mysql/probes/databases-list.test.js +54 -0
- package/dist/mysql/probes/databases-list.test.js.map +1 -0
- package/dist/mysql/probes/processlist.d.ts +18 -0
- package/dist/mysql/probes/processlist.d.ts.map +1 -0
- package/dist/mysql/probes/processlist.js +36 -0
- package/dist/mysql/probes/processlist.js.map +1 -0
- package/dist/mysql/probes/processlist.test.d.ts +2 -0
- package/dist/mysql/probes/processlist.test.d.ts.map +1 -0
- package/dist/mysql/probes/processlist.test.js +41 -0
- package/dist/mysql/probes/processlist.test.js.map +1 -0
- package/dist/mysql/probes/status.d.ts +14 -0
- package/dist/mysql/probes/status.d.ts.map +1 -0
- package/dist/mysql/probes/status.js +40 -0
- package/dist/mysql/probes/status.js.map +1 -0
- package/dist/mysql/probes/status.test.d.ts +2 -0
- package/dist/mysql/probes/status.test.d.ts.map +1 -0
- package/dist/mysql/probes/status.test.js +43 -0
- package/dist/mysql/probes/status.test.js.map +1 -0
- package/dist/nginx/index.d.ts +3 -0
- package/dist/nginx/index.d.ts.map +1 -0
- package/dist/nginx/index.js +13 -0
- package/dist/nginx/index.js.map +1 -0
- package/dist/nginx/manifest.d.ts +3 -0
- package/dist/nginx/manifest.d.ts.map +1 -0
- package/dist/nginx/manifest.js +68 -0
- package/dist/nginx/manifest.js.map +1 -0
- package/dist/nginx/probes/access-log-tail.d.ts +9 -0
- package/dist/nginx/probes/access-log-tail.d.ts.map +1 -0
- package/dist/nginx/probes/access-log-tail.js +14 -0
- package/dist/nginx/probes/access-log-tail.js.map +1 -0
- package/dist/nginx/probes/access-log-tail.test.d.ts +2 -0
- package/dist/nginx/probes/access-log-tail.test.d.ts.map +1 -0
- package/dist/nginx/probes/access-log-tail.test.js +40 -0
- package/dist/nginx/probes/access-log-tail.test.js.map +1 -0
- package/dist/nginx/probes/config-test.d.ts +8 -0
- package/dist/nginx/probes/config-test.d.ts.map +1 -0
- package/dist/nginx/probes/config-test.js +18 -0
- package/dist/nginx/probes/config-test.js.map +1 -0
- package/dist/nginx/probes/config-test.test.d.ts +2 -0
- package/dist/nginx/probes/config-test.test.d.ts.map +1 -0
- package/dist/nginx/probes/config-test.test.js +35 -0
- package/dist/nginx/probes/config-test.test.js.map +1 -0
- package/dist/nginx/probes/error-log-tail.d.ts +9 -0
- package/dist/nginx/probes/error-log-tail.d.ts.map +1 -0
- package/dist/nginx/probes/error-log-tail.js +14 -0
- package/dist/nginx/probes/error-log-tail.js.map +1 -0
- package/dist/nginx/probes/error-log-tail.test.d.ts +2 -0
- package/dist/nginx/probes/error-log-tail.test.d.ts.map +1 -0
- package/dist/nginx/probes/error-log-tail.test.js +34 -0
- package/dist/nginx/probes/error-log-tail.test.js.map +1 -0
- package/dist/postgres/index.d.ts +3 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +13 -0
- package/dist/postgres/index.js.map +1 -0
- package/dist/postgres/manifest.d.ts +3 -0
- package/dist/postgres/manifest.d.ts.map +1 -0
- package/dist/postgres/manifest.js +90 -0
- package/dist/postgres/manifest.js.map +1 -0
- package/dist/postgres/probes/connections-active.d.ts +17 -0
- package/dist/postgres/probes/connections-active.d.ts.map +1 -0
- package/dist/postgres/probes/connections-active.js +37 -0
- package/dist/postgres/probes/connections-active.js.map +1 -0
- package/dist/postgres/probes/connections-active.test.d.ts +2 -0
- package/dist/postgres/probes/connections-active.test.d.ts.map +1 -0
- package/dist/postgres/probes/connections-active.test.js +36 -0
- package/dist/postgres/probes/connections-active.test.js.map +1 -0
- package/dist/postgres/probes/databases-list.d.ts +14 -0
- package/dist/postgres/probes/databases-list.d.ts.map +1 -0
- package/dist/postgres/probes/databases-list.js +34 -0
- package/dist/postgres/probes/databases-list.js.map +1 -0
- package/dist/postgres/probes/databases-list.test.d.ts +2 -0
- package/dist/postgres/probes/databases-list.test.d.ts.map +1 -0
- package/dist/postgres/probes/databases-list.test.js +49 -0
- package/dist/postgres/probes/databases-list.test.js.map +1 -0
- package/dist/postgres/probes/query-slow.d.ts +17 -0
- package/dist/postgres/probes/query-slow.d.ts.map +1 -0
- package/dist/postgres/probes/query-slow.js +37 -0
- package/dist/postgres/probes/query-slow.js.map +1 -0
- package/dist/postgres/probes/query-slow.test.d.ts +2 -0
- package/dist/postgres/probes/query-slow.test.d.ts.map +1 -0
- package/dist/postgres/probes/query-slow.test.js +30 -0
- package/dist/postgres/probes/query-slow.test.js.map +1 -0
- package/dist/proxmox/index.d.ts +3 -0
- package/dist/proxmox/index.d.ts.map +1 -0
- package/dist/proxmox/index.js +23 -0
- package/dist/proxmox/index.js.map +1 -0
- package/dist/proxmox/manifest.d.ts +3 -0
- package/dist/proxmox/manifest.d.ts.map +1 -0
- package/dist/proxmox/manifest.js +75 -0
- package/dist/proxmox/manifest.js.map +1 -0
- package/dist/proxmox/probes/ceph-status.d.ts +36 -0
- package/dist/proxmox/probes/ceph-status.d.ts.map +1 -0
- package/dist/proxmox/probes/ceph-status.js +71 -0
- package/dist/proxmox/probes/ceph-status.js.map +1 -0
- package/dist/proxmox/probes/ceph-status.test.d.ts +2 -0
- package/dist/proxmox/probes/ceph-status.test.d.ts.map +1 -0
- package/dist/proxmox/probes/ceph-status.test.js +115 -0
- package/dist/proxmox/probes/ceph-status.test.js.map +1 -0
- package/dist/proxmox/probes/cluster-config.d.ts +31 -0
- package/dist/proxmox/probes/cluster-config.d.ts.map +1 -0
- package/dist/proxmox/probes/cluster-config.js +72 -0
- package/dist/proxmox/probes/cluster-config.js.map +1 -0
- package/dist/proxmox/probes/cluster-config.test.d.ts +2 -0
- package/dist/proxmox/probes/cluster-config.test.d.ts.map +1 -0
- package/dist/proxmox/probes/cluster-config.test.js +107 -0
- package/dist/proxmox/probes/cluster-config.test.js.map +1 -0
- package/dist/proxmox/probes/ha-status.d.ts +18 -0
- package/dist/proxmox/probes/ha-status.d.ts.map +1 -0
- package/dist/proxmox/probes/ha-status.js +38 -0
- package/dist/proxmox/probes/ha-status.js.map +1 -0
- package/dist/proxmox/probes/ha-status.test.d.ts +2 -0
- package/dist/proxmox/probes/ha-status.test.d.ts.map +1 -0
- package/dist/proxmox/probes/ha-status.test.js +66 -0
- package/dist/proxmox/probes/ha-status.test.js.map +1 -0
- package/dist/proxmox/probes/lvm.d.ts +35 -0
- package/dist/proxmox/probes/lvm.d.ts.map +1 -0
- package/dist/proxmox/probes/lvm.js +75 -0
- package/dist/proxmox/probes/lvm.js.map +1 -0
- package/dist/proxmox/probes/lvm.test.d.ts +2 -0
- package/dist/proxmox/probes/lvm.test.d.ts.map +1 -0
- package/dist/proxmox/probes/lvm.test.js +128 -0
- package/dist/proxmox/probes/lvm.test.js.map +1 -0
- package/dist/proxmox/probes/lxc-config.d.ts +29 -0
- package/dist/proxmox/probes/lxc-config.d.ts.map +1 -0
- package/dist/proxmox/probes/lxc-config.js +67 -0
- package/dist/proxmox/probes/lxc-config.js.map +1 -0
- package/dist/proxmox/probes/lxc-config.test.d.ts +2 -0
- package/dist/proxmox/probes/lxc-config.test.d.ts.map +1 -0
- package/dist/proxmox/probes/lxc-config.test.js +77 -0
- package/dist/proxmox/probes/lxc-config.test.js.map +1 -0
- package/dist/proxmox/probes/lxc-list.d.ts +20 -0
- package/dist/proxmox/probes/lxc-list.d.ts.map +1 -0
- package/dist/proxmox/probes/lxc-list.js +49 -0
- package/dist/proxmox/probes/lxc-list.js.map +1 -0
- package/dist/proxmox/probes/lxc-list.test.d.ts +2 -0
- package/dist/proxmox/probes/lxc-list.test.d.ts.map +1 -0
- package/dist/proxmox/probes/lxc-list.test.js +51 -0
- package/dist/proxmox/probes/lxc-list.test.js.map +1 -0
- package/dist/proxmox/probes/vm-config.d.ts +21 -0
- package/dist/proxmox/probes/vm-config.d.ts.map +1 -0
- package/dist/proxmox/probes/vm-config.js +58 -0
- package/dist/proxmox/probes/vm-config.js.map +1 -0
- package/dist/proxmox/probes/vm-config.test.d.ts +2 -0
- package/dist/proxmox/probes/vm-config.test.d.ts.map +1 -0
- package/dist/proxmox/probes/vm-config.test.js +80 -0
- package/dist/proxmox/probes/vm-config.test.js.map +1 -0
- package/dist/proxmox/probes/vm-locks.d.ts +16 -0
- package/dist/proxmox/probes/vm-locks.d.ts.map +1 -0
- package/dist/proxmox/probes/vm-locks.js +35 -0
- package/dist/proxmox/probes/vm-locks.js.map +1 -0
- package/dist/proxmox/probes/vm-locks.test.d.ts +2 -0
- package/dist/proxmox/probes/vm-locks.test.d.ts.map +1 -0
- package/dist/proxmox/probes/vm-locks.test.js +54 -0
- package/dist/proxmox/probes/vm-locks.test.js.map +1 -0
- package/dist/redis/index.d.ts +3 -0
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/redis/index.js +13 -0
- package/dist/redis/index.js.map +1 -0
- package/dist/redis/manifest.d.ts +3 -0
- package/dist/redis/manifest.d.ts.map +1 -0
- package/dist/redis/manifest.js +51 -0
- package/dist/redis/manifest.js.map +1 -0
- package/dist/redis/probes/info.d.ts +15 -0
- package/dist/redis/probes/info.d.ts.map +1 -0
- package/dist/redis/probes/info.js +32 -0
- package/dist/redis/probes/info.js.map +1 -0
- package/dist/redis/probes/info.test.d.ts +2 -0
- package/dist/redis/probes/info.test.d.ts.map +1 -0
- package/dist/redis/probes/info.test.js +64 -0
- package/dist/redis/probes/info.test.js.map +1 -0
- package/dist/redis/probes/keys-count.d.ts +13 -0
- package/dist/redis/probes/keys-count.d.ts.map +1 -0
- package/dist/redis/probes/keys-count.js +24 -0
- package/dist/redis/probes/keys-count.js.map +1 -0
- package/dist/redis/probes/keys-count.test.d.ts +2 -0
- package/dist/redis/probes/keys-count.test.d.ts.map +1 -0
- package/dist/redis/probes/keys-count.test.js +37 -0
- package/dist/redis/probes/keys-count.test.js.map +1 -0
- package/dist/redis/probes/memory-usage.d.ts +16 -0
- package/dist/redis/probes/memory-usage.d.ts.map +1 -0
- package/dist/redis/probes/memory-usage.js +31 -0
- package/dist/redis/probes/memory-usage.js.map +1 -0
- package/dist/redis/probes/memory-usage.test.d.ts +2 -0
- package/dist/redis/probes/memory-usage.test.d.ts.map +1 -0
- package/dist/redis/probes/memory-usage.test.js +48 -0
- package/dist/redis/probes/memory-usage.test.js.map +1 -0
- package/dist/runbooks/nutanix.d.ts +3 -0
- package/dist/runbooks/nutanix.d.ts.map +1 -0
- package/dist/runbooks/nutanix.js +619 -0
- package/dist/runbooks/nutanix.js.map +1 -0
- package/dist/runbooks/nutanix.test.d.ts +2 -0
- package/dist/runbooks/nutanix.test.d.ts.map +1 -0
- package/dist/runbooks/nutanix.test.js +971 -0
- package/dist/runbooks/nutanix.test.js.map +1 -0
- package/dist/runbooks/proxmox.d.ts +3 -0
- package/dist/runbooks/proxmox.d.ts.map +1 -0
- package/dist/runbooks/proxmox.js +451 -0
- package/dist/runbooks/proxmox.js.map +1 -0
- package/dist/runbooks/proxmox.test.d.ts +2 -0
- package/dist/runbooks/proxmox.test.d.ts.map +1 -0
- package/dist/runbooks/proxmox.test.js +700 -0
- package/dist/runbooks/proxmox.test.js.map +1 -0
- package/dist/signatures.d.ts +2 -0
- package/dist/signatures.d.ts.map +1 -0
- package/dist/signatures.js +2 -0
- package/dist/signatures.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +6 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +10 -1
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +60 -6
- package/src/integrations/citrix.test.ts +592 -0
- package/src/integrations/citrix.ts +557 -0
- package/src/integrations/graph.test.ts +478 -0
- package/src/integrations/graph.ts +413 -0
- package/src/integrations/httpbin.ts +72 -0
- package/src/integrations/nutanix.test.ts +1508 -0
- package/src/integrations/nutanix.ts +1460 -0
- package/src/integrations/proxmox.test.ts +1020 -0
- package/src/integrations/proxmox.ts +989 -0
- package/src/integrations/servicenow.test.ts +314 -0
- package/src/integrations/servicenow.ts +285 -0
- package/src/integrations/splunk.test.ts +440 -0
- package/src/integrations/splunk.ts +356 -0
- package/src/mysql/index.ts +14 -0
- package/src/mysql/manifest.ts +70 -0
- package/src/mysql/probes/databases-list.test.ts +62 -0
- package/src/mysql/probes/databases-list.ts +45 -0
- package/src/mysql/probes/processlist.test.ts +47 -0
- package/src/mysql/probes/processlist.ts +55 -0
- package/src/mysql/probes/status.test.ts +50 -0
- package/src/mysql/probes/status.ts +56 -0
- package/src/nginx/index.ts +14 -0
- package/src/nginx/manifest.ts +69 -0
- package/src/nginx/probes/access-log-tail.test.ts +51 -0
- package/src/nginx/probes/access-log-tail.ts +23 -0
- package/src/nginx/probes/config-test.test.ts +47 -0
- package/src/nginx/probes/config-test.ts +24 -0
- package/src/nginx/probes/error-log-tail.test.ts +44 -0
- package/src/nginx/probes/error-log-tail.ts +23 -0
- package/src/postgres/index.ts +14 -0
- package/src/postgres/manifest.ts +91 -0
- package/src/postgres/probes/connections-active.test.ts +42 -0
- package/src/postgres/probes/connections-active.ts +55 -0
- package/src/postgres/probes/databases-list.test.ts +57 -0
- package/src/postgres/probes/databases-list.ts +49 -0
- package/src/postgres/probes/query-slow.test.ts +37 -0
- package/src/postgres/probes/query-slow.ts +55 -0
- package/src/proxmox/index.ts +24 -0
- package/src/proxmox/manifest.ts +76 -0
- package/src/proxmox/probes/ceph-status.test.ts +126 -0
- package/src/proxmox/probes/ceph-status.ts +116 -0
- package/src/proxmox/probes/cluster-config.test.ts +118 -0
- package/src/proxmox/probes/cluster-config.ts +97 -0
- package/src/proxmox/probes/ha-status.test.ts +76 -0
- package/src/proxmox/probes/ha-status.ts +56 -0
- package/src/proxmox/probes/lvm.test.ts +140 -0
- package/src/proxmox/probes/lvm.ts +121 -0
- package/src/proxmox/probes/lxc-config.test.ts +89 -0
- package/src/proxmox/probes/lxc-config.ts +90 -0
- package/src/proxmox/probes/lxc-list.test.ts +60 -0
- package/src/proxmox/probes/lxc-list.ts +67 -0
- package/src/proxmox/probes/vm-config.test.ts +93 -0
- package/src/proxmox/probes/vm-config.ts +77 -0
- package/src/proxmox/probes/vm-locks.test.ts +63 -0
- package/src/proxmox/probes/vm-locks.ts +49 -0
- package/src/redis/index.ts +14 -0
- package/src/redis/manifest.ts +52 -0
- package/src/redis/probes/info.test.ts +73 -0
- package/src/redis/probes/info.ts +46 -0
- package/src/redis/probes/keys-count.test.ts +44 -0
- package/src/redis/probes/keys-count.ts +38 -0
- package/src/redis/probes/memory-usage.test.ts +54 -0
- package/src/redis/probes/memory-usage.ts +46 -0
- package/src/runbooks/nutanix.test.ts +1138 -0
- package/src/runbooks/nutanix.ts +941 -0
- package/src/runbooks/proxmox.test.ts +838 -0
- package/src/runbooks/proxmox.ts +626 -0
- package/src/signatures.ts +1 -0
- package/src/types.ts +62 -0
- package/src/validation.ts +21 -1
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ProbeHandler } from '../../types.js';
|
|
2
|
+
|
|
3
|
+
export interface MysqlStatusResult {
|
|
4
|
+
uptime: number;
|
|
5
|
+
threads: number;
|
|
6
|
+
questions: number;
|
|
7
|
+
slowQueries: number;
|
|
8
|
+
opens: number;
|
|
9
|
+
openTables: number;
|
|
10
|
+
queriesPerSecondAvg: number;
|
|
11
|
+
variables: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const status: ProbeHandler = async (params, exec) => {
|
|
15
|
+
const host = (params?.host as string) ?? 'localhost';
|
|
16
|
+
const port = String((params?.port as number) ?? 3306);
|
|
17
|
+
const user = (params?.user as string) ?? 'root';
|
|
18
|
+
|
|
19
|
+
const stdout = await exec('mysql', [
|
|
20
|
+
'-h',
|
|
21
|
+
host,
|
|
22
|
+
'-P',
|
|
23
|
+
port,
|
|
24
|
+
'-u',
|
|
25
|
+
user,
|
|
26
|
+
'--batch',
|
|
27
|
+
'--skip-column-names',
|
|
28
|
+
'-e',
|
|
29
|
+
'SHOW GLOBAL STATUS',
|
|
30
|
+
]);
|
|
31
|
+
return parseMysqlStatus(stdout);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function parseMysqlStatus(stdout: string): MysqlStatusResult {
|
|
35
|
+
const variables: Record<string, string> = {};
|
|
36
|
+
for (const line of stdout.split('\n')) {
|
|
37
|
+
const parts = line.split('\t');
|
|
38
|
+
if (parts.length >= 2) {
|
|
39
|
+
variables[parts[0]!] = parts[1]!;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const uptime = Number(variables['Uptime']) || 0;
|
|
44
|
+
const questions = Number(variables['Questions']) || 0;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
uptime,
|
|
48
|
+
threads: Number(variables['Threads_connected']) || 0,
|
|
49
|
+
questions,
|
|
50
|
+
slowQueries: Number(variables['Slow_queries']) || 0,
|
|
51
|
+
opens: Number(variables['Opened_tables']) || 0,
|
|
52
|
+
openTables: Number(variables['Open_tables']) || 0,
|
|
53
|
+
queriesPerSecondAvg: uptime > 0 ? Math.round((questions / uptime) * 100) / 100 : 0,
|
|
54
|
+
variables,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Pack } from '../types.js';
|
|
2
|
+
import { nginxManifest } from './manifest.js';
|
|
3
|
+
import { accessLogTail } from './probes/access-log-tail.js';
|
|
4
|
+
import { configTest } from './probes/config-test.js';
|
|
5
|
+
import { errorLogTail } from './probes/error-log-tail.js';
|
|
6
|
+
|
|
7
|
+
export const nginxPack: Pack = {
|
|
8
|
+
manifest: nginxManifest,
|
|
9
|
+
handlers: {
|
|
10
|
+
'nginx.config.test': configTest,
|
|
11
|
+
'nginx.access.log.tail': accessLogTail,
|
|
12
|
+
'nginx.error.log.tail': errorLogTail,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { PackManifest } from '@sonde/shared';
|
|
2
|
+
|
|
3
|
+
export const nginxManifest: PackManifest = {
|
|
4
|
+
name: 'nginx',
|
|
5
|
+
version: '0.1.0',
|
|
6
|
+
description: 'Nginx web server probes: config validation, access/error log tailing',
|
|
7
|
+
requires: {
|
|
8
|
+
groups: [],
|
|
9
|
+
files: ['/etc/nginx/nginx.conf'],
|
|
10
|
+
commands: ['nginx'],
|
|
11
|
+
},
|
|
12
|
+
probes: [
|
|
13
|
+
{
|
|
14
|
+
name: 'config.test',
|
|
15
|
+
description: 'Test nginx configuration for syntax errors',
|
|
16
|
+
capability: 'observe',
|
|
17
|
+
timeout: 10_000,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'access.log.tail',
|
|
21
|
+
description: 'Tail recent lines from the nginx access log',
|
|
22
|
+
capability: 'observe',
|
|
23
|
+
params: {
|
|
24
|
+
logPath: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Path to access log file',
|
|
27
|
+
required: false,
|
|
28
|
+
default: '/var/log/nginx/access.log',
|
|
29
|
+
},
|
|
30
|
+
lines: {
|
|
31
|
+
type: 'number',
|
|
32
|
+
description: 'Number of lines to tail',
|
|
33
|
+
required: false,
|
|
34
|
+
default: 100,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
timeout: 10_000,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'error.log.tail',
|
|
41
|
+
description: 'Tail recent lines from the nginx error log',
|
|
42
|
+
capability: 'observe',
|
|
43
|
+
params: {
|
|
44
|
+
logPath: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Path to error log file',
|
|
47
|
+
required: false,
|
|
48
|
+
default: '/var/log/nginx/error.log',
|
|
49
|
+
},
|
|
50
|
+
lines: {
|
|
51
|
+
type: 'number',
|
|
52
|
+
description: 'Number of lines to tail',
|
|
53
|
+
required: false,
|
|
54
|
+
default: 100,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
timeout: 10_000,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
runbook: {
|
|
61
|
+
category: 'nginx',
|
|
62
|
+
probes: ['config.test', 'error.log.tail'],
|
|
63
|
+
parallel: true,
|
|
64
|
+
},
|
|
65
|
+
detect: {
|
|
66
|
+
commands: ['nginx'],
|
|
67
|
+
files: ['/etc/nginx/nginx.conf'],
|
|
68
|
+
},
|
|
69
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { ExecFn } from '../../types.js';
|
|
3
|
+
import type { AccessLogTailResult } from './access-log-tail.js';
|
|
4
|
+
import { accessLogTail, parseLogTail } from './access-log-tail.js';
|
|
5
|
+
|
|
6
|
+
const SAMPLE_LOG = `192.168.1.1 - - [15/Jan/2024:10:30:00 +0000] "GET / HTTP/1.1" 200 612
|
|
7
|
+
192.168.1.2 - - [15/Jan/2024:10:30:01 +0000] "GET /api HTTP/1.1" 200 1234
|
|
8
|
+
192.168.1.1 - - [15/Jan/2024:10:30:02 +0000] "POST /login HTTP/1.1" 302 0
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
describe('parseLogTail', () => {
|
|
12
|
+
it('parses log lines correctly', () => {
|
|
13
|
+
const result = parseLogTail('/var/log/nginx/access.log', SAMPLE_LOG);
|
|
14
|
+
expect(result.logPath).toBe('/var/log/nginx/access.log');
|
|
15
|
+
expect(result.lineCount).toBe(3);
|
|
16
|
+
expect(result.lines[0]).toContain('GET / HTTP/1.1');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('handles empty log', () => {
|
|
20
|
+
const result = parseLogTail('/var/log/nginx/access.log', '');
|
|
21
|
+
expect(result.lineCount).toBe(0);
|
|
22
|
+
expect(result.lines).toEqual([]);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('accessLogTail handler', () => {
|
|
27
|
+
it('uses default path and line count', async () => {
|
|
28
|
+
const mockExec: ExecFn = async (cmd, args) => {
|
|
29
|
+
expect(cmd).toBe('tail');
|
|
30
|
+
expect(args).toEqual(['-n', '100', '/var/log/nginx/access.log']);
|
|
31
|
+
return SAMPLE_LOG;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const result = (await accessLogTail(undefined, mockExec)) as AccessLogTailResult;
|
|
35
|
+
expect(result.lineCount).toBe(3);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('uses custom path and lines', async () => {
|
|
39
|
+
const mockExec: ExecFn = async (cmd, args) => {
|
|
40
|
+
expect(cmd).toBe('tail');
|
|
41
|
+
expect(args).toEqual(['-n', '50', '/custom/access.log']);
|
|
42
|
+
return SAMPLE_LOG;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const result = (await accessLogTail(
|
|
46
|
+
{ logPath: '/custom/access.log', lines: 50 },
|
|
47
|
+
mockExec,
|
|
48
|
+
)) as AccessLogTailResult;
|
|
49
|
+
expect(result.logPath).toBe('/custom/access.log');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ProbeHandler } from '../../types.js';
|
|
2
|
+
|
|
3
|
+
export interface AccessLogTailResult {
|
|
4
|
+
logPath: string;
|
|
5
|
+
lines: string[];
|
|
6
|
+
lineCount: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const accessLogTail: ProbeHandler = async (params, exec) => {
|
|
10
|
+
const logPath = (params?.logPath as string) ?? '/var/log/nginx/access.log';
|
|
11
|
+
const lines = (params?.lines as number) ?? 100;
|
|
12
|
+
|
|
13
|
+
const stdout = await exec('tail', ['-n', String(lines), logPath]);
|
|
14
|
+
return parseLogTail(logPath, stdout);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function parseLogTail(logPath: string, stdout: string): AccessLogTailResult {
|
|
18
|
+
const lines = stdout.split('\n');
|
|
19
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
20
|
+
lines.pop();
|
|
21
|
+
}
|
|
22
|
+
return { logPath, lines, lineCount: lines.length };
|
|
23
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { ExecFn } from '../../types.js';
|
|
3
|
+
import type { ConfigTestResult } from './config-test.js';
|
|
4
|
+
import { configTest, parseConfigTest } from './config-test.js';
|
|
5
|
+
|
|
6
|
+
const SUCCESS_OUTPUT =
|
|
7
|
+
'nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful';
|
|
8
|
+
|
|
9
|
+
describe('parseConfigTest', () => {
|
|
10
|
+
it('parses successful config test', () => {
|
|
11
|
+
const result = parseConfigTest(SUCCESS_OUTPUT, true);
|
|
12
|
+
expect(result.valid).toBe(true);
|
|
13
|
+
expect(result.output).toContain('syntax is ok');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('parses failed config test', () => {
|
|
17
|
+
const result = parseConfigTest(
|
|
18
|
+
'nginx: [emerg] unexpected "}" in /etc/nginx/nginx.conf:42',
|
|
19
|
+
false,
|
|
20
|
+
);
|
|
21
|
+
expect(result.valid).toBe(false);
|
|
22
|
+
expect(result.output).toContain('unexpected');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('configTest handler', () => {
|
|
27
|
+
it('calls nginx -t and returns success', async () => {
|
|
28
|
+
const mockExec: ExecFn = async (cmd, args) => {
|
|
29
|
+
expect(cmd).toBe('nginx');
|
|
30
|
+
expect(args).toEqual(['-t']);
|
|
31
|
+
return SUCCESS_OUTPUT;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const result = (await configTest(undefined, mockExec)) as ConfigTestResult;
|
|
35
|
+
expect(result.valid).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('handles config error gracefully', async () => {
|
|
39
|
+
const mockExec: ExecFn = async () => {
|
|
40
|
+
throw new Error('nginx: [emerg] unknown directive');
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const result = (await configTest(undefined, mockExec)) as ConfigTestResult;
|
|
44
|
+
expect(result.valid).toBe(false);
|
|
45
|
+
expect(result.output).toContain('unknown directive');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ProbeHandler } from '../../types.js';
|
|
2
|
+
|
|
3
|
+
export interface ConfigTestResult {
|
|
4
|
+
valid: boolean;
|
|
5
|
+
output: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const configTest: ProbeHandler = async (_params, exec) => {
|
|
9
|
+
try {
|
|
10
|
+
const stdout = await exec('nginx', ['-t']);
|
|
11
|
+
return parseConfigTest(stdout, true);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
// nginx -t writes to stderr and exits non-zero on failure
|
|
14
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15
|
+
return parseConfigTest(message, false);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function parseConfigTest(output: string, valid: boolean): ConfigTestResult {
|
|
20
|
+
return {
|
|
21
|
+
valid,
|
|
22
|
+
output: output.trim(),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { ExecFn } from '../../types.js';
|
|
3
|
+
import type { ErrorLogTailResult } from './error-log-tail.js';
|
|
4
|
+
import { errorLogTail, parseErrorLogTail } from './error-log-tail.js';
|
|
5
|
+
|
|
6
|
+
const SAMPLE_ERROR_LOG = `2024/01/15 10:30:00 [error] 1234#1234: *5 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory)
|
|
7
|
+
2024/01/15 10:30:01 [warn] 1234#1234: conflicting server name "example.com"
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
describe('parseErrorLogTail', () => {
|
|
11
|
+
it('parses error log lines', () => {
|
|
12
|
+
const result = parseErrorLogTail('/var/log/nginx/error.log', SAMPLE_ERROR_LOG);
|
|
13
|
+
expect(result.logPath).toBe('/var/log/nginx/error.log');
|
|
14
|
+
expect(result.lineCount).toBe(2);
|
|
15
|
+
expect(result.lines[0]).toContain('[error]');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('errorLogTail handler', () => {
|
|
20
|
+
it('uses default path and line count', async () => {
|
|
21
|
+
const mockExec: ExecFn = async (cmd, args) => {
|
|
22
|
+
expect(cmd).toBe('tail');
|
|
23
|
+
expect(args).toEqual(['-n', '100', '/var/log/nginx/error.log']);
|
|
24
|
+
return SAMPLE_ERROR_LOG;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const result = (await errorLogTail(undefined, mockExec)) as ErrorLogTailResult;
|
|
28
|
+
expect(result.lineCount).toBe(2);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('uses custom parameters', async () => {
|
|
32
|
+
const mockExec: ExecFn = async (cmd, args) => {
|
|
33
|
+
expect(cmd).toBe('tail');
|
|
34
|
+
expect(args).toEqual(['-n', '25', '/custom/error.log']);
|
|
35
|
+
return SAMPLE_ERROR_LOG;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const result = (await errorLogTail(
|
|
39
|
+
{ logPath: '/custom/error.log', lines: 25 },
|
|
40
|
+
mockExec,
|
|
41
|
+
)) as ErrorLogTailResult;
|
|
42
|
+
expect(result.logPath).toBe('/custom/error.log');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ProbeHandler } from '../../types.js';
|
|
2
|
+
|
|
3
|
+
export interface ErrorLogTailResult {
|
|
4
|
+
logPath: string;
|
|
5
|
+
lines: string[];
|
|
6
|
+
lineCount: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const errorLogTail: ProbeHandler = async (params, exec) => {
|
|
10
|
+
const logPath = (params?.logPath as string) ?? '/var/log/nginx/error.log';
|
|
11
|
+
const lines = (params?.lines as number) ?? 100;
|
|
12
|
+
|
|
13
|
+
const stdout = await exec('tail', ['-n', String(lines), logPath]);
|
|
14
|
+
return parseErrorLogTail(logPath, stdout);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function parseErrorLogTail(logPath: string, stdout: string): ErrorLogTailResult {
|
|
18
|
+
const lines = stdout.split('\n');
|
|
19
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
20
|
+
lines.pop();
|
|
21
|
+
}
|
|
22
|
+
return { logPath, lines, lineCount: lines.length };
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Pack } from '../types.js';
|
|
2
|
+
import { postgresManifest } from './manifest.js';
|
|
3
|
+
import { connectionsActive } from './probes/connections-active.js';
|
|
4
|
+
import { databasesList } from './probes/databases-list.js';
|
|
5
|
+
import { querySlow } from './probes/query-slow.js';
|
|
6
|
+
|
|
7
|
+
export const postgresPack: Pack = {
|
|
8
|
+
manifest: postgresManifest,
|
|
9
|
+
handlers: {
|
|
10
|
+
'postgres.databases.list': databasesList,
|
|
11
|
+
'postgres.connections.active': connectionsActive,
|
|
12
|
+
'postgres.query.slow': querySlow,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { PackManifest } from '@sonde/shared';
|
|
2
|
+
|
|
3
|
+
export const postgresManifest: PackManifest = {
|
|
4
|
+
name: 'postgres',
|
|
5
|
+
version: '0.1.0',
|
|
6
|
+
description: 'PostgreSQL probes: database listing, active connections, slow queries',
|
|
7
|
+
requires: {
|
|
8
|
+
groups: [],
|
|
9
|
+
files: [],
|
|
10
|
+
commands: ['psql'],
|
|
11
|
+
},
|
|
12
|
+
probes: [
|
|
13
|
+
{
|
|
14
|
+
name: 'databases.list',
|
|
15
|
+
description: 'List all PostgreSQL databases with sizes',
|
|
16
|
+
capability: 'observe',
|
|
17
|
+
params: {
|
|
18
|
+
host: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Database host',
|
|
21
|
+
required: false,
|
|
22
|
+
default: 'localhost',
|
|
23
|
+
},
|
|
24
|
+
port: { type: 'number', description: 'Database port', required: false, default: 5432 },
|
|
25
|
+
user: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Database user',
|
|
28
|
+
required: false,
|
|
29
|
+
default: 'postgres',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
timeout: 15_000,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'connections.active',
|
|
36
|
+
description: 'List active PostgreSQL connections',
|
|
37
|
+
capability: 'observe',
|
|
38
|
+
params: {
|
|
39
|
+
host: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'Database host',
|
|
42
|
+
required: false,
|
|
43
|
+
default: 'localhost',
|
|
44
|
+
},
|
|
45
|
+
port: { type: 'number', description: 'Database port', required: false, default: 5432 },
|
|
46
|
+
user: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'Database user',
|
|
49
|
+
required: false,
|
|
50
|
+
default: 'postgres',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
timeout: 15_000,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'query.slow',
|
|
57
|
+
description: 'List currently running slow queries (> threshold)',
|
|
58
|
+
capability: 'observe',
|
|
59
|
+
params: {
|
|
60
|
+
host: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
description: 'Database host',
|
|
63
|
+
required: false,
|
|
64
|
+
default: 'localhost',
|
|
65
|
+
},
|
|
66
|
+
port: { type: 'number', description: 'Database port', required: false, default: 5432 },
|
|
67
|
+
user: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
description: 'Database user',
|
|
70
|
+
required: false,
|
|
71
|
+
default: 'postgres',
|
|
72
|
+
},
|
|
73
|
+
thresholdMs: {
|
|
74
|
+
type: 'number',
|
|
75
|
+
description: 'Slow query threshold in ms',
|
|
76
|
+
required: false,
|
|
77
|
+
default: 1000,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
timeout: 15_000,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
runbook: {
|
|
84
|
+
category: 'postgres',
|
|
85
|
+
probes: ['databases.list', 'connections.active', 'query.slow'],
|
|
86
|
+
parallel: true,
|
|
87
|
+
},
|
|
88
|
+
detect: {
|
|
89
|
+
commands: ['psql'],
|
|
90
|
+
},
|
|
91
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { ExecFn } from '../../types.js';
|
|
3
|
+
import type { ConnectionsActiveResult } from './connections-active.js';
|
|
4
|
+
import { connectionsActive, parseConnectionsActive } from './connections-active.js';
|
|
5
|
+
|
|
6
|
+
const SAMPLE_OUTPUT = `1234\tmyapp\tappuser\t192.168.1.10\tactive\tSELECT * FROM users\t2024-01-15 10:30:00
|
|
7
|
+
5678\tpostgres\tpostgres\t\tidle\t\t2024-01-15 09:00:00`;
|
|
8
|
+
|
|
9
|
+
describe('parseConnectionsActive', () => {
|
|
10
|
+
it('parses connections output', () => {
|
|
11
|
+
const result = parseConnectionsActive(SAMPLE_OUTPUT);
|
|
12
|
+
expect(result.total).toBe(2);
|
|
13
|
+
expect(result.connections[0]).toEqual({
|
|
14
|
+
pid: 1234,
|
|
15
|
+
database: 'myapp',
|
|
16
|
+
user: 'appuser',
|
|
17
|
+
clientAddr: '192.168.1.10',
|
|
18
|
+
state: 'active',
|
|
19
|
+
query: 'SELECT * FROM users',
|
|
20
|
+
backendStart: '2024-01-15 10:30:00',
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('handles empty output', () => {
|
|
25
|
+
const result = parseConnectionsActive('');
|
|
26
|
+
expect(result.total).toBe(0);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('connectionsActive handler', () => {
|
|
31
|
+
it('calls psql with correct args', async () => {
|
|
32
|
+
const mockExec: ExecFn = async (cmd, args) => {
|
|
33
|
+
expect(cmd).toBe('psql');
|
|
34
|
+
expect(args).toContain('-t');
|
|
35
|
+
expect(args).toContain('-A');
|
|
36
|
+
return SAMPLE_OUTPUT;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const result = (await connectionsActive(undefined, mockExec)) as ConnectionsActiveResult;
|
|
40
|
+
expect(result.total).toBe(2);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ProbeHandler } from '../../types.js';
|
|
2
|
+
|
|
3
|
+
export interface ConnectionInfo {
|
|
4
|
+
pid: number;
|
|
5
|
+
database: string;
|
|
6
|
+
user: string;
|
|
7
|
+
clientAddr: string;
|
|
8
|
+
state: string;
|
|
9
|
+
query: string;
|
|
10
|
+
backendStart: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ConnectionsActiveResult {
|
|
14
|
+
connections: ConnectionInfo[];
|
|
15
|
+
total: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const connectionsActive: ProbeHandler = async (params, exec) => {
|
|
19
|
+
const host = (params?.host as string) ?? 'localhost';
|
|
20
|
+
const port = String((params?.port as number) ?? 5432);
|
|
21
|
+
const user = (params?.user as string) ?? 'postgres';
|
|
22
|
+
|
|
23
|
+
const stdout = await exec('psql', [
|
|
24
|
+
'-h',
|
|
25
|
+
host,
|
|
26
|
+
'-p',
|
|
27
|
+
port,
|
|
28
|
+
'-U',
|
|
29
|
+
user,
|
|
30
|
+
'-t',
|
|
31
|
+
'-A',
|
|
32
|
+
'-F',
|
|
33
|
+
'\t',
|
|
34
|
+
'-c',
|
|
35
|
+
'SELECT pid, datname, usename, client_addr, state, LEFT(query, 200), backend_start FROM pg_stat_activity WHERE state IS NOT NULL AND pid <> pg_backend_pid() ORDER BY backend_start DESC',
|
|
36
|
+
]);
|
|
37
|
+
return parseConnectionsActive(stdout);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function parseConnectionsActive(stdout: string): ConnectionsActiveResult {
|
|
41
|
+
const lines = stdout.trim().split('\n').filter(Boolean);
|
|
42
|
+
const connections: ConnectionInfo[] = lines.map((line) => {
|
|
43
|
+
const parts = line.split('\t');
|
|
44
|
+
return {
|
|
45
|
+
pid: Number(parts[0]) || 0,
|
|
46
|
+
database: parts[1] ?? '',
|
|
47
|
+
user: parts[2] ?? '',
|
|
48
|
+
clientAddr: parts[3] ?? '',
|
|
49
|
+
state: parts[4] ?? '',
|
|
50
|
+
query: parts[5] ?? '',
|
|
51
|
+
backendStart: parts[6] ?? '',
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
return { connections, total: connections.length };
|
|
55
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { ExecFn } from '../../types.js';
|
|
3
|
+
import type { DatabasesListResult } from './databases-list.js';
|
|
4
|
+
import { databasesList, parseDatabasesList } from './databases-list.js';
|
|
5
|
+
|
|
6
|
+
const SAMPLE_OUTPUT = `postgres\tpostgres\tUTF8\t8537 kB
|
|
7
|
+
myapp\tappuser\tUTF8\t156 MB
|
|
8
|
+
analytics\tanalyst\tUTF8\t2345 MB`;
|
|
9
|
+
|
|
10
|
+
describe('parseDatabasesList', () => {
|
|
11
|
+
it('parses psql output into structured data', () => {
|
|
12
|
+
const result = parseDatabasesList(SAMPLE_OUTPUT);
|
|
13
|
+
expect(result.count).toBe(3);
|
|
14
|
+
expect(result.databases[0]).toEqual({
|
|
15
|
+
name: 'postgres',
|
|
16
|
+
owner: 'postgres',
|
|
17
|
+
encoding: 'UTF8',
|
|
18
|
+
sizePretty: '8537 kB',
|
|
19
|
+
});
|
|
20
|
+
expect(result.databases[1]?.name).toBe('myapp');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('handles empty output', () => {
|
|
24
|
+
const result = parseDatabasesList('');
|
|
25
|
+
expect(result.count).toBe(0);
|
|
26
|
+
expect(result.databases).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('databasesList handler', () => {
|
|
31
|
+
it('calls psql with default params', async () => {
|
|
32
|
+
const mockExec: ExecFn = async (cmd, args) => {
|
|
33
|
+
expect(cmd).toBe('psql');
|
|
34
|
+
expect(args).toContain('-h');
|
|
35
|
+
expect(args).toContain('localhost');
|
|
36
|
+
expect(args).toContain('-p');
|
|
37
|
+
expect(args).toContain('5432');
|
|
38
|
+
expect(args).toContain('-U');
|
|
39
|
+
expect(args).toContain('postgres');
|
|
40
|
+
return SAMPLE_OUTPUT;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const result = (await databasesList(undefined, mockExec)) as DatabasesListResult;
|
|
44
|
+
expect(result.count).toBe(3);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('passes custom host/port/user', async () => {
|
|
48
|
+
const mockExec: ExecFn = async (cmd, args) => {
|
|
49
|
+
expect(args).toContain('db.example.com');
|
|
50
|
+
expect(args).toContain('5433');
|
|
51
|
+
expect(args).toContain('admin');
|
|
52
|
+
return SAMPLE_OUTPUT;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
await databasesList({ host: 'db.example.com', port: 5433, user: 'admin' }, mockExec);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ProbeHandler } from '../../types.js';
|
|
2
|
+
|
|
3
|
+
export interface DatabaseInfo {
|
|
4
|
+
name: string;
|
|
5
|
+
owner: string;
|
|
6
|
+
encoding: string;
|
|
7
|
+
sizePretty: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface DatabasesListResult {
|
|
11
|
+
databases: DatabaseInfo[];
|
|
12
|
+
count: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const databasesList: ProbeHandler = async (params, exec) => {
|
|
16
|
+
const host = (params?.host as string) ?? 'localhost';
|
|
17
|
+
const port = String((params?.port as number) ?? 5432);
|
|
18
|
+
const user = (params?.user as string) ?? 'postgres';
|
|
19
|
+
|
|
20
|
+
const stdout = await exec('psql', [
|
|
21
|
+
'-h',
|
|
22
|
+
host,
|
|
23
|
+
'-p',
|
|
24
|
+
port,
|
|
25
|
+
'-U',
|
|
26
|
+
user,
|
|
27
|
+
'-t',
|
|
28
|
+
'-A',
|
|
29
|
+
'-F',
|
|
30
|
+
'\t',
|
|
31
|
+
'-c',
|
|
32
|
+
'SELECT datname, pg_catalog.pg_get_userbyid(datdba), pg_catalog.pg_encoding_to_char(encoding), pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(datname)) FROM pg_catalog.pg_database WHERE datistemplate = false ORDER BY datname',
|
|
33
|
+
]);
|
|
34
|
+
return parseDatabasesList(stdout);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function parseDatabasesList(stdout: string): DatabasesListResult {
|
|
38
|
+
const lines = stdout.trim().split('\n').filter(Boolean);
|
|
39
|
+
const databases: DatabaseInfo[] = lines.map((line) => {
|
|
40
|
+
const [name, owner, encoding, sizePretty] = line.split('\t');
|
|
41
|
+
return {
|
|
42
|
+
name: name ?? '',
|
|
43
|
+
owner: owner ?? '',
|
|
44
|
+
encoding: encoding ?? '',
|
|
45
|
+
sizePretty: sizePretty ?? '',
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
return { databases, count: databases.length };
|
|
49
|
+
}
|