From 6e0e43cde39f7614c79e75c84b8f4f2a13e587f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 16 Oct 2024 13:18:41 +0100 Subject: [PATCH 1/3] storage: add config conversion to JSON --- .../lib/agama/storage/config_conversions.rb | 1 + .../storage/config_conversions/to_json.rb | 48 ++++++++++ .../config_conversions/to_json_conversions.rb | 45 +++++++++ .../to_json_conversions/base.rb | 74 +++++++++++++++ .../to_json_conversions/boot.rb | 49 ++++++++++ .../to_json_conversions/config.rb | 68 ++++++++++++++ .../to_json_conversions/drive.rb | 64 +++++++++++++ .../to_json_conversions/encryption.rb | 87 ++++++++++++++++++ .../to_json_conversions/filesystem.rb | 72 +++++++++++++++ .../to_json_conversions/logical_volume.rb | 63 +++++++++++++ .../to_json_conversions/luks1.rb | 50 ++++++++++ .../to_json_conversions/luks2.rb | 52 +++++++++++ .../to_json_conversions/partition.rb | 83 +++++++++++++++++ .../to_json_conversions/pervasive_luks2.rb | 48 ++++++++++ .../to_json_conversions/search.rb | 57 ++++++++++++ .../to_json_conversions/size.rb | 57 ++++++++++++ .../to_json_conversions/volume_group.rb | 92 +++++++++++++++++++ .../to_json_conversions/with_encryption.rb | 41 +++++++++ .../to_json_conversions/with_filesystem.rb | 41 +++++++++ .../to_json_conversions/with_partitions.rb | 40 ++++++++ .../to_json_conversions/with_ptable_type.rb | 36 ++++++++ .../to_json_conversions/with_search.rb | 41 +++++++++ .../to_json_conversions/with_size.rb | 41 +++++++++ 23 files changed, 1250 insertions(+) create mode 100644 service/lib/agama/storage/config_conversions/to_json.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/base.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/boot.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/config.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/drive.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/encryption.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/filesystem.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/logical_volume.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/luks1.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/luks2.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/partition.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/pervasive_luks2.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/search.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/size.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/volume_group.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/with_encryption.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/with_filesystem.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/with_partitions.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/with_ptable_type.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/with_search.rb create mode 100644 service/lib/agama/storage/config_conversions/to_json_conversions/with_size.rb diff --git a/service/lib/agama/storage/config_conversions.rb b/service/lib/agama/storage/config_conversions.rb index c1284c76bf..e8f49d38f5 100644 --- a/service/lib/agama/storage/config_conversions.rb +++ b/service/lib/agama/storage/config_conversions.rb @@ -20,6 +20,7 @@ # find current contact information at www.suse.com. require "agama/storage/config_conversions/from_json" +require "agama/storage/config_conversions/to_json" module Agama module Storage diff --git a/service/lib/agama/storage/config_conversions/to_json.rb b/service/lib/agama/storage/config_conversions/to_json.rb new file mode 100644 index 0000000000..fc014cedb0 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/config" + +module Agama + module Storage + module ConfigConversions + # Config conversion to JSON hash according to schema. + class ToJSON + # @param config [Storage::Config] + def initialize(config) + @config = config + end + + # Performs the conversion to Hash according to the JSON schema. + # + # @return [Hash] + def convert + ToJSONConversions::Config.new(config).convert + end + + private + + # @return [Storage::Config] + attr_reader :config + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions.rb b/service/lib/agama/storage/config_conversions/to_json_conversions.rb new file mode 100644 index 0000000000..47a379ca02 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/from_json_conversions/base" +require "agama/storage/config_conversions/from_json_conversions/boot" +require "agama/storage/config_conversions/from_json_conversions/config" +require "agama/storage/config_conversions/from_json_conversions/drive" +require "agama/storage/config_conversions/from_json_conversions/encryption" +require "agama/storage/config_conversions/from_json_conversions/filesystem" +require "agama/storage/config_conversions/from_json_conversions/logical_volume" +require "agama/storage/config_conversions/from_json_conversions/luks1" +require "agama/storage/config_conversions/from_json_conversions/luks2" +require "agama/storage/config_conversions/from_json_conversions/partition" +require "agama/storage/config_conversions/from_json_conversions/pervasive_luks2" +require "agama/storage/config_conversions/from_json_conversions/search" +require "agama/storage/config_conversions/from_json_conversions/size" +require "agama/storage/config_conversions/from_json_conversions/volume_group" + +module Agama + module Storage + module ConfigConversions + # Conversions to JSON. + module ToJSONConversions + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/base.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/base.rb new file mode 100644 index 0000000000..e96efd3891 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/base.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Base class for conversions to JSON hash according to schema. + class Base + # Defines the expected config type to perform the conversion. + # + # @raise If a subclass does not defines a type. + # @return [Class] + def self.config_type + raise "Undefined config type" + end + + # @param config [Object] The config type is provided by the {.config_type} method. + def initialize(config) + type = self.class.config_type + raise "Invalid config (#{type} expected): #{config}" unless config.is_a?(type) + + @config = config + end + + # Performs the conversion to Hash according to the JSON schema. + # + # @return [Hash, nil] + def convert + config_json = {} + + conversions.each do |property, value| + next if value.nil? + + config_json[property] = value + end + + config_json.empty? ? nil : config_json + end + + private + + # @return [Object] The config type is provided by the {.config_type} method. + attr_reader :config + + # Values to generate the JSON. + # + # @return [Hash] e.g., { name: "/dev/vda" }. + def conversions + {} + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/boot.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/boot.rb new file mode 100644 index 0000000000..88a96e3716 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/boot.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/configs/logical_volume" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Boot conversion to JSON hash according to schema. + class Boot < Base + # @see Base + def self.config_type + Configs::Boot + end + + private + + # @see Base#conversions + def conversions + { + configure: config.configure?, + device: config.device + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/config.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/config.rb new file mode 100644 index 0000000000..2eb2017e7c --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/config.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config" +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/config_conversions/to_json_conversions/boot" +require "agama/storage/config_conversions/to_json_conversions/drive" +require "agama/storage/config_conversions/to_json_conversions/volume_group" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Config conversion to JSON hash according to schema. + class Config < Base + # @see Base + def self.config_type + Storage::Config + end + + private + + # @see Base#conversions + def conversions + { + boot: convert_boot, + drives: convert_drives, + volumeGroups: convert_volume_groups + } + end + + # @return [Hash] + def convert_boot + ToJSONConversions::Boot.new(config.boot).convert + end + + # @return [Array] + def convert_drives + config.drives.map { |d| ToJSONConversions::Drive.new(d).convert } + end + + # @return [Array] + def convert_volume_groups + config.volume_groups.map { |v| ToJSONConversions::VolumeGroup.new(v).convert } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/drive.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/drive.rb new file mode 100644 index 0000000000..5a6f9bf1b1 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/drive.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/config_conversions/to_json_conversions/with_encryption" +require "agama/storage/config_conversions/to_json_conversions/with_filesystem" +require "agama/storage/config_conversions/to_json_conversions/with_partitions" +require "agama/storage/config_conversions/to_json_conversions/with_ptable_type" +require "agama/storage/config_conversions/to_json_conversions/with_search" +require "agama/storage/configs/drive" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Drive conversion to JSON hash according to schema. + class Drive < Base + include WithSearch + include WithEncryption + include WithFilesystem + include WithPtableType + include WithPartitions + + # @see Base + def self.config_type + Configs::Drive + end + + private + + # @see Base#conversions + def conversions + { + search: convert_search, + alias: config.alias, + encryption: convert_encryption, + filesystem: convert_filesystem, + ptableType: convert_ptable_type, + partitions: convert_partitions + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/encryption.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/encryption.rb new file mode 100644 index 0000000000..f401a95f76 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/encryption.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/config_conversions/to_json_conversions/luks1" +require "agama/storage/config_conversions/to_json_conversions/luks2" +require "agama/storage/config_conversions/to_json_conversions/pervasive_luks2" +require "agama/storage/configs/encryption" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Encryption conversion to JSON hash according to schema. + class Encryption < Base + # @see Base + def self.config_type + Configs::Encryption + end + + # @see Base#convert + # @return [Hash, String, nil] + def convert + return unless config.method + + super || convert_swap_encryption + end + + private + + # @see Base#conversions + def conversions + method = config.method + + if method.is?(:luks1) + convert_luks1 + elsif method.is?(:luks2) + convert_luks2 + elsif method.is?(:pervasive_luks2) + convert_pervasive_luks2 + else + {} + end + end + + # @return [Hash] + def convert_luks1 + { luks1: ToJSONConversions::Luks1.new(config).convert } + end + + # @return [Hash] + def convert_luks2 + { luks2: ToJSONConversions::Luks2.new(config).convert } + end + + # @return [Hash] + def convert_pervasive_luks2 + { pervasiveLuks2: ToJSONConversions::PervasiveLuks2.new(config).convert } + end + + # @return [String] + def convert_swap_encryption + config.method.id.to_s + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/filesystem.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/filesystem.rb new file mode 100644 index 0000000000..45f67d7d18 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/filesystem.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/configs/filesystem" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Filesystem conversion to JSON hash according to schema. + class Filesystem < Base + # @see Base + def self.config_type + Configs::Filesystem + end + + private + + # @see Base#conversions + def conversions + { + reuseIfPossible: config.reuse?, + label: config.label, + path: config.path, + mountOptions: config.mount_options, + mkfsOptions: config.mkfs_options, + mountBy: config.mount_by&.to_s, + type: convert_type + } + end + + # @return [Hash, String, nil] + def convert_type + type = config.type + return unless type + + fs_type = type.fs_type&.to_s + return fs_type unless fs_type == "btrfs" + + btrfs = type.btrfs + return "btrfs" unless btrfs + + { + btrfs: { + snapshots: btrfs.snapshots? + } + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/logical_volume.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/logical_volume.rb new file mode 100644 index 0000000000..531bcd2054 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/logical_volume.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/config_conversions/to_json_conversions/with_encryption" +require "agama/storage/config_conversions/to_json_conversions/with_filesystem" +require "agama/storage/config_conversions/to_json_conversions/with_size" +require "agama/storage/configs/logical_volume" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Logical volume conversion to JSON hash according to schema. + class LogicalVolume < Base + include WithEncryption + include WithFilesystem + include WithSize + + # @see Base + def self.config_type + Configs::LogicalVolume + end + + private + + # @see Base#conversions + def conversions + { + alias: config.alias, + encryption: convert_encryption, + filesystem: convert_filesystem, + size: convert_size, + name: config.name, + stripes: config.stripes, + stripeSize: config.stripe_size&.to_i, + pool: config.pool?, + usedPool: config.used_pool + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/luks1.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/luks1.rb new file mode 100644 index 0000000000..7cf77d1e4f --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/luks1.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/configs/encryption" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Luks1 conversion to JSON hash according to schema. + class Luks1 < Base + # @see Base + def self.config_type + Configs::Encryption + end + + private + + # @see Base#conversions + def conversions + { + password: config.password, + keySize: config.key_size, + cipher: config.cipher + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/luks2.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/luks2.rb new file mode 100644 index 0000000000..84a9d600ef --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/luks2.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/configs/encryption" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Luks2 conversion to JSON hash according to schema. + class Luks2 < Base + # @see Base + def self.config_type + Configs::Encryption + end + + private + + # @see Base#conversions + def conversions + { + password: config.password, + keySize: config.key_size, + cipher: config.cipher, + label: config.label, + pbkdFunction: config.pbkd_function&.to_s + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/partition.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/partition.rb new file mode 100644 index 0000000000..9155ebe2aa --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/partition.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/config_conversions/to_json_conversions/with_encryption" +require "agama/storage/config_conversions/to_json_conversions/with_filesystem" +require "agama/storage/config_conversions/to_json_conversions/with_search" +require "agama/storage/config_conversions/to_json_conversions/with_size" +require "agama/storage/configs/partition" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Partition conversion to JSON hash according to schema. + class Partition < Base + include WithSearch + include WithEncryption + include WithFilesystem + include WithSize + + # @see Base + def self.config_type + Configs::Partition + end + + private + + # @see Base#conversions + def conversions + return convert_delete if config.delete? + + return convert_delete_if_needed if config.delete_if_needed? + + { + search: convert_search, + alias: config.alias, + encryption: convert_encryption, + filesystem: convert_filesystem, + size: convert_size, + id: config.id&.to_s + } + end + + # @return [Hash] + def convert_delete + { + search: convert_search, + delete: true + } + end + + # @return [Hash] + def convert_delete_if_needed + { + search: convert_search, + size: convert_size, + deleteIfNeeded: true + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/pervasive_luks2.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/pervasive_luks2.rb new file mode 100644 index 0000000000..63a129e968 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/pervasive_luks2.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/configs/encryption" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Pervasive Luks2 conversion to JSON hash according to schema. + class PervasiveLuks2 < Base + # @see Base + def self.config_type + Configs::Encryption + end + + private + + # @see Base#conversions + def conversions + { + password: config.password + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/search.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/search.rb new file mode 100644 index 0000000000..48108837f7 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/search.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/configs/search" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Search conversion to JSON hash according to schema. + class Search < Base + # @see Base + def self.config_type + Configs::Search + end + + private + + # @see Base#conversions + def conversions + { + condition: convert_condition, + ifNotFound: config.if_not_found.to_s + } + end + + # @return [Hash, nil] + def convert_condition + name = config.name || config.device&.name + return unless name + + { name: name } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/size.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/size.rb new file mode 100644 index 0000000000..f57c2ba55c --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/size.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/configs/size" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Size conversion to JSON hash according to schema. + class Size < Base + # @see Base + def self.config_type + Configs::Size + end + + private + + # @see Base#conversions + def conversions + { + min: config.min&.to_i, + max: convert_max_size + } + end + + # @return [Integer, nil] + def convert_max_size + max = config.max + return if max.nil? || max.unlimited? + + max.to_i + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/volume_group.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/volume_group.rb new file mode 100644 index 0000000000..ffe0be78a4 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/volume_group.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/base" +require "agama/storage/config_conversions/to_json_conversions/encryption" +require "agama/storage/config_conversions/to_json_conversions/logical_volume" +require "agama/storage/configs/volume_group" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Volume group conversion to JSON hash according to schema. + class VolumeGroup < Base + # @see Base + def self.config_type + Configs::VolumeGroup + end + + private + + # @see Base#conversions + def conversions + { + name: config.name, + extentSize: config.extent_size&.to_i, + physicalVolumes: convert_physical_volumes, + logicalVolumes: convert_logical_volumes + } + end + + # @return [Integer, nil] + def convert_extent_size + extent_size = config.extent_size + return unless extent_size + + extent_size.to_i + end + + # @return [Array] + def convert_physical_volumes + [ + config.physical_volumes, + convert_physical_volumes_devices + ].flatten.compact + end + + # @return [Hash, nil] + def convert_physical_volumes_devices + devices = config.physical_volumes_devices + return if devices.empty? + + encryption = config.physical_volumes_encryption + return { generate: devices } unless encryption + + { + generate: { + targetDevices: devices, + encryption: ToJSONConversions::Encryption.new(encryption).convert + } + } + end + + # @return [Array] + def convert_logical_volumes + config.logical_volumes + .map { |l| ToJSONConversions::LogicalVolume.new(l).convert } + .compact + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/with_encryption.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/with_encryption.rb new file mode 100644 index 0000000000..70efcd1f0c --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/with_encryption.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/encryption" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Mixin for encryption conversion to JSON. + module WithEncryption + # @return [Hash, String, nil] + def convert_encryption + encryption = config.encryption + return unless encryption + + ToJSONConversions::Encryption.new(encryption).convert + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/with_filesystem.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/with_filesystem.rb new file mode 100644 index 0000000000..f35a8992da --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/with_filesystem.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/filesystem" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Mixin for filesystem conversion to JSON. + module WithFilesystem + # @return [Hash, nil] + def convert_filesystem + filesystem = config.filesystem + return unless filesystem + + ToJSONConversions::Filesystem.new(filesystem).convert + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/with_partitions.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/with_partitions.rb new file mode 100644 index 0000000000..213a5053fe --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/with_partitions.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/partition" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Mixin for partitions conversion to JSON. + module WithPartitions + # @return [Array] + def convert_partitions + config.partitions + .map { |p| ToJSONConversions::Partition.new(p).convert } + .compact + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/with_ptable_type.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/with_ptable_type.rb new file mode 100644 index 0000000000..395897fd01 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/with_ptable_type.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Mixin for partition table type conversion to JSON. + module WithPtableType + # @return [String, nil] + def convert_ptable_type + config.ptable_type&.to_s + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/with_search.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/with_search.rb new file mode 100644 index 0000000000..178bf60e40 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/with_search.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/search" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Mixin for search conversion to JSON. + module WithSearch + # @return [Hash, nil] + def convert_search + search = config.search + return unless search + + ToJSONConversions::Search.new(search).convert + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_json_conversions/with_size.rb b/service/lib/agama/storage/config_conversions/to_json_conversions/with_size.rb new file mode 100644 index 0000000000..759bad13c9 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_json_conversions/with_size.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "agama/storage/config_conversions/to_json_conversions/size" + +module Agama + module Storage + module ConfigConversions + module ToJSONConversions + # Mixin for size conversion to JSON. + module WithSize + # @return [Hash, nil] + def convert_size + size = config.size + return unless size + + ToJSONConversions::Size.new(size).convert + end + end + end + end + end +end From 85ec73709a72b084c478e2f0abaae85fbc8e4e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 16 Oct 2024 13:19:09 +0100 Subject: [PATCH 2/3] storage: add tests --- .../config_conversions/to_json_test.rb | 1017 +++++++++++++++++ 1 file changed, 1017 insertions(+) create mode 100644 service/test/agama/storage/config_conversions/to_json_test.rb diff --git a/service/test/agama/storage/config_conversions/to_json_test.rb b/service/test/agama/storage/config_conversions/to_json_test.rb new file mode 100644 index 0000000000..0b6102e0e4 --- /dev/null +++ b/service/test/agama/storage/config_conversions/to_json_test.rb @@ -0,0 +1,1017 @@ +# frozen_string_literal: true + +# Copyright (c) [2024] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "agama/storage/config_conversions/from_json" +require "agama/storage/config_conversions/to_json" +require "y2storage/refinements" + +using Y2Storage::Refinements::SizeCasts + +shared_examples "without search" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:search) + end +end + +shared_examples "without alias" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:alias) + end +end + +shared_examples "without encryption" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:encryption) + end +end + +shared_examples "without filesystem" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:filesystem) + end +end + +shared_examples "without ptable_type" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:ptableType) + end +end + +shared_examples "without partitions" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json[:partitions]).to eq([]) + end +end + +shared_examples "without size" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:size) + end +end + +shared_examples "without delete" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:delete) + end +end + +shared_examples "without delete_if_needed" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:deleteIfNeeded) + end +end + +shared_examples "with search" do |result_scope| + let(:search) do + { + condition: { name: "/dev/vda1" }, + ifNotFound: "skip" + } + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + search_json = config_json[:search] + + expect(search_json).to eq( + { + condition: { name: "/dev/vda1" }, + ifNotFound: "skip" + } + ) + end + + context "if the device name is not provided" do + let(:search) { {} } + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + search_json = config_json[:search] + + expect(search_json).to eq( + { + ifNotFound: "error" + } + ) + end + + context "and a device was assigned" do + before do + allow_any_instance_of(Agama::Storage::Configs::Search) + .to(receive(:device)) + .and_return(device) + end + + let(:device) { instance_double(Y2Storage::BlkDevice, name: "/dev/vda") } + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + search_json = config_json[:search] + + expect(search_json).to eq( + { + condition: { name: "/dev/vda" }, + ifNotFound: "error" + } + ) + end + end + end +end + +shared_examples "with alias" do |result_scope| + let(:device_alias) { "test" } + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json[:alias]).to eq("test") + end +end + +shared_examples "with encryption" do |result_scope| + let(:encryption) do + { + luks2: { + password: "12345", + keySize: 256, + pbkdFunction: "argon2i", + cipher: "twofish", + label: "test" + } + } + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + encryption_json = config_json[:encryption] + + expect(encryption_json).to eq( + { + luks2: { + password: "12345", + keySize: 256, + pbkdFunction: "argon2i", + cipher: "twofish", + label: "test" + } + } + ) + end + + context "if encryption only configures #password" do + let(:encryption) do + { + luks2: { + password: "12345" + } + } + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + encryption_json = config_json[:encryption] + + expect(encryption_json).to eq( + { + luks2: { + password: "12345" + } + } + ) + end + end + + context "if encryption method is pervasive LUKS2" do + let(:encryption) do + { + pervasiveLuks2: { + password: "12345" + } + } + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + encryption_json = config_json[:encryption] + + expect(encryption_json).to eq( + { + pervasiveLuks2: { + password: "12345" + } + } + ) + end + end + + context "if encryption method is protected swap" do + let(:encryption) { "protected_swap" } + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + encryption_json = config_json[:encryption] + + expect(encryption_json).to eq("protected_swap") + end + end + + context "if encryption method is not configured" do + let(:encryption) { {} } + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + encryption_json = config_json[:encryption] + expect(encryption_json).to be_nil + end + end +end + +shared_examples "with filesystem" do |result_scope| + let(:filesystem) do + { + reuseIfPossible: true, + type: "xfs", + label: "test", + path: "/test", + mountBy: "device", + mkfsOptions: ["version=2"], + mountOptions: ["rw"] + } + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + filesystem_json = config_json[:filesystem] + + expect(filesystem_json).to eq( + { + reuseIfPossible: true, + type: "xfs", + label: "test", + path: "/test", + mountBy: "device", + mkfsOptions: ["version=2"], + mountOptions: ["rw"] + } + ) + end + + context "if filesystem configures #btrfs" do + let(:filesystem) do + { + type: { + btrfs: { + snapshots: true + } + } + } + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + filesystem_json = config_json[:filesystem] + + expect(filesystem_json).to eq( + { + reuseIfPossible: false, + type: { + btrfs: { snapshots: true } + }, + mkfsOptions: [], + mountOptions: [] + } + ) + end + end + + context "if filesystem does not configure #type" do + let(:filesystem) { {} } + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + filesystem_json = config_json[:filesystem] + + expect(filesystem_json).to eq( + { + reuseIfPossible: false, + mkfsOptions: [], + mountOptions: [] + } + ) + end + end +end + +shared_examples "with ptable_type" do |result_scope| + let(:ptableType) { "gpt" } + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json[:ptableType]).to eq("gpt") + end +end + +shared_examples "with size" do |result_scope, config_scope| + let(:size) do + { + min: "1 GiB", + max: "10 GiB" + } + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json[:size]).to eq( + { + min: 1.GiB.to_i, + max: 10.GiB.to_i + } + ) + end + + context "if max size is unlimited" do + let(:size) do + { + min: "1 GiB" + } + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json[:size]).to eq( + { + min: 1.GiB.to_i + } + ) + end + end + + context "if size was solved" do + before do + size_config = config_scope.call(config).size + size_config.default = true + size_config.min = 5.GiB + size_config.max = 25.GiB + end + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json[:size]).to eq( + { + min: 5.GiB.to_i, + max: 25.GiB.to_i + } + ) + end + end +end + +shared_examples "with delete" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json[:delete]).to eq(true) + end +end + +shared_examples "with delete_if_needed" do |result_scope| + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + expect(config_json[:deleteIfNeeded]).to eq(true) + end +end + +shared_examples "with partitions" do |result_scope, config_scope| + let(:partitions) do + [ + partition, + { search: "/dev/vda2", alias: "vda2" } + ] + end + + let(:partition) { { search: "/dev/vda1", alias: "vda1" } } + + it "generates the expected JSON" do + config_json = result_scope.call(subject.convert) + partitions_json = config_json[:partitions] + + expect(partitions_json).to eq( + [ + { + search: { + condition: { name: "/dev/vda1" }, + ifNotFound: "error" + }, + alias: "vda1" + }, + { + search: { + condition: { name: "/dev/vda2" }, + ifNotFound: "error" + }, + alias: "vda2" + } + ] + ) + end + + partition_result_scope = proc { |c| result_scope.call(c)[:partitions].first } + partition_scope = proc { |c| config_scope.call(c).partitions.first } + + context "if #search is not configured for a partition" do + let(:partition) { { alias: "vda1" } } + include_examples "without search", partition_result_scope + end + + context "if #alias is not configured for a partition" do + let(:partition) { { search: "/dev/vda1" } } + include_examples "without alias", partition_result_scope + end + + context "if #id is not configured for a partition" do + let(:partition) { { search: "/dev/vda1" } } + + it "generates the expected JSON" do + config_json = partition_result_scope.call(subject.convert) + expect(config_json.keys).to_not include(:id) + end + end + + context "if #size is not configured for a partition" do + let(:partition) { { search: "/dev/vda1" } } + include_examples "without size", partition_result_scope + end + + context "if #encryption is not configured for a partition" do + let(:partition) { { search: "/dev/vda1" } } + include_examples "without encryption", partition_result_scope + end + + context "if #filesystem is not configured for a partition" do + let(:partition) { { search: "/dev/vda1" } } + include_examples "without filesystem", partition_result_scope + end + + context "if #delete is not configured for a partition" do + let(:partition) { { search: "/dev/vda1" } } + include_examples "without delete", partition_result_scope + end + + context "if #delete_if_needed is not configured for a partition" do + let(:partition) { { search: "/dev/vda1" } } + include_examples "without delete_if_needed", partition_result_scope + end + + context "if #search is configured for a partition" do + let(:partition) { { search: search } } + include_examples "with search", partition_result_scope + end + + context "if #alias is configured for a partition" do + let(:partition) { { alias: device_alias } } + include_examples "with alias", partition_result_scope + end + + context "if #id is configured for a partition" do + let(:partition) { { id: "esp" } } + + it "generates the expected JSON" do + config_json = partition_result_scope.call(subject.convert) + expect(config_json[:id]).to eq("esp") + end + end + + context "if #size is configured for a partition" do + let(:partition) { { size: size } } + include_examples "with size", partition_result_scope, partition_scope + end + + context "if #encryption is configured for a partition" do + let(:partition) { { encryption: encryption } } + include_examples "with encryption", partition_result_scope + end + + context "if #filesystem is configured for a partition" do + let(:partition) { { filesystem: filesystem } } + include_examples "with filesystem", partition_result_scope + end + + context "if #delete is configured for a partition" do + let(:partition) { { delete: true } } + include_examples "with delete", partition_result_scope + end + + context "if #delete_if_needed is configured for a partition" do + let(:partition) { { deleteIfNeeded: true } } + include_examples "with delete_if_needed", partition_result_scope + end +end + +describe Agama::Storage::ConfigConversions::ToJSON do + before do + # Speed up tests by avoding real check of TPM presence. + allow(Y2Storage::EncryptionMethod::TPM_FDE).to receive(:possible?).and_return(true) + end + + subject { described_class.new(config) } + + let(:config) do + Agama::Storage::ConfigConversions::FromJSON + .new(config_json) + .convert + end + + describe "#convert" do + let(:config_json) { {} } + + it "returns a Hash" do + expect(subject.convert).to be_a(Hash) + end + + context "with the default config" do + let(:config_json) { {} } + + it "generates the expected JSON" do + expect(subject.convert).to eq( + { + boot: { + configure: true + }, + drives: [], + volumeGroups: [] + } + ) + end + end + + context "if #boot is configured" do + let(:config_json) do + { + boot: { + configure: true, + device: "/dev/vdb" + } + } + end + + it "generates the expected JSON for 'boot'" do + boot_json = subject.convert[:boot] + expect(boot_json).to eq( + { + configure: true, + device: "/dev/vdb" + } + ) + end + end + + context "if #drives is configured" do + let(:config_json) do + { drives: drives } + end + + let(:drives) do + [ + drive, + {} + ] + end + + let(:drive) { {} } + + it "generates the expected JSON for 'drives'" do + drives_json = subject.convert[:drives] + + default_drive_json = { + search: { + ifNotFound: "error" + }, + partitions: [] + } + + expect(drives_json).to eq( + [ + default_drive_json, + default_drive_json + ] + ) + end + + drive_result_scope = proc { |c| c[:drives].first } + drive_scope = proc { |c| c.drives.first } + + context "if #search is not configured for a drive" do + let(:drive) { {} } + + it "generates the expected JSON for 'search'" do + drive_json = drive_result_scope.call(subject.convert) + search_json = drive_json[:search] + + expect(search_json).to eq( + { + ifNotFound: "error" + } + ) + end + end + + context "if #alias is not configured for a drive" do + let(:drive) { {} } + include_examples "without alias", drive_result_scope + end + + context "if #encryption is not configured for a drive" do + let(:drive) { {} } + include_examples "without encryption", drive_result_scope + end + + context "if #filesystem is not configured for a drive" do + let(:drive) { {} } + include_examples "without filesystem", drive_result_scope + end + + context "if #ptable_type is not configured for a drive" do + let(:drive) { {} } + include_examples "without ptable_type", drive_result_scope + end + + context "if #partitions is not configured for a drive" do + let(:drive) { {} } + include_examples "without partitions", drive_result_scope + end + + context "if #search is configured for a drive" do + let(:drive) { { search: search } } + include_examples "with search", drive_result_scope + end + + context "if #alias is configured for a drive" do + let(:drive) { { alias: device_alias } } + include_examples "with alias", drive_result_scope + end + + context "if #encryption is configured for a drive" do + let(:drive) { { encryption: encryption } } + include_examples "with encryption", drive_result_scope + end + + context "if #filesystem is configured for a drive" do + let(:drive) { { filesystem: filesystem } } + include_examples "with filesystem", drive_result_scope + end + + context "if #ptable_type is configured for a drive" do + let(:drive) { { ptableType: ptableType } } + include_examples "with ptable_type", drive_result_scope + end + + context "if #partitions is configured for a drive" do + let(:drive) { { partitions: partitions } } + include_examples "with partitions", drive_result_scope, drive_scope + end + end + + context "if #volume_groups is configured" do + let(:config_json) do + { volumeGroups: volume_groups } + end + + let(:volume_groups) do + [ + volume_group, + { name: "vg2" } + ] + end + + let(:volume_group) { { name: "vg1" } } + + it "generates the expected JSON" do + volume_groups_json = subject.convert[:volumeGroups] + expect(volume_groups_json).to eq( + [ + { + name: "vg1", + physicalVolumes: [], + logicalVolumes: [] + }, + { + name: "vg2", + physicalVolumes: [], + logicalVolumes: [] + } + ] + ) + end + + vg_result_scope = proc { |c| c[:volumeGroups].first } + vg_scope = proc { |c| c.volume_groups.first } + + context "if #name is not configured for a volume group" do + let(:volume_group) { {} } + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json.keys).to_not include(:name) + end + end + + context "if #extent_size is not configured for a volume group" do + let(:volume_group) { {} } + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json.keys).to_not include(:extentSize) + end + end + + context "if #physical_volumes is not configured for a volume group" do + let(:volume_group) { {} } + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json[:physicalVolumes]).to eq([]) + end + end + + context "if #logical_volumes is not configured for a volume group" do + let(:volume_group) { {} } + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json[:logicalVolumes]).to eq([]) + end + end + + context "if #name is configured for a volume group" do + let(:volume_group) { { name: "test" } } + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json[:name]).to eq("test") + end + end + + context "if #extent_size is configured for a volume group" do + let(:volume_group) { { extentSize: "4 KiB" } } + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json[:extentSize]).to eq(4.KiB.to_i) + end + end + + context "if #physical_volumes is configured for a volume group" do + let(:volume_group) { { physicalVolumes: ["pv1", "pv2"] } } + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json[:physicalVolumes]).to eq(["pv1", "pv2"]) + end + + context "and #physical_volumes_devices is configured" do + let(:volume_group) do + { + physicalVolumes: [ + "pv1", + "pv2", + { + generate: { + targetDevices: ["disk1"] + } + } + ] + } + end + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json[:physicalVolumes]).to eq( + [ + "pv1", + "pv2", + { generate: ["disk1"] } + ] + ) + end + + context "and #physical_volumes_encryption is configured" do + let(:volume_group) do + { + physicalVolumes: [ + "pv1", + "pv2", + { + generate: { + targetDevices: ["disk1"], + encryption: { + luks1: { password: "12345" } + } + } + } + ] + } + end + + it "generates the expected JSON" do + vg_json = vg_result_scope.call(subject.convert) + expect(vg_json[:physicalVolumes]).to eq( + [ + "pv1", + "pv2", + { + generate: { + targetDevices: ["disk1"], + encryption: { + luks1: { password: "12345" } + } + } + } + ] + ) + end + end + end + end + + context "if #logical_volumes is configured for a volume group" do + let(:volume_group) { { logicalVolumes: logical_volumes } } + + let(:logical_volumes) do + [ + logical_volume, + { name: "lv2" } + ] + end + + let(:logical_volume) { { name: "lv1" } } + + it "generates the expected JSON" do + config_json = vg_result_scope.call(subject.convert) + expect(config_json[:logicalVolumes]).to eq( + [ + { + name: "lv1", + pool: false + }, + { + name: "lv2", + pool: false + } + ] + ) + end + + lv_result_scope = proc { |c| vg_result_scope.call(c)[:logicalVolumes].first } + lv_scope = proc { |c| vg_scope.call(c).logical_volumes.first } + + context "if #name is not configured for a logical volume" do + let(:logical_volume) { {} } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json.keys).to_not include(:name) + end + end + + context "if #stripes is not configured for a logical volume" do + let(:logical_volume) { {} } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json.keys).to_not include(:stripes) + end + end + + context "if #stripe_size is not configured for a logical volume" do + let(:logical_volume) { {} } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json.keys).to_not include(:stripeSize) + end + end + + context "if #pool is not configured for a logical volume" do + let(:logical_volume) { {} } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json[:pool]).to eq(false) + end + end + + context "if #used_pool is not configured for a logical volume" do + let(:logical_volume) { {} } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json.keys).to_not include(:usedPool) + end + end + + context "if #alias is not configured for a logical volume" do + let(:logical_volume) { {} } + include_examples "without alias", lv_result_scope + end + + context "if #size is not configured for a logical volume" do + let(:logical_volume) { {} } + include_examples "without size", lv_result_scope + end + + context "if #encryption is not configured for a logical volume" do + let(:logical_volume) { {} } + include_examples "without encryption", lv_result_scope + end + + context "if #filesystem is not configured for a logical volume" do + let(:logical_volume) { {} } + include_examples "without filesystem", lv_result_scope + end + + context "if #stripes is configured for a logical volume" do + let(:logical_volume) { { stripes: 10 } } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json[:stripes]).to eq(10) + end + end + + context "if #stripe_size is configured for a logical volume" do + let(:logical_volume) { { stripeSize: "4 KiB" } } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json[:stripeSize]).to eq(4.KiB.to_i) + end + end + + context "if #pool is configured for a logical volume" do + let(:logical_volume) { { pool: true } } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json[:pool]).to eq(true) + end + end + + context "if #used_pool is configured for a logical volume" do + let(:logical_volume) { { usedPool: "pool" } } + + it "generates the expected JSON" do + lv_json = lv_result_scope.call(subject.convert) + expect(lv_json[:usedPool]).to eq("pool") + end + end + + context "if #alias is configured for a logical volume" do + let(:logical_volume) { { alias: device_alias } } + include_examples "with alias", lv_result_scope + end + + context "if #size is configured for a logical volume" do + let(:logical_volume) { { size: size } } + include_examples "with size", lv_result_scope, lv_scope + end + + context "if #encryption is configured for a logical volume" do + let(:logical_volume) { { encryption: encryption } } + include_examples "with encryption", lv_result_scope + end + + context "if #filesystem is configured for a logical volume" do + let(:logical_volume) { { filesystem: filesystem } } + include_examples "with filesystem", lv_result_scope + end + end + end + end +end From 12fb25dbaf0722467112d053dffbb74259b4875e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Wed, 16 Oct 2024 15:37:57 +0100 Subject: [PATCH 3/3] service: changelog --- service/package/rubygem-agama-yast.changes | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/service/package/rubygem-agama-yast.changes b/service/package/rubygem-agama-yast.changes index 68d9fe4acc..17ec461878 100644 --- a/service/package/rubygem-agama-yast.changes +++ b/service/package/rubygem-agama-yast.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Oct 16 14:35:47 UTC 2024 - José Iván López González + +- Storage: add config conversion to JSON + (gh#agama-project/agama#1670). + ------------------------------------------------------------------- Mon Oct 14 14:52:26 UTC 2024 - Ladislav Slezák