Skip to content

feat: dynamic knative service orchestration for base model registration - #529

Open
yshmael wants to merge 5 commits into
hotosm:developfrom
yshmael:feature/dynamic-knative-deployment
Open

feat: dynamic knative service orchestration for base model registration#529
yshmael wants to merge 5 commits into
hotosm:developfrom
yshmael:feature/dynamic-knative-deployment

Conversation

@yshmael

@yshmael yshmael commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Description

Addresses the workflow requested by @Kshitij to fully automate Knative service deployment dynamically whenever a new base model is registered via the /api/v1/base-models/ endpoint.

This relies on the fair-model-deployer RBAC permissions recently merged into k8s-infra. The Django API now safely acts as an in-cluster operator using the Kubernetes Python SDK.

Technical Changes

  1. Added SDK: Added kubernetes to backend dependencies via uv.
  2. Knative Template: Created infra/knative-model-template.yaml as the foundational Knative configuration.
  3. Cluster Orchestration (knative_deployer.py): Added a utility to securely hook into EKS via load_incluster_config() and dynamically create/patch Knative CRDs via CustomObjectsApi.
  4. API Extension & Routing: Updated the POST /base-models/ endpoint in views.py to trigger the deployment. Added a "smart fallback" logic to automatically construct and inject the mlm:inference-endpoint STAC URL as https://<model_name>.predict.ai.hotosm.org/predict.
  5. CI/Testing: Mocked the new deploy_model_to_knative function in test_base_model_endpoints.py to ensure the automated GitHub Actions CI pipeline passes without requiring a live cluster connection. Additionally, updated the test suite to expect the new auto-generated Knative URL instead of a blank field when no endpoint is explicitly provided.

@yshmael
yshmael deployed to 529/merge August 18, 2026 22:33 — with GitHub Actions Active
@yshmael
yshmael deployed to 529/merge August 18, 2026 22:43 — with GitHub Actions Active
@yshmael
yshmael deployed to 529/merge August 18, 2026 22:58 — with GitHub Actions Active
@yshmael
yshmael deployed to 529/merge August 18, 2026 23:14 — with GitHub Actions Active
api_instance = client.CustomObjectsApi()

# Knative API Group definitions
group = "serving.knative.dev"

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.

Kindly read this from env variable with default value !


# 3. Load and populate the YAML template
# Adjust the path based on where this script runs relative to project root
template_path = os.path.join(

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.

use python relative import !

manifest_str = file.read()

# Determine dynamic image - assuming a base model server image for now
image_url = os.getenv("MODEL_SERVER_IMAGE", "ghcr.io/hotosm/fair-model-server:latest")

@kshitijrajsharma kshitijrajsharma Aug 19, 2026

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.

I am not sure this thing exists yet , and why this is needed ? if needed the env variable needs to be defined as clear self explanatory name so it won't create confusion , kindly read it from settings.variable andread it from env variable as well if the use of this is justified !

)
except ApiException as e:
if e.status == 409: # Conflict: Service already exists, patch it instead
api_instance.patch_namespaced_custom_object(

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.

patch doesn't create if the resources doesn't exist ?

time.sleep(1)

# Fallback to predictable internal cluster URL if polling times out
return f"http://{model_name}.{namespace}.svc.cluster.local"

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.

we can't have this fallback it should strictly be passing

# 5. Fetch the resulting Knative URL
# Knative takes a moment to assign the URL, we implement a brief polling loop
for _ in range(10):
try:

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.

this try except block should not be here and also the for loop for 10 seconds , there should be some function in upstream we can use ?


try:
# 3. Dynamic namespace detection based on Django environment
namespace = "fair-prod" if not settings.DEBUG else "fair-staging"

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.

env variable from settings ,


# 5. Construct the exact inference URL for the deployed model
# Using the sanitized model_name to ensure valid DNS formatting
expected_predict_url = f"https://{model_name}.predict.ai.hotosm.org/predict"

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.

also the env variable for the prediction url

expected_predict_url = f"https://{model_name}.predict.ai.hotosm.org/predict"

# 6. Save the correctly formatted endpoint into the STAC properties
stac_item.setdefault("properties", {})["knative_endpoint"] = expected_predict_url

@kshitijrajsharma kshitijrajsharma Aug 19, 2026

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.

knative_endpoint doesn't exist in stac it would be mlm:inference-endpoint

stac_item.setdefault("properties", {})["knative_endpoint"] = expected_predict_url

# 7. Register the Knative URL as the STAC mlm:inference-endpoint asset
if not data.get("inference_endpoint"):

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.

it shoudl always override ! so this is not needed


# ---> KNATIVE DEPLOYMENT BLOCK START <---
# 1. Fetch the STAC name (fallback to fair-base-model if missing)
stac_name = stac_item.get("properties", {}).get("mlm:name", "fair-base-model")

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.

no , not needed , regiseration is always for basemodel , local models are not registered !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

3 participants