Skip to content
This repository was archived by the owner on Apr 16, 2026. It is now read-only.

Commit b5409cd

Browse files
committed
Add singleton class
1 parent 7fc93c9 commit b5409cd

5 files changed

Lines changed: 93 additions & 58 deletions

File tree

include/cloysterhpc/functions.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,43 @@
1616
#include <type_traits>
1717

1818
namespace cloyster {
19+
20+
template <typename T>
21+
class Singleton {
22+
// Private constructor to prevent direct instantiation
23+
Singleton() = default;
24+
25+
// Static members for singleton management
26+
static std::unique_ptr<T> instance;
27+
static std::once_flag initFlag;
28+
public:
29+
Singleton(Singleton&&) = delete;
30+
Singleton& operator=(Singleton&&) = delete;
31+
Singleton(const Singleton&) = delete;
32+
Singleton& operator=(const Singleton&) = delete;
33+
~Singleton() = delete;
34+
35+
static void init(std::unique_ptr<T> value) {
36+
std::call_once(initFlag, [&](){
37+
instance = std::move(value);
38+
});
39+
}
40+
41+
static gsl::not_null<T*> get() {
42+
if (!instance) {
43+
throw std::runtime_error("Singleton read before initialization");
44+
}
45+
return gsl::not_null<T*>(instance.get());
46+
}
47+
};
48+
1949
// Globals
2050
extern bool dryRun;
2151

2252
using OS = cloyster::models::OS;
2353

2454
void initClusterSingleton(std::unique_ptr<models::Cluster> cluster);
25-
models::Cluster& getClusterSingleton();
55+
gsl::not_null<models::Cluster*> getClusterSingleton();
2656
std::shared_ptr<cloyster::services::BaseRunner> getRunner();
2757
std::shared_ptr<cloyster::services::repos::RepoManager> getRepoManager(
2858
const OS& osinfo);

src/functions.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ using cloyster::services::BaseRunner;
3030
using cloyster::services::DryRunner;
3131
using cloyster::services::Runner;
3232

33+
34+
template <typename T>
35+
std::unique_ptr<T> Singleton<T>::instance = nullptr;
36+
37+
template <typename T>
38+
std::once_flag Singleton<T>::initFlag;
39+
3340
namespace {
3441
std::tuple<bool, std::optional<std::string>> retrieveLine(
3542
boost::process::ipstream& pipe_stream,
@@ -69,14 +76,12 @@ using cloyster::services::repos::RepoManager;
6976
static std::unique_ptr<Cluster> clusterSingleton;
7077
void initClusterSingleton(std::unique_ptr<Cluster> cluster)
7178
{
72-
assert(!clusterSingleton);
73-
clusterSingleton = std::move(cluster);
79+
Singleton<Cluster>::init(std::move(cluster));
7480
}
7581

76-
Cluster& getClusterSingleton()
82+
gsl::not_null<Cluster*> getClusterSingleton()
7783
{
78-
assert(clusterSingleton);
79-
return *clusterSingleton;
84+
return Singleton<Cluster>::get();
8085
}
8186

8287
std::shared_ptr<RepoManager> getRepoManager(const OS& osinfo)

src/ofed.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ gpgkey=https://linux.mellanox.com/public/repo/doca/{0}/GPG-KEY-Mellanox
2929
// https://linux.mellanox.com/public/repo/doca/latest/
3030
std::string headnodeDistroName()
3131
{
32-
const auto& cluster = cloyster::getClusterSingleton();
33-
switch (cluster.getHeadnode().getOS().getDistro()) {
32+
const auto cluster = cloyster::getClusterSingleton();
33+
switch (cluster->getHeadnode().getOS().getDistro()) {
3434
// Assuming we'll be using the last distro version
3535
case cloyster::OS::Distro::RHEL:
3636
return "rhel9.5";

src/services/shell.cpp

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ auto getToEnableRepoNames(const OS& osinfo)
5151
}
5252
}
5353

54-
Cluster& cluster() { return getClusterSingleton(); }
54+
auto cluster() { return getClusterSingleton(); }
5555
}
5656

5757
namespace cloyster::services {
@@ -81,7 +81,7 @@ void Shell::configureSELinuxMode()
8181
{
8282
LOG_INFO("Setting up SELinux")
8383

84-
switch (cluster().getSELinux()) {
84+
switch (cluster()->getSELinux()) {
8585
case Cluster::SELinuxMode::Permissive:
8686
runCommand("setenforce 0");
8787
/* Permissive mode */
@@ -106,22 +106,22 @@ void Shell::configureFirewall()
106106
{
107107
LOG_INFO("Setting up firewall")
108108

109-
if (cluster().isFirewall()) {
109+
if (cluster()->isFirewall()) {
110110
runCommand("systemctl enable --now firewalld");
111111

112112
// Add the management interface as trusted
113113
runCommand(fmt::format(
114114
"firewall-cmd --permanent --zone=trusted --change-interface={}",
115-
cluster().getHeadnode()
115+
cluster()->getHeadnode()
116116
.getConnection(Network::Profile::Management)
117117
.getInterface()
118118
.value()));
119119

120120
// If we have IB, also add its interface as trusted
121-
if (cluster().getOFED())
121+
if (cluster()->getOFED())
122122
runCommand(fmt::format(
123123
"firewall-cmd --permanent --zone=trusted --change-interface={}",
124-
cluster().getHeadnode()
124+
cluster()->getHeadnode()
125125
.getConnection(Network::Profile::Application)
126126
.getInterface()
127127
.value()));
@@ -139,15 +139,15 @@ void Shell::configureFQDN()
139139
LOG_INFO("Setting up hostname")
140140

141141
runCommand(fmt::format(
142-
"hostnamectl set-hostname {}", cluster().getHeadnode().getFQDN()));
142+
"hostnamectl set-hostname {}", cluster()->getHeadnode().getFQDN()));
143143
}
144144

145145
// TODO: Proper file parsing
146146
void Shell::configureHostsFile()
147147
{
148148
LOG_INFO("Setting up additional entries on hosts file")
149149

150-
const auto& headnode = cluster().getHeadnode();
150+
const auto& headnode = cluster()->getHeadnode();
151151

152152
const auto& ip = headnode.getConnection(Network::Profile::Management)
153153
.getAddress()
@@ -167,15 +167,15 @@ void Shell::configureTimezone()
167167
LOG_INFO("Setting up timezone")
168168

169169
runCommand(fmt::format(
170-
"timedatectl set-timezone {}", cluster().getTimezone().getTimezone()));
170+
"timedatectl set-timezone {}", cluster()->getTimezone().getTimezone()));
171171
}
172172

173173
void Shell::configureLocale()
174174
{
175175
LOG_INFO("Setting up locale")
176176

177177
runCommand(fmt::format(
178-
"localectl set-locale {}", cluster().getLocale().getLocale()));
178+
"localectl set-locale {}", cluster()->getLocale().getLocale()));
179179
}
180180

181181
void Shell::disableNetworkManagerDNSOverride()
@@ -273,7 +273,7 @@ void Shell::configureNetworks(const std::list<Connection>& connections)
273273

274274
void Shell::runSystemUpdate()
275275
{
276-
if (cluster().isUpdateSystem()) {
276+
if (cluster()->isUpdateSystem()) {
277277
LOG_INFO("Checking if system updates are available")
278278
runCommand("dnf -y update");
279279
}
@@ -341,7 +341,7 @@ void Shell::configureQueueSystem()
341341
{
342342
LOG_INFO("Setting up the queue system")
343343

344-
if (const auto& queue = cluster().getQueueSystem()) {
344+
if (const auto& queue = cluster()->getQueueSystem()) {
345345
switch (queue.value()->getKind()) {
346346
case QueueSystem::Kind::None: {
347347
__builtin_unreachable();
@@ -377,14 +377,14 @@ void Shell::configureMailSystem()
377377
{
378378
LOG_INFO("Setting up the mail system");
379379

380-
cluster().getMailSystem()->setup();
380+
cluster()->getMailSystem()->setup();
381381
}
382382

383383
void Shell::configureInfiniband()
384384
{
385-
const auto& osinfo = cluster().getHeadnode().getOS();
385+
const auto& osinfo = cluster()->getHeadnode().getOS();
386386
auto repos = cloyster::getRepoManager(osinfo);
387-
if (const auto& ofed = cluster().getOFED()) {
387+
if (const auto& ofed = cluster()->getOFED()) {
388388
LOG_INFO("Setting up Infiniband support")
389389
ofed->install(*repos); // shared pointer
390390
}
@@ -423,7 +423,7 @@ void Shell::installDevelopmentComponents()
423423

424424
void Shell::configureRepositories()
425425
{
426-
const auto& osinfo = cluster().getHeadnode().getOS();
426+
const auto& osinfo = cluster()->getHeadnode().getOS();
427427
auto repos = cloyster::getRepoManager(osinfo);
428428
// 1. Install files into /etc, these files are the templates
429429
// at include/cloysterhpc/repos/el*/*.repo
@@ -439,7 +439,7 @@ void Shell::configureRepositories()
439439
*/
440440
void Shell::install()
441441
{
442-
auto systemdBus = cluster().getDaemonBus();
442+
auto systemdBus = cluster()->getDaemonBus();
443443

444444
configureSELinuxMode();
445445
configureFirewall();
@@ -450,17 +450,17 @@ void Shell::install()
450450
configureTimezone();
451451
configureLocale();
452452

453-
configureNetworks(cluster().getHeadnode().getConnections());
453+
configureNetworks(cluster()->getHeadnode().getConnections());
454454
runSystemUpdate();
455-
configureTimeService(cluster().getHeadnode().getConnections());
455+
configureTimeService(cluster()->getHeadnode().getConnections());
456456
installRequiredPackages();
457457
configureRepositories();
458458
installOpenHPCBase();
459459
configureInfiniband();
460460

461461
// BUG: Broken. Compute nodes does not mount anything.
462462
NFS networkFileSystem = NFS(systemdBus, "pub", "/opt/ohpc",
463-
cluster().getHeadnode()
463+
cluster()->getHeadnode()
464464
.getConnection(Network::Profile::Management)
465465
.getAddress(),
466466
"ro,no_subtree_check");
@@ -469,20 +469,20 @@ void Shell::install()
469469
networkFileSystem.start();
470470

471471
configureQueueSystem();
472-
if (cluster().getMailSystem().has_value()) {
472+
if (cluster()->getMailSystem().has_value()) {
473473
configureMailSystem();
474474
}
475475
removeMemlockLimits();
476476

477477
installDevelopmentComponents();
478478

479479
const auto& provisionerName { magic_enum::enum_name(
480-
cluster().getProvisioner()) };
480+
cluster()->getProvisioner()) };
481481

482482
LOG_DEBUG("Setting up the provisioner: {}", provisionerName)
483483
// std::unique_ptr<Provisioner> provisioner;
484484
std::unique_ptr<XCAT> provisioner;
485-
switch (cluster().getProvisioner()) {
485+
switch (cluster()->getProvisioner()) {
486486
case Cluster::Provisioner::xCAT:
487487
provisioner = std::make_unique<XCAT>();
488488
break;

0 commit comments

Comments
 (0)