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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ ci/
**/.Python
**/.pytest_cache
**/*.egg-info/
**/*.egg/
**/.uv/
**/.pip/

# Ignore build related stuff
**/build/
Expand Down
34 changes: 27 additions & 7 deletions .github/workflows/e2e-bm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ jobs:
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
echo "$HOME/.local/bin" >> $GITHUB_PATH
# Install uv to /usr/bin for flmrun service
sudo cp $HOME/.local/bin/uv /usr/bin/uv
sudo chmod +x /usr/bin/uv
echo "✓ Installed uv to /usr/bin/uv for flmrun"

- name: Install system dependencies
run: |
Expand Down Expand Up @@ -60,14 +64,27 @@ jobs:
- name: Install Flame with flmadm and systemd
run: |
sudo ./target/release/flmadm install --src-dir . --skip-build --prefix $INSTALL_PREFIX --enable
sudo chmod -R 777 $INSTALL_PREFIX
echo "$INSTALL_PREFIX/bin" >> $GITHUB_PATH

- name: Install Python SDK for current user
- name: Setup local configuration and packages directory
run: |
echo "=== Installing flamepy for current user ==="
echo "=== Copy local configuration ==="
mkdir -p ~/.flame
cp ci/flame-local.yaml ~/.flame/flame.yaml
echo "✓ Copied flame-local.yaml to ~/.flame/flame.yaml"

echo "=== Create packages directory ==="
sudo mkdir -p /opt/flame/packages
sudo chmod 0777 /opt/flame/packages
echo "✓ Created /opt/flame/packages with 0777 permissions"

- name: Install Python SDK for test client
run: |
echo "=== Installing flamepy for test client (not required for flmrun service) ==="
pip3 install --user ./sdk/python
echo "✓ flamepy installed for current user"
echo "✓ flamepy installed for test client"
echo ""
echo "Note: flmrun service uses 'uv run --with' to access flamepy from $INSTALL_PREFIX/sdk/python"

- name: Check service status
run: |
Expand All @@ -80,13 +97,16 @@ jobs:

echo "✓ Services are running via systemd"

- name: Verify Python SDK installation
- name: Verify Python SDK installation (for test client)
run: |
echo "=== Verify flamepy installation ==="
echo "=== Verify flamepy installation for test client ==="
pip3 show flamepy
echo ""
echo "=== Test import ==="
python3 -c "import flamepy; print('✓ flamepy imported successfully from:', flamepy.__file__)"
echo ""
echo "=== Verify SDK copied to installation directory ==="
ls -la $INSTALL_PREFIX/sdk/python/

- name: Install Python dependencies
run: |
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ e2e-py: ## Run Python E2E tests (use e2e-py-docker for docker compose or e2e-py-
@echo "Use 'make e2e-py-docker' for docker compose tests or 'make e2e-py-local' for local cluster tests"

e2e-py-docker: ## Run Python E2E tests with docker compose
$(CONTAINER_RUNTIME) compose exec -w /opt/e2e flame-console uv run -n pytest -vv --durations=0 .
$(CONTAINER_RUNTIME) compose exec -w /opt/e2e -e PYTHONPATH=/opt/e2e/src flame-console python3 -m pytest -vv --durations=0 .

e2e-py-local: ## Run Python E2E tests against local cluster
cd e2e && FLAME_ENDPOINT=$(FLAME_ENDPOINT) uv run pytest -vv --durations=0 .
e2e-py-local: ## Run Python E2E tests against local cluster (requires flamepy installed via pip)
cd e2e && PYTHONPATH="$(CURDIR)/e2e/src:$$PYTHONPATH" FLAME_ENDPOINT=$(FLAME_ENDPOINT) pytest -vv --durations=0 .

e2e-rs: ## Run Rust E2E tests
cargo test --workspace --exclude cri-rs -- --nocapture
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions ci/flame-local.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
current-cluster: flame
clusters:
- name: flame
endpoint: "http://127.0.0.1:8080"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The endpoint port 8080 in this client configuration file (ci/flame-local.yaml) does not match the endpoint port 30080 configured for the local Flame session manager in ci/flame-cluster-local.yaml. This mismatch will prevent the client from connecting to the session manager.

    endpoint: "http://127.0.0.1:30080"

cache:
endpoint: "grpc://127.0.0.1:9090"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The cache.endpoint protocol grpc in this client configuration file (ci/flame-local.yaml) does not match the http protocol configured for the local cache endpoint in ci/flame-cluster-local.yaml. This protocol mismatch will prevent the client from connecting to the cache service.

      endpoint: "http://127.0.0.1:9090"

package:
storage: "file:///opt/flame/packages"
excludes:
- "*.log"
- "*.pkl"
- "*.tmp"
17 changes: 12 additions & 5 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@ pub fn default_applications() -> HashMap<String, ApplicationAttributes> {
"description": "The output of the script in UTF-8."
});

// Get FLAME_HOME from environment or use default
let flame_home = std::env::var("FLAME_HOME").unwrap_or_else(|_| "/usr/local/flame".to_string());
let flmexec_cmd = format!("{}/bin/flmexec-service", flame_home);
let flmping_cmd = format!("{}/bin/flmping-service", flame_home);
let flmping_url = format!("file://{}/bin/flmping-service", flame_home);
let flamepy_sdk_path = format!("file://{}/sdk/python", flame_home);

HashMap::from([
(
"flmexec".to_string(),
Expand All @@ -189,7 +196,7 @@ pub fn default_applications() -> HashMap<String, ApplicationAttributes> {
description: Some(
"The Flame Executor application, which is used to run scripts.".to_string(),
),
command: Some("/usr/local/flame/bin/flmexec-service".to_string()),
command: Some(flmexec_cmd),
schema: Some(ApplicationSchema {
input: Some(script_input_schema.to_string()),
output: Some(script_output_schema.to_string()),
Expand All @@ -202,8 +209,8 @@ pub fn default_applications() -> HashMap<String, ApplicationAttributes> {
"flmping".to_string(),
ApplicationAttributes {
shim: Shim::Host,
url: Some("file:///opt/flame/bin/flmping-service".to_string()),
command: Some("/usr/local/flame/bin/flmping-service".to_string()),
url: Some(flmping_url),
command: Some(flmping_cmd),
..ApplicationAttributes::default()
},
),
Expand All @@ -215,13 +222,13 @@ pub fn default_applications() -> HashMap<String, ApplicationAttributes> {
"The Flame Runner application for executing customized Python applications."
.to_string(),
),
command: Some("/bin/uv".to_string()),
command: Some("/usr/bin/uv".to_string()),
arguments: vec![
"run".to_string(),
"--with".to_string(),
"pip".to_string(),
"--with".to_string(),
"flamepy @ file:///usr/local/flame/sdk/python".to_string(),
format!("flamepy @ {}", flamepy_sdk_path),
"python".to_string(),
"-m".to_string(),
"flamepy.rl.runpy".to_string(),
Expand Down
7 changes: 5 additions & 2 deletions docker/Dockerfile.console
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ RUN cargo install --path ./flmexec

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y wget vim iputils-ping ssh curl
RUN apt-get update && apt-get install -y wget vim iputils-ping ssh curl python3 python3-pip
COPY --from=builder /usr/local/cargo/bin/flmping /usr/local/bin/flmping
COPY --from=builder /usr/local/cargo/bin/flmctl /usr/local/bin/flmctl
COPY --from=builder /usr/local/cargo/bin/flmexec /usr/local/bin/flmexec

COPY --from=ghcr.io/astral-sh/uv:0.9.26 /uv /uvx /bin/
COPY --from=ghcr.io/astral-sh/uv:0.9.26 /uv /uvx /usr/bin/

RUN chmod +x /usr/local/bin/*

COPY ./sdk/python /usr/local/flame/sdk/python

# Install flamepy and pytest for test client using uv
RUN uv pip install --system --break-system-packages --no-cache /usr/local/flame/sdk/python pytest

CMD ["service", "ssh", "start", "-D"]
4 changes: 3 additions & 1 deletion docker/Dockerfile.fem
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ RUN mkdir -p /usr/local/flame/bin /usr/local/flame/work/tmp /usr/local/flame/sdk

WORKDIR /usr/local/flame/work

COPY --from=ghcr.io/astral-sh/uv:0.9.26 /uv /uvx /bin/
COPY --from=ghcr.io/astral-sh/uv:0.9.26 /uv /uvx /usr/bin/

COPY --from=builder /usr/local/cargo/bin/flame-executor-manager /usr/local/flame/bin/flame-executor-manager
COPY --from=builder /usr/local/cargo/bin/flmping-service /usr/local/flame/bin/flmping-service
Expand All @@ -26,4 +26,6 @@ RUN chmod +x /usr/local/flame/bin/*

COPY ./sdk/python /usr/local/flame/sdk/python

ENV FLAME_HOME=/usr/local/flame

ENTRYPOINT ["/usr/local/flame/bin/flame-executor-manager"]
7 changes: 5 additions & 2 deletions docker/Dockerfile.fsm
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ FROM ubuntu:24.04

RUN mkdir -p /usr/local/flame/bin
RUN mkdir -p /usr/local/flame/work
WORKDIR /usr/local/flame/work
RUN mkdir -p /usr/local/flame/migrations
WORKDIR /usr/local/flame

COPY session_manager/migrations /usr/local/flame/work/migrations
COPY session_manager/migrations /usr/local/flame/migrations
COPY --from=builder /usr/local/cargo/bin/flame-session-manager /usr/local/flame/bin/flame-session-manager

RUN chmod +x /usr/local/flame/bin/*

ENV FLAME_HOME=/usr/local/flame

ENTRYPOINT ["/usr/local/flame/bin/flame-session-manager"]
28 changes: 22 additions & 6 deletions docs/designs/RFE284-flmrun/RFE284-flmrun.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ spec:
command: /usr/bin/uv
arguments:
- run
- -n
- flamepy.runpy
- --with
- pip
- --with
- flamepy @ file:///usr/local/flame/sdk/python
- python
- -m
- flamepy.rl.runpy
...
```

Expand Down Expand Up @@ -137,20 +142,31 @@ dependencies = [
flamepy = { path = "/usr/local/flame/sdk/python" }
```

When building the image of `flame-executor-manager` and `flame-console`, the `flmrun` directory should be copy into `/usr/local/flame/work/flmrun` which is the working directory of `flmrun` application. The application should be started with `uv`.
When deploying Flame, the `flamepy` SDK source is copied to `${FLAME_HOME}/sdk/python`. The flmrun application uses `uv run --with` to reference flamepy directly from this location, eliminating the need for separate installation.

```yaml
metadata:
name: flmrun
spec:
working_directory: /usr/local/flame/work/flmrun
command: /usr/bin/uv
arguments:
- run
- -n
- flamepy.runpy
- --with
- pip # Provides pip for installing user packages
- --with
- flamepy @ file://${FLAME_HOME}/sdk/python # Provides flamepy SDK
- python
- -m
- flamepy.rl.runpy
```

This approach:
- **Self-contained**: All dependencies stay within the Flame installation directory
- **No user-level installation**: Flamepy SDK doesn't require separate pip install
- **User packages**: `pip` is available to install user-provided applications from the `url` field
- **Consistent**: Works identically in Docker, bare-metal, and CI environments
- **Isolated**: Multiple Flame versions can coexist without conflicts


## Use Cases

Expand Down
14 changes: 10 additions & 4 deletions e2e/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[project]
name = "e2e"
version = "0.1.0"
description = "Add your description here"
description = "The e2e test of Flame"
readme = "README.md"
authors = [
{ name = "XFLOPS Authors", email = "support@xflops.io" }
]
requires-python = ">=3.12"
requires-python = ">=3.9"
dependencies = [
"grpcio>=1.76",
"grpcio-tools>=1.76",
Expand All @@ -17,8 +17,13 @@ dependencies = [
e2e = "e2e:main"

[build-system]
requires = ["uv_build>=0.8.15,<0.9.0"]
build-backend = "uv_build"
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.setuptools.packages.find]
where = ["src"]
include = ["e2e*"]
exclude = ["tests*"]

[dependency-groups]
dev = [
Expand All @@ -29,3 +34,4 @@ dev = [

[tool.uv.sources]
flamepy = { path = "/usr/local/flame/sdk/python", editable = true }

5 changes: 2 additions & 3 deletions e2e/src/e2e/basic_svc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import logging
from typing import Optional

from flamepy.core.types import TaskOutput

from e2e.api import (
TestRequest,
Expand Down Expand Up @@ -58,7 +57,7 @@ def on_session_enter(self, context: flamepy.SessionContext):
f"task count reset to: {self._task_count}"
)

def on_task_invoke(self, context: flamepy.TaskContext) -> Optional[TaskOutput]:
def on_task_invoke(self, context: flamepy.TaskContext) -> Optional[flamepy.TaskOutput]:
"""Handle task invoke and return response with optional context information."""
logger.info(
f"Task invoked: task_id={context.task_id}, "
Expand Down Expand Up @@ -204,7 +203,7 @@ def on_task_invoke(self, context: flamepy.TaskContext) -> Optional[TaskOutput]:
f"Task completed successfully: task_id={context.task_id}, "
f"response_size={len(response_bytes)} bytes"
)
return TaskOutput(response_bytes)
return flamepy.TaskOutput(response_bytes)

def on_session_leave(self):
"""Handle session leave."""
Expand Down
3 changes: 1 addition & 2 deletions e2e/src/e2e/error_svc.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import flamepy
import logging
from typing import Optional
from flamepy.core.types import TaskOutput

logger = logging.getLogger(__name__)

Expand All @@ -31,7 +30,7 @@ def on_session_enter(self, context: flamepy.SessionContext):
logger.info(f"Session entered: session_id={context.session_id}")
self._session_context = context

def on_task_invoke(self, context: flamepy.TaskContext) -> Optional[TaskOutput]:
def on_task_invoke(self, context: flamepy.TaskContext) -> Optional[flamepy.TaskOutput]:
"""Handle task invoke by raising an exception."""
logger.info(
f"Task invoked: task_id={context.task_id}, session_id={context.session_id}"
Expand Down
6 changes: 3 additions & 3 deletions e2e/src/e2e/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import cloudpickle
from dataclasses import asdict
from typing import Optional, Any
from flamepy.core import ObjectRef, put_object, get_object
from flamepy.core.types import short_name
from flamepy.rl.types import RunnerContext, RunnerRequest
from flamepy import ObjectRef, put_object, get_object
from flamepy.util import short_name
from flamepy.rl import RunnerContext, RunnerRequest

from e2e.api import (
TestRequest,
Expand Down
1 change: 0 additions & 1 deletion e2e/src/e2e/instance_svc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
limitations under the License.
"""

import flamepy
from flamepy import agent

from e2e.api import TestRequest, TestResponse, TestContext
Expand Down
2 changes: 1 addition & 1 deletion e2e/tests/test_flmrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def test_flmrun_application_registered():
assert flmrun.name == FLMRUN_E2E_APP
assert flmrun.shim == flamepy.Shim.Host
assert flmrun.state == flamepy.ApplicationState.ENABLED
assert flmrun.command == "/bin/uv"
assert flmrun.command == "/usr/bin/uv"


def test_flmrun_sum_function():
Expand Down
11 changes: 11 additions & 0 deletions flmadm/src/commands/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ fn validate_config(config: &InstallConfig) -> Result<()> {
anyhow::bail!("Cannot use --enable without systemd (--no-systemd conflicts with --enable)");
}

// Check if uv is available at /usr/bin/uv (required by flmrun)
let uv_path = std::path::Path::new("/usr/bin/uv");
if !uv_path.exists() {
anyhow::bail!(
"uv is not installed at /usr/bin/uv (required by flmrun service)\n\
Please install uv using one of these methods:\n\
1. curl -LsSf https://astral.sh/uv/install.sh | sh && sudo cp ~/.local/bin/uv /usr/bin/uv\n\
2. Or install uv via your package manager and ensure it's at /usr/bin/uv"
);
}

println!("✓ Configuration validated");
Ok(())
}
Expand Down
Loading
Loading