Skip to content

Commit

Permalink
Fondant build - add test case and documentation (#546)
Browse files Browse the repository at this point in the history
This PR adds documentation and a test case for the fondant build
command.

fix #530
  • Loading branch information
mrchtr authored Oct 24, 2023
1 parent d075a6c commit d0c64c0
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 5 deletions.
40 changes: 35 additions & 5 deletions docs/components/custom_component.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ To implement a custom component, a couple of files need to be defined:

Each Fondant component is defined by a specification which describes its interface. This
specification is represented by a single `fondant_component.yaml` file. See the [component
specification page](../components/component_spec.md) for info on how to write the specification for your component.
specification page](../components/component_spec.md) for info on how to write the specification for
your component.

## Main.py script

Expand Down Expand Up @@ -64,8 +65,8 @@ class ExampleComponent(PandasTransformComponent):
```

The `__init__` method is called once for each component class with custom arguments defined in the
`args` section of the [component specification](../components/component_spec.md).) This is a good
place to initialize resources and costly initializations such as network connections, models,
`args` section of the [component specification](../components/component_spec.md).) This is a good
place to initialize resources and costly initializations such as network connections, models,
parsing a config file, etc. By doing so, you can effectively prevent the redundant re-initialization
of resources each time the `transform` method is invoked.

Expand All @@ -87,7 +88,8 @@ which is then imported in the `main.py` script.

## Dockerfile

The `Dockerfile` defines how to build the component into a Docker image. An example Dockerfile is defined below.
The `Dockerfile` defines how to build the component into a Docker image. An example Dockerfile is
defined below.

```bash
FROM --platform=linux/amd64 pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime
Expand All @@ -112,11 +114,39 @@ ENTRYPOINT ["fondant", "execute", "main"]

## Requirements.txt

A `requirements.txt` file lists the Python dependencies of the component. Note that any Fondant component will always have `Fondant` as the minimum requirement. It's important to also pin the version of each dependency to make sure the component remains working as expected. Below is an example of a component that relies on several Python libraries such as Pillow, PyTorch and Transformers.
A `requirements.txt` file lists the Python dependencies of the component. Note that any Fondant
component will always have `Fondant` as the minimum requirement. It's important to also pin the
version of each dependency to make sure the component remains working as expected. Below is an
example of a component that relies on several Python libraries such as Pillow, PyTorch and
Transformers.

```
fondant
Pillow==10.0.1
torch==2.0.1
transformers==4.29.2
```

## Build the component

Before you can use your custom component within your pipeline you have to build the component into a
Docker container. The Fondant CLI offers a specialized `fondant build` command designed
explicitly for this task.
To commence the component building process, simply execute:

```
fondant build <component dir> -t <image tag>
```

Ensure that you replace `<component dir>` with the accurate path to the directory where your
component is located. The `-t` flag is used to specify the Docker container tag.

When you use the `-t` flag, the tag in the referenced component specification yaml will also be
updated, ensuring that the next pipeline run correctly references the image.

It's important to note that the `fondant build` command offers additional arguments. To access a
complete list of all available arguments, you can execute `fondant build -h`.




1 change: 1 addition & 0 deletions tests/example_component/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FROM --platform=linux/amd64 python:3.8-slim as base
3 changes: 3 additions & 0 deletions tests/example_component/fondant_component.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: dummy component
description: dummy component for testing purpose
image: image:local
50 changes: 50 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import argparse
import subprocess
from pathlib import Path
from unittest.mock import patch

import pytest
from fondant.cli import (
ComponentImportError,
PipelineImportError,
build,
compile_kfp,
compile_local,
compile_vertex,
Expand Down Expand Up @@ -314,3 +316,51 @@ def test_vertex_run(tmp_path_factory):
service_account=None,
network=None,
)


@patch("docker.api.client.APIClient.push")
@patch("docker.api.client.APIClient.build")
def test_component_build(mock_build, mock_push):
"""Test that the build command works as expected."""
args = argparse.Namespace(
component_dir=Path(__file__).parent / "example_component",
tag="image:test",
build_arg=["key=value"],
nocache=True,
pull=True,
target="base",
)

# Set up the return values for the mocked methods
mock_build.return_value = ["Dummy logs build"]
mock_push.return_value = [{"status": "dummy log status"}]

# Run build command
build(args)

# Check that docker build and push were executed correctly
mock_build.assert_called_with(
path=str(Path(__file__).parent / "example_component"),
tag="image:test",
buildargs={"key": "value"},
nocache=True,
pull=True,
target="base",
decode=True,
)

mock_push.assert_called_with("image", tag="test", stream=True, decode=True)

# Check that the component specification file was updated correctly
with open(
Path(__file__).parent / "example_component" / "fondant_component.yaml",
"r+",
) as f:
content = f.read()
assert "image:test" in content

# Revert image name in component specification
content = content.replace("image:test", "image:local")
f.seek(0)
f.write(content)
f.truncate()

0 comments on commit d0c64c0

Please sign in to comment.