-
Notifications
You must be signed in to change notification settings - Fork 326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remote SSH connection support #1898
Conversation
core/app/layout/layout.go
Outdated
@@ -88,6 +90,10 @@ func withLibraryPlatformSuffix(lib string, os device.OSKind) string { | |||
} | |||
} | |||
|
|||
func GetLibraryName(lib LibraryType, abi *device.ABI) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Doc strings everywhere
cmd/gapis/main.go
Outdated
@@ -101,13 +105,31 @@ func run(ctx context.Context) error { | |||
r.SetDeviceProperty(ctx, host, client.LaunchArgsKey, text.SplitArgs(*gapirArgStr)) | |||
} | |||
|
|||
deviceScanDone, onDeviceScanDone := task.NewSignal() | |||
numAsyncScans := 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More common to use a sync.WaitGroup
:
wg := sync.WaitGroup{}
if *scanAndroidDevs {
wg.Add(1)
crash.Go(func() { monitorAndroidDevices(ctx, r, wg.Done) })
}
...
crash.Go(func(){
wg.Wait()
onDeviceScanDone(ctx)
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/app/layout/layout.go
Outdated
func (l pkgLayout) Gapir(ctx context.Context, abi *device.ABI) (file.Path, error) { | ||
if hostOS(ctx) == abi.OS { | ||
return l.root.Join(withExecutablePlatformSuffix("gapir", hostOS(ctx))), nil | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No else when the true branch always returns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/app/layout/layout.go
Outdated
func (l pkgLayout) Library(ctx context.Context, lib LibraryType, abi *device.ABI) (file.Path, error) { | ||
if hostOS(ctx) == abi.OS { | ||
return l.root.Join("lib", withLibraryPlatformSuffix(libTypeToName[lib], hostOS(ctx))), nil | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/app/layout/layout.go
Outdated
func (l pkgLayout) DeviceInfo(ctx context.Context, os device.OSKind) (file.Path, error) { | ||
if hostOS(ctx) == os { | ||
return l.root.Join(withExecutablePlatformSuffix("device_info", os)), nil | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/app/layout/layout.go
Outdated
func (l *ZipLayout) Gapir(ctx context.Context, abi *device.ABI) (*zip.File, error) { | ||
if l.os == abi.OS { | ||
return l.file(withExecutablePlatformSuffix("gapir", l.os)) | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/vulkan/loader/loader.go
Outdated
if err != nil { | ||
return func() {}, err | ||
return func(ctx context.Context) {}, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to call cleanup
here and return nil, err
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/vulkan/loader/loader.go
Outdated
} | ||
return setupJSON(lib, json, env) | ||
|
||
err = setupJSON(ctx, lib, json, setup, tempdir, env) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I think it would be better to check err
, and if not nil
, call cleanup
and return nil, err
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/vulkan/loader/loader.go
Outdated
return setupJSON(lib, json, env) | ||
|
||
err = setupJSON(ctx, lib, json, setup, tempdir, env) | ||
return cleanup, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then this becomes return cleanup, nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/vulkan/loader/loader.go
Outdated
} | ||
|
||
func findLibraryAndJSON(ctx context.Context, libType layout.LibraryType) (file.Path, file.Path, error) { | ||
lib, err := layout.Library(ctx, libType) | ||
func findLibraryAndJSON(ctx context.Context, rs DeviceReplaySetup, tempdir string, libType layout.LibraryType) (string, file.Path, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although this is private, would be great to document the return values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
err = s.newHost(ctx, d, launchArgs) | ||
} else if d, ok := d.(adb.Device); ok { | ||
err = s.newADB(ctx, d, abi) | ||
err = s.newHost(ctx, d, abi, launchArgs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, it's a shame with the device abstractions that host and remote can't share the same code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
I think I might be able to clean this up a bit. I wanted to see how the gapit changes shook out before I tried unifying things like this. Since they will be similar in nature.
This will print the device information on stdout, as either json or a binary protobuf.
This allows an ssh configuration file to be created, and used to detect a remote SSH device.
We were writing both stdout and stderr to the same buffer in a very thread-unsafe way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a few comments I've made that don't seem to have been addressed. Please can you mark those you've done with a comment so I know what's still being looked at vs got skipped?
cmd/gapis/main.go
Outdated
@@ -142,6 +162,21 @@ func monitorAndroidDevices(ctx context.Context, r *bind.Registry, onDeviceScanDo | |||
} | |||
} | |||
|
|||
func getRemoteSSHDevices(ctx context.Context, r *bind.Registry, f io.Reader, scanDone func()) { | |||
// Populate the registry with all the existing devices. | |||
func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of this func
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function was originally longer, so it was for the defer(), but no longer needed.
core/app/layout/layout.go
Outdated
@@ -88,6 +90,11 @@ func withLibraryPlatformSuffix(lib string, os device.OSKind) string { | |||
} | |||
} | |||
|
|||
// GetLibraryName returns the filename of the given Library. | |||
func GetLibraryName(lib LibraryType, abi *device.ABI) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: drop Get
unless it collides with something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/os/device/remotessh/commands.go
Outdated
// remoteProcess is the interface to a running process, as started by a Target. | ||
type remoteProcess struct { | ||
session *ssh.Session | ||
wg sync.WaitGroup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gofmt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/os/device/remotessh/commands.go
Outdated
if err != nil { | ||
return nil, err | ||
} | ||
go func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping
core/os/device/remotessh/commands.go
Outdated
} | ||
p := &remoteProcess{ | ||
session: session, | ||
wg: sync.WaitGroup{}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gofmt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/os/device/remotessh/commands.go
Outdated
if err != nil { | ||
return | ||
} | ||
b.doTunnel(ctx, local, remotePort) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you be leaving this loop if doTunnel
returns an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely yes thanks. If we can't connect once, its not likely the next attempt will work.
core/os/device/remotessh/commands.go
Outdated
if err != nil { | ||
return 0, err | ||
} | ||
crash.Go( func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want to spin up (yet) another go-routine to monitor the cancellation of ctx
.
crash.Go(func() {
<-task.ShouldStop(ctx)
listener.Close()
})
Alternatively:
type TimeoutErr interface { Timeout() bool }
for !task.Stopped(ctx) {
listener.(*net.TCPListener).SetDeadline(time.Now().Add(time.Second))
local, err := listener.Accept()
if err != nil {
if t, ok := err.(TimeoutErr); ok && t.Timeout() {
continue
}
break // not a timeout
}
b.doTunnel(ctx, local, remotePort)
}
// UnmarshalJSON is used by json.Unmashall, this allows us to set | ||
// up a default configuration, so that unknown parameters have | ||
// sane defaults. | ||
func (c *Configuration) UnmarshalJSON(data []byte) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my previous comments here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the reason is very much for default values.
I was hoping to make sane defaults, but it seemed less clean (to me at least) to have a bunch of
func (c Configuration) Port() int {
if Port == 0 {
return 22;
}
}
For every parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, fair enough
core/os/device/remotessh/device.go
Outdated
WriteFile(ctx context.Context, contents io.Reader, mode string, destPath string) error | ||
// ForwardPort forwards the remote port. It automatically selects an open | ||
// local port. | ||
ForwardPort(ctx context.Context, remoteport int) (int, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually a more convenient API than the Forward
which was shared with ADB. I do wonder if we should have a common interface here (by adopting this new interface).
core/vulkan/loader/loader.go
Outdated
type deviceReplaySetup interface { | ||
// MakeTempDir returns a path to a created temporary. The returned function | ||
// can be called to clean up the temporary directory. | ||
MakeTempDir(ctx context.Context) (string, func(ctx context.Context), error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public methods in a private interface only really makes sense if the implementations are on public types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
core/os/device/remotessh/commands.go
Outdated
return | ||
} | ||
if err = b.doTunnel(ctx, local, remotePort); err != nil { | ||
break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Inconsistent mix of return
and break
KnownHosts string | ||
} | ||
|
||
// UnmarshalJSON is used by json.Unmashall, this allows us to set |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Unmarshal
// ReadConfiguration reads a set of configurations from then | ||
// given reader, and returns the configurations to the user. | ||
func ReadConfiguration(r io.Reader) ([]Configuration, error) { | ||
cfg := []Configuration{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cfgs := []Configuration{}
d := json.Decoder(r)
for d.More() {
cfg := {
User: u.Username,
Port: 22,
Keyfile: u.HomeDir + "/.ssh/id_rsa",
KnownHosts: u.HomeDir + "/.ssh/known_hosts",
}
if err := d.Decode(&cfg); err != nil {
return nil, err
}
cfgs = append(cfgs, cfg)
}
return cfgs, nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect, thank you for all the changes!
Thanks for the careful reviews! |
This is still somewhat in the experimental phase, it has no UI present yet, and can only be
enabled by command-line flags, but it adds functional remote SSH support.
This depends on both #1896 #1897 to be useful.