[release] v0.23.0-unstable012

This commit is contained in:
Yann Stepienik
2026-08-24 19:08:56 +01:00
parent 59227018d4
commit a168900b55
10 changed files with 74 additions and 18 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
"description": "REST API for Cosmos Cloud server management",
"title": "Cosmos Server API",
"contact": {},
"version": "0.23.0-unstable011"
"version": "0.23.0-unstable012"
},
"paths": {
"/_logs": {
+1 -1
View File
@@ -4,7 +4,7 @@
"description": "REST API for Cosmos Cloud server management",
"title": "Cosmos Server API",
"contact": {},
"version": "0.23.0-unstable011"
"version": "0.23.0-unstable012"
},
"basePath": "/cosmos",
"paths": {
+1 -1
View File
@@ -2066,7 +2066,7 @@ info:
contact: {}
description: REST API for Cosmos Cloud server management
title: Cosmos Server API
version: 0.23.0-unstable011
version: 0.23.0-unstable012
paths:
/_logs:
get:
+1 -1
View File
@@ -36609,7 +36609,7 @@ var swaggerSpec = []string{
"nGxBNLXkUfj7lkPcQUjdGbelSmt304IyXddrXqw5z0qWUrk8EUnAHcqGl25pEp74g/Jl1d3u3hk2LCRU",
"6XVLbP1rYT4/mVM+C86QMH7bvpTDmi/0tu+nX7KtTYIraxsK4Xp88iUXLkpfjw0Q2YN/C1SCPM6t3GzO",
"sTe3sm6BWi7FAfvDSkorEMuY2ffXr1gSaCpczVtNUZu5kYVldDq+xtjpqZDElqgjJ4nIY2KLu5B0VXzJ",
"CHEanYhdw7Ftcnx13uv3FkVgZu/l4PXPg5eHOVfaUIiXr14VRXFoxnpvej8PXg5euqQ1uNujz6g8evNn",
"CHEanYhdw7Ftcnx13uv3FkVgZu/l4PXPg5eHOVfaUIiXr14XRXFoxnpvej8PXg5euqQ1uNujz6g8evNn",
"z1HU9VAznUvucqTZKRMxw0BaQpXNzsY40fBFE1trzy3XZSwV/Dzuven9CgYmFXIz6zuNU75++bI4o8KV",
"EL7oIxwTiQCidi11zoRxKpceP+qN433/T7P1X16+ajhFs6JPVTuSZ9IPnDrYgNhO//O3nP5MyAmLY0A+",
"8LfGp/sgc5+7itAFaGLzGpJhSF0Vvf796eunfk/lqaGtGP5+xxNB4wqooeMRRkH+u2dDt3ufzJhHNGNH",
+1 -1
View File
@@ -1,4 +1,4 @@
package cosmossdk
// Version is the SDK version, synced with Cosmos Server at build time.
const Version = "0.23.0-unstable011"
const Version = "0.23.0-unstable012"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.23.0-unstable011",
"version": "0.23.0-unstable012",
"description": "",
"main": "test-server.js",
"bugs": {
@@ -1,6 +1,7 @@
package constellation
import (
"encoding/json"
"reflect"
"testing"
@@ -143,3 +144,38 @@ func TestUnitTunnelHostOverridden(t *testing.T) {
}
}
}
// Replays the production scenario: a manager (non-LB) receives an agent's
// heartbeat over the wire and must answer DNS for the agent's plain routes.
func TestUnitHandleDNSRequestFromWireHeartbeat(t *testing.T) {
setupTestEnv(t, func(cfg *utils.Config) {
cfg.ConstellationConfig.ThisDeviceName = "cosmos-0"
cfg.HTTPConfig.Hostname = "cosmos.mocham.com"
})
seedDeviceCache(t, utils.ConstellationDevice{DeviceName: "cosmos-0", IP: "192.168.201.1"})
// verbatim shape of an agent heartbeat as stored in constellation-nodes
wire := `{"DeviceName":"vpn","IP":"192.168.201.2","IsRelay":true,"IsLighthouse":true,"IsExitNode":true,"CosmosNode":1,"Tunnels":[],"hostnames":["vpn.mocham.com","test-url.com","filebrowser-vpn.mocham.com","159.65.210.47"],"runningDeployments":[],"monitoringOn":false}`
var hb NodeHeartbeat
if err := json.Unmarshal([]byte(wire), &hb); err != nil {
t.Fatal("unmarshal wire heartbeat:", err)
}
if len(hb.Hostnames) != 4 {
t.Fatalf("wire heartbeat decoded %d hostnames, want 4 (json tag drift?)", len(hb.Hostnames))
}
seedClusterDNS(t, buildClusterDNS([]NodeHeartbeat{hb}, nil))
tests := []struct {
qName string
want []string
}{
{"filebrowser-vpn.mocham.com.", []string{"192.168.201.2"}},
{"vpn.mocham.com.", []string{"192.168.201.2"}},
{"cosmos.mocham.com.", []string{"192.168.201.1"}}, // own hostname stays local
}
for _, tt := range tests {
if got := answeredIPs(t, tt.qName); !reflect.DeepEqual(got, tt.want) {
t.Errorf("DNS answer for %s = %v, want %v", tt.qName, got, tt.want)
}
}
}
+10 -11
View File
@@ -642,6 +642,15 @@ func mergeTunnelHeartbeats(heartbeats []NodeHeartbeat, currentDeviceName string)
return tunnels
}
// tunnelsForNode gates serving on load-balancer status: only LBs serve tunnels,
func tunnelsForNode(heartbeats []NodeHeartbeat, currentDeviceName string) []utils.ConstellationTunnel {
isLB, err := GetCurrentDeviceIsLoadbalancer()
if err != nil || !isLB {
return []utils.ConstellationTunnel{}
}
return mergeTunnelHeartbeats(heartbeats, currentDeviceName)
}
func UpdateLocalTunnelCache() {
if IsConstellationStandalone() {
return
@@ -707,7 +716,7 @@ func UpdateLocalTunnelCache() {
// every node keeps the cluster DNS map, even non load balancers
setClusterDNS(buildClusterDNS(heartbeats, loadBalancerIPs()))
tunnels := mergeTunnelHeartbeats(heartbeats, currentDeviceName)
tunnels := tunnelsForNode(heartbeats, currentDeviceName)
// Compare old and new cache using sorted copies for consistent comparison
sortTunnelsForComparison := func(t []utils.ConstellationTunnel) []utils.ConstellationTunnel {
@@ -746,16 +755,6 @@ func GetLocalTunnelCache() []utils.ConstellationTunnel {
return []utils.ConstellationTunnel{}
}
isLB, err := GetCurrentDeviceIsLoadbalancer()
if err != nil {
utils.Debug("[constellation] Failed to get current device load balancer status for tunnel cache retrieval " + err.Error())
return []utils.ConstellationTunnel{}
}
if !isLB {
return []utils.ConstellationTunnel{}
}
localTunnelCacheMutex.RLock()
defer localTunnelCacheMutex.RUnlock()
@@ -176,3 +176,24 @@ func TestUnitMergeTunnelHeartbeatsIgnoresUnnamedAdvertiser(t *testing.T) {
}
}
}
// Serving tunnels is LB-only — even _ANY_ routes must not land on a
// non-load-balancer, while an LB merges them as before.
func TestUnitTunnelsForNodeGatesOnLoadBalancer(t *testing.T) {
setupTestEnv(t, func(cfg *utils.Config) {
cfg.ConstellationConfig.ThisDeviceName = "node-a"
})
heartbeats := []NodeHeartbeat{
tunnelAdvertiser("node_b", "round_robin", false, false, 10*time.Second),
}
seedDeviceCache(t, utils.ConstellationDevice{DeviceName: "node-a", IP: "192.168.201.5"})
if got := tunnelsForNode(heartbeats, "node-a"); len(got) != 0 {
t.Errorf("non-LB node serves %d tunnels, want 0", len(got))
}
seedDeviceCache(t, utils.ConstellationDevice{DeviceName: "node-a", IP: "192.168.201.5", IsLoadBalancer: true})
if got := tunnelsForNode(heartbeats, "node-a"); len(got) != 1 {
t.Errorf("LB node serves %d tunnels, want 1", len(got))
}
}
+1 -1
View File
@@ -206,7 +206,7 @@ func main() {
}
// @title Cosmos Server API
// @version 0.23.0-unstable011
// @version 0.23.0-unstable012
// @description REST API for Cosmos Cloud server management
// @BasePath /cosmos
// @securityDefinitions.apikey BearerAuth