aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil/consul/consul.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-01-19 13:14:42 +0100
committerGitHub Enterprise <[email protected]>2026-01-19 13:14:42 +0100
commit5081a33dcd19c5d6f9d9cdae9f8745ba786374d4 (patch)
tree6733e6f69137afc690a8c6dcdf618e69ce8029c5 /src/zenutil/consul/consul.cpp
parentMerge pull request #701 from ue-foundation/lm/mac-daemon-mode (diff)
downloadzen-5081a33dcd19c5d6f9d9cdae9f8745ba786374d4.tar.xz
zen-5081a33dcd19c5d6f9d9cdae9f8745ba786374d4.zip
consul package and basic client added (#716)
* this adds a consul package which can be used to fetch a consul binary * it also adds a `ConsulProcess` helper which can be used to spawn and manage a consul service instance * zencore dependencies brought across: - `except_fmt.h` for easer generation of formatted exception messages - `process.h/cpp` changes (adds `Kill` operation and process group support on Windows) - `string.h` changes to allow generic use of `WideToUtf8()`
Diffstat (limited to 'src/zenutil/consul/consul.cpp')
-rw-r--r--src/zenutil/consul/consul.cpp140
1 files changed, 140 insertions, 0 deletions
diff --git a/src/zenutil/consul/consul.cpp b/src/zenutil/consul/consul.cpp
new file mode 100644
index 000000000..6ddebf97a
--- /dev/null
+++ b/src/zenutil/consul/consul.cpp
@@ -0,0 +1,140 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zenutil/consul.h>
+
+#include <zencore/except_fmt.h>
+#include <zencore/fmtutils.h>
+#include <zencore/logging.h>
+#include <zencore/process.h>
+#include <zencore/string.h>
+#include <zencore/timer.h>
+
+#include <fmt/format.h>
+
+namespace zen::consul {
+
+//////////////////////////////////////////////////////////////////////////
+
+struct ConsulProcess::Impl
+{
+ Impl(std::string_view BaseUri) : m_HttpClient(BaseUri) {}
+ ~Impl() = default;
+
+ void SpawnConsulAgent()
+ {
+ if (m_ProcessHandle.IsValid())
+ {
+ return;
+ }
+
+ CreateProcOptions Options;
+ Options.Flags |= CreateProcOptions::Flag_Windows_NewProcessGroup;
+
+ CreateProcResult Result = CreateProc("consul" ZEN_EXE_SUFFIX_LITERAL, "consul" ZEN_EXE_SUFFIX_LITERAL " agent -dev", Options);
+
+ if (Result)
+ {
+ m_ProcessHandle.Initialize(Result);
+
+ Stopwatch Timer;
+
+ // Poll to check when the agent is ready
+
+ do
+ {
+ Sleep(100);
+ HttpClient::Response Resp = m_HttpClient.Get("v1/status/leader");
+ if (Resp)
+ {
+ ZEN_INFO("Consul agent started successfully (waited {})", NiceTimeSpanMs(Timer.GetElapsedTimeMs()));
+
+ return;
+ }
+ } while (Timer.GetElapsedTimeMs() < 10000);
+ }
+
+ // Report failure!
+
+ ZEN_WARN("Consul agent failed to start within timeout period");
+ }
+
+ void StopConsulAgent()
+ {
+ if (!m_ProcessHandle.IsValid())
+ {
+ return;
+ }
+
+ // This waits for the process to exit and also resets the handle
+ m_ProcessHandle.Kill();
+ }
+
+private:
+ ProcessHandle m_ProcessHandle;
+ HttpClient m_HttpClient;
+};
+
+ConsulProcess::ConsulProcess() : m_Impl(std::make_unique<Impl>("http://localhost:8500/"))
+{
+}
+
+ConsulProcess::~ConsulProcess()
+{
+}
+
+void
+ConsulProcess::SpawnConsulAgent()
+{
+ m_Impl->SpawnConsulAgent();
+}
+
+void
+ConsulProcess::StopConsulAgent()
+{
+ m_Impl->StopConsulAgent();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+ConsulClient::ConsulClient(std::string_view BaseUri) : m_HttpClient(BaseUri)
+{
+}
+
+ConsulClient::~ConsulClient()
+{
+}
+
+void
+ConsulClient::SetKeyValue(std::string_view Key, std::string_view Value)
+{
+ IoBuffer ValueBuffer = IoBufferBuilder::MakeFromMemory(MakeMemoryView(Value));
+ HttpClient::Response Result =
+ m_HttpClient.Put(fmt::format("v1/kv/{}", Key), ValueBuffer, {{"Content-Type", "text/plain"}, {"Accept", "application/json"}});
+ if (!Result)
+ {
+ throw runtime_error("ConsulClient::SetKeyValue() failed to set key '{}' ({})", Key, Result.ErrorMessage(""));
+ }
+}
+
+std::string
+ConsulClient::GetKeyValue(std::string_view Key)
+{
+ HttpClient::Response Result = m_HttpClient.Get(fmt::format("v1/kv/{}?raw", Key));
+ if (!Result)
+ {
+ throw runtime_error("ConsulClient::GetKeyValue() failed to get key '{}' ({})", Key, Result.ErrorMessage(""));
+ }
+ return Result.ToText();
+}
+
+void
+ConsulClient::DeleteKey(std::string_view Key)
+{
+ HttpClient::Response Result = m_HttpClient.Delete(fmt::format("v1/kv/{}", Key));
+ if (!Result)
+ {
+ throw runtime_error("ConsulClient::DeleteKey() failed to delete key '{}' ({})", Key, Result.ErrorMessage(""));
+ }
+}
+
+} // namespace zen::consul