Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Repository class refactoring #95

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open

Repository class refactoring #95

wants to merge 30 commits into from

Conversation

arthurmco
Copy link
Collaborator

@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

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

Choose a reason for hiding this comment

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

No...

m_repositories.push_back(std::move(repo));
}

for (const auto& r : m_repositories) {
Copy link
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
Collaborator 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
Owner

Refactor the repository class based on @dhilst 's modeling

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

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
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
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
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
Collaborator 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 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
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
Collaborator 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
Collaborator

Choose a reason for hiding this comment

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

Inside ELRepoFile

struct ELRepo {
std::string group;
std::string name;
std::optional<std::string> baseURL;
Copy link
Collaborator

Choose a reason for hiding this comment

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

base_url

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
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

Copy link
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
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.


std::vector<ELRepo> ELRepoFile::parse()
{
this->read();
Copy link
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"?

@@ -164,6 +166,9 @@ std::string findAndReplace(const std::string_view& source,
*/
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
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
Collaborator 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

@@ -351,4 +351,12 @@ 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
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
Collaborator 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
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.

Copy link
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 on lines 26 to 27
virtual void read() { }
virtual void write() { }
Copy link
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.

{
}

virtual void read() { }
Copy link
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?

virtual void read() { }
virtual void write() { }

virtual ~GenericFile() = default;
Copy link
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
Collaborator

Choose a reason for hiding this comment

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

Good catch

repo.enabled = enabled;
repo.gpgcheck = gpgcheck;
repo.gpgkey = gpgkey;
repositories.push_back(std::move(repo));
Copy link
Owner

Choose a reason for hiding this comment

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

emplace_back.

}

[[nodiscard]] const std::vector<ELCloneRepo>&
ELRepoFile::getRepositoriesConst() const
Copy link
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.

@dhilst dhilst assigned dhilst and unassigned arthurmco Feb 7, 2025
@dhilst dhilst force-pushed the repo-class branch 4 times, most recently from c00412d to 79954f1 Compare February 18, 2025 03:46
@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
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
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
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
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
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
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
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.

case OS::Platform::el8:
case OS::Platform::el9:
case OS::Platform::el10:
return "/etc/yum.repos.d/cloyster.repo";
Copy link
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?

std::vector<std::string> dependencies;

std::size_t version = osinfo.getMajorVersion();
std::string powertools = "powertools";
Copy link
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
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?

void RepoManager::enable(const std::string& repoid)
{
if (cloyster::dryRun) {
LOG_WARN("Dry Run: Would enable repository {}", repoid);
Copy link
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
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 on lines +39 to +41
return std::vector<std::string>(
{ "-beegfs", "-elrepo", "-epel", "-openhpc",
"-openhpc-updates", "-rpmfusion-free-updates" })
Copy link
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
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>
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
20 Security Hotspots

See analysis details on SonarQube Cloud

@dhilst
Copy link
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

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants