@@ -6,13 +6,19 @@ import (
66 "sort"
77 "time"
88
9+ "github.com/Velocidex/ordereddict"
910 "google.golang.org/protobuf/proto"
1011 crypto_proto "www.velocidex.com/golang/velociraptor/crypto/proto"
12+ "www.velocidex.com/golang/velociraptor/flows"
13+ flows_proto "www.velocidex.com/golang/velociraptor/flows/proto"
14+ "www.velocidex.com/golang/velociraptor/json"
1115 "www.velocidex.com/golang/velociraptor/services"
1216 "www.velocidex.com/golang/velociraptor/services/client_info"
1317 "www.velocidex.com/golang/velociraptor/utils"
18+ "www.velocidex.com/golang/velociraptor/vql/acl_managers"
1419 "www.velocidex.com/golang/velociraptor/vtesting"
1520 "www.velocidex.com/golang/velociraptor/vtesting/assert"
21+ "www.velocidex.com/golang/velociraptor/vtesting/goldie"
1622)
1723
1824func (self * ClientInfoTestSuite ) TestQueueMessages () {
@@ -85,3 +91,111 @@ func (self *ClientInfoTestSuite) TestFastQueueMessages() {
8591 assert .True (self .T (), proto .Equal (tasks [i ], written [i ]))
8692 }
8793}
94+
95+ func (self * ClientInfoTestSuite ) TestInFlightMessages () {
96+ closer := utils .MockTime (utils .NewMockClock (time .Unix (10 , 0 )))
97+ defer closer ()
98+
99+ launcher , err := services .GetLauncher (self .ConfigObj )
100+ assert .NoError (self .T (), err )
101+
102+ var flow_ids []string
103+ acl_manager := acl_managers.NullACLManager {}
104+ manager , _ := services .GetRepositoryManager (self .ConfigObj )
105+ repository , _ := manager .GetGlobalRepository (self .ConfigObj )
106+
107+ for i := 0 ; i < 10 ; i ++ {
108+ closer := utils .SetFlowIdForTests (fmt .Sprintf ("F.%d" , i ))
109+
110+ flow_id , err := launcher .ScheduleArtifactCollection (self .Ctx ,
111+ self .ConfigObj , acl_manager ,
112+ repository , & flows_proto.ArtifactCollectorArgs {
113+ Creator : "admin" ,
114+ ClientId : self .client_id ,
115+ Artifacts : []string {"Client.Test" },
116+ }, utils .SyncCompleter )
117+ assert .NoError (self .T (), err )
118+
119+ flow_ids = append (flow_ids , flow_id )
120+
121+ closer ()
122+ }
123+
124+ client_info_manager , err := services .GetClientInfoManager (self .ConfigObj )
125+ assert .NoError (self .T (), err )
126+
127+ tasks , err := client_info_manager .GetClientTasks (self .Ctx , self .client_id )
128+ assert .NoError (self .T (), err )
129+
130+ // 4 tasks are queued
131+ assert .Equal (self .T (), len (tasks ), 4 )
132+
133+ tasks , err = client_info_manager .GetClientTasks (self .Ctx , self .client_id )
134+ assert .NoError (self .T (), err )
135+
136+ // Tasks are still in flight, so we can not get any new tasks yet.
137+ assert .Equal (self .T (), len (tasks ), 0 )
138+
139+ client_info , err := client_info_manager .Get (self .Ctx , self .client_id )
140+ assert .NoError (self .T (), err )
141+
142+ // Should contain only 4 flow ids in the in_flight_flows set.
143+ golden := ordereddict .NewDict ().
144+ Set ("InFlightFlows" , client_info )
145+
146+ // Pass some time
147+ closer = utils .MockTime (utils .NewMockClock (time .Unix (100 , 0 )))
148+ defer closer ()
149+
150+ // Tasks are still in flight, so we do not send any flows, instead
151+ // we send a task status request to see how those other tasks are
152+ // going.
153+ tasks , err = client_info_manager .GetClientTasks (self .Ctx , self .client_id )
154+ assert .NoError (self .T (), err )
155+
156+ sort .Strings (tasks [0 ].FlowStatsRequest .FlowId )
157+
158+ assert .Equal (self .T (), len (tasks ), 1 )
159+
160+ // Should contains a status check request for all inflight flows.
161+ golden .Set ("StatusChecks" , tasks )
162+
163+ // Now complete the flows
164+ runner := flows .NewFlowRunner (self .Ctx , self .ConfigObj )
165+ for flow_id := range client_info .InFlightFlows {
166+ runner .ProcessSingleMessage (self .Ctx ,
167+ & crypto_proto.VeloMessage {
168+ Source : self .client_id ,
169+ SessionId : flow_id ,
170+ FlowStats : & crypto_proto.FlowStats {
171+ FlowComplete : true ,
172+ QueryStatus : []* crypto_proto.VeloStatus {{
173+ Status : crypto_proto .VeloStatus_OK ,
174+ }},
175+ }})
176+ }
177+ runner .Close (self .Ctx )
178+
179+ client_info , err = client_info_manager .Get (self .Ctx , self .client_id )
180+ assert .NoError (self .T (), err )
181+
182+ // Completing the flows removes the flows from the in flight set.
183+ assert .Equal (self .T (), len (client_info .InFlightFlows ), 0 )
184+
185+ // Should contain no in flight flows but still contain the
186+ // has_tasks flag.
187+ golden .Set ("AfterCompletion" , client_info )
188+
189+ // Now read some more tasks
190+ tasks , err = client_info_manager .GetClientTasks (self .Ctx , self .client_id )
191+ assert .NoError (self .T (), err )
192+
193+ // Should conatin
194+ assert .Equal (self .T (), len (tasks ), 4 )
195+
196+ client_info , err = client_info_manager .Get (self .Ctx , self .client_id )
197+ assert .NoError (self .T (), err )
198+ golden .Set ("SecondSetOfTasks" , client_info )
199+
200+ goldie .Assert (self .T (), "TestInFlightMessages" , json .MustMarshalIndent (golden ))
201+ }
0 commit comments