provider_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. package alibaba
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/aliyun/alibaba-cloud-sdk-go/sdk"
  6. "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
  7. "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers"
  8. "github.com/opencost/opencost/pkg/cloud/models"
  9. v1 "k8s.io/api/core/v1"
  10. "k8s.io/apimachinery/pkg/api/resource"
  11. )
  12. func TestCreateDescribePriceACSRequest(t *testing.T) {
  13. node := &SlimK8sNode{
  14. InstanceType: "ecs.g6.large",
  15. RegionID: "cn-hangzhou",
  16. PriceUnit: "Hour",
  17. MemorySizeInKiB: "16KiB",
  18. IsIoOptimized: true,
  19. OSType: "Linux",
  20. ProviderID: "Ali-XXX-node-01",
  21. InstanceTypeFamily: "g6",
  22. }
  23. disk := &SlimK8sDisk{
  24. DiskType: "data",
  25. RegionID: "cn-hangzhou",
  26. PriceUnit: "Hour",
  27. SizeInGiB: "20",
  28. DiskCategory: "diskCategory",
  29. PerformanceLevel: "cloud_essd",
  30. ProviderID: "d-Ali-XXX-01",
  31. StorageClass: "testStorageClass",
  32. }
  33. cases := []struct {
  34. name string
  35. testStruct interface{}
  36. expectedError error
  37. }{
  38. {
  39. name: "test CreateDescribePriceACSRequest with SlimK8sNode struct Object",
  40. testStruct: node,
  41. expectedError: nil,
  42. },
  43. {
  44. name: "test CreateDescribePriceACSRequest with SlimK8sDisk struct Object",
  45. testStruct: disk,
  46. expectedError: nil,
  47. },
  48. }
  49. for _, c := range cases {
  50. t.Run(c.name, func(t *testing.T) {
  51. _, err := createDescribePriceACSRequest(c.testStruct)
  52. if err != nil && c.expectedError == nil {
  53. t.Fatalf("Case name %s: Error converting to Alibaba cloud request", c.name)
  54. }
  55. })
  56. }
  57. }
  58. func TestProcessDescribePriceAndCreateAlibabaPricing(t *testing.T) {
  59. // Skipping this test case since it exposes secret but a good test case to verify when
  60. // supporting a new family of instances, steps to perform are
  61. // STEP 1: Comment the t.Skip() line and then replace XXX_KEY_ID with the alibaba key id of your account and XXX_SECRET_ID with alibaba cloud secret of your account.
  62. // STEP 2: Once you verify describePrice is working and no change needed in processDescribePriceAndCreateAlibabaPricing, you can go ahead and revert the step 1 changes.
  63. // This test case was use to test all general puprose instances
  64. t.Skip()
  65. client, err := sdk.NewClientWithAccessKey("cn-hangzhou", "XXX_KEY_ID", "XXX_SECRET_ID")
  66. if err != nil {
  67. t.Errorf("Error connecting to the Alibaba cloud")
  68. }
  69. aak := credentials.NewAccessKeyCredential("XXX_KEY_ID", "XXX_SECRET_ID")
  70. signer := signers.NewAccessKeySigner(aak)
  71. cases := []struct {
  72. name string
  73. teststruct interface{}
  74. expectedError error
  75. }{
  76. {
  77. name: "test General Purpose Type g7 instance family",
  78. teststruct: &SlimK8sNode{
  79. InstanceType: "ecs.g7.4xlarge",
  80. RegionID: "cn-hangzhou",
  81. PriceUnit: "Hour",
  82. MemorySizeInKiB: "16777216KiB",
  83. IsIoOptimized: true,
  84. OSType: "Linux",
  85. ProviderID: "cn-hangzhou.i-test-01a",
  86. InstanceTypeFamily: "g7",
  87. },
  88. expectedError: nil,
  89. },
  90. {
  91. name: "test General Purpose Type g7a instance family",
  92. teststruct: &SlimK8sNode{
  93. InstanceType: "ecs.g7a.8xlarge",
  94. RegionID: "cn-hangzhou",
  95. PriceUnit: "Hour",
  96. MemorySizeInKiB: "33554432KiB",
  97. IsIoOptimized: true,
  98. OSType: "Linux",
  99. ProviderID: "cn-hangzhou.i-test-01b",
  100. InstanceTypeFamily: "g7a",
  101. },
  102. expectedError: nil,
  103. },
  104. {
  105. name: "test Enhanced General Purpose Type g6e instance family",
  106. teststruct: &SlimK8sNode{
  107. InstanceType: "ecs.g6e.xlarge",
  108. RegionID: "cn-hangzhou",
  109. PriceUnit: "Hour",
  110. MemorySizeInKiB: "16777216KiB",
  111. IsIoOptimized: true,
  112. OSType: "Linux",
  113. ProviderID: "cn-hangzhou.i-test-01",
  114. InstanceTypeFamily: "g6e",
  115. },
  116. expectedError: nil,
  117. },
  118. {
  119. name: "test General Purpose Type g6 instance family",
  120. teststruct: &SlimK8sNode{
  121. InstanceType: "ecs.g6.3xlarge",
  122. RegionID: "cn-hangzhou",
  123. PriceUnit: "Hour",
  124. MemorySizeInKiB: "50331648KiB",
  125. IsIoOptimized: true,
  126. OSType: "Linux",
  127. ProviderID: "cn-hangzhou.i-test-02",
  128. InstanceTypeFamily: "g6",
  129. },
  130. expectedError: nil,
  131. },
  132. {
  133. name: "test General Purpose Type g5 instance family",
  134. teststruct: &SlimK8sNode{
  135. InstanceType: "ecs.g5.2xlarge",
  136. RegionID: "cn-hangzhou",
  137. PriceUnit: "Hour",
  138. MemorySizeInKiB: "33554432KiB",
  139. IsIoOptimized: true,
  140. OSType: "Linux",
  141. ProviderID: "cn-hangzhou.i-test-03",
  142. InstanceTypeFamily: "g5",
  143. },
  144. expectedError: nil,
  145. },
  146. {
  147. name: "test General Purpose Type sn2 instance family",
  148. teststruct: &SlimK8sNode{
  149. InstanceType: "ecs.sn2.large",
  150. RegionID: "cn-hangzhou",
  151. PriceUnit: "Hour",
  152. MemorySizeInKiB: "16777216KiB",
  153. IsIoOptimized: true,
  154. OSType: "Linux",
  155. ProviderID: "cn-hangzhou.i-test-04",
  156. InstanceTypeFamily: "sn2",
  157. },
  158. expectedError: nil,
  159. },
  160. {
  161. name: "test General Purpose Type with Enhanced Network Performance sn2ne instance family",
  162. teststruct: &SlimK8sNode{
  163. InstanceType: "ecs.sn2ne.2xlarge",
  164. RegionID: "cn-hangzhou",
  165. PriceUnit: "Hour",
  166. MemorySizeInKiB: "33554432KiB",
  167. IsIoOptimized: true,
  168. OSType: "Linux",
  169. ProviderID: "cn-hangzhou.i-test-05",
  170. InstanceTypeFamily: "sn2ne",
  171. },
  172. expectedError: nil,
  173. },
  174. {
  175. name: "test Memory Optmized instance type r7 instance family",
  176. teststruct: &SlimK8sNode{
  177. InstanceType: "ecs.r7.6xlarge",
  178. RegionID: "cn-hangzhou",
  179. PriceUnit: "Hour",
  180. MemorySizeInKiB: "2013265592KiB",
  181. IsIoOptimized: true,
  182. OSType: "Linux",
  183. ProviderID: "cn-hangzhou.i-test-06",
  184. InstanceTypeFamily: "r7",
  185. },
  186. expectedError: nil,
  187. },
  188. {
  189. name: "test Memory Optmized instance type r7a instance family",
  190. teststruct: &SlimK8sNode{
  191. InstanceType: "ecs.r7a.8xlarge",
  192. RegionID: "cn-hangzhou",
  193. PriceUnit: "Hour",
  194. MemorySizeInKiB: "33554432KiB",
  195. IsIoOptimized: true,
  196. OSType: "Linux",
  197. ProviderID: "cn-hangzhou.i-test-06a",
  198. InstanceTypeFamily: "r7a",
  199. },
  200. expectedError: nil,
  201. },
  202. {
  203. name: "test Enhanced Memory Optmized instance type r6e instance family",
  204. teststruct: &SlimK8sNode{
  205. InstanceType: "ecs.r6e.4xlarge",
  206. RegionID: "cn-hangzhou",
  207. PriceUnit: "Hour",
  208. MemorySizeInKiB: "2013265592KiB",
  209. IsIoOptimized: true,
  210. OSType: "Linux",
  211. ProviderID: "cn-hangzhou.i-test-07",
  212. InstanceTypeFamily: "r6e",
  213. },
  214. expectedError: nil,
  215. },
  216. {
  217. name: "test Memory Optmized instance type r6a instance family",
  218. teststruct: &SlimK8sNode{
  219. InstanceType: "ecs.r6a.8xlarge",
  220. RegionID: "cn-hangzhou",
  221. PriceUnit: "Hour",
  222. MemorySizeInKiB: "33554432KiB",
  223. IsIoOptimized: true,
  224. OSType: "Linux",
  225. ProviderID: "cn-hangzhou.i-test-07a",
  226. InstanceTypeFamily: "r6a",
  227. },
  228. expectedError: nil,
  229. },
  230. {
  231. name: "test Memory Optmized instance type r6 instance family",
  232. teststruct: &SlimK8sNode{
  233. InstanceType: "ecs.r6.8xlarge",
  234. RegionID: "cn-hangzhou",
  235. PriceUnit: "Hour",
  236. MemorySizeInKiB: "33554432KiB",
  237. IsIoOptimized: true,
  238. OSType: "Linux",
  239. ProviderID: "cn-hangzhou.i-test-08",
  240. InstanceTypeFamily: "r6",
  241. },
  242. expectedError: nil,
  243. },
  244. {
  245. name: "test Memory type instance and r5 instance family",
  246. teststruct: &SlimK8sNode{
  247. InstanceType: "ecs.r5.xlarge",
  248. RegionID: "cn-hangzhou",
  249. PriceUnit: "Hour",
  250. MemorySizeInKiB: "33554432KiB",
  251. IsIoOptimized: true,
  252. OSType: "Linux",
  253. ProviderID: "cn-hangzhou.i-test-09",
  254. InstanceTypeFamily: "r5",
  255. },
  256. expectedError: nil,
  257. },
  258. {
  259. name: "test Memory Optmized instance type with se1 instance family",
  260. teststruct: &SlimK8sNode{
  261. InstanceType: "ecs.se1.4xlarge",
  262. RegionID: "cn-hangzhou",
  263. PriceUnit: "Hour",
  264. MemorySizeInKiB: "16777216KiB",
  265. IsIoOptimized: true,
  266. OSType: "Linux",
  267. ProviderID: "cn-hangzhou.i-test-10",
  268. InstanceTypeFamily: "se1",
  269. },
  270. expectedError: nil,
  271. },
  272. {
  273. name: "test Memory Optmized instance type with Enhanced Network Performance se1ne instance family",
  274. teststruct: &SlimK8sNode{
  275. InstanceType: "ecs.se1ne.3xlarge",
  276. RegionID: "cn-hangzhou",
  277. PriceUnit: "Hour",
  278. MemorySizeInKiB: "100663296KiB",
  279. IsIoOptimized: true,
  280. OSType: "Linux",
  281. ProviderID: "cn-hangzhou.i-test-11",
  282. InstanceTypeFamily: "se1ne",
  283. },
  284. expectedError: nil,
  285. },
  286. {
  287. name: "test High Memory type with re6 instance family",
  288. teststruct: &SlimK8sNode{
  289. InstanceType: "ecs.re6.8xlarge",
  290. RegionID: "cn-hangzhou",
  291. PriceUnit: "Hour",
  292. MemorySizeInKiB: "33554432KiB",
  293. IsIoOptimized: true,
  294. OSType: "Linux",
  295. ProviderID: "cn-hangzhou.i-test-12",
  296. InstanceTypeFamily: "re6",
  297. },
  298. expectedError: nil,
  299. },
  300. {
  301. name: "test Persistent Memory Optimized type with re6p instance family",
  302. teststruct: &SlimK8sNode{
  303. InstanceType: "ecs.re6p.4xlarge",
  304. RegionID: "cn-hangzhou",
  305. PriceUnit: "Hour",
  306. MemorySizeInKiB: "33554432KiB",
  307. IsIoOptimized: true,
  308. OSType: "Linux",
  309. ProviderID: "cn-hangzhou.i-test-13",
  310. InstanceTypeFamily: "re6p",
  311. },
  312. expectedError: nil,
  313. },
  314. {
  315. name: "test Memory type with re4 instance family",
  316. teststruct: &SlimK8sNode{
  317. InstanceType: "ecs.re4.10xlarge",
  318. RegionID: "cn-hangzhou",
  319. PriceUnit: "Hour",
  320. MemorySizeInKiB: "41943040KiB",
  321. IsIoOptimized: true,
  322. OSType: "Linux",
  323. ProviderID: "cn-hangzhou.i-test-14",
  324. InstanceTypeFamily: "re4",
  325. },
  326. expectedError: nil,
  327. },
  328. {
  329. name: "test Memory optimized type with se1 instance family",
  330. teststruct: &SlimK8sNode{
  331. InstanceType: "ecs.se1.8xlarge",
  332. RegionID: "cn-hangzhou",
  333. PriceUnit: "Hour",
  334. MemorySizeInKiB: "33554432KiB",
  335. IsIoOptimized: true,
  336. OSType: "Linux",
  337. ProviderID: "cn-hangzhou.i-test-15",
  338. InstanceTypeFamily: "se1",
  339. },
  340. expectedError: nil,
  341. },
  342. {
  343. name: "test for a nil information",
  344. teststruct: nil,
  345. expectedError: fmt.Errorf("unsupported ECS pricing component at this time"),
  346. },
  347. {
  348. name: "test Cloud Disk with Category cloud representing basic disk",
  349. teststruct: &SlimK8sDisk{
  350. DiskType: "data",
  351. RegionID: "cn-hangzhou",
  352. PriceUnit: "Hour",
  353. SizeInGiB: "20",
  354. DiskCategory: "cloud",
  355. ProviderID: "d-Ali-cloud-XXX-01",
  356. StorageClass: "temp",
  357. },
  358. expectedError: nil,
  359. },
  360. {
  361. name: "test Cloud Disk with Category cloud_efficiency representing ultra disk",
  362. teststruct: &SlimK8sDisk{
  363. DiskType: "data",
  364. RegionID: "cn-hangzhou",
  365. PriceUnit: "Hour",
  366. SizeInGiB: "40",
  367. DiskCategory: "cloud_efficiency",
  368. ProviderID: "d-Ali-cloud-XXX-02",
  369. StorageClass: "temp",
  370. },
  371. expectedError: nil,
  372. },
  373. {
  374. name: "test Cloud Disk with Category cloud_ssd representing standard SSD",
  375. teststruct: &SlimK8sDisk{
  376. DiskType: "data",
  377. RegionID: "cn-hangzhou",
  378. PriceUnit: "Hour",
  379. SizeInGiB: "40",
  380. DiskCategory: "cloud_efficiency",
  381. ProviderID: "d-Ali-cloud-XXX-02",
  382. StorageClass: "temp",
  383. },
  384. expectedError: nil,
  385. },
  386. {
  387. name: "test Cloud Disk with Category cloud_essd representing Enhanced SSD with PL2 performance level",
  388. teststruct: &SlimK8sDisk{
  389. DiskType: "data",
  390. RegionID: "cn-hangzhou",
  391. PriceUnit: "Hour",
  392. SizeInGiB: "80",
  393. DiskCategory: "cloud_ssd",
  394. PerformanceLevel: "PL2",
  395. ProviderID: "d-Ali-cloud-XXX-04",
  396. StorageClass: "temp",
  397. },
  398. expectedError: nil,
  399. },
  400. {
  401. name: "test incorrect disk type",
  402. teststruct: &SlimK8sNode{
  403. InstanceType: "ecs.g6.xlarge",
  404. RegionID: "ap-northeast-1",
  405. PriceUnit: "Hour",
  406. MemorySizeInKiB: "33554432KiB",
  407. IsIoOptimized: true,
  408. OSType: "Linux",
  409. ProviderID: "cn-hangzhou.i-test-15",
  410. InstanceTypeFamily: "se1",
  411. SystemDisk: &SlimK8sDisk{
  412. DiskType: "data",
  413. RegionID: "ap-northeast-1",
  414. PriceUnit: "Hour",
  415. SizeInGiB: "40",
  416. DiskCategory: "cloud_essd",
  417. PerformanceLevel: "PL1",
  418. ProviderID: "d-Ali-cloud-XXX-04",
  419. StorageClass: "temp",
  420. },
  421. },
  422. expectedError: nil,
  423. },
  424. }
  425. custom := &models.CustomPricing{}
  426. for _, c := range cases {
  427. t.Run(c.name, func(t *testing.T) {
  428. pricingObj, err := processDescribePriceAndCreateAlibabaPricing(client, c.teststruct, signer, custom)
  429. if err != nil && c.expectedError == nil {
  430. t.Fatalf("Case name %s: got an error %s", c.name, err)
  431. }
  432. if c.teststruct != nil {
  433. if pricingObj == nil {
  434. t.Fatalf("Case name %s: got a nil pricing object", c.name)
  435. }
  436. t.Logf("Case name %s: Pricing Information gathered for instanceType is %v", c.name, pricingObj.PricingTerms.PricingDetails.TradePrice)
  437. }
  438. })
  439. }
  440. }
  441. func TestGetInstanceFamilyFromType(t *testing.T) {
  442. cases := []struct {
  443. name string
  444. instanceType string
  445. expectedInstanceFamily string
  446. }{
  447. {
  448. name: "test if ecs.[instance-family].[different-type] work",
  449. instanceType: "ecs.sn2ne.2xlarge",
  450. expectedInstanceFamily: "sn2ne",
  451. },
  452. {
  453. name: "test if random word gives you ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE value ",
  454. instanceType: "random.value",
  455. expectedInstanceFamily: ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE,
  456. },
  457. }
  458. for _, c := range cases {
  459. t.Run(c.name, func(t *testing.T) {
  460. returnValue := getInstanceFamilyFromType(c.instanceType)
  461. if returnValue != c.expectedInstanceFamily {
  462. t.Fatalf("Case name %s: expected instance family of type %s but got %s", c.name, c.expectedInstanceFamily, returnValue)
  463. }
  464. })
  465. }
  466. }
  467. func TestDetermineKeyForPricing(t *testing.T) {
  468. type randomK8sStruct struct {
  469. name string
  470. }
  471. cases := []struct {
  472. name string
  473. testVar interface{}
  474. expectedKey string
  475. expectedError error
  476. }{
  477. {
  478. name: "test when all RegionID, InstanceType, OSType & ALIBABA_OPTIMIZE_KEYWORD words are used in Node key",
  479. testVar: &SlimK8sNode{
  480. InstanceType: "ecs.sn2.large",
  481. RegionID: "cn-hangzhou",
  482. PriceUnit: "Hour",
  483. MemorySizeInKiB: "16777216KiB",
  484. IsIoOptimized: true,
  485. OSType: "linux",
  486. ProviderID: "cn-hangzhou.i-test-04",
  487. InstanceTypeFamily: "sn2",
  488. },
  489. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize",
  490. expectedError: nil,
  491. },
  492. {
  493. name: "test missing InstanceType to create Node key",
  494. testVar: &SlimK8sNode{
  495. RegionID: "cn-hangzhou",
  496. PriceUnit: "Hour",
  497. MemorySizeInKiB: "16777216KiB",
  498. IsIoOptimized: true,
  499. OSType: "linux",
  500. ProviderID: "cn-hangzhou.i-test-04",
  501. },
  502. expectedKey: "cn-hangzhou::linux::optimize",
  503. expectedError: nil,
  504. },
  505. {
  506. name: "test when node has a systemDisk Information with missing Performance level",
  507. testVar: &SlimK8sNode{
  508. InstanceType: "ecs.sn2.large",
  509. RegionID: "cn-hangzhou",
  510. PriceUnit: "Hour",
  511. MemorySizeInKiB: "16777216KiB",
  512. IsIoOptimized: true,
  513. OSType: "linux",
  514. ProviderID: "cn-hangzhou.i-test-04",
  515. InstanceTypeFamily: "sn2",
  516. SystemDisk: &SlimK8sDisk{
  517. DiskType: "system",
  518. RegionID: "cn-hangzhou",
  519. PriceUnit: "Hour",
  520. SizeInGiB: "40",
  521. DiskCategory: "cloud_efficiency",
  522. ProviderID: "d-Ali-cloud-XXX-i1",
  523. StorageClass: "",
  524. },
  525. },
  526. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize::cloud_efficiency::40",
  527. expectedError: nil,
  528. },
  529. {
  530. name: "test when node has a systemDisk Information with all information",
  531. testVar: &SlimK8sNode{
  532. InstanceType: "ecs.sn2.large",
  533. RegionID: "cn-hangzhou",
  534. PriceUnit: "Hour",
  535. MemorySizeInKiB: "16777216KiB",
  536. IsIoOptimized: true,
  537. OSType: "linux",
  538. ProviderID: "cn-hangzhou.i-test-04",
  539. InstanceTypeFamily: "sn2",
  540. SystemDisk: &SlimK8sDisk{
  541. DiskType: "data",
  542. RegionID: "cn-hangzhou",
  543. PriceUnit: "Hour",
  544. SizeInGiB: "80",
  545. DiskCategory: "cloud_ssd",
  546. PerformanceLevel: "PL2",
  547. ProviderID: "d-Ali-cloud-XXX-04",
  548. StorageClass: "",
  549. },
  550. },
  551. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize::cloud_ssd::80::PL2",
  552. expectedError: nil,
  553. },
  554. {
  555. name: "test random k8s struct should return unsupported error",
  556. testVar: &randomK8sStruct{
  557. name: "test struct",
  558. },
  559. expectedKey: "",
  560. expectedError: fmt.Errorf("unsupported ECS type randomK8sStruct for DescribePrice at this time"),
  561. },
  562. {
  563. name: "test for nil check",
  564. testVar: nil,
  565. expectedKey: "",
  566. expectedError: fmt.Errorf("unsupported ECS type randomK8sStruct for DescribePrice at this time"),
  567. },
  568. {
  569. name: "test when all RegionID, InstanceType, OSType & ALIBABA_OPTIMIZE_KEYWORD words are used to key",
  570. testVar: &SlimK8sDisk{
  571. DiskType: "data",
  572. RegionID: "cn-hangzhou",
  573. PriceUnit: "Hour",
  574. SizeInGiB: "40",
  575. DiskCategory: "cloud_efficiency",
  576. ProviderID: "d-Ali-cloud-XXX-02",
  577. StorageClass: "temp",
  578. },
  579. expectedKey: "cn-hangzhou::data::cloud_efficiency::40",
  580. expectedError: nil,
  581. },
  582. {
  583. name: "test missing InstanceType to create key",
  584. testVar: &SlimK8sDisk{
  585. DiskType: "data",
  586. RegionID: "cn-hangzhou",
  587. PriceUnit: "Hour",
  588. SizeInGiB: "80",
  589. DiskCategory: "cloud_ssd",
  590. PerformanceLevel: "PL2",
  591. ProviderID: "d-Ali-cloud-XXX-04",
  592. StorageClass: "temp",
  593. },
  594. expectedKey: "cn-hangzhou::data::cloud_ssd::PL2::80",
  595. expectedError: nil,
  596. },
  597. }
  598. for _, c := range cases {
  599. t.Run(c.name, func(t *testing.T) {
  600. returnString, returnErr := determineKeyForPricing(c.testVar)
  601. if c.expectedError == nil && returnErr != nil {
  602. t.Fatalf("Case name %s: expected error was nil but received error %v", c.name, returnErr)
  603. }
  604. if returnString != c.expectedKey {
  605. t.Fatalf("Case name %s: determineKeyForPricing received %s but expected %s", c.name, returnString, c.expectedKey)
  606. }
  607. })
  608. }
  609. }
  610. func TestGenerateSlimK8sNodeFromV1Node(t *testing.T) {
  611. testv1Node := &v1.Node{}
  612. testv1Node.Labels = make(map[string]string)
  613. testv1Node.Labels["topology.kubernetes.io/region"] = "us-east-1"
  614. testv1Node.Labels["beta.kubernetes.io/os"] = "linux"
  615. testv1Node.Labels["node.kubernetes.io/instance-type"] = "ecs.sn2ne.2xlarge"
  616. testv1Node.Status.Capacity = v1.ResourceList{
  617. v1.ResourceMemory: *resource.NewQuantity(16, resource.BinarySI),
  618. }
  619. cases := []struct {
  620. name string
  621. testNode *v1.Node
  622. expectedSlimNode *SlimK8sNode
  623. }{
  624. {
  625. name: "test a generic *v1.Node to *SlimK8sNode Conversion",
  626. testNode: testv1Node,
  627. expectedSlimNode: &SlimK8sNode{
  628. InstanceType: "ecs.sn2ne.2xlarge",
  629. RegionID: "us-east-1",
  630. PriceUnit: ALIBABA_HOUR_PRICE_UNIT,
  631. MemorySizeInKiB: "16",
  632. IsIoOptimized: true,
  633. OSType: "linux",
  634. InstanceTypeFamily: "sn2ne",
  635. },
  636. },
  637. }
  638. for _, c := range cases {
  639. t.Run(c.name, func(t *testing.T) {
  640. returnSlimK8sNode := generateSlimK8sNodeFromV1Node(c.testNode)
  641. if returnSlimK8sNode.InstanceType != c.expectedSlimNode.InstanceType {
  642. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected InstanceType: %s , received InstanceType: %s", c.expectedSlimNode.InstanceType, returnSlimK8sNode.InstanceType)
  643. }
  644. if returnSlimK8sNode.RegionID != c.expectedSlimNode.RegionID {
  645. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected RegionID: %s , received RegionID: %s", c.expectedSlimNode.RegionID, returnSlimK8sNode.RegionID)
  646. }
  647. if returnSlimK8sNode.PriceUnit != c.expectedSlimNode.PriceUnit {
  648. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected PriceUnit: %s , received PriceUnit: %s", c.expectedSlimNode.PriceUnit, returnSlimK8sNode.PriceUnit)
  649. }
  650. if returnSlimK8sNode.MemorySizeInKiB != c.expectedSlimNode.MemorySizeInKiB {
  651. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected MemorySizeInKiB: %s , received MemorySizeInKiB: %s", c.expectedSlimNode.MemorySizeInKiB, returnSlimK8sNode.MemorySizeInKiB)
  652. }
  653. if returnSlimK8sNode.OSType != c.expectedSlimNode.OSType {
  654. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected OSType: %s , received OSType: %s", c.expectedSlimNode.OSType, returnSlimK8sNode.OSType)
  655. }
  656. if returnSlimK8sNode.InstanceTypeFamily != c.expectedSlimNode.InstanceTypeFamily {
  657. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected InstanceTypeFamily: %s , received InstanceTypeFamily: %s", c.expectedSlimNode.InstanceTypeFamily, returnSlimK8sNode.InstanceTypeFamily)
  658. }
  659. })
  660. }
  661. }
  662. func TestGenerateSlimK8sDiskFromV1PV(t *testing.T) {
  663. testv1PV := &v1.PersistentVolume{}
  664. testv1PV.Spec.Capacity = v1.ResourceList{
  665. v1.ResourceStorage: *resource.NewQuantity(16*1024*1024*1024, resource.BinarySI),
  666. }
  667. testv1PV.Spec.CSI = &v1.CSIPersistentVolumeSource{}
  668. testv1PV.Spec.CSI.VolumeHandle = "testPV"
  669. testv1PV.Spec.CSI.VolumeAttributes = map[string]string{
  670. "performanceLevel": "PL2",
  671. "type": "cloud_essd",
  672. }
  673. testv1PV.Spec.CSI.VolumeHandle = "testPV"
  674. testv1PV.Spec.StorageClassName = "testStorageClass"
  675. cases := []struct {
  676. name string
  677. testPV *v1.PersistentVolume
  678. expectedSlimDisk *SlimK8sDisk
  679. inpRegionID string
  680. }{
  681. {
  682. name: "test a generic *v1.Node to *SlimK8sNode Conversion",
  683. testPV: testv1PV,
  684. expectedSlimDisk: &SlimK8sDisk{
  685. DiskType: ALIBABA_DATA_DISK_CATEGORY,
  686. RegionID: "us-east-1",
  687. PriceUnit: ALIBABA_HOUR_PRICE_UNIT,
  688. SizeInGiB: "16",
  689. DiskCategory: "cloud_essd",
  690. PerformanceLevel: "PL2",
  691. ProviderID: "testPV",
  692. StorageClass: "testStorageClass",
  693. },
  694. inpRegionID: "us-east-1",
  695. },
  696. }
  697. for _, c := range cases {
  698. t.Run(c.name, func(t *testing.T) {
  699. returnSlimK8sDisk := generateSlimK8sDiskFromV1PV(c.testPV, c.inpRegionID)
  700. if returnSlimK8sDisk.DiskType != c.expectedSlimDisk.DiskType {
  701. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected DiskType: %s , received DiskType: %s", c.expectedSlimDisk.DiskType, returnSlimK8sDisk.DiskType)
  702. }
  703. if returnSlimK8sDisk.RegionID != c.expectedSlimDisk.RegionID {
  704. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected RegionID: %s , received RegionID Type: %s", c.expectedSlimDisk.RegionID, returnSlimK8sDisk.RegionID)
  705. }
  706. if returnSlimK8sDisk.PriceUnit != c.expectedSlimDisk.PriceUnit {
  707. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected PriceUnit: %s , received PriceUnit Type: %s", c.expectedSlimDisk.PriceUnit, returnSlimK8sDisk.PriceUnit)
  708. }
  709. if returnSlimK8sDisk.SizeInGiB != c.expectedSlimDisk.SizeInGiB {
  710. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected SizeInGiB: %s , received SizeInGiB Type: %s", c.expectedSlimDisk.SizeInGiB, returnSlimK8sDisk.SizeInGiB)
  711. }
  712. if returnSlimK8sDisk.DiskCategory != c.expectedSlimDisk.DiskCategory {
  713. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected DiskCategory: %s , received DiskCategory Type: %s", c.expectedSlimDisk.DiskCategory, returnSlimK8sDisk.DiskCategory)
  714. }
  715. if returnSlimK8sDisk.PerformanceLevel != c.expectedSlimDisk.PerformanceLevel {
  716. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected PerformanceLevel: %s , received PerformanceLevel Type: %s", c.expectedSlimDisk.PerformanceLevel, returnSlimK8sDisk.PerformanceLevel)
  717. }
  718. if returnSlimK8sDisk.ProviderID != c.expectedSlimDisk.ProviderID {
  719. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected ProviderID: %s , received ProviderID Type: %s", c.expectedSlimDisk.ProviderID, returnSlimK8sDisk.ProviderID)
  720. }
  721. if returnSlimK8sDisk.StorageClass != c.expectedSlimDisk.StorageClass {
  722. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected StorageClass: %s , received StorageClass Type: %s", c.expectedSlimDisk.StorageClass, returnSlimK8sDisk.StorageClass)
  723. }
  724. })
  725. }
  726. }
  727. func TestGetNumericalValueFromResourceQuantity(t *testing.T) {
  728. cases := []struct {
  729. name string
  730. inputResourceQuanity string
  731. expectedValue string
  732. }{
  733. {
  734. name: "positive scenario: when inputResourceQuantity is 10Gi",
  735. inputResourceQuanity: "10Gi",
  736. expectedValue: "10",
  737. },
  738. {
  739. name: "negative scenario: when inputResourceQuantity is Gi",
  740. inputResourceQuanity: "Gi",
  741. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  742. },
  743. {
  744. name: "negative scenario: when inputResourceQuantity is 10",
  745. inputResourceQuanity: "10",
  746. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  747. },
  748. {
  749. name: "negative scenario: when inputResourceQuantity is empty string",
  750. inputResourceQuanity: "",
  751. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  752. },
  753. }
  754. for _, c := range cases {
  755. t.Run(c.name, func(t *testing.T) {
  756. returnValue := getNumericalValueFromResourceQuantity(c.inputResourceQuanity)
  757. if c.expectedValue != returnValue {
  758. t.Fatalf("Case name %s: getNumericalValueFromResourceQuantity received %s but expected %s", c.name, returnValue, c.expectedValue)
  759. }
  760. })
  761. }
  762. }
  763. func TestDeterminePVRegion(t *testing.T) {
  764. genericNodeAffinityTestStruct := v1.NodeSelectorTerm{
  765. MatchExpressions: []v1.NodeSelectorRequirement{
  766. {
  767. Key: "topology.diskplugin.csi.alibabacloud.com/zone",
  768. Operator: v1.NodeSelectorOpIn,
  769. Values: []string{"us-east-1a"},
  770. },
  771. },
  772. MatchFields: []v1.NodeSelectorRequirement{},
  773. }
  774. // testPV1 contains the Label with region information as well as node affinity in spec
  775. testPV1 := &v1.PersistentVolume{}
  776. testPV1.Name = "testPV1"
  777. testPV1.Labels = make(map[string]string)
  778. testPV1.Labels[ALIBABA_DISK_TOPOLOGY_REGION_LABEL] = "us-east-1"
  779. testPV1.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
  780. Required: &v1.NodeSelector{
  781. NodeSelectorTerms: []v1.NodeSelectorTerm{genericNodeAffinityTestStruct},
  782. },
  783. }
  784. // testPV2 contains the only zone label
  785. testPV2 := &v1.PersistentVolume{}
  786. testPV2.Name = "testPV2"
  787. testPV2.Labels = make(map[string]string)
  788. testPV2.Labels[ALIBABA_DISK_TOPOLOGY_ZONE_LABEL] = "us-east-1a"
  789. // testPV3 contains only node affinity in spec
  790. testPV3 := &v1.PersistentVolume{}
  791. testPV3.Name = "testPV3"
  792. testPV3.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
  793. Required: &v1.NodeSelector{
  794. NodeSelectorTerms: []v1.NodeSelectorTerm{genericNodeAffinityTestStruct},
  795. },
  796. }
  797. // testPV4 contains no label/annotation or any node affinity
  798. testPV4 := &v1.PersistentVolume{}
  799. testPV4.Name = "testPV4"
  800. cases := []struct {
  801. name string
  802. inputPV *v1.PersistentVolume
  803. expectedRegion string
  804. }{
  805. {
  806. name: "When Region label topology.diskplugin.csi.alibabacloud.com/region is present along with node affinity details",
  807. inputPV: testPV1,
  808. expectedRegion: "us-east-1",
  809. },
  810. {
  811. name: "When zone label topology.diskplugin.csi.alibabacloud.com/zone is present function has to determine region",
  812. inputPV: testPV2,
  813. expectedRegion: "us-east-1",
  814. },
  815. {
  816. name: "When only node affinity detail is present function has to determine the region",
  817. inputPV: testPV3,
  818. expectedRegion: "us-east-1",
  819. },
  820. {
  821. name: "When no region/zone information is present function returns empty to default to cluster region",
  822. inputPV: testPV4,
  823. expectedRegion: "",
  824. },
  825. }
  826. for _, c := range cases {
  827. t.Run(c.name, func(t *testing.T) {
  828. returnRegion := determinePVRegion(c.inputPV)
  829. if c.expectedRegion != returnRegion {
  830. t.Fatalf("Case name %s: determinePVRegion received region :%s but expected region: %s", c.name, returnRegion, c.expectedRegion)
  831. }
  832. })
  833. }
  834. }