Skip to content

Commit

Permalink
storage: avoid reference to compound action
Browse files Browse the repository at this point in the history
  • Loading branch information
joseivanlopez committed Jun 28, 2024
1 parent fc04e35 commit 0638ef9
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 73 deletions.
2 changes: 1 addition & 1 deletion service/lib/agama/dbus/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def deprecated_system
def read_actions
backend.actions.map do |action|
{
"Device" => action.device.sid,
"Device" => action.device_sid,
"Text" => action.text,
"Subvol" => action.on_btrfs_subvolume?,
"Delete" => action.delete?,
Expand Down
2 changes: 1 addition & 1 deletion service/lib/agama/dbus/storage/proposal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def actions
# * "Resize" [Boolean]
def to_dbus_action(action)
{
"Device" => action.device.sid,
"Device" => action.device_sid,
"Text" => action.text,
"Subvol" => action.on_btrfs_subvolume?,
"Delete" => action.delete?,
Expand Down
53 changes: 33 additions & 20 deletions service/lib/agama/storage/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,49 +23,51 @@ module Agama
module Storage
# Represents an action to perform in the storage devices.
class Action
# @param action [Y2Storage::CompoundAction]
# @param system_graph [Y2Storage::Devicegraph]
def initialize(action, system_graph)
@action = action
@system_graph = system_graph
end

# Affected device
# SID of the affected device
#
# @return [Y2Storage::Device]
def device
action.target_device
end
# @return [Integer]
attr_reader :device_sid

# Text describing the action.
#
# @return [String]
def text
action.sentence
attr_reader :text

# @note Do not keep a reference to the compound action. Accessing to the compound action could
# raise a segmentation fault if the source actiongraph object was killed by the ruby GC.
#
# See https://github.com/openSUSE/agama/issues/1396.
#
# @param action [Y2Storage::CompoundAction]
# @param system_graph [Y2Storage::Devicegraph]
def initialize(action, system_graph)
@system_graph = system_graph
@device_sid = action.target_device.sid
@text = action.sentence
@delete = action.delete?
@resize = resize_action?(action.target_device, system_graph)
@on_btrfs_subvolume = action.device_is?(:btrfs_subvolume)
end

# Whether the action affects to a Btrfs subvolume.
#
# @return [Boolean]
def on_btrfs_subvolume?
action.device_is?(:btrfs_subvolume)
@on_btrfs_subvolume
end

# Whether the action deletes the device.
#
# @return [Boolean]
def delete?
action.delete?
@delete
end

# Whether the action resizes the device.
#
# @return [Boolean]
def resize?
return false unless device.exists_in_devicegraph?(system_graph)
return false unless device.respond_to?(:size)

system_graph.find_device(device.sid).size != device.size
@resize
end

private
Expand All @@ -75,6 +77,17 @@ def resize?

# @return [Y2Storage::Devicegraph]
attr_reader :system_graph

# @param device [Y2Storage::Device]
# @param system_graph [Y2Storage::Devicegraph]
#
# @return [Boolean]
def resize_action?(device, system_graph)
return false unless device.exists_in_devicegraph?(system_graph)
return false unless device.respond_to?(:size)

system_graph.find_device(device.sid).size != device.size
end
end
end
end
11 changes: 4 additions & 7 deletions service/lib/agama/storage/actions_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ class ActionsGenerator
# param target_graph [Y2Storage::Devicegraph]
def initialize(system_graph, target_graph)
@system_graph = system_graph
# It is important to keep a reference to the actiongraph. Otherwise, the gargabe collector
# could kill the actiongraph, leaving the compound actions orphan.
# See https://github.com/openSUSE/agama/issues/1377.
@actiongraph = target_graph.actiongraph
@target_graph = target_graph
end

# All actions properly sorted.
Expand All @@ -47,8 +44,8 @@ def generate
# @return [Y2Storage::Devicegraph]
attr_reader :system_graph

# @return [Y2Storage::Actiongraph]
attr_reader :actiongraph
# @return [Y2Storage::Devicegraph]
attr_reader :target_graph

# Sorted main actions (everything except subvolume actions).
#
Expand All @@ -70,7 +67,7 @@ def subvolume_actions
#
# @return [Array<Action>]
def actions
@actions ||= actiongraph.compound_actions.map do |action|
@actions ||= target_graph.actiongraph.compound_actions.map do |action|
Action.new(action, system_graph)
end
end
Expand Down
23 changes: 2 additions & 21 deletions service/lib/agama/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,32 +165,13 @@ def software

# Storage actions.
#
# @return [Array<Y2Storage::CompoundAction>]
# @return [Array<Action>]
def actions
return [] unless Y2Storage::StorageManager.instance.probed?

probed = Y2Storage::StorageManager.instance.probed
staging = Y2Storage::StorageManager.instance.staging
# FIXME: This is a hot-fix to avoid segmentation fault in the actions, see
# https://github.com/openSUSE/agama/issues/1396.
#
# Source of the problem:
# * An actiongraph is generated from the target devicegraph.
# * The list of compound actions is recovered from the actiongraph.
# * No refrence to the actiongraph is kept, so the object is a candidate to be cleaned by
# the ruby GC.
# * Accessing to the generated actions raises a segmentation fault if the actiongraph was
# cleaned.
#
# There was a previous attempt of fixing the issue by keeping a reference to the
# actiongraph in the ActionsGenerator object. But that solution is not enough because
# the ActionGenerator object is also cleaned up.
#
# As a hot-fix, the generator is kept in an instance variable to avoid the GC to kill it.
# A better solution is needed, for example, by avoiding to store an instance of a compound
# action in the Action object.
@generator = ActionsGenerator.new(probed, staging)
@generator.generate
ActionsGenerator.new(probed, staging).generate
end

# Changes the service's locale
Expand Down
2 changes: 1 addition & 1 deletion service/lib/agama/storage/proposal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def calculate_autoyast(partitioning)

# Storage actions.
#
# @return [Array<Y2Storage::CompoundAction>]
# @return [Array<Action>]
def actions
return [] unless proposal&.devices

Expand Down
13 changes: 4 additions & 9 deletions service/test/agama/dbus/storage/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
let(:action1) do
instance_double(Agama::Storage::Action,
text: "test1",
device: device1,
device_sid: 1,
on_btrfs_subvolume?: false,
delete?: false,
resize?: false)
Expand All @@ -128,7 +128,7 @@
let(:action2) do
instance_double(Agama::Storage::Action,
text: "test2",
device: device2,
device_sid: 2,
on_btrfs_subvolume?: false,
delete?: true,
resize?: false)
Expand All @@ -137,7 +137,7 @@
let(:action3) do
instance_double(Agama::Storage::Action,
text: "test3",
device: device3,
device_sid: 3,
on_btrfs_subvolume?: false,
delete?: false,
resize?: true)
Expand All @@ -146,17 +146,12 @@
let(:action4) do
instance_double(Agama::Storage::Action,
text: "test4",
device: device4,
device_sid: 4,
on_btrfs_subvolume?: true,
delete?: false,
resize?: false)
end

let(:device1) { instance_double(Y2Storage::Device, sid: 1) }
let(:device2) { instance_double(Y2Storage::Device, sid: 2) }
let(:device3) { instance_double(Y2Storage::Device, sid: 3) }
let(:device4) { instance_double(Y2Storage::Device, sid: 4) }

it "returns a list with a hash for each action" do
expect(subject.actions.size).to eq(4)
expect(subject.actions).to all(be_a(Hash))
Expand Down
13 changes: 4 additions & 9 deletions service/test/agama/dbus/storage/proposal_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
let(:action1) do
instance_double(Agama::Storage::Action,
text: "test1",
device: device1,
device_sid: 1,
on_btrfs_subvolume?: false,
delete?: false,
resize?: false)
Expand All @@ -123,7 +123,7 @@
let(:action2) do
instance_double(Agama::Storage::Action,
text: "test2",
device: device2,
device_sid: 2,
on_btrfs_subvolume?: false,
delete?: true,
resize?: false)
Expand All @@ -132,7 +132,7 @@
let(:action3) do
instance_double(Agama::Storage::Action,
text: "test3",
device: device3,
device_sid: 3,
on_btrfs_subvolume?: false,
delete?: false,
resize?: true)
Expand All @@ -141,17 +141,12 @@
let(:action4) do
instance_double(Agama::Storage::Action,
text: "test4",
device: device4,
device_sid: 4,
on_btrfs_subvolume?: true,
delete?: false,
resize?: false)
end

let(:device1) { instance_double(Y2Storage::Device, sid: 1) }
let(:device2) { instance_double(Y2Storage::Device, sid: 2) }
let(:device3) { instance_double(Y2Storage::Device, sid: 3) }
let(:device4) { instance_double(Y2Storage::Device, sid: 4) }

it "returns a list with a hash for each action" do
expect(subject.actions.size).to eq(4)
expect(subject.actions).to all(be_a(Hash))
Expand Down
8 changes: 4 additions & 4 deletions service/test/agama/storage/action_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,17 @@
described_class.new(action, system_graph)
end

describe "#device" do
it "returns the affected device" do
describe "#device_sid" do
it "returns the SID of the affected device" do
sda2 = system_graph.find_by_name("/dev/sda2")
expect(sda2_action.device.sid).to eq(sda2.sid)
expect(sda2_action.device_sid).to eq(sda2.sid)

home_subvol = sda2
.filesystem
.btrfs_subvolumes
.find { |s| s.path == "home" }

expect(subvol_action.device.sid).to eq(home_subvol.sid)
expect(subvol_action.device_sid).to eq(home_subvol.sid)
end
end

Expand Down

0 comments on commit 0638ef9

Please sign in to comment.