awsprovider_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. package cloud
  2. import (
  3. "bytes"
  4. "io/ioutil"
  5. "net/http"
  6. "net/url"
  7. "reflect"
  8. "testing"
  9. )
  10. func Test_awsKey_getUsageType(t *testing.T) {
  11. type fields struct {
  12. Labels map[string]string
  13. ProviderID string
  14. }
  15. type args struct {
  16. labels map[string]string
  17. }
  18. tests := []struct {
  19. name string
  20. fields fields
  21. args args
  22. want string
  23. }{
  24. {
  25. // test with no labels should return false
  26. name: "Label does not have the capacityType label associated with it",
  27. args: args{
  28. labels: map[string]string{},
  29. },
  30. want: "",
  31. },
  32. {
  33. name: "EKS label with a capacityType set to empty string should return empty string",
  34. args: args{
  35. labels: map[string]string{
  36. EKSCapacityTypeLabel: "",
  37. },
  38. },
  39. want: "",
  40. },
  41. {
  42. name: "EKS label with capacityType set to a random value should return empty string",
  43. args: args{
  44. labels: map[string]string{
  45. EKSCapacityTypeLabel: "TEST_ME",
  46. },
  47. },
  48. want: "",
  49. },
  50. {
  51. name: "EKS label with capacityType set to spot should return spot",
  52. args: args{
  53. labels: map[string]string{
  54. EKSCapacityTypeLabel: EKSCapacitySpotTypeValue,
  55. },
  56. },
  57. want: PreemptibleType,
  58. },
  59. {
  60. name: "Karpenter label with a capacityType set to empty string should return empty string",
  61. args: args{
  62. labels: map[string]string{
  63. KarpenterCapacityTypeLabel: "",
  64. },
  65. },
  66. want: "",
  67. },
  68. {
  69. name: "Karpenter label with capacityType set to a random value should return empty string",
  70. args: args{
  71. labels: map[string]string{
  72. KarpenterCapacityTypeLabel: "TEST_ME",
  73. },
  74. },
  75. want: "",
  76. },
  77. {
  78. name: "Karpenter label with capacityType set to spot should return spot",
  79. args: args{
  80. labels: map[string]string{
  81. KarpenterCapacityTypeLabel: KarpenterCapacitySpotTypeValue,
  82. },
  83. },
  84. want: PreemptibleType,
  85. },
  86. }
  87. for _, tt := range tests {
  88. t.Run(tt.name, func(t *testing.T) {
  89. k := &awsKey{
  90. Labels: tt.fields.Labels,
  91. ProviderID: tt.fields.ProviderID,
  92. }
  93. if got := k.getUsageType(tt.args.labels); got != tt.want {
  94. t.Errorf("getUsageType() = %v, want %v", got, tt.want)
  95. }
  96. })
  97. }
  98. }
  99. // Test_populate_pricing
  100. //
  101. // Objective: To test core pricing population logic for AWS
  102. //
  103. // Case 0: US endpoints
  104. // Take a portion of json returned from ondemand terms in us endpoints
  105. // load the request into the http response and give it to the function
  106. // inspect the resulting aws object after the function returns and validate fields
  107. // Case 1: Chinese endpoints
  108. // Same as above US test case, except using CN PV offer codes
  109. // Validate populated fields in AWS object
  110. func Test_populate_pricing(t *testing.T) {
  111. awsTest := AWS{
  112. ValidPricingKeys: map[string]bool{},
  113. }
  114. inputkeys := map[string]bool{
  115. "us-east-2,m5.large,linux": true,
  116. }
  117. // Case 0
  118. awsUSEastString := `
  119. {
  120. "formatVersion" : "v1.0",
  121. "disclaimer" : "This pricing list is for informational purposes only. All prices are subject to the additional terms included in the pricing pages on http://aws.amazon.com. All Free Tier prices are also subject to the terms included at https://aws.amazon.com/free/",
  122. "offerCode" : "AmazonEC2",
  123. "version" : "20230322145651",
  124. "publicationDate" : "2023-03-22T14:56:51Z",
  125. "products" : {
  126. "8D49XP354UEYTHGM" : {
  127. "sku" : "8D49XP354UEYTHGM",
  128. "productFamily" : "Compute Instance",
  129. "attributes" : {
  130. "servicecode" : "AmazonEC2",
  131. "location" : "US East (Ohio)",
  132. "locationType" : "AWS Region",
  133. "instanceType" : "m5.large",
  134. "currentGeneration" : "Yes",
  135. "instanceFamily" : "General purpose",
  136. "vcpu" : "2",
  137. "physicalProcessor" : "Intel Xeon Platinum 8175",
  138. "clockSpeed" : "3.1 GHz",
  139. "memory" : "8 GiB",
  140. "storage" : "EBS only",
  141. "networkPerformance" : "Up to 10 Gigabit",
  142. "processorArchitecture" : "64-bit",
  143. "tenancy" : "Shared",
  144. "operatingSystem" : "Linux",
  145. "licenseModel" : "No License required",
  146. "usagetype" : "USE2-BoxUsage:m5.large",
  147. "operation" : "RunInstances",
  148. "availabilityzone" : "NA",
  149. "capacitystatus" : "Used",
  150. "classicnetworkingsupport" : "false",
  151. "dedicatedEbsThroughput" : "Up to 2120 Mbps",
  152. "ecu" : "10",
  153. "enhancedNetworkingSupported" : "Yes",
  154. "gpuMemory" : "NA",
  155. "intelAvxAvailable" : "Yes",
  156. "intelAvx2Available" : "Yes",
  157. "intelTurboAvailable" : "Yes",
  158. "marketoption" : "OnDemand",
  159. "normalizationSizeFactor" : "4",
  160. "preInstalledSw" : "NA",
  161. "processorFeatures" : "Intel AVX; Intel AVX2; Intel AVX512; Intel Turbo",
  162. "regionCode" : "us-east-2",
  163. "servicename" : "Amazon Elastic Compute Cloud",
  164. "vpcnetworkingsupport" : "true"
  165. }
  166. },
  167. "9ZEEN7WWWQKAG292" : {
  168. "sku" : "9ZEEN7WWWQKAG292",
  169. "productFamily" : "Compute Instance",
  170. "attributes" : {
  171. "servicecode" : "AmazonEC2",
  172. "location" : "US East (Ohio)",
  173. "locationType" : "AWS Region",
  174. "instanceType" : "p3.8xlarge",
  175. "currentGeneration" : "Yes",
  176. "instanceFamily" : "GPU instance",
  177. "vcpu" : "32",
  178. "physicalProcessor" : "Intel Xeon E5-2686 v4 (Broadwell)",
  179. "clockSpeed" : "2.3 GHz",
  180. "memory" : "244 GiB",
  181. "storage" : "EBS only",
  182. "networkPerformance" : "10 Gigabit",
  183. "processorArchitecture" : "64-bit",
  184. "tenancy" : "Shared",
  185. "operatingSystem" : "Windows",
  186. "licenseModel" : "Bring your own license",
  187. "usagetype" : "USE2-BoxUsage:p3.8xlarge",
  188. "operation" : "RunInstances:0800",
  189. "availabilityzone" : "NA",
  190. "capacitystatus" : "Used",
  191. "classicnetworkingsupport" : "false",
  192. "dedicatedEbsThroughput" : "7000 Mbps",
  193. "ecu" : "97",
  194. "enhancedNetworkingSupported" : "Yes",
  195. "gpu" : "4",
  196. "gpuMemory" : "NA",
  197. "intelAvxAvailable" : "Yes",
  198. "intelAvx2Available" : "Yes",
  199. "intelTurboAvailable" : "Yes",
  200. "marketoption" : "OnDemand",
  201. "normalizationSizeFactor" : "64",
  202. "preInstalledSw" : "NA",
  203. "processorFeatures" : "Intel AVX; Intel AVX2; Intel Turbo",
  204. "regionCode" : "us-east-2",
  205. "servicename" : "Amazon Elastic Compute Cloud",
  206. "vpcnetworkingsupport" : "true"
  207. }
  208. },
  209. "M6UGCCQ3CDJQAA37" : {
  210. "sku" : "M6UGCCQ3CDJQAA37",
  211. "productFamily" : "Storage",
  212. "attributes" : {
  213. "servicecode" : "AmazonEC2",
  214. "location" : "US East (Ohio)",
  215. "locationType" : "AWS Region",
  216. "storageMedia" : "SSD-backed",
  217. "volumeType" : "General Purpose",
  218. "maxVolumeSize" : "16 TiB",
  219. "maxIopsvolume" : "16000",
  220. "maxThroughputvolume" : "1000 MiB/s",
  221. "usagetype" : "USE2-EBS:VolumeUsage.gp3",
  222. "operation" : "",
  223. "regionCode" : "us-east-2",
  224. "servicename" : "Amazon Elastic Compute Cloud",
  225. "volumeApiName" : "gp3"
  226. }
  227. }
  228. },
  229. "terms" : {
  230. "OnDemand" : {
  231. "M6UGCCQ3CDJQAA37" : {
  232. "M6UGCCQ3CDJQAA37.JRTCKXETXF" : {
  233. "offerTermCode" : "JRTCKXETXF",
  234. "sku" : "M6UGCCQ3CDJQAA37",
  235. "effectiveDate" : "2023-03-01T00:00:00Z",
  236. "priceDimensions" : {
  237. "M6UGCCQ3CDJQAA37.JRTCKXETXF.6YS6EN2CT7" : {
  238. "rateCode" : "M6UGCCQ3CDJQAA37.JRTCKXETXF.6YS6EN2CT7",
  239. "description" : "$0.08 per GB-month of General Purpose (gp3) provisioned storage - US East (Ohio)",
  240. "beginRange" : "0",
  241. "endRange" : "Inf",
  242. "unit" : "GB-Mo",
  243. "pricePerUnit" : {
  244. "USD" : "0.0800000000"
  245. },
  246. "appliesTo" : [ ]
  247. }
  248. },
  249. "termAttributes" : { }
  250. }
  251. },
  252. "9ZEEN7WWWQKAG292" : {
  253. "9ZEEN7WWWQKAG292.JRTCKXETXF" : {
  254. "offerTermCode" : "JRTCKXETXF",
  255. "sku" : "9ZEEN7WWWQKAG292",
  256. "effectiveDate" : "2023-03-01T00:00:00Z",
  257. "priceDimensions" : {
  258. "9ZEEN7WWWQKAG292.JRTCKXETXF.6YS6EN2CT7" : {
  259. "rateCode" : "9ZEEN7WWWQKAG292.JRTCKXETXF.6YS6EN2CT7",
  260. "description" : "$12.24 per On Demand Windows BYOL p3.8xlarge Instance Hour",
  261. "beginRange" : "0",
  262. "endRange" : "Inf",
  263. "unit" : "Hrs",
  264. "pricePerUnit" : {
  265. "USD" : "12.2400000000"
  266. },
  267. "appliesTo" : [ ]
  268. }
  269. },
  270. "termAttributes" : { }
  271. }
  272. },
  273. "8D49XP354UEYTHGM" : {
  274. "8D49XP354UEYTHGM.MZU6U2429S" : {
  275. "offerTermCode" : "MZU6U2429S",
  276. "sku" : "8D49XP354UEYTHGM",
  277. "effectiveDate" : "2019-01-01T00:00:00Z",
  278. "priceDimensions" : {
  279. "8D49XP354UEYTHGM.MZU6U2429S.2TG2D8R56U" : {
  280. "rateCode" : "8D49XP354UEYTHGM.MZU6U2429S.2TG2D8R56U",
  281. "description" : "Upfront Fee",
  282. "unit" : "Quantity",
  283. "pricePerUnit" : {
  284. "USD" : "1161"
  285. },
  286. "appliesTo" : [ ]
  287. },
  288. },
  289. "termAttributes" : {
  290. "LeaseContractLength" : "3yr",
  291. "OfferingClass" : "convertible",
  292. "PurchaseOption" : "All Upfront"
  293. }
  294. }
  295. }
  296. }
  297. },
  298. "attributesList" : { }
  299. }
  300. `
  301. testResponse := http.Response{
  302. Body: ioutil.NopCloser(bytes.NewBufferString(awsUSEastString)),
  303. Request: &http.Request{
  304. URL: &url.URL{
  305. Scheme: "https",
  306. Host: "test-aws-http-endpoint:443",
  307. },
  308. },
  309. }
  310. awsTest.populatePricing(&testResponse, inputkeys)
  311. expectedProdTermsDisk := &AWSProductTerms{
  312. Sku: "M6UGCCQ3CDJQAA37",
  313. Memory: "",
  314. Storage: "",
  315. VCpu: "",
  316. GPU: "",
  317. OnDemand: &AWSOfferTerm{
  318. Sku: "M6UGCCQ3CDJQAA37",
  319. OfferTermCode: "JRTCKXETXF",
  320. PriceDimensions: map[string]*AWSRateCode{
  321. "M6UGCCQ3CDJQAA37.JRTCKXETXF.6YS6EN2CT7": &AWSRateCode{
  322. Unit: "GB-Mo",
  323. PricePerUnit: AWSCurrencyCode{
  324. USD: "0.0800000000",
  325. CNY: "",
  326. },
  327. },
  328. },
  329. },
  330. PV: &PV{
  331. Cost: "0.00010958904109589041",
  332. CostPerIO: "",
  333. Class: "gp3",
  334. Size: "",
  335. Region: "us-east-2",
  336. ProviderID: "",
  337. },
  338. }
  339. expectedProdTermsInstanceOndemand := &AWSProductTerms{
  340. Sku: "8D49XP354UEYTHGM",
  341. Memory: "8 GiB",
  342. Storage: "EBS only",
  343. VCpu: "2",
  344. GPU: "",
  345. OnDemand: &AWSOfferTerm{
  346. Sku: "",
  347. OfferTermCode: "",
  348. PriceDimensions: nil,
  349. },
  350. }
  351. expectedProdTermsInstanceSpot := &AWSProductTerms{
  352. Sku: "8D49XP354UEYTHGM",
  353. Memory: "8 GiB",
  354. Storage: "EBS only",
  355. VCpu: "2",
  356. GPU: "",
  357. OnDemand: &AWSOfferTerm{
  358. Sku: "",
  359. OfferTermCode: "",
  360. PriceDimensions: nil,
  361. },
  362. }
  363. expectedPricing := map[string]*AWSProductTerms{
  364. "us-east-2,EBS:VolumeUsage.gp3": expectedProdTermsDisk,
  365. "us-east-2,EBS:VolumeUsage.gp3,preemptible": expectedProdTermsDisk,
  366. "us-east-2,m5.large,linux": expectedProdTermsInstanceOndemand,
  367. "us-east-2,m5.large,linux,preemptible": expectedProdTermsInstanceSpot,
  368. }
  369. if !reflect.DeepEqual(expectedPricing, awsTest.Pricing) {
  370. t.Fatalf("expected parsed pricing did not match actual parsed result (us-east-1)")
  371. }
  372. // Case 1
  373. awsCnString := `
  374. {
  375. "formatVersion" : "v1.0",
  376. "disclaimer" : "This pricing list is for informational purposes only. All prices are subject to the additional terms included in the pricing pages on http://www.amazonaws.cn.",
  377. "offerCode" : "AmazonEC2",
  378. "version" : "20230314154740",
  379. "publicationDate" : "2023-03-14T15:47:40Z",
  380. "products" : {
  381. "R83VXG9NAPDASEGN" : {
  382. "sku" : "R83VXG9NAPDASEGN",
  383. "productFamily" : "Storage",
  384. "attributes" : {
  385. "servicecode" : "AmazonEC2",
  386. "location" : "China (Ningxia)",
  387. "locationType" : "AWS Region",
  388. "storageMedia" : "SSD-backed",
  389. "volumeType" : "General Purpose",
  390. "maxVolumeSize" : "16 TiB",
  391. "maxIopsvolume" : "16000",
  392. "maxThroughputvolume" : "1000 MiB/s",
  393. "usagetype" : "CNW1-EBS:VolumeUsage.gp3",
  394. "operation" : "",
  395. "regionCode" : "cn-northwest-1",
  396. "servicename" : "Amazon Elastic Compute Cloud",
  397. "volumeApiName" : "gp3"
  398. }
  399. }
  400. },
  401. "terms" : {
  402. "OnDemand" : {
  403. "R83VXG9NAPDASEGN" : {
  404. "R83VXG9NAPDASEGN.5Y9WH78GDR" : {
  405. "offerTermCode" : "5Y9WH78GDR",
  406. "sku" : "R83VXG9NAPDASEGN",
  407. "effectiveDate" : "2023-03-01T00:00:00Z",
  408. "priceDimensions" : {
  409. "R83VXG9NAPDASEGN.5Y9WH78GDR.Q7UJUT2CE6" : {
  410. "rateCode" : "R83VXG9NAPDASEGN.5Y9WH78GDR.Q7UJUT2CE6",
  411. "description" : "0.5312 CNY per GB-month of General Purpose (gp3) provisioned storage - China (Ningxia)",
  412. "beginRange" : "0",
  413. "endRange" : "Inf",
  414. "unit" : "GB-Mo",
  415. "pricePerUnit" : {
  416. "CNY" : "0.5312000000"
  417. },
  418. "appliesTo" : [ ]
  419. }
  420. },
  421. "termAttributes" : { }
  422. }
  423. }
  424. }
  425. },
  426. "attributesList" : { }
  427. }
  428. `
  429. awsTest = AWS{
  430. ValidPricingKeys: map[string]bool{},
  431. }
  432. testResponse = http.Response{
  433. Body: ioutil.NopCloser(bytes.NewBufferString(awsCnString)),
  434. Request: &http.Request{
  435. URL: &url.URL{
  436. Scheme: "https",
  437. Host: "test-aws-http-endpoint:443",
  438. },
  439. },
  440. }
  441. awsTest.populatePricing(&testResponse, inputkeys)
  442. expectedProdTermsDisk = &AWSProductTerms{
  443. Sku: "R83VXG9NAPDASEGN",
  444. Memory: "",
  445. Storage: "",
  446. VCpu: "",
  447. GPU: "",
  448. OnDemand: &AWSOfferTerm{
  449. Sku: "R83VXG9NAPDASEGN",
  450. OfferTermCode: "5Y9WH78GDR",
  451. PriceDimensions: map[string]*AWSRateCode{
  452. "R83VXG9NAPDASEGN.5Y9WH78GDR.Q7UJUT2CE6": &AWSRateCode{
  453. Unit: "GB-Mo",
  454. PricePerUnit: AWSCurrencyCode{
  455. USD: "",
  456. CNY: "0.5312000000",
  457. },
  458. },
  459. },
  460. },
  461. PV: &PV{
  462. Cost: "0.0007276712328767123",
  463. CostPerIO: "",
  464. Class: "gp3",
  465. Size: "",
  466. Region: "cn-northwest-1",
  467. ProviderID: "",
  468. },
  469. }
  470. expectedPricing = map[string]*AWSProductTerms{
  471. "cn-northwest-1,EBS:VolumeUsage.gp3": expectedProdTermsDisk,
  472. "cn-northwest-1,EBS:VolumeUsage.gp3,preemptible": expectedProdTermsDisk,
  473. }
  474. if !reflect.DeepEqual(expectedPricing, awsTest.Pricing) {
  475. t.Fatalf("expected parsed pricing did not match actual parsed result (cn)")
  476. }
  477. }