Is it possible to load livebooks from connected node?

I was wondering if it possible to load a notebook that is stored locally to a connected node?

2 Likes

@D4no0 there is no direct way. You would need to do it manually, so first read the content:

File.read!("/path/to/notebook") |> IO.puts()

Then copy the source, go to the Open page and in the “From source” tab paste the notebook content.

2 Likes

Thanks, but this is not quite what I had in mind.

I want to have some livebooks as interactive documentation for a project. I want to execute them from the remote server, as the server has a specific configuration and dependencies. I guess one way to achieve this is to build a custom docker image (as we are running infrastructure in docker) with livebook server and the files, as I don’t want to expose the node to the internet.

I want to execute them from the remote server, as the server has a specific configuration and dependencies.

Where the notebooks are stored is not related to where they are executed. If we are talking about a production app node, then baking the notebooks in there doesn’t really help. If you are running Livebook in Docker, then having notebooks in the image sounds better, the only disadvantage is that you need to redeploy Livebook if you change the notebooks (unless you use a volume). Alternatively you could plug S3 file system in Livebook settings and keep the notebooks there.

1 Like

I would like to keep the notebooks in a git system. I could configure the image to perform a git pull before starting the container and with a little configuration this can be pretty universal.

I could even do the pull script in a notebook itself :open_mouth:

1 Like

@D4no0 calling git pull from a notebook should totally work, nice!

1 Like

It is even better with the freshly released library: Egit - native interface to git

Aside from this, is it possible to run commands from local node when connected to remote node?

Because I’ve tried to run:

Node.spawn(:"livebook_node_name", fn -> File.ls!() end)

and it returns a strange error:

** (BadFunctionError) function #Function<0.63084264/3 in :elixir.eval_external_handler/1> is invalid, likely because it points to an old version of the code
(stdlib 4.3.1.1) erl_eval.erl:752: :erl_eval.do_apply/4

It is strange because livebook was escript installed by exactly the same version of elixir/OTP as the node it is connecting to.

The best way is to use :erpc, either :erpc.call(node, File, :ls!, []), or slightly more flexible :erpa.call(node, fn -> File.ls!() end). The latter requires closer Elixir/OTP versions because the representation of the anonymous function must match across nodes.

It is strange because livebook was escript installed by exactly the same version of elixir/OTP as the node it is connecting to.

That’s strange, my bet would be on version difference, not sure what else could cause this. That said, :erpc.call/4 has a better chance of working.

2 Likes

It works! Time to make some abominations.

1 Like

I started down this road on huggingface: Dockerfile · w0rd-driven/livebook at main.

I found git-sync which I believe is a series of shell scripts, which could be fine. One thing I couldn’t mentally work out is how to segregate a repo because I wouldn’t want to see generic sync commits every few minutes as notebooks save.

I thought about having a notebook as an app that uses Kino.start_child to load a filesystem watcher that’s responsible for kicking off the sync process. That could be configured independently, and perhaps have better more meaningful commit messages. It could potentially be interactive but I’ll forget to commit changes before huggingface kills an image. Apps execute top to bottom once the image boots up so it seems perfect as a supplement since we can’t really get to Livebook’s internal application/supervisors.

1 Like