Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[dnf5] config-manager plugin #907

Merged
merged 3 commits into from
Nov 13, 2023

Conversation

jrohel
Copy link
Contributor

@jrohel jrohel commented Sep 21, 2023

CI tests rpm-software-management/ci-dnf-stack#1402

Manage libdnf5 configuration.

Subcommands:

  • addrepo - Add repositories from the specified configuration file or define a new repository using user options
  • setopt - Set configuration and repositories options
  • unsetopt - Uset/remove configuration and repositories options
  • setvar - Set variables
  • unsetvar - Unset/remove variables

Note:
Main configuration:
libdnf5 reads the distribution configuration from the drop-in directory
("/usr/share/dnf5/libdnf.conf.d") and system configuration from drop-in
directory ("/etc/dnf/libdnf5.conf.d"). Last, it loads the system configuration
file (by default "/etc/dnf/dnf.conf"). The latter has the highest priority
and is modified by config-manager.

Repository configuration:
Libdnf5 loads the repositories configuration and then loads the configuration
overrides. Configuration overrides are stored in files in
the "/usr/share/dnf5/repos.override.d" and "/etc/dnf/repos.override.d"
directories. The files are sorted alphabetically. The override from the next
file overrides the previous one - the last override value wins.
The config-manager writes the repositories configuration changes to the file
"/etc/dnf/repos.override.d/99-config-manager.repo".

@jrohel
Copy link
Contributor Author

jrohel commented Oct 13, 2023

I put back the work in progress. I am thinking about solving a corner case.

@j-mracek
Copy link
Contributor

@jrohel May I ask you to enable CI tests or to create new test cases for config-manager?


// Computes CRC32 checksum of input string.
// Slow bitwise implementation. Used to calculate the checksum of the omitted part of long URLs.
uint32_t crc32(std::string_view input) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a question - would be possible to replace that code by something from a standard library?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. I'm wondering what library to use so I don't introduce more dependencies.

Copy link
Member

Choose a reason for hiding this comment

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

If I understand correctly, this is used in a specific use case only for generating shorter URLs. Why not utilize a hash function (like SHA1, etc.) from the existing dependency libcrypto and extract a substring of the appropriate length from the result?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jan-kolarik I don't currently know why, but there is indeed a dependency on libcrypto in "dnf5.spec". There is a line: BuildRequires: pkgconfig(libcrypto).
Are we really using libcrypto in libdnf5? Yes libcurl which we depend on is compiled against libcrypto in Fedora. But libcurl also supports other crypto libraries.

Copy link
Member

@jan-kolarik jan-kolarik Nov 1, 2023

Choose a reason for hiding this comment

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

I was curious, so I've queued a testing scratch build without BuildRequires: pkgconfig(libcrypto) dependency and it's still building successfully. And when looking to the optional dependencies not included in this build, like tests and some bindings, it really seems unused in the end.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't use SHA1 for any purpose. It is not available in FIPS mode in RHEL9.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure whether it is a good idea but what about to use SHA2 but from that long hash use only few first characters. I believe that it is available from libsolv API.

Copy link
Contributor

Choose a reason for hiding this comment

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

Well, SHA-256 is also not recommended by NIST now. In general, a choice of hashing algorithm depends on what you want to use the hash for.

If it is for cryptographic purposes, then it you should go with something implemented by a certified library and be prepared to change the algorithm during a life cycle of the code (i.e. always handle hash value and hash algorithm as a pair in your data and code).

If it is for mapping data onto memory buckets, e.g. for implementing associative arrays, then choose an algorithm witch is designed for it (i.e. fair and fast). E.g. MurmurHash. MD5 and SHA-1 are needlessly slow.

If it is for error detection, then CRC can be pretty fine.

For the last to uses, it's unfortunate that they are usually implemented as a copylib library. Not as a standalone, dynamic library. It's similar to Base64.

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe that this part can be used like it is, it could be modify in future because the hash is not something that cannot be changed. I do not consider it as a blocking problem.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yesterday I learnt that zlib has crc32() and crc32_z() in its public API. From zlib.h:

     uLong crc = crc32(0L, Z_NULL, 0);

     while (read_buffer(buffer, length) != EOF) {
       crc = crc32(crc, buffer, length);
     }
     if (crc != original_crc) error();

void add_repo_from_repofile(const std::string & url, const std::filesystem::path & repo_dir);
void create_repo(const std::string & url, const std::filesystem::path & repo_dir);
void test_filepath(const std::filesystem::path & dest_path) const;
void test_if_ids_not_already_exist(
Copy link
Contributor

Choose a reason for hiding this comment

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

May I ask you for a description of the method. The name is quite self descriptive but additional information how arguments are used might be helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added doc string

@j-mracek
Copy link
Contributor

j-mracek commented Oct 31, 2023

I would like to open a discussion related to plural form of sub-commands:
setvars
unsetvars
setopts
unsetopts

If I am not mistaken DNF5 uses singular forms like install, remove even if it is possible to perform multiple operations.

@j-mracek
Copy link
Contributor

j-mracek commented Oct 31, 2023

Problems related to absence of directory

Creating a repository from file in emptuy installroot ends in

filesystem error: cannot create temporary file: No such file or directory [/tmp/testconf/etc/yum.repos.d/rpmsoftwaremanagement-dnf5-deps-fedora-rawhide.repo.DKkNpb]

Setvars:

dnf5 config-manager setvars  acpi=ss --installroot=/tmp/testconf
Cannot write variable to file "/tmp/testconf/etc/dnf/vars/acpi": basic_ios::clear: iostream error
dnf5 config-manager setopts strict=false --installroot=/tmp/testconf2
filesystem error: cannot open file: No such file or directory [/tmp/testconf2/etc/dnf/dnf.conf]

Errors are misleading, because the presence of directory will resolve the issue.

I am suggesting to improve the situation. There are multiple options how to resolve it and the list bellow does not represent all of them.

  1. test whether directory is present first, then use specific exception for that.
  2. Create a directory before the attempt of creation file.

@j-mracek
Copy link
Contributor

This is not a problem, but interesting discovery - Command dnf5 config-manager setopts strict=false --installroot=/tmp/testconf2 creates RPMDB. It is unneeded, but I think there is a low chance that it will create any problem.

@jrohel
Copy link
Contributor Author

jrohel commented Oct 31, 2023

If I am not mistaken DNF5 uses singular forms like install, remove even if it is possible to perform multiple operations.

Comparing setopts and install? From my point of view, this is an inappropriate comparison. install is like set. setvars and setopts are like installpackages (which is not used in dnf).

@j-mracek
Copy link
Contributor

j-mracek commented Nov 1, 2023

If I am not mistaken DNF5 uses singular forms like install, remove even if it is possible to perform multiple operations.

Comparing setopts and install? From my point of view, this is an inappropriate comparison. install is like set. setvars and setopts are like installpackages (which is not used in dnf).

I would like to double-check that proposed names are the best option. After release it is difficult to modify.

@pkratoch
Copy link
Contributor

pkratoch commented Nov 1, 2023

I am for singular form (i.e. setopt etc.).

  1. There is also subcommand addrepo (and not addrepos).
  2. It will be less confusing with the options --setopt and --setvar that already exist.
  3. When we discussed the naming previously, the singular form didn't seem unnatural. It's only now that it's put in comparison.

@jan-kolarik
Copy link
Member

I am also for singular forms. I like keeping the consistency with already existing names in DNF4/DNF5 for the same or very similar behavior. I believe it will also improve the transition for users to DNF5.

@jrohel jrohel force-pushed the dnf5/config_manager branch 2 times, most recently from 89e7f0d to 87cbea0 Compare November 1, 2023 12:37
@jrohel
Copy link
Contributor Author

jrohel commented Nov 1, 2023

  • I rebased the PR.
  • I have made changes to reflect the comments.
  • I also added support for the new --create-missing-dirs option. Defines how the plugin should behave if the target directory does not exist. It either prints an error message about it or creates the missing directories.

It remains to solve the plural/singular form of sub-commands.
And do CI tests and documentation.

@jrohel
Copy link
Contributor Author

jrohel commented Nov 2, 2023

Plural/singular - current state:
Runnings variables: dnf5 --setvar=var1=value --setvar=var2=value --setvar=var3=value ...
Configure variables: dnf5 config-manager setvars var1=value var2=value var3=value ...
dnf5 config-manager unsetvars var1 var2 var3 ...

Runnings options: dnf5 --setopt=[repo.]opt1=value --setopt=[repo.]opt2=value --setopt=[repo.]opt3=value ...
Configure options: dnf5 config-manager setopts [repo.]opt1=value [repo.]opt2=value [repo.]opt3=value ...
dnf5 config-manager unsetopts [repo.]opt1 [repo.]opt2 [repo.]opt3 ...

Enabled running repositories: dnf5 --repo=repo1,repo2,...

  1. Add one new repository from user options: dnf5 config-manager addrepo [id=repoid] --set=repo_opt1=value --set=repo_opt2=value ...
  2. Add one repository config file (can contain more repositories): dnf5 config-manager addrepo --from-repofile=<URL|filepath>
    Maybe different commands can be implemented for these two cases.

@ppisar
Copy link
Contributor

ppisar commented Nov 2, 2023

I'm almost indifferent. I had to choose I would use a singular form. Actually if this pull request had a manual page, it would be easier to consider this question from a usability point of view.

@jrohel
Copy link
Contributor Author

jrohel commented Nov 2, 2023

The singular form was chosen. I updated the PR.

// Checks if the `path` directory exists. If not, according to the `create_missing_dirs` argument,
// the directories (missing paths elements) are created or `ConfigManagerError` exception is thrown.
inline void resolve_missing_dir(const std::filesystem::path & path, bool create_missing_dirs) {
if (!std::filesystem::exists(path)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is real nitpicking - if the path is a file, the test pass because the path exists.

$ mkdir /tmp/config-manager/etc/ -p
$ touch /tmp/config-manager/etc/dnf
$ dnf5 --installroot=/tmp/config-manager config-manager setopt gpgcheck=false

There is std::filesystem::is_directory method. But if the path is a symlink to an existing directory then it will not work according to expectation - pass.

In other cases it works perfectly.

Manage libdnf5 configuration.

Subcommands:
addrepo      Add repositories from the specified configuration file
             or create a new repository using user options
setopt       Set configuration and repositories options
unsetopt     Uset/remove configuration and repositories options
setvar       Set variables
unsetvar     Unset/remove variables

Note:
Main configuration:
libdnf5 reads the distribution configuration from the drop-in directory
("/usr/share/dnf5/libdnf.conf.d") and system configuration from drop-in
directory ("/etc/dnf/libdnf5.conf.d"). Last, it loads the system
configuration file (by default "/etc/dnf/dnf.conf"). The latter has
the highest priority and is modified by config-manager.

Repository configuration:
Libdnf5 loads the repositories configuration and then loads the configuration
overrides. Configuration overrides are stored in files in
the "/usr/share/dnf5/repos.override.d" and "/etc/dnf/repos.override.d"
directories. The files are sorted alphabetically. The override from the next
file overrides the previous one - the last override value wins.
The config-manager writes the repositories configuration changes to the file
"/etc/dnf/repos.override.d/99-config-manager.repo".
New option:
--create-missing-dir - Allow to create missing directories

Without this option, an exception is thrown if the directory is missing.
What happens when the destination repository configuration file already
exists?
By default throw an error.

--overwrite  - Allow overwriting of existing repository configuration file

--add-or-replace - Allow adding or replacing a repository in the existing
                   configuration file
@jrohel
Copy link
Contributor Author

jrohel commented Nov 7, 2023

  • rebased the PR
  • fixed saving the repository override configuration in the installation root different from "/".
  • setopt does not use RepoQuery to be consistent with the implementation of other subcommands
  • if the path exists, the resolve_missing_dir function tests whether it is a directory or a symlink to an existing directory, if not it throws an exception

And I created a lot of CI tests rpm-software-management/ci-dnf-stack#1402.

parser.add_section(repo_id);

// Sets the default repository name. May be overwritten with "--set=name=<name>".
parser.set_value(repo_id, "name", "created by dnf5 config-manager");
Copy link
Contributor

Choose a reason for hiding this comment

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

What about to include repoid in the name? Proposing something like <RepoID> created by dnf5 config-manager. Keeping like it is is also OK, therefore the change is optional.

@j-mracek
Copy link
Contributor

I would also suggest to add a warning when user want to unset a value but value is not in configuration file. Unset of option for repository should be explicitly documented in man pages, because unset only removes overrides but does not touch the original repo file.

Copy link
Contributor

@j-mracek j-mracek left a comment

Choose a reason for hiding this comment

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

Remaining suggestions or comments are not blocking the merge and could be addressed in the next PR if they are considered as something to worth to implement The PR was tested with provided test cases.

@j-mracek j-mracek added this pull request to the merge queue Nov 13, 2023
Merged via the queue into rpm-software-management:main with commit da5b114 Nov 13, 2023
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

DNF config-manager Plugin
5 participants