@@ -2,12 +2,16 @@ package commands
22
33import (
44 "context"
5+ "crypto/tls"
56 "encoding/json"
67 "net/http"
78 "strings"
89 "time"
910
1011 "patchmon-agent/internal/client"
12+ "patchmon-agent/internal/integrations"
13+ "patchmon-agent/internal/integrations/docker"
14+ "patchmon-agent/pkg/models"
1115
1216 "github.com/gorilla/websocket"
1317 "github.com/spf13/cobra"
@@ -46,14 +50,30 @@ func runService() error {
4650 ticker := time .NewTicker (time .Duration (intervalMinutes ) * time .Minute )
4751 defer ticker .Stop ()
4852
53+ // Send startup ping to notify server that agent has started
54+ logger .Info ("🚀 Agent starting up, notifying server..." )
55+ if _ , err := httpClient .Ping (ctx ); err != nil {
56+ logger .WithError (err ).Warn ("startup ping failed, will retry" )
57+ } else {
58+ logger .Info ("✅ Startup notification sent to server" )
59+ }
60+
4961 // initial report on boot
62+ logger .Info ("Sending initial report on startup..." )
5063 if err := sendReport (); err != nil {
5164 logger .WithError (err ).Warn ("initial report failed" )
65+ } else {
66+ logger .Info ("✅ Initial report sent successfully" )
5267 }
5368
5469 // start websocket loop
70+ logger .Info ("Establishing WebSocket connection..." )
5571 messages := make (chan wsMsg , 10 )
56- go wsLoop (messages )
72+ dockerEvents := make (chan interface {}, 100 )
73+ go wsLoop (messages , dockerEvents )
74+
75+ // Start integration monitoring (Docker real-time events, etc.)
76+ startIntegrationMonitoring (ctx , dockerEvents )
5777
5878 for {
5979 select {
@@ -92,17 +112,40 @@ func runService() error {
92112 }
93113}
94114
115+ // startIntegrationMonitoring starts real-time monitoring for integrations that support it
116+ func startIntegrationMonitoring (ctx context.Context , eventChan chan <- interface {}) {
117+ // Create integration manager
118+ integrationMgr := integrations .NewManager (logger )
119+
120+ // Register integrations
121+ dockerInteg := docker .New (logger )
122+ integrationMgr .Register (dockerInteg )
123+
124+ // Start monitoring for real-time integrations
125+ realtimeIntegrations := integrationMgr .GetRealtimeIntegrations ()
126+ for _ , integration := range realtimeIntegrations {
127+ logger .WithField ("integration" , integration .Name ()).Info ("Starting real-time monitoring" )
128+
129+ // Start monitoring in a goroutine
130+ go func (integ integrations.RealtimeIntegration ) {
131+ if err := integ .StartMonitoring (ctx , eventChan ); err != nil {
132+ logger .WithError (err ).Warn ("Failed to start integration monitoring" )
133+ }
134+ }(integration )
135+ }
136+ }
137+
95138type wsMsg struct {
96139 kind string
97140 interval int
98141 version string
99142 force bool
100143}
101144
102- func wsLoop (out chan <- wsMsg ) {
145+ func wsLoop (out chan <- wsMsg , dockerEvents <- chan interface {} ) {
103146 backoff := time .Second
104147 for {
105- if err := connectOnce (out ); err != nil {
148+ if err := connectOnce (out , dockerEvents ); err != nil {
106149 logger .WithError (err ).Warn ("ws disconnected; retrying" )
107150 }
108151 time .Sleep (backoff )
@@ -112,7 +155,7 @@ func wsLoop(out chan<- wsMsg) {
112155 }
113156}
114157
115- func connectOnce (out chan <- wsMsg ) error {
158+ func connectOnce (out chan <- wsMsg , dockerEvents <- chan interface {} ) error {
116159 server := cfgManager .GetConfig ().PatchmonServer
117160 if server == "" {
118161 return nil
@@ -135,7 +178,18 @@ func connectOnce(out chan<- wsMsg) error {
135178 header .Set ("X-API-ID" , apiID )
136179 header .Set ("X-API-KEY" , apiKey )
137180
138- conn , _ , err := websocket .DefaultDialer .Dial (wsURL , header )
181+ // Configure WebSocket dialer for insecure connections if needed
182+ dialer := websocket .DefaultDialer
183+ if cfgManager .GetConfig ().SkipSSLVerify {
184+ dialer = & websocket.Dialer {
185+ TLSClientConfig : & tls.Config {
186+ InsecureSkipVerify : true ,
187+ },
188+ }
189+ logger .Warn ("⚠️ SSL certificate verification is disabled for WebSocket" )
190+ }
191+
192+ conn , _ , err := dialer .Dial (wsURL , header )
139193 if err != nil {
140194 return err
141195 }
@@ -157,6 +211,32 @@ func connectOnce(out chan<- wsMsg) error {
157211 })
158212
159213 logger .WithField ("url" , wsURL ).Info ("WebSocket connected" )
214+
215+ // Create a goroutine to send Docker events through WebSocket
216+ go func () {
217+ for event := range dockerEvents {
218+ if dockerEvent , ok := event .(models.DockerStatusEvent ); ok {
219+ eventJSON , err := json .Marshal (map [string ]interface {}{
220+ "type" : "docker_status" ,
221+ "event" : dockerEvent ,
222+ "container_id" : dockerEvent .ContainerID ,
223+ "name" : dockerEvent .Name ,
224+ "status" : dockerEvent .Status ,
225+ "timestamp" : dockerEvent .Timestamp ,
226+ })
227+ if err != nil {
228+ logger .WithError (err ).Warn ("Failed to marshal Docker event" )
229+ continue
230+ }
231+
232+ if err := conn .WriteMessage (websocket .TextMessage , eventJSON ); err != nil {
233+ logger .WithError (err ).Debug ("Failed to send Docker event via WebSocket" )
234+ return
235+ }
236+ }
237+ }
238+ }()
239+
160240 for {
161241 _ , data , err := conn .ReadMessage ()
162242 if err != nil {
0 commit comments