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

Repository class refactoring#95

Merged
dhilst merged 31 commits intomasterfrom
repo-class
Jul 2, 2025
Merged

Repository class refactoring#95
dhilst merged 31 commits intomasterfrom
repo-class

Conversation

@arthurmco
Copy link
Copy Markdown
Contributor

@arthurmco arthurmco commented Jan 27, 2025

This is a HUGE refactoring adding some structure to the code base (namespace and folders) and removing old RepoManager code

  • Generalized RepoManager over the repository type and the runner,
  • Removed RepoManger and Runner from cluster
  • Replace the old implementation of RepoManager with the new implementation
  • Remove the dependency Runner from Cluster, if something is depending on the cluster's runner to run commands we'll find out, this something should be running commands on its own
  • Added getRepoManager and getRunner for retrieving the respective singletons
  • I added namespaces cloyster::models and cloyster::services to start splitting the code according to the model-view-presenter architecture
  • I moved the files so they correspond to their namespaces
  • Refactored RepoManager to use glibmm KeyFile for parsing
  • Refactored RepoManager public API
  • Refactored RepoManager implementation
  • Removed XCAT code from repo manager
  • Added KeyFile implementation hiding glibmm KeyFile (files.h/files.cpp)
  • Added concepts.h header for general concepts
  • Standarise dry run logging messages
  • Isolate presenter and viewer in its own cmake target
  • Add CATTUS_SKIP_DISK_CHECKSUM environment to skip checksum of the ISO
  • Add --test-conf-file for testing KeyFile implementation against arbitrary files (it loads and prints the file)

@arthurmco arthurmco requested a review from dhilst January 27, 2025 14:11
@arthurmco arthurmco self-assigned this Jan 27, 2025
Comment thread src/services/repo.cpp Outdated
Comment thread src/services/repo.cpp Outdated

void ELRepoFile::read()
{
printf("reading\n");
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No...

Comment thread src/services/repo.cpp Outdated
Comment thread include/cloysterhpc/services/repo.h Outdated
Comment thread include/cloysterhpc/services/repo.h Outdated
Comment thread src/services/repo.cpp Outdated
m_repositories.push_back(std::move(repo));
}

for (const auto& r : m_repositories) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would avoid using only r as variable name. Usually we use it for well defined counters, like: i, j and k. Or for iterators, like it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only code for toying with glibmm

In real life, this would not be like this

(This comment applies to every printf/fmt::format comment above)

@viniciusferrao
Copy link
Copy Markdown
Owner

Refactor the repository class based on @dhilst 's modeling

The modeling was done by the three of us. You included!

Comment thread include/cloysterhpc/services/repo.h Outdated
Comment thread include/cloysterhpc/services/repo.h Outdated
Comment on lines +95 to +107
class ELRepoFile : public RepoFile<ELRepo> {
private:
Glib::RefPtr<Glib::KeyFile> loadFile();

public:
ELRepoFile(const std::filesystem::path& path)
: RepoFile(path)
{
}

void read() override;
void write() override;
};
Copy link
Copy Markdown
Collaborator

@dhilst dhilst Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have an abstract RepoFile which extends the File, adds the m_repositories generic member, and with parse unparse methods, read is super read followed by parse and write is unparse followed by super write

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::filesystem::path is convertible to std::string or const char*. We must use it. It's the right API.

However the file class that be dependent on std::filesystem::path and not the derived one.

Copy link
Copy Markdown
Collaborator

@dhilst dhilst Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be clear

  • parse loads data from the file into the m_repositories member
  • unparse loads the m_repositories member into the file

Also, @arthurmco, add overloads to parse and unparse which I/O into istringstream ostringstream instead of directly into the file so we can test without touching the disks. I don't know if this is possible because I don't know the interface of glibmm if it can load/unload keyfiles from strings, can it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is possible; glibmm can load keyhole data from a string.

Comment thread include/cloysterhpc/services/repo.h Outdated
Comment thread src/services/repo.cpp Outdated
Comment thread src/services/repo.cpp Outdated
Comment on lines +81 to +86
std::vector<ELRepo> ELRepoFile::parse(const std::stringstream& ss)
{
m_file = Glib::KeyFile::create();
m_file->load_from_data(ss.str().c_str());
return this->parseData();
}
Copy link
Copy Markdown
Collaborator

@dhilst dhilst Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, now

  • Add m_repositories member as a vector of ELRepo with a getter
  • The getter should load the data into the member if the member is empty and return at the end

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside of ELRepo?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside ELRepoFile

Comment thread include/cloysterhpc/services/repo.h Outdated
struct ELRepo {
std::string group;
std::string name;
std::optional<std::string> baseURL;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

base_url

Comment thread include/cloysterhpc/services/repo.h Outdated
Comment thread src/services/repo.cpp Outdated
Comment on lines +95 to +100
auto get_optional_string
= [this](auto group, auto key) -> std::optional<Glib::ustring> {
return m_file->has_key(group, key)
? std::make_optional(m_file->get_string(group, key))
: std::nullopt;
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this captures this, anyway, move it to functions.cpp

dhilst
dhilst previously requested changes Jan 31, 2025
Copy link
Copy Markdown
Collaborator

@dhilst dhilst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API looks good, there are a few points

  • Make sure to remove the prints
  • Rename the ELReo to ELCloneRepo
  • Remove the PR from draft

Copy link
Copy Markdown
Owner

@viniciusferrao viniciusferrao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another issue that I've observed. The virtual classes should be on its own files. This needs to be fixed.

Comment thread src/services/repo.cpp Outdated

std::vector<ELRepo> ELRepoFile::parse()
{
this->read();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need "this"?

Comment thread src/services/repo.cpp Outdated
Comment thread include/cloysterhpc/functions.h Outdated
void copyFile(std::filesystem::path source, std::filesystem::path destination);

std::optional<Glib::ustring> readKeyfileString(Glib::RefPtr<Glib::KeyFile> file,
const std::string_view& group, const std::string_view& key);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std:string_view is already a pointer to a string. It should not be passed as const ref.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know that. I thought it was pointer+length. It will be fixed

Comment thread include/cloysterhpc/services/repo.h Outdated
Comment thread src/functions.cpp Outdated
}

std::optional<Glib::ustring> readKeyfileString(Glib::RefPtr<Glib::KeyFile> file,
const std::string_view& group, const std::string_view& key)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to be const ref.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arthurmco arthurmco requested a review from dhilst February 4, 2025 13:44
@arthurmco arthurmco marked this pull request as ready for review February 4, 2025 13:45
Copy link
Copy Markdown
Owner

@viniciusferrao viniciusferrao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up adding a lot of comments.

I think we should think a little bit more on this implementation.

Comment thread include/cloysterhpc/file.h Outdated
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class name must match de filename. And also the #ifndef guards.

I would recommend using a namespace to avoid clashing.

Comment thread include/cloysterhpc/file.h Outdated
Comment on lines +26 to +27
virtual void read() { }
virtual void write() { }
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be = 0? I can't find the implementation in other files, only the specialization. It should be pure virtual.

Comment thread include/cloysterhpc/file.h Outdated
{
}

virtual void read() { }
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think read will not modify the state of the object right? Should it be const?

Comment thread include/cloysterhpc/file.h Outdated
Comment thread include/cloysterhpc/file.h Outdated
virtual void read() { }
virtual void write() { }

virtual ~GenericFile() = default;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you defaulted a destructor, AFAIK, the move operations are by default deleted. I think we should define the move operations:

GenericFile(GenericFile&&) = default;
GenericFile& operator=(GenericFile&&) = default;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

Comment thread src/services/repofile.cpp Outdated
Comment thread src/services/repofile.cpp Outdated
repo.enabled = enabled;
repo.gpgcheck = gpgcheck;
repo.gpgkey = gpgkey;
repositories.push_back(std::move(repo));
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emplace_back.

Comment thread src/services/repofile.cpp Outdated
Comment thread src/services/repofile.cpp Outdated
}

[[nodiscard]] const std::vector<ELCloneRepo>&
ELRepoFile::getRepositoriesConst() const
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the qualifier in the method name is a very bad approach.

Comment thread src/services/repofile.cpp Outdated
@dhilst dhilst assigned dhilst and unassigned arthurmco Feb 7, 2025
@dhilst dhilst marked this pull request as ready for review February 19, 2025 20:21
@dhilst dhilst changed the title WIP: Repository class refactoring Repository class refactoring Feb 19, 2025
@dhilst dhilst force-pushed the repo-class branch 2 times, most recently from 9d0b02b to ecb7a02 Compare February 25, 2025 00:45
Copy link
Copy Markdown
Owner

@viniciusferrao viniciusferrao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a big one. There's some comments that were probably not from this PR originally, we should mark those as a BUG to be fixed later.

For everything else can you evaluate @dhilst? Don't be economic when naming things, you can abuse it. Avoids guessing. The compiler will optimize anyway.

Comment on lines +1 to +2
#ifndef CLOYSTER_CONCEPTS_H
#define CLOYSTER_CONCEPTS_H
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#ifndef CLOYSTER_CONCEPTS_H
#define CLOYSTER_CONCEPTS_H
/*
* Copyright 2025 Vinícius Ferrão <vinicius@ferrao.net.br>
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef CLOYSTERHPC_CONCEPTS_H_
#define CLOYSTERHPC_CONCEPTS_H_

* @brief IsParser<P, T> means: P can parse and unparse Ts from streams. The
* parsers are allowed to throw exceptions
*/
template <typename Parser_, typename I, typename O>
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I is input and O is output? If yes I think we should use more descriptive names.

* The parsers are not allowed to throw exceptions, it must return errors of
* type E
*/
template <typename Parser_, typename T, typename E>
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is T and E?

Also the naming with _ at the end, is that a convention?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this concept, anyway T is the parsed type and E the error type


}

#endif // CLOYSTER_CONCEPTS_H
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#endif // CLOYSTER_CONCEPTS_H
#endif // CLOYSTERHPC_CONCEPTS_H_

Comment on lines +32 to 34
{ "AlmaLinux-8.8-x86_64-dvd.iso", "OracleLinux-R8-U8-x86_64-dvd.iso",
"rhel-8.8-x86_64-dvd.iso", "Rocky-8.8-x86_64-dvd1.iso",
"Rocky-9.5-x86_64-dvd.iso" }) };
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be better organized or generated since it's a pattern and not hardcoded like that. I know it's a problem from legacy code. But maybe this should be removed.

Comment thread src/services/repos.cpp Outdated
Comment thread src/services/repos.cpp Outdated
case OS::Platform::el8:
case OS::Platform::el9:
case OS::Platform::el10:
return "/etc/yum.repos.d/cloyster.repo";
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will explicit return as const char*. Should we force it to return an std::string?

Comment thread src/services/repos.cpp Outdated
std::vector<std::string> dependencies;

std::size_t version = osinfo.getMajorVersion();
std::string powertools = "powertools";
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That logic is not good at all.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

I didn't get it, how exactly it is not good?

Comment thread src/services/repos.cpp Outdated
void RepoManager::enable(const std::string& repoid)
{
if (cloyster::dryRun) {
LOG_WARN("Dry Run: Would enable repository {}", repoid);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should all dry run statements be LOG_INFO instead? It's not a warning.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because I standardized all the dry-run logging messages to be warning and have Dry Run: as the prefix, for the sake of consistency

Comment thread src/services/shell.cpp Outdated
Comment on lines +39 to +41
return std::vector<std::string>(
{ "-beegfs", "-elrepo", "-epel", "-openhpc",
"-openhpc-updates", "-rpmfusion-free-updates" })
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's temporary?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not. These are the repositories enabled by default in EL distros, it comes from old/refactored code. Is it wrong?

* Add script to build rpms

* Fix debug build

* Link against host Glibmm using pkg-config

* Extract glibmm from diskImage, move it to files.cpp

---------

Co-authored-by: Daniel Hilst <daniel@versatushpc.com.br>
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
20 Security Hotspots

See analysis details on SonarQube Cloud

@dhilst
Copy link
Copy Markdown
Collaborator

dhilst commented Mar 18, 2025

@viniciusferrao Changing the smart pointers in the postfix (for the client bus) requires refactoring and changing the IService interface. Replacing shared with unique pointers in the presenter requires a major refactoring too because these classes should not have ownership of these objects

I will address these problems in another PR

* Adding OFED option and Cluster singleton with global getter

* Add singleton class

* Use Singleton<T> class for other singletons

* Make all Dry Run message LOG_INFO

* Remove print and test option

* Implement DOCA kernel driver installation into the netboot image

* Fix OFED installation in headnode

* Fix review comments

* Generalize testing and fix createHTTPRepo function

* Remove CATTUS_... environment variables in favor of --force + --skip flags

* Rename BaseRunner to IRunner for consistency

* Wrap magic_enum lib

* Remove package_manager in favor of IOSService interface

* Deduplicate code

* Run clang-format

* Remove hwinfo, cloyster/hardware.h and tests

The tests are breaking because of the refactoring, I'll
redo the tests after I finish it

* Remove inifile in favor of KeyFile

* Removing old repository code

* Add comments and FIXME

* Use boost to parse options

* Run clang-format

* Decouple message bus from implementors

* Fix test command handling

* Fix warning in options.h

* Restores runtime behavior after refactoring (WIP)

* Replace boost::program_options with CLI11

* Implement repository sanity checking logic in repos.cpp

* WIP generalizing repository addition

* Restore testing code infra

* Add test on the CI

* Move singletons initalization to its own file

* [WIP] Add libbacktrace to the dependencies

* Decouple I/O from repository config classes to make them testable

* Remove stacktrace attempt

There is no libstdc++exp.a in Rocklinux repositories, stacktrace
requires it.

* Continuing the repositories core logic implementation

* Remove old repository installation for xCAT and OFED

* Add test files

* Add ScriptBuilder service

* Update NFS with script builder

* Refactor NFS to use ScriptBuilder for installation and image customization.

* WIP Fixing repositories bugs after the revamp

* Fixing image generation and bugs after revamp

* Fix the tests

---------

Co-authored-by: Daniel Hilst <daniel@versatushpc.com.br>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jul 2, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
22 Security Hotspots

See analysis details on SonarQube Cloud

@dhilst dhilst dismissed stale reviews from viniciusferrao and themself July 2, 2025 15:17

This and #95 were big refactoring, a new RPM was generated and sent for acceptance testing, there is no point into review currently as it is now

@dhilst dhilst merged commit 163866b into master Jul 2, 2025
1 of 6 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants