d-installer 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +29 -0
- data/Gemfile.lock +75 -0
- data/bin/d-installer +74 -0
- data/etc/d-installer.yaml +284 -0
- data/lib/dinstaller/can_ask_question.rb +48 -0
- data/lib/dinstaller/cmdline_args.rb +80 -0
- data/lib/dinstaller/cockpit_manager.rb +176 -0
- data/lib/dinstaller/config.rb +128 -0
- data/lib/dinstaller/config_reader.rb +164 -0
- data/lib/dinstaller/dbus/base_object.rb +58 -0
- data/lib/dinstaller/dbus/clients/base.rb +71 -0
- data/lib/dinstaller/dbus/clients/language.rb +86 -0
- data/lib/dinstaller/dbus/clients/manager.rb +76 -0
- data/lib/dinstaller/dbus/clients/software.rb +185 -0
- data/lib/dinstaller/dbus/clients/users.rb +112 -0
- data/lib/dinstaller/dbus/clients/with_progress.rb +56 -0
- data/lib/dinstaller/dbus/clients/with_service_status.rb +75 -0
- data/lib/dinstaller/dbus/clients.rb +34 -0
- data/lib/dinstaller/dbus/interfaces/progress.rb +113 -0
- data/lib/dinstaller/dbus/interfaces/service_status.rb +89 -0
- data/lib/dinstaller/dbus/language.rb +93 -0
- data/lib/dinstaller/dbus/language_service.rb +92 -0
- data/lib/dinstaller/dbus/manager.rb +147 -0
- data/lib/dinstaller/dbus/manager_service.rb +132 -0
- data/lib/dinstaller/dbus/question.rb +176 -0
- data/lib/dinstaller/dbus/questions.rb +124 -0
- data/lib/dinstaller/dbus/service_runner.rb +97 -0
- data/lib/dinstaller/dbus/service_status.rb +87 -0
- data/lib/dinstaller/dbus/software/manager.rb +131 -0
- data/lib/dinstaller/dbus/software/proposal.rb +82 -0
- data/lib/dinstaller/dbus/software.rb +31 -0
- data/lib/dinstaller/dbus/software_service.rb +86 -0
- data/lib/dinstaller/dbus/storage/proposal.rb +170 -0
- data/lib/dinstaller/dbus/storage.rb +30 -0
- data/lib/dinstaller/dbus/users.rb +132 -0
- data/lib/dinstaller/dbus/users_service.rb +92 -0
- data/lib/dinstaller/dbus/with_service_status.rb +48 -0
- data/lib/dinstaller/dbus/y2dir/manager/modules/Package.rb +51 -0
- data/lib/dinstaller/dbus/y2dir/manager/modules/PackagesProposal.rb +62 -0
- data/lib/dinstaller/dbus/y2dir/modules/Autologin.rb +214 -0
- data/lib/dinstaller/dbus/y2dir/software/modules/SpaceCalculation.rb +44 -0
- data/lib/dinstaller/dbus.rb +11 -0
- data/lib/dinstaller/errors.rb +28 -0
- data/lib/dinstaller/installation_phase.rb +106 -0
- data/lib/dinstaller/language.rb +73 -0
- data/lib/dinstaller/luks_activation_question.rb +92 -0
- data/lib/dinstaller/manager.rb +261 -0
- data/lib/dinstaller/network.rb +53 -0
- data/lib/dinstaller/package_callbacks.rb +69 -0
- data/lib/dinstaller/progress.rb +149 -0
- data/lib/dinstaller/question.rb +103 -0
- data/lib/dinstaller/questions_manager.rb +145 -0
- data/lib/dinstaller/security.rb +96 -0
- data/lib/dinstaller/service_status_recorder.rb +58 -0
- data/lib/dinstaller/software.rb +232 -0
- data/lib/dinstaller/storage/actions.rb +90 -0
- data/lib/dinstaller/storage/callbacks/activate.rb +93 -0
- data/lib/dinstaller/storage/callbacks/activate_luks.rb +93 -0
- data/lib/dinstaller/storage/callbacks/activate_multipath.rb +77 -0
- data/lib/dinstaller/storage/callbacks.rb +30 -0
- data/lib/dinstaller/storage/manager.rb +104 -0
- data/lib/dinstaller/storage/proposal.rb +197 -0
- data/lib/dinstaller/storage.rb +30 -0
- data/lib/dinstaller/users.rb +156 -0
- data/lib/dinstaller/with_progress.rb +63 -0
- data/lib/dinstaller.rb +5 -0
- data/share/dbus.conf +38 -0
- data/share/dbus.service +5 -0
- data/share/systemd.service +14 -0
- metadata +295 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) [2022] SUSE LLC
|
4
|
+
#
|
5
|
+
# All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or modify it
|
8
|
+
# under the terms of version 2 of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
13
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
14
|
+
# more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with this program; if not, contact SUSE LLC.
|
18
|
+
#
|
19
|
+
# To contact SUSE LLC about this file by physical or electronic mail, you may
|
20
|
+
# find current contact information at www.suse.com.
|
21
|
+
|
22
|
+
require "yast"
|
23
|
+
require "yast2/systemd/service"
|
24
|
+
require "cfa/base_model"
|
25
|
+
require "transfer/file_from_url"
|
26
|
+
require "fileutils"
|
27
|
+
|
28
|
+
Yast.import "URL"
|
29
|
+
|
30
|
+
module DInstaller
|
31
|
+
# Cockpit configuration file representation
|
32
|
+
#
|
33
|
+
# @example Set the AllowUnencrypted option
|
34
|
+
# file = CockpitConfig.new
|
35
|
+
# file.load
|
36
|
+
# file.web_service["AllowUnencrypted"] = "true"
|
37
|
+
# file.save
|
38
|
+
class CockpitConfig < CFA::BaseModel
|
39
|
+
# Constructor
|
40
|
+
#
|
41
|
+
# @param path [String] File path
|
42
|
+
# @param file_handler [.read, .write] Object to read/write the file.
|
43
|
+
def initialize(path: DEFAULT_PATH, file_handler: nil)
|
44
|
+
super(CFA::AugeasParser.new("puppet.lns"), path, file_handler: file_handler)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the augeas tree for the "WebService" section
|
48
|
+
#
|
49
|
+
# If the given section does not exist, it returns an empty one
|
50
|
+
#
|
51
|
+
# @param name [String] section name
|
52
|
+
# @return [AugeasTree]
|
53
|
+
def web_service
|
54
|
+
data["WebService"] ||= CFA::AugeasTree.new
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handles the Cockpit service
|
59
|
+
#
|
60
|
+
# This API offers an API to adjust Cockpit configuration and restart/reload the process. At this
|
61
|
+
# point, just a few options are allowed (@see #setup).
|
62
|
+
class CockpitManager
|
63
|
+
include Yast::Logger
|
64
|
+
include Yast::Transfer::FileFromUrl
|
65
|
+
include Yast::I18n
|
66
|
+
|
67
|
+
# Directory to store Cockpit certificates
|
68
|
+
WS_CERTS_DIR = "/etc/cockpit/ws-certs.d"
|
69
|
+
COCKPIT_SERVICE = "cockpit"
|
70
|
+
COCKPIT_CONF_PATH = "/etc/cockpit/cockpit.conf"
|
71
|
+
|
72
|
+
def initialize(logger, prefix: "/")
|
73
|
+
@prefix = prefix
|
74
|
+
@logger = logger
|
75
|
+
end
|
76
|
+
|
77
|
+
# Adjust Cockpit configuration and restart the process if needed
|
78
|
+
#
|
79
|
+
# If all arguments are nil, the configuration is not modified and the process is not restarted.
|
80
|
+
#
|
81
|
+
# @param config [Hash]
|
82
|
+
# @option ssl [Boolean,nil] SSL is enabled
|
83
|
+
# @option ssl_cert [String,nil] SSL/TLS certificate URL
|
84
|
+
# @option ssl_key [String,nil] SSL/TLS key URL
|
85
|
+
def setup(options)
|
86
|
+
return if options.values.all?(&:nil?)
|
87
|
+
|
88
|
+
enable_ssl(options["ssl"]) unless options["ssl"].nil?
|
89
|
+
if options["ssl_cert"]
|
90
|
+
copy_ssl_cert(options["ssl_cert"])
|
91
|
+
copy_ssl_key(options["ssl_key"]) unless options["ssl_key"].nil?
|
92
|
+
clear_self_signed_cert
|
93
|
+
end
|
94
|
+
|
95
|
+
restart_cockpit
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
attr_reader :prefix
|
101
|
+
|
102
|
+
# @return [Logger]
|
103
|
+
attr_reader :logger
|
104
|
+
|
105
|
+
# Enable/Disable SSL
|
106
|
+
#
|
107
|
+
# @param enabled [Boolean] Whether to enable or disable SSL
|
108
|
+
def enable_ssl(enabled)
|
109
|
+
path = File.join(prefix, COCKPIT_CONF_PATH)
|
110
|
+
config = CockpitConfig.new(path: path)
|
111
|
+
config.load if File.readable?(path)
|
112
|
+
config.web_service["AllowUnencrypted"] = (!enabled).to_s
|
113
|
+
config.save
|
114
|
+
end
|
115
|
+
|
116
|
+
# Copy the SSL certificate to Cockpit's certificates directory
|
117
|
+
#
|
118
|
+
# The certificate is renamed as `0-d-installer.cert`.
|
119
|
+
#
|
120
|
+
# @param location [String] Certificate location
|
121
|
+
def copy_ssl_cert(location)
|
122
|
+
logger.info "Retrieving SSL certificate from #{location}"
|
123
|
+
copy_file(location, File.join(prefix, WS_CERTS_DIR, "0-d-installer.cert"))
|
124
|
+
end
|
125
|
+
|
126
|
+
# Copy the SSL certificate key to Cockpit's certificates directory
|
127
|
+
#
|
128
|
+
# The certificate is renamed as `0-d-installer.key`.
|
129
|
+
#
|
130
|
+
# @param location [String] Certificate key location
|
131
|
+
def copy_ssl_key(location)
|
132
|
+
logger.info "Retrieving SSL key from #{location}"
|
133
|
+
copy_file(location, File.join(prefix, WS_CERTS_DIR, "0-d-installer.key"))
|
134
|
+
end
|
135
|
+
|
136
|
+
# Remove Cockpit's self signed certificates if they exist
|
137
|
+
def clear_self_signed_cert
|
138
|
+
self_signed = Dir[File.join(prefix, WS_CERTS_DIR, "0-self-signed.*")]
|
139
|
+
::FileUtils.rm(self_signed)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Copy a file from a potentially remote location
|
143
|
+
#
|
144
|
+
# @param location [String] File location. It might be an URL-like string (e.g.,
|
145
|
+
# "http://example.net/example.cert").
|
146
|
+
# @param target [String] Path to copy the file to.
|
147
|
+
# @return [Boolean] Whether the file was sucessfully copied or not
|
148
|
+
def copy_file(location, target)
|
149
|
+
url = Yast::URL.Parse(location)
|
150
|
+
|
151
|
+
res = get_file_from_url(
|
152
|
+
scheme: url["scheme"],
|
153
|
+
host: url["host"],
|
154
|
+
urlpath: url["path"],
|
155
|
+
localfile: target,
|
156
|
+
urltok: url,
|
157
|
+
destdir: "/"
|
158
|
+
)
|
159
|
+
# TODO: exception?
|
160
|
+
logger.error "script #{location} could not be retrieved" unless res
|
161
|
+
res
|
162
|
+
end
|
163
|
+
|
164
|
+
# Restart the Cockpit service
|
165
|
+
def restart_cockpit
|
166
|
+
logger.info "Restarting Cockpit"
|
167
|
+
service = Yast2::Systemd::Service.find(COCKPIT_SERVICE)
|
168
|
+
if service.nil?
|
169
|
+
logger.error "Could not found #{COCKPIT_SERVICE} service"
|
170
|
+
return
|
171
|
+
end
|
172
|
+
|
173
|
+
service.restart
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) [2022] SUSE LLC
|
4
|
+
#
|
5
|
+
# All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or modify it
|
8
|
+
# under the terms of version 2 of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
13
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
14
|
+
# more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with this program; if not, contact SUSE LLC.
|
18
|
+
#
|
19
|
+
# To contact SUSE LLC about this file by physical or electronic mail, you may
|
20
|
+
# find current contact information at www.suse.com.
|
21
|
+
|
22
|
+
require "yaml"
|
23
|
+
require "dinstaller/config_reader"
|
24
|
+
|
25
|
+
module DInstaller
|
26
|
+
# Class responsible for getting current configuration.
|
27
|
+
# It is smarter then just plain yaml reader as it also evaluates
|
28
|
+
# conditions in it, so it is result of all conditions in file.
|
29
|
+
# This also means that config needs to be re-evaluated if conditions
|
30
|
+
# data change, like if user pick different distro to install.
|
31
|
+
class Config
|
32
|
+
# @return [Hash] configuration data
|
33
|
+
attr_accessor :pure_data
|
34
|
+
|
35
|
+
class << self
|
36
|
+
attr_accessor :current, :base
|
37
|
+
|
38
|
+
# Loads base and current config reading configuration from the system
|
39
|
+
def load(logger = Logger.new($stdout))
|
40
|
+
@base = ConfigReader.new(logger: logger).config
|
41
|
+
@current = @base&.copy
|
42
|
+
end
|
43
|
+
|
44
|
+
# It resets the configuration internal state
|
45
|
+
def reset
|
46
|
+
@base = nil
|
47
|
+
@current = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
# Load the configuration from a given file
|
51
|
+
#
|
52
|
+
# @param path [String|Pathname] File path
|
53
|
+
def from_file(path)
|
54
|
+
new(YAML.safe_load(File.read(path.to_s)))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Constructor
|
59
|
+
#
|
60
|
+
# @param config_data [Hash] configuration data
|
61
|
+
def initialize(config_data = nil)
|
62
|
+
@pure_data = config_data
|
63
|
+
end
|
64
|
+
|
65
|
+
# parse loaded yaml file, so it properly applies conditions
|
66
|
+
# with default options it load file without conditions
|
67
|
+
def parse_file(_arch = nil, _distro = nil)
|
68
|
+
# TODO: move to internal only. public one should be something
|
69
|
+
# like evaluate or just setter for distro and arch
|
70
|
+
# logger.info "parse file with #{arch} and #{distro}"
|
71
|
+
# TODO: do real evaluation of conditions
|
72
|
+
data
|
73
|
+
end
|
74
|
+
|
75
|
+
def data
|
76
|
+
return @data if @data
|
77
|
+
|
78
|
+
@data = @pure_data.dup || {}
|
79
|
+
pick_product(@data["products"].keys.first) if @data["products"]
|
80
|
+
@data
|
81
|
+
end
|
82
|
+
|
83
|
+
def pick_product(product)
|
84
|
+
data.merge!(data[product])
|
85
|
+
end
|
86
|
+
|
87
|
+
# Whether there are more than one product
|
88
|
+
#
|
89
|
+
# @return [Boolean] false if there is only one product; true otherwise
|
90
|
+
def multi_product?
|
91
|
+
data["products"].size > 1
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns a copy of this Object
|
95
|
+
#
|
96
|
+
# @return [Config]
|
97
|
+
def copy
|
98
|
+
Marshal.load(Marshal.dump(self))
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns a new {Config} with the merge of the given ones
|
102
|
+
#
|
103
|
+
# @params config [Config, Hash]
|
104
|
+
# @return [Config] new Configuration with the merge of the given ones
|
105
|
+
def merge(config)
|
106
|
+
Config.new(simple_merge(data, config.data))
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# Simple deep merge
|
112
|
+
#
|
113
|
+
# @param a_hash [Hash] Default values
|
114
|
+
# @param another_hash [Hash] Pillar data
|
115
|
+
# @return [Hash]
|
116
|
+
def simple_merge(a_hash, another_hash)
|
117
|
+
a_hash.reduce({}) do |all, (k, v)|
|
118
|
+
next all.merge(k => v) if another_hash[k].nil?
|
119
|
+
|
120
|
+
if v.is_a?(Hash)
|
121
|
+
all.merge(k => simple_merge(a_hash[k], another_hash[k]))
|
122
|
+
else
|
123
|
+
all.merge(k => another_hash[k])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) [2022] SUSE LLC
|
4
|
+
#
|
5
|
+
# All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or modify it
|
8
|
+
# under the terms of version 2 of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
13
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
14
|
+
# more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with this program; if not, contact SUSE LLC.
|
18
|
+
#
|
19
|
+
# To contact SUSE LLC about this file by physical or electronic mail, you may
|
20
|
+
# find current contact information at www.suse.com.
|
21
|
+
|
22
|
+
require "yast"
|
23
|
+
require "yaml"
|
24
|
+
require "logger"
|
25
|
+
require "dinstaller/config"
|
26
|
+
require "dinstaller/cmdline_args"
|
27
|
+
require "transfer/file_from_url"
|
28
|
+
|
29
|
+
Yast.import "URL"
|
30
|
+
Yast.import "Directory"
|
31
|
+
|
32
|
+
module DInstaller
|
33
|
+
# This class is responsible for reading DInstaller configuration from different locations
|
34
|
+
# including kernel cmdline options
|
35
|
+
class ConfigReader
|
36
|
+
include Yast::Transfer::FileFromUrl
|
37
|
+
include Yast::I18n
|
38
|
+
|
39
|
+
# Default DInstaller configuration which should define all the possible values
|
40
|
+
SYSTEM_PATH = "/etc/d-installer.yaml"
|
41
|
+
GIT_PATH = File.expand_path("#{__dir__}/../../etc/d-installer.yaml")
|
42
|
+
REMOTE_BOOT_CONFIG = "d-installer_boot.yaml"
|
43
|
+
|
44
|
+
PATHS = [
|
45
|
+
"/usr/lib/d-installer.d",
|
46
|
+
"/etc/d-installer.d",
|
47
|
+
"/run/d-installer.d"
|
48
|
+
].freeze
|
49
|
+
|
50
|
+
attr_reader :logger
|
51
|
+
attr_reader :workdir
|
52
|
+
|
53
|
+
# Constructor
|
54
|
+
#
|
55
|
+
# @param logger [Logger]
|
56
|
+
# @param workdir [String] Root directory to read the configuration from
|
57
|
+
def initialize(logger: nil, workdir: "/")
|
58
|
+
@logger = logger || ::Logger.new($stdout)
|
59
|
+
@workdir = workdir
|
60
|
+
end
|
61
|
+
|
62
|
+
# loads correct yaml file
|
63
|
+
def config_from_file(path = nil)
|
64
|
+
raise "Missing config file at #{path}" unless File.exist?(path)
|
65
|
+
|
66
|
+
Config.from_file(path)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return an {Array} with the different {Config} objects read from the different locations
|
70
|
+
#
|
71
|
+
# TODO: handle precedence correctly
|
72
|
+
#
|
73
|
+
# @returm [Array<Config>] an array with all the configurations read from the system
|
74
|
+
def configs
|
75
|
+
return @configs if @configs
|
76
|
+
|
77
|
+
@configs = config_paths.map { |path| config_from_file(path) }
|
78
|
+
@configs << remote_config if remote_config
|
79
|
+
@configs << cmdline_config if cmdline_config
|
80
|
+
@configs
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return a {Config} oject
|
84
|
+
# @return [Config] resultant Config after merging all the configurations
|
85
|
+
def config
|
86
|
+
config = configs.first || Config.new
|
87
|
+
(configs[1..-1] || []).each { |c| config = config.merge(c) }
|
88
|
+
config
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# Copy a file from a potentially remote location
|
94
|
+
#
|
95
|
+
# @param location [String] File location. It might be an URL-like string (e.g.,
|
96
|
+
# "http://example.net/example.yml").
|
97
|
+
# @param target [String] Path to copy the file to.
|
98
|
+
# @return [Boolean] Whether the file was sucessfully copied or not
|
99
|
+
def copy_file(location, target)
|
100
|
+
url = Yast::URL.Parse(location)
|
101
|
+
|
102
|
+
res = get_file_from_url(
|
103
|
+
scheme: url["scheme"],
|
104
|
+
host: url["host"],
|
105
|
+
urlpath: url["path"],
|
106
|
+
localfile: target,
|
107
|
+
urltok: url,
|
108
|
+
destdir: "/"
|
109
|
+
)
|
110
|
+
|
111
|
+
# TODO: exception?
|
112
|
+
logger.error "script #{location} could not be retrieved" unless res
|
113
|
+
res
|
114
|
+
end
|
115
|
+
|
116
|
+
# @return [CmdlineArgs]
|
117
|
+
def cmdline_args
|
118
|
+
@cmdline_args ||= CmdlineArgs.read_from(File.join(workdir, "/proc/cmdline"))
|
119
|
+
end
|
120
|
+
|
121
|
+
# return [Config]
|
122
|
+
def cmdline_config
|
123
|
+
Config.new(cmdline_args.data)
|
124
|
+
end
|
125
|
+
|
126
|
+
# return [Config]
|
127
|
+
def remote_config
|
128
|
+
return unless cmdline_args.config_url
|
129
|
+
|
130
|
+
file_path = File.join(Yast::Directory.tmpdir, REMOTE_BOOT_CONFIG)
|
131
|
+
logger.info "Copying boot config to #{file_path}"
|
132
|
+
|
133
|
+
copy_file(cmdline_args.config_url, file_path)
|
134
|
+
config_from_file(file_path)
|
135
|
+
end
|
136
|
+
|
137
|
+
def default_path
|
138
|
+
File.exist?(GIT_PATH) ? GIT_PATH : SYSTEM_PATH
|
139
|
+
end
|
140
|
+
|
141
|
+
def config_paths
|
142
|
+
paths = PATHS.each_with_object([]) do |path, all|
|
143
|
+
all.concat(file_paths_in(File.join(workdir, path)))
|
144
|
+
end
|
145
|
+
|
146
|
+
paths.uniq! { |f| File.basename(f) }
|
147
|
+
# Sort files lexicographic
|
148
|
+
paths.sort_by! { |f| File.basename(f) }
|
149
|
+
paths.prepend(default_path)
|
150
|
+
|
151
|
+
paths
|
152
|
+
end
|
153
|
+
|
154
|
+
def file_paths_in(path)
|
155
|
+
if File.file?(path)
|
156
|
+
[path]
|
157
|
+
elsif File.directory?(path)
|
158
|
+
Dir.glob("#{path}/*.{yml,yaml}")
|
159
|
+
else
|
160
|
+
[]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) [2021] SUSE LLC
|
4
|
+
#
|
5
|
+
# All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or modify it
|
8
|
+
# under the terms of version 2 of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
13
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
14
|
+
# more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with this program; if not, contact SUSE LLC.
|
18
|
+
#
|
19
|
+
# To contact SUSE LLC about this file by physical or electronic mail, you may
|
20
|
+
# find current contact information at www.suse.com.
|
21
|
+
|
22
|
+
require "dbus"
|
23
|
+
|
24
|
+
module DInstaller
|
25
|
+
module DBus
|
26
|
+
# Base class for DBus objects
|
27
|
+
class BaseObject < ::DBus::Object
|
28
|
+
# Constructor
|
29
|
+
#
|
30
|
+
# @param path [::DBus::ObjectPath]
|
31
|
+
# @param logger [Logger, nil]
|
32
|
+
def initialize(path, logger: nil)
|
33
|
+
@logger = logger || Logger.new($stdout)
|
34
|
+
super(path)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Generates information about interfaces and properties of the object
|
38
|
+
#
|
39
|
+
# Returns a hash containing interfaces names as keys. Each value is the same hash that would
|
40
|
+
# be returned by the org.freedesktop.DBus.Properties.GetAll() method for that combination of
|
41
|
+
# object path and interface. If an interface has no properties, the empty hash is returned.
|
42
|
+
#
|
43
|
+
# @return [Hash]
|
44
|
+
def interfaces_and_properties
|
45
|
+
get_all_method = self.class.make_method_name("org.freedesktop.DBus.Properties", :GetAll)
|
46
|
+
|
47
|
+
intfs.keys.each_with_object({}) do |interface, hash|
|
48
|
+
hash[interface] = public_send(get_all_method, interface).first
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# @return [Logger]
|
55
|
+
attr_reader :logger
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) [2022] SUSE LLC
|
4
|
+
#
|
5
|
+
# All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or modify it
|
8
|
+
# under the terms of version 2 of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
13
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
14
|
+
# more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with this program; if not, contact SUSE LLC.
|
18
|
+
#
|
19
|
+
# To contact SUSE LLC about this file by physical or electronic mail, you may
|
20
|
+
# find current contact information at www.suse.com.
|
21
|
+
|
22
|
+
require "dbus"
|
23
|
+
require "abstract_method"
|
24
|
+
|
25
|
+
module DInstaller
|
26
|
+
module DBus
|
27
|
+
module Clients
|
28
|
+
# Base class for D-Bus clients
|
29
|
+
class Base
|
30
|
+
# @!method service_name
|
31
|
+
# Name of the D-Bus service
|
32
|
+
# @return [String]
|
33
|
+
abstract_method :service_name
|
34
|
+
|
35
|
+
# D-Bus service
|
36
|
+
#
|
37
|
+
# @return [::DBus::Service]
|
38
|
+
def service
|
39
|
+
@service ||= bus.service(service_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Registers callback to be called when the properties of the given object changes
|
45
|
+
#
|
46
|
+
# @note Signal subscription is done only once. Otherwise, the latest subscription overrides
|
47
|
+
# the previous one.
|
48
|
+
#
|
49
|
+
# @param dbus_object [::DBus::Object]
|
50
|
+
# @param block [Proc]
|
51
|
+
def on_properties_change(dbus_object, &block)
|
52
|
+
@on_properties_change_callbacks ||= {}
|
53
|
+
@on_properties_change_callbacks[dbus_object.path] ||= []
|
54
|
+
@on_properties_change_callbacks[dbus_object.path] << block
|
55
|
+
|
56
|
+
return if @on_properties_change_callbacks[dbus_object.path].size > 1
|
57
|
+
|
58
|
+
dbus_properties = dbus_object["org.freedesktop.DBus.Properties"]
|
59
|
+
dbus_properties.on_signal("PropertiesChanged") do |interface, changes, invalid|
|
60
|
+
callbacks = @on_properties_change_callbacks[dbus_object.path]
|
61
|
+
callbacks.each { |c| c.call(interface, changes, invalid) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def bus
|
66
|
+
@bus ||= ::DBus::SystemBus.instance
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) [2022] SUSE LLC
|
4
|
+
#
|
5
|
+
# All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or modify it
|
8
|
+
# under the terms of version 2 of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
12
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
13
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
14
|
+
# more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License along
|
17
|
+
# with this program; if not, contact SUSE LLC.
|
18
|
+
#
|
19
|
+
# To contact SUSE LLC about this file by physical or electronic mail, you may
|
20
|
+
# find current contact information at www.suse.com.
|
21
|
+
|
22
|
+
require "dinstaller/dbus/clients/base"
|
23
|
+
|
24
|
+
module DInstaller
|
25
|
+
module DBus
|
26
|
+
module Clients
|
27
|
+
# D-Bus client for language configuration
|
28
|
+
class Language < Base
|
29
|
+
def initialize
|
30
|
+
super
|
31
|
+
|
32
|
+
@dbus_object = service.object("/org/opensuse/DInstaller/Language1")
|
33
|
+
@dbus_object.introspect
|
34
|
+
end
|
35
|
+
|
36
|
+
def service_name
|
37
|
+
@service_name ||= "org.opensuse.DInstaller.Language"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Available languages for the installation
|
41
|
+
#
|
42
|
+
# @return [Array<Array<String, String>>] id and name of each language
|
43
|
+
def available_languages
|
44
|
+
dbus_object["org.opensuse.DInstaller.Language1"]["AvailableLanguages"].map { |l| l[0..1] }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Languages selected to install
|
48
|
+
#
|
49
|
+
# @return [Array<String>] ids of the languages
|
50
|
+
def selected_languages
|
51
|
+
dbus_object["org.opensuse.DInstaller.Language1"]["MarkedForInstall"]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Selects the languages to install
|
55
|
+
#
|
56
|
+
# @param ids [Array<String>]
|
57
|
+
def select_languages(ids)
|
58
|
+
dbus_object.ToInstall(ids)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Finishes the language installation
|
62
|
+
def finish
|
63
|
+
dbus_object.Finish
|
64
|
+
end
|
65
|
+
|
66
|
+
# Registers a callback to run when the language changes
|
67
|
+
#
|
68
|
+
# @note Signal subscription is done only once. Otherwise, the latest subscription overrides
|
69
|
+
# the previous one.
|
70
|
+
#
|
71
|
+
# @param block [Proc] Callback to run when a language is selected
|
72
|
+
def on_language_selected(&block)
|
73
|
+
on_properties_change(dbus_object) do |_, changes, _|
|
74
|
+
languages = changes["MarkedForInstall"]
|
75
|
+
block.call(languages)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# @return [::DBus::Object]
|
82
|
+
attr_reader :dbus_object
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|