Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions plugin/examples/basic.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import marimo

__generated_with = "0.16.4"
__generated_with = "0.18.4"
app = marimo.App()


@app.cell
def hello():
return


@app.cell
def _():
print("In notebook.py")
Expand Down
24 changes: 15 additions & 9 deletions plugin/examples/getting-started.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,15 @@
app = marimo.App()


@app.cell
def _():
import marimo as mo

@app.cell(hide_code=True)
def _(mo):
mo.md("""
# Welcome to marimo on Kubernetes!

This notebook introduces marimo's **reactive execution model** — if you're
coming from Jupyter, this is the key difference to understand.
""")
return (mo,)
return


@app.cell
Expand All @@ -31,9 +29,10 @@ def _(mo):
return (slider,)


@app.cell
@app.cell(hide_code=True)
def _(mo, slider):
mo.md(f"""
mo.md(
f"""
## Reactive execution

You picked **{slider.value}**. Try moving the slider above!
Expand All @@ -48,7 +47,8 @@ def _(mo, slider):
2. **Cells run in dependency order** — Not top-to-bottom, but based on
which variables each cell uses
3. **No cell numbers** — Order on the page doesn't determine execution order
""")
"""
)
return


Expand All @@ -66,7 +66,7 @@ def _(mo, slider):
return


@app.cell
@app.cell(hide_code=True)
def _(mo):
mo.md("""
## Next steps
Expand All @@ -80,5 +80,11 @@ def _(mo):
return


@app.cell
def _():
import marimo as mo
return (mo,)


if __name__ == "__main__":
app.run()
8 changes: 3 additions & 5 deletions plugin/examples/with-cw.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# /// script
# dependencies = ["marimo"]
# ///
# [tool.marimo.k8s]
# mounts = ["cw://operator-bucket"]
# ///
# Note: cw-credentials secret is auto-created from ~/.s3cfg by the plugin

import marimo
Expand Down Expand Up @@ -33,13 +33,11 @@ def check_mount():
result = subprocess.run(["ps", "aux"], capture_output=True, text=True)
if "s3fs" in result.stdout:
print("\ns3fs process is running!")
return
return (os,)


@app.cell
def read_test_file():
import os

def read_test_file(os):
test_file = "/home/marimo/notebooks/mounts/cw-0/test-data.txt"
if os.path.exists(test_file):
with open(test_file) as f:
Expand Down
4 changes: 0 additions & 4 deletions plugin/examples/with-rsync.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@
app = marimo.App()


@app.cell
def _():
import marimo as mo

return


if __name__ == "__main__":
Expand Down
3 changes: 1 addition & 2 deletions plugin/examples/with-sshfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
@app.cell
def check_mount():
import os
import marimo as mo

mount_path = "/home/marimo/notebooks/mounts/sshfs-0"
exists = os.path.exists(mount_path)
files = os.listdir(mount_path) if exists else []
os.listdir(mount_path) if exists else []
return


Expand Down
3 changes: 1 addition & 2 deletions plugin/examples/with-storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
@app.cell
def check():
import os
import marimo as mo

path = "/home/marimo/notebooks"
files = os.listdir(path) if os.path.exists(path) else []
os.listdir(path) if os.path.exists(path) else []
return


Expand Down
33 changes: 28 additions & 5 deletions plugin/kubectl_marimo/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
import click

from .formats import parse_file
from .k8s import apply_resource
from .k8s import apply_resource, delete_resource, resource_exists
from .resources import build_marimo_notebook, resource_name, compute_hash, to_yaml
from .swap import read_swap_file, write_swap_file, create_swap_meta
from .swap import read_swap_file, write_swap_file, create_swap_meta, delete_swap_file
from .sync import sync_notebook


Expand Down Expand Up @@ -401,9 +401,17 @@ def deploy_notebook(
click.echo("Error: SSH pubkey required for sshfs mounts", err=True)
sys.exit(1)

# Apply to cluster
if not apply_resource(resource):
sys.exit(1)
# Check if resource already exists
already_exists = resource_exists("marimos.marimo.io", name, namespace)

if already_exists:
click.echo(
click.style(f"Pod '{name}' already exists. Reconnecting...", fg="yellow")
)
else:
# Apply to cluster
if not apply_resource(resource):
sys.exit(1)

# Handle rsync mounts - need to wait for pod ready first
if rsync_mounts:
Expand Down Expand Up @@ -573,6 +581,21 @@ def open_notebook(
sync_notebook(file_path, namespace=namespace, force=True)
except Exception as e:
click.echo(f"Warning: Sync failed: {e}", err=True)

# Prompt user about teardown
keep_running = click.confirm("Keep pod running?", default=False)
if not keep_running:
click.echo("Tearing down pod...")
try:
delete_resource("marimos.marimo.io", name, namespace)
delete_swap_file(file_path)
except Exception as e:
click.echo(f"Warning: Teardown failed: {e}", err=True)
else:
click.echo(
f"Pod '{name}' left running. Use 'kubectl-marimo delete' to remove later."
)

click.echo("Done")


Expand Down
21 changes: 21 additions & 0 deletions plugin/kubectl_marimo/k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,27 @@ def delete_resource(
return False


def resource_exists(kind: str, name: str, namespace: str | None = None) -> bool:
"""Check if a Kubernetes resource exists.

Args:
kind: Resource kind (e.g., "marimos.marimo.io", "pod")
name: Resource name
namespace: Kubernetes namespace (None = use kubectl context)

Returns:
True if resource exists, False otherwise
"""
cmd = ["kubectl", "get", kind, name, "-o", "name"]
if namespace is not None:
cmd.extend(["-n", namespace])
try:
result = subprocess.run(cmd, capture_output=True, text=True)
return result.returncode == 0
except FileNotFoundError:
return False


def exec_in_pod(
pod_name: str,
namespace: str | None,
Expand Down
58 changes: 58 additions & 0 deletions plugin/tests/test_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,64 @@ def test_continues_if_patch_fails(self, notebook_file, mock_k8s, mock_swap, mock
mock_k8s["delete_resource"].assert_called_once()


class TestResourceExists:
"""Tests for resource_exists function."""

def test_resource_exists_returns_true(self, mocker):
"""Returns True when resource exists."""
from kubectl_marimo.k8s import resource_exists

mock_run = mocker.patch("kubectl_marimo.k8s.subprocess.run")
mock_run.return_value.returncode = 0

result = resource_exists("marimos.marimo.io", "test-notebook", "default")

assert result is True
args = mock_run.call_args[0][0]
assert "kubectl" in args
assert "get" in args
assert "marimos.marimo.io" in args
assert "test-notebook" in args
assert "-n" in args
assert "default" in args

def test_resource_exists_returns_false_not_found(self, mocker):
"""Returns False when resource doesn't exist."""
from kubectl_marimo.k8s import resource_exists

mock_run = mocker.patch("kubectl_marimo.k8s.subprocess.run")
mock_run.return_value.returncode = 1

result = resource_exists("marimos.marimo.io", "nonexistent", "default")

assert result is False

def test_resource_exists_no_namespace(self, mocker):
"""Works without namespace argument."""
from kubectl_marimo.k8s import resource_exists

mock_run = mocker.patch("kubectl_marimo.k8s.subprocess.run")
mock_run.return_value.returncode = 0

result = resource_exists("marimos.marimo.io", "test-notebook")

assert result is True
args = mock_run.call_args[0][0]
assert "-n" not in args

def test_resource_exists_kubectl_not_found(self, mocker):
"""Returns False when kubectl not in PATH."""
from kubectl_marimo.k8s import resource_exists

mocker.patch(
"kubectl_marimo.k8s.subprocess.run", side_effect=FileNotFoundError()
)

result = resource_exists("marimos.marimo.io", "test-notebook", "default")

assert result is False


class TestPatchResource:
"""Tests for patch_resource function."""

Expand Down
Loading