Pārlūkot izejas kodu

Merge branch 'develop' into claude/enhance-nodepricing-logging-63G1M

Warwick 3 mēneši atpakaļ
vecāks
revīzija
c2c70ab559

+ 1 - 1
go.mod

@@ -25,7 +25,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/config v1.29.10
 	github.com/aws/aws-sdk-go-v2/credentials v1.17.63
 	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.5
-	github.com/aws/aws-sdk-go-v2/service/athena v1.55.8
+	github.com/aws/aws-sdk-go-v2/service/athena v1.56.6
 	github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.2
 	github.com/aws/aws-sdk-go-v2/service/s3 v1.51.0
 	github.com/aws/aws-sdk-go-v2/service/sts v1.33.17

+ 2 - 2
go.sum

@@ -153,8 +153,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.1 h1:rtYJd3w6IWCTVS8vmMaiXjW198noh2PBm5CiXyJea9o=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.1/go.mod h1:zvXu+CTlib30LUy4LTNFc6HTZ/K6zCae5YIHTdX9wIo=
-github.com/aws/aws-sdk-go-v2/service/athena v1.55.8 h1:DOKfSP7IcvjyVme2EoMGWv9K002TbwSewdKX2GzucjI=
-github.com/aws/aws-sdk-go-v2/service/athena v1.55.8/go.mod h1:Eqple+QAWYzX5Fo7RoLn4fjnviCFrqlLuCaEp/pnOn4=
+github.com/aws/aws-sdk-go-v2/service/athena v1.56.6 h1:jtsbyd7mHipxxqqIlz0vIieVCEGXvFP3VPQDI9TqoAY=
+github.com/aws/aws-sdk-go-v2/service/athena v1.56.6/go.mod h1:4Hg2qtNOcRb/+xXK5wR+RbhIUV2/kKVLwtQg+Zih+X4=
 github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.2 h1:MG12Z/W1zzJLkw2gCU2gKZ872rqLM0pi9LdkZ/z3FHc=
 github.com/aws/aws-sdk-go-v2/service/ec2 v1.279.2/go.mod h1:Uy+C+Sc58jozdoL1McQr8bDsEvNFx+/nBY+vpO1HVUY=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=

+ 1 - 1
pkg/costmodel/cluster_helpers.go

@@ -385,7 +385,7 @@ func buildCPUBreakdownMap(resNodeCPUModeTotal []*source.NodeCPUModeTotalResult)
 
 		mode := result.Mode
 		if mode == "" {
-			log.Warnf("ClusterNodes: unable to read CPU mode data.")
+			log.DedupedWarningf(10, "ClusterNodes: unable to read CPU mode data for node %s.", node)
 			mode = "other"
 		}
 

+ 121 - 0
pkg/costmodel/cluster_helpers_test.go

@@ -1230,3 +1230,124 @@ func TestBuildLabelsMap(t *testing.T) {
 		}
 	}
 }
+
+func TestBuildCPUBreakdownMap(t *testing.T) {
+	cases := []struct {
+		name     string
+		input    []*source.NodeCPUModeTotalResult
+		expected map[nodeIdentifierNoProviderID]*ClusterCostsBreakdown
+	}{
+		{
+			name:     "empty input",
+			input:    []*source.NodeCPUModeTotalResult{},
+			expected: map[nodeIdentifierNoProviderID]*ClusterCostsBreakdown{},
+		},
+		{
+			name: "normal modes",
+			input: []*source.NodeCPUModeTotalResult{
+				{
+					Cluster: "cluster1",
+					Node:    "node1",
+					Mode:    "idle",
+					Data:    []*util.Vector{{Value: 50.0}},
+				},
+				{
+					Cluster: "cluster1",
+					Node:    "node1",
+					Mode:    "user",
+					Data:    []*util.Vector{{Value: 30.0}},
+				},
+				{
+					Cluster: "cluster1",
+					Node:    "node1",
+					Mode:    "system",
+					Data:    []*util.Vector{{Value: 20.0}},
+				},
+			},
+			expected: map[nodeIdentifierNoProviderID]*ClusterCostsBreakdown{
+				{Cluster: "cluster1", Name: "node1"}: {
+					Idle:   0.5,
+					User:   0.3,
+					System: 0.2,
+				},
+			},
+		},
+		{
+			name: "empty mode falls back to other",
+			input: []*source.NodeCPUModeTotalResult{
+				{
+					Cluster: "cluster1",
+					Node:    "node1",
+					Mode:    "idle",
+					Data:    []*util.Vector{{Value: 50.0}},
+				},
+				{
+					Cluster: "cluster1",
+					Node:    "node1",
+					Mode:    "", // empty mode should be treated as "other"
+					Data:    []*util.Vector{{Value: 50.0}},
+				},
+			},
+			expected: map[nodeIdentifierNoProviderID]*ClusterCostsBreakdown{
+				{Cluster: "cluster1", Name: "node1"}: {
+					Idle:  0.5,
+					Other: 0.5,
+				},
+			},
+		},
+		{
+			name: "missing node is skipped",
+			input: []*source.NodeCPUModeTotalResult{
+				{
+					Cluster: "cluster1",
+					Node:    "", // empty node should be skipped
+					Mode:    "idle",
+					Data:    []*util.Vector{{Value: 50.0}},
+				},
+				{
+					Cluster: "cluster1",
+					Node:    "node1",
+					Mode:    "user",
+					Data:    []*util.Vector{{Value: 100.0}},
+				},
+			},
+			expected: map[nodeIdentifierNoProviderID]*ClusterCostsBreakdown{
+				{Cluster: "cluster1", Name: "node1"}: {
+					User: 1.0,
+				},
+			},
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := buildCPUBreakdownMap(tc.input)
+
+			if len(result) != len(tc.expected) {
+				t.Errorf("buildCPUBreakdownMap case %s: expected %d entries, got %d", tc.name, len(tc.expected), len(result))
+				return
+			}
+
+			for key, expectedBreakdown := range tc.expected {
+				actualBreakdown, ok := result[key]
+				if !ok {
+					t.Errorf("buildCPUBreakdownMap case %s: missing key %+v", tc.name, key)
+					continue
+				}
+
+				if !util.IsApproximately(actualBreakdown.Idle, expectedBreakdown.Idle) {
+					t.Errorf("buildCPUBreakdownMap case %s: Idle mismatch for %+v: expected %f, got %f", tc.name, key, expectedBreakdown.Idle, actualBreakdown.Idle)
+				}
+				if !util.IsApproximately(actualBreakdown.User, expectedBreakdown.User) {
+					t.Errorf("buildCPUBreakdownMap case %s: User mismatch for %+v: expected %f, got %f", tc.name, key, expectedBreakdown.User, actualBreakdown.User)
+				}
+				if !util.IsApproximately(actualBreakdown.System, expectedBreakdown.System) {
+					t.Errorf("buildCPUBreakdownMap case %s: System mismatch for %+v: expected %f, got %f", tc.name, key, expectedBreakdown.System, actualBreakdown.System)
+				}
+				if !util.IsApproximately(actualBreakdown.Other, expectedBreakdown.Other) {
+					t.Errorf("buildCPUBreakdownMap case %s: Other mismatch for %+v: expected %f, got %f", tc.name, key, expectedBreakdown.Other, actualBreakdown.Other)
+				}
+			}
+		})
+	}
+}

+ 1 - 2
pkg/costmodel/costmodel.go

@@ -915,8 +915,7 @@ func (cm *CostModel) GetNodeCost() (map[string]*costAnalyzerCloud.Node, error) {
 
 		cnode, _, err := cp.NodePricing(cp.GetKey(nodeLabels, n))
 		if err != nil {
-			log.Infof("Could not get node pricing for node %s. Falling back to default pricing", name)
-			log.Debugf("Error getting node pricing: %s", err.Error())
+			log.DedupedInfof(10, "Could not get node pricing for node %s: %s. Falling back to default pricing.", name, err.Error())
 			if cnode != nil {
 				nodes[name] = cnode
 				continue