master
h 165 lines 5.57 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 DockerEventTracker.h
8
9 Abstract:
10
11 Contains the definition for DockerEventTracker.
12
13 --*/
14
15 #pragma once
16
17 #include "DockerHTTPClient.h"
18 #include "IORelay.h"
19
20 namespace wsl::windows::service::wslc {
21
22 class WSLCSession;
23 class WSLCVirtualMachine;
24
25 enum class ContainerEvent
26 {
27 Create,
28 Start,
29 Restart,
30 Stop,
31 Exit,
32 Destroy,
33 ExecDied,
34 Kill
35 };
36
37 enum class VolumeEvent
38 {
39 Create,
40 Destroy
41 };
42
43 class DockerEventTracker
44 {
45 public:
46 NON_COPYABLE(DockerEventTracker);
47 NON_MOVABLE(DockerEventTracker);
48
49 struct EventTrackingReference
50 {
51 NON_COPYABLE(EventTrackingReference);
52
53 EventTrackingReference() = default;
54 EventTrackingReference(DockerEventTracker* tracker, size_t id) noexcept;
55 EventTrackingReference(EventTrackingReference&& other) noexcept;
56 ~EventTrackingReference() noexcept;
57
58 EventTrackingReference& operator=(EventTrackingReference&&) noexcept;
59
60 void Reset() noexcept;
61
62 size_t m_id;
63 DockerEventTracker* m_tracker = nullptr;
64 };
65
66 using ContainerStateChangeCallback = std::function<void(ContainerEvent, std::optional<int>, std::int64_t)>;
67 using VolumeEventCallback = std::function<void(const std::string&, VolumeEvent, std::int64_t)>;
68 using ContainerCreateCallback = std::function<void(const std::string& ContainerId, std::int64_t Time)>;
69
70 explicit DockerEventTracker(WSLCSession& session);
71 ~DockerEventTracker();
72
73 // Binds the tracker to a VM's docker client and IO relay. Called on every VM start. Existing
74 // container/volume registrations are preserved across (re)connects so callers do not re-register
75 // when the VM is idle-terminated and later restarted.
76 void Connect(DockerHTTPClient& dockerClient, IORelay& relay);
77
78 EventTrackingReference RegisterContainerStateUpdates(const std::string& ContainerId, ContainerStateChangeCallback&& Callback) noexcept;
79 EventTrackingReference RegisterExecStateUpdates(const std::string& ContainerId, const std::string& ExecId, ContainerStateChangeCallback&& Callback) noexcept;
80 EventTrackingReference RegisterVolumeUpdates(VolumeEventCallback&& Callback) noexcept;
81
82 // Invoked for every container create event, after the per-container state callbacks. Unlike those,
83 // this isn't keyed by container id, because the id isn't known until Docker assigns it.
84 EventTrackingReference RegisterContainerCreate(ContainerCreateCallback&& Callback) noexcept;
85 void UnregisterCallback(size_t Id) noexcept;
86
87 private:
88 void OnEvent(const std::string_view& event);
89 void OnContainerEvent(const nlohmann::json& parsed, const std::string& action, std::int64_t eventTime);
90 void OnContainerCreated(const nlohmann::json& parsed, std::int64_t eventTime);
91 void OnVolumeEvent(const nlohmann::json& parsed, const std::string& action, std::int64_t eventTime);
92
93 // Callbacks are invoked without holding m_lock so that a callback can register or unregister callbacks, and so
94 // that a callback taking its own lock can't invert with a thread that registers a callback under that same lock.
95 struct CallbackRegistration
96 {
97 NON_COPYABLE(CallbackRegistration);
98 NON_MOVABLE(CallbackRegistration);
99
100 CallbackRegistration(size_t Id) noexcept : CallbackId(Id)
101 {
102 }
103
104 const size_t CallbackId;
105
106 // Held while the callback runs so it can't be invoked once UnregisterCallback() returned for it.
107 // N.B. Recursive so a running callback can unregister itself.
108 std::recursive_mutex InvokeLock;
109 _Guarded_by_(InvokeLock) bool Unregistered = false;
110 };
111
112 struct ContainerCallback : CallbackRegistration
113 {
114 ContainerCallback(size_t Id, std::string&& ContainerId, std::optional<std::string>&& ExecId, ContainerStateChangeCallback&& Callback) :
115 CallbackRegistration(Id), ContainerId(std::move(ContainerId)), ExecId(std::move(ExecId)), Callback(std::move(Callback))
116 {
117 }
118
119 const std::string ContainerId;
120 const std::optional<std::string> ExecId;
121 const ContainerStateChangeCallback Callback;
122 };
123
124 struct VolumeCallback : CallbackRegistration
125 {
126 VolumeCallback(size_t Id, VolumeEventCallback&& Callback) : CallbackRegistration(Id), Callback(std::move(Callback))
127 {
128 }
129
130 const VolumeEventCallback Callback;
131 };
132
133 struct ContainerCreateCallbackEntry : CallbackRegistration
134 {
135 ContainerCreateCallbackEntry(size_t Id, ContainerCreateCallback&& Callback) :
136 CallbackRegistration(Id), Callback(std::move(Callback))
137 {
138 }
139
140 const ContainerCreateCallback Callback;
141 };
142
143 _Guarded_by_(m_lock) std::vector<std::shared_ptr<ContainerCallback>> m_containerCallbacks;
144 _Guarded_by_(m_lock) std::vector<std::shared_ptr<VolumeCallback>> m_volumeCallbacks;
145 _Guarded_by_(m_lock) std::vector<std::shared_ptr<ContainerCreateCallbackEntry>> m_containerCreateCallbacks;
146
147 // Invokes a snapshot of callbacks taken under m_lock, skipping registrations that have since been unregistered.
148 template <typename TCallback, typename TInvoke>
149 static void InvokeCallbacks(const std::vector<std::shared_ptr<TCallback>>& Callbacks, const TInvoke& Invoke)
150 {
151 for (const auto& e : Callbacks)
152 {
153 std::lock_guard invokeLock{e->InvokeLock};
154 if (!e->Unregistered)
155 {
156 Invoke(*e);
157 }
158 }
159 }
160
161 WSLCSession& m_session;
162 std::mutex m_lock;
163 std::atomic<size_t> m_callbackId{0};
164 };
165 } // namespace wsl::windows::service::wslc