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 <albert.kostusev@siderolabs.com>
This commit is contained in:
Orzelius 2025-05-22 14:16:35 +09:00
parent 11c17fb9aa
commit f438cdb099
No known key found for this signature in database
GPG Key ID: C17C8E3962A0D9B1
4 changed files with 116 additions and 8 deletions

View File

@ -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

View File

@ -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)

View File

@ -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"))
})
}

View File

@ -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)
}