Skip to content

Migrate models to Pydantic 2 #47

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
58 changes: 25 additions & 33 deletions python/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
FROM ubuntu:jammy as build-image
FROM ubuntu:noble as python-builder

RUN apt-get update && \
apt-get upgrade -y && \
apt-get install --no-install-recommends python3.10-venv git -y && \
apt-get install -y python3.12 python3.12-venv && \
rm -rf /var/lib/apt/lists/*

# build into a venv we can copy across
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN python3.12 -m venv /venv && \
/venv/bin/pip install -U pip setuptools

COPY . /perftest
RUN pip install -U pip setuptools
RUN pip install --no-deps --requirement /perftest/requirements.txt
RUN pip install -e /perftest
COPY requirements.txt /app/requirements.txt
RUN /venv/bin/pip install --no-deps --requirement /app/requirements.txt

#
# Now the image we run with
#
FROM ubuntu:jammy as run-image
# In order for template loading to work correctly, this has to be an editable mode install
COPY . /app
RUN /venv/bin/pip install --no-deps -e /app

RUN apt-get update && \
apt-get upgrade -y && \
apt-get install --no-install-recommends python3 tini ca-certificates -y && \
rm -rf /var/lib/apt/lists/*

# Copy accross the venv
COPY --from=build-image /opt/venv /opt/venv
# Copy code to keep editable install working
COPY . /perftest
ENV PATH="/opt/venv/bin:$PATH"
FROM ubuntu:noble

# Don't buffer stdout and stderr as it breaks realtime logging
ENV PYTHONUNBUFFERED 1

# Make httpx use the system trust roots
# By default, this means we use the roots baked into the image
ENV SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt

# Create the user that will be used to run the app
ENV APP_UID 1001
Expand All @@ -44,19 +38,17 @@ RUN groupadd --gid $APP_GID $APP_GROUP && \
--uid $APP_UID \
$APP_USER

# Install tini, which we will use to marshal the processes
RUN apt-get update && \
apt-get install -y tini && \
apt-get install -y \
--no-install-recommends \
--no-install-suggests \
ca-certificates python3.12 tini \
&& \
rm -rf /var/lib/apt/lists/*

# Don't buffer stdout and stderr as it breaks realtime logging
ENV PYTHONUNBUFFERED 1

# Make httpx use the system trust roots
# By default, this means we use the CAs from the ca-certificates package
ENV SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt
COPY --from=python-builder /venv /venv
COPY --from=python-builder /app /app

# By default, run the operator using kopf
USER $APP_UID
ENTRYPOINT ["tini", "-g", "--"]
CMD ["kopf", "run", "--module", "perftest.operator", "--all-namespaces"]
CMD ["/venv/bin/kopf", "run", "--module", "perftest.operator", "--all-namespaces"]
26 changes: 13 additions & 13 deletions python/perftest/models/v1alpha1/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import ipaddress
import typing as t

from pydantic import Field, constr
from pydantic import Field

from kube_custom_resource import CustomResource, schema

Expand All @@ -22,11 +22,11 @@ class ContainerResources(schema.BaseModel):
"""
Model for specifying container resources.
"""
requests: schema.Dict[str, t.Any] = Field(
requests: schema.Dict[str, schema.Any] = Field(
default_factory = dict,
description = "The resource requests for the pod."
)
limits: schema.Dict[str, t.Any] = Field(
limits: schema.Dict[str, schema.Any] = Field(
default_factory = dict,
description = "The resource limits for the pod."
)
Expand All @@ -40,18 +40,18 @@ class BenchmarkSpec(schema.BaseModel):
False,
description = "Indicates whether to use host networking or not."
)
network_name: t.Optional[constr(min_length = 1)] = Field(
network_name: schema.Optional[schema.constr(min_length = 1)] = Field(
None,
description = (
"The name of a Multus network over which to run the benchmark. "
"Only used when host networking is false."
)
)
resources: t.Optional[ContainerResources] = Field(
resources: schema.Optional[ContainerResources] = Field(
None,
description = "The resources to use for benchmark containers."
)
mtu: t.Optional[schema.conint(gt = 0)] = Field(
mtu: schema.Optional[schema.conint(gt = 0)] = Field(
None,
description = (
"The MTU to use for the benchmark. "
Expand Down Expand Up @@ -96,15 +96,15 @@ class ResourceRef(schema.BaseModel):
"""
Reference to a resource that is part of a benchmark.
"""
api_version: constr(min_length = 1) = Field(
api_version: schema.constr(min_length = 1) = Field(
...,
description = "The API version of the resource."
)
kind: constr(min_length = 1) = Field(
kind: schema.constr(min_length = 1) = Field(
...,
description = "The kind of the resource."
)
name: constr(min_length = 1) = Field(
name: schema.constr(min_length = 1) = Field(
...,
description = "The name of the resource."
)
Expand All @@ -118,7 +118,7 @@ class PodInfo(schema.BaseModel):
...,
description = "The IP of the pod."
)
node_name: constr(min_length = 1) = Field(
node_name: schema.constr(min_length = 1) = Field(
...,
description = "The name of the node that the pod was scheduled on."
)
Expand Down Expand Up @@ -147,19 +147,19 @@ class BenchmarkStatus(schema.BaseModel):
BenchmarkPhase.UNKNOWN,
description = "The phase of the benchmark."
)
priority_class_name: t.Optional[constr(min_length = 1)] = Field(
priority_class_name: schema.Optional[schema.constr(min_length = 1)] = Field(
None,
description = "The name of the priority class for the benchmark."
)
managed_resources: t.List[ResourceRef] = Field(
default_factory = list,
description = "List of references to the managed resources for this benchmark."
)
started_at: t.Optional[datetime.datetime] = Field(
started_at: schema.Optional[datetime.datetime] = Field(
None,
description = "The time at which the benchmark started."
)
finished_at: t.Optional[datetime.datetime] = Field(
finished_at: schema.Optional[datetime.datetime] = Field(
None,
description = "The time at which the benchmark finished."
)
Expand Down
36 changes: 20 additions & 16 deletions python/perftest/models/v1alpha1/fio.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import itertools as it
import json
import re
import typing as t

from pydantic import Field, constr
from pydantic import Field

from kube_custom_resource import schema

Expand All @@ -13,6 +11,7 @@

from . import base


class FioRW(str, schema.Enum):
"""
Enumeration of supported Fio rw modes.
Expand All @@ -24,24 +23,27 @@ class FioRW(str, schema.Enum):
RW_READWRITE = "rw,readwrite"
RANDRW = "randrw"


class FioDirect(int, schema.Enum):
"""
Enumeration of supported Fio direct Bools.
"""
TRUE = 1
FALSE = 0


class FioIOEngine(str, schema.Enum):
"""
Enumeration of supported Fio ioengines.
"""
LIBAIO = "libaio"


class FioSpec(base.BenchmarkSpec):
"""
Defines the parameters for the Fio benchmark.
"""
image: constr(min_length = 1) = Field(
image: schema.constr(min_length = 1) = Field(
f"{settings.default_image_prefix}fio:{settings.default_image_tag}",
description = "The image to use for the benchmark."
)
Expand All @@ -53,7 +55,7 @@ class FioSpec(base.BenchmarkSpec):
8765,
description = "The port that the Fio sever listens on."
)
volume_claim_template: schema.Dict[str, t.Any] = Field(
volume_claim_template: schema.Dict[str, schema.Any] = Field(
default_factory = dict,
description = "The template that describes the PVC to mount on workers."
)
Expand All @@ -66,7 +68,7 @@ class FioSpec(base.BenchmarkSpec):
FioRW.READ,
description = "The value of the Fio rw config option."
)
bs: constr(regex = "\\d+(K|M|G|T|P)?") = Field(
bs: schema.constr(pattern = "\\d+(K|M|G|T|P)?") = Field(
"4M",
description = "The value of the Fio bs config option."
)
Expand Down Expand Up @@ -98,15 +100,15 @@ class FioSpec(base.BenchmarkSpec):
FioIOEngine.LIBAIO,
description = "The value of the Fio ioengine config option."
)
runtime: constr(regex = "\\d+(D|H|M|s|ms|us)?") = Field(
runtime: schema.constr(pattern = "\\d+(D|H|M|s|ms|us)?") = Field(
"30s",
description = "The value of the Fio runtime config option."
)
num_jobs: schema.conint(gt = 0) = Field(
1,
description = "The value of the Fio numjobs config option."
)
size: constr(regex = "\\d+(K|M|G|T|P)?") = Field(
size: schema.constr(pattern = "\\d+(K|M|G|T|P)?") = Field(
"10G",
description = "The value of the Fio size config option."
)
Expand Down Expand Up @@ -152,44 +154,46 @@ class FioResult(schema.BaseModel):
...,
description = "The aggregate read latency standard deviation."
)



class FioStatus(base.BenchmarkStatus):
"""
Represents the status of the Fio benchmark.
"""
result: t.Optional[FioResult] = Field(
result: schema.Optional[FioResult] = Field(
None,
description = "The result of the benchmark."
)
read_bw_result: t.Optional[schema.IntOrString] = Field(
read_bw_result: schema.Optional[schema.IntOrString] = Field(
None,
description = "The summary result for read bw, used for display."
)
write_bw_result: t.Optional[schema.IntOrString] = Field(
write_bw_result: schema.Optional[schema.IntOrString] = Field(
None,
description = "The summary result for write bw, used for display."
)
read_iops_result: t.Optional[schema.confloat(ge = 0)] = Field(
read_iops_result: schema.Optional[schema.confloat(ge = 0)] = Field(
None,
description = "The summary result for read IOPs, used for display."
)
write_iops_result: t.Optional[schema.confloat(ge = 0)] = Field(
write_iops_result: schema.Optional[schema.confloat(ge = 0)] = Field(
None,
description = "The summary result for write IOPs, used for display."
)
master_pod: t.Optional[base.PodInfo] = Field(
master_pod: schema.Optional[base.PodInfo] = Field(
None,
description = "Pod information for the Fio master pod."
)
worker_pods: schema.Dict[str, base.PodInfo] = Field(
default_factory = dict,
description = "Pod information for the worker pods, indexed by pod name."
)
client_log: t.Optional[constr(min_length = 1)] = Field(
client_log: schema.Optional[schema.constr(min_length = 1)] = Field(
None,
description = "The raw pod log of the client pod."
)


class Fio(
base.Benchmark,
subresources = {"status": {}},
Expand Down
14 changes: 7 additions & 7 deletions python/perftest/models/v1alpha1/iperf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import re
import typing as t

from pydantic import Field, constr
from pydantic import Field

from kube_custom_resource import schema

Expand All @@ -17,7 +17,7 @@ class IPerfSpec(base.BenchmarkSpec):
"""
Defines the parameters for the iperf benchmark.
"""
image: constr(min_length = 1) = Field(
image: schema.constr(min_length = 1) = Field(
f"{settings.default_image_prefix}iperf:{settings.default_image_tag}",
description = "The image to use for the benchmark."
)
Expand Down Expand Up @@ -67,23 +67,23 @@ class IPerfStatus(base.BenchmarkStatus):
"""
Represents the status of the iperf benchmark.
"""
summary_result: t.Optional[schema.IntOrString] = Field(
summary_result: schema.Optional[schema.IntOrString] = Field(
None,
description = "The summary result for the benchmark, used for display."
)
result: t.Optional[IPerfResult] = Field(
result: schema.Optional[IPerfResult] = Field(
None,
description = "The complete result for the benchmark."
)
client_log: t.Optional[constr(min_length = 1)] = Field(
client_log: schema.Optional[schema.constr(min_length = 1)] = Field(
None,
description = "The raw pod log of the client pod."
)
server_pod: t.Optional[base.PodInfo] = Field(
server_pod: schema.Optional[base.PodInfo] = Field(
None,
description = "Pod information for the server pod."
)
client_pod: t.Optional[base.PodInfo] = Field(
client_pod: schema.Optional[base.PodInfo] = Field(
None,
description = "Pod information for the client pod."
)
Expand Down
8 changes: 4 additions & 4 deletions python/perftest/models/v1alpha1/openfoam.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re
import typing as t

from pydantic import Field, constr
from pydantic import Field

from kube_custom_resource import schema

Expand Down Expand Up @@ -49,7 +49,7 @@ class OpenFOAMSpec(base.BenchmarkSpec):
"""
Defines the parameters for the openFOAM benchmark.
"""
image: constr(min_length = 1) = Field(
image: schema.constr(min_length = 1) = Field(
f"{settings.default_image_prefix}openfoam:{settings.default_image_tag}",
description = "The image to use for the benchmark."
)
Expand Down Expand Up @@ -105,11 +105,11 @@ class OpenFOAMStatus(base.BenchmarkStatus):
"""
Represents the status of the iperf benchmark.
"""
result: t.Optional[OpenFOAMResult] = Field(
result: schema.Optional[OpenFOAMResult] = Field(
None,
description = "The result of the benchmark."
)
master_pod: t.Optional[base.PodInfo] = Field(
master_pod: schema.Optional[base.PodInfo] = Field(
None,
description = "Pod information for the MPI master pod."
)
Expand Down
Loading