From 218a3f571b15a65ee0101a5757a598a6ab6e7e26 Mon Sep 17 00:00:00 2001 From: Abdul Karim Date: Tue, 27 Sep 2022 13:52:21 +0100 Subject: [PATCH] ncm-spma: dnf support for enabling module streams add support for enabling module stream using modulemd-defaults for dnf backend. Similar to yumdnf, but this configures via modules defaults configs using drop-in files in /etc/dnf/modules.defaults.d, includes setting of default profiles. --- .../main/pan/components/spma/dnf/config.pan | 4 +- .../main/pan/components/spma/dnf/schema.pan | 10 +++ ncm-spma/src/main/perl/spma/dnf.pm | 66 +++++++++++++++++++ .../src/main/resources/dnf_module_defaults.tt | 8 +++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 ncm-spma/src/main/resources/dnf_module_defaults.tt diff --git a/ncm-spma/src/main/pan/components/spma/dnf/config.pan b/ncm-spma/src/main/pan/components/spma/dnf/config.pan index 1f20d11713..4c956018e7 100644 --- a/ncm-spma/src/main/pan/components/spma/dnf/config.pan +++ b/ncm-spma/src/main/pan/components/spma/dnf/config.pan @@ -7,8 +7,10 @@ unique template components/spma/dnf/config; prefix '/software'; # Package to install 'packages' = pkg_repl("ncm-${project.artifactId}", "${no-snapshot-version}-${rpm.release}", "noarch"); +# modules can be empty, when nothing is set +"modules" ?= dict(); # Set prefix to root of component configuration. prefix '/software/components/spma'; -'register_change' ?= list("/software/packages", "/software/repositories"); +'register_change' ?= list("/software/packages", "/software/repositories", "/software/modules"); diff --git a/ncm-spma/src/main/pan/components/spma/dnf/schema.pan b/ncm-spma/src/main/pan/components/spma/dnf/schema.pan index cb91b63a5c..c881313e67 100644 --- a/ncm-spma/src/main/pan/components/spma/dnf/schema.pan +++ b/ncm-spma/src/main/pan/components/spma/dnf/schema.pan @@ -22,4 +22,14 @@ type component_spma_dnf = { "run" ? boolean # Run the transaction after configuring DNF }; +@{DNF module structure. To be used as dict, with name as key. + to set default profiles for modules not enabled by default and change existing defaults. +} +type component_spma_dnf_module_defaults = { + 'stream' : string_trimmed + 'profiles' ? string_trimmed[] + @documentation{ defaults to 1 in component, provided in-case it changes in a future release } + 'modulemd_version' : long(1..) = 1 +}; bind "/software/components/spma" = component_spma_dnf; +bind '/software/modules' = component_spma_dnf_module_defaults{}; diff --git a/ncm-spma/src/main/perl/spma/dnf.pm b/ncm-spma/src/main/perl/spma/dnf.pm index 4bbcbcc7f2..e2f9a1483f 100644 --- a/ncm-spma/src/main/perl/spma/dnf.pm +++ b/ncm-spma/src/main/perl/spma/dnf.pm @@ -24,6 +24,8 @@ use POSIX; use constant REPOS_DIR => "/etc/yum.repos.d"; use constant REPOS_TREE => "/software/repositories"; +use constant DNF_MODULES_DIR => "/etc/dnf/modules.defaults.d"; +use constant MODULES_TREE => "/software/modules"; use constant PKGS_TREE => "/software/packages"; use constant CMP_TREE => "/software/components/spma"; use constant DNF_PACKAGE_LIST => "/etc/dnf/plugins/versionlock.list"; @@ -102,6 +104,67 @@ sub get_installed_rpms return Set::Scalar->new(split (/\n/, $cmd_out)); } +# configure default modules via config file +sub generate_dnf_modules { + my ($self, $directory, $modules) = @_; + + + my $changes = 0; + + foreach my $name (sort keys %$modules) { + my $module = $modules->{$name}; + + $module->{name} = $name; + + my $trd = EDG::WP4::CCM::TextRender->new("dnf_module_defaults", $module, relpath => 'spma'); + if (! defined($trd->get_text())) { + $self->error ("Unable to generate module $name: $trd->{fail}"); + return; + }; + + my $fh = $trd->filewriter("$directory/$name.yaml", + header => "# File generated by " . __PACKAGE__ . ". Do not edit", + log => $self); + $changes += $fh->close() || 0; # handle undef + } + + return $changes; +}; + + +# support to override/set default stream in /etc/dnf/modules.defaults.d/ +# directory is here for unittesting +sub set_default_modules { + my ($self, $config, $directory) = @_; + + $directory = DNF_MODULES_DIR if ! defined($directory); + + my $modules = $config->getTree(MODULES_TREE); + + # For easier lookup + my %modulenames; + foreach my $module (sort keys %$modules) { + $modulenames{$module} = $module; + } + # Remove default module definitions not present in the profile + my @module_defaults = glob "$directory/*"; + foreach my $module (@module_defaults) { + my $m_name = basename($module); + $m_name =~ s/\.[^.]+$//; + # only keep managed module files with .yaml extension + next if ($module =~ m/^(.*).yaml$/ && exists $modulenames{$m_name}); + if (!defined($self->cleanup($module))) { + $self->error("Unable to remove file $module: $!"); + return 0; + } + } + + $self->verbose("Managing modules defaults: ", join(",", sort keys %$modules)); + return 0 if ! defined $self->generate_dnf_modules($directory, $modules); + + return 1; +}; + sub Configure { my ($self, $config) = @_; @@ -248,6 +311,8 @@ sub Configure } $fh->close(); } + # set module stream configs + $self->set_default_modules($config) or return 0; # Preprocess required packages and separate version-locked # - also skip package dualities - e.g. kernel and kernel-2.6.32-504.1.3.el6.x86_64 @@ -379,6 +444,7 @@ sub Configure my $dnf_test_chroot = $self->directory("/tmp/spma_dnf_testroot_XXXXXX", temp => 1, keeps_state => 1); $self->directory($dnf_test_chroot . "/var/cache", keeps_state => 1); $self->symlink("/var/cache/dnf", $dnf_test_chroot . "/var/cache/dnf", keeps_state => 1); + $self->symlink("/etc/dnf", $dnf_test_chroot . "/etc/dnf", keeps_state => 1); # Run test transaction to get complete list of packages to be present on the system my $dnf_install_test_command = [DNF_INSTALL, DNF_PLUGIN_OPTS, "--installroot=" . $dnf_test_chroot]; diff --git a/ncm-spma/src/main/resources/dnf_module_defaults.tt b/ncm-spma/src/main/resources/dnf_module_defaults.tt new file mode 100644 index 0000000000..e5b3b1b9b6 --- /dev/null +++ b/ncm-spma/src/main/resources/dnf_module_defaults.tt @@ -0,0 +1,8 @@ +--- +document: modulemd-defaults +version: [% modulemd_version.defined ? modulemd_version : "1" %] +data: + module: [% name %] + stream: "[% stream %]" + profiles: + [% stream %]: [[% profiles.join(",") %]]