Add an API for exporting activity log data (#15586)
* Add an API for exporting activity log data * Add changelog entry * Switch to error logs
This commit is contained in:
parent
6d668aa60a
commit
df8ae055be
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
core: Add an export API for historical activity log data
|
||||
```
|
|
@ -88,6 +88,8 @@ func buildLogicalRequestNoAuth(perfStandby bool, w http.ResponseWriter, r *http.
|
|||
responseWriter = w
|
||||
case path == "sys/storage/raft/snapshot":
|
||||
responseWriter = w
|
||||
case path == "sys/internal/counters/activity/export":
|
||||
responseWriter = w
|
||||
case path == "sys/monitor":
|
||||
passHTTPReq = true
|
||||
responseWriter = w
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
client_id,namespace_id,timestamp,non_entity,mount_accessor
|
||||
111122222-3333-4444-5555-000000000000,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000001,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000002,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000003,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000004,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000005,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000006,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000007,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000008,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000009,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000010,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000011,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000012,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000013,bbbbb,2,false,auth_3
|
||||
111122222-3333-4444-5555-000000000014,bbbbb,2,false,auth_3
|
||||
111122222-3333-4444-5555-000000000015,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000016,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000017,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000018,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000019,root,2,false,auth_4
|
|
|
@ -0,0 +1,20 @@
|
|||
{"client_id":"111122222-3333-4444-5555-000000000000","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000001","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000002","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000003","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000004","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000005","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000006","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000007","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000008","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000009","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000010","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000011","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000012","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000013","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000014","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000015","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000016","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000017","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000018","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000019","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
|
@ -0,0 +1,41 @@
|
|||
client_id,namespace_id,timestamp,non_entity,mount_accessor
|
||||
111122222-3333-4444-5555-000000000000,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000001,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000002,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000003,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000004,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000005,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000006,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000007,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000008,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000009,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000010,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000011,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000012,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000013,bbbbb,2,false,auth_3
|
||||
111122222-3333-4444-5555-000000000014,bbbbb,2,false,auth_3
|
||||
111122222-3333-4444-5555-000000000015,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000016,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000017,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000018,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000019,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000020,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000021,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000022,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000023,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000024,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000025,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000026,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000027,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000028,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000029,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000030,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000031,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000032,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000033,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000034,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000035,bbbbb,4,false,auth_8
|
||||
111122222-3333-4444-5555-000000000036,bbbbb,4,false,auth_8
|
||||
111122222-3333-4444-5555-000000000037,bbbbb,4,false,auth_8
|
||||
111122222-3333-4444-5555-000000000038,bbbbb,4,false,auth_8
|
||||
111122222-3333-4444-5555-000000000039,bbbbb,4,false,auth_8
|
|
|
@ -0,0 +1,40 @@
|
|||
{"client_id":"111122222-3333-4444-5555-000000000000","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000001","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000002","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000003","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000004","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000005","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000006","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000007","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000008","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000009","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000010","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000011","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000012","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000013","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000014","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000015","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000016","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000017","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000018","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000019","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000020","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000021","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000022","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000023","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000024","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000025","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000026","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000027","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000028","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000029","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000030","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000031","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000032","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000033","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000034","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000035","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000036","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000037","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000038","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000039","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
|
@ -0,0 +1,31 @@
|
|||
client_id,namespace_id,timestamp,non_entity,mount_accessor
|
||||
111122222-3333-4444-5555-000000000000,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000001,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000002,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000003,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000004,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000005,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000006,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000007,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000008,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000009,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000010,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000011,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000012,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000013,bbbbb,2,false,auth_3
|
||||
111122222-3333-4444-5555-000000000014,bbbbb,2,false,auth_3
|
||||
111122222-3333-4444-5555-000000000015,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000016,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000017,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000018,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000019,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000020,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000021,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000022,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000023,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000024,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000025,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000026,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000027,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000028,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000029,ccccc,3,false,auth_6
|
|
|
@ -0,0 +1,30 @@
|
|||
{"client_id":"111122222-3333-4444-5555-000000000000","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000001","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000002","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000003","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000004","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000005","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000006","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000007","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000008","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000009","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000010","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000011","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000012","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000013","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000014","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000015","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000016","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000017","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000018","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000019","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000020","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000021","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000022","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000023","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000024","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000025","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000026","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000027","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000028","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000029","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
|
@ -0,0 +1,46 @@
|
|||
client_id,namespace_id,timestamp,non_entity,mount_accessor
|
||||
111122222-3333-4444-5555-000000000040,rrrrr,0,false,auth_9
|
||||
111122222-3333-4444-5555-000000000041,rrrrr,0,false,auth_9
|
||||
111122222-3333-4444-5555-000000000042,rrrrr,0,false,auth_9
|
||||
111122222-3333-4444-5555-000000000043,rrrrr,0,false,auth_9
|
||||
111122222-3333-4444-5555-000000000044,rrrrr,0,false,auth_9
|
||||
111122222-3333-4444-5555-000000000000,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000001,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000002,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000003,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000004,root,1,false,auth_1
|
||||
111122222-3333-4444-5555-000000000005,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000006,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000007,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000008,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000009,aaaaa,1,false,auth_2
|
||||
111122222-3333-4444-5555-000000000010,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000011,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000012,bbbbb,1,false,auth_3
|
||||
111122222-3333-4444-5555-000000000013,bbbbb,2,false,auth_3
|
||||
111122222-3333-4444-5555-000000000014,bbbbb,2,false,auth_3
|
||||
111122222-3333-4444-5555-000000000015,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000016,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000017,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000018,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000019,root,2,false,auth_4
|
||||
111122222-3333-4444-5555-000000000020,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000021,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000022,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000023,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000024,root,3,false,auth_5
|
||||
111122222-3333-4444-5555-000000000025,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000026,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000027,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000028,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000029,ccccc,3,false,auth_6
|
||||
111122222-3333-4444-5555-000000000030,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000031,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000032,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000033,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000034,root,4,false,auth_7
|
||||
111122222-3333-4444-5555-000000000035,bbbbb,4,false,auth_8
|
||||
111122222-3333-4444-5555-000000000036,bbbbb,4,false,auth_8
|
||||
111122222-3333-4444-5555-000000000037,bbbbb,4,false,auth_8
|
||||
111122222-3333-4444-5555-000000000038,bbbbb,4,false,auth_8
|
||||
111122222-3333-4444-5555-000000000039,bbbbb,4,false,auth_8
|
|
|
@ -0,0 +1,45 @@
|
|||
{"client_id":"111122222-3333-4444-5555-000000000040","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000041","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000042","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000043","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000044","namespace_id":"rrrrr","mount_accessor":"auth_9"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000000","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000001","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000002","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000003","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000004","namespace_id":"root","timestamp":1,"mount_accessor":"auth_1"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000005","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000006","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000007","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000008","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000009","namespace_id":"aaaaa","timestamp":1,"mount_accessor":"auth_2"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000010","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000011","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000012","namespace_id":"bbbbb","timestamp":1,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000013","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000014","namespace_id":"bbbbb","timestamp":2,"mount_accessor":"auth_3"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000015","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000016","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000017","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000018","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000019","namespace_id":"root","timestamp":2,"mount_accessor":"auth_4"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000020","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000021","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000022","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000023","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000024","namespace_id":"root","timestamp":3,"mount_accessor":"auth_5"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000025","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000026","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000027","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000028","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000029","namespace_id":"ccccc","timestamp":3,"mount_accessor":"auth_6"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000030","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000031","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000032","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000033","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000034","namespace_id":"root","timestamp":4,"mount_accessor":"auth_7"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000035","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000036","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000037","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000038","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
||||
{"client_id":"111122222-3333-4444-5555-000000000039","namespace_id":"bbbbb","timestamp":4,"mount_accessor":"auth_8"}
|
|
@ -2,9 +2,12 @@ package vault
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -20,6 +23,7 @@ import (
|
|||
"github.com/hashicorp/vault/helper/timeutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/vault/activity"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -160,6 +164,8 @@ type ActivityLog struct {
|
|||
|
||||
// partialMonthClientTracker tracks active clients this month. Protected by fragmentLock.
|
||||
partialMonthClientTracker map[string]*activity.EntityRecord
|
||||
|
||||
inprocessExport *atomic.Bool
|
||||
}
|
||||
|
||||
// These non-persistent configuration options allow us to disable
|
||||
|
@ -207,6 +213,7 @@ func NewActivityLog(core *Core, logger log.Logger, view *BarrierView, metrics me
|
|||
clientSequenceNumber: 0,
|
||||
},
|
||||
standbyFragmentsReceived: make([]*activity.LogFragment, 0),
|
||||
inprocessExport: atomic.NewBool(false),
|
||||
}
|
||||
|
||||
config, err := a.loadConfigOrDefault(core.activeContext)
|
||||
|
@ -525,10 +532,7 @@ func (a *ActivityLog) getLastEntitySegmentNumber(ctx context.Context, startTime
|
|||
}
|
||||
|
||||
// WalkEntitySegments loads each of the entity segments for a particular start time
|
||||
func (a *ActivityLog) WalkEntitySegments(ctx context.Context,
|
||||
startTime time.Time,
|
||||
walkFn func(*activity.EntityActivityLog, time.Time),
|
||||
) error {
|
||||
func (a *ActivityLog) WalkEntitySegments(ctx context.Context, startTime time.Time, walkFn func(*activity.EntityActivityLog, time.Time) error) error {
|
||||
basePath := activityEntityBasePath + fmt.Sprint(startTime.Unix()) + "/"
|
||||
pathList, err := a.view.List(ctx, basePath)
|
||||
if err != nil {
|
||||
|
@ -550,7 +554,10 @@ func (a *ActivityLog) WalkEntitySegments(ctx context.Context,
|
|||
if err != nil {
|
||||
return fmt.Errorf("unable to parse segment %v%v: %w", basePath, path, err)
|
||||
}
|
||||
walkFn(out, startTime)
|
||||
err = walkFn(out, startTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to walk entities: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2054,7 +2061,7 @@ func (a *ActivityLog) precomputedQueryWorker(ctx context.Context) error {
|
|||
byNamespace := make(map[string]*processByNamespace)
|
||||
byMonth := make(map[int64]*processMonth)
|
||||
|
||||
walkEntities := func(l *activity.EntityActivityLog, startTime time.Time) {
|
||||
walkEntities := func(l *activity.EntityActivityLog, startTime time.Time) error {
|
||||
for _, e := range l.Clients {
|
||||
processClientRecord(e, byNamespace, byMonth, startTime)
|
||||
|
||||
|
@ -2102,6 +2109,8 @@ func (a *ActivityLog) precomputedQueryWorker(ctx context.Context) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
walkTokens := func(l *activity.TokenCount) {
|
||||
|
@ -2569,3 +2578,163 @@ func (a *ActivityLog) partialMonthClientCount(ctx context.Context) (map[string]i
|
|||
|
||||
return responseData, nil
|
||||
}
|
||||
|
||||
func (a *ActivityLog) writeExport(ctx context.Context, rw http.ResponseWriter, format string, startTime, endTime time.Time) error {
|
||||
// For capacity reasons only allow a single in-process export at a time.
|
||||
// TODO do we really need to do this?
|
||||
if !a.inprocessExport.CAS(false, true) {
|
||||
return fmt.Errorf("existing export in progress")
|
||||
}
|
||||
defer a.inprocessExport.Store(false)
|
||||
|
||||
// Find the months with activity log data that are between the start and end
|
||||
// months. We want to walk this in cronological order so the oldest instance of a
|
||||
// client usage is recorded, not the most recent.
|
||||
times, err := a.getMostRecentNonContiguousActivityLogSegments(ctx)
|
||||
if err != nil {
|
||||
a.logger.Warn("failed to list recent segments", "error", err)
|
||||
return fmt.Errorf("failed to list recent segments: %w", err)
|
||||
}
|
||||
sort.Slice(times, func(i, j int) bool {
|
||||
// sort in chronological order to produce the output we want showing what
|
||||
// month an entity first had activity.
|
||||
return times[i].Before(times[j])
|
||||
})
|
||||
|
||||
// Filter over just the months we care about
|
||||
filteredList := make([]time.Time, 0, len(times))
|
||||
for _, t := range times {
|
||||
if timeutil.InRange(t, startTime, endTime) {
|
||||
filteredList = append(filteredList, t)
|
||||
}
|
||||
}
|
||||
if len(filteredList) == 0 {
|
||||
a.logger.Info("no data to export", "start_time", startTime, "end_time", endTime)
|
||||
return fmt.Errorf("no data to export in provided time range")
|
||||
}
|
||||
|
||||
actualStartTime := filteredList[len(filteredList)-1]
|
||||
a.logger.Trace("choose start time for export", "actualStartTime", actualStartTime, "months_included", filteredList)
|
||||
|
||||
// Add headers here because we start to immediately write in the csv encoder
|
||||
// constructor.
|
||||
rw.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=\"activity_export_%d_to_%d.%s\"", actualStartTime.Unix(), endTime.Unix(), format))
|
||||
rw.Header().Add("Content-Type", fmt.Sprintf("application/%s", format))
|
||||
|
||||
var encoder encoder
|
||||
switch format {
|
||||
case "json":
|
||||
encoder = newJSONEncoder(rw)
|
||||
case "csv":
|
||||
var err error
|
||||
encoder, err = newCSVEncoder(rw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create csv encoder: %w", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid format: %s", format)
|
||||
}
|
||||
|
||||
a.logger.Info("starting activity log export", "start_time", startTime, "end_time", endTime, "format", format)
|
||||
|
||||
dedupedIds := make(map[string]struct{})
|
||||
walkEntities := func(l *activity.EntityActivityLog, startTime time.Time) error {
|
||||
for _, e := range l.Clients {
|
||||
if _, ok := dedupedIds[e.ClientID]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
dedupedIds[e.ClientID] = struct{}{}
|
||||
err := encoder.Encode(e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// For each month in the filtered list walk all the log segments
|
||||
for _, startTime := range filteredList {
|
||||
err := a.WalkEntitySegments(ctx, startTime, walkEntities)
|
||||
if err != nil {
|
||||
a.logger.Error("failed to load segments for export", "error", err)
|
||||
return fmt.Errorf("failed to load segments for export: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Flush and error check the encoder. This is neccessary for buffered
|
||||
// encoders like csv.
|
||||
encoder.Flush()
|
||||
if err := encoder.Error(); err != nil {
|
||||
a.logger.Error("failed to flush export encoding", "error", err)
|
||||
return fmt.Errorf("failed to flush export encoding: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type encoder interface {
|
||||
Encode(*activity.EntityRecord) error
|
||||
Flush()
|
||||
Error() error
|
||||
}
|
||||
|
||||
var _ encoder = (*jsonEncoder)(nil)
|
||||
|
||||
type jsonEncoder struct {
|
||||
e *json.Encoder
|
||||
}
|
||||
|
||||
func newJSONEncoder(w io.Writer) *jsonEncoder {
|
||||
return &jsonEncoder{
|
||||
e: json.NewEncoder(w),
|
||||
}
|
||||
}
|
||||
|
||||
func (j *jsonEncoder) Encode(er *activity.EntityRecord) error {
|
||||
return j.e.Encode(er)
|
||||
}
|
||||
|
||||
// Flush is a no-op because json.Encoder doesn't buffer data
|
||||
func (j *jsonEncoder) Flush() {}
|
||||
|
||||
// Error is a no-op because flushing is a no-op.
|
||||
func (j *jsonEncoder) Error() error { return nil }
|
||||
|
||||
var _ encoder = (*csvEncoder)(nil)
|
||||
|
||||
type csvEncoder struct {
|
||||
*csv.Writer
|
||||
}
|
||||
|
||||
func newCSVEncoder(w io.Writer) (*csvEncoder, error) {
|
||||
writer := csv.NewWriter(w)
|
||||
|
||||
err := writer.Write([]string{
|
||||
"client_id",
|
||||
"namespace_id",
|
||||
"timestamp",
|
||||
"non_entity",
|
||||
"mount_accessor",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &csvEncoder{
|
||||
Writer: writer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encode converts an export bundle into a set of strings and writes them to the
|
||||
// csv writer.
|
||||
func (c *csvEncoder) Encode(e *activity.EntityRecord) error {
|
||||
return c.Writer.Write([]string{
|
||||
e.ClientID,
|
||||
e.NamespaceID,
|
||||
fmt.Sprintf("%d", e.Timestamp),
|
||||
fmt.Sprintf("%t", e.NonEntity),
|
||||
e.MountAccessor,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -1715,6 +1719,192 @@ func TestActivityLog_refreshFromStoredLogPreviousMonth(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestActivityLog_Export(t *testing.T) {
|
||||
timeutil.SkipAtEndOfMonth(t)
|
||||
|
||||
january := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
august := time.Date(2020, 8, 15, 12, 0, 0, 0, time.UTC)
|
||||
september := timeutil.StartOfMonth(time.Date(2020, 9, 1, 0, 0, 0, 0, time.UTC))
|
||||
october := timeutil.StartOfMonth(time.Date(2020, 10, 1, 0, 0, 0, 0, time.UTC))
|
||||
november := timeutil.StartOfMonth(time.Date(2020, 11, 1, 0, 0, 0, 0, time.UTC))
|
||||
|
||||
core, _, _, _ := TestCoreUnsealedWithMetrics(t)
|
||||
a := core.activityLog
|
||||
ctx := namespace.RootContext(nil)
|
||||
|
||||
// Generate overlapping sets of entity IDs from this list.
|
||||
// january: 40-44 RRRRR
|
||||
// first month: 0-19 RRRRRAAAAABBBBBRRRRR
|
||||
// second month: 10-29 BBBBBRRRRRRRRRRCCCCC
|
||||
// third month: 15-39 RRRRRRRRRRCCCCCRRRRRBBBBB
|
||||
|
||||
entityRecords := make([]*activity.EntityRecord, 45)
|
||||
entityNamespaces := []string{"root", "aaaaa", "bbbbb", "root", "root", "ccccc", "root", "bbbbb", "rrrrr"}
|
||||
authMethods := []string{"auth_1", "auth_2", "auth_3", "auth_4", "auth_5", "auth_6", "auth_7", "auth_8", "auth_9"}
|
||||
|
||||
for i := range entityRecords {
|
||||
entityRecords[i] = &activity.EntityRecord{
|
||||
ClientID: fmt.Sprintf("111122222-3333-4444-5555-%012v", i),
|
||||
NamespaceID: entityNamespaces[i/5],
|
||||
MountAccessor: authMethods[i/5],
|
||||
}
|
||||
}
|
||||
|
||||
toInsert := []struct {
|
||||
StartTime int64
|
||||
Segment uint64
|
||||
Clients []*activity.EntityRecord
|
||||
}{
|
||||
// January, should not be included
|
||||
{
|
||||
january.Unix(),
|
||||
0,
|
||||
entityRecords[40:45],
|
||||
},
|
||||
// Artifically split August and October
|
||||
{ // 1
|
||||
august.Unix(),
|
||||
0,
|
||||
entityRecords[:13],
|
||||
},
|
||||
{ // 2
|
||||
august.Unix(),
|
||||
1,
|
||||
entityRecords[13:20],
|
||||
},
|
||||
{ // 3
|
||||
september.Unix(),
|
||||
0,
|
||||
entityRecords[10:30],
|
||||
},
|
||||
{ // 4
|
||||
october.Unix(),
|
||||
0,
|
||||
entityRecords[15:40],
|
||||
},
|
||||
{
|
||||
october.Unix(),
|
||||
1,
|
||||
entityRecords[15:40],
|
||||
},
|
||||
{
|
||||
october.Unix(),
|
||||
2,
|
||||
entityRecords[17:23],
|
||||
},
|
||||
}
|
||||
|
||||
for i, segment := range toInsert {
|
||||
eal := &activity.EntityActivityLog{
|
||||
Clients: segment.Clients,
|
||||
}
|
||||
|
||||
// Mimic a lower time stamp for earlier clients
|
||||
for _, c := range eal.Clients {
|
||||
c.Timestamp = int64(i)
|
||||
}
|
||||
|
||||
data, err := proto.Marshal(eal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path := fmt.Sprintf("%ventity/%v/%v", ActivityLogPrefix, segment.StartTime, segment.Segment)
|
||||
WriteToStorage(t, core, path, data)
|
||||
}
|
||||
|
||||
tCases := []struct {
|
||||
format string
|
||||
startTime time.Time
|
||||
endTime time.Time
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
format: "json",
|
||||
startTime: august,
|
||||
endTime: timeutil.EndOfMonth(september),
|
||||
expected: "aug_sep.json",
|
||||
},
|
||||
{
|
||||
format: "csv",
|
||||
startTime: august,
|
||||
endTime: timeutil.EndOfMonth(september),
|
||||
expected: "aug_sep.csv",
|
||||
},
|
||||
{
|
||||
format: "json",
|
||||
startTime: january,
|
||||
endTime: timeutil.EndOfMonth(november),
|
||||
expected: "full_history.json",
|
||||
},
|
||||
{
|
||||
format: "csv",
|
||||
startTime: january,
|
||||
endTime: timeutil.EndOfMonth(november),
|
||||
expected: "full_history.csv",
|
||||
},
|
||||
{
|
||||
format: "json",
|
||||
startTime: august,
|
||||
endTime: timeutil.EndOfMonth(october),
|
||||
expected: "aug_oct.json",
|
||||
},
|
||||
{
|
||||
format: "csv",
|
||||
startTime: august,
|
||||
endTime: timeutil.EndOfMonth(october),
|
||||
expected: "aug_oct.csv",
|
||||
},
|
||||
{
|
||||
format: "json",
|
||||
startTime: august,
|
||||
endTime: timeutil.EndOfMonth(august),
|
||||
expected: "aug.json",
|
||||
},
|
||||
{
|
||||
format: "csv",
|
||||
startTime: august,
|
||||
endTime: timeutil.EndOfMonth(august),
|
||||
expected: "aug.csv",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tCase := range tCases {
|
||||
rw := &fakeResponseWriter{
|
||||
buffer: &bytes.Buffer{},
|
||||
headers: http.Header{},
|
||||
}
|
||||
if err := a.writeExport(ctx, rw, tCase.format, tCase.startTime, tCase.endTime); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected, err := os.ReadFile(filepath.Join("activity", "test_fixtures", tCase.expected))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(rw.buffer.Bytes(), expected) {
|
||||
t.Fatal(rw.buffer.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fakeResponseWriter struct {
|
||||
buffer *bytes.Buffer
|
||||
headers http.Header
|
||||
}
|
||||
|
||||
func (f *fakeResponseWriter) Write(b []byte) (int, error) {
|
||||
return f.buffer.Write(b)
|
||||
}
|
||||
|
||||
func (f *fakeResponseWriter) Header() http.Header {
|
||||
return f.headers
|
||||
}
|
||||
|
||||
func (f *fakeResponseWriter) WriteHeader(statusCode int) {
|
||||
panic("unimplmeneted")
|
||||
}
|
||||
|
||||
func TestActivityLog_IncludeNamespace(t *testing.T) {
|
||||
root := namespace.RootNamespace
|
||||
a := &ActivityLog{}
|
||||
|
|
|
@ -5198,6 +5198,10 @@ This path responds to the following HTTP methods.
|
|||
"Query the historical count of clients.",
|
||||
"Query the historical count of clients.",
|
||||
},
|
||||
"activity-export": {
|
||||
"Export the historical activity of clients.",
|
||||
"Export the historical activity of clients.",
|
||||
},
|
||||
"activity-monthly": {
|
||||
"Count of active clients so far this month.",
|
||||
"Count of active clients so far this month.",
|
||||
|
|
|
@ -2,7 +2,9 @@ package vault
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -97,15 +99,37 @@ func (b *SystemBackend) rootActivityPaths() []*framework.Path {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Pattern: "internal/counters/activity/export$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"start_time": {
|
||||
Type: framework.TypeTime,
|
||||
Description: "Start of query interval",
|
||||
},
|
||||
"end_time": {
|
||||
Type: framework.TypeTime,
|
||||
Description: "End of query interval",
|
||||
},
|
||||
"format": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Format of the file. Either a CSV or a JSON file with an object per line.",
|
||||
Default: "json",
|
||||
},
|
||||
},
|
||||
HelpSynopsis: strings.TrimSpace(sysHelp["activity-export"][0]),
|
||||
HelpDescription: strings.TrimSpace(sysHelp["activity-export"][1]),
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.handleClientExport,
|
||||
Summary: "Report the client count metrics, for this namespace and all child namespaces.",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SystemBackend) handleClientMetricQuery(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
a := b.Core.activityLog
|
||||
if a == nil {
|
||||
return logical.ErrorResponse("no activity log present"), nil
|
||||
}
|
||||
|
||||
func parseStartEndTimes(a *ActivityLog, d *framework.FieldData) (time.Time, time.Time, error) {
|
||||
startTime := d.Get("start_time").(time.Time)
|
||||
endTime := d.Get("end_time").(time.Time)
|
||||
|
||||
|
@ -126,7 +150,52 @@ func (b *SystemBackend) handleClientMetricQuery(ctx context.Context, req *logica
|
|||
startTime = startTime.UTC()
|
||||
}
|
||||
if startTime.After(endTime) {
|
||||
return logical.ErrorResponse("start_time is later than end_time"), nil
|
||||
return time.Time{}, time.Time{}, fmt.Errorf("start_time is later than end_time")
|
||||
}
|
||||
|
||||
return startTime, endTime, nil
|
||||
}
|
||||
|
||||
func (b *SystemBackend) handleClientExport(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
a := b.Core.activityLog
|
||||
if a == nil {
|
||||
return logical.ErrorResponse("no activity log present"), nil
|
||||
}
|
||||
|
||||
startTime, endTime, err := parseStartEndTimes(a, d)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
// This is to avoid the default 90s context timeout.
|
||||
timeout := 10 * time.Minute
|
||||
if durationRaw := os.Getenv("VAULT_ACTIVITY_EXPORT_DURATION"); durationRaw != "" {
|
||||
d, err := time.ParseDuration(durationRaw)
|
||||
if err == nil {
|
||||
timeout = d
|
||||
}
|
||||
}
|
||||
|
||||
runCtx, cancelFunc := context.WithTimeout(b.Core.activeContext, timeout)
|
||||
defer cancelFunc()
|
||||
|
||||
err = a.writeExport(runCtx, req.ResponseWriter, d.Get("format").(string), startTime, endTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *SystemBackend) handleClientMetricQuery(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
a := b.Core.activityLog
|
||||
if a == nil {
|
||||
return logical.ErrorResponse("no activity log present"), nil
|
||||
}
|
||||
|
||||
startTime, endTime, err := parseStartEndTimes(a, d)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
results, err := a.handleQuery(ctx, startTime, endTime)
|
||||
|
|
|
@ -848,3 +848,58 @@ $ curl \
|
|||
"warnings": null
|
||||
}
|
||||
```
|
||||
|
||||
## Activity Export
|
||||
|
||||
This endpoint returns an export of the clients that had activity within the
|
||||
provided start and end times. The returned set of client information will be
|
||||
deduplicated over the time window and will show the earliest activity logged for
|
||||
each client. The output will be ordered chronologically by month of activity.
|
||||
|
||||
~> **NOTE**: This endpoint is currently in tech preview status.
|
||||
|
||||
There are a few things to keep in mind while using this API.
|
||||
|
||||
- The response includes the actual time period covered, which may not exactly
|
||||
match the query parameters due to the month granularity of data or missing
|
||||
months in the requested time range.
|
||||
|
||||
- If the `end_date` supplied to the API is for the current month, the activity
|
||||
information returned by this API will include activity for this month, however
|
||||
it may be up to 20 minutes delayed.
|
||||
|
||||
This endpoint was added in Vault 1.11.
|
||||
|
||||
| Method | Path |
|
||||
| :----- | :---------------------------------------- |
|
||||
| `GET` | `/sys/internal/counters/activity/export` |
|
||||
|
||||
### Parameters
|
||||
|
||||
- `start_time` `(string, optional)` - An RFC3339 timestamp or Unix epoch time. Specifies the start of the
|
||||
period for which client counts will be reported. If no start time is specified, the `default_report_months`
|
||||
prior to the `end_time` will be used.
|
||||
- `end_time` `(string, optional)` - An RFC3339 timestamp or Unix epoch time. Specifies the end of the period
|
||||
for which client counts will be reported. If no end time is specified, the end of the previous calendar
|
||||
month will be used.
|
||||
- `format` `(string, optional)` - The desired format of the output file. Allowed
|
||||
values are `csv` and `json`. If no format is provided a default of `json`
|
||||
will be used.
|
||||
|
||||
### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--header "X-Vault-Token: ..." \
|
||||
--request GET \
|
||||
http://127.0.0.1:8200/v1/sys/internal/counters/activity/export
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
{"client_id":"3f210722-7210-98e8-1f0d-e6a39ffb29c6","namespace_id":"root","timestamp":1653350457,"mount_accessor":"auth_userpass_bb52979d"}
|
||||
{"client_id":"X/Yed4Oj4cqODj9tSHjKwnRy5QVSBRlX3COxjjWSXyI=","namespace_id":"root","timestamp":1653350491,"non_entity":true,"mount_accessor":"auth_token_f6f2c11c"}
|
||||
{"client_id":"d93405dc-b592-b1c3-a520-14e618d359c1","namespace_id":"root","timestamp":1653350501,"mount_accessor":"auth_userpass_bb52979d"}
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in New Issue