Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bdf3c6b
api spec and models
wesjdj May 12, 2026
57358b5
add knative servive crd pydantic types
wesjdj May 13, 2026
b197e1f
reshape api spec for /apps endpoints
wesjdj May 18, 2026
081b4f6
fix quantity types and collapse models
wesjdj May 19, 2026
fb608f8
add k8s client
wesjdj May 19, 2026
85ad1ee
add knative service converter and make url optional
wesjdj May 19, 2026
f5b5b5c
make url nullable
wesjdj May 21, 2026
83e5521
add /apps POST and GET endpoints
wesjdj May 22, 2026
84c8a3e
fix: bundle renku_apps component in data_service wheel
wesjdj May 27, 2026
5256b3e
get apps kantive manifest from session launcher
wesjdj May 27, 2026
b49d236
derive app URL from project path
wesjdj May 27, 2026
7c3481e
get URL from knative status. name services per project
wesjdj May 28, 2026
7af9966
cache KnativeService GVK
wesjdj May 28, 2026
b8627b7
add dummy renku apps user id
wesjdj May 29, 2026
1310765
subscribe k8s watcher to Knative Services
wesjdj May 29, 2026
858b4ff
bundle renku_apps component in k8s_watcher
wesjdj May 29, 2026
814be8f
keep K8s types out of core.py and repository
wesjdj Jun 3, 2026
3b2dbcf
restore oci_schema target
wesjdj Jun 3, 2026
651da95
address PR review comments
wesjdj Jun 4, 2026
65ca6ec
add delete to api spec
wesjdj Jun 1, 2026
6c6be10
add delete app function to repository
wesjdj Jun 1, 2026
73e7473
add delete apps endpoint
wesjdj Jun 1, 2026
ea0d84d
use injected cluster_id in delete_app_deployment
wesjdj Jun 5, 2026
20777fc
Merge branch 'add-apps-feature' into add-apps-delete
wesjdj Jun 8, 2026
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
16 changes: 16 additions & 0 deletions components/renku_data_services/renku_apps/api.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ paths:
$ref: "#/components/responses/Error"
tags:
- apps
delete:
summary: Delete an app
parameters:
- in: path
name: app_name
required: true
schema:
$ref: "#/components/schemas/AppName"
description: The name of the app to delete
responses:
"204":
description: The app was successfully deleted or did not exist
default:
$ref: "#/components/responses/Error"
tags:
- apps
components:
schemas:
Ulid:
Expand Down
13 changes: 12 additions & 1 deletion components/renku_data_services/renku_apps/blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dataclasses import dataclass

from sanic import Request
from sanic import HTTPResponse, Request
from sanic.response import JSONResponse, json
from sanic_ext import validate
from ulid import ULID
Expand Down Expand Up @@ -42,3 +42,14 @@ async def _get_one(_: Request, user: base_models.APIUser, app_name: str) -> JSON
return json(app.as_apispec().model_dump(exclude_none=True, mode="json"))

return "/apps/<app_name>", ["GET"], _get_one

def delete_one(self) -> BlueprintFactoryResponse:
"""Delete an app by name."""

@authenticate(self.authenticator)
@only_authenticated
async def _delete_one(_: Request, user: base_models.APIUser, app_name: str) -> HTTPResponse:
await self.apps_repo.delete_app(user=user, app_name=app_name)
return HTTPResponse(status=204)

return "/apps/<app_name>", ["DELETE"], _delete_one
12 changes: 10 additions & 2 deletions components/renku_data_services/renku_apps/k8s_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,16 @@ async def get_app_deployment_for_project(
return await self.get_app_deployment(_generate_app_name(project, session_launcher))

async def delete_app_deployment(self, app_name: str) -> None:
"""Delete the deployment for the given app name. NOT IMPLEMENTED."""
raise NotImplementedError("Deleting app deployment is not implemented yet")
"""Delete the deployment for the given app name."""
cluster = await self.__client.cluster_by_id(self.__cluster_id)
meta = K8sObjectMeta(
name=app_name,
namespace=cluster.namespace,
cluster=cluster.id,
gvk=KNATIVE_SERVICE_GVK,
user_id=DUMMY_RENKU_APP_USER_ID,
)
await self.__client.delete(meta)

async def list_app_deployments(self) -> AsyncGenerator[AppRuntimeState, None]:
"""List all app deployments. NOT IMPLEMENTED."""
Expand Down
25 changes: 25 additions & 0 deletions components/renku_data_services/renku_apps/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import renku_data_services.base_models as base_models
from renku_data_services import errors
from renku_data_services.app_config import logging
from renku_data_services.authz.authz import Authz, ResourceType
from renku_data_services.authz.models import Scope
from renku_data_services.crc.db import ResourcePoolRepository
Expand All @@ -14,6 +15,8 @@
from renku_data_services.renku_apps.models import App
from renku_data_services.session.db import SessionRepository

logger = logging.getLogger(__name__)


class RenkuAppsRepository:
"""Use-case-focused API for Renku apps, dispatching to k8s rather than SQL."""
Expand Down Expand Up @@ -65,3 +68,25 @@ async def get_app(self, user: base_models.APIUser, app_name: str) -> App:

launcher = await self.session_repo.get_launcher(user, runtime_state.launcher_id)
return build_app(launcher, runtime_state)

async def delete_app(self, user: base_models.APIUser, app_name: str) -> None:
"""Delete an app by its name."""
if not user.is_authenticated or user.id is None:
raise errors.UnauthorizedError(message="You do not have the required permissions for this operation.")

runtime_state = await self.k8s_client.get_app_deployment(app_name)
if runtime_state is None:
logger.info(f"App with name {app_name} was not found.")
return None

Comment on lines +72 to +81

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You are missing the authorization check.

Suggested change
async def delete_app(self, user: base_models.APIUser, app_name: str) -> None:
"""Delete an app by its name."""
if not user.is_authenticated or user.id is None:
raise errors.UnauthorizedError(message="You do not have the required permissions for this operation.")
runtime_state = await self.k8s_client.get_app_deployment(app_name)
if runtime_state is None:
logger.info(f"App with name {app_name} was not found.")
return None
async def delete_app(self, user: base_models.APIUser, app_name: str) -> None:
"""Delete an app by its name."""
if not user.is_authenticated or user.id is None:
raise errors.UnauthorizedError(message="You do not have the required permissions for this operation.")
authorized = await self.authz.has_permission(user, ResourceType.project, launcher.project_id, Scope.WRITE)
if not authorized:
raise errors.MissingResourceError(
message=f"Project with id '{launcher.project_id}' does not exist or you do not have access to it."
)
runtime_state = await self.k8s_client.get_app_deployment(app_name)
if runtime_state is None:
logger.info(f"App with name {app_name} was not found.")
return None

launcher = await self.session_repo.get_launcher(user, runtime_state.launcher_id)

authorized = await self.authz.has_permission(user, ResourceType.project, launcher.project_id, Scope.WRITE)
if not authorized:
raise errors.MissingResourceError(
message=f"App with name '{app_name}' does not exist or you do not have authorization to modify it."
)

await self.k8s_client.delete_app_deployment(app_name)
logger.info(f"App with name {app_name} has been deleted.")
return None
Loading