mirror of
https://github.com/taylanbakircioglu/flowfish.git
synced 2026-09-12 05:48:55 +00:00
6e503368f7
- Grafana Beyla DaemonSet for kernel-level HTTP/gRPC/DNS capture (passive, zero application changes, W3C traceparent header propagation) - flowfish-l7-collector in-cluster bridge: OTLP receiver + buffered pull API - L7 Ingestion Service: K8s service-proxy poll → enrich → RabbitMQ - ClickHouse l7_http_flows / l7_grpc_flows / l7_dns_flows + APM RED MVs - Neo4j L7Workload nodes + SAME_WORKLOAD cross-cluster bridges - New pages: Service Map, Trace Explorer, APM Services List, APM Service Detail - Analysis Wizard now supports L4 / L7 / Both modes with HTTP/gRPC/DNS picks - Integration Hub gains L7 dependency summary + tree-summary integrations - Multi-Cluster Management: dual-agent install (Inspector Gadget L4 + Beyla L7), runtime OpenShift detection so SCCs auto-install with kubectl too - ServiceMap edge → Trace Explorer drill-down with virtual_trace_id correlation - Docs: new L7 architecture diagram, README L7 sections, 3 new screenshots
89 lines
4.0 KiB
Python
89 lines
4.0 KiB
Python
"""
|
|
Cluster and namespace models
|
|
"""
|
|
|
|
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Text, ForeignKey
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import relationship
|
|
from models.base import BaseModel
|
|
|
|
|
|
class Cluster(BaseModel):
|
|
"""Kubernetes/OpenShift cluster model"""
|
|
|
|
__tablename__ = "clusters"
|
|
|
|
name = Column(String(255), unique=True, nullable=False, index=True)
|
|
description = Column(Text, nullable=True)
|
|
cluster_type = Column(String(50), nullable=False) # 'kubernetes', 'openshift'
|
|
api_url = Column(Text, nullable=False)
|
|
kubeconfig_encrypted = Column(Text, nullable=True) # Encrypted kubeconfig
|
|
token_encrypted = Column(Text, nullable=True) # Encrypted SA token
|
|
ca_cert_encrypted = Column(Text, nullable=True) # Encrypted CA certificate for remote clusters
|
|
skip_tls_verify = Column(Boolean, default=False) # Skip TLS verification for remote clusters
|
|
|
|
# Inspector Gadget configuration
|
|
gadget_namespace = Column(String(255), nullable=False) # Namespace where Inspector Gadget is deployed (from UI)
|
|
gadget_endpoint = Column(Text, nullable=True) # Deprecated - kept for backward compatibility
|
|
gadget_health_status = Column(String(50), default="not_installed") # 'healthy', 'degraded', 'unhealthy', 'unknown', 'not_installed'
|
|
gadget_version = Column(String(50), nullable=True) # Detected IG version
|
|
|
|
# Beyla L7 configuration
|
|
beyla_namespace = Column(String(255), nullable=True)
|
|
beyla_health_status = Column(String(50), default="not_installed")
|
|
beyla_version = Column(String(50), nullable=True)
|
|
l7_collector_endpoint = Column(Text, nullable=True)
|
|
beyla_last_check = Column(DateTime, nullable=True)
|
|
|
|
is_in_cluster = Column(Boolean, default=False) # Is Flowfish running in this cluster?
|
|
is_active = Column(Boolean, default=True, index=True)
|
|
is_default = Column(Boolean, default=False, index=True)
|
|
connection_type = Column(String(50), default="in-cluster") # 'in-cluster', 'kubeconfig', 'token'
|
|
kubernetes_version = Column(String(50), nullable=True)
|
|
node_count = Column(Integer, nullable=True)
|
|
pod_count = Column(Integer, nullable=True)
|
|
namespace_count = Column(Integer, nullable=True)
|
|
last_sync_at = Column(DateTime, nullable=True)
|
|
health_status = Column(String(50), default="unknown") # 'healthy', 'degraded', 'unhealthy', 'unknown'
|
|
created_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
|
metadata = Column(JSONB, default={})
|
|
|
|
# Relationships
|
|
creator = relationship("User")
|
|
namespaces = relationship("Namespace", back_populates="cluster", cascade="all, delete-orphan")
|
|
workloads = relationship("Workload", back_populates="cluster", cascade="all, delete-orphan")
|
|
communications = relationship("Communication", back_populates="cluster", cascade="all, delete-orphan")
|
|
analyses = relationship("Analysis", back_populates="cluster", cascade="all, delete-orphan")
|
|
|
|
def to_dict(self, include_sensitive: bool = False):
|
|
"""Convert to dictionary"""
|
|
data = super().to_dict()
|
|
|
|
if not include_sensitive:
|
|
data.pop("kubeconfig_encrypted", None)
|
|
data.pop("token_encrypted", None)
|
|
|
|
return data
|
|
|
|
|
|
class Namespace(BaseModel):
|
|
"""Kubernetes namespace model"""
|
|
|
|
__tablename__ = "namespaces"
|
|
|
|
cluster_id = Column(Integer, ForeignKey("clusters.id"), nullable=False, index=True)
|
|
name = Column(String(255), nullable=False, index=True)
|
|
uid = Column(String(255), nullable=True) # Kubernetes UID
|
|
labels = Column(JSONB, default={})
|
|
annotations = Column(JSONB, default={})
|
|
status = Column(String(50), default="Active")
|
|
|
|
# Relationships
|
|
cluster = relationship("Cluster", back_populates="namespaces")
|
|
workloads = relationship("Workload", back_populates="namespace", cascade="all, delete-orphan")
|
|
|
|
@property
|
|
def full_name(self) -> str:
|
|
"""Get cluster/namespace full name"""
|
|
return f"{self.cluster.name}/{self.name}" if self.cluster else self.name
|