Class: OsCtld::DistConfig::Distributions::Base

Inherits:
Object
  • Object
show all
Includes:
OsCtl::Lib::Utils::Log, OsCtl::Lib::Utils::System, Utils::SwitchUser
Defined in:
lib/osctld/dist_config/distributions/base.rb

Direct Known Subclasses

Arch, Debian, Gentoo, Guix, NixOS, OpenSuse, Other, RedHat, Slackware, Void

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Utils::SwitchUser

#ct_attach, #ct_syscmd

Constructor Details

#initialize(ctrc) ⇒ Base

Returns a new instance of Base.

Parameters:



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

#configuratorObject (readonly, protected)

Returns the value of attribute configurator.



243
244
245
# File 'lib/osctld/dist_config/distributions/base.rb', line 243

def configurator
  @configurator
end

#ctObject (readonly)

Returns the value of attribute ct.



18
19
20
# File 'lib/osctld/dist_config/distributions/base.rb', line 18

def ct
  @ct
end

#ctrcObject (readonly)

Returns the value of attribute ctrc.



18
19
20
# File 'lib/osctld/dist_config/distributions/base.rb', line 18

def ctrc
  @ctrc
end

#distributionObject (readonly)

Returns the value of attribute distribution.



18
19
20
# File 'lib/osctld/dist_config/distributions/base.rb', line 18

def distribution
  @distribution
end

#versionObject (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

Parameters:

  • opts (Hash)

Options Hash (opts):



183
184
185
186
187
# File 'lib/osctld/dist_config/distributions/base.rb', line 183

def add_netif(opts)
  with_rootfs do
    configurator.add_netif(ct.netifs, opts[:netif])
  end
end

#apply_hostnameObject

Configure hostname in a running system



152
153
154
# File 'lib/osctld/dist_config/distributions/base.rb', line 152

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

Returns:

  • (String)


233
234
235
# File 'lib/osctld/dist_config/distributions/base.rb', line 233

def bin_path(_opts)
  '/bin'
end

#configurator_classObject



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



208
209
210
211
212
# File 'lib/osctld/dist_config/distributions/base.rb', line 208

def dns_resolvers(_opts = {})
  with_rootfs do
    configurator.dns_resolvers(ct.dns_resolvers)
  end
end

#log_typeObject



237
238
239
# File 'lib/osctld/dist_config/distributions/base.rb', line 237

def log_type
  ct.id
end

#network(_opts = {}) ⇒ Object



174
175
176
177
178
# File 'lib/osctld/dist_config/distributions/base.rb', line 174

def network(_opts = {})
  with_rootfs do
    configurator.network(ct.netifs)
  end
end

#passwd(opts) ⇒ Object

Parameters:

  • opts (Hash)

    options

Options Hash (opts):

  • user (String)
  • password (String)


217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/osctld/dist_config/distributions/base.rb', line 217

def passwd(opts)
  ret = ct_syscmd(
    ct,
    %w[chpasswd],
    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

Parameters:

  • opts (Hash)

Options Hash (opts):

  • :ns_pid (Integer)
  • :rootfs_mount (String)


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 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],
    chroot: opts[:rootfs_mount],
    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

#remove_netif(opts) ⇒ Object

Called when a network interface is removed from a container

Parameters:

  • opts (Hash)

Options Hash (opts):



192
193
194
195
196
# File 'lib/osctld/dist_config/distributions/base.rb', line 192

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

Parameters:

  • opts (Hash)

Options Hash (opts):



202
203
204
205
206
# File 'lib/osctld/dist_config/distributions/base.rb', line 202

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

Parameters:

  • opts (Hash) (defaults to: {})

    options

Options Hash (opts):

  • :original (OsCtl::Lib::Hostname)

    previous hostname



142
143
144
145
146
147
148
149
# File 'lib/osctld/dist_config/distributions/base.rb', line 142

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



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/osctld/dist_config/distributions/base.rb', line 104

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

Parameters:

  • opts (Hash)

Options Hash (opts):

  • :mode (:stop, :shutdown, :kill)
  • :message (String)
  • :timeout (Integer)


129
130
131
132
133
134
135
136
# File 'lib/osctld/dist_config/distributions/base.rb', line 129

def stop(opts)
  ContainerControl::Commands::Stop.run!(
    ct,
    opts[:mode],
    message: opts[:message],
    timeout: opts[:timeout]
  )
end

#unset_etc_hosts(_opts = {}) ⇒ Object

Remove the osctld-generated notice from /etc/hosts



168
169
170
171
172
# File 'lib/osctld/dist_config/distributions/base.rb', line 168

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.

Parameters:

  • opts (Hash) (defaults to: {})

    options

Options Hash (opts):

  • :old_hostname (OsCtl::Lib::Hostname, nil)


161
162
163
164
165
# File 'lib/osctld/dist_config/distributions/base.rb', line 161

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.

Returns:

  • (Boolean)


273
274
275
276
277
278
279
280
281
# File 'lib/osctld/dist_config/distributions/base.rb', line 273

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)



245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/osctld/dist_config/distributions/base.rb', line 245

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