From d9b836925d5384e2450b5da77612bb39f460d316 Mon Sep 17 00:00:00 2001 From: fchrstou Date: Mon, 3 Jul 2023 13:52:04 +0200 Subject: [PATCH] Added tests and review comments --- .github/workflows/CI.yml | 1 + bin/julia-r | 2 +- docs/src/howto.md | 9 +++++++++ docs/src/reference.md | 2 +- src/RemoteREPL.jl | 2 +- src/client.jl | 30 ++++++++++++++---------------- src/server.jl | 9 ++++----- test/runtests.jl | 24 ++++++++++++++++++------ 8 files changed, 49 insertions(+), 30 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0f95f7e..8a04004 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -73,6 +73,7 @@ jobs: - uses: julia-actions/julia-runtest@v1 env: SSH_AUTH_SOCK: /tmp/ssh_agent.sock + - run: julia -e "import Pkg; Pkg.add(\"TestEnv\")" docs: name: Documentation runs-on: ubuntu-latest diff --git a/bin/julia-r b/bin/julia-r index be8881e..f14db85 100755 --- a/bin/julia-r +++ b/bin/julia-r @@ -19,7 +19,7 @@ atreplinit() do repl # Run one command as part of connection setup to trigger compilation # This makes the REPL more immediately responsive after it prints the # welcome message. - RemoteREPL.run_remote_repl_command(RemoteREPL._repl_client_connection, + remotecmd(RemoteREPL._repl_client_connection, stdout, "\"hi\"") println(""" Connected to $host diff --git a/docs/src/howto.md b/docs/src/howto.md index 33c2963..f3e4813 100644 --- a/docs/src/howto.md +++ b/docs/src/howto.md @@ -118,6 +118,15 @@ julia> session_id = UUID("f03aec15-3e14-4d58-bcfa-82f8d33c9f9a") julia> connect_repl(; session_id=session_id) ``` +## Pass a command non-interactively +To programmatically pass a command to the remote julia kernel use [`remotecmd`](@ref). For example: + +```julia +julia> con2server = connect_remote(Sockets.localhost, 9093) # connect to port 9093 in localhost + +julia> remotecmd(con2server, "myvar = 1") # define a new var +``` + ## Use alternatives to SSH ### AWS Session Manager diff --git a/docs/src/reference.md b/docs/src/reference.md index 11c2919..7995035 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -42,7 +42,7 @@ serve_repl connect_remote RemoteREPL.@remote RemoteREPL.remote_eval -RemoteREPL.run_remote_repl_command +RemoteREPL.remotecmd RemoteREPL.remote_module! ``` diff --git a/src/RemoteREPL.jl b/src/RemoteREPL.jl index f010c7c..eb2f267 100644 --- a/src/RemoteREPL.jl +++ b/src/RemoteREPL.jl @@ -5,7 +5,7 @@ using Sockets, Serialization using UUIDs, Logging using OpenSSH_jll -export connect_repl, serve_repl, @remote, connect_remote, run_remote_repl_command, remote_module! +export connect_repl, serve_repl, @remote, connect_remote, remotecmd, remote_module! const DEFAULT_PORT = 27754 const PROTOCOL_MAGIC = "RemoteREPL" diff --git a/src/client.jl b/src/client.jl index 6a60eeb..73828e6 100644 --- a/src/client.jl +++ b/src/client.jl @@ -307,12 +307,12 @@ function REPL.complete_line(provider::RemoteCompletionProvider, end """ - run_remote_repl_command(conn::Connection, out_stream::IO, cmdstr::String) + remotecmd(conn::Connection, out_stream::IO, cmdstr::String) Evaluate `cmdstr` in the remote session of connection `conn` and write result into `out_stream`. Also supports the magic `RemoteREPL` commands like `%module` and `%include`. """ -function run_remote_repl_command(conn::Connection, out_stream::IO, cmdstr::String) +function remotecmd(conn::Connection, out_stream::IO, cmdstr::String) # Compute command magic = match_magic_syntax(cmdstr) if isnothing(magic) @@ -382,41 +382,39 @@ function run_remote_repl_command(conn::Connection, out_stream::IO, cmdstr::Strin end """ - run_remote_repl_command(cmdstr::String) + remotecmd(cmdstr::String) Evaluate `cmdstr` in the last opened RemoteREPL connection and print result to `Base.stdout` """ -run_remote_repl_command(cmd::String) = run_remote_repl_command(_repl_client_connection, Base.stdout, cmd) +remotecmd(cmd::String) = remotecmd(_repl_client_connection, Base.stdout, cmd) """ - run_remote_repl_command(conn::Connection, cmdstr::String) + remotecmd(conn::Connection, cmdstr::String) Evaluate `cmdstr` in the connection `conn` and print result to `Base.stdout`. """ -run_remote_repl_command(conn::Connection, cmd::String) = run_remote_repl_command(conn, Base.stdout, cmd) +remotecmd(conn::Connection, cmd::String) = remotecmd(conn, Base.stdout, cmd) """ - remote_module!(mod::Module, conn::Connection = _repl_client_connection) + remote_module!(conn::Connection = _repl_client_connection, mod::Module) Change future remote commands in the session of connection `conn` to be evaluated into module `mod`. The default connection `_repl_client_connection` is the last established RemoteREPL connection. If the module cannot be evaluated locally pass the name as a string. Equivalent to using the `%module` magic. """ -function remote_module!(mod::Module, conn=_repl_client_connection) - run_remote_repl_command(conn, Base.stdout, "%module $(mod)") -end +remote_module!(conn::Connection, mod::Module) = remotecmd(conn, Base.stdout, "%module $(mod)") +remote_module!(mod::Module) = remotecmd(_repl_client_connection, Base.stdout, "%module $(mod)") """ - remote_module!(modstr::String, conn::Connection = _repl_client_connection) + remote_module!(conn::Connection = _repl_client_connection, modstr::String) Change future remote commands in the session of connection `conn` to be evaluated into module identified by `modstr`. The default connection `_repl_client_connection` is the last established RemoteREPL connection. Equivalent to using the `%module` magic. """ -function remote_module!(modstr::String, conn=_repl_client_connection) - run_remote_repl_command(conn, Base.stdout, "%module "*modstr) -end +remote_module!(conn::Connection, modstr::String) = remotecmd(conn, Base.stdout, "%module "*modstr) +remote_module!(modstr::String) = remotecmd(_repl_client_connection, Base.stdout, "%module "*modstr) @@ -491,7 +489,7 @@ function connect_repl(host=Sockets.localhost, port::Integer=DEFAULT_PORT; conn = connect_remote(host, port; tunnel, ssh_opts, region, namespace, session_id) out_stream = stdout - prompt = ReplMaker.initrepl(c->run_remote_repl_command(conn, out_stream, c), + prompt = ReplMaker.initrepl(c->remotecmd(conn, out_stream, c), repl = Base.active_repl, valid_input_checker = valid_input_checker, prompt_text = ()->repl_prompt_text(conn), @@ -606,7 +604,7 @@ function remote_eval(host, port::Integer, cmdstr::AbstractString; local result try setup_connection!(conn) - result = run_remote_repl_command(conn, IOBuffer(), cmdstr) + result = remotecmd(conn, IOBuffer(), cmdstr) finally close(conn) end diff --git a/src/server.jl b/src/server.jl index bef1ac9..e33ec4e 100644 --- a/src/server.jl +++ b/src/server.jl @@ -252,16 +252,15 @@ function serve_repl(server::Base.IOServer; on_client_connect=nothing) while isopen(server) socket = accept(server) - session, session_id, socketidx = lock(session_lock) do - # expect session id - session_id = deserialize(socket) - session = if haskey(open_sessions, session_id) + # expect session id + session_id = deserialize(socket) + session = lock(session_lock) do + if haskey(open_sessions, session_id) push!(open_sessions[session_id].sockets, socket) open_sessions[session_id] else open_sessions[session_id] = ServerSideSession([socket], Dict(), Main) end - session, session_id, length(session.sockets) end peer = getpeername(socket) diff --git a/test/runtests.jl b/test/runtests.jl index 6e1579e..53e197d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -64,12 +64,13 @@ end @test repl_prompt_text(fake_conn("ABC", DEFAULT_PORT, is_open=false)) == "julia@ABC [disconnected]> " end -function wait_conn(host, port, use_ssh; max_tries=4) +function wait_conn(host, port, use_ssh; max_tries=4, session_id=nothing) for i=1:max_tries try return RemoteREPL.Connection(host=host, port=port, - tunnel=use_ssh ? :ssh : :none, - ssh_opts=`-o StrictHostKeyChecking=no`) + tunnel=use_ssh ? :ssh : :none, + ssh_opts=`-o StrictHostKeyChecking=no`, + session_id=session_id) catch exc if i == max_tries rethrow() @@ -81,7 +82,7 @@ function wait_conn(host, port, use_ssh; max_tries=4) end function runcommand_unwrap(conn, cmdstr) - result = RemoteREPL.run_remote_repl_command(conn, IOBuffer(), cmdstr) + result = remotecmd(conn, IOBuffer(), cmdstr) # Unwrap Text for testing purposes return result isa Text ? result.content : result end @@ -113,7 +114,7 @@ end # Use non-default port to avoid clashes with concurrent interactive use or testing. test_port = RemoteREPL.find_free_port(Sockets.localhost) -server_proc = run(`$(Base.julia_cmd()) -e "using Sockets; using RemoteREPL; serve_repl($test_port)"`, wait=false) +server_proc = run(`$(Base.julia_cmd()) --project -e "using TestEnv; TestEnv.activate(); using RemoteREPL, Sockets, UUIDs ; serve_repl($test_port)"`, wait=false) try @@ -301,7 +302,7 @@ end test_port = RemoteREPL.find_free_port(Sockets.localhost) -server_proc = run(```$(Base.julia_cmd()) -e "using Sockets; using RemoteREPL; module EvalInMod ; end; +server_proc = run(```$(Base.julia_cmd()) --project -e "using TestEnv; TestEnv.activate(); using RemoteREPL, Sockets, UUIDs ; module EvalInMod ; end; serve_repl($test_port, on_client_connect=sess->sess.in_module=EvalInMod)"```, wait=false) try @@ -311,6 +312,17 @@ try runcommand(cmdstr) = runcommand_unwrap(conn, cmdstr) @test runcommand("@__MODULE__") == "Main.EvalInMod" + + # common sessions + sid = uuid4() + conn2 = wait_conn(test_interface, test_port, use_ssh; session_id = sid) + conn3 = wait_conn(test_interface, test_port, use_ssh; session_id = sid) + + # change module to Main once + remote_module!(conn2, "Main") + + @test runcommand_unwrap(conn2, "@__MODULE__") == "Main" + @test runcommand_unwrap(conn3, "@__MODULE__") == "Main" end finally