service_test.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package kubemodel
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/stretchr/testify/assert"
  6. "github.com/stretchr/testify/require"
  7. "github.com/opencost/opencost/core/pkg/model/kubemodel"
  8. "github.com/opencost/opencost/core/pkg/source"
  9. )
  10. func TestComputeServices(t *testing.T) {
  11. start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
  12. end := start.Add(time.Hour)
  13. tests := []struct {
  14. name string
  15. overrides map[string]any
  16. want map[string]*kubemodel.Service
  17. }{
  18. {
  19. name: "no data returns empty service map",
  20. overrides: map[string]any{},
  21. want: map[string]*kubemodel.Service{},
  22. },
  23. {
  24. name: "basic service info and uptime",
  25. overrides: map[string]any{
  26. source.QueryServiceInfo: []*source.ServiceInfoResult{
  27. {UID: "svc-1", Service: "my-service", NamespaceUID: "ns-1", ServiceType: "ClusterIP"},
  28. },
  29. source.QueryServiceUptime: []*source.UptimeResult{
  30. {UID: "svc-1", First: start, Last: end},
  31. },
  32. },
  33. want: map[string]*kubemodel.Service{
  34. "svc-1": {
  35. UID: "svc-1",
  36. Name: "my-service",
  37. NamespaceUID: "ns-1",
  38. Type: kubemodel.ServiceTypeClusterIP,
  39. Start: start,
  40. End: end,
  41. },
  42. },
  43. },
  44. {
  45. name: "service without uptime is not registered",
  46. overrides: map[string]any{
  47. source.QueryServiceInfo: []*source.ServiceInfoResult{
  48. {UID: "svc-1", Service: "my-service", NamespaceUID: "ns-1"},
  49. },
  50. },
  51. want: map[string]*kubemodel.Service{},
  52. },
  53. {
  54. name: "service without namespace uid is not registered",
  55. overrides: map[string]any{
  56. source.QueryServiceInfo: []*source.ServiceInfoResult{
  57. {UID: "svc-1", Service: "my-service"},
  58. },
  59. source.QueryServiceUptime: []*source.UptimeResult{
  60. {UID: "svc-1", First: start, Last: end},
  61. },
  62. },
  63. want: map[string]*kubemodel.Service{},
  64. },
  65. {
  66. name: "load balancer service with ingress address",
  67. overrides: map[string]any{
  68. source.QueryServiceInfo: []*source.ServiceInfoResult{
  69. {UID: "svc-1", Service: "my-lb", NamespaceUID: "ns-1", ServiceType: "LoadBalancer", LBIngressAddress: "1.2.3.4"},
  70. },
  71. source.QueryServiceUptime: []*source.UptimeResult{
  72. {UID: "svc-1", First: start, Last: end},
  73. },
  74. },
  75. want: map[string]*kubemodel.Service{
  76. "svc-1": {
  77. UID: "svc-1",
  78. Name: "my-lb",
  79. NamespaceUID: "ns-1",
  80. Type: kubemodel.ServiceTypeLoadBalancer,
  81. LBIngressAddress: "1.2.3.4",
  82. Start: start,
  83. End: end,
  84. },
  85. },
  86. },
  87. {
  88. name: "service selector labels are attached",
  89. overrides: map[string]any{
  90. source.QueryServiceInfo: []*source.ServiceInfoResult{
  91. {UID: "svc-1", Service: "my-service", NamespaceUID: "ns-1", ServiceType: "ClusterIP"},
  92. },
  93. source.QueryServiceUptime: []*source.UptimeResult{
  94. {UID: "svc-1", First: start, Last: end},
  95. },
  96. source.QueryServiceSelectorLabels: []*source.ServiceLabelsResult{
  97. {UID: "svc-1", Labels: map[string]string{"app": "web", "tier": "frontend"}},
  98. },
  99. },
  100. want: map[string]*kubemodel.Service{
  101. "svc-1": {
  102. UID: "svc-1",
  103. Name: "my-service",
  104. NamespaceUID: "ns-1",
  105. Type: kubemodel.ServiceTypeClusterIP,
  106. Start: start,
  107. End: end,
  108. Selector: map[string]string{"app": "web", "tier": "frontend"},
  109. },
  110. },
  111. },
  112. {
  113. name: "uptime for unknown service is ignored",
  114. overrides: map[string]any{
  115. source.QueryServiceInfo: []*source.ServiceInfoResult{
  116. {UID: "svc-1", Service: "my-service", NamespaceUID: "ns-1"},
  117. },
  118. source.QueryServiceUptime: []*source.UptimeResult{
  119. {UID: "svc-1", First: start, Last: end},
  120. {UID: "unknown-svc", First: start, Last: end},
  121. },
  122. },
  123. want: map[string]*kubemodel.Service{
  124. "svc-1": {
  125. UID: "svc-1",
  126. Name: "my-service",
  127. NamespaceUID: "ns-1",
  128. Type: kubemodel.ServiceTypeClusterIP,
  129. Start: start,
  130. End: end,
  131. },
  132. },
  133. },
  134. }
  135. for _, tt := range tests {
  136. t.Run(tt.name, func(t *testing.T) {
  137. ds := source.NewMockOpenCostDataSource()
  138. ds.ResolutionValue = 5 * time.Minute
  139. seedCluster(ds, start, end)
  140. for method, result := range tt.overrides {
  141. ds.Querier.SetOverride(method, result)
  142. }
  143. km, err := NewKubeModel(testClusterUID, false, ds)
  144. require.NoError(t, err)
  145. kms, err := km.ComputeKubeModelSet(start, end)
  146. require.NoError(t, err)
  147. assert.Equal(t, tt.want, kms.Services)
  148. })
  149. }
  150. }