feat(policies): define ObservabilityK8s policy type with deploy-and-connect and connect-only modes [C9S-121] (#2706)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Xing
2026-06-11 15:09:33 +12:00
committed by GitHub
parent ade5b2a3db
commit ac3416c5a2
3 changed files with 63 additions and 4 deletions
+2
View File
@@ -838,6 +838,7 @@ type (
NodeCount int `json:"NodeCount" validate:"required"`
TotalCPU int64 `json:"TotalCPU" validate:"required"`
TotalMemory int64 `json:"TotalMemory" validate:"required"`
ClusterType string `json:"ClusterType,omitempty"`
DiagnosticsData *DiagnosticsData `json:"DiagnosticsData,omitempty"`
PerformanceMetrics *PerformanceMetrics `json:"PerformanceMetrics,omitempty"`
}
@@ -2641,6 +2642,7 @@ const (
RegistryDocker PolicyType = "registry-docker"
ChangeConfirmation PolicyType = "change-confirmation"
CleanupDocker PolicyType = "cleanup-docker"
ObservabilityK8s PolicyType = "observability-k8s"
)
type HelmInstallStatus string
+29
View File
@@ -66,10 +66,39 @@ func kubernetesSnapshotNodes(snapshot *portainer.KubernetesSnapshot, cli kuberne
snapshot.TotalCPU = totalCPUs
snapshot.TotalMemory = totalMemory
snapshot.NodeCount = len(nodeList.Items)
snapshot.ClusterType = clusterTypeFromProviderID(nodeList.Items[0].Spec.ProviderID)
return nil
}
const (
ClusterTypeGKEAutopilot = "gke-autopilot"
ClusterTypeEKSFargate = "eks-fargate"
ClusterTypeAKS = "aks"
ClusterTypeUnknown = ""
)
func clusterTypeFromProviderID(providerID string) string {
switch {
case strings.HasPrefix(providerID, "gce://") && isGKEAutopilotProviderID(providerID):
return ClusterTypeGKEAutopilot
case strings.HasPrefix(providerID, "aws://") && strings.Contains(strings.ToLower(providerID), "fargate"):
return ClusterTypeEKSFargate
case strings.HasPrefix(providerID, "azure://"):
return ClusterTypeAKS
default:
return ClusterTypeUnknown
}
}
// isGKEAutopilotProviderID detects GKE Autopilot via the gk3- node-name prefix.
// ProviderID format: gce://PROJECT/REGION/NODE-NAME
// Autopilot nodes: gk3-CLUSTER-POOL-SUFFIX; Standard nodes: gke-CLUSTER-POOL-SUFFIX
func isGKEAutopilotProviderID(providerID string) bool {
parts := strings.Split(providerID, "/")
return len(parts) > 0 && strings.HasPrefix(parts[len(parts)-1], "gk3-")
}
// KubernetesSnapshotDiagnostics returns the diagnostics data for the agent
func KubernetesSnapshotDiagnostics(cli *kubernetes.Clientset, edgeKey string) (*portainer.DiagnosticsData, error) {
podID := os.Getenv("HOSTNAME")
+32 -4
View File
@@ -15,6 +15,29 @@ import (
ktesting "k8s.io/client-go/testing"
)
func TestClusterTypeFromProviderID(t *testing.T) {
t.Parallel()
tests := []struct {
providerID string
expected string
}{
{"gce://my-project/us-central1/gk3-my-cluster-pool-abc123", ClusterTypeGKEAutopilot},
{"gce://my-project/us-central1/gke-my-cluster-pool-abc123", ClusterTypeUnknown},
{"aws:///us-east-1/fargate-12345", ClusterTypeEKSFargate},
{"aws:///us-east-1/i-1234567890abcdef0", ClusterTypeUnknown},
{"azure:///subscriptions/x/resourceGroups/y/providers/Microsoft.Compute/virtualMachines/NODE", ClusterTypeAKS},
{"", ClusterTypeUnknown},
}
for _, tt := range tests {
t.Run(tt.providerID, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.expected, clusterTypeFromProviderID(tt.providerID))
})
}
}
func TestKubernetesSnapshotNodes(t *testing.T) {
t.Parallel()
// Create a fake client
@@ -25,6 +48,9 @@ func TestKubernetesSnapshotNodes(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "test-node-1",
},
Spec: corev1.NodeSpec{
ProviderID: "gce://my-project/us-central1/gk3-my-cluster-pool-abc123",
},
Status: corev1.NodeStatus{
Capacity: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("6"), // 6 CPU cores
@@ -72,10 +98,11 @@ func TestKubernetesSnapshotNodes(t *testing.T) {
require.NoError(t, err)
// Verify the results - these should match what kubernetesSnapshotNodes would produce
require.Equal(t, 3, snapshot.NodeCount) // 3 nodes
require.Equal(t, int64(12), snapshot.TotalCPU) // 6 + 4 + 2 = 12 CPUs
require.Equal(t, int64(25769803776), snapshot.TotalMemory) // 12GB + 8GB + 4GB = 24GB in bytes
require.Nil(t, snapshot.PerformanceMetrics) // Performance metrics are no longer collected server-side
require.Equal(t, 3, snapshot.NodeCount) // 3 nodes
require.Equal(t, int64(12), snapshot.TotalCPU) // 6 + 4 + 2 = 12 CPUs
require.Equal(t, int64(25769803776), snapshot.TotalMemory) // 12GB + 8GB + 4GB = 24GB in bytes
require.Equal(t, ClusterTypeGKEAutopilot, snapshot.ClusterType) // detected from node1's ProviderID
require.Nil(t, snapshot.PerformanceMetrics) // Performance metrics are no longer collected server-side
t.Logf("kubernetesSnapshotNodes test result: Nodes=%d, CPUs=%d, Memory=%d bytes",
snapshot.NodeCount, snapshot.TotalCPU, snapshot.TotalMemory)
@@ -94,6 +121,7 @@ func TestKubernetesSnapshotNodesEmptyCluster(t *testing.T) {
require.Equal(t, 0, snapshot.NodeCount)
require.Equal(t, int64(0), snapshot.TotalCPU)
require.Equal(t, int64(0), snapshot.TotalMemory)
require.Equal(t, ClusterTypeUnknown, snapshot.ClusterType)
require.Nil(t, snapshot.PerformanceMetrics) // Performance metrics should not be set for empty cluster
t.Log("Empty cluster test passed - no nodes found, early return behavior confirmed")