kubemodel_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. package kubemodel
  2. import (
  3. "errors"
  4. "fmt"
  5. "testing"
  6. "time"
  7. "github.com/stretchr/testify/require"
  8. )
  9. func TestCheckWindow(t *testing.T) {
  10. start := time.Now().UTC().Truncate(time.Hour)
  11. end := start.Add(time.Hour)
  12. window := Window{Start: start, End: end}
  13. windowErrMsg := func(s, e time.Time) string {
  14. return fmt.Sprintf(
  15. "start or end time (%s-%s) is outside of the window %s-%s",
  16. s.Format(time.RFC3339),
  17. e.Format(time.RFC3339),
  18. start.Format(time.RFC3339),
  19. end.Format(time.RFC3339),
  20. )
  21. }
  22. tests := []struct {
  23. name string
  24. start time.Time
  25. end time.Time
  26. wantErr string
  27. }{
  28. {
  29. name: "start before window",
  30. start: start.Add(-time.Minute),
  31. end: end,
  32. wantErr: windowErrMsg(start.Add(-time.Minute), end),
  33. },
  34. {
  35. name: "end after window",
  36. start: start,
  37. end: end.Add(time.Minute),
  38. wantErr: windowErrMsg(start, end.Add(time.Minute)),
  39. },
  40. {
  41. name: "entirely outside window",
  42. start: end.Add(time.Hour),
  43. end: end.Add(2 * time.Hour),
  44. wantErr: windowErrMsg(end.Add(time.Hour), end.Add(2*time.Hour)),
  45. },
  46. {
  47. name: "exact window boundaries",
  48. start: start,
  49. end: end,
  50. },
  51. {
  52. name: "within window",
  53. start: start.Add(15 * time.Minute),
  54. end: end.Add(-15 * time.Minute),
  55. },
  56. }
  57. for _, tt := range tests {
  58. t.Run(tt.name, func(t *testing.T) {
  59. err := checkWindow(window, tt.start, tt.end)
  60. if tt.wantErr != "" {
  61. require.EqualError(t, err, tt.wantErr)
  62. } else {
  63. require.NoError(t, err)
  64. }
  65. })
  66. }
  67. }
  68. func TestKubeModel(t *testing.T) {
  69. start := time.Now().UTC().Truncate(time.Hour)
  70. end := start.Add(time.Hour)
  71. t.Run("RegisterError", func(t *testing.T) {
  72. kms := NewKubeModelSet(start, end)
  73. require.NotNil(t, kms.Metadata)
  74. require.Len(t, kms.GetErrors(), 0)
  75. kms.Error(errors.New("test error"))
  76. require.Len(t, kms.GetErrors(), 1)
  77. require.Equal(t, "test error", kms.GetErrors()[0].Message)
  78. kms.Error(errors.New("test error 2"))
  79. require.Len(t, kms.GetErrors(), 2)
  80. require.Equal(t, "test error 2", kms.GetErrors()[1].Message)
  81. })
  82. t.Run("RegisterCluster", func(t *testing.T) {
  83. t.Run("empty cluster UID", func(t *testing.T) {
  84. var err error
  85. kms := NewKubeModelSet(start, end)
  86. err = kms.RegisterCluster(&Cluster{UID: ""})
  87. require.NotNil(t, err)
  88. require.Len(t, kms.GetErrors(), 1)
  89. require.Equal(t, "RegisterCluster: invalid cluster: UID is missing for Cluster with name ''", kms.GetErrors()[0].Message)
  90. require.Nil(t, kms.Cluster)
  91. })
  92. t.Run("new cluster UID", func(t *testing.T) {
  93. var err error
  94. var clusterUID = "cluster-uid"
  95. kms := NewKubeModelSet(start, end)
  96. err = kms.RegisterCluster(&Cluster{UID: clusterUID, Start: start, End: end})
  97. require.Nil(t, err)
  98. require.Len(t, kms.GetErrors(), 0)
  99. require.NotNil(t, kms.Cluster)
  100. require.Equal(t, clusterUID, kms.Cluster.UID)
  101. })
  102. t.Run("multiple Register calls", func(t *testing.T) {
  103. var err error
  104. var clusterUID = "cluster-uid"
  105. kms := NewKubeModelSet(start, end)
  106. err = kms.RegisterCluster(&Cluster{UID: clusterUID, Start: start, End: end})
  107. require.Nil(t, err)
  108. require.Len(t, kms.GetErrors(), 0)
  109. require.NotNil(t, kms.Cluster)
  110. require.Equal(t, clusterUID, kms.Cluster.UID)
  111. // Register cluster with same UID, expect no-op on second try
  112. err = kms.RegisterCluster(&Cluster{UID: clusterUID, Start: start, End: end})
  113. require.Nil(t, err)
  114. require.Len(t, kms.GetErrors(), 0)
  115. require.NotNil(t, kms.Cluster)
  116. require.Equal(t, clusterUID, kms.Cluster.UID)
  117. // Register cluster with another UID (should not happen), expect no-op
  118. err = kms.RegisterCluster(&Cluster{UID: "another-uid", Start: start, End: end})
  119. require.Nil(t, err)
  120. require.Len(t, kms.GetWarnings(), 1)
  121. require.Equal(t, "RegisterCluster(another-uid): attempting to change cluster UID from cluster-uid to another-uid", kms.GetWarnings()[0].Message)
  122. require.NotNil(t, kms.Cluster)
  123. require.Equal(t, clusterUID, kms.Cluster.UID) // original kms.Cluster is not modified
  124. })
  125. })
  126. t.Run("RegisterNamespace", func(t *testing.T) {
  127. t.Run("empty namespace UID", func(t *testing.T) {
  128. var err error
  129. kms := NewKubeModelSet(start, end)
  130. err = kms.RegisterNamespace(&Namespace{UID: "", Name: ""})
  131. require.NotNil(t, err)
  132. require.Len(t, kms.GetErrors(), 1)
  133. require.Equal(t, "RegisterNamespace: invalid namespace: UID is missing for Namespace with name ''", kms.GetErrors()[0].Message)
  134. require.Len(t, kms.Namespaces, 0)
  135. })
  136. t.Run("register namespace on KMS w/o cluster", func(t *testing.T) {
  137. var err error
  138. kms := NewKubeModelSet(start, end)
  139. testUID := "uid"
  140. testName := "name"
  141. err = kms.RegisterNamespace(&Namespace{UID: testUID, Name: testName, Start: start, End: end})
  142. require.Nil(t, err)
  143. require.Len(t, kms.GetWarnings(), 1)
  144. require.Equal(t, "RegisterNamespace: Cluster is nil", kms.GetWarnings()[0].Message)
  145. testNamespace := &Namespace{UID: testUID, Name: testName, Start: start, End: end}
  146. require.NotNil(t, kms.Namespaces[testUID])
  147. require.Equal(t, testNamespace, kms.Namespaces[testUID])
  148. require.Equal(t, 1, kms.Metadata.ObjectCount)
  149. })
  150. t.Run("register namespace on KMS w/ cluster", func(t *testing.T) {
  151. var err error
  152. kms := NewKubeModelSet(start, end)
  153. err = kms.RegisterCluster(&Cluster{UID: "cluster-uid", Start: start, End: end})
  154. require.Nil(t, err)
  155. // At this point we have a KMS with a cluster registered
  156. testUID := "uid"
  157. testName := "name"
  158. err = kms.RegisterNamespace(&Namespace{UID: testUID, Name: testName, Start: start, End: end})
  159. require.Nil(t, err)
  160. require.Len(t, kms.GetErrors(), 0)
  161. require.NotNil(t, kms.Namespaces[testUID])
  162. testNamespace := &Namespace{UID: testUID, Name: testName, Start: start, End: end}
  163. require.Equal(t, testNamespace, kms.Namespaces[testUID])
  164. require.Equal(t, 1, kms.Metadata.ObjectCount)
  165. // Register same namespace again, expect no-op on second try
  166. err = kms.RegisterNamespace(&Namespace{UID: testUID, Name: testName, Start: start, End: end})
  167. require.Nil(t, err)
  168. require.Len(t, kms.GetErrors(), 0)
  169. require.NotNil(t, kms.Namespaces[testUID])
  170. require.Equal(t, testNamespace, kms.Namespaces[testUID])
  171. require.Equal(t, 1, kms.Metadata.ObjectCount) // remains 1
  172. })
  173. })
  174. t.Run("RegisterResourceQuota", func(t *testing.T) {
  175. t.Run("empty resourceQuota UID", func(t *testing.T) {
  176. var err error
  177. kms := NewKubeModelSet(start, end)
  178. err = kms.RegisterResourceQuota(&ResourceQuota{UID: "", Name: "test"})
  179. require.NotNil(t, err)
  180. require.Len(t, kms.GetErrors(), 1)
  181. require.Equal(t, "RegisterResourceQuota: invalid resource quota: UID is missing for ResourceQuota with name 'test'", kms.GetErrors()[0].Message)
  182. require.Len(t, kms.ResourceQuotas, 0)
  183. })
  184. t.Run("register resource quota with empty NamespaceUID", func(t *testing.T) {
  185. var err error
  186. kms := NewKubeModelSet(start, end)
  187. err = kms.RegisterResourceQuota(&ResourceQuota{UID: "uid", Name: "name", NamespaceUID: ""})
  188. require.NotNil(t, err)
  189. require.Len(t, kms.GetErrors(), 1)
  190. require.Equal(t, "RegisterResourceQuota: invalid resource quota: NamespaceUID is missing for ResourceQuota 'uid'", kms.GetErrors()[0].Message)
  191. require.Len(t, kms.ResourceQuotas, 0)
  192. })
  193. t.Run("register resource quota on KMS w/ namespace", func(t *testing.T) {
  194. kms := NewKubeModelSet(start, end)
  195. kms.RegisterCluster(&Cluster{UID: "cluster-uid", Start: start, End: end})
  196. kms.RegisterNamespace(&Namespace{UID: "namespace-uid", Name: "namespace", Start: start, End: end})
  197. // At this point we have a KMS with a cluster and namespace registered
  198. testUID := "uid"
  199. testName := "name"
  200. kms.RegisterResourceQuota(&ResourceQuota{UID: testUID, Name: testName, NamespaceUID: "namespace-uid", Start: start, End: end})
  201. testRQ := &ResourceQuota{
  202. UID: "uid",
  203. NamespaceUID: "namespace-uid",
  204. Name: "name",
  205. Start: start,
  206. End: end,
  207. Spec: &ResourceQuotaSpec{Hard: &ResourceQuotaSpecHard{}},
  208. Status: &ResourceQuotaStatus{Used: &ResourceQuotaStatusUsed{}},
  209. }
  210. require.Len(t, kms.GetErrors(), 0)
  211. require.NotNil(t, kms.ResourceQuotas[testUID])
  212. require.Equal(t, testRQ, kms.ResourceQuotas[testUID])
  213. require.Equal(t, 2, kms.Metadata.ObjectCount) // 1 namespace and 1 RQ
  214. // Register same RQ again, expect no-op on second try
  215. kms.RegisterResourceQuota(&ResourceQuota{UID: testUID, Name: testName, NamespaceUID: "namespace-uid", Start: start, End: end})
  216. require.Len(t, kms.GetErrors(), 0)
  217. require.NotNil(t, kms.ResourceQuotas[testUID])
  218. require.Equal(t, testRQ, kms.ResourceQuotas[testUID])
  219. require.Equal(t, 2, kms.Metadata.ObjectCount) // 1 namespace and 1 RQ
  220. })
  221. t.Run("register multiple RQs in multiple namespaces", func(t *testing.T) {
  222. kms := NewKubeModelSet(start, end)
  223. kms.RegisterCluster(&Cluster{UID: "cluster-uid", Start: start, End: end})
  224. kms.RegisterNamespace(&Namespace{UID: "namespace-1-uid", Name: "namespace-1", Start: start, End: end})
  225. kms.RegisterNamespace(&Namespace{UID: "namespace-2-uid", Name: "namespace-2", Start: start, End: end})
  226. kms.RegisterResourceQuota(&ResourceQuota{UID: "uid-1", Name: "name-1", NamespaceUID: "namespace-1-uid", Start: start, End: end})
  227. kms.RegisterResourceQuota(&ResourceQuota{UID: "uid-2", Name: "name-2", NamespaceUID: "namespace-2-uid", Start: start, End: end})
  228. require.Len(t, kms.GetErrors(), 0)
  229. require.NotNil(t, kms.ResourceQuotas)
  230. require.Len(t, kms.ResourceQuotas, 2)
  231. testRQ1 := &ResourceQuota{
  232. UID: "uid-1",
  233. NamespaceUID: "namespace-1-uid",
  234. Name: "name-1",
  235. Start: start,
  236. End: end,
  237. Spec: &ResourceQuotaSpec{Hard: &ResourceQuotaSpecHard{}},
  238. Status: &ResourceQuotaStatus{Used: &ResourceQuotaStatusUsed{}},
  239. }
  240. testRQ2 := &ResourceQuota{
  241. UID: "uid-2",
  242. NamespaceUID: "namespace-2-uid",
  243. Name: "name-2",
  244. Start: start,
  245. End: end,
  246. Spec: &ResourceQuotaSpec{Hard: &ResourceQuotaSpecHard{}},
  247. Status: &ResourceQuotaStatus{Used: &ResourceQuotaStatusUsed{}},
  248. }
  249. require.Equal(t, testRQ1, kms.ResourceQuotas["uid-1"])
  250. require.Equal(t, testRQ2, kms.ResourceQuotas["uid-2"])
  251. require.Equal(t, 4, kms.Metadata.ObjectCount) // 2 namespaces and 2 RQs
  252. // Register a third RQ with empty NamespaceUID — expect error, not registered
  253. err := kms.RegisterResourceQuota(&ResourceQuota{UID: "uid-3", Name: "name-3", NamespaceUID: ""})
  254. require.NotNil(t, err)
  255. require.Len(t, kms.GetErrors(), 1)
  256. require.Equal(t, "RegisterResourceQuota: invalid resource quota: NamespaceUID is missing for ResourceQuota 'uid-3'", kms.GetErrors()[0].Message)
  257. require.Len(t, kms.ResourceQuotas, 2) // still 2
  258. require.Equal(t, 4, kms.Metadata.ObjectCount) // unchanged
  259. })
  260. })
  261. }