Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/g
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg/mod \
go build -ldflags '-s -w' -o /go/bin/rancher-desktop-guest-agent ./src/go/guestagent

COPY src /rd
WORKDIR /rd/rd-init
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg/mod \
go build -ldflags '-s -w' -o /go/bin/rd-init .

FROM registry.opensuse.org/opensuse/bci/kiwi:10 AS builder
ARG type=qcow2
ARG NERDCTL_VERSION
Expand Down
20 changes: 14 additions & 6 deletions config.kiwi
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,16 @@
<rpm-excludedocs>true</rpm-excludedocs>
</preferences>
<preferences profiles="lima">
<type image="oem" format="qcow2" filesystem="ext4" firmware="efi" />
<type image="oem" format="qcow2" filesystem="ext4" fsmountoptions="x-systemd.growfs"
firmware="efi">
<oemconfig>
<!--
Disable Kiwi's built-in partition resizing; that is very generic and
pulls in lots of dependencies. We use systemd-repart + x-systemd.growfs
-->
<oem-resize>false</oem-resize>
</oemconfig>
</type>
</preferences>
<preferences profiles="wsl">
<type image="tbz" />
Expand Down Expand Up @@ -71,6 +80,7 @@
<package name="mkcert" /> <!-- used by the image-allow-list feature -->
<package name="iptables-backend-nft" />
<package name="openssh-server" />
<package name="sudo" />
<package name="system-user-nobody" />
<package name="systemd" />
<package name="rd-openresty" />
Expand All @@ -79,17 +89,15 @@
</packages>
<packages type="image" profiles="wsl">
<package name="hostname" /> <!-- needed for lima on WSL (getWslSSHAddress) -->
<package name="sudo" />
<package name="systemd &gt;= 255.7" />
</packages>
<packages type="image" profiles="lima">
<package name="cloud-init" />
<package name="cni-plugin-flannel" />
<package name="dracut-kiwi-oem-repart" />
<package name="dracut" />
<package name="grub2" />
<package name="kernel-default" />
<package name="NetworkManager" />
<package name="kernel-default-base" />
<package name="systemd-networkd" />
<package name="systemd-resolved" />
<package name="grub2-x86_64-efi" arch="x86_64" />
</packages>
<users>
Expand Down
13 changes: 9 additions & 4 deletions config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ done
# tini-static has a different name
ln /usr/sbin/tini-static /usr/sbin/tini

# This file name is invalid on Windows, so we have to rename it as part of the
# build process to prevent issues checking the repository out.
mv /usr/local/lib/systemd/system/mnt-lima{-,\\x2d}cidata.mount

#======================================
# Fix permissions
#--------------------------------------
Expand All @@ -99,11 +103,12 @@ if [[ ${kiwi_profiles:-} =~ lima ]]; then
# Enable services
systemctl enable buildkitd
systemctl enable containerd
systemctl enable cloud-config
systemctl enable cloud-final
systemctl enable cloud-init
systemctl enable docker
systemctl enable NetworkManager # Needed for cloud-init to work correctly
systemctl enable systemd-networkd
systemctl enable systemd-resolved

systemctl enable lima-init.service
systemctl enable rd-init.service
# Disable network namespace related functionality (WSL only)
rm -f /usr/local/lib/systemd/system/*/network-namespace.conf
# Remove the docker config that is only used on Windows
Expand Down
4 changes: 4 additions & 0 deletions root/etc/dracut.conf.d/repart.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Enable systemd-repart instead of the kiwi one
add_dracutmodules+=" systemd-repart "
# Add the repart configuration file so it gets run in initrd
install_items+=" /etc/dracut.conf.d/repart.conf "
2 changes: 2 additions & 0 deletions root/usr/local/lib/repart.d/50-root.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[Partition]
Type=root
21 changes: 21 additions & 0 deletions root/usr/local/lib/systemd/system/lima-init.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[Unit]
Description=Minimal cloud-init for Rancher Desktop
ConditionVirtualization=vm
Wants=rd-init.service ssh-keygen.service sshd.service
After=rd-init.service systemd-networkd-wait-online.service
After=network.service NetworkManager.service NetworkManager-wait-online.service
RequiresMountsFor=/mnt/lima-cidata
Before=network-online.target sshd-keygen.service sshd.service shutdown.target
Conflicts=shutdown.target

[Service]
Type=exec
Environment=LIMA_CIDATA_MNT=/mnt/lima-cidata
EnvironmentFile=/mnt/lima-cidata/lima.env
ExecStart=/mnt/lima-cidata/boot.sh
RemainAfterExit=yes
TimeoutSec=0
StandardOutput=journal+console

[Install]
WantedBy=multi-user.target
9 changes: 9 additions & 0 deletions root/usr/local/lib/systemd/system/mnt-lima-cidata.mount
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Unit]
Description=/mnt/lima-cidata Mount Point
ConditionVirtualization=vm

[Mount]
Where=/mnt/lima-cidata
What=/dev/disk/by-label/cidata
Options=ro,mode=0700,dmode=0700,overriderockperm,exec,uid=0
DirectoryMode=0700
21 changes: 21 additions & 0 deletions root/usr/local/lib/systemd/system/rd-init.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[Unit]
Description=Minimal cloud-init for Rancher Desktop (pre-networking)
ConditionVirtualization=vm
Wants=network-pre.target
After=systemd-remount-fs.service
Requires=systemd-udevd.service
After=systemd-udevd.service
Before=network-pre.target
Before=shutdown.target
RequiresMountsFor=/mnt/lima-cidata

[Service]
Type=notify
EnvironmentFile=/mnt/lima-cidata/lima.env
ExecStart=/usr/local/bin/rd-init
RemainAfterExit=yes
TimeoutSec=0
StandardOutput=journal+console

[Install]
WantedBy=multi-user.target
12 changes: 12 additions & 0 deletions src/rd-init/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/rancher-sandbox/rancher-desktop-opensuse/src/rd-init

go 1.24.2

require (
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/coreos/go-systemd/v22 v22.5.0
github.com/goccy/go-yaml v1.17.1
golang.org/x/sys v0.33.0
)

require github.com/godbus/dbus/v5 v5.0.4 // indirect
10 changes: 10 additions & 0 deletions src/rd-init/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
95 changes: 95 additions & 0 deletions src/rd-init/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Command rd-init is a minimal implementation of cloud-init to start lima.
// Note that this only implements `cloud-init-local`.
package main

import (
"context"
"fmt"
"log/slog"
"os"
"os/exec"

"github.com/coreos/go-systemd/daemon"
"github.com/coreos/go-systemd/v22/dbus"
)

func runCommand(ctx context.Context, name string, arg... string) error {
slog.DebugContext(ctx, "Running command", "name", name, "arg", arg)
cmd := exec.CommandContext(ctx, name, arg...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

func run(ctx context.Context) error {
var units []string
fns := []func(context.Context)([]string, error) {
LoadMetadata,
LoadUserData,
LoadNetworkConfig,
}
for _, fn := range fns {
if newUnits, err := fn(ctx); err != nil {
return err
} else {
units = append(units, newUnits...)
}
}

slog.InfoContext(ctx, "reloading systemd", "units", units)

conn, err := dbus.NewSystemConnectionContext(ctx)
if err != nil {
return fmt.Errorf("failed to connect to systemd: %w", err)
}
defer conn.Close()

if err := conn.ReloadContext(ctx); err != nil {
return fmt.Errorf("failed to reload systemd: %w", err)
}

// Trigger udevadm to cause the network interface to change name.
slog.InfoContext(ctx, "triggering udevadm to rename interfaces")
if err := runCommand(ctx, "/usr/bin/udevadm", "control", "--reload"); err != nil {
return fmt.Errorf("failed to reload udev: %w", err)
}
err = runCommand(ctx, "/usr/bin/udevadm", "trigger", "--type=devices", "--subsystem-match=net")
if err != nil {
return fmt.Errorf("failed to rename network devices: %w", err)
}

// Notify ready before we reload the other units; otherwise we end up
// blocking startup due to a loop with systemd-networkd.
if _, err := daemon.SdNotify(true, daemon.SdNotifyReady); err != nil {
return fmt.Errorf("failed to notify systemd: %w", err)
}

seenUnits := make(map[string]bool)
for _, unit := range units {
if seenUnits[unit] {
continue
}
seenUnits[unit] = true
ch := make(chan string)
_, err = conn.RestartUnitContext(ctx, unit, "replace", ch)
if err != nil {
return fmt.Errorf("failed to start unit %s: %w", unit, err)
}
select {
case result := <-ch:
slog.InfoContext(ctx, "restarted systemd unit", "unit", unit, "result", result)
case <-ctx.Done():
return fmt.Errorf("context closed while waiting for systemd unit %s: %w", unit, ctx.Err())
}
}

return nil
}

func main () {
ctx := context.Background()
if err := run(ctx); err != nil {
slog.ErrorContext(ctx, "rd-init failed", "error", err)
os.Exit(1)
}
}
37 changes: 37 additions & 0 deletions src/rd-init/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"context"
"fmt"
"log/slog"
"os"

"github.com/goccy/go-yaml"
"golang.org/x/sys/unix"
)

const (
// Path to cloud-config style metadata file provided by Lima.
metadataPath = "/mnt/lima-cidata/meta-data"
)

// Load /mnt/lima-cidata/meta-data
func LoadMetadata(ctx context.Context) ([]string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can only assume that we are forced to adhere to function signature that returns ([]string, error) because []string is not used at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var metaData struct {
LocalHostName string `yaml:"local-hostname"`
}

file, err := os.Open(metadataPath)
if err != nil {
return nil, fmt.Errorf("failed to load meta-data file: %w", err)
}
defer file.Close()
if err := yaml.NewDecoder(file).DecodeContext(ctx, &metaData); err != nil {
return nil, fmt.Errorf("failed to unmarshal meta-data file: %w", err)
}
slog.InfoContext(ctx, "setting host name", "hostname", metaData.LocalHostName)
if err := unix.Sethostname([]byte(metaData.LocalHostName)); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the hostname already validated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't make much sense for us to validate it (more than would otherwise be done); we don't have any specific requirements other than whatever the kernel supports. This is generally generated by Lima anyway, so we don't actually care.

Note that since this is golang, we don't use the native libc and just use the syscall directly; this means we end up calling the implementation, which doesn't appear to do much checking.

https://github.com/torvalds/linux/blob/7d0a66e4bb9081d75c82ec4957c50034cb0ea449/kernel/sys.c#L1419-L1443

return nil, fmt.Errorf("failed to set hostname: %w", err)
}
return nil, nil
}
Loading