Add system namespace filtering

- Add configuration to exclude system namespaces by default
- Add UI checkbox to include system namespaces when needed
- Update API endpoints to accept include_system_namespaces parameter
- Update Kubernetes client to apply namespace filtering
- Update ConfigMap and deployment with new environment variables
- Fix Dockerfile to install dependencies globally
- Test functionality with both filtered and unfiltered results
This commit is contained in:
2025-09-25 17:39:33 -03:00
parent 3a6875a80e
commit 071ffefef7
7 changed files with 90 additions and 9 deletions

View File

@@ -29,11 +29,9 @@ RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN mkdir -p /app /tmp/reports && \ RUN mkdir -p /app /tmp/reports && \
chown -R appuser:appuser /app /tmp/reports chown -R appuser:appuser /app /tmp/reports
# Copiar dependências Python do stage anterior # Instalar dependências Python globalmente
COPY --from=builder /root/.local /home/appuser/.local COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Definir PATH para incluir dependências locais
ENV PATH=/home/appuser/.local/bin:$PATH
# Definir diretório de trabalho # Definir diretório de trabalho
WORKDIR /app WORKDIR /app

View File

@@ -175,12 +175,13 @@ async def get_validations_by_namespace(
severity: Optional[str] = None, severity: Optional[str] = None,
page: int = 1, page: int = 1,
page_size: int = 20, page_size: int = 20,
include_system_namespaces: bool = False,
k8s_client=Depends(get_k8s_client) k8s_client=Depends(get_k8s_client)
): ):
"""Listar validações agrupadas por namespace com paginação""" """Listar validações agrupadas por namespace com paginação"""
try: try:
# Coletar todos os pods # Coletar todos os pods com filtro de namespaces do sistema
pods = await k8s_client.get_all_pods() pods = await k8s_client.get_all_pods(include_system_namespaces=include_system_namespaces)
# Validar recursos e agrupar por namespace # Validar recursos e agrupar por namespace
namespace_validations = {} namespace_validations = {}

View File

@@ -4,6 +4,7 @@ Configurações da aplicação
import os import os
from typing import List, Optional from typing import List, Optional
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
from pydantic import Field
class Settings(BaseSettings): class Settings(BaseSettings):
"""Configurações da aplicação""" """Configurações da aplicação"""
@@ -31,6 +32,24 @@ class Settings(BaseSettings):
"openshift-sdn" "openshift-sdn"
] ]
# Configurações de filtro de namespaces
include_system_namespaces: bool = Field(default=False, alias="INCLUDE_SYSTEM_NAMESPACES")
system_namespace_prefixes: List[str] = Field(
default=[
"kube-",
"openshift-",
"default",
"kube-system",
"kube-public",
"kube-node-lease"
],
alias="SYSTEM_NAMESPACE_PREFIXES"
)
class Config:
env_file = ".env"
case_sensitive = False
# Configurações de relatório # Configurações de relatório
report_export_path: str = "/tmp/reports" report_export_path: str = "/tmp/reports"

View File

@@ -44,7 +44,20 @@ class K8sClient:
logger.error(f"Erro ao inicializar cliente Kubernetes: {e}") logger.error(f"Erro ao inicializar cliente Kubernetes: {e}")
raise raise
async def get_all_pods(self) -> List[PodResource]: def _is_system_namespace(self, namespace: str, include_system: bool = None) -> bool:
"""Verificar se um namespace é do sistema"""
# Usar parâmetro se fornecido, senão usar configuração global
should_include = include_system if include_system is not None else settings.include_system_namespaces
if should_include:
return False
for prefix in settings.system_namespace_prefixes:
if namespace.startswith(prefix):
return True
return False
async def get_all_pods(self, include_system_namespaces: bool = None) -> List[PodResource]:
"""Coletar informações de todos os pods do cluster""" """Coletar informações de todos os pods do cluster"""
if not self.initialized: if not self.initialized:
raise RuntimeError("Cliente Kubernetes não inicializado") raise RuntimeError("Cliente Kubernetes não inicializado")
@@ -56,6 +69,9 @@ class K8sClient:
pods = self.v1.list_pod_for_all_namespaces(watch=False) pods = self.v1.list_pod_for_all_namespaces(watch=False)
for pod in pods.items: for pod in pods.items:
# Filtrar namespaces do sistema
if self._is_system_namespace(pod.metadata.namespace, include_system_namespaces):
continue
pod_resource = PodResource( pod_resource = PodResource(
name=pod.metadata.name, name=pod.metadata.name,
namespace=pod.metadata.namespace, namespace=pod.metadata.namespace,
@@ -102,6 +118,18 @@ class K8sClient:
if not self.initialized: if not self.initialized:
raise RuntimeError("Cliente Kubernetes não inicializado") raise RuntimeError("Cliente Kubernetes não inicializado")
# Verificar se é namespace do sistema
if self._is_system_namespace(namespace):
logger.info(f"Namespace {namespace} é do sistema, retornando vazio")
return NamespaceResources(
name=namespace,
pods=[],
total_cpu_requests="0",
total_cpu_limits="0",
total_memory_requests="0",
total_memory_limits="0"
)
try: try:
# Listar pods do namespace # Listar pods do namespace
pods = self.v1.list_namespaced_pod(namespace=namespace) pods = self.v1.list_namespaced_pod(namespace=namespace)

View File

@@ -488,6 +488,19 @@
border-radius: 4px; border-radius: 4px;
} }
.checkbox-label {
display: flex !important;
flex-direction: row !important;
align-items: center;
gap: 0.5rem;
cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
margin: 0;
width: auto;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.container { .container {
padding: 1rem; padding: 1rem;
@@ -635,6 +648,12 @@
<option value="100">100</option> <option value="100">100</option>
</select> </select>
</div> </div>
<div class="filter-group">
<label class="checkbox-label">
<input type="checkbox" id="includeSystemNamespaces" onchange="loadValidationsByNamespace()">
Incluir namespaces do sistema
</label>
</div>
<button class="btn" onclick="loadValidationsByNamespace()">Aplicar Filtros</button> <button class="btn" onclick="loadValidationsByNamespace()">Aplicar Filtros</button>
</div> </div>
@@ -727,6 +746,7 @@
try { try {
const severity = document.getElementById('severityFilter').value; const severity = document.getElementById('severityFilter').value;
const pageSize = parseInt(document.getElementById('pageSizeFilter').value); const pageSize = parseInt(document.getElementById('pageSizeFilter').value);
const includeSystem = document.getElementById('includeSystemNamespaces').checked;
currentSeverity = severity; currentSeverity = severity;
currentPageSize = pageSize; currentPageSize = pageSize;
@@ -734,7 +754,8 @@
const params = new URLSearchParams({ const params = new URLSearchParams({
page: currentPage, page: currentPage,
page_size: currentPageSize page_size: currentPageSize,
include_system_namespaces: includeSystem
}); });
if (severity) { if (severity) {

View File

@@ -16,6 +16,10 @@ data:
# Namespaces críticos para VPA # Namespaces críticos para VPA
CRITICAL_NAMESPACES: '["openshift-monitoring", "openshift-ingress", "openshift-apiserver", "openshift-controller-manager", "openshift-sdn"]' CRITICAL_NAMESPACES: '["openshift-monitoring", "openshift-ingress", "openshift-apiserver", "openshift-controller-manager", "openshift-sdn"]'
# Configurações de filtro de namespaces
INCLUDE_SYSTEM_NAMESPACES: "false"
SYSTEM_NAMESPACE_PREFIXES: '["kube-", "openshift-", "default", "kube-system", "kube-public", "kube-node-lease"]'
# URL do Prometheus # URL do Prometheus
PROMETHEUS_URL: "http://prometheus.openshift-monitoring.svc.cluster.local:9090" PROMETHEUS_URL: "http://prometheus.openshift-monitoring.svc.cluster.local:9090"

View File

@@ -89,6 +89,16 @@ spec:
configMapKeyRef: configMapKeyRef:
name: resource-governance-config name: resource-governance-config
key: CRITICAL_NAMESPACES key: CRITICAL_NAMESPACES
- name: INCLUDE_SYSTEM_NAMESPACES
valueFrom:
configMapKeyRef:
name: resource-governance-config
key: INCLUDE_SYSTEM_NAMESPACES
- name: SYSTEM_NAMESPACE_PREFIXES
valueFrom:
configMapKeyRef:
name: resource-governance-config
key: SYSTEM_NAMESPACE_PREFIXES
- name: PROMETHEUS_URL - name: PROMETHEUS_URL
valueFrom: valueFrom:
configMapKeyRef: configMapKeyRef: