gcpprovider_test.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. package cloud
  2. import (
  3. "bytes"
  4. "io/ioutil"
  5. "reflect"
  6. "testing"
  7. )
  8. func TestParseGCPInstanceTypeLabel(t *testing.T) {
  9. cases := []struct {
  10. input string
  11. expected string
  12. }{
  13. {
  14. input: "n1-standard-2",
  15. expected: "n1standard",
  16. },
  17. {
  18. input: "e2-medium",
  19. expected: "e2medium",
  20. },
  21. {
  22. input: "k3s",
  23. expected: "unknown",
  24. },
  25. {
  26. input: "custom-n1-standard-2",
  27. expected: "custom",
  28. },
  29. {
  30. input: "n2d-highmem-8",
  31. expected: "n2dstandard",
  32. },
  33. }
  34. for _, test := range cases {
  35. result := parseGCPInstanceTypeLabel(test.input)
  36. if result != test.expected {
  37. t.Errorf("Input: %s, Expected: %s, Actual: %s", test.input, test.expected, result)
  38. }
  39. }
  40. }
  41. func TestParseGCPProjectID(t *testing.T) {
  42. cases := []struct {
  43. input string
  44. expected string
  45. }{
  46. {
  47. input: "gce://guestbook-12345/...",
  48. expected: "guestbook-12345",
  49. },
  50. {
  51. input: "gce:/guestbook-12345/...",
  52. expected: "",
  53. },
  54. {
  55. input: "asdfa",
  56. expected: "",
  57. },
  58. {
  59. input: "",
  60. expected: "",
  61. },
  62. }
  63. for _, test := range cases {
  64. result := parseGCPProjectID(test.input)
  65. if result != test.expected {
  66. t.Errorf("Input: %s, Expected: %s, Actual: %s", test.input, test.expected, result)
  67. }
  68. }
  69. }
  70. func TestGetUsageType(t *testing.T) {
  71. cases := []struct {
  72. input map[string]string
  73. expected string
  74. }{
  75. {
  76. input: map[string]string{
  77. GKEPreemptibleLabel: "true",
  78. },
  79. expected: "preemptible",
  80. },
  81. {
  82. input: map[string]string{
  83. GKESpotLabel: "true",
  84. },
  85. expected: "preemptible",
  86. },
  87. {
  88. input: map[string]string{
  89. KarpenterCapacityTypeLabel: KarpenterCapacitySpotTypeValue,
  90. },
  91. expected: "preemptible",
  92. },
  93. {
  94. input: map[string]string{
  95. "someotherlabel": "true",
  96. },
  97. expected: "ondemand",
  98. },
  99. {
  100. input: map[string]string{},
  101. expected: "ondemand",
  102. },
  103. }
  104. for _, test := range cases {
  105. result := getUsageType(test.input)
  106. if result != test.expected {
  107. t.Errorf("Input: %v, Expected: %s, Actual: %s", test.input, test.expected, result)
  108. }
  109. }
  110. }
  111. // tests basic parsing of GCP pricing API responses
  112. // Load a reader object on a portion of a GCP api response
  113. // Confirm that the resting *GCP object contains the correctly parsed pricing info
  114. func TestParsePage(t *testing.T) {
  115. gcpSkuString := `
  116. {
  117. "skus": [
  118. {
  119. "name": "services/6F81-5844-456A/skus/039F-D0DA-4055",
  120. "skuId": "039F-D0DA-4055",
  121. "description": "Nvidia Tesla A100 GPU running in Americas",
  122. "category": {
  123. "serviceDisplayName": "Compute Engine",
  124. "resourceFamily": "Compute",
  125. "resourceGroup": "GPU",
  126. "usageType": "OnDemand"
  127. },
  128. "serviceRegions": [
  129. "us-central1",
  130. "us-east1",
  131. "us-west1"
  132. ],
  133. "pricingInfo": [
  134. {
  135. "summary": "",
  136. "pricingExpression": {
  137. "usageUnit": "h",
  138. "displayQuantity": 1,
  139. "tieredRates": [
  140. {
  141. "startUsageAmount": 0,
  142. "unitPrice": {
  143. "currencyCode": "USD",
  144. "units": "2",
  145. "nanos": 933908000
  146. }
  147. }
  148. ],
  149. "usageUnitDescription": "hour",
  150. "baseUnit": "s",
  151. "baseUnitDescription": "second",
  152. "baseUnitConversionFactor": 3600
  153. },
  154. "currencyConversionRate": 1,
  155. "effectiveTime": "2023-03-24T10:52:50.681Z"
  156. }
  157. ],
  158. "serviceProviderName": "Google",
  159. "geoTaxonomy": {
  160. "type": "MULTI_REGIONAL",
  161. "regions": [
  162. "us-central1",
  163. "us-east1",
  164. "us-west1"
  165. ]
  166. }
  167. },
  168. {
  169. "name": "services/6F81-5844-456A/skus/2390-DCAF-DA38",
  170. "skuId": "2390-DCAF-DA38",
  171. "description": "A2 Instance Ram running in Americas",
  172. "category": {
  173. "serviceDisplayName": "Compute Engine",
  174. "resourceFamily": "Compute",
  175. "resourceGroup": "RAM",
  176. "usageType": "OnDemand"
  177. },
  178. "serviceRegions": [
  179. "us-central1",
  180. "us-east1",
  181. "us-west1"
  182. ],
  183. "pricingInfo": [
  184. {
  185. "summary": "",
  186. "pricingExpression": {
  187. "usageUnit": "GiBy.h",
  188. "displayQuantity": 1,
  189. "tieredRates": [
  190. {
  191. "startUsageAmount": 0,
  192. "unitPrice": {
  193. "currencyCode": "USD",
  194. "units": "0",
  195. "nanos": 4237000
  196. }
  197. }
  198. ],
  199. "usageUnitDescription": "gibibyte hour",
  200. "baseUnit": "By.s",
  201. "baseUnitDescription": "byte second",
  202. "baseUnitConversionFactor": 3865470566400
  203. },
  204. "currencyConversionRate": 1,
  205. "effectiveTime": "2023-03-24T10:52:50.681Z"
  206. }
  207. ],
  208. "serviceProviderName": "Google",
  209. "geoTaxonomy": {
  210. "type": "MULTI_REGIONAL",
  211. "regions": [
  212. "us-central1",
  213. "us-east1",
  214. "us-west1"
  215. ]
  216. }
  217. },
  218. {
  219. "name": "services/6F81-5844-456A/skus/2922-40C5-B19F",
  220. "skuId": "2922-40C5-B19F",
  221. "description": "A2 Instance Core running in Americas",
  222. "category": {
  223. "serviceDisplayName": "Compute Engine",
  224. "resourceFamily": "Compute",
  225. "resourceGroup": "CPU",
  226. "usageType": "OnDemand"
  227. },
  228. "serviceRegions": [
  229. "us-central1",
  230. "us-east1",
  231. "us-west1"
  232. ],
  233. "pricingInfo": [
  234. {
  235. "summary": "",
  236. "pricingExpression": {
  237. "usageUnit": "h",
  238. "displayQuantity": 1,
  239. "tieredRates": [
  240. {
  241. "startUsageAmount": 0,
  242. "unitPrice": {
  243. "currencyCode": "USD",
  244. "units": "0",
  245. "nanos": 31611000
  246. }
  247. }
  248. ],
  249. "usageUnitDescription": "hour",
  250. "baseUnit": "s",
  251. "baseUnitDescription": "second",
  252. "baseUnitConversionFactor": 3600
  253. },
  254. "currencyConversionRate": 1,
  255. "effectiveTime": "2023-03-24T10:52:50.681Z"
  256. }
  257. ],
  258. "serviceProviderName": "Google",
  259. "geoTaxonomy": {
  260. "type": "MULTI_REGIONAL",
  261. "regions": [
  262. "us-central1",
  263. "us-east1",
  264. "us-west1"
  265. ]
  266. }
  267. }
  268. ],
  269. "nextPageToken": "APKCS1HVa0YpwgyTFbqbJ1eGwzKZmsPwLqzMZPTSNia5ck1Hc54Tx_Kz3oBxwSnRIdGVxXoSPdf-XlDpyNBf4QuxKcIEgtrQ1LDLWAgZowI0ns7HjrGta2s="
  270. }
  271. `
  272. reader := ioutil.NopCloser(bytes.NewBufferString(gcpSkuString))
  273. testGcp := &GCP{}
  274. inputKeys := map[string]Key{
  275. "us-central1,a2highgpu,ondemand,gpu": &gcpKey{
  276. Labels: map[string]string{
  277. "node.kubernetes.io/instance-type": "a2-highgpu-1g",
  278. "cloud.google.com/gke-gpu": "true",
  279. "cloud.google.com/gke-accelerator": "nvidia-tesla-a100",
  280. "topology.kubernetes.io/region": "us-central1",
  281. },
  282. },
  283. }
  284. pvKeys := map[string]PVKey{}
  285. actualPrices, token, err := testGcp.parsePage(reader, inputKeys, pvKeys)
  286. if err != nil {
  287. t.Fatalf("got error parsing page: %v", err)
  288. }
  289. const expectedToken = "APKCS1HVa0YpwgyTFbqbJ1eGwzKZmsPwLqzMZPTSNia5ck1Hc54Tx_Kz3oBxwSnRIdGVxXoSPdf-XlDpyNBf4QuxKcIEgtrQ1LDLWAgZowI0ns7HjrGta2s="
  290. if token != expectedToken {
  291. t.Fatalf("error parsing GCP next page token, parsed %s but expected %s", token, expectedToken)
  292. }
  293. expectedActualPrices := map[string]*GCPPricing{
  294. "us-central1,a2highgpu,ondemand,gpu": &GCPPricing{
  295. Name: "services/6F81-5844-456A/skus/039F-D0DA-4055",
  296. SKUID: "039F-D0DA-4055",
  297. Description: "Nvidia Tesla A100 GPU running in Americas",
  298. Category: &GCPResourceInfo{
  299. ServiceDisplayName: "Compute Engine",
  300. ResourceFamily: "Compute",
  301. ResourceGroup: "GPU",
  302. UsageType: "OnDemand",
  303. },
  304. ServiceRegions: []string{"us-central1", "us-east1", "us-west1"},
  305. PricingInfo: []*PricingInfo{
  306. &PricingInfo{
  307. Summary: "",
  308. PricingExpression: &PricingExpression{
  309. UsageUnit: "h",
  310. UsageUnitDescription: "hour",
  311. BaseUnit: "s",
  312. BaseUnitConversionFactor: 0,
  313. DisplayQuantity: 1,
  314. TieredRates: []*TieredRates{
  315. &TieredRates{
  316. StartUsageAmount: 0,
  317. UnitPrice: &UnitPriceInfo{
  318. CurrencyCode: "USD",
  319. Units: "2",
  320. Nanos: 933908000,
  321. },
  322. },
  323. },
  324. },
  325. CurrencyConversionRate: 1,
  326. EffectiveTime: "2023-03-24T10:52:50.681Z",
  327. },
  328. },
  329. ServiceProviderName: "Google",
  330. Node: &Node{
  331. VCPUCost: "0.031611",
  332. RAMCost: "0.004237",
  333. UsesBaseCPUPrice: false,
  334. GPU: "1",
  335. GPUName: "nvidia-tesla-a100",
  336. GPUCost: "2.933908",
  337. },
  338. },
  339. "us-central1,a2highgpu,ondemand": &GCPPricing{
  340. Node: &Node{
  341. VCPUCost: "0.031611",
  342. RAMCost: "0.004237",
  343. UsesBaseCPUPrice: false,
  344. UsageType: "ondemand",
  345. },
  346. },
  347. }
  348. if !reflect.DeepEqual(actualPrices, expectedActualPrices) {
  349. t.Fatalf("error parsing GCP prices. parsed %v but expected %v", actualPrices, expectedActualPrices)
  350. }
  351. }