Skip to content

Commit

Permalink
add shipwright resource
Browse files Browse the repository at this point in the history
  • Loading branch information
Panaetius committed Dec 2, 2024
1 parent 0958b43 commit 41b31b9
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 47 deletions.
5 changes: 3 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
"dockerComposeFile": "docker-compose.yml",
"service": "data_service",
"containerUser": "root",
"remoteUser": "root",
"workspaceFolder": "/workspace",
"shutdownAction": "stopCompose",
"features": {
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
"ghcr.io/devcontainers-extra/features/poetry:2": {},
"ghcr.io/devcontainers-contrib/features/bash-command:1": {
"command": "poetry self add poetry-polylith-plugin"
},
Expand All @@ -31,7 +32,7 @@
"./k3d": {}
},
"overrideFeatureInstallOrder": [
"ghcr.io/devcontainers-contrib/features/poetry",
"ghcr.io/devcontainers-extra/features/poetry",
"ghcr.io/devcontainers-contrib/features/bash-command"
],
"postCreateCommand": "poetry install --with dev && mkdir -p /home/vscode/.config/k9s",
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ help: ## Display this help.

.PHONY: k3d_cluster
k3d_cluster: ## Creates a k3d cluster for testing
k3d node delete k3d-myregistry.localhost
k3d cluster delete
# k3d registry delete myregistry.localhost || true
# k3d registry create myregistry.localhost
Expand Down
38 changes: 15 additions & 23 deletions components/renku_data_services/session/kpack_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from renku_data_services.errors.errors import CannotStartBuildError, DeleteBuildError
from renku_data_services.notebooks.errors.intermittent import IntermittentError
from renku_data_services.notebooks.util.retries import retry_with_exponential_backoff_async
from renku_data_services.session.crs import KpackBuild, KpackImage
from renku_data_services.session.kpack_crs import Build, Image


class KpackImageV1Alpha2Kr8s(APIObject):
Expand Down Expand Up @@ -43,7 +43,7 @@ def __init__(self, namespace: str) -> None:
self.namespace = namespace
self.sanitize = ApiClient().sanitize_for_serialization

async def create_build(self, manifest: KpackBuild) -> KpackBuild:
async def create_build(self, manifest: Build) -> Build:
"""Create a new image build."""
manifest.metadata.namespace = self.namespace
build = await KpackBuildV1Alpha2Kr8s(manifest.model_dump(exclude_none=True, mode="json"))
Expand All @@ -59,33 +59,29 @@ async def create_build(self, manifest: KpackBuild) -> KpackBuild:
raise CannotStartBuildError(message=f"Cannot create the image build {build_name}")
return build_resource

async def get_build(self, name: str) -> KpackBuild | None:
async def get_build(self, name: str) -> Build | None:
"""Get an image build."""
try:
build = await KpackBuildV1Alpha2Kr8s.get(name=name, namespace=self.namespace)
except NotFoundError:
return None
except ServerError as e:
if e.status not in [400, 404]:
if not e.response or e.response.status_code not in [400, 404]:
logging.exception(f"Cannot get the build {name} because of {e}")
raise IntermittentError(f"Cannot get build {name} from the k8s API.")
return None
return KpackBuild.model_validate(build.to_dict())
return Build.model_validate(build.to_dict())

async def list_builds(self, label_selector: str | None = None) -> list[KpackBuild]:
async def list_builds(self, label_selector: str | None = None) -> list[Build]:
"""Get a list of kpack builds."""
try:
builds = await KpackBuildV1Alpha2Kr8s.list(namespace=self.namespace, label_selector=label_selector)
except ServerError as e:
if e.status not in [400, 404]:
if not e.response or e.response.status_code not in [400, 404]:
logging.exception(f"Cannot list builds because of {e}")
raise IntermittentError("Cannot list builds")
return []
output: list[KpackBuild]
if isinstance(builds, APIObject):
output = [KpackBuild.model_validate(builds.to_dict())]
else:
output = [KpackBuild.model_validate(b.to_dict()) for b in builds]
output = [Build.model_validate(b.to_dict()) for b in builds]
return output

async def delete_build(self, name: str) -> None:
Expand All @@ -98,7 +94,7 @@ async def delete_build(self, name: str) -> None:
raise DeleteBuildError()
return None

async def create_image(self, manifest: KpackImage) -> KpackImage:
async def create_image(self, manifest: Image) -> Image:
"""Create a new image image."""
manifest.metadata.namespace = self.namespace
image = await KpackImageV1Alpha2Kr8s(manifest.model_dump(exclude_none=True, mode="json"))
Expand All @@ -114,33 +110,29 @@ async def create_image(self, manifest: KpackImage) -> KpackImage:
raise CannotStartBuildError(message=f"Cannot create the kpack image {image_name}")
return image_resource

async def get_image(self, name: str) -> KpackImage | None:
async def get_image(self, name: str) -> Image | None:
"""Get an image image."""
try:
image = await KpackImageV1Alpha2Kr8s.get(name=name, namespace=self.namespace)
except NotFoundError:
return None
except ServerError as e:
if e.status not in [400, 404]:
if not e.response or e.response.status_code not in [400, 404]:
logging.exception(f"Cannot get the image {name} because of {e}")
raise IntermittentError(f"Cannot get image {name} from the k8s API.")
return None
return KpackImage.model_validate(image.to_dict())
return Image.model_validate(image.to_dict())

async def list_images(self, label_selector: str | None = None) -> list[KpackImage]:
async def list_images(self, label_selector: str | None = None) -> list[Image]:
"""Get a list of kpack images."""
try:
images = await KpackImageV1Alpha2Kr8s.list(namespace=self.namespace, label_selector=label_selector)
except ServerError as e:
if e.status not in [400, 404]:
if not e.response or e.response.status_code not in [400, 404]:
logging.exception(f"Cannot list images because of {e}")
raise IntermittentError("Cannot list images")
return []
output: list[KpackImage]
if isinstance(images, APIObject):
output = [KpackImage.model_validate(images.to_dict())]
else:
output = [KpackImage.model_validate(b.to_dict()) for b in images]
output = [Image.model_validate(b.to_dict()) for b in images]
return output

async def delete_image(self, name: str) -> None:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Custom Resources for environments, mainly kpack."""
"""Custom Resources for kpack environments."""

from datetime import datetime
from typing import Self
Expand Down Expand Up @@ -56,7 +56,7 @@ class PersistentVolumeReference(BaseModel):
persistentVolumeClaimName: str


class KpackBuilderReference(BaseModel):
class BuilderReference(BaseModel):
"""Refernce to Kpack builder."""

name: str
Expand All @@ -75,54 +75,54 @@ class DockerImageWithSecret(DockerImage):
imagePullSecrets: list[ImagePullSecret]


class KpackGitSource(BaseModel):
class GitSource(BaseModel):
"""Git repository source."""

url: str
revision: str


class KpackBlobSource(BaseModel):
class BlobSource(BaseModel):
"""Blob/file archive source."""

url: str
stripComponents: str


class KpackSource(BaseModel):
class Source(BaseModel):
"""Kpack files source resource."""

git: KpackGitSource | None = None
blob: KpackBlobSource | None = None
git: GitSource | None = None
blob: BlobSource | None = None

@model_validator(mode="after")
def validate(self) -> Self:
def validate_source(self) -> Self:
"""Validate mode data."""
if bool(self.git) == bool(self.blob):
raise ValueError("'git' and 'blob' are mutually exclusive and one of them must be set.")
return self


class KpackBuildCustomization(BaseModel):
class BuildCustomization(BaseModel):
"""Customization of a kpack build."""

env: list[EnvItem]


class KpackImageSpec(BaseModel):
class ImageSpec(BaseModel):
"""KPack image spec model."""

tag: str
additionalTags: list[str]
serviceAccountName: str
builder: KpackBuilderReference
source: KpackSource
build: KpackBuildCustomization
builder: BuilderReference
source: Source
build: BuildCustomization
successBuildHistoryLimit: int = 1
failedBuildHistoryLimit: int = 1


class KpackImage(BaseModel):
class Image(BaseModel):
"""Kpack Image resource."""

model_config = ConfigDict(
Expand All @@ -131,10 +131,10 @@ class KpackImage(BaseModel):
kind: str = "Image"
apiVersion: str = "kpack.io/v1alpha2"
metadata: Metadata
spec: KpackImageSpec
spec: ImageSpec


class KpackVolumeCache(BaseModel):
class VolumeCache(BaseModel):
"""Persistent volume to serve as cache for kpack build."""

volume: PersistentVolumeReference
Expand All @@ -146,27 +146,27 @@ class ImageTagReference(BaseModel):
tag: str


class KpackCacheImage(BaseModel):
class CacheImage(BaseModel):
"""Image definition to use as build cache."""

registry: ImageTagReference


class KpackBuildSpec(BaseModel):
class BuildSpec(BaseModel):
"""Spec for kpack build."""

builder: DockerImageWithSecret
cache: KpackVolumeCache | KpackCacheImage
cache: VolumeCache | CacheImage
env: list[EnvItem]
resources: K8sResourceRequest
runImage: DockerImage
serviceAccountName: str
source: KpackSource
source: Source
tags: list[str]
activeDeadlineSeconds: int = 1800


class KpackBuild(BaseModel):
class Build(BaseModel):
"""KPack build resource."""

model_config = ConfigDict(
Expand All @@ -175,4 +175,4 @@ class KpackBuild(BaseModel):
kind: str = "Build"
apiVersion: str = "kpack.io/v1alpha2"
metadata: Metadata
spec: KpackBuildSpec
spec: BuildSpec
Loading

0 comments on commit 41b31b9

Please sign in to comment.