From f438cdb0993b17f0e540ecefa39cde09f89730f4 Mon Sep 17 00:00:00 2001 From: Orzelius Date: Thu, 22 May 2025 14:16:35 +0900 Subject: [PATCH] chore: use custom dhcpd server on macos qemu add usage of our in-house dhcpd server instead of apple's bootpd Signed-off-by: Orzelius --- pkg/provision/providers/qemu/create.go | 13 ++-- pkg/provision/providers/vm/dhcpd.go | 4 +- pkg/provision/providers/vm/dhcpd_darwin.go | 91 ++++++++++++++++++++++ pkg/provision/providers/vm/dhcpd_linux.go | 16 ++++ 4 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 pkg/provision/providers/vm/dhcpd_darwin.go create mode 100644 pkg/provision/providers/vm/dhcpd_linux.go diff --git a/pkg/provision/providers/qemu/create.go b/pkg/provision/providers/qemu/create.go index ae16eed5b..1ccd7ebca 100644 --- a/pkg/provision/providers/qemu/create.go +++ b/pkg/provision/providers/qemu/create.go @@ -86,12 +86,6 @@ func (p *provisioner) Create(ctx context.Context, request provision.ClusterReque } } - fmt.Fprintln(options.LogWriter, "creating dhcpd") - - if err = p.CreateDHCPd(state, request); err != nil { - return nil, fmt.Errorf("error creating dhcpd: %w", err) - } - var nodeInfo []provision.NodeInfo fmt.Fprintln(options.LogWriter, "creating controlplane nodes") @@ -100,6 +94,13 @@ func (p *provisioner) Create(ctx context.Context, request provision.ClusterReque return nil, err } + // On darwin, qemu creates the bridge interface to which the dhcpd server is attached to, so at least one machine has to be created first. + fmt.Fprintln(options.LogWriter, "creating dhcpd") + + if err = p.CreateDHCPd(ctx, state, request); err != nil { + return nil, fmt.Errorf("error creating dhcpd: %w", err) + } + fmt.Fprintln(options.LogWriter, "creating worker nodes") var workerNodeInfo []provision.NodeInfo diff --git a/pkg/provision/providers/vm/dhcpd.go b/pkg/provision/providers/vm/dhcpd.go index ca73621a9..a67e0eaf2 100644 --- a/pkg/provision/providers/vm/dhcpd.go +++ b/pkg/provision/providers/vm/dhcpd.go @@ -275,8 +275,8 @@ const ( dhcpLog = "dhcpd.log" ) -// CreateDHCPd creates DHCPd. -func (p *Provisioner) CreateDHCPd(state *State, clusterReq provision.ClusterRequest) error { +// startDHCPd starts the DHCPd server. +func (p *Provisioner) startDHCPd(state *State, clusterReq provision.ClusterRequest) error { pidPath := state.GetRelativePath(dhcpPid) logFile, err := os.OpenFile(state.GetRelativePath(dhcpLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) diff --git a/pkg/provision/providers/vm/dhcpd_darwin.go b/pkg/provision/providers/vm/dhcpd_darwin.go new file mode 100644 index 000000000..89a1b448b --- /dev/null +++ b/pkg/provision/providers/vm/dhcpd_darwin.go @@ -0,0 +1,91 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package vm + +import ( + "context" + "fmt" + "net" + "os" + "os/exec" + "strings" + "time" + + "github.com/siderolabs/go-retry/retry" + + "github.com/siderolabs/talos/pkg/provision" +) + +// CreateDHCPd creates a DHCP server on darwin. +// It waits for the interface to appear, shut's down the apple bootp DHCPd server created by qemu by default, +// starts the talos DHCP server and then starts the apple bootp server again, which is configured such +// that it detects existing dhcp servers on interfaces and doesn't interfare with them. +func (p *Provisioner) CreateDHCPd(ctx context.Context, state *State, clusterReq provision.ClusterRequest) error { + err := waitForInterface(ctx, state.BridgeName) + if err != nil { + return err + } + + cmd := exec.CommandContext(ctx, "/bin/launchctl", "unload", "-w", "/System/Library/LaunchDaemons/bootps.plist") + + err = cmd.Run() + if err != nil { + return fmt.Errorf("failed to stop native dhcp server: %w", err) + } + + err = p.startDHCPd(state, clusterReq) + if err != nil { + return err + } + + err = waitForDHCPServerUp(ctx, state) + if err != nil { + return err + } + + time.Sleep(time.Second) + + cmd = exec.CommandContext(ctx, "/bin/launchctl", "load", "-w", "/System/Library/LaunchDaemons/bootps.plist") + + err = cmd.Run() + if err != nil { + fmt.Printf("warning: failed to start native dhcp server after creating a talos dhcp server: %s", err) + } + + return nil +} + +// waitForInterface returns when interface is found or errors after a minute. +func waitForInterface(ctx context.Context, interfaceName string) error { + return retry.Constant(1*time.Minute, retry.WithUnits(50*time.Millisecond)).RetryWithContext(ctx, func(_ context.Context) error { + ifaces, err := net.Interfaces() + if err != nil { + return err + } + + for _, iface := range ifaces { + if iface.Name == interfaceName { + return nil + } + } + + return retry.ExpectedError(fmt.Errorf("interface %s not found", interfaceName)) + }) +} + +func waitForDHCPServerUp(ctx context.Context, state *State) error { + return retry.Constant(1*time.Minute, retry.WithUnits(100*time.Millisecond)).RetryWithContext(ctx, func(_ context.Context) error { + logFileData, err := os.ReadFile(state.GetRelativePath(dhcpLog)) + if err != nil { + return retry.ExpectedError(err) + } + + if strings.Contains(string(logFileData), "Ready to handle requests") { + return nil + } + + return retry.ExpectedError(fmt.Errorf("failure: DHCPd server has not started")) + }) +} diff --git a/pkg/provision/providers/vm/dhcpd_linux.go b/pkg/provision/providers/vm/dhcpd_linux.go new file mode 100644 index 000000000..eb3162b6a --- /dev/null +++ b/pkg/provision/providers/vm/dhcpd_linux.go @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package vm + +import ( + "context" + + "github.com/siderolabs/talos/pkg/provision" +) + +// CreateDHCPd creates a DHCP server. +func (p *Provisioner) CreateDHCPd(ctx context.Context, state *State, clusterReq provision.ClusterRequest) error { + return p.startDHCPd(state, clusterReq) +}