mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
refactor(ios): drive device tunnel via io.Closer seam
Replace the tunnelChild *exec.Cmd and spawnTunnel seam with a tunnel io.Closer and startTunnel seam backed by the in-process usbmux forwarder.
This commit is contained in:
1 parent
956b114cba
commit
033b17a102
2 files changed
+28
-24
No files matched your search
@@ -23,7 +23,7 @@ import (
|
|||||||
// carried here: realSpawnDeviceRunner reads them from the environment at the
|
// carried here: realSpawnDeviceRunner reads them from the environment at the
|
||||||
// point of use so secrets never reach the Options struct or run artifacts.
|
// point of use so secrets never reach the Options struct or run artifacts.
|
||||||
type DeviceOptions struct {
|
type DeviceOptions struct {
|
||||||
// HardwareUDID feeds xcodebuild -destination and iproxy -u.
|
// HardwareUDID feeds xcodebuild -destination and the usbmux device match.
|
||||||
HardwareUDID string
|
HardwareUDID string
|
||||||
// CoreDeviceID feeds devicectl install/uninstall.
|
// CoreDeviceID feeds devicectl install/uninstall.
|
||||||
CoreDeviceID string
|
CoreDeviceID string
|
||||||
@@ -39,7 +39,7 @@ type DeviceOptions struct {
|
|||||||
// Test seams. Production leaves them nil and NewDevice wires the real
|
// Test seams. Production leaves them nil and NewDevice wires the real
|
||||||
// build/spawn/tunnel/dial.
|
// build/spawn/tunnel/dial.
|
||||||
spawnRunner func(ctx context.Context, address string) (*exec.Cmd, error)
|
spawnRunner func(ctx context.Context, address string) (*exec.Cmd, error)
|
||||||
spawnTunnel func(ctx context.Context, hardwareUDID, localPort, devicePort string) (*exec.Cmd, error)
|
startTunnel func(ctx context.Context, hardwareUDID, localAddress, devicePort string) (io.Closer, error)
|
||||||
dialRunner func(address string) (transport.Companion, error)
|
dialRunner func(address string) (transport.Companion, error)
|
||||||
pickAddress func() (string, error)
|
pickAddress func() (string, error)
|
||||||
}
|
}
|
||||||
@@ -80,13 +80,13 @@ func NewDevice(ctx context.Context, options DeviceOptions) (*Driver, error) {
|
|||||||
deviceMode: true,
|
deviceMode: true,
|
||||||
hybrid: false,
|
hybrid: false,
|
||||||
spawnRunner: options.spawnRunner,
|
spawnRunner: options.spawnRunner,
|
||||||
spawnTunnel: options.spawnTunnel,
|
startTunnel: options.startTunnel,
|
||||||
}
|
}
|
||||||
if d.spawnRunner == nil {
|
if d.spawnRunner == nil {
|
||||||
d.spawnRunner = d.realSpawnDeviceRunner
|
d.spawnRunner = d.realSpawnDeviceRunner
|
||||||
}
|
}
|
||||||
if d.spawnTunnel == nil {
|
if d.startTunnel == nil {
|
||||||
d.spawnTunnel = spawnTunnel
|
d.startTunnel = startUsbmuxTunnel
|
||||||
}
|
}
|
||||||
d.dialRunner = options.dialRunner
|
d.dialRunner = options.dialRunner
|
||||||
if d.dialRunner == nil {
|
if d.dialRunner == nil {
|
||||||
@@ -147,25 +147,27 @@ func (d *Driver) bringUpDevice(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
d.runnerChild = runnerChild
|
d.runnerChild = runnerChild
|
||||||
|
|
||||||
tunnelChild, err := d.spawnTunnel(d.processContext, d.udid, port, port)
|
// The forwarder listens on the host loopback port and bridges to the same
|
||||||
|
// port number on the device, where the runner listens.
|
||||||
|
tunnel, err := d.startTunnel(d.processContext, d.udid, address, port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.stopRunnerChild()
|
d.stopRunnerChild()
|
||||||
return fmt.Errorf("spawn tunnel: %w", err)
|
return fmt.Errorf("start tunnel: %w", err)
|
||||||
}
|
}
|
||||||
d.tunnelChild = tunnelChild
|
d.tunnel = tunnel
|
||||||
|
|
||||||
startupCtx, cancel := context.WithTimeout(ctx, deviceStartupTimeout)
|
startupCtx, cancel := context.WithTimeout(ctx, deviceStartupTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := waitForListener(startupCtx, address); err != nil {
|
if err := waitForListener(startupCtx, address); err != nil {
|
||||||
d.stopTunnelChild()
|
d.stopTunnel()
|
||||||
d.stopRunnerChild()
|
d.stopRunnerChild()
|
||||||
return fmt.Errorf("device runner listener: %w", err)
|
return fmt.Errorf("device runner listener: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion, err := d.dialRunner(address)
|
companion, err := d.dialRunner(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.stopTunnelChild()
|
d.stopTunnel()
|
||||||
d.stopRunnerChild()
|
d.stopRunnerChild()
|
||||||
return fmt.Errorf("dial device runner: %w", err)
|
return fmt.Errorf("dial device runner: %w", err)
|
||||||
}
|
}
|
||||||
@@ -173,7 +175,7 @@ func (d *Driver) bringUpDevice(ctx context.Context) error {
|
|||||||
|
|
||||||
if err := d.waitForHealth(startupCtx); err != nil {
|
if err := d.waitForHealth(startupCtx); err != nil {
|
||||||
_ = companion.Close()
|
_ = companion.Close()
|
||||||
d.stopTunnelChild()
|
d.stopTunnel()
|
||||||
d.stopRunnerChild()
|
d.stopRunnerChild()
|
||||||
return fmt.Errorf("device runner health: %w", err)
|
return fmt.Errorf("device runner health: %w", err)
|
||||||
}
|
}
|
||||||
@@ -189,7 +191,7 @@ func (d *Driver) respawnDevice(ctx context.Context) error {
|
|||||||
_ = d.companion.Close()
|
_ = d.companion.Close()
|
||||||
}
|
}
|
||||||
d.stopRunnerChild()
|
d.stopRunnerChild()
|
||||||
d.stopTunnelChild()
|
d.stopTunnel()
|
||||||
return d.bringUpDevice(ctx)
|
return d.bringUpDevice(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,12 +127,12 @@ type Driver struct {
|
|||||||
|
|
||||||
// Device-mode fields. On the physical-device path d.companion is the runner
|
// Device-mode fields. On the physical-device path d.companion is the runner
|
||||||
// dialed over a usbmux tunnel, hybrid is false, and runnerClient is nil.
|
// dialed over a usbmux tunnel, hybrid is false, and runnerClient is nil.
|
||||||
// coreDeviceID feeds devicectl; tunnelChild is the iproxy process forwarding
|
// coreDeviceID feeds devicectl; tunnel is the in-process usbmux forwarder
|
||||||
// the host loopback port to the runner's device-side port.
|
// bridging the host loopback port to the runner's device-side port.
|
||||||
deviceMode bool
|
deviceMode bool
|
||||||
coreDeviceID string
|
coreDeviceID string
|
||||||
tunnelChild *exec.Cmd
|
tunnel io.Closer
|
||||||
spawnTunnel func(ctx context.Context, hardwareUDID, localPort, devicePort string) (*exec.Cmd, error)
|
startTunnel func(ctx context.Context, hardwareUDID, localAddress, devicePort string) (io.Closer, error)
|
||||||
pickDeviceAddress func() (string, error)
|
pickDeviceAddress func() (string, error)
|
||||||
|
|
||||||
// processContext owns the companion child's lifetime: it is derived from
|
// processContext owns the companion child's lifetime: it is derived from
|
||||||
@@ -995,19 +995,21 @@ func (d *Driver) Close() {
|
|||||||
d.runnerClient = nil
|
d.runnerClient = nil
|
||||||
}
|
}
|
||||||
d.stopRunnerChild()
|
d.stopRunnerChild()
|
||||||
d.stopTunnelChild()
|
d.stopTunnel()
|
||||||
if d.processCancel != nil {
|
if d.processCancel != nil {
|
||||||
d.processCancel()
|
d.processCancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopTunnelChild terminates the iproxy usbmux tunnel on the device path.
|
// stopTunnel closes the in-process usbmux forwarder on the device path. Closing
|
||||||
// SIGTERM lets it close its forwarded sockets before exit; a nil child (the
|
// its listener ends the accept loop and lets the open bridges drain; a nil
|
||||||
// simulator path) is a no-op.
|
// tunnel (the simulator path) is a no-op.
|
||||||
func (d *Driver) stopTunnelChild() {
|
func (d *Driver) stopTunnel() {
|
||||||
child := d.tunnelChild
|
tunnel := d.tunnel
|
||||||
d.tunnelChild = nil
|
d.tunnel = nil
|
||||||
stopProcess(child)
|
if tunnel != nil {
|
||||||
|
_ = tunnel.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopChild terminates the companion child gracefully (SIGTERM, grace window,
|
// stopChild terminates the companion child gracefully (SIGTERM, grace window,
|
||||||
|
|||||||
Reference in new issue
Block a user