Skip to content

GreenKube

v0.2.9 Breaking

This release includes breaking changes for platform teams planning a safe upgrade.

✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →

✓ No known CVEs patched in this version

Topics

carbon-emissions carbon-footprint cloud co2 cost-optimization devops
+12 more
fastapi finops grafana green-computing green-software greenops helm kubernetes prometheus python sustainability svelte

Affected surfaces

auth rbac

Summary

AI summary

Adds frontend config persistence, GHG Scope 2/3 classification, and pre‑computed dashboard caches for raw and archived metrics.

Full changelog

What's Changed

Added

  • Frontend config persistence via K8s Secret (#219): UI-applied settings (PROMETHEUS_URL, OPENCOST_API_URL, ELECTRICITY_MAPS_TOKEN, BOAVIZTA_API_URL) are now patched into the GreenKube Kubernetes Secret immediately after being saved, so they survive pod restarts and helm upgrade --reuse-values without manual intervention. A namespaced Role/RoleBinding grants the service account get+patch access to exactly the GreenKube Secret (no cluster-wide secret access).
  • GreenKube favicon: The browser tab now displays the real GreenKube logo (favicon.ico) instead of the Svelte placeholder SVG. The SVG favicon reference has been removed from app.html and build/index.html; favicon.ico is served with the correct image/vnd.microsoft.icon MIME type.
  • GET /api/v1/metrics/by-namespace: New lightweight endpoint returning CO2e, embodied emissions, energy, and cost aggregated by namespace over a time window. Queries both combined_metrics (raw) and combined_metrics_hourly (archived) tables via a single UNION ALL + GROUP BY — avoids loading full row sets into memory.
  • GET /api/v1/metrics/top-pods: New lightweight endpoint returning the top-N pods by CO2e over a time window, also using the dual-table UNION ALL + GROUP BY pattern. Dashboard donut and top-pods charts now call these two endpoints instead of the expensive GET /metrics route, eliminating OOM restarts when browsing large time ranges.
  • GHG Scope 2 / Scope 3 carbon classification: Emissions are now formally categorised per the GHG Protocol Corporate Standard.
  • Pre-computed dashboard cache (metrics_summary + metrics_timeseries_cache): Two new database tables (migrations 0004 and 0005 for PostgreSQL and SQLite) store pre-aggregated KPI scalars and time-series buckets for five fixed windows (24h, 7d, 30d, 1y, ytd). Tables are refreshed hourly by the background scheduler, eliminating full-table scans on every dashboard load and preventing OOM errors on large datasets.
  • SummaryRefresher: New src/greenkube/core/summary_refresher.py service that computes cluster-wide and per-namespace KPI totals and time-series buckets, then upserts them into the two cache tables. Supports adaptive granularity per window (hourly / daily / weekly / monthly buckets).
  • SummaryRepository and TimeseriesCacheRepository: New abstract base classes in storage/base_repository.py with PostgreSQL and SQLite implementations.
  • Dashboard API endpoints: Three new FastAPI routes for the pre-computed tables:
    • GET /api/v1/metrics/dashboard-summary — cached KPI scalars, optionally filtered by namespace.
    • GET /api/v1/metrics/dashboard-timeseries/{window_slug} — cached time-series buckets for 24h, 7d, 30d, 1y, or ytd.
    • POST /api/v1/metrics/dashboard-summary/refresh — trigger an on-demand background refresh (HTTP 202 Accepted).
  • MetricsSummaryRow and TimeseriesCachePoint Pydantic models: New DTOs in src/greenkube/models/metrics.py representing rows from the two cache tables.
  • Adaptive chart granularity (frontend): Dashboard charts now select the optimal time bucket per window — hourly for 24h, daily for 7d/30d, weekly for 1y, monthly for ytd — resulting in consistently readable x-axes regardless of the selected range.
  • Boavizta fallback with configurable default: When the Boavizta API does not recognise a cloud provider or instance type (returns no data), EmbodiedEmissionsService now injects a fallback embodied-emissions profile using DEFAULT_EMBODIED_EMISSIONS_KG (default: 350 kg CO2e) instead of silently using 0 g, which was incorrect. The resulting CombinedMetric is flagged is_estimated=True with a descriptive estimation_reasons entry. Exposed as config.boavizta.defaultEmbodiedEmissionsKg in values.yaml and DEFAULT_EMBODIED_EMISSIONS_KG in configmap.yaml.
  • EmbodiedEmissionsService.is_embodied_fallback(): New helper method returns True when a node's cached profile was produced by the fallback rather than a real Boavizta response, enabling the metric assembler to set estimation flags accurately.

Fixed

  • Async K8s Secret patching (kubernetes_asyncio): The in-cluster Secret patch now correctly uses kubernetes_asyncio (the async client that is actually installed) instead of the sync kubernetes package. load_incluster_config() is called without await (it reads files synchronously); failures are caught and logged without interrupting the API response.
  • Elasticsearch removed from production dependencies: elasticsearch and elasticsearch-dsl packages moved to an optional extra (pip install greenkube[elasticsearch]). All imports are now lazy (loaded only when the ES storage backend is actually selected), removing heavy transitive dependencies and startup warnings for users on PostgreSQL or SQLite.
  • Trivy KSV-0109 false positive: GREENKUBE_SECRET_NAME is a resource name, not a secret value — suppressed in .trivyignore with justification. KSV-0113 (Role granting secret access) also documented as intentional for the UI persistence feature.
  • Electricity Maps API not called for OpenStack-based providers (zone = nova): The scheduler's carbon-intensity collection loop now falls back to the node's geographic region when the provider-specific zone identifier is not a recognised Electricity Maps zone code. This restores carbon-intensity data collection on OVH, Infomaniak, and similar OpenStack-based clouds where the K8s node zone label is set to nova rather than a country/region code.
  • Race condition in collection orchestrator: CollectionOrchestrator no longer collects nodes internally. Node collection is now an explicit Phase 1 in DataProcessor.run() that runs alone before any concurrent collection, preventing shared Kubernetes API client races and the cascade of Electricity Maps API errors they caused.
  • DEFAULT_ZONE spurious warning: The NodeZoneMapper no longer emits a warning when the zone was actually resolved correctly — the warning was incorrectly triggered even when a valid DEFAULT_ZONE was set.
  • Pod CPU utilisation aggregation per node: CollectionOrchestrator was averaging pod CPU usage per node across timestamps instead of summing, causing underestimated energy figures on nodes with multiple measured pods.
  • Chart legends overlapping (frontend): ECharts legend layout fixed to prevent label overlap on small viewports.

Changed

  • DataProcessor.run() pipeline restructured into four explicit phases: Phase 1 (node discovery, sequential), Phase 2 (zone resolution), Phase 3 (parallel metrics + Boavizta), Phase 4 (carbon-intensity prefetch + assembly). This eliminates the previous race condition and removes the redundant second collect_instance_types() K8s call that used to happen at the end of the pipeline.
  • CollectionOrchestrator simplified: NodeCollector dependency removed; node enrichment for Prometheus instance-type labels now uses the nodes_info dict passed in from Phase 1, avoiding any duplicate K8s API calls.

Docker: docker pull greenkube/greenkube:0.2.9

Helm:

helm repo add greenkube https://GreenKubeCloud.github.io/GreenKube
helm repo update
helm install greenkube greenkube/greenkube -n greenkube --create-namespace

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track GreenKube

Get notified when new releases ship.

Sign up free

About GreenKube

All releases →

Related context

Related tools

Beta — feedback welcome: [email protected]