From 26906e19f742bc4da01d93f8647805d682aeef36 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Mon, 17 Mar 2025 14:31:40 +0000 Subject: [PATCH 1/6] Symlink the resolv.conf around post scripts run --- service/lib/agama/network.rb | 21 +++++++++++++++++++++ service/lib/agama/storage/finisher.rb | 8 +++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/service/lib/agama/network.rb b/service/lib/agama/network.rb index 972ec5162f..1433d481aa 100644 --- a/service/lib/agama/network.rb +++ b/service/lib/agama/network.rb @@ -46,11 +46,32 @@ def install ProxySetup.instance.install end + def link_resolv + return unless File.exist?(RESOLV) + + target = File.join(Yast::Installation.destdir, RESOLV) + source = File.join(RUN_NM_DIR, File.basename(RESOLV)) + return if File.exist?(target) + + FileUtils.touch RESOLV_FLAG + FileUtils.ln_s source, target + end + + def unlink_resolv + return unless File.exist?(RESOLV_FLAG) + + target = File.join(Yast::Installation.destdir, RESOLV) + FileUtils.rm_f target + FileUtils.rm_f RESOLV_FLAG + end + private # @return [Logger] attr_reader :logger + RESOLV = "/etc/resolv.conf" + RESOLV_FLAG = "/run/agama/manage_resolv" ETC_NM_DIR = "/etc/NetworkManager" RUN_NM_DIR = "/run/NetworkManager" private_constant :ETC_NM_DIR diff --git a/service/lib/agama/storage/finisher.rb b/service/lib/agama/storage/finisher.rb index e04e42fc95..eaacb33420 100644 --- a/service/lib/agama/storage/finisher.rb +++ b/service/lib/agama/storage/finisher.rb @@ -27,6 +27,7 @@ require "agama/with_progress" require "agama/helpers" require "agama/http" +require "agama/network" require "abstract_method" require "fileutils" @@ -261,9 +262,14 @@ def run # Run the post scripts def run_post_scripts - require "agama/http" + network.link_resolv client = Agama::HTTP::Clients::Scripts.new client.run("post") + network.unlink_resolv + end + + def network + @network ||= Agama::Network.new(logger) end # Enables the agama-scripts service to run init scripts From 641f90c463ff3132152f712842c5f9148806df92 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Thu, 20 Mar 2025 07:51:46 +0000 Subject: [PATCH 2/6] Ensure the symlink is removed if created --- service/lib/agama/storage/finisher.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/service/lib/agama/storage/finisher.rb b/service/lib/agama/storage/finisher.rb index eaacb33420..13051aead7 100644 --- a/service/lib/agama/storage/finisher.rb +++ b/service/lib/agama/storage/finisher.rb @@ -265,6 +265,7 @@ def run_post_scripts network.link_resolv client = Agama::HTTP::Clients::Scripts.new client.run("post") + ensure network.unlink_resolv end From e38f35cce08e4153e781659f1e96986d2ff6a35a Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Thu, 20 Mar 2025 08:04:54 +0000 Subject: [PATCH 3/6] Adapted unit test --- service/test/agama/storage/manager_test.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/service/test/agama/storage/manager_test.rb b/service/test/agama/storage/manager_test.rb index a278f6a314..9d668f5979 100644 --- a/service/test/agama/storage/manager_test.rb +++ b/service/test/agama/storage/manager_test.rb @@ -59,6 +59,7 @@ # mock writting config as proposal call can do storage probing, which fails in CI allow_any_instance_of(Agama::Storage::Bootloader).to receive(:write_config) allow(Agama::HTTP::Clients::Scripts).to receive(:new).and_return(scripts_client) + allow(Agama::Network).to receive(:new).and_return(network) allow(Yast::Installation).to receive(:destdir).and_return(File.join(tmp_dir, "mnt")) stub_const("Agama::Storage::Finisher::CopyLogsStep::SCRIPTS_DIR", File.join(tmp_dir, "run", "agama", "scripts")) @@ -73,6 +74,7 @@ let(:software) do instance_double(Agama::DBus::Clients::Software, selected_product: "ALP") end + let(:network) { instance_double(Agama::Network, link_resolv: nil, unlink_resolv: nil) } let(:bootloader_finish) { instance_double(Bootloader::FinishClient, write: nil) } let(:security) { instance_double(Agama::Security, probe: nil, write: nil) } @@ -378,13 +380,16 @@ let(:devicegraph) { "staging-plain-partitions.yaml" } it "copy needed files, installs the bootloader, sets up the snapshots, " \ - "copy logs, runs the post-installation scripts, and umounts the file systems" do + "copy logs, symlink resolv.conf, runs the post-installation scripts, " \ + "unlink resolv.conf and umounts the file systems" do expect(security).to receive(:write) expect(copy_files).to receive(:run) expect(bootloader_finish).to receive(:write) expect(Yast::WFM).to receive(:CallFunction).with("storage_finish", ["Write"]) expect(Yast::WFM).to receive(:CallFunction).with("snapshots_finish", ["Write"]) + expect(network).to receive(:link_resolv) expect(scripts_client).to receive(:run).with("post") + expect(network).to receive(:unlink_resolv) expect(Yast::Execute).to receive(:on_target!) .with("systemctl", "enable", "agama-scripts", allowed_exitstatus: [0, 1]) expect(Yast::WFM).to receive(:CallFunction).with("umount_finish", ["Write"]) From 4d0e8413369eff97e1225eeab180322b23956b58 Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Thu, 20 Mar 2025 08:43:42 +0000 Subject: [PATCH 4/6] Added changelog --- service/package/rubygem-agama-yast.changes | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/service/package/rubygem-agama-yast.changes b/service/package/rubygem-agama-yast.changes index 1232f89b92..0d3897badd 100644 --- a/service/package/rubygem-agama-yast.changes +++ b/service/package/rubygem-agama-yast.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Thu Mar 20 08:41:23 UTC 2025 - Knut Anderssen + +- Symlink the /mnt/etc/resolv.conf to the NetworkManager running + one in order to have DNS resolution in the chroot post scripts + (bsc#1235617, gh#agama-project/agama#2179). + ------------------------------------------------------------------- Fri Mar 14 12:34:03 UTC 2025 - Imobach Gonzalez Sosa From 23d51768bf1269101064b63cd37bc7fe4398a536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Alejandro=20Anderssen=20Gonz=C3=A1lez?= Date: Thu, 20 Mar 2025 08:59:49 +0000 Subject: [PATCH 5/6] Update service/test/agama/storage/manager_test.rb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Díaz <1691872+dgdavid@users.noreply.github.com> --- service/test/agama/storage/manager_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/test/agama/storage/manager_test.rb b/service/test/agama/storage/manager_test.rb index 9d668f5979..0d1a593376 100644 --- a/service/test/agama/storage/manager_test.rb +++ b/service/test/agama/storage/manager_test.rb @@ -381,7 +381,7 @@ it "copy needed files, installs the bootloader, sets up the snapshots, " \ "copy logs, symlink resolv.conf, runs the post-installation scripts, " \ - "unlink resolv.conf and umounts the file systems" do + "unlink resolv.conf, and umounts the file systems" do expect(security).to receive(:write) expect(copy_files).to receive(:run) expect(bootloader_finish).to receive(:write) From e5aa7a4c45652052d4ec7021fe34097b8a3fec3e Mon Sep 17 00:00:00 2001 From: Knut Anderssen Date: Thu, 20 Mar 2025 13:59:57 +0000 Subject: [PATCH 6/6] Added unit test for creating resolv.conf symlink --- service/lib/agama/network.rb | 13 +-- service/test/agama/network_test.rb | 86 +++++++++++++++++-- service/test/agama/storage/manager_test.rb | 2 +- .../root_dir/run/NetworkManager/resolv.conf | 3 + 4 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 service/test/fixtures/root_dir/run/NetworkManager/resolv.conf diff --git a/service/lib/agama/network.rb b/service/lib/agama/network.rb index 1433d481aa..4a7b0053b8 100644 --- a/service/lib/agama/network.rb +++ b/service/lib/agama/network.rb @@ -49,19 +49,20 @@ def install def link_resolv return unless File.exist?(RESOLV) - target = File.join(Yast::Installation.destdir, RESOLV) - source = File.join(RUN_NM_DIR, File.basename(RESOLV)) - return if File.exist?(target) + link = File.join(Yast::Installation.destdir, RESOLV) + target = File.join(RUN_NM_DIR, File.basename(RESOLV)) + + return if File.exist?(link) FileUtils.touch RESOLV_FLAG - FileUtils.ln_s source, target + FileUtils.ln_s target, link end def unlink_resolv return unless File.exist?(RESOLV_FLAG) - target = File.join(Yast::Installation.destdir, RESOLV) - FileUtils.rm_f target + link = File.join(Yast::Installation.destdir, RESOLV) + FileUtils.rm_f link FileUtils.rm_f RESOLV_FLAG end diff --git a/service/test/agama/network_test.rb b/service/test/agama/network_test.rb index d67e12a7d1..eee39498ac 100644 --- a/service/test/agama/network_test.rb +++ b/service/test/agama/network_test.rb @@ -28,25 +28,29 @@ subject(:network) { described_class.new(logger) } let(:logger) { Logger.new($stdout, level: :warn) } + let(:targetdir) { File.join(rootdir, "mnt") } + + before do + allow(Yast::Installation).to receive(:destdir).and_return(targetdir) + end + + after do + FileUtils.remove_entry(rootdir) + end describe "#install" do let(:rootdir) { Dir.mktmpdir } + let(:etcdir) do File.join(rootdir, "etc", "NetworkManager", "system-connections") end - let(:targetdir) { File.join(rootdir, "mnt") } let(:service) { instance_double(Yast2::Systemd::Service, enable: nil) } before do - allow(Yast::Installation).to receive(:destdir).and_return(targetdir) allow(Yast2::Systemd::Service).to receive(:find).with("NetworkManager").and_return(service) stub_const("Agama::Network::ETC_NM_DIR", etcdir) end - after do - FileUtils.remove_entry(rootdir) - end - context "when NetworkManager configuration files are present" do before do FileUtils.mkdir_p(File.join(etcdir, "system-connections")) @@ -95,4 +99,74 @@ end end end + + describe "#link_resolv" do + let(:rootdir) { Dir.mktmpdir } + + let(:fixtures) { File.join(FIXTURES_PATH, "root_dir") } + let(:resolv_fixture) { File.join(FIXTURES_PATH, "etc", "resolv.conf") } + let(:resolv_flag) { File.join(rootdir, "run", "agama", "manage_resolv") } + let(:resolv) { File.join(targetdir, "etc", "resolv.conf") } + + before do + stub_const("Agama::Network::RESOLV_FLAG", resolv_flag) + stub_const("Agama::Network::RUN_NM_DIR", File.join(rootdir, "run", "NetworkManager")) + FileUtils.mkdir_p targetdir + FileUtils.cp_r(Dir["#{fixtures}/*"], rootdir) + FileUtils.cp_r(Dir["#{fixtures}/*"], targetdir) + end + + context "when the /etc/resolv.conf exists in the installation destdir" do + before do + FileUtils.mkdir_p File.join(targetdir, "etc") + FileUtils.touch File.join(targetdir, "etc", "resolv.conf") + end + + it "does nothing" do + expect(FileUtils).to_not receive(:ln_s) + network.link_resolv + end + end + + context "when there is no /etc/resolv.conf in the installation destdir" do + it "symlinks it to /run/NetworkManager/resolv.conf" do + network.link_resolv + expect(File.exist?(resolv)).to eql(true) + expect(File.symlink?(resolv)).to eql(true) + end + + it "creates a flag indicating that the resolv.conf is managed by Agama" do + network.link_resolv + expect(File.exist?(resolv_flag)).to eql(true) + end + end + end + + describe "#unlink_resolv" do + let(:rootdir) { Dir.mktmpdir } + + let(:fixtures) { File.join(FIXTURES_PATH, "root_dir") } + let(:resolv_fixture) { File.join(FIXTURES_PATH, "etc", "resolv.conf") } + let(:resolv_flag) { File.join(rootdir, "run", "agama", "manage_resolv") } + let(:resolv) { File.join(targetdir, "etc", "resolv.conf") } + + before do + stub_const("Agama::Network::RESOLV_FLAG", resolv_flag) + stub_const("Agama::Network::RUN_NM_DIR", File.join(rootdir, "run", "NetworkManager")) + FileUtils.mkdir_p targetdir + FileUtils.cp_r(Dir["#{fixtures}/*"], rootdir) + FileUtils.cp_r(Dir["#{fixtures}/*"], targetdir) + end + + context "when the /etc/resolv.conf was marked as managed by Agama" do + it "removes the /etc/resolv.con symlink from the installation destdir" do + network.link_resolv + expect(File.exist?(resolv_flag)).to eql(true) + expect(File.symlink?(resolv)).to eql(true) + network.unlink_resolv + expect(File.exist?(resolv)).to eql(false) + expect(File.exist?(resolv_flag)).to eql(false) + end + end + end end diff --git a/service/test/agama/storage/manager_test.rb b/service/test/agama/storage/manager_test.rb index 0d1a593376..89772044e5 100644 --- a/service/test/agama/storage/manager_test.rb +++ b/service/test/agama/storage/manager_test.rb @@ -22,7 +22,7 @@ require_relative "../../test_helper" require_relative "../with_progress_examples" require_relative "../with_issues_examples" -require_relative "./storage_helpers" +require_relative "storage_helpers" require "agama/dbus/clients/questions" require "agama/config" require "agama/http" diff --git a/service/test/fixtures/root_dir/run/NetworkManager/resolv.conf b/service/test/fixtures/root_dir/run/NetworkManager/resolv.conf new file mode 100644 index 0000000000..04aa3d9f6d --- /dev/null +++ b/service/test/fixtures/root_dir/run/NetworkManager/resolv.conf @@ -0,0 +1,3 @@ +# Generated by NetworkManager +search default +nameserver 192.168.100.1