|
|
@@ -2,6 +2,10 @@ package scrape
|
|
|
|
|
|
import (
|
|
|
"testing"
|
|
|
+
|
|
|
+ "github.com/opencost/opencost/core/pkg/clustercache"
|
|
|
+ "github.com/opencost/opencost/core/pkg/source"
|
|
|
+ "github.com/opencost/opencost/modules/collector-source/pkg/metric"
|
|
|
)
|
|
|
|
|
|
func Test_isDCGM(t *testing.T) {
|
|
|
@@ -62,3 +66,149 @@ func Test_isDCGM(t *testing.T) {
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+func Test_podUIDEnricher(t *testing.T) {
|
|
|
+ cache := &clustercache.MockClusterCache{
|
|
|
+ Pods: []*clustercache.Pod{
|
|
|
+ {UID: "pod-uid-1", Name: "pod1", Namespace: "namespace1"},
|
|
|
+ {UID: "pod-uid-2", Name: "pod2", Namespace: "namespace2"},
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ tests := map[string]struct {
|
|
|
+ labels map[string]string
|
|
|
+ want map[string]string
|
|
|
+ }{
|
|
|
+ "resolves pod_uid from namespace and pod": {
|
|
|
+ labels: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ },
|
|
|
+ want: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ source.PodUIDLabel: "pod-uid-1",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "unknown pod is left unset": {
|
|
|
+ labels: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ source.PodLabel: "unknown-pod",
|
|
|
+ },
|
|
|
+ want: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ source.PodLabel: "unknown-pod",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "pod name from wrong namespace is left unset": {
|
|
|
+ labels: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace2",
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ },
|
|
|
+ want: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace2",
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "missing namespace label is left unset": {
|
|
|
+ labels: map[string]string{
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ },
|
|
|
+ want: map[string]string{
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "missing pod label is left unset": {
|
|
|
+ labels: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ },
|
|
|
+ want: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ "nil labels is left untouched": {
|
|
|
+ labels: nil,
|
|
|
+ want: nil,
|
|
|
+ },
|
|
|
+ "existing non-empty pod_uid is left untouched": {
|
|
|
+ labels: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ source.PodUIDLabel: "already-set",
|
|
|
+ },
|
|
|
+ want: map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ source.PodUIDLabel: "already-set",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for name, tt := range tests {
|
|
|
+ t.Run(name, func(t *testing.T) {
|
|
|
+ enrich := podUIDEnricher(cache)
|
|
|
+ updates := []metric.Update{{Labels: cloneLabels(tt.labels)}}
|
|
|
+
|
|
|
+ enrich(updates)
|
|
|
+
|
|
|
+ assertLabels(t, updates[0].Labels, tt.want)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // A single call must correctly enrich every update in the batch, not just
|
|
|
+ // the first, since updates are processed together rather than one at a time.
|
|
|
+ t.Run("enriches every update in a multi-update batch", func(t *testing.T) {
|
|
|
+ enrich := podUIDEnricher(cache)
|
|
|
+ updates := []metric.Update{
|
|
|
+ {Labels: map[string]string{source.NamespaceLabel: "namespace1", source.PodLabel: "pod1"}},
|
|
|
+ {Labels: map[string]string{source.NamespaceLabel: "namespace2", source.PodLabel: "pod2"}},
|
|
|
+ {Labels: map[string]string{source.NamespaceLabel: "namespace1", source.PodLabel: "unknown-pod"}},
|
|
|
+ }
|
|
|
+
|
|
|
+ enrich(updates)
|
|
|
+
|
|
|
+ assertLabels(t, updates[0].Labels, map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ source.PodLabel: "pod1",
|
|
|
+ source.PodUIDLabel: "pod-uid-1",
|
|
|
+ })
|
|
|
+ assertLabels(t, updates[1].Labels, map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace2",
|
|
|
+ source.PodLabel: "pod2",
|
|
|
+ source.PodUIDLabel: "pod-uid-2",
|
|
|
+ })
|
|
|
+ assertLabels(t, updates[2].Labels, map[string]string{
|
|
|
+ source.NamespaceLabel: "namespace1",
|
|
|
+ source.PodLabel: "unknown-pod",
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ t.Run("empty batch does not panic", func(t *testing.T) {
|
|
|
+ enrich := podUIDEnricher(cache)
|
|
|
+ enrich(nil)
|
|
|
+ enrich([]metric.Update{})
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+func cloneLabels(labels map[string]string) map[string]string {
|
|
|
+ if labels == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ clone := make(map[string]string, len(labels))
|
|
|
+ for k, v := range labels {
|
|
|
+ clone[k] = v
|
|
|
+ }
|
|
|
+ return clone
|
|
|
+}
|
|
|
+
|
|
|
+func assertLabels(t *testing.T, got, want map[string]string) {
|
|
|
+ t.Helper()
|
|
|
+ if len(got) != len(want) {
|
|
|
+ t.Fatalf("got labels %v, want %v", got, want)
|
|
|
+ }
|
|
|
+ for k, v := range want {
|
|
|
+ if got[k] != v {
|
|
|
+ t.Errorf("label %q = %q, want %q", k, got[k], v)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|