Class: OsCtld::DistConfig::Distributions::Base
- Inherits:
-
Object
- Object
- OsCtld::DistConfig::Distributions::Base
- Includes:
- OsCtl::Lib::Utils::Log, OsCtl::Lib::Utils::System, Utils::SwitchUser
- Defined in:
- lib/osctld/dist_config/distributions/base.rb
Instance Attribute Summary collapse
-
#configurator ⇒ Object
readonly
protected
Returns the value of attribute configurator.
-
#ct ⇒ Object
readonly
Returns the value of attribute ct.
-
#ctrc ⇒ Object
readonly
Returns the value of attribute ctrc.
-
#distribution ⇒ Object
readonly
Returns the value of attribute distribution.
-
#version ⇒ Object
readonly
Returns the value of attribute version.
Class Method Summary collapse
Instance Method Summary collapse
-
#add_netif(opts) ⇒ Object
Called when a new network interface is added to a container.
-
#apply_hostname ⇒ Object
Configure hostname in a running system.
-
#bin_path(_opts) ⇒ String
Return path to
/binor an alternative, where a shell is looked up. - #chpasswd_command ⇒ Object
- #configurator_class ⇒ Object
- #dns_resolvers(opts = {}) ⇒ Object
-
#initialize(ctrc) ⇒ Base
constructor
A new instance of Base.
- #log_type ⇒ Object
- #network(_opts = {}) ⇒ Object
- #passwd(opts) ⇒ Object
-
#post_mount(opts) ⇒ Object
Called by LXC post-mount hook on container start.
-
#pre_start(_opts = {}) ⇒ Object
Called before container start, can be used to e.g.
- #reload_network_manager_dns ⇒ Object protected
-
#remove_netif(opts) ⇒ Object
Called when a network interface is removed from a container.
-
#rename_netif(opts) ⇒ Object
Called when an existing network interface is renamed.
-
#set_hostname(opts = {}) ⇒ Object
Set container hostname.
-
#start(_opts = {}) ⇒ Object
Run just before the container is started.
-
#stop(opts) ⇒ Object
Gracefully stop the container.
- #unset_dns_resolvers(_opts = {}) ⇒ Object
-
#unset_etc_hosts(_opts = {}) ⇒ Object
Remove the osctld-generated notice from /etc/hosts.
-
#update_etc_hosts(opts = {}) ⇒ Object
Update hostname in
/etc/hosts, optionally removing configuration of old hostname. -
#volatile_is_systemd? ⇒ Boolean
protected
Check if the container is using systemd as init.
- #with_rootfs(&block) ⇒ Object protected
Methods included from Utils::SwitchUser
Constructor Details
#initialize(ctrc) ⇒ Base
Returns a new instance of Base.
21 22 23 24 25 26 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 21 def initialize(ctrc) @ctrc = ctrc @ct = ctrc.ct @distribution = ctrc.distribution @version = ctrc.version end |
Instance Attribute Details
#configurator ⇒ Object (readonly, protected)
Returns the value of attribute configurator.
263 264 265 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 263 def configurator @configurator end |
#ct ⇒ Object (readonly)
Returns the value of attribute ct.
18 19 20 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 18 def ct @ct end |
#ctrc ⇒ Object (readonly)
Returns the value of attribute ctrc.
18 19 20 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 18 def ctrc @ctrc end |
#distribution ⇒ Object (readonly)
Returns the value of attribute distribution.
18 19 20 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 18 def distribution @distribution end |
#version ⇒ Object (readonly)
Returns the value of attribute version.
18 19 20 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 18 def version @version end |
Class Method Details
.distribution(n = nil) ⇒ Object
10 11 12 13 14 15 16 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 10 def self.distribution(n = nil) if n DistConfig.register(n, self) else n end end |
Instance Method Details
#add_netif(opts) ⇒ Object
Called when a new network interface is added to a container
185 186 187 188 189 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 185 def add_netif(opts) with_rootfs do configurator.add_netif(ct.netifs, opts[:netif]) end end |
#apply_hostname ⇒ Object
Configure hostname in a running system
154 155 156 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 154 def apply_hostname log(:warn, ct, "Unable to apply hostname on #{distribution}: not implemented") end |
#bin_path(_opts) ⇒ String
Return path to /bin or an alternative, where a shell is looked up
249 250 251 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 249 def bin_path(_opts) '/bin' end |
#chpasswd_command ⇒ Object
253 254 255 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 253 def chpasswd_command %w[chpasswd] end |
#configurator_class ⇒ Object
28 29 30 31 32 33 34 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 28 def configurator_class raise "define #{self.class}#configurator_class" unless self.class.const_defined?(:Configurator) cls = self.class::Configurator log(:debug, "Using #{cls} for #{ctrc.distribution}") cls end |
#dns_resolvers(opts = {}) ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 210 def dns_resolvers(opts = {}) resolvers = opts.fetch(:resolvers) { ct.dns_resolvers } # The start hook is already inside a rootfs helper, before guest init. # Live reload must run from the host frontend, not from that chroot. if @within_rootfs || !ct.running? with_rootfs { configurator.dns_resolvers(resolvers) } return end reload = with_rootfs { configurator.prepare_dns_resolvers } reload_network_manager_dns if reload with_rootfs { configurator.write_dns_resolvers(resolvers) } end |
#log_type ⇒ Object
257 258 259 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 257 def log_type ct.id end |
#network(_opts = {}) ⇒ Object
176 177 178 179 180 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 176 def network(_opts = {}) with_rootfs do configurator.network(ct.netifs) end end |
#passwd(opts) ⇒ Object
233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 233 def passwd(opts) ret = ct_syscmd( ct, chpasswd_command, stdin: "#{opts[:user]}:#{opts[:password]}\n", run: true, valid_rcs: :all ) return true if ret.success? log(:warn, ct, "Unable to set password: #{ret.output}") end |
#post_mount(opts) ⇒ Object
Called by LXC post-mount hook on container start
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 80 def post_mount(opts) return unless volatile_is_systemd? ContainerControl::Commands::WithMountns.run!( ct, ns_pid: opts[:ns_pid], mnt_ns: opts[:mnt_ns], chroot: opts[:rootfs_mount], root_dir: opts[:root_dir], block: proc do # /run is mounted by {#pre_start} FileUtils.mkdir_p('/run/udev') # Create /run/udev/control if it isn't already there begin File.stat('/run/udev/control') rescue Errno::ENOENT File.new('/run/udev/control', 'w').close end true end ) end |
#pre_start(_opts = {}) ⇒ Object
Called before container start, can be used to e.g. add temporary mounts
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 37 def pre_start(_opts = {}) return unless volatile_is_systemd? # systemd by default does not monitor udev events in containers, which # means that there are no device units to depend on, e.g. for network # interfaces. systemd decides this by the existence of socket # /run/udev/control and that /dev is devtmpfs. Our /dev cannot be # devtmpfs since that doesn't work in containers, and /run/udev/control # is created by a socket unit *after* systemd makes the decision to not # monitor udev events. After `systemctl daemon-reload`, it actually # starts to monitor udev events and the device units are created. # # See https://github.com/systemd/systemd/blob/729d2df8065ac90ac606e1fff91dc2d588b2795d/src/libsystemd/sd-device/device-monitor.c#L125 # # We therefore mount /run as tmpfs before systemd is run and create # a stub for /run/udev/control, so that the check passes and udev events # are monitored from the start. # # /run has to be mounted by LXC from the container's user namespace, so # that it is owned by that user namespace. File /run/udev/control is # created later in {#post_mount}. # Check if /run isn't mounted already by user configuration return unless ct.mounts.detect { |mnt| %w[/run /run/].include?(mnt.mountpoint) }.nil? mem_limit = ct.find_memory_limit mnt_opts = %w[nosuid nodev mode=755 create=dir] mnt_opts << "size=#{mem_limit / 2}" if mem_limit ct.mounts.add(Mount::Entry.new( 'tmpfs', '/run', 'tmpfs', mnt_opts.join(','), false, temp: true, in_config: true )) end |
#reload_network_manager_dns ⇒ Object (protected)
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 265 def reload_network_manager_dns # Query the running daemon, not a client version string. Exit 8 means # NM is stopped and will read the policy at its next normal start. result = ct_syscmd(ct, %w[nmcli -t -f VERSION general], valid_rcs: [0, 8]) return if result.exitstatus == 8 version = result.output.strip.match(/\A(\d+)\.(\d+)\./) raise 'unable to determine NetworkManager version' unless version if (version.captures.map(&:to_i) <=> [1, 22]) >= 0 ct_syscmd(ct, %w[nmcli general reload conf,dns-rc], valid_rcs: [0, 8]) else # The daemon already supports synchronous Reload(flags) in 1.18, # but nmcli gained general reload only in 1.22. Do not replace it # with asynchronous SIGHUP or restart the guest's connections. ct_syscmd(ct, [ 'dbus-send', '--system', '--print-reply', '--type=method_call', '--dest=org.freedesktop.NetworkManager', '/org/freedesktop/NetworkManager', 'org.freedesktop.NetworkManager.Reload', 'uint32:3' ]) end end |
#remove_netif(opts) ⇒ Object
Called when a network interface is removed from a container
194 195 196 197 198 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 194 def remove_netif(opts) with_rootfs do configurator.remove_netif(ct.netifs, opts[:netif]) end end |
#rename_netif(opts) ⇒ Object
Called when an existing network interface is renamed
204 205 206 207 208 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 204 def rename_netif(opts) with_rootfs do configurator.rename_netif(ct.netifs, opts[:netif], opts[:original_name]) end end |
#set_hostname(opts = {}) ⇒ Object
Set container hostname
144 145 146 147 148 149 150 151 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 144 def set_hostname(opts = {}) with_rootfs do configurator.set_hostname(ct.hostname, old_hostname: opts[:original]) configurator.update_etc_hosts(ct.hostname, old_hostname: opts[:original]) end apply_hostname if ct.running? end |
#start(_opts = {}) ⇒ Object
Run just before the container is started
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 106 def start(_opts = {}) return unless ct.hostname || ct.dns_resolvers || ctrc.dist_configure_network? net_configured = with_rootfs do ret = false set_hostname if ct.hostname dns_resolvers if ct.dns_resolvers if ctrc.dist_configure_network? network ret = true end ret end ctrc.dist_network_configured = true if net_configured end |
#stop(opts) ⇒ Object
Gracefully stop the container
131 132 133 134 135 136 137 138 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 131 def stop(opts) ContainerControl::Commands::Stop.run!( ct, opts[:mode], message: opts[:message], timeout: opts[:timeout] ) end |
#unset_dns_resolvers(_opts = {}) ⇒ Object
225 226 227 228 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 225 def unset_dns_resolvers(_opts = {}) reload = with_rootfs { configurator.unset_dns_resolvers } reload_network_manager_dns if reload && !@within_rootfs && ct.running? end |
#unset_etc_hosts(_opts = {}) ⇒ Object
Remove the osctld-generated notice from /etc/hosts
170 171 172 173 174 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 170 def unset_etc_hosts(_opts = {}) with_rootfs do configurator.unset_etc_hosts end end |
#update_etc_hosts(opts = {}) ⇒ Object
Update hostname in /etc/hosts, optionally removing configuration of old
hostname.
163 164 165 166 167 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 163 def update_etc_hosts(opts = {}) with_rootfs do configurator.update_etc_hosts(ct.hostname, old_hostname: opts[:old_hostname]) end end |
#volatile_is_systemd? ⇒ Boolean (protected)
Check if the container is using systemd as init
This method accesses the container's rootfs from the host, which is dangerous because of symlinks and we really shouldn't be doing it... but in this case, we only do readlink(), so it shouldn't do any harm.
316 317 318 319 320 321 322 323 324 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 316 def volatile_is_systemd? return true if ctrc.distribution == 'nixos' begin File.readlink(File.join(ctrc.rootfs, 'sbin/init')).include?('systemd') rescue SystemCallError false end end |
#with_rootfs(&block) ⇒ Object (protected)
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/osctld/dist_config/distributions/base.rb', line 288 def with_rootfs(&block) if @within_rootfs block.call else ContainerControl::Commands::WithRootfs.run!( ctrc.ct, ctrc:, block: proc do @configurator = configurator_class.new( ct.id, '/', ct.distribution, ct.version ) @within_rootfs = true block.call end ) end end |