Restructure k8s manifests for GitOps alignment in k8s/base/

This commit is contained in:
fchinembiri 2026-04-23 22:14:31 +02:00
parent 79093f7d3c
commit 3b6005b4fd
14 changed files with 102 additions and 11 deletions

View File

@ -26,13 +26,15 @@ const DW_CLASSES = [
interface MapComponentProps { interface MapComponentProps {
onCoordsSelected: (lat: number, lon: number) => void; onCoordsSelected: (lat: number, lon: number) => void;
resultUrl?: string; resultUrl?: string;
baselineUrl?: string; // New: Immediate Dynamic World baseline
roi?: { lat: number, lon: number, radius_m: number }; roi?: { lat: number, lon: number, radius_m: number };
} }
const MapComponent: React.FC<MapComponentProps> = ({ onCoordsSelected, resultUrl, roi }) => { const MapComponent: React.FC<MapComponentProps> = ({ onCoordsSelected, resultUrl, baselineUrl, roi }) => {
const mapRef = useRef<HTMLDivElement>(null); const mapRef = useRef<HTMLDivElement>(null);
const mapInstance = useRef<Map | null>(null); const mapInstance = useRef<Map | null>(null);
const [activeResultLayer, setActiveResultLayer] = useState<TileLayer<XYZ> | null>(null); const [activeResultLayer, setActiveResultLayer] = useState<TileLayer<XYZ> | null>(null);
const [activeBaselineLayer, setActiveBaselineLayer] = useState<TileLayer<XYZ> | null>(null);
useEffect(() => { useEffect(() => {
if (!mapRef.current) return; if (!mapRef.current) return;
@ -62,17 +64,40 @@ const MapComponent: React.FC<MapComponentProps> = ({ onCoordsSelected, resultUrl
}; };
}, []); }, []);
// Handle Result Layer and Zoom // Handle Baseline Layer (Instant Feedback)
useEffect(() => {
if (!mapInstance.current || !baselineUrl) return;
if (activeBaselineLayer) {
mapInstance.current.removeLayer(activeBaselineLayer);
}
const newBaseline = new TileLayer({
source: new XYZ({
url: `${TITILER_ENDPOINT}/cog/tiles/{z}/{x}/{y}?url=${baselineUrl}`,
}),
opacity: 0.7, // Slightly transparent to differentiate from prediction
});
// Insert baseline below the prediction layer if it exists
const layers = mapInstance.current.getLayers();
if (activeResultLayer) {
layers.insertAt(1, newBaseline);
} else {
mapInstance.current.addLayer(newBaseline);
}
setActiveBaselineLayer(newBaseline);
}, [baselineUrl]);
// Handle Result Layer (ML Prediction)
useEffect(() => { useEffect(() => {
if (!mapInstance.current || !resultUrl) return; if (!mapInstance.current || !resultUrl) return;
// Remove existing result layer if any
if (activeResultLayer) { if (activeResultLayer) {
mapInstance.current.removeLayer(activeResultLayer); mapInstance.current.removeLayer(activeResultLayer);
} }
// Add new result layer
// Format: TITILER/cog/tiles/{z}/{x}/{y}?url=S3_URL
const newLayer = new TileLayer({ const newLayer = new TileLayer({
source: new XYZ({ source: new XYZ({
url: `${TITILER_ENDPOINT}/cog/tiles/{z}/{x}/{y}?url=${resultUrl}`, url: `${TITILER_ENDPOINT}/cog/tiles/{z}/{x}/{y}?url=${resultUrl}`,
@ -96,18 +121,18 @@ const MapComponent: React.FC<MapComponentProps> = ({ onCoordsSelected, resultUrl
<div style={{ position: 'relative', width: '100%', height: '100vh' }}> <div style={{ position: 'relative', width: '100%', height: '100vh' }}>
<div ref={mapRef} style={{ width: '100%', height: '100%' }} /> <div ref={mapRef} style={{ width: '100%', height: '100%' }} />
{/* Map Legend */} {/* Legend & Controls Overlay */}
<div style={{ <div style={{
position: 'absolute', position: 'absolute',
bottom: '30px', bottom: '30px',
right: '20px', right: '20px',
background: 'rgba(255, 255, 255, 0.9)', background: 'rgba(255, 255, 255, 0.9)',
padding: '10px', padding: '12px',
borderRadius: '8px', borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0,0,0,0.2)', boxShadow: '0 2px 10px rgba(0,0,0,0.2)',
zIndex: 1000, zIndex: 1000,
fontSize: '12px', fontSize: '12px',
maxWidth: '150px' maxWidth: '180px'
}}> }}>
<h4 style={{ margin: '0 0 8px 0', fontSize: '13px', borderBottom: '1px solid #ddd', paddingBottom: '3px' }}>Class Legend</h4> <h4 style={{ margin: '0 0 8px 0', fontSize: '13px', borderBottom: '1px solid #ddd', paddingBottom: '3px' }}>Class Legend</h4>
{DW_CLASSES.map(cls => ( {DW_CLASSES.map(cls => (
@ -122,6 +147,11 @@ const MapComponent: React.FC<MapComponentProps> = ({ onCoordsSelected, resultUrl
<span>{cls.name}</span> <span>{cls.name}</span>
</div> </div>
))} ))}
<div style={{ marginTop: '10px', fontSize: '11px', color: '#666', fontStyle: 'italic' }}>
{activeBaselineLayer && !activeResultLayer && "⏳ Loading ML inference..."}
{activeResultLayer && "✅ ML prediction active"}
</div>
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,18 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gitea.yaml
- jupyter.yaml
- mlflow.yaml
- postgres-postgis.yaml
- 10-redis.yaml
- 20-minio.yaml
- 25-tiler.yaml
- 26-tiler-ingress.yaml
- 40-web.yaml
- 80-api.yaml
- 90-worker.yaml
- geocrop-web-ingress.yaml
- geocrop-tiler-rewrite.yaml
- 60-ingress-minio.yaml

View File

@ -11,11 +11,19 @@ provider "kubernetes" {
config_path = "/etc/rancher/k3s/k3s.yaml" config_path = "/etc/rancher/k3s/k3s.yaml"
} }
# Core application namespace
resource "kubernetes_namespace" "geocrop" { resource "kubernetes_namespace" "geocrop" {
metadata { metadata {
name = "geocrop" name = "geocrop"
} }
} }
# Note: Resource quotas are intentionally omitted here and will be managed dynamically # GitOps management namespace
# based on cluster telemetry to allow MLflow and Argo to consume available resources. resource "kubernetes_namespace" "argocd" {
metadata {
name = "argocd"
}
}
# Note: Resource quotas are intentionally omitted for now
# to allow heavy MLOps processes (Jupyter/MLflow) to use available cluster RAM.

View File

@ -1,10 +1,45 @@
{ {
"version": 4, "version": 4,
"terraform_version": "1.14.9", "terraform_version": "1.14.9",
"serial": 1, "serial": 2,
"lineage": "80e41663-9b90-f349-cc6c-be6879179605", "lineage": "80e41663-9b90-f349-cc6c-be6879179605",
"outputs": {}, "outputs": {},
"resources": [ "resources": [
{
"mode": "managed",
"type": "kubernetes_namespace",
"name": "argocd",
"provider": "provider[\"registry.terraform.io/hashicorp/kubernetes\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "argocd",
"metadata": [
{
"annotations": {},
"generate_name": "",
"generation": 0,
"labels": {},
"name": "argocd",
"resource_version": "3078893",
"uid": "9e556e04-d31e-4f3f-9968-abe3fa1d9362"
}
],
"timeouts": null,
"wait_for_default_service_account": null
},
"sensitive_attributes": [],
"identity_schema_version": 1,
"identity": {
"api_version": "v1",
"kind": "Namespace",
"name": "argocd"
},
"private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjozMDAwMDAwMDAwMDB9LCJzY2hlbWFfdmVyc2lvbiI6IjAifQ=="
}
]
},
{ {
"mode": "managed", "mode": "managed",
"type": "kubernetes_namespace", "type": "kubernetes_namespace",